[Checkins] SVN: transaction/branches/chrism-py3/ prelim py3 support

Chris McDonough chrism at plope.com
Sun Sep 25 17:16:09 EST 2011


Log message for revision 122936:
  prelim py3 support

Changed:
  _U  transaction/branches/chrism-py3/
  U   transaction/branches/chrism-py3/setup.py
  U   transaction/branches/chrism-py3/transaction/__init__.py
  U   transaction/branches/chrism-py3/transaction/_transaction.py
  A   transaction/branches/chrism-py3/transaction/compat.py
  U   transaction/branches/chrism-py3/transaction/interfaces.py
  U   transaction/branches/chrism-py3/transaction/tests/convenience.txt
  U   transaction/branches/chrism-py3/transaction/tests/doom.txt
  U   transaction/branches/chrism-py3/transaction/tests/savepoint.txt
  U   transaction/branches/chrism-py3/transaction/tests/savepointsample.py
  U   transaction/branches/chrism-py3/transaction/tests/test_transaction.py

-=-

Property changes on: transaction/branches/chrism-py3
___________________________________________________________________
Modified: svn:ignore
   - *.egg
*.egg-info
build
dist
bin
parts
eggs
develop-eggs
.installed.cfg

   + *.egg
*.egg-info
build
dist
bin
parts
eggs
develop-eggs
.installed.cfg
.coverage


Modified: transaction/branches/chrism-py3/setup.py
===================================================================
--- transaction/branches/chrism-py3/setup.py	2011-09-25 20:29:37 UTC (rev 122935)
+++ transaction/branches/chrism-py3/setup.py	2011-09-25 22:16:09 UTC (rev 122936)
@@ -16,9 +16,6 @@
 
 import os
 
-from ez_setup import use_setuptools
-use_setuptools()
-
 from setuptools import setup, find_packages
 
 here = os.path.abspath(os.path.dirname(__file__))

Modified: transaction/branches/chrism-py3/transaction/__init__.py
===================================================================
--- transaction/branches/chrism-py3/transaction/__init__.py	2011-09-25 20:29:37 UTC (rev 122935)
+++ transaction/branches/chrism-py3/transaction/__init__.py	2011-09-25 22:16:09 UTC (rev 122936)
@@ -20,6 +20,11 @@
 from transaction._manager import TransactionManager
 from transaction._manager import ThreadTransactionManager
 
+# NB: "with transaction:" does not work under Python 3 because they worked
+# really hard to break looking up special methods like __enter__ and __exit__
+# via getattr and getattribute; see http://bugs.python.org/issue12022.  On
+# Python 3, you must use ``with transaction.manager`` instead.
+
 manager = ThreadTransactionManager()
 get = __enter__ = manager.get
 begin = manager.begin

Modified: transaction/branches/chrism-py3/transaction/_transaction.py
===================================================================
--- transaction/branches/chrism-py3/transaction/_transaction.py	2011-09-25 20:29:37 UTC (rev 122935)
+++ transaction/branches/chrism-py3/transaction/_transaction.py	2011-09-25 22:16:09 UTC (rev 122936)
@@ -100,13 +100,16 @@
 import binascii
 import logging
 import sys
-import thread
 import weakref
 import traceback
-from cStringIO import StringIO
 
 from zope import interface
 
+from transaction.compat import reraise
+from transaction.compat import get_thread_ident
+from transaction.compat import native_
+from transaction.compat import bytes_
+from transaction.compat import StringIO
 from transaction.weakset import WeakSet
 from transaction.interfaces import TransactionFailedError
 from transaction import interfaces
@@ -174,7 +177,7 @@
         # directly by storages, leading underscore notwithstanding.
         self._extension = {}
 
-        self.log = logging.getLogger("txn.%d" % thread.get_ident())
+        self.log = logging.getLogger("txn.%d" % get_thread_ident())
         self.log.debug("new transaction")
 
         # If a commit fails, the traceback is saved in _failure_traceback.
@@ -272,8 +275,8 @@
     def _remove_and_invalidate_after(self, savepoint):
         savepoint2index = self._savepoint2index
         index = savepoint2index[savepoint]
