[Python-modules-commits] [python-axiom] 01/06: Import python-axiom_0.7.5.orig.tar.gz

Tristan Seligmann mithrandi at moszumanska.debian.org
Thu Feb 4 07:13:38 UTC 2016


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

mithrandi pushed a commit to branch master
in repository python-axiom.

commit 9fba2622ce4ed3c88d401dbd4aa56ab9444f5c63
Author: Tristan Seligmann <mithrandi at debian.org>
Date:   Thu Feb 4 09:07:26 2016 +0200

    Import python-axiom_0.7.5.orig.tar.gz
---
 Axiom.egg-info/PKG-INFO      |   2 +-
 PKG-INFO                     |   2 +-
 axiom/_version.py            |   2 +-
 axiom/attributes.py          |  32 +++++++++++-
 axiom/scripts/axiomatic.py   |  32 +++++++++---
 axiom/store.py               |   7 +++
 axiom/test/morenewapp.py     |  13 ++++-
 axiom/test/test_axiomatic.py | 113 +++++++++++++++++++++++++++++++++++++------
 axiom/test/test_query.py     |  85 ++++++++++++++++++--------------
 axiom/test/test_reference.py |  42 ++++++++++++----
 axiom/test/test_upgrading.py |  10 +++-
 11 files changed, 264 insertions(+), 76 deletions(-)

diff --git a/Axiom.egg-info/PKG-INFO b/Axiom.egg-info/PKG-INFO
index a9f70b7..4a02ba3 100644
--- a/Axiom.egg-info/PKG-INFO
+++ b/Axiom.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: Axiom
-Version: 0.7.4
+Version: 0.7.5
 Summary: An in-process object-relational database
 Home-page: https://github.com/twisted/axiom
 Author: Divmod, Inc.
diff --git a/PKG-INFO b/PKG-INFO
index a9f70b7..4a02ba3 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.1
 Name: Axiom
-Version: 0.7.4
+Version: 0.7.5
 Summary: An in-process object-relational database
 Home-page: https://github.com/twisted/axiom
 Author: Divmod, Inc.
diff --git a/axiom/_version.py b/axiom/_version.py
index ed9d4d8..ab55bb1 100644
--- a/axiom/_version.py
+++ b/axiom/_version.py
@@ -1 +1 @@
-__version__ = "0.7.4"
+__version__ = "0.7.5"
diff --git a/axiom/attributes.py b/axiom/attributes.py
index b2b6b34..3ebf7d6 100644
--- a/axiom/attributes.py
+++ b/axiom/attributes.py
@@ -10,7 +10,9 @@ hotfix.require('twisted', 'filepath_copyTo')
 from zope.interface import implements
 
 from twisted.python import filepath
+from twisted.python.deprecate import deprecated
 from twisted.python.components import registerAdapter
+from twisted.python.versions import Version
 
 from epsilon.extime import Time
 
@@ -915,11 +917,34 @@ class bytes(SQLAttribute):
             raise ConstraintError(self, "str or other byte buffer", pyval)
         return buffer(pyval)
 
+
     def outfilter(self, dbval, oself):
         if dbval is None:
             return None
         return str(dbval)
 
