From 8f866b12443e299fddb1fd031413cd06e5761420 Mon Sep 17 00:00:00 2001 From: George Hopkins Date: Fri, 6 Jan 2017 19:54:07 +0100 Subject: [PATCH 1/6] Prepare for Python 3 --- .travis.yml | 10 ++++++---- setup.py | 14 ++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 113802f..52da2cc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,15 @@ -dist: trusty language: python + cache: pip + python: - "2.7" -os: - - "linux" + - "3.5" + - "3.6" + - "3.7-dev" install: - pip install twisted setuptools_trial simplejson script: - - python ./setup.py trial -s pyutil.test + - python ./setup.py trial -s pyutil.test.current diff --git a/setup.py b/setup.py index 6a28338..c3a3168 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ # # This file is part of pyutil; see README.rst for licensing terms. -import os, re, sys +import os, io, re, sys from setuptools import find_packages, setup @@ -23,6 +23,10 @@ u"Programming Language :: Python", u"Programming Language :: Python :: 2", u"Programming Language :: Python :: 2.7", + u"Programming Language :: Python :: 3", + u"Programming Language :: Python :: 3.5", + u"Programming Language :: Python :: 3.6", + u"Programming Language :: Python :: 3.7", u"Topic :: Utilities", u"Topic :: Software Development :: Libraries", ] @@ -31,6 +35,7 @@ VERSIONFILE = os.path.join(PKG, u"_version.py") import versioneer +versioneer.versionfile_source = VERSIONFILE versioneer.versionfile_build = VERSIONFILE versioneer.tag_prefix = PKG+u'-' # tags are like pyutil-1.2.0 versioneer.parentdir_prefix = PKG+u'-' # dirname like 'myproject-1.2.0' @@ -51,16 +56,13 @@ u'simplejson >= 2.1.0', ] -readmetext_bytes = open(u'README.rst').read() -readmetext_unicode = readmetext_bytes.decode('utf-8') -while readmetext_unicode[0] == u'\ufeff': - readmetext_unicode = readmetext_unicode[1:] +readmetext = io.open(u'README.rst').read() setup(name=PKG, version=versioneer.get_version(), cmdclass=versioneer.get_cmdclass(), description=u'a collection of utilities for Python programmers', - long_description=readmetext_unicode, + long_description=readmetext, author=u"tpltnt", author_email=u'tpltnt+pyutil@il38.nbkawtg.net', url=u'https://github.com/tpltnt/pyutil' + PKG, From 8d907801f803c83e0653d265129ac16fa69903d6 Mon Sep 17 00:00:00 2001 From: George Hopkins Date: Fri, 6 Jan 2017 20:23:26 +0100 Subject: [PATCH 2/6] Remove hard dependency on zbase32 --- setup.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/setup.py b/setup.py index c3a3168..1a4c83b 100644 --- a/setup.py +++ b/setup.py @@ -51,11 +51,6 @@ (os.path.join(u'pyutil', u'data'), [os.path.join(u'pyutil', u'data', u'wordlist.txt')]) ] -install_requires = [ - u'zbase32 >= 1.0', - u'simplejson >= 2.1.0', -] - readmetext = io.open(u'README.rst').read() setup(name=PKG, @@ -70,8 +65,11 @@ packages=find_packages(), include_package_data=True, data_files=data_files, - extras_require={u'jsonutil': [u'simplejson >= 2.1.0',]}, - install_requires=install_requires, + install_requires=[], + extras_require={ + u'jsonutil': [u'simplejson >= 2.1.0',], + u'randcookie': [u'zbase32 >= 1.0',], + }, tests_require=[ u'twisted >= 15.5.0', # for trial (eg user: test_observer) u'mock >= 1.3.0', From 9182938d24b4d40acc633a8dd4189ff64b7ad537 Mon Sep 17 00:00:00 2001 From: George Hopkins Date: Fri, 6 Jan 2017 23:23:37 +0100 Subject: [PATCH 3/6] Use relative imports --- pyutil/PickleSaver.py | 6 +++--- pyutil/assertutil.py | 2 +- pyutil/benchutil.py | 2 +- pyutil/cache.py | 4 ++-- pyutil/memutil.py | 4 ++-- pyutil/nummedobj.py | 2 +- pyutil/odict.py | 4 ++-- pyutil/testutil.py | 2 +- pyutil/twistedutil.py | 2 +- pyutil/weakutil.py | 2 +- pyutil/zlibutil.py | 4 ++-- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pyutil/PickleSaver.py b/pyutil/PickleSaver.py index f3b937b..62a16c4 100644 --- a/pyutil/PickleSaver.py +++ b/pyutil/PickleSaver.py @@ -14,9 +14,9 @@ import warnings # from the pyutil library -import fileutil -import nummedobj -import twistedutil +import .fileutil +import .nummedobj +import .twistedutil # from the Twisted library from twisted.python import log diff --git a/pyutil/assertutil.py b/pyutil/assertutil.py index dc2f2a3..e2a46e3 100644 --- a/pyutil/assertutil.py +++ b/pyutil/assertutil.py @@ -7,7 +7,7 @@ Tests useful in assertion checking, prints out nicely formated messages too. """ -from humanreadable import hr +from .humanreadable import hr def _assert(___cond=False, *___args, **___kwargs): if ___cond: diff --git a/pyutil/benchutil.py b/pyutil/benchutil.py index 1ae1761..a0c337c 100644 --- a/pyutil/benchutil.py +++ b/pyutil/benchutil.py @@ -101,7 +101,7 @@ else: clock = time.time -from assertutil import _assert +from .assertutil import _assert def makeg(func): def blah(n, func=func): diff --git a/pyutil/cache.py b/pyutil/cache.py index e0be6d5..501221b 100644 --- a/pyutil/cache.py +++ b/pyutil/cache.py @@ -15,8 +15,8 @@ import operator -from assertutil import _assert, precondition -from humanreadable import hr +from .assertutil import _assert, precondition +from .humanreadable import hr class LRUCache: """ diff --git a/pyutil/memutil.py b/pyutil/memutil.py index 8016667..a61424e 100644 --- a/pyutil/memutil.py +++ b/pyutil/memutil.py @@ -7,8 +7,8 @@ import exceptions, gc, math, operator, os, sys, types # from the pyutil library -from assertutil import precondition -import mathutil +from .assertutil import precondition +import .mathutil class Canary: """ diff --git a/pyutil/nummedobj.py b/pyutil/nummedobj.py index a00fe20..a766d3e 100644 --- a/pyutil/nummedobj.py +++ b/pyutil/nummedobj.py @@ -3,7 +3,7 @@ # This file is part of pyutil; see README.rst for licensing terms. -import dictutil +import .dictutil class NummedObj(object): """ diff --git a/pyutil/odict.py b/pyutil/odict.py index 3be3d6f..0571098 100644 --- a/pyutil/odict.py +++ b/pyutil/odict.py @@ -17,8 +17,8 @@ import operator -from assertutil import _assert, precondition -from humanreadable import hr +from .assertutil import _assert, precondition +from .humanreadable import hr class OrderedDict: """ diff --git a/pyutil/testutil.py b/pyutil/testutil.py index 46cb817..818c510 100644 --- a/pyutil/testutil.py +++ b/pyutil/testutil.py @@ -5,7 +5,7 @@ from twisted.internet import defer, reactor from twisted.trial import unittest -import repeatable_random +from . import repeatable_random repeatable_random # http://divmod.org/trac/ticket/1499 class SignalMixin: diff --git a/pyutil/twistedutil.py b/pyutil/twistedutil.py index b48c155..ef428fb 100644 --- a/pyutil/twistedutil.py +++ b/pyutil/twistedutil.py @@ -9,7 +9,7 @@ from twisted.internet import reactor # from the pyutil library -from weakutil import WeakMethod +from .weakutil import WeakMethod def callLater_weakly(delay, func, *args, **kwargs): """ diff --git a/pyutil/weakutil.py b/pyutil/weakutil.py index 5944518..80d75e3 100644 --- a/pyutil/weakutil.py +++ b/pyutil/weakutil.py @@ -9,7 +9,7 @@ from weakref import ref # from the pyutil library -from assertutil import precondition +from .assertutil import precondition class WeakMethod: """ Wraps a function or, more importantly, a bound method, in diff --git a/pyutil/zlibutil.py b/pyutil/zlibutil.py index 561850d..f218c58 100644 --- a/pyutil/zlibutil.py +++ b/pyutil/zlibutil.py @@ -13,8 +13,8 @@ import exceptions, string, zlib -from humanreadable import hr -from pyutil.assertutil import precondition +from .humanreadable import hr +from .assertutil import precondition class DecompressError(exceptions.StandardError, zlib.error): pass class UnsafeDecompressError(DecompressError): pass # This means it would take more memory to decompress than we can spare. From 07edae7fad58a6aa918da638095d93884684d3c1 Mon Sep 17 00:00:00 2001 From: George Hopkins Date: Fri, 6 Jan 2017 23:25:01 +0100 Subject: [PATCH 4/6] Simplify assertutil --- pyutil/assertutil.py | 64 +++++++++----------------- pyutil/test/current/test_assertutil.py | 11 +++-- 2 files changed, 28 insertions(+), 47 deletions(-) diff --git a/pyutil/assertutil.py b/pyutil/assertutil.py index e2a46e3..390fb9c 100644 --- a/pyutil/assertutil.py +++ b/pyutil/assertutil.py @@ -9,54 +9,32 @@ from .humanreadable import hr -def _assert(___cond=False, *___args, **___kwargs): +def _format_error(prefix, args, kwargs): + if prefix: + msgbuf=[prefix] + if args or kwargs: + msgbuf.append(": ") + else: + msgbuf=[] + if args: + msgbuf.append(", ".join(["%s %s" % tuple(map(hr, (arg, type(arg),))) for arg in args])) + if kwargs: + if args: + msgbuf.append(", ") + msgbuf.append(", ".join(["%s: %s %s" % tuple(map(hr, (k, kwargs[k], type(kwargs[k]),))) for k in sorted(kwargs.keys())])) + return "".join(msgbuf) + +def _assert(___cond=False, *args, **kwargs): if ___cond: return True - msgbuf=[] - if ___args: - msgbuf.append("%s %s" % tuple(map(hr, (___args[0], type(___args[0]),)))) - msgbuf.extend([", %s %s" % tuple(map(hr, (arg, type(arg),))) for arg in ___args[1:]]) - if ___kwargs: - msgbuf.append(", %s: %s %s" % ((___kwargs.items()[0][0],) + tuple(map(hr, (___kwargs.items()[0][1], type(___kwargs.items()[0][1]),))))) - else: - if ___kwargs: - msgbuf.append("%s: %s %s" % ((___kwargs.items()[0][0],) + tuple(map(hr, (___kwargs.items()[0][1], type(___kwargs.items()[0][1]),))))) - msgbuf.extend([", %s: %s %s" % tuple(map(hr, (k, v, type(v),))) for k, v in ___kwargs.items()[1:]]) + raise AssertionError(_format_error(None, args, kwargs)) - raise AssertionError, "".join(msgbuf) - -def precondition(___cond=False, *___args, **___kwargs): +def precondition(___cond=False, *args, **kwargs): if ___cond: return True - msgbuf=["precondition", ] - if ___args or ___kwargs: - msgbuf.append(": ") - if ___args: - msgbuf.append("%s %s" % tuple(map(hr, (___args[0], type(___args[0]),)))) - msgbuf.extend([", %s %s" % tuple(map(hr, (arg, type(arg),))) for arg in ___args[1:]]) - if ___kwargs: - msgbuf.append(", %s: %s %s" % ((___kwargs.items()[0][0],) + tuple(map(hr, (___kwargs.items()[0][1], type(___kwargs.items()[0][1]),))))) - else: - if ___kwargs: - msgbuf.append("%s: %s %s" % ((___kwargs.items()[0][0],) + tuple(map(hr, (___kwargs.items()[0][1], type(___kwargs.items()[0][1]),))))) - msgbuf.extend([", %s: %s %s" % tuple(map(hr, (k, v, type(v),))) for k, v in ___kwargs.items()[1:]]) + raise AssertionError(_format_error("precondition", args, kwargs)) - raise AssertionError, "".join(msgbuf) - -def postcondition(___cond=False, *___args, **___kwargs): +def postcondition(___cond=False, *args, **kwargs): if ___cond: return True - msgbuf=["postcondition", ] - if ___args or ___kwargs: - msgbuf.append(": ") - if ___args: - msgbuf.append("%s %s" % tuple(map(hr, (___args[0], type(___args[0]),)))) - msgbuf.extend([", %s %s" % tuple(map(hr, (arg, type(arg),))) for arg in ___args[1:]]) - if ___kwargs: - msgbuf.append(", %s: %s %s" % ((___kwargs.items()[0][0],) + tuple(map(hr, (___kwargs.items()[0][1], type(___kwargs.items()[0][1]),))))) - else: - if ___kwargs: - msgbuf.append("%s: %s %s" % ((___kwargs.items()[0][0],) + tuple(map(hr, (___kwargs.items()[0][1], type(___kwargs.items()[0][1]),))))) - msgbuf.extend([", %s: %s %s" % tuple(map(hr, (k, v, type(v),))) for k, v in ___kwargs.items()[1:]]) - - raise AssertionError, "".join(msgbuf) + raise AssertionError(_format_error("postcondition", args, kwargs)) diff --git a/pyutil/test/current/test_assertutil.py b/pyutil/test/current/test_assertutil.py index cb309ec..3d40bea 100644 --- a/pyutil/test/current/test_assertutil.py +++ b/pyutil/test/current/test_assertutil.py @@ -5,14 +5,17 @@ # This file is part of pyutil; see README.rst for licensing terms. # Python Standard Library modules -import unittest +import sys, unittest from pyutil import assertutil -class Testy(unittest.TestCase): +class AssertUtilTestCase(unittest.TestCase): def test_bad_precond(self): adict=23 try: assertutil.precondition(isinstance(adict, dict), "adict is required to be a dict.", 23, adict=adict, foo=None) - except AssertionError, le: - self.failUnless(le.args[0] == "precondition: 'adict is required to be a dict.' , 23 , foo: None , 'adict': 23 ") + except AssertionError as le: + if sys.version_info[0] == 2: + self.assertEqual(le.args[0], "precondition: 'adict is required to be a dict.' , 23 , 'adict': 23 , 'foo': None ") + else: + self.assertEqual(le.args[0], "precondition: 'adict is required to be a dict.' , 23 , 'adict': 23 , 'foo': None ") From e75257f793d9c9c9624edee66b12563cd8c1e1ab Mon Sep 17 00:00:00 2001 From: George Hopkins Date: Fri, 6 Jan 2017 23:36:44 +0100 Subject: [PATCH 5/6] Fix Python 3 compatibility --- pyutil/fileutil.py | 18 +++++++++--------- pyutil/humanreadable.py | 12 ++++++++---- pyutil/observer.py | 12 +++++------- pyutil/odict.py | 16 ++++++++-------- pyutil/test/current/json_tests/test_dump.py | 5 ++++- .../json_tests/test_encode_basestring_ascii.py | 6 ++++-- pyutil/test/current/json_tests/test_unicode.py | 5 +++++ pyutil/test/current/test_fileutil.py | 3 +-- pyutil/test/current/test_iputil.py | 8 +++++--- pyutil/testutil.py | 5 ++++- pyutil/time_format.py | 2 +- pyutil/verlib.py | 5 +++++ pyutil/version_class.py | 17 +++++++++++------ 13 files changed, 70 insertions(+), 44 deletions(-) diff --git a/pyutil/fileutil.py b/pyutil/fileutil.py index 9e785e2..6385a31 100644 --- a/pyutil/fileutil.py +++ b/pyutil/fileutil.py @@ -7,7 +7,7 @@ Futz with files like a pro. """ -import errno, exceptions, os, stat, tempfile +import errno, os, stat, tempfile try: import bsddb @@ -176,7 +176,7 @@ def __del__(self): def shutdown(self): remove(self.name) -def make_dirs(dirname, mode=0777): +def make_dirs(dirname, mode=0o777): """ An idempotent version of os.makedirs(). If the dir already exists, do nothing and return without raising an exception. If this call creates the @@ -187,13 +187,13 @@ def make_dirs(dirname, mode=0777): tx = None try: os.makedirs(dirname, mode) - except OSError, x: + except OSError as x: tx = x if not os.path.isdir(dirname): if tx: raise tx - raise exceptions.IOError, "unknown error prevented creation of directory, or deleted the directory immediately after creation: %s" % dirname # careful not to construct an IOError with a 2-tuple, as that has a special meaning... + raise IOError("unknown error prevented creation of directory, or deleted the directory immediately after creation: %s" % dirname) # careful not to construct an IOError with a 2-tuple, as that has a special meaning... def rmtree(dirname): """ @@ -214,11 +214,11 @@ def rmtree(dirname): else: remove(fullname) os.rmdir(dirname) - except EnvironmentError, le: + except EnvironmentError as le: # Ignore "No such file or directory", collect any other exception. if (le.args[0] != 2 and le.args[0] != 3) or (le.args[0] != errno.ENOENT): excs.append(le) - except Exception, le: + except Exception as le: excs.append(le) # Okay, now we've recursively removed everything, ignoring any "No @@ -228,8 +228,8 @@ def rmtree(dirname): if len(excs) == 1: raise excs[0] if len(excs) == 0: - raise OSError, "Failed to remove dir for unknown reason." - raise OSError, excs + raise OSError("Failed to remove dir for unknown reason.") + raise OSError(excs) def rm_dir(dirname): # Renamed to be like shutil.rmtree and unlike rmdir. @@ -244,7 +244,7 @@ def remove_if_possible(f): def remove_if_present(f): try: remove(f) - except EnvironmentError, le: + except EnvironmentError as le: # Ignore "No such file or directory", re-raise any other exception. if (le.args[0] != 2 and le.args[0] != 3) or (le.args[0] != errno.ENOENT): raise diff --git a/pyutil/humanreadable.py b/pyutil/humanreadable.py index cf09a51..899a12f 100644 --- a/pyutil/humanreadable.py +++ b/pyutil/humanreadable.py @@ -3,8 +3,12 @@ # This file is part of pyutil; see README.rst for licensing terms. -import exceptions, os -from repr import Repr +import os + +try: + from reprlib import Repr +except ImportError: + from repr import Repr class BetterRepr(Repr): def __init__(self): @@ -31,7 +35,7 @@ def repr_instance_method(self, obj, level): return '<' + obj.im_class.__name__ + '.' + obj.im_func.__name__ + '() at (builtin)' def repr_long(self, obj, level): - s = `obj` # XXX Hope this isn't too slow... + s = repr(obj) # XXX Hope this isn't too slow... if len(s) > self.maxlong: i = max(0, (self.maxlong-3)/2) j = max(0, self.maxlong-3-i) @@ -48,7 +52,7 @@ def repr_instance(self, obj, level): on it. If it is an instance of list call self.repr_list() on it. Else call Repr.repr_instance(). """ - if isinstance(obj, exceptions.Exception): + if isinstance(obj, Exception): # Don't cut down exception strings so much. tms = self.maxstring self.maxstring = max(512, tms * 4) diff --git a/pyutil/observer.py b/pyutil/observer.py index aa45bc4..3b5973a 100644 --- a/pyutil/observer.py +++ b/pyutil/observer.py @@ -23,13 +23,12 @@ def __init__(self): self._fired = False self._result = None self._watchers = [] - self.__repr__ = self._unfired_repr - def _unfired_repr(self): - return "" % (self._watchers, ) - - def _fired_repr(self): - return " %s>" % (self._result, ) + def __repr__(self): + if self._fired: + return " %s>" % (self._result, ) + else: + return "" % (self._watchers, ) def _get_result(self): return self._result @@ -51,7 +50,6 @@ def _fire(self, result): for w in self._watchers: eventually(w.callback, result) del self._watchers - self.__repr__ = self._fired_repr def fire_if_not_fired(self, result): if not self._fired: diff --git a/pyutil/odict.py b/pyutil/odict.py index 0571098..a6af576 100644 --- a/pyutil/odict.py +++ b/pyutil/odict.py @@ -143,7 +143,7 @@ def move_to_most_recent(self, k, strictkey=False): if not self.d.has_key(k): if strictkey: - raise KeyError, k + raise KeyError(k) return node = self.d[k] @@ -178,7 +178,7 @@ def __getitem__(self, key, default=None, strictkey=True): node = self.d.get(key) if not node: if strictkey: - raise KeyError, key + raise KeyError(key) return default return node[0] @@ -221,7 +221,7 @@ def __delitem__(self, key, default=None, strictkey=True): return node[0] elif strictkey: assert self._assert_invariants() - raise KeyError, key + raise KeyError(key) else: assert self._assert_invariants() return default @@ -256,7 +256,7 @@ def update(self, otherdict): def pop(self): assert self._assert_invariants() if len(self.d) < 2: # the +2 is for the sentinels - raise KeyError, 'popitem(): dictionary is empty' + raise KeyError('popitem(): dictionary is empty') k = self.d[self.hs][2] self.remove(k) assert self._assert_invariants() @@ -265,7 +265,7 @@ def pop(self): def popitem(self): assert self._assert_invariants() if len(self.d) < 2: # the +2 is for the sentinels - raise KeyError, 'popitem(): dictionary is empty' + raise KeyError('popitem(): dictionary is empty') k = self.d[self.hs][2] val = self.remove(k) assert self._assert_invariants() @@ -475,7 +475,7 @@ def __delitem__(self, key, default=None, strictkey=True): return val elif strictkey: assert self._assert_invariants() - raise KeyError, key + raise KeyError(key) else: assert self._assert_invariants() return default @@ -542,14 +542,14 @@ def refresh(self, key, strictkey=True): assert self._assert_invariants() if not dict.has_key(self, key): if strictkey: - raise KeyError, key + raise KeyError(key) return self._lru.remove(key) self._lru.append(key) def popitem(self): if not self._lru: - raise KeyError, 'popitem(): dictionary is empty' + raise KeyError('popitem(): dictionary is empty') k = self._lru[-1] obj = self.remove(k) return (k, obj,) diff --git a/pyutil/test/current/json_tests/test_dump.py b/pyutil/test/current/json_tests/test_dump.py index cea13f8..f328a34 100644 --- a/pyutil/test/current/json_tests/test_dump.py +++ b/pyutil/test/current/json_tests/test_dump.py @@ -1,7 +1,10 @@ # -*- coding: utf-8-with-signature-unix; fill-column: 77 -*- # -*- indent-tabs-mode: nil -*- from unittest import TestCase -from cStringIO import StringIO +try: + from cStringIO import StringIO +except ImportError: + from io import StringIO from pyutil import jsonutil as json diff --git a/pyutil/test/current/json_tests/test_encode_basestring_ascii.py b/pyutil/test/current/json_tests/test_encode_basestring_ascii.py index 85a1fce..d526092 100644 --- a/pyutil/test/current/json_tests/test_encode_basestring_ascii.py +++ b/pyutil/test/current/json_tests/test_encode_basestring_ascii.py @@ -1,5 +1,7 @@ # -*- coding: utf-8-with-signature-unix; fill-column: 77 -*- # -*- indent-tabs-mode: nil -*- + +import sys from twisted.trial.unittest import SkipTest, TestCase from pyutil.jsonutil import encoder @@ -13,9 +15,9 @@ (u' s p a c e d ', '" s p a c e d "'), (u'\U0001d120', '"\\ud834\\udd20"'), (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'), - ('\xce\xb1\xce\xa9', '"\\u03b1\\u03a9"'), + (b'\xce\xb1\xce\xa9', '"\\u03b1\\u03a9"'), (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'), - ('\xce\xb1\xce\xa9', '"\\u03b1\\u03a9"'), + (b'\xce\xb1\xce\xa9', '"\\u03b1\\u03a9"'), (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'), (u'\u03b1\u03a9', '"\\u03b1\\u03a9"'), (u"`1~!@#$%^&*()_+-={':[,]}|;.?", '"`1~!@#$%^&*()_+-={\':[,]}|;.?"'), diff --git a/pyutil/test/current/json_tests/test_unicode.py b/pyutil/test/current/json_tests/test_unicode.py index 5e1aaf3..7354ca2 100644 --- a/pyutil/test/current/json_tests/test_unicode.py +++ b/pyutil/test/current/json_tests/test_unicode.py @@ -4,6 +4,11 @@ from pyutil import jsonutil as json +try: + unichr = unichr +except NameError: + unichr = chr + class TestUnicode(TestCase): def test_encoding1(self): encoder = json.JSONEncoder(encoding='utf-8') diff --git a/pyutil/test/current/test_fileutil.py b/pyutil/test/current/test_fileutil.py index 4900ce5..ce007d5 100644 --- a/pyutil/test/current/test_fileutil.py +++ b/pyutil/test/current/test_fileutil.py @@ -10,7 +10,7 @@ from pyutil import fileutil class FileUtil(unittest.TestCase): - def mkdir(self, basedir, path, mode=0777): + def mkdir(self, basedir, path, mode=0o777): fn = os.path.join(basedir, path) fileutil.make_dirs(fn, mode) @@ -35,4 +35,3 @@ def test_du(self): used = fileutil.du(basedir) self.failUnlessEqual(10+11+12+13, used) - diff --git a/pyutil/test/current/test_iputil.py b/pyutil/test/current/test_iputil.py index 410231b..9802ca9 100644 --- a/pyutil/test/current/test_iputil.py +++ b/pyutil/test/current/test_iputil.py @@ -4,11 +4,13 @@ # This file is part of pyutil; see README.rst for licensing terms. +from __future__ import print_function + try: from twisted.trial import unittest unittest # http://divmod.org/trac/ticket/1499 -except ImportError, le: - print "Skipping test_iputil since it requires Twisted and Twisted could not be imported: %s" % (le,) +except ImportError as le: + print("Skipping test_iputil since it requires Twisted and Twisted could not be imported: %s" % (le,)) else: from pyutil import iputil, testutil import re @@ -25,7 +27,7 @@ def test_list_async(self): from twisted.trial import unittest unittest # http://divmod.org/trac/ticket/1499 from pyutil import iputil - except ImportError, le: + except ImportError as le: raise unittest.SkipTest("iputil could not be imported (probably because its dependency, Twisted, is not installed). %s" % (le,)) d = iputil.get_local_addresses_async() diff --git a/pyutil/testutil.py b/pyutil/testutil.py index 818c510..ff57b01 100644 --- a/pyutil/testutil.py +++ b/pyutil/testutil.py @@ -1,5 +1,8 @@ # -*- coding: utf-8-with-signature-unix; fill-column: 77 -*- # -*- indent-tabs-mode: nil -*- + +from __future__ import print_function + import os, signal, time from twisted.internet import defer, reactor @@ -99,7 +102,7 @@ def clean_pending(self, dummy=None, required_to_quiesce=True): if p.active(): p.cancel() else: - print "WEIRDNESS! pending timed call not active!" + print("WEIRDNESS! pending timed call not active!") if required_to_quiesce and active: self.fail("Reactor was still active when it was required to be quiescent.") diff --git a/pyutil/time_format.py b/pyutil/time_format.py index 5e6da22..62d6718 100644 --- a/pyutil/time_format.py +++ b/pyutil/time_format.py @@ -30,7 +30,7 @@ def iso_utc_time_to_seconds(isotime, _conversion_re=re.compile(r"(?P\d{4}) """ m = _conversion_re.match(isotime) if not m: - raise ValueError, (isotime, "not a complete ISO8601 timestamp") + raise ValueError(isotime, "not a complete ISO8601 timestamp") year, month, day = int(m.group('year')), int(m.group('month')), int(m.group('day')) hour, minute, second = int(m.group('hour')), int(m.group('minute')), int(m.group('second')) diff --git a/pyutil/verlib.py b/pyutil/verlib.py index 9476d66..945cd09 100644 --- a/pyutil/verlib.py +++ b/pyutil/verlib.py @@ -7,6 +7,11 @@ import re +try: + unicode = unicode +except NameError: + basestring = (str, bytes) + class IrrationalVersionError(Exception): """This is an irrational version.""" pass diff --git a/pyutil/version_class.py b/pyutil/version_class.py index c824123..7dbcfb1 100644 --- a/pyutil/version_class.py +++ b/pyutil/version_class.py @@ -9,11 +9,9 @@ # verlib a.k.a. distutils.version by Tarek Ziadé. from pyutil.verlib import NormalizedVersion -def cmp_version(v1, v2): - return cmp(NormalizedVersion(str(v1)), NormalizedVersion(str(v2))) # Python Standard Library -import re +import functools, re # End users see version strings like this: @@ -86,6 +84,7 @@ def cmp_version(v1, v2): VERSION_RE_STR=VERSION_BASE_RE_STR + VERSION_SUFFIX_RE_STR VERSION_RE=re.compile("^" + VERSION_RE_STR + "$") +@functools.total_ordering class Version(object): def __init__(self, vstring=None): self.major = None @@ -98,14 +97,14 @@ def __init__(self, vstring=None): if vstring: try: self.parse(vstring) - except ValueError, le: + except ValueError as le: le.args = tuple(le.args + ('vstring:', vstring,)) raise def parse(self, vstring): mo = VERSION_RE.search(vstring) if not mo: - raise ValueError, "Not a valid version string for pyutil.version_class.Version(): %r" % (vstring,) + raise ValueError("Not a valid version string for pyutil.version_class.Version(): %r" % (vstring,)) self.major = int(mo.group(1)) self.minor = mo.group(3) and int(mo.group(3)) or 0 @@ -146,4 +145,10 @@ def __repr__(self): return self.__str__() def __cmp__ (self, other): - return cmp_version(self, other) + return cmp(NormalizedVersion(str(self)), NormalizedVersion(str(other))) + + def __eq__ (self, other): + return NormalizedVersion(str(self)) == NormalizedVersion(str(other)) + + def __lt__ (self, other): + return NormalizedVersion(str(self)) < NormalizedVersion(str(other)) From 0ab1af467841e70b0a788381eec6f12f9d9813b6 Mon Sep 17 00:00:00 2001 From: George Hopkins Date: Fri, 12 Jan 2018 15:57:28 +0100 Subject: [PATCH 6/6] Simplify versioneer configuration --- setup.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/setup.py b/setup.py index 1a4c83b..db34c9a 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,8 @@ import os, io, re, sys +import versioneer + from setuptools import find_packages, setup trove_classifiers=[ @@ -32,13 +34,6 @@ ] PKG=u'pyutil' -VERSIONFILE = os.path.join(PKG, u"_version.py") - -import versioneer -versioneer.versionfile_source = VERSIONFILE -versioneer.versionfile_build = VERSIONFILE -versioneer.tag_prefix = PKG+u'-' # tags are like pyutil-1.2.0 -versioneer.parentdir_prefix = PKG+u'-' # dirname like 'myproject-1.2.0' doc_fnames=[ u'COPYING.SPL.txt', u'COPYING.GPL', u'COPYING.TGPPL.rst', u'README.rst', u'CREDITS' ]