-        # use items() to make copy to avoid mutating while iterating
-        for savepoint, i in savepoint2index.items():
+        # use list(items()) to make copy to avoid mutating while iterating
+        for savepoint, i in list(savepoint2index.items()):
             if i > index:
                 savepoint.transaction = None # invalidate
                 del savepoint2index[savepoint]
@@ -335,7 +338,7 @@
             try:
                 t, v, tb = self._saveAndGetCommitishError()
                 self._callAfterCommitHooks(status=False)
-                raise t, v, tb
+                reraise(t, v, tb)
             finally:
                 del t, v, tb
         else:
@@ -371,7 +374,7 @@
         tb = None
         try:
             t, v, tb = self._saveAndGetCommitishError()
-            raise t, v, tb
+            reraise(t, v, tb)
         finally:
             del t, v, tb
             
@@ -435,7 +438,7 @@
         # Execute the two-phase commit protocol.
 
         L = list(self._resources)
-        L.sort(rm_cmp)
+        L.sort(key=rm_key)
         try:
             for rm in L:
                 rm.tpc_begin(self)
@@ -466,7 +469,7 @@
                     self._cleanup(L)
                 finally:
                     self._synchronizers.map(lambda s: s.afterCompletion(self))
-                raise t, v, tb
+                reraise(t, v, tb)
             finally:
                 del t, v, tb
 
@@ -515,7 +518,7 @@
             self.log.debug("abort")
 
             if tb is not None:
-                raise t, v, tb
+                reraise(t, v, tb)
         finally:
             del t, v, tb
 
@@ -588,12 +591,14 @@
                                   object_hint(o), exc_info=sys.exc_info())
 
             if tb is not None:
-                raise t, v, tb
+                reraise(t, v, tb)
         finally:
             del t, v, tb
 
-def rm_cmp(rm1, rm2):
-    return cmp(rm1.sortKey(), rm2.sortKey())
+def rm_key(rm):
+    func = getattr(rm, 'sortKey', None)
+    if func is not None:
+        return func()
 
 def object_hint(o):
     """Return a string describing the object.
@@ -612,7 +617,8 @@
 def oid_repr(oid):
     if isinstance(oid, str) and len(oid) == 8:
         # Convert to hex and strip leading zeroes.
-        as_hex = binascii.hexlify(oid).lstrip('0')
+        as_hex = native_(
+            binascii.hexlify(bytes_(oid, 'ascii')), 'ascii').lstrip('0')
         # Ensure two characters per input byte.
         if len(as_hex) & 1:
             as_hex = '0' + as_hex

Added: transaction/branches/chrism-py3/transaction/compat.py
===================================================================
--- transaction/branches/chrism-py3/transaction/compat.py	                        (rev 0)
+++ transaction/branches/chrism-py3/transaction/compat.py	2011-09-25 22:16:09 UTC (rev 122936)
@@ -0,0 +1,92 @@
+import sys
+import types
+
+PY3 = sys.version_info[0] == 3
+
+if PY3: # pragma: no cover
+    string_types = str,
+    integer_types = int,
+    class_types = type,
+    text_type = str
+    binary_type = bytes
+    long = int
+else:
+    string_types = basestring,
+    integer_types = (int, long)
+    class_types = (type, types.ClassType)
+    text_type = unicode
+    binary_type = str
+    long = long
+
+def text_(s, encoding='latin-1', errors='strict'):
+    if isinstance(s, binary_type):
+        return s.decode(encoding, errors)
+    return s # pragma: no cover
+
+def bytes_(s, encoding='latin-1', errors='strict'):
+    if isinstance(s, text_type):
+        return s.encode(encoding, errors)
+    return s
+
+if PY3: # pragma: no cover
+    def native_(s, encoding='latin-1', errors='strict'):
+        if isinstance(s, text_type):
+            return s
+        return str(s, encoding, errors)
+else:
+    def native_(s, encoding='latin-1', errors='strict'):
+        if isinstance(s, text_type):
+            return s.encode(encoding, errors)
+        return str(s)
+
+if PY3:
+    from io import StringIO
+else:
+    from io import BytesIO as StringIO
+
+if PY3:
+    from collections import MutableMapping
+else:
+    from UserDict import UserDict as MutableMapping
+
+if PY3: # pragma: no cover
+    import builtins
+    exec_ = getattr(builtins, "exec")
+
+
+    def reraise(tp, value, tb=None):
+        if value.__traceback__ is not tb:
+            raise value.with_traceback(tb)
+        raise value
+
+else: # pragma: no cover
+    def exec_(code, globs=None, locs=None):
+        """Execute code in a namespace."""
+        if globs is None:
+            frame = sys._getframe(1)
+            globs = frame.f_globals
+            if locs is None:
+                locs = frame.f_locals
+            del frame
+        elif locs is None:
+            locs = globs
+        exec("""exec code in globs, locs""")
+
+    exec_("""def reraise(tp, value, tb=None):
+    raise tp, value, tb
+""")
+
+
+if PY3:
+    from threading import _get_ident as get_thread_ident
+else:
+    from thread import get_ident as get_thread_ident
+    
+    
+if PY3:
+    def func_name(func):
+        return func.__name__
+else:
+    def func_name(func):
+        return func.func_name
+    

Modified: transaction/branches/chrism-py3/transaction/interfaces.py
===================================================================
--- transaction/branches/chrism-py3/transaction/interfaces.py	2011-09-25 20:29:37 UTC (rev 122935)
+++ transaction/branches/chrism-py3/transaction/interfaces.py	2011-09-25 22:16:09 UTC (rev 122936)
@@ -486,6 +486,7 @@
     invoking abort() on the transaction, or begin() on its transaction
     manager.
     """