+
+    @deprecated(Version("Axiom", 0, 7, 5))
+    def like(self, *others):
+        return super(SQLAttribute, self).like(*others)
+
+
+    @deprecated(Version("Axiom", 0, 7, 5))
+    def notLike(self, *others):
+        return super(SQLAttribute, self).notLike(*others)
+
+
+    @deprecated(Version("Axiom", 0, 7, 5))
+    def startswith(self, other):
+        return super(SQLAttribute, self).startswith(other)
+
+
+    @deprecated(Version("Axiom", 0, 7, 5))
+    def endswith(self, other):
+        return super(SQLAttribute, self).endswith(other)
+
+
+
 class InvalidPathError(ValueError):
     """
     A path that could not be used with the database was attempted to be used
@@ -1123,7 +1148,12 @@ class reference(integer):
             assert self.whenDeleted is reference.NULLIFY, (
                 "not sure what to do if not...")
             return None
-        if rv.__legacy__:
+        # If the current cached value is a legacy item, we discard it in order
+        # to force another fetch from the database. However, if *this item* is
+        # also a legacy item, then the item referred to may have been created
+        # in an upgrader and not have been stored yet, so we shouldn't discard
+        # it.
+        if rv.__legacy__ and not oself.__legacy__:
             delattr(oself, self.underlying)
             return super(reference, self).__get__(oself, cls)
         return rv
diff --git a/axiom/scripts/axiomatic.py b/axiom/scripts/axiomatic.py
index 2eb9f1c..826ebe3 100644
--- a/axiom/scripts/axiomatic.py
+++ b/axiom/scripts/axiomatic.py
@@ -104,11 +104,23 @@ class Start(twistd.ServerOptions):
     def getArguments(self, store, args):
         run = store.dbdir.child("run")
         logs = run.child("logs")
-        if "--logfile" not in args and "-l" not in args and "--nodaemon" not in args and "-n" not in args:
+        handleLogfile = True
+        handlePidfile = True
+
+        for arg in args:
+            if arg.startswith("--logfile=") or arg in (
+                "-l", "--logfile", "-n", "--nodaemon"
+            ):
+                handleLogfile = False
+            elif arg.startswith("--pidfile=") or arg == "--pidfile":
+                handlePidfile = False
+
+        if handleLogfile:
             if not logs.exists():
                 logs.makedirs()
             args.extend(["--logfile", logs.child("axiomatic.log").path])
-        if not platform.isWindows() and "--pidfile" not in args:
+
+        if not platform.isWindows() and handlePidfile:
             args.extend(["--pidfile", run.child("axiomatic.pid").path])
         args.extend(["axiomatic-start", "--dbdir", store.dbdir.path])
         return args
@@ -121,11 +133,17 @@ class Start(twistd.ServerOptions):
             # If a reactor is being selected, it must be done before the store
             # is opened, since that may execute arbitrary application code
             # which may in turn install the default reactor.
-            if "--reactor" in args:
-                reactorIndex = args.index("--reactor")
-                shortName = args[reactorIndex + 1]
-                del args[reactorIndex:reactorIndex + 2]
-                self.opt_reactor(shortName)
+            for index, arg in enumerate(args):
+                if arg in ("--reactor", "-r"):
+                    shortName = args[index + 1]
+                    del args[index:index + 2]
+                    self.opt_reactor(shortName)
+                    break
+                elif arg.startswith("--reactor="):
+                    shortName = arg.split("=")[1]
+                    del args[index]
+                    self.opt_reactor(shortName)
+                    break
             sys.argv[1:] = self.getArguments(self.parent.getStore(), args)
             self.run()
 
diff --git a/axiom/store.py b/axiom/store.py
index f795e9d..76bcc9a 100644
--- a/axiom/store.py
+++ b/axiom/store.py
@@ -2099,6 +2099,13 @@ class Store(Empowered):
         if self.tablesCreatedThisTransaction is not None:
             self.tablesCreatedThisTransaction.append(tableClass)
 
+        # If the new type is a legacy type (not the current version), we need
+        # to queue it for upgrade to ensure that if we are in the middle of an
+        # upgrade, legacy items of this version get upgraded.
+        cls = _typeNameToMostRecentClass.get(tableClass.typeName)
+        if cls is not None and tableClass.schemaVersion != cls.schemaVersion:
+            self._upgradeManager.queueTypeUpgrade(tableClass)
+
         # We can pass () for extantIndexes here because since the table didn't
         # exist for tableClass, none of its indexes could have either.
         # Whatever checks _createIndexesFor will make would give the same
diff --git a/axiom/test/morenewapp.py b/axiom/test/morenewapp.py
index 6c4b17d..45cd846 100644
--- a/axiom/test/morenewapp.py
+++ b/axiom/test/morenewapp.py
@@ -4,7 +4,7 @@
 from axiom.item import Item
 from axiom.attributes import text, integer, reference, inmemory
 
-from axiom.upgrade import registerUpgrader
+from axiom.upgrade import registerUpgrader, registerAttributeCopyingUpgrader
 
 class ActivateHelper:
     activated = 0
@@ -13,7 +13,7 @@ class ActivateHelper:
 
 class Adventurer(ActivateHelper, Item):
     typeName = 'test_app_player'
-    schemaVersion = 2
+    schemaVersion = 3
 
     name = text()
     activated = inmemory()
@@ -65,6 +65,15 @@ registerUpgrader(sword2to3, 'test_app_sword', 2, 3)
 
 from axiom.item import declareLegacyItem
 
+
+declareLegacyItem(
+    typeName='test_app_player',
+    schemaVersion=2,
+    attributes=dict(
+        name=text(allowNone=True)))
+
+registerAttributeCopyingUpgrader(Adventurer, 2, 3)
+
 declareLegacyItem(typeName = 'test_app_sword',
                   schemaVersion = 2,
 
diff --git a/axiom/test/test_axiomatic.py b/axiom/test/test_axiomatic.py
index db528bc..075120f 100644
--- a/axiom/test/test_axiomatic.py
+++ b/axiom/test/test_axiomatic.py
@@ -117,6 +117,9 @@ class StartTests(TestCase):
             start.getArguments(store, []),
             logfileArg + pidfileArg + restArg)
         self.assertEqual(
+            start.getArguments(store, ["--logfile=foo"]),
+            ["--logfile=foo"] + pidfileArg + restArg)
+        self.assertEqual(
             start.getArguments(store, ["--logfile", "foo"]),
             ["--logfile", "foo"] + pidfileArg + restArg)
         self.assertEqual(
@@ -129,6 +132,9 @@ class StartTests(TestCase):
             start.getArguments(store, ["-n"]),
             ["-n"] + pidfileArg + restArg)
         self.assertEqual(
+            start.getArguments(store, ["--pidfile=foo"]),
+            ["--pidfile=foo"] + logfileArg + restArg)
+        self.assertEqual(
             start.getArguments(store, ["--pidfile", "foo"]),
             ["--pidfile", "foo"] + logfileArg + restArg)
 
@@ -256,21 +262,7 @@ class StartTests(TestCase):
         self.assertTrue(store.getItemByID(recorder.storeID).started)
 
 
-    def test_reactorSelection(self):
-        """
-        L{AxiomaticStart} optionally takes the name of a reactor and
-        installs it instead of the default reactor.
-        """
-        # Since this process is already hopelessly distant from the state in
-        # which I{axiomatic start} operates, it would make no sense to try a
-        # functional test of this behavior in this process.  Since the
-        # behavior being tested involves lots of subtle interactions between
-        # lots of different pieces of code (the reactor might get installed
-        # at the end of a ten-deep chain of imports going through as many
-        # different projects), it also makes no sense to try to make this a
-        # unit test.  So, start a child process and try to use the alternate
-        # reactor functionality there.
-
+    def _getAxiomaticScript(self):
         here = FilePath(__file__)
         # Try to find it relative to the source of this test.
         bin = here.parent().parent().parent().child("bin")
@@ -289,6 +281,25 @@ class StartTests(TestCase):
                 raise SkipTest(
                     "Could not find axiomatic script on path or at %s" % (
                         axiomatic.path,))
+        return axiomatic
+
+
+    def test_reactorSelection(self):
+        """
+        L{AxiomaticStart} optionally takes the name of a reactor in the form
+        --reactor [shortName] and installs it instead of the default reactor.
+        """
+        # Since this process is already hopelessly distant from the state in
+        # which I{axiomatic start} operates, it would make no sense to try a
+        # functional test of this behavior in this process.  Since the
+        # behavior being tested involves lots of subtle interactions between
+        # lots of different pieces of code (the reactor might get installed
+        # at the end of a ten-deep chain of imports going through as many
+        # different projects), it also makes no sense to try to make this a
+        # unit test.  So, start a child process and try to use the alternate
+        # reactor functionality there.
+
+        axiomatic = self._getAxiomaticScript()
 
         # Create a store for the child process to use and put an item in it.
         # This will force an import of the module that defines that item's
@@ -300,7 +311,7 @@ class StartTests(TestCase):
         SomeItem(store=store)
         store.close()
 
-        # Install select reactor because it available on all platforms, and
+        # Install select reactor because it is available on all platforms, and
         # it is still an error to try to install the select reactor even if
         # the already installed reactor was the select reactor.
         argv = [
@@ -317,6 +328,76 @@ class StartTests(TestCase):
         return complete
 
 
+    def test_reactorSelectionLongOptionStyle(self):
+        """
+        L{AxiomaticStart} optionally takes the name of a reactor in the form
+        --reactor=[shortName] and installs it instead of the default reactor.
+        """
+
+        axiomatic = self._getAxiomaticScript()
+
+        # Create a store for the child process to use and put an item in it.
+        # This will force an import of the module that defines that item's
+        # class when the child process starts.  The module imports the default
+        # reactor at the top-level, making this the worst-case for the reactor
+        # selection code.
+        storePath = self.mktemp()
+        store = Store(storePath)
+        SomeItem(store=store)
+        store.close()
+
+        # Install select reactor because it is available on all platforms, and
+        # it is still an error to try to install the select reactor even if
+        # the already installed reactor was the select reactor.
+        argv = [
+            sys.executable,
+            axiomatic, "-d", storePath,
+            "start", "--reactor=select", "-n"]
+        expected = [
+            "reactor class: twisted.internet.selectreactor.SelectReactor.",
+            "reactor class: <class 'twisted.internet.selectreactor.SelectReactor'>"]
+        proto, complete = AxiomaticStartProcessProtocol.protocolAndDeferred(expected)
+
+        environ = os.environ.copy()
+        reactor.spawnProcess(proto, sys.executable, argv, env=environ)
+        return complete
+
+
+    def test_reactorSelectionShortOptionStyle(self):
+        """
+        L{AxiomaticStart} optionally takes the name of a reactor in the form
+        -r [shortName] and installs it instead of the default reactor.
+        """
+
+        axiomatic = self._getAxiomaticScript()
+
+        # Create a store for the child process to use and put an item in it.
+        # This will force an import of the module that defines that item's
+        # class when the child process starts.  The module imports the default
+        # reactor at the top-level, making this the worst-case for the reactor
+        # selection code.
+        storePath = self.mktemp()
+        store = Store(storePath)
+        SomeItem(store=store)
+        store.close()
+
+        # Install select reactor because it is available on all platforms, and
+        # it is still an error to try to install the select reactor even if
+        # the already installed reactor was the select reactor.
+        argv = [
+            sys.executable,
+            axiomatic, "-d", storePath,
+            "start", "-r", "select", "-n"]
+        expected = [
+            "reactor class: twisted.internet.selectreactor.SelectReactor.",
+            "reactor class: <class 'twisted.internet.selectreactor.SelectReactor'>"]
+        proto, complete = AxiomaticStartProcessProtocol.protocolAndDeferred(expected)
+
+        environ = os.environ.copy()
+        reactor.spawnProcess(proto, sys.executable, argv, env=environ)
+        return complete
+
+
 
 class AxiomaticStartProcessProtocol(ProcessProtocol):
     """
