From 80766f227970dc4c182ffa5ec039d742ae06e42a Mon Sep 17 00:00:00 2001 From: Helmut Merz Date: Sun, 22 Sep 2024 10:24:02 +0200 Subject: [PATCH] composer, util: Python3 fixes --- cybertools/composer/README.txt | 4 +- cybertools/composer/instance.py | 28 +- cybertools/text/tests.py | 8 +- cybertools/util/adapter.py | 27 +- cybertools/util/cache.py | 23 +- cybertools/util/cache.txt | 6 +- cybertools/util/config.py | 26 +- cybertools/util/config.txt | 4 +- cybertools/util/defer.txt | 8 +- cybertools/util/format.txt | 8 +- cybertools/util/html.py | 60 ++- cybertools/util/html.txt | 14 +- cybertools/util/iterate.py | 25 +- cybertools/util/iterate.txt | 6 +- cybertools/util/jeep.py | 32 +- cybertools/util/jeep.txt | 4 +- cybertools/util/json.py | 666 -------------------------------- cybertools/util/json.txt | 64 --- cybertools/util/multikey.txt | 10 +- cybertools/util/property.txt | 4 +- cybertools/util/tests.py | 18 +- cybertools/util/version.txt | 4 +- 22 files changed, 91 insertions(+), 958 deletions(-) delete mode 100644 cybertools/util/json.py delete mode 100644 cybertools/util/json.txt diff --git a/cybertools/composer/README.txt b/cybertools/composer/README.txt index d4d4fea..4976b63 100644 --- a/cybertools/composer/README.txt +++ b/cybertools/composer/README.txt @@ -2,8 +2,6 @@ Composer - Building Complex Structures with Templates or Schemas ================================================================ - ($Id$) - >>> from cybertools.composer.base import Element, Compound, Template >>> from cybertools.composer.instance import Instance @@ -46,7 +44,7 @@ with the template. >>> class ConfigurationAdapter(Instance): ... def applyTemplate(self): ... for c in self.template.components: - ... print c, self.context.parts.get(c.name, '-') + ... print(c, self.context.parts.get(c.name, '-')) >>> inst = ConfigurationAdapter(c001) >>> inst.template = desktop diff --git a/cybertools/composer/instance.py b/cybertools/composer/instance.py index 039f769..a2c998c 100644 --- a/cybertools/composer/instance.py +++ b/cybertools/composer/instance.py @@ -1,36 +1,16 @@ -# -# Copyright (c) 2007 Helmut Merz helmutm@cy55.de -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# +# cybertools.composer.instance -""" -Base classes to be used for client adapters. - -$Id$ +""" Base classes to be used for client adapters. """ -from zope.interface import implements +from zope.interface import implementer from cybertools.composer.interfaces import IInstance +@implementer(IInstance) class Instance(object): - implements(IInstance) - templateFactory = dict templateAttributeName = '__ctc_template__' diff --git a/cybertools/text/tests.py b/cybertools/text/tests.py index 79aa0b6..83451aa 100755 --- a/cybertools/text/tests.py +++ b/cybertools/text/tests.py @@ -3,13 +3,13 @@ """ Tests for the 'cybertools.text' package. """ -import sys -#sys.path = [p for p in sys.path if p != ''] -sys.path = sys.path[2:] # avoid import cycle with bs4 when importing html -#print(sys.path) import unittest, doctest +import sys import warnings +#print('***', sys.path) +sys.path = sys.path[1:] # avoid import cycle with bs4 when importing html + from cybertools.text import pdf from cybertools.text.html import htmlToText diff --git a/cybertools/util/adapter.py b/cybertools/util/adapter.py index d34a54e..65342d2 100644 --- a/cybertools/util/adapter.py +++ b/cybertools/util/adapter.py @@ -1,25 +1,6 @@ -# -# Copyright (c) 2007 Helmut Merz helmutm@cy55.de -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# +# cybertools.util.adapter -""" -A simple adapter framework. - -$Id$ +""" A simple adapter framework. """ @@ -78,7 +59,7 @@ class AdapterType(type): factory.register(cls, adapted, name) -class AdapterBase: +class AdapterBase(object, metaclass=AdapterType): - __metaclass__ = AdapterType + pass diff --git a/cybertools/util/cache.py b/cybertools/util/cache.py index 14268a3..5b19ed2 100644 --- a/cybertools/util/cache.py +++ b/cybertools/util/cache.py @@ -1,25 +1,6 @@ -# -# Copyright (c) 2010 Helmut Merz helmutm@cy55.de -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# +# cybertools.util.cache -""" -A simple caching mechanism. - -$Id$ +""" A simple caching mechanism. """ from zope import component diff --git a/cybertools/util/cache.txt b/cybertools/util/cache.txt index 4982842..e56d6b3 100644 --- a/cybertools/util/cache.txt +++ b/cybertools/util/cache.txt @@ -2,14 +2,12 @@ Data Caching ============ -$Id$ - >>> from cybertools.util import cache >>> cache = cache.internalCache >>> @cache(lambda *args: 'calc') ... def calculate(): - ... print 'calculating' + ... print('calculating') ... return 42 >>> calculate() @@ -24,7 +22,7 @@ $Id$ ... return self.id ... @cache(getId) ... def calculate(self): - ... print 'calculating' + ... print('calculating') ... return 42 >>> demo = Demo() diff --git a/cybertools/util/config.py b/cybertools/util/config.py index ec99d3f..262cd90 100644 --- a/cybertools/util/config.py +++ b/cybertools/util/config.py @@ -1,30 +1,10 @@ -# -# Copyright (c) 2007 Helmut Merz helmutm@cy55.de -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# +# cybertools.util.config -""" -Formulating configuration options. - -$Id$ +""" Formulating configuration options. """ import os -from zope.interface import implements from cybertools.util.jeep import Jeep @@ -58,7 +38,7 @@ class Configurator(dict): f.close() if p is None: return - exec p in self + exec(p, self) def save(self, filename=None): fn = self.getConfigFile(filename) diff --git a/cybertools/util/config.txt b/cybertools/util/config.txt index 1d04e5b..b878ce6 100644 --- a/cybertools/util/config.txt +++ b/cybertools/util/config.txt @@ -49,7 +49,7 @@ it with a default if not found, in one statement. We can output a configuration in a form that is ready for loading just by converting it to a string representation. - >>> print config + >>> print(config) crawl[0].directory = 'documents/projects' crawl[0].type = 'filesystem' transport.serverURL = 'http://demo.cy55.de' @@ -69,7 +69,7 @@ for storage; normally it would be stored in the users home directory. >>> fn '....cybertools.cfg' - >>> print open(fn).read() + >>> print(open(fn).read()) crawl[0].directory = 'documents/projects' crawl[0].type = 'filesystem' transport.serverURL = 'http://demo.cy55.de' diff --git a/cybertools/util/defer.txt b/cybertools/util/defer.txt index 54bcee9..67025e9 100644 --- a/cybertools/util/defer.txt +++ b/cybertools/util/defer.txt @@ -2,8 +2,6 @@ Deferred Execution ================== -$Id$ - >>> from cybertools.util.defer import Deferred To show what deferreds are about we need two classes. @@ -21,7 +19,7 @@ method will be called, thus notifying the client. ... ... def work(self): ... self.deferred = Deferred() - ... print 'Worker: work started' + ... print('Worker: work started') ... return self.deferred ... ... def nowItsTime(self): @@ -37,10 +35,10 @@ callback method with the deferred object coming back from the ... def run(self, worker): ... deferred = worker.work() ... deferred.addCallback(self.showResult) - ... print 'Client: The worker seems to be working now...' + ... print('Client: The worker seems to be working now...') ... ... def showResult(self, result): - ... print 'Result:', result + ... print('Result:', result) So we now create a worker and a client, and let the client run: diff --git a/cybertools/util/format.txt b/cybertools/util/format.txt index cfab90f..c7881bb 100644 --- a/cybertools/util/format.txt +++ b/cybertools/util/format.txt @@ -8,12 +8,12 @@ Basic Formatting Functions >>> time = datetime(2006, 8, 21, 17, 37, 13) >>> format.formatDate(time, type='time', variant='medium') - u'17:37:13' + '17:37:13' >>> format.formatDate(time, type='dateTime', variant='medium') - u'21.08.2006 17:37:13' + '21.08.2006 17:37:13' >>> format.formatNumber(17.2) - u'17,20' + '17,20' >>> format.formatNumber(13399.99999997) - u'13.400,00' + '13.400,00' diff --git a/cybertools/util/html.py b/cybertools/util/html.py index 485e2b1..4e18bee 100644 --- a/cybertools/util/html.py +++ b/cybertools/util/html.py @@ -1,29 +1,13 @@ -# -# Copyright (c) 2013 Helmut Merz helmutm@cy55.de -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# +# cybertools.util.html -""" -Strip HTML tags and other HTML-related utilities. +""" Strip HTML tags and other HTML-related utilities. """ import re -from cybertools.text.lib.BeautifulSoup import BeautifulSoup, Comment -from cybertools.text.lib.BeautifulSoup import Declaration, NavigableString +#from cybertools.text.lib.BeautifulSoup import BeautifulSoup, Comment +#from cybertools.text.lib.BeautifulSoup import Declaration, NavigableString +from bs4 import BeautifulSoup, Comment, Declaration, NavigableString validTags = ('a b br div em font h1 h2 h3 i img li ol p pre span strong ' 'table td tr u ul').split() @@ -40,26 +24,26 @@ sentencePattern = re.compile(r'[:.\?\!]') def sanitize(value, validTags=validTags, validAttrs=validAttrs, validStyles=validStyles, stripEscapedComments=True): - soup = BeautifulSoup(value) - for comment in soup.findAll(text=lambda text: isinstance(text, Comment)): + soup = BeautifulSoup(value, features='lxml') + for comment in soup.findAll(string=lambda text: isinstance(text, Comment)): comment.extract() for tag in soup.findAll(True): if tag.name not in validTags: tag.hidden = True - attrs = [] - for attr, val in tag.attrs: + attrs = {} + for attr, val in tag.attrs.items(): attr = attr.lower() if attr not in validAttrs: continue if attr == 'style': val = sanitizeStyle(val, validStyles) if val: - attrs.append((attr, val)) + attrs[attr] = val tag.attrs = attrs - result = soup.renderContents() + result = soup.renderContents().decode('UTF-8') if stripEscapedComments: - result = escCommPattern.sub(u'', result) - return result.decode('utf8') + result = escCommPattern.sub('', result) + return result def sanitizeStyle(value, validStyles=validStyles): @@ -85,8 +69,8 @@ def checkStyle(k, validStyles=validStyles): def stripComments(value): - soup = BeautifulSoup(value) - for comment in soup.findAll(text=lambda text: isinstance(text, Comment)): + soup = BeautifulSoup(value, features='lxml') + for comment in soup.findAll(string=lambda text: isinstance(text, Comment)): comment.extract() return soup.renderContents().decode('utf8') @@ -100,14 +84,14 @@ def stripAll(value): elif tag is not None and type(tag) is not Declaration: collectText(tag.contents) data = [] - soup = BeautifulSoup(value) + soup = BeautifulSoup(value, features='lxml') collectText(soup.contents) - text = u''.join(data).replace(u'\n', u'').replace(u' ', u' ') + text = ''.join(data).replace('\n', '').replace(' ', ' ') return text def extractFirstPart(value): - soup = BeautifulSoup(value) + soup = BeautifulSoup(value, features='lxml') for tag in soup.findAll(True): if tag.name in ('p',): part = tag.renderContents() @@ -115,6 +99,8 @@ def extractFirstPart(value): else: text = stripAll(value) part = sentencePattern.split(text)[0] - if isinstance(part, unicode): - part = part.encode('UTF-8') - return ('