+
 class DoomedTransaction(TransactionError):
     """A commit was attempted on a transaction that was doomed."""
 

Modified: transaction/branches/chrism-py3/transaction/tests/convenience.txt
===================================================================
--- transaction/branches/chrism-py3/transaction/tests/convenience.txt	2011-09-25 20:29:37 UTC (rev 122935)
+++ transaction/branches/chrism-py3/transaction/tests/convenience.txt	2011-09-25 22:16:09 UTC (rev 122936)
@@ -12,31 +12,9 @@
 
     >>> import transaction.tests.savepointsample
     >>> dm = transaction.tests.savepointsample.SampleSavepointDataManager()
-    >>> dm.keys()
+    >>> list(dm.keys())
     []
 
-We can use the transaction module directly:
-
-    >>> with transaction as t:
-    ...     dm['z'] = 1
-    ...     t.note('test 1')
-
-    >>> dm['z']
-    1
-
-    >>> dm.last_note
-    'test 1'
-
-    >>> with transaction:
-    ...     dm['z'] = 2
-    ...     xxx
-    Traceback (most recent call last):
-    ...
-    NameError: name 'xxx' is not defined
-
-    >>> dm['z']
-    1
-
 We can use it with a manager:
 
     >>> with transaction.manager as t:
@@ -49,7 +27,7 @@
     >>> dm.last_note
     'test 3'
 
-    >>> with transaction:
+    >>> with transaction.manager:
     ...     dm['z'] = 4
     ...     xxx
     Traceback (most recent call last):
@@ -59,6 +37,10 @@
     >>> dm['z']
     3
 