diff --git a/axiom/test/test_query.py b/axiom/test/test_query.py
index 139a6ce..8048661 100644
--- a/axiom/test/test_query.py
+++ b/axiom/test/test_query.py
@@ -1218,8 +1218,8 @@ class WildcardQueries(QueryingTestCase):
             D.one.notLike('foobar%'),
             '(%s NOT LIKE (?))' % (D.one.getColumnName(self.store),),
             ['foobar%'])
-        self.assertEquals(self.query(D, D.one.like('d1.one')), [self.d1])
-        self.assertEquals(self.query(D, D.one.notLike('d%.one')), [])
+        self.assertEquals(self.query(D, D.four.like(u'd1.four')), [self.d1])
+        self.assertEquals(self.query(D, D.four.notLike(u'd%.four')), [])
 
     def testOneColumn(self):
         self.assertQuery(
@@ -1230,11 +1230,11 @@ class WildcardQueries(QueryingTestCase):
 
     def testOneColumnAndStrings(self):
         self.assertQuery(
-            D.one.like('%', D.id, '%one'),
-            '(%s LIKE (? || %s || ?))' % (D.one.getColumnName(self.store),
+            D.four.like(u'%', D.id, u'%four'),
+            '(%s LIKE (? || %s || ?))' % (D.four.getColumnName(self.store),
                                           D.id.getColumnName(self.store)),
-            ['%', '%one'])
-        q = self.query(D, D.one.like('%', D.id, '%one'))
+            [u'%', u'%four'])
+        q = self.query(D, D.four.like(u'%', D.id, u'%four'))
         e = [self.d1, self.d2, self.d3]
         self.assertEquals(sorted(q), sorted(e))
 
@@ -1251,27 +1251,17 @@ class WildcardQueries(QueryingTestCase):
 
     def testStartsEndsWith(self):
         self.assertQuery(
-            D.one.startswith('foo'),
-            '(%s LIKE (?))' % (D.one.getColumnName(self.store),),
+            D.four.startswith(u'foo'),
+            '(%s LIKE (?))' % (D.four.getColumnName(self.store),),
             ['foo%'])
         self.assertQuery(
-            D.one.endswith('foo'),
-            '(%s LIKE (?))' % (D.one.getColumnName(self.store),),
+            D.four.endswith(u'foo'),
+            '(%s LIKE (?))' % (D.four.getColumnName(self.store),),
             ['%foo'])
         self.assertEquals(
-            self.query(D, D.one.startswith('d1')), [self.d1])
+            self.query(D, D.four.startswith(u'd1')), [self.d1])
         self.assertEquals(
-            self.query(D, D.one.endswith('3.one')), [self.d3])
-
-
-    def testStartsEndsWithColumn(self):
-        self.assertQuery(
-            D.one.startswith(D.two),
-            '(%s LIKE (%s || ?))' % (D.one.getColumnName(self.store),
-                                     D.two.getColumnName(self.store)),
-            ['%'])
-        self.assertEquals(
-            self.query(D, D.one.startswith(D.two)), [])
+            self.query(D, D.four.endswith(u'3.four')), [self.d3])
 
 
     def testStartsEndsWithText(self):