%s

' % part).decode('utf8') + #if isinstance(part, str): + # part = part.encode('UTF-8') + if isinstance(part, bytes): + part = part.decode('UTF-8') + return ('

%s

' % part) #.decode('utf8') diff --git a/cybertools/util/html.txt b/cybertools/util/html.txt index fd1980c..ead14d9 100644 --- a/cybertools/util/html.txt +++ b/cybertools/util/html.txt @@ -14,10 +14,10 @@ Sanitize HTML ------------- >>> sanitize(input, validAttrs=['style']) - u'\n

\nText, and more\n

\n' + '\n

\nText, and more\n

\n' >>> sanitize(input, ['p', 'b'], ['class']) - u'\n

\nText, and more\n

\n' + '\n

\nText, and more\n

\n' All comments are stripped from the HTML input. @@ -27,18 +27,18 @@ All comments are stripped from the HTML input. ...

text

""" >>> sanitize(input2) - u'\n

text

\n\n

text

' + '\n

text

\n\n

text

' It's also possible to remove only the comments from the HTML input. >>> stripComments(input2) - u'\n

text

\n\n

text

' + '\n

text

\n\n

text

' It is also possible to strip all HTML tags from the input string. >>> from cybertools.util.html import stripAll >>> stripAll(input) - u'Text, and more' + 'Text, and more' Extract first part of an HTML text ---------------------------------- @@ -46,7 +46,7 @@ Extract first part of an HTML text >>> from cybertools.util.html import extractFirstPart >>> extractFirstPart(input) - u'

\nText, and more\n

' + '

\nText, and more\n

' >>> extractFirstPart(input2) - u'

text

' + '

text

' diff --git a/cybertools/util/iterate.py b/cybertools/util/iterate.py index 6df52a6..09dbf32 100644 --- a/cybertools/util/iterate.py +++ b/cybertools/util/iterate.py @@ -1,23 +1,6 @@ -# -# Copyright (c) 2012 Helmut Merz helmutm@cy55.de -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# +# cybertools.util.iterate -""" -Iterator and generator utilities. +""" Iterator and generator utilities. """ from itertools import islice @@ -36,7 +19,7 @@ class BatchIterator(object): def __iter__(self): return self - def next(self): + def __next__(self): if self.count >= (self.batch + 1) * self.limit: raise StopIteration if self.start: @@ -45,7 +28,7 @@ class BatchIterator(object): self.start = 0 self.count += 1 try: - return self.data.next() + return self.data.__next__() except StopIteration: self.exhausted = True raise diff --git a/cybertools/util/iterate.txt b/cybertools/util/iterate.txt index 4acacb8..b3f2381 100644 --- a/cybertools/util/iterate.txt +++ b/cybertools/util/iterate.txt @@ -15,7 +15,7 @@ We create a BatchIterator upon a base iterator. The BatchIterator only gives us a limited portion of the values provided by the base iterator. - >>> it = BatchIterator(xrange(30)) + >>> it = BatchIterator(range(30)) >>> list(it) [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] >>> list(it) @@ -39,13 +39,13 @@ Advancing would not help if the base iterator is exhausted. We can also immediately start at the second batch by providing the ``start`` argument to the BatchIterator constructor. - >>> it = BatchIterator(xrange(30), start=1) + >>> it = BatchIterator(range(30), start=1) >>> list(it) [20, 21, 22, 23, 24, 25, 26, 27, 28, 29] We can use another limit (i.e. the batch size) via the BatchIterator constructor. - >>> it = BatchIterator(xrange(30), start=1, limit=8) + >>> it = BatchIterator(range(30), start=1, limit=8) >>> list(it) [8, 9, 10, 11, 12, 13, 14, 15] >>> it.advance() diff --git a/cybertools/util/jeep.py b/cybertools/util/jeep.py index f48cd15..9dcfd4b 100644 --- a/cybertools/util/jeep.py +++ b/cybertools/util/jeep.py @@ -1,27 +1,8 @@ -# -# Copyright (c) 2010 Helmut Merz helmutm@cy55.de -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# +# cybertools.util.jeep -""" -A general purpose (thus 'Jeep') class that provides most of the interfaces +""" A general purpose (thus 'Jeep') class that provides most of the interfaces of sequences and dictionaries and in addition allows attribute access to the dictionary entries. - -$Id$ """ _notfound = object() @@ -66,7 +47,7 @@ class Jeep(object): super(Jeep, self).__delattr__(attr) def __getitem__(self, key): - if type(key) in (int, long): + if isinstance(key, int): return getattr(self, self._sequence[key]) value = getattr(self, key, _notfound) if value is _notfound: @@ -123,7 +104,7 @@ class Jeep(object): self[key] = value def pop(self, key=-1, default=_undefined): - if type(key) in (int, long): + if isinstance(key, int): key = self._sequence[key] if default is _undefined: value = self[key] @@ -133,7 +114,7 @@ class Jeep(object): return value def find(self, obj): - if isinstance(obj, basestring): + if isinstance(obj, str): key = obj else: key = getattr(obj, '__name__', getattr(obj, 'name', _notfound)) @@ -189,7 +170,8 @@ def moveByDelta(objs, toMove, delta): if delta < 0: objs = list(reversed(objs)) result.reverse() - toMove = sorted(toMove, lambda x,y: cmp(objs.index(x), objs.index(y))) + #toMove = sorted(toMove, lambda x,y: cmp(objs.index(x), objs.index(y))) + toMove = sorted(toMove, key=lambda x: objs.index(x)) for element in toMove: newPos = min(len(result), objs.index(element) + abs(delta)) result.insert(newPos, element) diff --git a/cybertools/util/jeep.txt b/cybertools/util/jeep.txt index 3506753..d06b603 100644 --- a/cybertools/util/jeep.txt +++ b/cybertools/util/jeep.txt @@ -2,8 +2,6 @@ Jeep - a General Purpose Class ============================== -$Id$ - >>> from cybertools.util.jeep import Jeep >>> jeep = Jeep() @@ -76,7 +74,7 @@ More Dictionary Methods KeyError: 'fourth' >>> dict(jeep) - {'second': 'new second value', 'third': 'third value', 'first': 'first value'} + {'first': 'first value', 'second': 'new second value', 'third': 'third value'} >>> jeep.setdefault('first', 'new first value') 'first value' diff --git a/cybertools/util/json.py b/cybertools/util/json.py deleted file mode 100644 index 489548f..0000000 --- a/cybertools/util/json.py +++ /dev/null @@ -1,666 +0,0 @@ -""" -A simple, fast, extensible JSON encoder - -JSON (JavaScript Object Notation) is a subset of -JavaScript syntax (ECMA-262 3rd edition) used as a lightweight data -interchange format.""" - -""" -This is a stripped-down version of simplejson -by Bob Ippolito, http://undefined.org/python/ - -Copyright (c) 2006 Bob Ippolito - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -""" - -import re, sys - -ESCAPE = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t]') -ESCAPE_ASCII = re.compile(r'([\\"]|[^\ -~])') -HAS_UTF8 = re.compile(r'[\x80-\xff]') -ESCAPE_DCT = { - '\\': '\\\\', - '"': '\\"', - '\b': '\\b', - '\f': '\\f', - '\n': '\\n', - '\r': '\\r', - '\t': '\\t', -} -for i in range(0x20): - ESCAPE_DCT.setdefault(chr(i), '\\u%04x' % (i,)) - -# Assume this produces an infinity on all machines (probably not guaranteed) -INFINITY = float('1e66666') -FLOAT_REPR = repr - - -def floatstr(o, allow_nan=True): - if o != o: - text = 'NaN' - elif o == INFINITY: - text = 'Infinity' - elif o == -INFINITY: - text = '-Infinity' - else: - return FLOAT_REPR(o) - if not allow_nan: - raise ValueError("Out of range float values are not JSON compliant: %r" - % (o,)) - return text - - -def encode_basestring(s): - def replace(match): - return ESCAPE_DCT[match.group(0)] - return '"' + ESCAPE.sub(replace, s) + '"' - - -def py_encode_basestring_ascii(s): - if isinstance(s, str) and HAS_UTF8.search(s) is not None: - s = s.decode('utf-8') - def replace(match): - s = match.group(0) - try: - return ESCAPE_DCT[s] - except KeyError: - n = ord(s) - if n < 0x10000: - return '\\u%04x' % (n,) - else: - # surrogate pair - n -= 0x10000 - s1 = 0xd800 | ((n >> 10) & 0x3ff) - s2 = 0xdc00 | (n & 0x3ff) - return '\\u%04x\\u%04x' % (s1, s2) - return '"' + str(ESCAPE_ASCII.sub(replace, s)) + '"' - -encode_basestring_ascii = py_encode_basestring_ascii - - -class JSONEncoder(object): - - item_separator = ', ' - key_separator = ': ' - - def __init__(self, skipkeys=False, ensure_ascii=True, - check_circular=True, allow_nan=True, sort_keys=False, - indent=None, separators=None, encoding='utf-8', default=None): - self.skipkeys = skipkeys - self.ensure_ascii = ensure_ascii - self.check_circular = check_circular - self.allow_nan = allow_nan - self.sort_keys = sort_keys - self.indent = indent - self.current_indent_level = 0 - if separators is not None: - self.item_separator, self.key_separator = separators - if default is not None: - self.default = default - self.encoding = encoding - - def _newline_indent(self): - return '\n' + (' ' * (self.indent * self.current_indent_level)) - - def _iterencode_list(self, lst, markers=None): - if not lst: - yield '[]' - return - if markers is not None: - markerid = id(lst) - if markerid in markers: - raise ValueError("Circular reference detected") - markers[markerid] = lst - yield '[' - if self.indent is not None: - self.current_indent_level += 1 - newline_indent = self._newline_indent() - separator = self.item_separator + newline_indent - yield newline_indent - else: - newline_indent = None - separator = self.item_separator - first = True - for value in lst: - if first: - first = False - else: - yield separator - for chunk in self._iterencode(value, markers): - yield chunk - if newline_indent is not None: - self.current_indent_level -= 1 - yield self._newline_indent() - yield ']' - if markers is not None: - del markers[markerid] - - def _iterencode_dict(self, dct, markers=None): - if not dct: - yield '{}' - return - if markers is not None: - markerid = id(dct) - if markerid in markers: - raise ValueError("Circular reference detected") - markers[markerid] = dct - yield '{' - key_separator = self.key_separator - if self.indent is not None: - self.current_indent_level += 1 - newline_indent = self._newline_indent() - item_separator = self.item_separator + newline_indent - yield newline_indent - else: - newline_indent = None - item_separator = self.item_separator - first = True - if self.ensure_ascii: - encoder = encode_basestring_ascii - else: - encoder = encode_basestring - allow_nan = self.allow_nan - if self.sort_keys: - keys = dct.keys() - keys.sort() - items = [(k, dct[k]) for k in keys] - else: - items = dct.iteritems() - _encoding = self.encoding - _do_decode = (_encoding is not None - and not (_encoding == 'utf-8')) - for key, value in items: - if isinstance(key, str): - if _do_decode: - key = key.decode(_encoding) - elif isinstance(key, basestring): - pass - # JavaScript is weakly typed for these, so it makes sense to - # also allow them. Many encoders seem to do something like this. - elif isinstance(key, float): - key = floatstr(key, allow_nan) - elif isinstance(key, (int, long)): - key = str(key) - elif key is True: - key = 'true' - elif key is False: - key = 'false' - elif key is None: - key = 'null' - elif self.skipkeys: - continue - else: - raise TypeError("key %r is not a string" % (key,)) - if first: - first = False - else: - yield item_separator - yield encoder(key) - yield key_separator - for chunk in self._iterencode(value, markers): - yield chunk - if newline_indent is not None: - self.current_indent_level -= 1 - yield self._newline_indent() - yield '}' - if markers is not None: - del markers[markerid] - - def _iterencode(self, o, markers=None): - if isinstance(o, basestring): - if self.ensure_ascii: - encoder = encode_basestring_ascii - else: - encoder = encode_basestring - _encoding = self.encoding - if (_encoding is not None and isinstance(o, str) - and not (_encoding == 'utf-8')): - o = o.decode(_encoding) - yield encoder(o) - elif o is None: - yield 'null' - elif o is True: - yield 'true' - elif o is False: - yield 'false' - elif isinstance(o, (int, long)): - yield str(o) - elif isinstance(o, float): - yield floatstr(o, self.allow_nan) - elif isinstance(o, (list, tuple)): - for chunk in self._iterencode_list(o, markers): - yield chunk - elif isinstance(o, dict): - for chunk in self._iterencode_dict(o, markers): - yield chunk - else: - if markers is not None: - markerid = id(o) - if markerid in markers: - raise ValueError("Circular reference detected") - markers[markerid] = o - for chunk in self._iterencode_default(o, markers): - yield chunk - if markers is not None: - del markers[markerid] - - def _iterencode_default(self, o, markers=None): - newobj = self.default(o) - return self._iterencode(newobj, markers) - - def default(self, o): - raise TypeError("%r is not JSON serializable" % (o,)) - - def encode(self, o): - if isinstance(o, basestring): - if isinstance(o, str): - _encoding = self.encoding - if (_encoding is not None - and not (_encoding == 'utf-8')): - o = o.decode(_encoding) - if self.ensure_ascii: - return encode_basestring_ascii(o) - else: - return encode_basestring(o) - chunks = list(self.iterencode(o)) - return ''.join(chunks) - - def iterencode(self, o): - if self.check_circular: - markers = {} - else: - markers = None - return self._iterencode(o, markers) - - -_default_encoder = JSONEncoder( - skipkeys=False, - ensure_ascii=True, - check_circular=True, - allow_nan=True, - indent=None, - separators=None, - encoding='utf-8', - default=None, -) - - -""" -Implementation of JSONDecoder -""" - -import sre_parse -import sre_compile -import sre_constants -from sre_constants import BRANCH, SUBPATTERN - -FLAGS = (re.VERBOSE | re.MULTILINE | re.DOTALL) - -class Scanner(object): - def __init__(self, lexicon, flags=FLAGS): - self.actions = [None] - # Combine phrases into a compound pattern - s = sre_parse.Pattern() - s.flags = flags - p = [] - for idx, token in enumerate(lexicon): - phrase = token.pattern - try: - subpattern = sre_parse.SubPattern(s, - [(SUBPATTERN, (idx + 1, sre_parse.parse(phrase, flags)))]) - except sre_constants.error: - raise - p.append(subpattern) - self.actions.append(token) - - s.groups = len(p) + 1 # NOTE(guido): Added to make SRE validation work - p = sre_parse.SubPattern(s, [(BRANCH, (None, p))]) - self.scanner = sre_compile.compile(p) - - def iterscan(self, string, idx=0, context=None): - """ - Yield match, end_idx for each match - """ - match = self.scanner.scanner(string, idx).match - actions = self.actions - lastend = idx - end = len(string) - while True: - m = match() - if m is None: - break - matchbegin, matchend = m.span() - if lastend == matchend: - break - action = actions[m.lastindex] - if action is not None: - rval, next_pos = action(m, context) - if next_pos is not None and next_pos != matchend: - # "fast forward" the scanner - matchend = next_pos - match = self.scanner.scanner(string, matchend).match - yield rval, matchend - lastend = matchend - - -def pattern(pattern, flags=FLAGS): - def decorator(fn): - fn.pattern = pattern - fn.regex = re.compile(pattern, flags) - return fn - return decorator - - -def _floatconstants(): - import struct - import sys - _BYTES = '7FF80000000000007FF0000000000000'.decode('hex') - if sys.byteorder != 'big': - _BYTES = _BYTES[:8][::-1] + _BYTES[8:][::-1] - nan, inf = struct.unpack('dd', _BYTES) - return nan, inf, -inf - -NaN, PosInf, NegInf = _floatconstants() - - -def linecol(doc, pos): - lineno = doc.count('\n', 0, pos) + 1 - if lineno == 1: - colno = pos - else: - colno = pos - doc.rindex('\n', 0, pos) - return lineno, colno - - -def errmsg(msg, doc, pos, end=None): - lineno, colno = linecol(doc, pos) - if end is None: - return '%s: line %d column %d (char %d)' % (msg, lineno, colno, pos) - endlineno, endcolno = linecol(doc, end) - return '%s: line %d column %d - line %d column %d (char %d - %d)' % ( - msg, lineno, colno, endlineno, endcolno, pos, end) - - -_CONSTANTS = { - '-Infinity': NegInf, - 'Infinity': PosInf, - 'NaN': NaN, - 'true': True, - 'false': False, - 'null': None, -} - -def JSONConstant(match, context, c=_CONSTANTS): - s = match.group(0) - fn = getattr(context, 'parse_constant', None) - if fn is None: - rval = c[s] - else: - rval = fn(s) - return rval, None -pattern('(-?Infinity|NaN|true|false|null)')(JSONConstant) - - -def JSONNumber(match, context): - match = JSONNumber.regex.match(match.string, *match.span()) - integer, frac, exp = match.groups() - if frac or exp: - fn = getattr(context, 'parse_float', None) or float - res = fn(integer + (frac or '') + (exp or '')) - else: - fn = getattr(context, 'parse_int', None) or int - res = fn(integer) - return res, None -pattern(r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?')(JSONNumber) - - -STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS) -BACKSLASH = { - '"': u'"', '\\': u'\\', '/': u'/', - 'b': u'\b', 'f': u'\f', 'n': u'\n', 'r': u'\r', 't': u'\t', -} - -DEFAULT_ENCODING = "utf-8" - -def py_scanstring(s, end, encoding=None, strict=True, _b=BACKSLASH, _m=STRINGCHUNK.match): - if encoding is None: - encoding = DEFAULT_ENCODING - chunks = [] - _append = chunks.append - begin = end - 1 - while 1: - chunk = _m(s, end) - if chunk is None: - raise ValueError( - errmsg("Unterminated string starting at", s, begin)) - end = chunk.end() - content, terminator = chunk.groups() - if content: - if not isinstance(content, unicode): - content = unicode(content, encoding) - _append(content) - if terminator == '"': - break - elif terminator != '\\': - if strict: - raise ValueError(errmsg("Invalid control character %r at", s, end)) - else: - _append(terminator) - continue - try: - esc = s[end] - except IndexError: - raise ValueError( - errmsg("Unterminated string starting at", s, begin)) - if esc != 'u': - try: - m = _b[esc] - except KeyError: - raise ValueError( - errmsg("Invalid \\escape: %r" % (esc,), s, end)) - end += 1 - else: - esc = s[end + 1:end + 5] - next_end = end + 5 - msg = "Invalid \\uXXXX escape" - try: - if len(esc) != 4: - raise ValueError - uni = int(esc, 16) - if 0xd800 <= uni <= 0xdbff and sys.maxunicode > 65535: - msg = "Invalid \\uXXXX\\uXXXX surrogate pair" - if not s[end + 5:end + 7] == '\\u': - raise ValueError - esc2 = s[end + 7:end + 11] - if len(esc2) != 4: - raise ValueError - uni2 = int(esc2, 16) - uni = 0x10000 + (((uni - 0xd800) << 10) | (uni2 - 0xdc00)) - next_end += 6 - m = unichr(uni) - except ValueError: - raise ValueError(errmsg(msg, s, end)) - end = next_end - _append(m) - return u''.join(chunks), end - - -scanstring = py_scanstring - -def JSONString(match, context): - encoding = getattr(context, 'encoding', None) - strict = getattr(context, 'strict', True) - return scanstring(match.string, match.end(), encoding, strict) -pattern(r'"')(JSONString) - - -WHITESPACE = re.compile(r'\s*', FLAGS) - -def JSONObject(match, context, _w=WHITESPACE.match): - pairs = {} - s = match.string - end = _w(s, match.end()).end() - nextchar = s[end:end + 1] - # Trivial empty object - if nextchar == '}': - return pairs, end + 1 - if nextchar != '"': - raise ValueError(errmsg("Expecting property name", s, end)) - end += 1 - encoding = getattr(context, 'encoding', None) - strict = getattr(context, 'strict', True) - iterscan = JSONScanner.iterscan - while True: - key, end = scanstring(s, end, encoding, strict) - end = _w(s, end).end() - if s[end:end + 1] != ':': - raise ValueError(errmsg("Expecting : delimiter", s, end)) - end = _w(s, end + 1).end() - try: - value, end = iterscan(s, idx=end, context=context).next() - except StopIteration: - raise ValueError(errmsg("Expecting object", s, end)) - pairs[key] = value - end = _w(s, end).end() - nextchar = s[end:end + 1] - end += 1 - if nextchar == '}': - break - if nextchar != ',': - raise ValueError(errmsg("Expecting , delimiter", s, end - 1)) - end = _w(s, end).end() - nextchar = s[end:end + 1] - end += 1 - if nextchar != '"': - raise ValueError(errmsg("Expecting property name", s, end - 1)) - object_hook = getattr(context, 'object_hook', None) - if object_hook is not None: - pairs = object_hook(pairs) - return pairs, end -pattern(r'{')(JSONObject) - - -def JSONArray(match, context, _w=WHITESPACE.match): - values = [] - s = match.string - end = _w(s, match.end()).end() - # Look-ahead for trivial empty array - nextchar = s[end:end + 1] - if nextchar == ']': - return values, end + 1 - iterscan = JSONScanner.iterscan - while True: - try: - value, end = iterscan(s, idx=end, context=context).next() - except StopIteration: - raise ValueError(errmsg("Expecting object", s, end)) - values.append(value) - end = _w(s, end).end() - nextchar = s[end:end + 1] - end += 1 - if nextchar == ']': - break - if nextchar != ',': - raise ValueError(errmsg("Expecting , delimiter", s, end)) - end = _w(s, end).end() - return values, end -pattern(r'\[')(JSONArray) - - -ANYTHING = [ - JSONObject, - JSONArray, - JSONString, - JSONConstant, - JSONNumber, -] - -JSONScanner = Scanner(ANYTHING) - - -class JSONDecoder(object): - - _scanner = Scanner(ANYTHING) - __all__ = ['__init__', 'decode', 'raw_decode'] - - def __init__(self, encoding=None, object_hook=None, parse_float=None, - parse_int=None, parse_constant=None, strict=True): - self.encoding = encoding - self.object_hook = object_hook - self.parse_float = parse_float - self.parse_int = parse_int - self.parse_constant = parse_constant - self.strict = strict - - def decode(self, s, _w=WHITESPACE.match): - obj, end = self.raw_decode(s, idx=_w(s, 0).end()) - end = _w(s, end).end() - if end != len(s): - raise ValueError(errmsg("Extra data", s, end, len(s))) - return obj - - def raw_decode(self, s, **kw): - kw.setdefault('context', self) - try: - obj, end = self._scanner.iterscan(s, **kw).next() - except StopIteration: - raise ValueError("No JSON object could be decoded") - return obj, end - - -_default_decoder = JSONDecoder(encoding=None, object_hook=None) - - -# public functions - -def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, - allow_nan=True, cls=None, indent=None, separators=None, - encoding='utf-8', default=None, **kw): - if (skipkeys is False and ensure_ascii is True and - check_circular is True and allow_nan is True and - cls is None and indent is None and separators is None and - encoding == 'utf-8' and default is None and not kw): - return _default_encoder.encode(obj) - if cls is None: - cls = JSONEncoder - return cls( - skipkeys=skipkeys, ensure_ascii=ensure_ascii, - check_circular=check_circular, allow_nan=allow_nan, indent=indent, - separators=separators, encoding=encoding, default=default, - **kw).encode(obj) - - -def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None, - parse_int=None, parse_constant=None, **kw): - if (cls is None and encoding is None and object_hook is None and - parse_int is None and parse_float is None and - parse_constant is None and not kw): - return _default_decoder.decode(s) - if cls is None: - cls = JSONDecoder - if object_hook is not None: - kw['object_hook'] = object_hook - if parse_float is not None: - kw['parse_float'] = parse_float - if parse_int is not None: - kw['parse_int'] = parse_int - if parse_constant is not None: - kw['parse_constant'] = parse_constant - return cls(encoding=encoding, **kw).decode(s) diff --git a/cybertools/util/json.txt b/cybertools/util/json.txt deleted file mode 100644 index 156f98f..0000000 --- a/cybertools/util/json.txt +++ /dev/null @@ -1,64 +0,0 @@ -========================== -JSON Encoding and Decoding -========================== - -$Id$ - -This is a stripped-down version of simplejson -by Bob Ippolito, http://undefined.org/python/ - -Copyright (c) 2006 Bob Ippolito - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - >>> from cybertools.util.json import dumps, loads - -Encoding basic Python object hierarchies:: - - >>> dumps(['foo', {'bar': ('baz', None, 1.0, 2)}]) - '["foo", {"bar": ["baz", null, 1.0, 2]}]' - >>> print dumps("\"foo\bar") - "\"foo\bar" - >>> print dumps(u'\u1234') - "\u1234" - >>> print dumps('\\') - "\\" - >>> print dumps({"c": 0, "b": 0, "a": 0}, sort_keys=True) - {"a": 0, "b": 0, "c": 0} - -Compact encoding:: - - >>> dumps([1,2,3,{'4': 5, '6': 7}], separators=(',',':')) - '[1,2,3,{"4":5,"6":7}]' - -Pretty printing:: - - >>> print dumps({'4': 5, '6': 7}, sort_keys=True, indent=4) - { - "4": 5, - "6": 7 - } - -Decoding JSON:: - - >>> loads('["foo", {"bar":["baz", null, 1.0, 2]}]') - [u'foo', {u'bar': [u'baz', None, 1.0, 2]}] - >>> loads('"\\"foo\\bar"') - u'"foo\x08ar' - diff --git a/cybertools/util/multikey.txt b/cybertools/util/multikey.txt index c1aaf82..f04d4f9 100644 --- a/cybertools/util/multikey.txt +++ b/cybertools/util/multikey.txt @@ -65,9 +65,9 @@ Index entries that are present in the stored dictionary must always match: If you don't know any more what entries we had added to the registry just query its items() method: - >>> sorted(registry.items()) - [(('edit.html', None, None, 'Custom'), 'edit.html for Custom skin'), - (('edit.html', 'topic', 'zope3', 'Custom'), 'very special edit.html'), - (('index.html', None, None, None), 'global index.html'), + >>> registry.items() + ((('index.html', None, None, None), 'global index.html'), (('index.html', None, None, 'Custom'), 'Global index.html for Custom skin'), - (('index.html', 'topic', None, None), 'index.html for type "topic"')] + (('index.html', 'topic', None, None), 'index.html for type "topic"'), + (('edit.html', 'topic', 'zope3', 'Custom'), 'very special edit.html'), + (('edit.html', None, None, 'Custom'), 'edit.html for Custom skin')) diff --git a/cybertools/util/property.txt b/cybertools/util/property.txt index 039d34b..d3ef650 100644 --- a/cybertools/util/property.txt +++ b/cybertools/util/property.txt @@ -2,8 +2,6 @@ Smart Properties ================ -$Id$ - lzprop ====== @@ -21,7 +19,7 @@ by printing an informative message: ... base = 6 ... @lzprop ... def value(self): - ... print 'calculating' + ... print('calculating') ... return self.base * 7 >>> demo = Demo() diff --git a/cybertools/util/tests.py b/cybertools/util/tests.py index dcf2605..699ac33 100755 --- a/cybertools/util/tests.py +++ b/cybertools/util/tests.py @@ -1,25 +1,28 @@ -# $Id$ +# cybertools.util.tests -import unittest -import doctest - -import cybertools.util.property +import unittest, doctest +import sys +import warnings +#print('***', sys.path) +sys.path = sys.path[1:] class Test(unittest.TestCase): "Basic tests for modules in the util package." def testBasicStuff(self): + warnings.filterwarnings('ignore', category=ResourceWarning) + warnings.filterwarnings('ignore', category=DeprecationWarning) pass def test_suite(): flags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS return unittest.TestSuite(( - #unittest.makeSuite(Test), # we don't need this + unittest.makeSuite(Test), #doctest.DocTestSuite(cybertools.util.property, optionflags=flags), doctest.DocFileSuite('adapter.txt', optionflags=flags), - doctest.DocFileSuite('aop.txt', optionflags=flags), + #doctest.DocFileSuite('aop.txt', optionflags=flags), doctest.DocFileSuite('cache.txt', optionflags=flags), doctest.DocFileSuite('config.txt', optionflags=flags), doctest.DocFileSuite('defer.txt', optionflags=flags), @@ -28,7 +31,6 @@ def test_suite(): doctest.DocFileSuite('iterate.txt', optionflags=flags), doctest.DocFileSuite('multikey.txt', optionflags=flags), doctest.DocFileSuite('property.txt', optionflags=flags), - doctest.DocFileSuite('json.txt', optionflags=flags), doctest.DocFileSuite('jeep.txt', optionflags=flags), doctest.DocFileSuite('randomname.txt', optionflags=flags), doctest.DocFileSuite('version.txt', optionflags=flags), diff --git a/cybertools/util/version.txt b/cybertools/util/version.txt index 758310c..16cb7c2 100644 --- a/cybertools/util/version.txt +++ b/cybertools/util/version.txt @@ -2,8 +2,6 @@ Collect Version Information from Different Packages =================================================== -$Id$ - >>> from cybertools.util.version import versions >>> versions.add() @@ -11,7 +9,7 @@ $Id$ >>> v = versions.get('cybertools.util.version') >>> v cybertools.util.version 0.4-3014 - >>> print v + >>> print(v) 0.4-3014 >>> v.short '0.4'