+On Python 2, you can also abbreviate ``with transaction.manager:`` as ``with
+transaction:``.  This does not work on Python 3 (see see
+http://bugs.python.org/issue12022).
+
 Retries
 -------
 
@@ -68,7 +50,7 @@
 
     for i in range(3):
         try:
-           with transaction:
+           with transaction.manager:
                ... some something ...
         except SomeTransientException:
            contine
@@ -82,7 +64,7 @@
 
 
     >>> ntry = 0
-    >>> with transaction:
+    >>> with transaction.manager:
     ...      dm['ntry'] = 0
 
     >>> import transaction.interfaces
@@ -92,7 +74,7 @@
     >>> for attempt in transaction.manager.attempts():
     ...     with attempt as t:
     ...         t.note('test')
-    ...         print dm['ntry'], ntry
+    ...         print("%s %s" % (dm['ntry'], ntry))
     ...         ntry += 1
     ...         dm['ntry'] = ntry
     ...         if ntry % 3:
@@ -143,7 +125,7 @@
     >>> for attempt in transaction.attempts():
     ...     with attempt as t:
     ...         t.note('test')
-    ...         print dm['ntry'], ntry
+    ...         print("%s %s" % (dm['ntry'], ntry))
     ...         ntry += 1
     ...         dm['ntry'] = ntry
     ...         if ntry % 3:
@@ -165,11 +147,11 @@
 
     >>> ntry = 0
     >>> dm2 = DM()
-    >>> with transaction:
+    >>> with transaction.manager:
     ...     dm2['ntry'] = 0
     >>> for attempt in transaction.manager.attempts():
     ...     with attempt:
-    ...         print dm['ntry'], ntry
+    ...         print("%s %s" % (dm['ntry'], ntry))
     ...         ntry += 1
     ...         dm['ntry'] = ntry
     ...         dm2['ntry'] = ntry

Modified: transaction/branches/chrism-py3/transaction/tests/doom.txt
===================================================================
--- transaction/branches/chrism-py3/transaction/tests/doom.txt	2011-09-25 20:29:37 UTC (rev 122935)
+++ transaction/branches/chrism-py3/transaction/tests/doom.txt	2011-09-25 22:16:09 UTC (rev 122936)
@@ -77,13 +77,11 @@
 Attempting to commit a doomed transaction any number of times raises a
 DoomedTransaction:
 
-    >>> txn.commit() # doctest: +ELLIPSIS
+    >>> txn.commit() # doctest: +IGNORE_EXCEPTION_DETAIL
     Traceback (most recent call last):
-        ...
     DoomedTransaction
-    >>> txn.commit() # doctest: +ELLIPSIS
+    >>> txn.commit() # doctest: +IGNORE_EXCEPTION_DETAIL
     Traceback (most recent call last):
-        ...
     DoomedTransaction
 
 But still leaves the data manager unchanged:

Modified: transaction/branches/chrism-py3/transaction/tests/savepoint.txt
===================================================================
--- transaction/branches/chrism-py3/transaction/tests/savepoint.txt	2011-09-25 20:29:37 UTC (rev 122935)
+++ transaction/branches/chrism-py3/transaction/tests/savepoint.txt	2011-09-25 22:16:09 UTC (rev 122936)
@@ -75,14 +75,14 @@
     ...             try:
     ...                 dm[name+'-balance'] += amount
     ...                 validate_account(name)
-    ...             except ValueError, error:
+    ...             except ValueError as error:
     ...                 entry_savepoint.rollback()
-    ...                 print 'Error', str(error)
+    ...                 print("%s %s" % ('Error', str(error)))
     ...             else:
-    ...                 print 'Updated', name
-    ...     except Exception, error:
+    ...                 print("%s %s" % ('Updated', name))
+    ...     except Exception as error:
     ...         savepoint.rollback()
-    ...         print 'Unexpected exception', error
+    ...         print("%s %s" % ('Unexpected exception', error))
 
 Now let's try applying some entries:
 
@@ -184,12 +184,12 @@
     >>> dm['bob-balance']
     100.0
 
-    >>> savepoint2.rollback()
+    >>> savepoint2.rollback() #doctest: +IGNORE_EXCEPTION_DETAIL
     Traceback (most recent call last):
     ...
     InvalidSavepointRollbackError
 
-    >>> savepoint1.rollback()
+    >>> savepoint1.rollback() #doctest: +IGNORE_EXCEPTION_DETAIL
     Traceback (most recent call last):
     ...
     InvalidSavepointRollbackError
@@ -207,7 +207,7 @@
     >>> dm_no_sp['name'] = 'bob'
     >>> transaction.commit()
     >>> dm_no_sp['name'] = 'sally'
-    >>> savepoint = transaction.savepoint()
+    >>> savepoint = transaction.savepoint() #doctest +IGNORE_EXCEPTION_DETAIL
     Traceback (most recent call last):
     ...
     TypeError: ('Savepoints unsupported', {'name': 'bob'})
@@ -228,8 +228,8 @@
 
     >>> dm_no_sp['name'] = 'sam'
     >>> savepoint = transaction.savepoint(1)
-    >>> savepoint.rollback()
-    Traceback (most recent call last):
+    >>> savepoint.rollback() #doctest +IGNORE_EXCEPTION_DETAIL
+    Traceback (most recent call last): 
     ...
     TypeError: ('Savepoints unsupported', {'name': 'sam'})
 
@@ -245,7 +245,7 @@
 In the previous example, we got an error when we tried to rollback the
 savepoint.  If we try to commit the transaction, the commit will fail:
 
-    >>> transaction.commit() # doctest: +ELLIPSIS
+    >>> transaction.commit() #doctest: +IGNORE_EXCEPTION_DETAIL
     Traceback (most recent call last):
     ...
     TransactionFailedError: An operation previously failed, with traceback:
@@ -262,12 +262,12 @@
 
     >>> dm_no_sp['name'] = 'sally'
     >>> dm['name'] = 'sally'
-    >>> savepoint = transaction.savepoint()
+    >>> savepoint = transaction.savepoint() # doctest +IGNORE_EXCEPTION_DETAIL
     Traceback (most recent call last):
     ...
     TypeError: ('Savepoints unsupported', {'name': 'sue'})
 
-    >>> transaction.commit() # doctest: +ELLIPSIS
+    >>> transaction.commit() # doctest: +IGNORE_EXCEPTION_DETAIL
     Traceback (most recent call last):
     ...
     TransactionFailedError: An operation previously failed, with traceback:

Modified: transaction/branches/chrism-py3/transaction/tests/savepointsample.py
===================================================================
--- transaction/branches/chrism-py3/transaction/tests/savepointsample.py	2011-09-25 20:29:37 UTC (rev 122935)
+++ transaction/branches/chrism-py3/transaction/tests/savepointsample.py	2011-09-25 22:16:09 UTC (rev 122936)
@@ -19,11 +19,11 @@
 See savepoint.txt in the transaction package.
 """
 
-import UserDict
 from zope import interface
 import transaction.interfaces
 
-class SampleDataManager(UserDict.DictMixin):
+ at interface.implementer(transaction.interfaces.IDataManager)
+class SampleDataManager(object):
     """Sample implementation of data manager that doesn't support savepoints
 
     This data manager stores named simple values, like strings and numbers.
@@ -71,6 +71,17 @@
     def keys(self):
         return self.uncommitted.keys()
 
+    __iter__ = keys
+
+    def __contains__(self, k):
+        return k in self.uncommitted
+
+    def __len__(self):
+        return len(self.keys())
+
+    def __repr__(self):
+        return repr(self.uncommitted)
+
     #
     #######################################################################
 
@@ -151,6 +162,7 @@
     #
     #######################################################################
 
+ at interface.implementer(transaction.interfaces.ISavepointDataManager)
 class SampleSavepointDataManager(SampleDataManager):
     """Sample implementation of a savepoint-supporting data manager
 
@@ -172,6 +184,7 @@
         # savepoint was done again.  IOW, copy() is necessary.
         self.uncommitted = savepoint.data.copy()
 
+ at interface.implementer(transaction.interfaces.IDataManagerSavepoint)
 class SampleSavepoint:
 
     interface.implements(transaction.interfaces.IDataManagerSavepoint)

Modified: transaction/branches/chrism-py3/transaction/tests/test_transaction.py
===================================================================
--- transaction/branches/chrism-py3/transaction/tests/test_transaction.py	2011-09-25 20:29:37 UTC (rev 122935)
+++ transaction/branches/chrism-py3/transaction/tests/test_transaction.py	2011-09-25 22:16:09 UTC (rev 122936)
@@ -307,7 +307,7 @@
 
     def check(self, method):
         if self.tracing:
-            print '%s calling method %s'%(str(self.tracing),method)
+            print('%s calling method %s'%(str(self.tracing),method))
 
         if method in self.errors:
             raise TestTxnException("error %s" % method)
@@ -402,13 +402,14 @@
 
     Now register the hook with a transaction.
 
+      >>> from transaction.compat import func_name
       >>> import transaction
       >>> t = transaction.begin()
       >>> t.addBeforeCommitHook(hook, '1')
 
     We can see that the hook is indeed registered.
 
-      >>> [(hook.func_name, args, kws)
+      >>> [(func_name(hook), args, kws)
       ...  for hook, args, kws in t.getBeforeCommitHooks()]
       [('hook', ('1',), {})]
 
@@ -470,7 +471,7 @@
       >>> t.join(FailingDataManager())
 
       >>> t.addBeforeCommitHook(hook, '2')
-      >>> t.commit()
+      >>> t.commit() #doctest +IGNORE_EXCEPTION_DETAIL
       Traceback (most recent call last):
       ...
       CommitFailure
@@ -486,7 +487,7 @@
 
     They are returned in the same order by getBeforeCommitHooks.
 
-      >>> [(hook.func_name, args, kws)     #doctest: +NORMALIZE_WHITESPACE
+      >>> [(func_name(hook), args, kws)  #doctest: +NORMALIZE_WHITESPACE
       ...  for hook, args, kws in t.getBeforeCommitHooks()]
       [('hook', ('4',), {'kw1': '4.1'}),
        ('hook', ('5',), {'kw2': '5.2'})]
@@ -539,13 +540,14 @@
 
     Now register the hook with a transaction.
 
+      >>> from transaction.compat import func_name
       >>> import transaction
       >>> t = transaction.begin()
       >>> t.addAfterCommitHook(hook, '1')
 
     We can see that the hook is indeed registered.
 
-      >>> [(hook.func_name, args, kws)
+      >>> [(func_name(hook), args, kws)
       ...  for hook, args, kws in t.getAfterCommitHooks()]
       [('hook', ('1',), {})]
 
@@ -607,7 +609,7 @@
       >>> t.join(FailingDataManager())
 
       >>> t.addAfterCommitHook(hook, '2')
-      >>> t.commit()
+      >>> t.commit() #doctest +IGNORE_EXCEPTION_DETAIL
       Traceback (most recent call last):
       ...
       CommitFailure
@@ -623,7 +625,7 @@
 
     They are returned in the same order by getAfterCommitHooks.
 
-      >>> [(hook.func_name, args, kws)     #doctest: +NORMALIZE_WHITESPACE
+      >>> [(func_name(hook), args, kws)     #doctest: +NORMALIZE_WHITESPACE
       ...  for hook, args, kws in t.getAfterCommitHooks()]
       [('hook', ('4',), {'kw1': '4.1'}),
        ('hook', ('5',), {'kw2': '5.2'})]
@@ -711,18 +713,18 @@
 
     >>> import transaction.tests.savepointsample
     >>> dm = transaction.tests.savepointsample.SampleSavepointDataManager()
-    >>> dm.keys()
+    >>> list(dm.keys())
     []
 
     >>> class Sync:
     ...      def __init__(self, label):
     ...          self.label = label
     ...      def beforeCompletion(self, t):
-    ...          print self.label, 'before'
+    ...          print('%s %s' % (self.label, 'before'))
     ...      def afterCompletion(self, t):
-    ...          print self.label, 'after'
+    ...          print('%s %s' % (self.label, 'after'))
     ...      def newTransaction(self, t):
-    ...          print self.label, 'new'
+    ...          print('%s %s' % (self.label, 'new'))
     >>> sync = Sync(1)
 
     >>> import threading
@@ -742,11 +744,11 @@
     ... def second():
     ...     transaction.abort() # should do nothing.
 
-    >>> dm.keys()
+    >>> list(dm.keys())
     ['a']
 
     >>> dm = transaction.tests.savepointsample.SampleSavepointDataManager()
-    >>> dm.keys()
+    >>> list(dm.keys())
     []
 
     >>> @run_in_thread
@@ -754,7 +756,7 @@
     ...     dm['a'] = 1
 
     >>> transaction.abort() # should do nothing
-    >>> dm.keys()
+    >>> list(dm.keys())
     ['a']
 
     """



More information about the checkins mailing list