[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