@@ -1283,20 +1273,6 @@ class WildcardQueries(QueryingTestCase):
             [self.d2])
 
 
-    def testOtherTable(self):
-        self.assertQuery(
-            D.one.startswith(A.type),
-            '(%s LIKE (%s || ?))' % (D.one.getColumnName(self.store),
-                                     A.type.getColumnName(self.store)),
-            ['%'])
-
-        C(store=self.store, name=u'd1.')
-        C(store=self.store, name=u'2.one')
-        self.assertEquals(
-            self.query(D, D.one.startswith(C.name)), [self.d1])
-        self.assertEquals(
-            self.query(D, D.one.endswith(C.name)), [self.d2])
-
 
 class UniqueTest(TestCase):
 
@@ -1796,3 +1772,40 @@ class PlaceholderTestCase(TestCase):
         expectedSQL = "placeholder_0.oid, placeholder_0.[attr], placeholder_0.[characters], placeholder_0.[other]"
 
         self.assertEquals(query._queryTarget, expectedSQL)
+
+
+
+class BytesDeprecatedLikeTests(TestCase):
+    """
+    Deprecated tests for LIKE queries on L{axiom.attributes.bytes}.
+    """
+    def test_startsWith(self):
+        self.assertWarns(
+            DeprecationWarning,
+            'axiom.attributes.startswith was deprecated in Axiom 0.7.5',
+            __file__,
+            lambda: D.one.startswith('string'))
+
+
+    def test_endsWith(self):
+        self.assertWarns(
+            DeprecationWarning,
+            'axiom.attributes.endswith was deprecated in Axiom 0.7.5',
+            __file__,
+            lambda: D.one.endswith('string'))
+
+
+    def test_like(self):
+        self.assertWarns(
+            DeprecationWarning,
+            'axiom.attributes.like was deprecated in Axiom 0.7.5',
+            __file__,
+            lambda: D.one.like('string'))
+
+
+    def test_notLike(self):
+        self.assertWarns(
+            DeprecationWarning,
+            'axiom.attributes.notLike was deprecated in Axiom 0.7.5',
+            __file__,
+            lambda: D.one.notLike('string'))
diff --git a/axiom/test/test_reference.py b/axiom/test/test_reference.py
index 301ba14..89bd6ee 100644
--- a/axiom/test/test_reference.py
+++ b/axiom/test/test_reference.py
@@ -3,7 +3,7 @@ import gc
 from twisted.trial.unittest import TestCase
 
 from axiom.store import Store
