changeset 71:54bb53434887

begin work on decoder that allows identifiers as keys and values
author Henry S. Thompson <ht@markup.co.uk>
date Wed, 28 Jun 2017 19:08:30 +0100
parents 0003fe7b6b67
children 0654e37583b5
files eDecoder.py excel.py sample2y.xlsx
diffstat 3 files changed, 164 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eDecoder.py	Wed Jun 28 19:08:30 2017 +0100
@@ -0,0 +1,104 @@
+'''Extend JSON to support atoms as properties and class names'''
+from json import decoder
+from json.decoder import JSONDecoder, JSONObject, _CONSTANTS, WHITESPACE, WHITESPACE_STR, FLAGS, errmsg
+
+import json, re
+
+JSONObject_orig=JSONObject
+
+class eDecoder(JSONDecoder):
+  def __init__(self, encoding=None, object_hook=None, parse_float=None,
+            parse_int=None, parse_constant=None, strict=True,
+            object_pairs_hook=None):
+
+    self.parse_object = eJSONObject
+    super(eDecoder,self).__init__(encoding, object_hook, parse_float,
+                                  parse_int, e_parse_constant, strict,
+                                  object_pairs_hook)
+
+def e_parse_constant(istr):
+  print('constant: %s'%istr)
+  return _CONSTANTS[istr]
+
+IDENT=re.compile(r'[a-zA-Z0-9_.-]+', FLAGS)
+
+def eJSONObject(s_and_end, encoding, strict, scan_once, object_hook,
+               object_pairs_hook, _w=WHITESPACE.match, _ws=WHITESPACE_STR):
+  s, end = s_and_end
+  if (s[end] in ('"','}') or
+      (s[end] in _ws and
+       s[_w(s,end).end()] in ('"','}'))):
+    return JSONObject_orig(s_and_end, encoding, strict, scan_once, object_hook,
+                           object_pairs_hook, _w, _ws)
+  while True:
+    #key, end = scanstring(s, end, encoding, strict)
+    isId = IDENT.match(s,end)
+    if isId:
+      key=isId.group()
+      end=isId.end()
+    else:
+      raise ValueError(errmsg(
+        "Expecting property name with or w/o in double quotes", s, end))
+
+    # To skip some function call overhead we optimize the fast paths where
+    # the JSON key separator is ": " or just ":".
+    if s[end:end + 1] != ':':
+      end = _w(s, end).end()
+      if s[end:end + 1] != ':':
+        raise ValueError(errmsg("Expecting ':' delimiter", s, end))
+    end += 1
+
+    try:
+      if s[end] in _ws:
+        end += 1
+        if s[end] in _ws:
+          end = _w(s, end + 1).end()
+    except IndexError:
+      pass
+
+    try:
+      value, end = scan_once(s, end)
+    except StopIteration:
+      raise ValueError(errmsg("Expecting object", s, end))
+    pairs_append((key, value))
+
+    try:
+      nextchar = s[end]
+      if nextchar in _ws:
+        end = _w(s, end + 1).end()
+        nextchar = s[end]
+    except IndexError:
+      nextchar = ''
+    end += 1
+
+    if nextchar == '}':
+      break
+    elif nextchar != ',':
+      raise ValueError(errmsg("Expecting ',' delimiter", s, end - 1))
+
+    try:
+      nextchar = s[end]
+      if nextchar in _ws:
+        end += 1
+        nextchar = s[end]
+        if nextchar in _ws:
+          end = _w(s, end + 1).end()
+          nextchar = s[end]
+    except IndexError:
+      nextchar = ''
+
+    end += 1
+    if nextchar != '"':
+      raise ValueError(errmsg(
+          "Expecting property name enclosed in double quotes", s, end - 1))
+  if object_pairs_hook is not None:
+    result = object_pairs_hook(pairs)
+    return result, end
+  pairs = dict(pairs)
+  if object_hook is not None:
+    pairs = object_hook(pairs)
+  return pairs, end
+  
+
+json.scanner.make_scanner=json.scanner.py_make_scanner
+json.decoder.JSONObject=eJSONObject
--- a/excel.py	Mon Jun 26 18:08:25 2017 +0100
+++ b/excel.py	Wed Jun 28 19:08:30 2017 +0100
@@ -1,11 +1,16 @@
 #!/usr/bin/python3
 '''Class model for analysis of Excel spreadsheets'''
 from jsonweb.encode import to_object, dumper
