[pyzo] 30/68: Use zon.py as a light version of ssdf (I wrote that some time ago in the context of Zoof)

Ghislain Vaillant ghisvail-guest at moszumanska.debian.org
Wed Sep 28 09:47:10 UTC 2016


This is an automated email from the git hooks/post-receive script.

ghisvail-guest pushed a commit to branch debian/master
in repository pyzo.

commit e4483c124297f4a4bc9d4ba424b601c72b7f6f21
Author: Almar Klein <almar.klein at gmail.com>
Date:   Mon Sep 5 16:00:31 2016 +0200

    Use zon.py as a light version of ssdf (I wrote that some time ago in the context of Zoof)
---
 pyzo/__init__.py                       |   2 +-
 pyzo/core/baseTextCtrl.py              |   1 -
 pyzo/core/kernelbroker.py              |   4 +-
 pyzo/core/main.py                      |   2 +-
 pyzo/core/shell.py                     |   8 +-
 pyzo/core/shellInfoDialog.py           |   2 +-
 pyzo/resources/defaultConfig.ssdf      |   5 +-
 pyzo/tools/__init__.py                 |   5 +-
 pyzo/tools/pyzoFileBrowser/__init__.py |   4 +-
 pyzo/tools/pyzoFileBrowser/browser.py  |   3 +-
 pyzo/util/bootstrapconda.py            |   2 +-
 pyzo/util/interpreters/__init__.py     |   1 -
 pyzo/util/zon.py                       | 478 +++++++++++++++++++++++++++++++++
 13 files changed, 495 insertions(+), 22 deletions(-)

diff --git a/pyzo/__init__.py b/pyzo/__init__.py
index 3f3f468..81f2103 100644
--- a/pyzo/__init__.py
+++ b/pyzo/__init__.py
@@ -79,7 +79,7 @@ else:
         print('Our command server is *not* running')
 
 
-from pyzolib import ssdf
+from pyzo.util import zon as ssdf  # zon is ssdf-light
 from pyzo.util.qt import QtCore, QtGui
 
 # Import language/translation tools
diff --git a/pyzo/core/baseTextCtrl.py b/pyzo/core/baseTextCtrl.py
index 0084edf..6537b3a 100644
--- a/pyzo/core/baseTextCtrl.py
+++ b/pyzo/core/baseTextCtrl.py
@@ -15,7 +15,6 @@ is common for both shells and editors.
 import pyzo
 import os, sys, time
 import weakref
-from pyzolib import ssdf
 from pyzo.core.pyzoLogging import print
 import pyzo.codeeditor.parsers.tokens as Tokens
 
diff --git a/pyzo/core/kernelbroker.py b/pyzo/core/kernelbroker.py
index d82b577..a13a3b7 100644
--- a/pyzo/core/kernelbroker.py
+++ b/pyzo/core/kernelbroker.py
@@ -16,9 +16,9 @@ import signal
 import threading
 import ctypes
 
-from pyzolib import ssdf
 import yoton
 import pyzo # local Pyzo (can be on a different box than where the user is)
+from pyzo.util import zon as ssdf  # zon is ssdf-light
 
 
 # To allow interpreters relative to (frozen) Pyzo app
@@ -111,8 +111,6 @@ class KernelInfo(ssdf.Struct):
             # Get struct
             if isinstance(info, dict):
                 s = info
-            elif ssdf.isstruct(info):
-                s = info
             elif isinstance(info, str):
                 s = ssdf.loads(info)
             else:
diff --git a/pyzo/core/main.py b/pyzo/core/main.py
index ba2c6c1..c893ba6 100644
--- a/pyzo/core/main.py
+++ b/pyzo/core/main.py
@@ -15,7 +15,6 @@ function which is also defined here.
 import os, sys, time
 import base64
 from queue import Queue, Empty
-from pyzolib import ssdf
 
 import pyzo
 from pyzo.core.icons import IconArtist
