diff --git a/CHANGES.rst b/CHANGES.rst
index 6c2743a..56dfef0 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -2,10 +2,15 @@
  CHANGES
 =========
 
-4.1.1 (unreleased)
+5.0.0 (unreleased)
 ==================
 
-- Nothing changed yet.
+- Add support for transaction managers operating in explicit mode.
+  Schema managers were previously required not to commit transactions
+  in their ``evolve`` or ``install`` methods, but a loophole was open
+  to allow them to commit "if there were no subsequent operations".
+  That loophole is now closed, at least in explicit mode. See `issue 8
+  <https://github.com/zopefoundation/zope.generations/issues/8>`_.
 
 
 4.1.0 (2018-10-23)
@@ -45,7 +50,7 @@
 3.7.0 (2010-09-18)
 ==================
 
-- Initial release extracted from `zope.app.generations`.
+- Initial release extracted from ``zope.app.generations``.
 
 - Generations key (stored in database root) has been changed from
   ``zope.app.generations`` to ``zope.generations``.  Migration is done when
diff --git a/setup.py b/setup.py
index b3d0744..d979c26 100644
--- a/setup.py
+++ b/setup.py
@@ -34,7 +34,7 @@ TESTS_REQUIRE = [
 ]
 
 setup(name='zope.generations',
-      version='4.1.1.dev0',
+      version='5.0.0.dev0',
       author='Zope Corporation and Contributors',
       author_email='zope-dev@zope.org',
       description='Zope application schema generations',
diff --git a/src/zope/generations/README.rst b/src/zope/generations/README.rst
index 1617084..b12c88d 100644
--- a/src/zope/generations/README.rst
+++ b/src/zope/generations/README.rst
@@ -18,10 +18,11 @@ connection:
     ...     from cgi import escape
     >>> from pprint import pprint
     >>> from zope.interface import implementer
-
+    >>> import transaction
     >>> from ZODB.MappingStorage import DB
     >>> db = DB()
     >>> conn = db.open()
+    >>> tx = transaction.begin()
     >>> root = conn.root()
 
 Imagine that our application is an oracle: you can teach it to react to
@@ -31,7 +32,7 @@ phrases.  Let's keep it simple and store the data in a dict:
     ...                    'Meaning of life?': '42',
     ...                    'four < ?': 'four < five'}
     >>> import transaction
-    >>> transaction.commit()
+    >>> tx.commit()
 
 
 ===============
@@ -75,8 +76,10 @@ that our current schema number is 0.  When we update the schema, Zope3 will
 have an idea of what the starting point was.  Here, see?
 
     >>> from zope.generations.generations import generations_key
+    >>> tx = transaction.begin()
     >>> root[generations_key]['some.app']
     0
+    >>> tx.abort()
 
 In real life you should never have to bother with this key directly,
 but you should be aware that it exists.
@@ -118,7 +121,6 @@ one):
     ...         else:
     ...             raise ValueError("Bummer")
     ...         root['answers'] = answers # ping persistence
-    ...         transaction.commit()
 
     >>> manager = MySchemaManager()
     >>> zope.component.provideUtility(manager, ISchemaManager, name='some.app')
@@ -162,6 +164,7 @@ is automatically sent again:
 
 Shazam!  The client is happy again!
 