-from axiom.upgrade import registerUpgrader
+from axiom.upgrade import registerUpgrader, registerAttributeCopyingUpgrader
 from axiom.item import Item, declareLegacyItem
 from axiom.attributes import integer, reference
 from axiom.errors import BrokenReference, DeletionDisallowed
@@ -187,18 +187,35 @@ class BadReferenceTestCase(TestCase):
         """
         store = Store()
         referent = SimpleReferent(store=store)
-        oldReferee = nonUpgradedItem(store=store)
+        oldReferee = nonUpgradedItem2(store=store)
         referent.ref = oldReferee
         # Manually run the upgrader on this specific legacy item. This is the
         # same as if the SimpleReferent item had been created in an upgrader
         # for UpgradedItem, except that we can keep a strong reference to
         # oldReferee to ensure it is not garbage collected (this would
         # otherwise happen nondeterministically on platforms like PyPy).
-        newReferee = item1to2(oldReferee)
+        newReferee = item2to3(oldReferee)
         self.assertIsInstance(newReferee, UpgradedItem)
         self.assertIdentical(referent.ref, newReferee)
 
 
+    def test_dummyItemReferenceInUpgrade(self):
+        """
+        Setting the value of a reference attribute to a legacy item during an
+        upgrade results in the same value being set on the upgraded item.
+        """
+        store = Store()
+        def tx():
+            oldReferent = nonUpgradedItem(store=store)
+            oldReferee = nonUpgradedItem(store=store)
+            newReferent = oldReferent.upgradeVersion(
+                UpgradedItem.typeName, 1, 2)
+            newReferee = oldReferee.upgradeVersion(
+                UpgradedItem.typeName, 1, 2, ref=newReferent)
+            self.assertIdentical(newReferee.ref, newReferent)
+        store.transact(tx)
+
+
     def test_dummyItemGetItemByID(self):
         """
         Instantiating a dummy item and then getting it by its storeID should