@@ -23,6 +22,7 @@ from pyzo.core import commandline
 from pyzo.util.qt import QtCore, QtGui
 from pyzo.core.splash import SplashWidget
 from pyzo.util import paths
+from pyzo.util import zon as ssdf  # zon is ssdf-light
 
 
 class MainWindow(QtGui.QMainWindow):
diff --git a/pyzo/core/shell.py b/pyzo/core/shell.py
index ace41cb..325f182 100644
--- a/pyzo/core/shell.py
+++ b/pyzo/core/shell.py
@@ -16,14 +16,16 @@ code in it.
 
 """
 
-from pyzo.util.qt import QtCore, QtGui
-Qt = QtCore.Qt
 
 import os, sys, time, subprocess
 import re
+
 import yoton
+
 import pyzo
-from pyzolib import ssdf
+from pyzo.util import zon as ssdf  # zon is ssdf-light
+from pyzo.util.qt import QtCore, QtGui
+Qt = QtCore.Qt
 
 from pyzo.codeeditor.highlighter import Highlighter
 from pyzo.codeeditor import parsers
diff --git a/pyzo/core/shellInfoDialog.py b/pyzo/core/shellInfoDialog.py
index 5257d19..b7db015 100644
--- a/pyzo/core/shellInfoDialog.py
+++ b/pyzo/core/shellInfoDialog.py
@@ -579,7 +579,7 @@ class ShellInfoTab(QtGui.QScrollArea):
             print("Error getting info in shell config:", why)
             print(info)
         
-        # Return the original (but modified) ssdf struct object
+        # Return the original (but modified) ssdf Dict object
         return info
 
 
diff --git a/pyzo/resources/defaultConfig.ssdf b/pyzo/resources/defaultConfig.ssdf
index e54b5f4..1062746 100644
--- a/pyzo/resources/defaultConfig.ssdf
+++ b/pyzo/resources/defaultConfig.ssdf
@@ -1,6 +1,5 @@
-# This Simple Structured Data Format (SSDF) file contains the default 
-# configuration for Pyzo. The user configuration is stored in the Pyzo
-# application data directory.
+# This file contains the default configuration for Pyzo. The user
+# configuration is stored in the Pyzo application data directory.
 
 # Some parameters are named xxx2. This was done in case the representation
 # of a parameter was changed, or if we wanted to "refresh" the parameter for
diff --git a/pyzo/tools/__init__.py b/pyzo/tools/__init__.py
index a7e574d..7ab76ee 100644
--- a/pyzo/tools/__init__.py
+++ b/pyzo/tools/__init__.py
@@ -35,10 +35,9 @@ displayed in the statusbar.
 
 import os, sys, imp
 
-from pyzo.util.qt import QtCore, QtGui
 import pyzo
-
-from pyzolib import ssdf
+from pyzo.util.qt import QtCore, QtGui
+from pyzo.util import zon as ssdf
 
 
 class ToolDockWidget(QtGui.QDockWidget):
diff --git a/pyzo/tools/pyzoFileBrowser/__init__.py b/pyzo/tools/pyzoFileBrowser/__init__.py
index 08bed12..fc5bf01 100644
--- a/pyzo/tools/pyzoFileBrowser/__init__.py
+++ b/pyzo/tools/pyzoFileBrowser/__init__.py
@@ -42,10 +42,10 @@ The config consists of three fields:
 import sys
 import os.path as op
 
-from pyzolib import ssdf
-
 import pyzo
+from pyzo.util import zon as ssdf
 from pyzo.util.qt import QtCore, QtGui
+
 from .browser import Browser
 from .utils import cleanpath, isdir
 
diff --git a/pyzo/tools/pyzoFileBrowser/browser.py b/pyzo/tools/pyzoFileBrowser/browser.py
index 802e8a6..11968e2 100644
--- a/pyzo/tools/pyzoFileBrowser/browser.py
+++ b/pyzo/tools/pyzoFileBrowser/browser.py
@@ -1,10 +1,9 @@
 import sys
 import os.path as op
 
-from pyzolib import ssdf
-
 import pyzo
 from pyzo import translate
+from pyzo.util import zon as ssdf
 
 from . import QtCore, QtGui
 from . import proxies
diff --git a/pyzo/util/bootstrapconda.py b/pyzo/util/bootstrapconda.py
index ea348b0..3a1768e 100644
--- a/pyzo/util/bootstrapconda.py
+++ b/pyzo/util/bootstrapconda.py
@@ -15,7 +15,7 @@ import urllib.request
 
 import pyzo
 from pyzo.util.qt import QtCore, QtGui
-
+from pyzo import translate
 
 base_url = 'http://repo.continuum.io/miniconda/'
 links = {'win32': 'Miniconda3-latest-Windows-x86.exe',
diff --git a/pyzo/util/interpreters/__init__.py b/pyzo/util/interpreters/__init__.py
index 95110e2..79c8f5a 100644
--- a/pyzo/util/interpreters/__init__.py
+++ b/pyzo/util/interpreters/__init__.py
@@ -9,7 +9,6 @@ registry, and conda's environment list.
 
 import sys
 import os
-from pyzolib import ssdf
 
 from .pythoninterpreter import EXE_DIR, PythonInterpreter, versionStringToTuple
 from .inwinreg import get_interpreters_in_reg
diff --git a/pyzo/util/zon.py b/pyzo/util/zon.py
new file mode 100644
index 0000000..4e00a5b
--- /dev/null
+++ b/pyzo/util/zon.py
@@ -0,0 +1,478 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2016, Almar Klein
+
+"""
+Reading and saving Zoof Object Notation files. ZON is like JSON, but a
+more Pythonic format. It's just about 500 lines of code.
+
+This format is a spin-off from the SSDF format, it is fully compatible,
+except that ZON does not support numpy arrays.
+"""
+
+import re
+import sys
+import time
+
+
+# From six.py
+if sys.version_info[0] >= 3:
+    string_types = str,
+    integer_types = int,
+else:
+    string_types = basestring,  # noqa
+    integer_types = (int, long)  # noqa
+float_types = float,
+
+
+## Dict class 
+
+try:  # pragma: no cover
+    from collections import OrderedDict as _dict
+except ImportError:
+    _dict = dict
+
+def isidentifier(s):
+    # http://stackoverflow.com/questions/2544972/
+    if not isinstance(s, str):
+        return False
+    return re.match(r'^\w+$', s, re.UNICODE) and re.match(r'^[0-9]', s) is None 
+
+class Dict(_dict):
+    """ A dict in which the items can be get/set as attributes.
+    """
+    
+    __reserved_names__ = dir(_dict())  # Also from OrderedDict
+    __pure_names__ = dir(dict())
+    
+    __slots__ = []
+    
+    def __repr__(self):
+        identifier_items = []
+        nonidentifier_items = []
+        for key, val in self.items():
+            if isidentifier(key):
+                identifier_items.append('%s=%r' % (key, val))
+            else:
+                nonidentifier_items.append('(%r, %r)' % (key, val))
+        if nonidentifier_items:
+            return 'Dict([%s], %s)' % (', '.join(nonidentifier_items),
+                                       ', '.join(identifier_items))
+        else:
+            return 'Dict(%s)' % (', '.join(identifier_items))
+    
+    def __getattribute__(self, key):
+        try:
+            return object.__getattribute__(self, key)
+        except AttributeError:
+            if key in self:
+                return self[key]
+            else:
+                raise
+    
+    def __setattr__(self, key, val):
+        if key in Dict.__reserved_names__:
+            # Either let OrderedDict do its work, or disallow
+            if key not in Dict.__pure_names__:
+                return _dict.__setattr__(self, key, val)
+            else:
+                raise AttributeError('Reserved name, this key can only ' +
+                                     'be set via ``d[%r] = X``' % key)
+        else:
+            # if isinstance(val, dict): val = Dict(val) -> no, makes a copy!
+            self[key] = val
+    
+    def __dir__(self):
+        names = [k for k in self.keys() if isidentifier(k)]
+        return Dict.__reserved_names__ + names
+
+# SSDF compatibility
+Struct = Dict
+Struct.__is_ssdf_struct__ = True
+
+
+## Public functions
+
+def isstruct(ob):  # SSDF compatibility
+    """ isstruct(ob)
+    
+    Returns whether the given object is an SSDF struct.
+    """
+    if hasattr(ob, '__is_ssdf_struct__'):
+        return bool(ob.__is_ssdf_struct__)
+    else:
+        return False
+
+def new():
+    """ new()
+    
+    Create a new Dict object. The same as "Dict()".
+    """
+    return Dict()
+
+def clear(d):  # SSDF compatibility
+    """ clear(d)
+    
+    Clear all elements of the given Dict object.
+    """
+    d.clear()
+
+def copy(object):
+    """ copy(objec)
+    
+    Return a deep copy the given object. The object and its children
+    should be dict-compatible data types. Note that dicts are converted
+    to Dict and tuples to lists.
+    """
+    if isstruct(object) or isinstance(object, dict):
+        newObject = Dict()
+        for key in object:
+            val = object[key]
+            newObject[key] = copy(val)
+        return newObject
+    elif isinstance(object, (tuple, list)):
+        return [copy(ob) for ob in object]
+    else:
+        return object  # immutable
+
+def count(object, cache=None):
+    """ count(object):
+    
+    Count the number of elements in the given object. An element is
+    defined as one of the 6 datatypes supported by ZON (dict,
+    tuple/list, string, int, float, None).
+    """
+    cache = cache or []
+    if isstruct(object) or isinstance(object, (dict, list)):
+        if id(object) in cache:
+            raise RuntimeError('recursion!')
+        cache.append(id(object))
+    n = 1
+    if isstruct(object) or isinstance(object, dict):
+        for key in object:
+            val = object[key]
+            n += count(val, cache)
+    elif isinstance(object, (tuple, list)):
+        for val in object:
+            n += count(val, cache)
+    return n
+
+def loads(text):
+    """ loads(text)
+    
+    Load a Dict from the given Unicode) string in ZON syntax.
+    """
+    if not isinstance(text, string_types):
+        raise ValueError('zon.loads() expects a string.')
+    reader = ReaderWriter()
+    return reader.read(text)
+
+def load(file):
+    """ load(filename)
+    
+    Load a Dict from the given file or filename.
+    """
+    if isinstance(file, string_types):
+        file = open(file, 'rb')
+    text = file.read().decode('utf-8')
+    return loads(text)
+
+def saves(d):
+    """ saves(d)
+    
+    Serialize the given dict to a (Unicode) string.
+    """
+    if not (isstruct(d) or isinstance(d, dict)):
+        raise ValueError('ssdf.saves() expects a dict.')
+    writer = ReaderWriter()
+    text = writer.save(d)
+    return text
+
+def save(file, d):
+    """ save(file, d)
+    
+    Serialize the given dict to the given file or filename.
+    """
+    text = saves(d)
+    if isinstance(file, string_types):
+        file = open(file, 'wb')
+    with file:
+        file.write(text.encode('utf-8'))
+
+
+## The core
+
+class ReaderWriter(object):
+    
+    def read(self, text):
+        
+        indent = 0
+        root = Dict()
+        container_stack = [(0, root)]
+        new_container = None
+        
+        for i, line in enumerate(text.splitlines()):
+            linenr = i + 1
+            
+            # Strip line
+            line2 = line.lstrip()
+            
+            # Skip comments and empty lines        
+            if not line2 or line2[0] == '#':        
+                continue 
+            
+            # Find the indentation
+            prev_indent = indent
+            indent = len(line) - len(line2)
+            if indent == prev_indent:
+                pass
+            elif indent < prev_indent:
+                while container_stack[-1][0] > indent:
+                    container_stack.pop(-1)
+                if container_stack[-1][0] != indent:
+                    print('ZON: Ignoring wrong dedentation at %i' % linenr)
+            elif indent > prev_indent and new_container is not None:
+                container_stack.append((indent, new_container))
+                new_container = None
+            else:
+                print('ZON: Ignoring wrong indentation at %i' % linenr)
+                indent = prev_indent
+            
+            # Split name and data using a regular expression
+            m = re.search("^\w+? *?=", line2)
+            if m:
+                i = m.end(0)
+                name = line2[:i-1].strip()
+                data = line2[i:].lstrip()
+            else:
+                name = None
+                data = line2
+           
+            # Get value
+            value = self.to_object(data, linenr)
+            
+            # Store the value
+            _indent, current_container = container_stack[-1]
+            if isinstance(current_container, dict):
+                if name:
+                    current_container[name] = value
+                else:
+                    print('ZON: unnamed item in dict on line %i' % linenr)
+            elif isinstance(current_container, list):
+                if name:
+                    print('ZON: named item in list on line %i' % linenr)
+                else:
+                    current_container.append(value)
+            else:
+                raise RuntimeError('Invalid container %r' % current_container)
+            
+            # Prepare for next round
+            if isinstance(value, (dict, list)):
+                new_container = value
+        
+        return root
+    
+    def save(self, d):
+        
+        pyver = '%i.%i.%i' % sys.version_info[:3]
+        ct = time.asctime()
+        lines = []
+        lines.append('# -*- coding: utf-8 -*-')
+        lines.append('# This Zoof Object Notation (ZON) file was')
+        lines.append('# created from Python %s on %s.\n' % (pyver, ct))
+        lines.append('')
+        lines.extend(self.from_dict(d, -2)[1:])
+        
+        return '\r\n'.join(lines)
+        # todo: pop toplevel dict
+    
+    def from_object(self, name, value, indent):
+        
+        # Get object's data
+        if value is None:
+            data = 'Null'
+        elif isinstance(value, integer_types):
+            data = self.from_int(value)
+        elif isinstance(value, float_types):
+            data = self.from_float(value)
+        elif isinstance(value, bool):
+            data = self.from_int(int(value))
+        elif isinstance(value, string_types):
+            data = self.from_unicode(value)
+        elif isinstance(value, dict):
+            data = self.from_dict(value, indent)
+        elif isinstance(value, (list, tuple)):
+            data = self.from_list(value, indent)
+        else:
+            # We do not know            
+            data = 'Null'
+            tmp = repr(value)
+            if len(tmp) > 64:
+                tmp = tmp[:64] + '...'
+            if name is not None:
+                print("ZON: %s is unknown object: %s" %  (name, tmp))
+            else:
+                print("ZON: unknown object: %s" % tmp)
+        
+        # Finish line (or first line)
+        if isinstance(data, string_types):
+            data = [data]
+        if name:
+            data[0] = '%s%s = %s' % (' ' * indent, name, data[0])
+        else:
+            data[0] = '%s%s' % (' ' * indent, data[0])
+        
+        return data
+    
+    def to_object(self, data, linenr):
+        
+        data = data.lstrip()
+        
+        # Determine what type of object we're dealing with by reading
+        # like a human.
+        if not data:
+            print('ZON: no value specified at line %i.' % linenr)
+        elif data[0] in '-.0123456789':
+            return self.to_int_or_float(data, linenr)
+        elif data[0] == "'":
+            return self.to_unicode(data, linenr)
+        elif data.startswith('dict:'):  
+            return self.to_dict(data, linenr)
+        elif data.startswith('list:') or  data[0] == '[':
+            return self.to_list(data, linenr)
+        elif data.startswith('Null') or data.startswith('None'):
+            return None
+        else:
+            print("ZON: invalid type on line %i." % linenr)
+            return None
+    
+    def to_int_or_float(self, data, linenr):
+        line = data.partition('#')[0]
+        try:
+            return int(line)
+        except ValueError:
+            try:
+                return float(line)
+            except ValueError:
+                print("ZON: could not parse number on line %i." % linenr)
+                return None
+    
+    def from_int(self, value):
+        return repr(int(value)).rstrip('L')
+    
+    def from_float(self, value):
+        # Use general specifier with a very high precision.
+        # Any spurious zeros are automatically removed. The precision
+        # should be sufficient such that any numbers saved and loaded 
+        # back will have the exact same value again. 
+        # see e.g. http://bugs.python.org/issue1580
+        return repr(float(value))  # '%0.17g' % value
+    
+    
+    def from_unicode(self, value):
+        value = value.replace('\\', '\\\\')
+        value = value.replace('\n','\\n')
+        value = value.replace('\r','\\r')
+        value = value.replace('\x0b', '\\x0b').replace('\x0c', '\\x0c')
+        value = value.replace("'", "\\'")
+        return "'" + value + "'"
+    
+    def to_unicode(self, data, linenr):
+        # Encode double slashes
+        line = data.replace('\\\\','0x07') # temp
+        
+        # Find string using a regular expression
+        m = re.search("'.*?[^\\\\]'|''", line)
+        if not m:
+            print("ZON: string not ended correctly on line %i." % linenr)
+            return None # return not-a-string
+        else:
+            line = m.group(0)[1:-1]
+        
+        # Decode stuff        
+        line = line.replace('\\n','\n')
+        line = line.replace('\\r','\r')
+        line = line.replace('\\x0b', '\x0b').replace('\\x0c', '\x0c')
+        line = line.replace("\\'","'")
+        line = line.replace('0x07','\\')
+        return line
+    
+    def from_dict(self, value, indent):
+        lines = ["dict:"]
+        # Process children        
+        for key, val in value.items():
+            # Skip all the builtin stuff
+            if key.startswith("__"):
+                continue
+            # Skip methods, or anything else we can call
+            if hasattr(val, '__call__'): 
+                continue  # Note: py3.x does not have function callable
+            # Add!
+            lines.extend(self.from_object(key, val, indent+2))
+        return lines
+    
+    def to_dict(self, data, linenr):
+        return Dict()
+    
+    def from_list(self, value, indent):
+        # Collect subdata and check whether this is a "small list"
+        isSmallList = True
+        allowedTypes = integer_types + float_types + string_types
+        subItems = []
+        for element in value:
+            if not isinstance(element, allowedTypes):
+                isSmallList = False
+            subdata = self.from_object(None, element, 0)  # No indent
+            subItems.extend(subdata)
+        isSmallList = isSmallList and len(subItems) < 256
+        
+        # Return data
+        if isSmallList:
+            return '[%s]' % (', '.join(subItems))
+        else:            
+            data = ["list:"]
+            ind = ' ' * (indent + 2)
+            for item in subItems:
+                data.append(ind + item)
+            return data
+    
+    def to_list(self, data, linenr):
+        value = []
+        if data[0] == 'l': # list:
+            return list()
+        else:
+            i0 = 1
+            pieces = []
+            inString = False
+            escapeThis = False
+            line = data
+            for i in range(1,len(line)):
+                if inString:
+                    # Detect how to get out
+                    if escapeThis:
+                        escapeThis = False
+                        continue
+                    elif line[i] == "\\":
+                        escapeThis = True
+                    elif line[i] == "'":
+                        inString = False
+                else:
+                    # Detect going in a string, break, or end
+                    if line[i] == "'":
+                        inString = True
+                    elif line[i] == ",":
+                        pieces.append(line[i0:i])
+                        i0 = i+1
+                    elif line[i] == "]":
+                        piece = line[i0:i]
+                        if piece.strip(): # Do not add if empty
+                            pieces.append(piece)
+                        break
+            else:
+                print("ZON: short list not closed right on line %i." % linenr)
+            
+            # Cut in pieces and process each piece
+            value = []
+            for piece in pieces:
+                v = self.to_object(piece, linenr)
+                value.append(v)
+            return value

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/pyzo.git



More information about the debian-science-commits mailing list