+    >>> tx = transaction.begin()
     >>> pprint(root['answers'])
     {'Hello': 'Hi &amp; how do you do?',
      'Meaning of life?': '42',
@@ -174,6 +177,7 @@ to 1:
 
     >>> root[generations_key]['some.app']
     1
+    >>> tx.abort()
 
 We see that generations are working, so we decide to take the next step
 and evolve to generation 2.  Let's see how this can be done manually:
@@ -181,12 +185,14 @@ and evolve to generation 2.  Let's see how this can be done manually:
     >>> from zope.generations.generations import evolve
     >>> evolve(db)
 
+    >>> tx = transaction.begin()
     >>> pprint(root['answers'])
     {'Hello': 'Hi &amp; how do you do?',
      'Meaning of life?': '42',
      'four &lt; ?': 'four &lt; five'}
     >>> root[generations_key]['some.app']
     2
+    >>> tx.abort()
 
 Default behaviour of `evolve` upgrades to the latest generation provided by
 the SchemaManager. You can use the `how` argument to evolve() when you want
@@ -257,7 +263,6 @@ verify the result:
     ...         else:
     ...             raise ValueError("Bummer")
     ...         root['ordering'] = ordering # ping persistence
-    ...         transaction.commit()
 
     >>> @implementer(ISchemaManager)
     ... class DependentSchemaManager(object):
@@ -274,7 +279,6 @@ verify the result:
     ...         else:
     ...             raise ValueError("Bummer")
     ...         root['ordering'] = ordering # ping persistence
-    ...         transaction.commit()
 
     >>> manager1 = FoundationSchemaManager()
     >>> manager2 = DependentSchemaManager()
@@ -325,7 +329,6 @@ Let's define a new schema manager that includes installation:
     ...         root['answers'] = {'Hello': 'Hi &amp; how do you do?',
     ...                            'Meaning of life?': '42',
     ...                            'four &lt; ?': 'four &lt; five'}
-    ...         transaction.commit()
     ...
     ...     def evolve(self, context, generation):
     ...         root = context.connection.root()
@@ -340,24 +343,26 @@ Let's define a new schema manager that includes installation:
     ...         else:
     ...             raise ValueError("Bummer")
     ...         root['answers'] = answers # ping persistence
-    ...         transaction.commit()
 
     >>> manager = MySchemaManager()
     >>> zope.component.provideUtility(manager, ISchemaManager, name='some.app')
 
 Now, lets open a new database:
 
+    >>> conn.close()
     >>> db.close()
+
     >>> db = DB()
+    >>> tx = transaction.begin()
     >>> conn = db.open()
     >>> 'answers' in conn.root()
     False
-
+    >>> tx.abort()
 
     >>> event = DatabaseOpenedEventStub(db)
     >>> evolveMinimumSubscriber(event)
 
-    >>> conn.sync()
+    >>> tx = transaction.begin()
     >>> root = conn.root()
 
     >>> pprint(root['answers'])
@@ -366,12 +371,11 @@ Now, lets open a new database:
      'four &lt; ?': 'four &lt; five'}
     >>> root[generations_key]['some.app']
     2
+    >>> tx.abort()
+    >>> conn.close()
 
 The ZODB transaction log notes that our install script was executed
 
-    >>> [it.description for it in conn.db().storage.iterator()][-2]
+    >>> [it.description for it in db.storage.iterator()][-1]
     u'some.app: running install generation'