@@ -218,21 +235,28 @@ class UpgradedItem(Item):
     """
     A simple item which is the current version of L{nonUpgradedItem}.
     """
-    schemaVersion = 2
-    dummy = integer()
+    schemaVersion = 3
+    ref = reference()
 
 
 
 nonUpgradedItem = declareLegacyItem(
     UpgradedItem.typeName, 1,
-    dict(dummy=integer()))
+    dict(ref=reference()))
+
+
+
+nonUpgradedItem2 = declareLegacyItem(
+    UpgradedItem.typeName, 2,
+    dict(ref=reference()))
 
+registerAttributeCopyingUpgrader(UpgradedItem, 1, 2)
 
 
-def item1to2(old):
+def item2to3(old):
     """
     Upgrade an nonUpgradedItem to UpgradedItem
     """
-    return old.upgradeVersion(UpgradedItem.typeName, 1, 2, dummy=old.dummy)
+    return old.upgradeVersion(UpgradedItem.typeName, 2, 3, ref=old.ref)
 
-registerUpgrader(item1to2, UpgradedItem.typeName, 1, 2)
+registerUpgrader(item2to3, UpgradedItem.typeName, 2, 3)
diff --git a/axiom/test/test_upgrading.py b/axiom/test/test_upgrading.py
index 177afa8..2cb19aa 100644
--- a/axiom/test/test_upgrading.py
+++ b/axiom/test/test_upgrading.py
@@ -239,7 +239,14 @@ class SwordUpgradeTest(SchemaUpgradeTest):
         Verify that if an exception is raised in an upgrader, the exception
         will be logged.
         """
-        playerID, swordID = self._testTwoObjectUpgrade()
+        choose(oldapp)
+        s = self.openStore()
+        swordID = oldapp.Sword(
+            store=s,
+            name=u'flaming vorpal doom',
+            hurtfulness=7).storeID
+        self.closeStore()
+
         choose(brokenapp)
         s = self.openStore()
         self.startStoreService()
@@ -253,7 +260,6 @@ class SwordUpgradeTest(SchemaUpgradeTest):
 
             loggedErrors = self.flushLoggedErrors(brokenapp.UpgradersAreBrokenHere)
             self.assertEqual(len(loggedErrors), 1)
-            originalError = loggedErrors[0]
 
             oldType = item.declareLegacyItem(
                 oldapp.Sword.typeName,

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-axiom.git



More information about the Python-modules-commits mailing list