+from jsonweb.decode import from_object, loader
+import json
+import eDecoder
+
 try:
   string_types=basestring
 except NameError:
   string_types=str
 
+@from_object()
 @to_object(exclude_nulls=True)
 class Book(object):
   def __init__(self,source,sheets=[],formats=[]):
@@ -21,15 +26,14 @@
     assert(isinstance(sheet,Sheet))
     self.sheets.append(sheet)
 
+@from_object()
 @to_object(exclude_nulls=True,suppress=["book"])
 class Sheet(object):
-  def __init__(self,name,book,tables=[],docs=[],misc=[]):
+  def __init__(self,name,book=None,tables=[],docs=[],misc=[]):
     assert(isinstance(name,string_types))
     self.name=name
-    assert(isinstance(book,Book))
-    self.book=book
     tables=list(tables)
-    assert(all(isinstance(s,Region) for s in tables))
+    assert(all(isinstance(s,Table) for s in tables))
     self.tables=tables
     docs=list(docs)
     assert(all(isinstance(s,Region) for s in docs))
@@ -37,22 +41,68 @@
     misc=list(misc)
     assert(all(isinstance(s,Region) for s in misc))
     self.misc=misc
-    book.addSheet(self)
+    if book is not None:
+      assert(isinstance(book,Book))
+      book.addSheet(self)
+    self.book=book
 
   def addTable(self,table):
     assert(isinstance(table,Region))
     self.tables.append(table)
 
-@to_object(exclude_nulls=True,suppress=["sheet"])
+@from_object()
+@to_object(exclude_nulls=True,suppress=["parent"])
 class Region(object):
-  def __init__(self,name,sheet=None,ranges=[]):
-    if sheet is not None:
-      assert(isinstance(sheet,Sheet))
-    self.sheet=sheet
+  def __init__(self,name,parent=None,ranges=[],content=[]):
+    if parent is not None:
+      assert(isinstance(parent,(Region,Sheet)))
+    self.parent=parent
     ranges=list(ranges)
     assert(all(isinstance(s,Range) for s in ranges))
     self.ranges=ranges
+    content=list(content)
+    assert(all(isinstance(s,(Region,string_types)) for s in content))
+    self.content=content
     assert(isinstance(name,string_types))
     self.name=name
+    if parent is not None:
+      parent.addRegion(self)
+
+  def addRegion(self,content):
+    assert(isinstance(content,(Region,string_types)))
+    self.content.append(content)
+
+@from_object()
+@to_object(exclude_nulls=True,suppress=["sheet"])
+class Table(Region):
+  def __init__(self,name,sheet=None,ranges=[],labels=[],data=[]):
+    super(Table,self).__init__(name,sheet,ranges)
+    labels=list(labels)
+    assert(all(isinstance(s,Label) for s in labels))
+    self.labels=labels
+    data=list(data)
+    assert(all(isinstance(s,Region) for s in data))
+    self.data=data
     if sheet is not None:
+      assert(isinstance(sheet,Sheet))
       sheet.addTable(self)
+    self.sheet=sheet
+
+  def addLabel(self,label):
+    assert(isinstance(label,Label))
+    self.labels.append(label)
+
+  def addData(self,data):
+    assert(isinstance(data,Data))
+    self.data.append(data)
+
+  def addRegion(self,region):
+    assert(isinstance(region,(Label,Data)))
+    if isinstance(region,Label):
+      self.addLabel(region)
+    else:
+      self.addData(region)
+
+def lt(filename):
+  with open(filename,'r') as js:
+    return json.loads(js.read(),cls=eDecoder.eDecoder)
Binary file sample2y.xlsx has changed