-
-(Minor note: it's not the last record because there are two commits:
-MySchemaManager performs one, and evolveMinimumSubscriber performs
-the second one.  MySchemaManager doesn't really need to commit.)
+    >>> db.close()
diff --git a/src/zope/generations/generations.py b/src/zope/generations/generations.py
index 3755c98..a6de8a3 100644
--- a/src/zope/generations/generations.py
+++ b/src/zope/generations/generations.py
@@ -55,13 +55,15 @@ class SchemaManager(object):
        Then we'll evolve the database from generation 1 to 3:
 
          >>> import transaction
+         >>> tx = transaction.begin()
          >>> manager.evolve(context, 2)
          >>> manager.evolve(context, 3)
-         >>> transaction.commit()
+         >>> tx.commit()
 
        The demo evolvers simply record their data in a root key:
 
          >>> from zope.generations.demo import key
+         >>> tx = transaction.begin()
          >>> conn = db.open()
          >>> conn.root()[key]
          (2, 3)
@@ -80,14 +82,16 @@ class SchemaManager(object):
        If a package provides an install script, then it will be called
        when the manager's intall method is called:
 
-         >>> conn.sync()
+         >>> tx.abort()
+         >>> tx = transaction.begin()
          >>> del conn.root()[key]
-         >>> transaction.commit()
+         >>> tx.commit()
+         >>> tx = transaction.begin()
          >>> conn.root().get(key)
 
          >>> manager.install(context)
-         >>> transaction.commit()
-         >>> conn.sync()
+         >>> tx.commit()
+         >>> tx = transaction.begin()
          >>> conn.root()[key]
          ('installed',)
 
@@ -107,6 +111,7 @@ class SchemaManager(object):
 
        We'd better clean up:
 
+         >>> tx.abort()
          >>> context.connection.close()
          >>> conn.close()
          >>> db.close()
@@ -165,7 +170,7 @@ class SchemaManager(object):
 
 
 class Context(object):
-    pass
+    connection = None
 
 
 def findManagers():
@@ -213,12 +218,6 @@ def evolve(db, how=EVOLVE):
       ...    print(loghandler)
       ...    loghandler.clear()
 
-    We also need to set up the component system, since we'll be
-    registering utilities:
-
-      >>> from zope.component.testing import setUp, tearDown
-      >>> setUp()
-
     Now, we'll create and register some handlers:
 
       >>> import zope.component
@@ -232,10 +231,9 @@ def evolve(db, how=EVOLVE):
 
       >>> from ZODB.MappingStorage import DB
       >>> db = DB(database_name='testdb')
+      >>> evolve(db)
       >>> conn = db.open()
       >>> root = conn.root()
-      >>> evolve(db)
-      >>> conn.sync()
       >>> root[generations_key]['app1']
       1
       >>> root[generations_key]['app2']
@@ -267,7 +265,6 @@ def evolve(db, how=EVOLVE):
 
     We'll see that the generation data has updated:
 
-      >>> conn.sync()
       >>> root[generations_key]['app1']
       2
       >>> root[generations_key]['app2']
@@ -308,7 +305,6 @@ def evolve(db, how=EVOLVE):
 
     The database will have been updated for previous generations:
 
-      >>> conn.sync()
       >>> root[generations_key]['app1']
       3
       >>> root.get('app1')
@@ -369,7 +365,6 @@ def evolve(db, how=EVOLVE):
     app1.  The database generation for app1 is still 3 because we
     didn't do any evolution:
 
-      >>> conn.sync()
       >>> root[generations_key]['app1']
       3
       >>> root.get('app1')
@@ -379,7 +374,6 @@ def evolve(db, how=EVOLVE):
     generation:
 
       >>> evolve(db, EVOLVEMINIMUM)
-      >>> conn.sync()
       >>> root[generations_key]['app1']
       4
       >>> root.get('app1')
@@ -415,6 +409,7 @@ def evolve(db, how=EVOLVE):
 
     We'd better clean up:
 
+      >>> from zope.testing.cleanup import tearDown
       >>> loghandler.uninstall()
       >>> conn.close()
       >>> db.close()
@@ -422,51 +417,50 @@ def evolve(db, how=EVOLVE):
 
     """
     db_name = db.database_name or 'main db'
-    logger.info('%s: evolving in mode %s' %
-                (db_name, how))
+    logger.info('%s: evolving in mode %s',
+                db_name, how)
     conn = db.open()
     try:
         context = Context()
         context.connection = conn
-        root = conn.root()
-        generations = root.get(generations_key)
-        if generations is None:
-            # backward compatibility with zope.app.generations
-            generations = root.get(old_generations_key)
-            if generations is not None:
-                # switch over to new generations_key
-                root[generations_key] = generations
-            else:
-                generations = root[generations_key] = PersistentDict()
-            transaction.commit()
+        with transaction.manager:
+            root = conn.root()
+            generations = root.get(generations_key)
+            if generations is None:
+                # backward compatibility with zope.app.generations
+                generations = root.get(old_generations_key)
+                if generations is not None:
+                    # switch over to new generations_key
+                    root[generations_key] = generations
+                else:
+                    generations = root[generations_key] = PersistentDict()
 
         for key, manager in sorted(findManagers()):
-            generation = generations.get(key)
-
-            if generation == manager.generation:
-                logger.debug('%s/%s: up-to-date at generation %s' %
-                             (db_name, key, generation))
-                continue
-
-            if generation is None:
-                # This is a new database, so no old data
-
-                if IInstallableSchemaManager.providedBy(manager):
-                    try:
-                        transaction.get().note('%s: running install generation'
-                                               % key)
-                        logger.info("%s/%s: running install generation",
-                                    db_name, key)
-                        manager.install(context)
-                    except:
-                        transaction.abort()
-                        logger.exception("%s/%s: failed to run install",
-                                         db_name, key)
-                        raise
-
-                generations[key] = manager.generation
-                transaction.commit()
-                continue
+            with transaction.manager as tx:
+                generation = generations.get(key)
+
+                if generation == manager.generation:
+                    logger.debug('%s/%s: up-to-date at generation %s',
+                                 db_name, key, generation)
+                    continue
+
+                if generation is None:
+                    # This is a new database, so no old data
+
+                    if IInstallableSchemaManager.providedBy(manager):
+                        try:
+                            tx.note('%s: running install generation'
+                                    % key)
+                            logger.info("%s/%s: running install generation",
+                                        db_name, key)
+                            manager.install(context)
+                        except:
+                            logger.exception("%s/%s: failed to run install",
+                                             db_name, key)
+                            raise
+
+                    generations[key] = manager.generation
+                    continue
 
             if generation > manager.generation:
                 logger.error('%s/%s: current generation too high (%d > %d)',
@@ -498,17 +492,18 @@ def evolve(db, how=EVOLVE):
 
             while generation < target:
                 generation += 1
+                tx = transaction.begin()
                 try:
-                    transaction.begin().note('%s: evolving to generation %d'
-                                             % (key, generation))
+                    tx.note('%s: evolving to generation %d'
+                            % (key, generation))
                     logger.debug('%s/%s: evolving to generation %d',
                                  db_name, key, generation)
                     manager.evolve(context, generation)
                     generations[key] = generation
-                    transaction.commit()
+                    tx.commit()
                 except:
                     # An unguarded handler is intended here
-                    transaction.abort()
+                    tx.abort()
                     logger.exception(
                         "%s/%s: failed to evolve to generation %d",
                         db_name, key, generation)
diff --git a/src/zope/generations/interfaces.py b/src/zope/generations/interfaces.py
index 88294e8..3d7cd63 100644
--- a/src/zope/generations/interfaces.py
+++ b/src/zope/generations/interfaces.py
@@ -43,22 +43,28 @@ class ISchemaManager(zope.interface.Interface):
     def evolve(context, generation):
         """Evolve a database to the given schema generation.
 
-        The database should be assumed to be at the schema
-        generation one less than the given `generation`
-        argument. In other words, the `evolve` method is only
-        required to make one evolutionary step.
-
-        The `context` argument has a connection attribute,
-        providing a database connection to be used to change
-        the database.  A `context` argument is passed rather than
-        a connection to make it possible to provide additional
-        information later, if it becomes necessary.
-
-        This method should *not* commit a transaction.  The
-        transaction will be committed by the caller if there is no
-        error.  It is acceptable to commit a transaction if there are no
-        subsequent operations.  The method may create savepoints.
+        The database should be assumed to be at the schema generation
+        one less than the given `generation` argument. In other words,
+        the `evolve` method is only required to make one evolutionary
+        step.
 
+        The `context` argument has a connection attribute, providing a
+        database connection to be used to change the database. A
+        `context` argument is passed rather than a connection to make
+        it possible to provide additional information later, if it
+        becomes necessary.
+
+        This method should *not* commit a transaction. The transaction
+        will be committed by the caller if there is no error. The
+        method may create savepoints.
+
+        .. versionchanged:: 5.0
+
+            Previously this documentation contained a provision for committing
+            the transaction "if there are no subsequent operations." That
+            was unclear and incompatible with explicit transaction managers.
+
+            Now, this method must *never* commit the transaction.
         """
 
     def getInfo(generation):
@@ -76,12 +82,18 @@ class IInstallableSchemaManager(ISchemaManager):
         """Perform any initial installation tasks
 
         The application has never had the application installed
-        before.  The schema manager should bring the database to the
+        before. The schema manager should bring the database to the
         current generation.
 
-        This method should *not* commit a transaction.  The
-        transaction will be committed by the caller if there is no
-        error.  It is acceptable to commit a transaction if there are no
-        subsequent operations.  The method may create savepoints.
+        This method should *not* commit a transaction. The transaction
+        will be committed by the caller if there is no error. The
+        method may create savepoints.
+
+        .. versionchanged:: 5.0
+
+            Previously this documentation contained a provision for committing
+            the transaction "if there are no subsequent operations." That
+            was unclear and incompatible with explicit transaction managers.
 
+            Now, this method must *never* commit the transaction.
         """
diff --git a/src/zope/generations/utility.py b/src/zope/generations/utility.py
index 68169e2..f860342 100644
--- a/src/zope/generations/utility.py
+++ b/src/zope/generations/utility.py
@@ -151,17 +151,19 @@ def getRootFolder(context):
 
     >>> from ZODB.MappingStorage import DB
     >>> from zope.generations.generations import Context
+    >>> import transaction
     >>> db = DB()
     >>> context = Context()
+    >>> tx = transaction.begin()
     >>> context.connection = db.open()
     >>> root = context.connection.root()
 
     Add a root folder:
 
     >>> from zope.site.folder import rootFolder
-    >>> import transaction
     >>> root[ROOT_NAME] = rootFolder()
-    >>> transaction.commit()
+    >>> tx.commit()
+    >>> tx = transaction.begin()
 
     Now we can get the root folder using the function:
 
@@ -170,6 +172,7 @@ def getRootFolder(context):
 
     We'd better clean up:
 
+    >>> tx.abort()
     >>> context.connection.close()
     >>> db.close()
 

`evolve` not fully compatible with an explicit TransactionManager
When actually executing user-supplied evolutions, `evolve` is careful to call `transaction.begin()` before executing any actions or committing/aborting.

However, in the earlier stages, there are a few places it calls `commit()` without calling `begin()`. Indeed, it mutates persistent objects of the connection without calling `begin`:
```python
        generations = root.get(generations_key)
        if generations is None:
            # backward compatibility with zope.app.generations
            generations = root.get(old_generations_key)
            if generations is not None:
                # switch over to new generations_key
                root[generations_key] = generations
            else:
                generations = root[generations_key] = PersistentDict()
            transaction.commit()
```

If the transaction manager is explicit, this can lead to `NoTransaction` errors:
```python traceback
    notify(DatabaseOpenedWithRoot(self.db))
  File "//eggs/zope.event-4.4-py2.7.egg/zope/event/__init__.py", line 32, in notify
    subscriber(event)
  File "//eggs/zope.component-4.5-py2.7.egg/zope/component/event.py", line 27, in dispatch
    component_subscribers(event, None)
  File "//eggs/zope.component-4.5-py2.7.egg/zope/component/_api.py", line 124, in subscribers
    return sitemanager.subscribers(objects, interface)
  File "//eggs/zope.interface-4.6.0-py2.7.egg/zope/interface/registry.py", line 442, in subscribers
    return self.adapters.subscribers(objects, provided)
  File "//eggs/zope.interface-4.6.0-py2.7.egg/zope/interface/adapter.py", line 607, in subscribers
    subscription(*objects)
  File "//eggs/zope.generations-4.1.0-py2.7.egg/zope/generations/generations.py", line 533, in evolveMinimumSubscriber
    evolve(event.database, EVOLVEMINIMUM)
  File "//eggs/zope.generations-4.1.0-py2.7.egg/zope/generations/generations.py", line 440, in evolve
    generations = root[generations_key] = PersistentDict()
  File "//eggs/persistent-4.5.0-py2.7-macosx-10.14-x86_64.egg/persistent/mapping.py", line 64, in __setitem__
    self._p_changed = 1
...
  File "//eggs/ZODB-5.5.1-py2.7.egg/ZODB/Connection.py", line 830, in register
    self._register(obj)
  File "//eggs/ZODB-5.5.1-py2.7.egg/ZODB/Connection.py", line 840, in _register
    self.transaction_manager.get().join(self)
  File "//eggs/transaction-2.4.0-py2.7.egg/transaction/_manager.py", line 87, in get
    raise NoTransaction()
NoTransaction
```

It seems like it should be easy to add the missing `begin()` calls.
diff --git a/src/zope/generations/tests/test_generations.py b/src/zope/generations/tests/test_generations.py
index d1ff525..7c984d1 100644
--- a/src/zope/generations/tests/test_generations.py
+++ b/src/zope/generations/tests/test_generations.py
@@ -17,7 +17,7 @@ import doctest
 import re
 import unittest
 
-import zope.component.testing
+from zope.testing import cleanup
 from zope.testing import renormalizing
 
 
@@ -55,7 +55,7 @@ class TestSchemaManager(unittest.TestCase):
             self._makeOne(generation=1)
 
 
-class TestEvolve(zope.component.testing.PlacelessSetup,
+class TestEvolve(cleanup.CleanUp,
                  unittest.TestCase):
 
     def test_error_on_install_propagates(self):
@@ -123,6 +123,18 @@ class TestEvolve(zope.component.testing.PlacelessSetup,
         evolve(db, EVOLVEMINIMUM)
         self.assertEqual(manager.evolved, (2,))
 
+class TestEvolveExplicit(TestEvolve):
+
+    def setUp(self):
+        super(TestEvolveExplicit, self).setUp()
+        import transaction
+        self.txm = transaction.manager
+        self.txm_explicit = self.txm.explicit
+        self.txm.explicit = True
+
+    def tearDown(self):
+        self.txm.explicit = self.txm_explicit
+
 
 class TestSubscribers(unittest.TestCase):
 
@@ -184,30 +196,59 @@ checker = renormalizing.RENormalizing([
 ])
 
 
-def tearDownREADME(test):
-    zope.component.testing.tearDown(test)
-    test.globs['db'].close()
-
-
 def test_suite():
     suite = unittest.defaultTestLoader.loadTestsFromName(__name__)
     flags = \
         doctest.NORMALIZE_WHITESPACE | \
         doctest.ELLIPSIS | \
         doctest.IGNORE_EXCEPTION_DETAIL
-    docs = unittest.TestSuite((
-        doctest.DocFileSuite(
-            'README.rst',
-            setUp=zope.component.testing.setUp,
-            tearDown=tearDownREADME,
-            package='zope.generations',
-            checker=checker
+
+    def setUpGeneric(test):
+        cleanup.setUp()
+
+    def tearDownGeneric(test):
+        if 'db' in test.globs:
+            test.globs['db'].close()
+        cleanup.tearDown()
+
+    def setUpExplicit(test):
+        setUpGeneric(test)
+        import transaction
+        manager = test.globs['_txm'] = transaction.manager
+        test.globs['_was_explicit'] = manager.explicit
+        manager.explicit = True
+
+    def tearDownExplicit(test):
+        test.globs['_txm'] = test.globs['_was_explicit']
+        tearDownGeneric(test)
+
+    doc_tests = []
+
+    for setUp, tearDown in (
+            (setUpGeneric, tearDownGeneric),
+            (setUpExplicit, tearDownExplicit),
+    ):
+        doc_tests.extend([
+            doctest.DocFileSuite(
+                'README.rst',
+                setUp=setUp,
+                tearDown=tearDown,
+                package='zope.generations',
+                checker=checker
+            ),
+            doctest.DocTestSuite(
+                'zope.generations.generations',
+                checker=checker,
+                optionflags=flags,
+                setUp=setUp,
+                tearDown=tearDown,
             ),
-        doctest.DocTestSuite(
-            'zope.generations.generations',
-            checker=checker, optionflags=flags),
-        doctest.DocTestSuite(
-            'zope.generations.utility'),
-        ))
-    suite.addTest(docs)
+            doctest.DocTestSuite(
+                'zope.generations.utility',
+                setUp=setUp,
+                tearDown=tearDown,
+            ),
+        ])
+
+    suite.addTest(unittest.TestSuite(doc_tests))
     return suite

pip install -e .[test]
parse_log_pytest
requirements.txt
pytest
apt-get update apt-get install -y gcc
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
BTrees==6.1
cffi==1.17.1
exceptiongroup==1.2.2
iniconfig==2.1.0
multipart==1.2.1
packaging==24.2
persistent==6.1.1
pluggy==1.5.0
pycparser==2.22
pytest==8.3.5
python-gettext==5.0
pytz==2025.2
tomli==2.2.1
transaction==5.0
zc.lockfile==3.0.post1
ZConfig==4.2
ZODB==6.0
zodbpickle==4.2
zope.annotation==5.1
zope.browser==3.1
zope.cachedescriptors==5.1
zope.component==6.0
zope.configuration==6.0
zope.container==6.1
zope.contenttype==5.2
zope.deferredimport==5.0
zope.deprecation==5.1
zope.dottedname==6.0
zope.event==5.0
zope.exceptions==5.2
zope.filerepresentation==6.1
-e git+https://github.com/zopefoundation/zope.generations.git@63801e6798ec129c7cf1ae7e59c23291b071ec63#egg=zope.generations
zope.hookable==7.0
zope.i18n==5.2
zope.i18nmessageid==7.0
zope.interface==7.2
zope.lifecycleevent==5.1
zope.location==5.1
zope.processlifetime==3.1
zope.proxy==6.1
zope.publisher==7.3
zope.schema==7.0.1
zope.security==7.3
zope.site==5.1
zope.size==5.1
zope.testing==5.1
zope.testrunner==7.2
zope.traversing==5.1

name: zope.generations
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - btrees==6.1
      - cffi==1.17.1
      - exceptiongroup==1.2.2
      - iniconfig==2.1.0
      - multipart==1.2.1
      - packaging==24.2
      - persistent==6.1.1
      - pluggy==1.5.0
      - pycparser==2.22
      - pytest==8.3.5
      - python-gettext==5.0
      - pytz==2025.2
      - tomli==2.2.1
      - transaction==5.0
      - zc-lockfile==3.0.post1
      - zconfig==4.2
      - zodb==6.0
      - zodbpickle==4.2
      - zope-annotation==5.1
      - zope-browser==3.1
      - zope-cachedescriptors==5.1
      - zope-component==6.0
      - zope-configuration==6.0
      - zope-container==6.1
      - zope-contenttype==5.2
      - zope-deferredimport==5.0
      - zope-deprecation==5.1
      - zope-dottedname==6.0
      - zope-event==5.0
      - zope-exceptions==5.2
      - zope-filerepresentation==6.1
      - zope-hookable==7.0
      - zope-i18n==5.2
      - zope-i18nmessageid==7.0
      - zope-interface==7.2
      - zope-lifecycleevent==5.1
      - zope-location==5.1
      - zope-processlifetime==3.1
      - zope-proxy==6.1
      - zope-publisher==7.3
      - zope-schema==7.0.1
      - zope-security==7.3
      - zope-site==5.1
      - zope-size==5.1
      - zope-testing==5.1
      - zope-testrunner==7.2
      - zope-traversing==5.1
prefix: /opt/conda/envs/zope.generations

src/zope/generations/tests/test_generations.py::TestEvolveExplicit::test_error_on_install_propagates src/zope/generations/tests/test_generations.py::TestEvolveExplicit::test_evolve_min_twice
src/zope/generations/tests/test_generations.py::TestSchemaManager::test_ctor_invalid_generation src/zope/generations/tests/test_generations.py::TestSchemaManager::test_ctor_invalid_generation_wo_package src/zope/generations/tests/test_generations.py::TestSchemaManager::test_ctor_invalid_min_generation src/zope/generations/tests/test_generations.py::TestEvolve::test_error_on_install_propagates src/zope/generations/tests/test_generations.py::TestEvolve::test_evolve_min_twice src/zope/generations/tests/test_generations.py::TestSubscribers::test_evolveNotSubscriber src/zope/generations/tests/test_generations.py::TestSubscribers::test_evolveSubscriber src/zope/generations/tests/test_generations.py::test_suite
src/zope/generations/interfaces.py setup.py CHANGES.rst src/zope/generations/generations.py src/zope/generations/utility.py src/zope/generations/README.rst
src/zope/generations/interfaces.py setup.py CHANGES.rst src/zope/generations/generations.py src/zope/generations/utility.py src/zope/generations/README.rst