Commit 2657d6b5 authored by Éloi Rivard's avatar Éloi Rivard Committed by GitHub

Merge pull request #299 from azmeuk/doc

Some documentation love
parents 0e343a8c 5bbd987d
......@@ -4,7 +4,7 @@ develop-eggs
parts
.installed.cfg
build
docs/_build
doc/_build
__pycache__
*.pyc
*.so
......
......@@ -23,6 +23,7 @@ install:
script:
- if [[ $TRAVIS_PYTHON_VERSION != pypy* ]]; then bin/coverage run bin/coverage-test -v; fi
- if [[ $TRAVIS_PYTHON_VERSION == pypy* ]]; then bin/test -v; fi
- if [[ $TRAVIS_PYTHON_VERSION != pypy3* ]]; then pip install --upgrade --requirement doc/requirements.txt; fi
- if [[ $TRAVIS_PYTHON_VERSION != pypy3* ]]; then make -C doc html; fi
- if [[ $TRAVIS_PYTHON_VERSION != pypy* ]]; then pip install coveralls; fi # install early enough to get into the cache
after_success:
......
======================
For developers of ZODB
======================
================
Developers notes
================
Building
========
......
......@@ -13,13 +13,13 @@ ZODB, a Python object-oriented database
.. image:: https://travis-ci.org/zopefoundation/ZODB.svg?branch=master
:target: https://travis-ci.org/zopefoundation/ZODB
:alt: Build status
.. image:: https://coveralls.io/repos/github/zopefoundation/ZODB/badge.svg
:target: https://coveralls.io/github/zopefoundation/ZODB
:alt: Coverage status
.. image:: https://readthedocs.org/projects/zodb/badge/?version=latest
:target: https://zodb.readthedocs.io/en/latest/
.. image:: https://readthedocs.org/projects/zodb-docs/badge/?version=latest
:target: https://zodb-docs.readthedocs.io/en/latest/
:alt: Documentation status
ZODB provides an object-oriented database for Python that provides a
......@@ -39,7 +39,7 @@ above. It also runs on PyPy.
ZODB is an ACID Transactional database.
To learn more, visit: http://www.zodb.org
To learn more, visit: https://zodb-docs.readthedocs.io
The github repository is: at https://github.com/zopefoundation/zodb
......
......@@ -53,7 +53,9 @@ interpreter = py
eggs = Sphinx
[sphinx_egg:python2 or python34]
eggs = Sphinx < 2
eggs =
Sphinx < 2
pygments < 2.6
[sphinx]
recipe = zc.recipe.egg
......
.. include:: ../src/ZODB/ConflictResolution.rst
......@@ -2,7 +2,7 @@
ZODB documentation
==================
``zodbdocs`` is the source documentation for the website http://zodb.org. It
``zodbdocs`` is the source documentation for the website http://zodb-docs.readthedocs.io. It
contains all ZODB relevant documentation like "ZODB/ZEO Programming Guide",
some ZODB articles and links to the ZODB release notes.
......
.. include:: ../CHANGES.rst
.. include:: ../HISTORY.rst
......@@ -2,6 +2,10 @@
Collabortation Diagrams
=======================
.. caution::
This document hasn't been reviewed since 2005
and is likely out of date.
This file contains several collaboration diagrams for the ZODB.
Simple fetch, modify, commit
......
......@@ -14,19 +14,33 @@
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys
import os
import pkg_resources
# If your extensions are in another directory, add it here. If the directory
# is relative to the documentation root, use os.path.abspath to make it
# absolute, like shown here.
#sys.path.append(os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath(".."))
# General configuration
# ---------------------
rqmt = pkg_resources.require('ZODB')[0]
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['sphinx.ext.autodoc',
'j1m.sphinxautointerface',
'j1m.sphinxautozconfig']
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.doctest',
'sphinx.ext.intersphinx',
'sphinx.ext.viewcode',
'j1m.sphinxautointerface',
'j1m.sphinxautozconfig',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['.templates']
......@@ -41,15 +55,15 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'ZODB'
copyright = u'2009-2016, Zope Foundation'
project = 'ZODB'
copyright = '2009-2020, Zope Foundation'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '5.0'
version = '%s.%s' % tuple(map(int, rqmt.version.split('.')[:2]))
# The full version, including alpha/beta/rc tags.
#release = '3.10.3'
......@@ -89,6 +103,10 @@ pygments_style = 'sphinx'
# Options for HTML output
# -----------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'sphinx_rtd_theme'
# The style sheet to use for HTML and HTML Help pages. A file of that name
# must exist either in Sphinx' static/ path, or in one of the custom paths
# given in html_static_path.
......@@ -166,8 +184,8 @@ htmlhelp_basename = 'ZODBdocumentationandarticlesdoc'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, document class [howto/manual]).
latex_documents = [
('index', 'ZODBdocumentationandarticles.tex', ur'ZODB documentation and articles',
ur'Zope Developer Community', 'manual'),
('index', 'ZODBdocumentationandarticles.tex', 'ZODB documentation and articles',
'Zope Developer Community', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
......@@ -186,3 +204,11 @@ latex_documents = [
# If false, no module index is generated.
#latex_use_modindex = True
intersphinx_mapping = {
"python": ('https://docs.python.org/3/', None),
"persistent": ('https://persistent.readthedocs.io/en/latest/', None),
"zodburi": ("https://docs.pylonsproject.org/projects/zodburi/en/latest/", None),
"btrees": ("https://btrees.readthedocs.io/en/latest/", None),
"zodburi": ("https://docs.pylonsproject.org/projects/zodburi/en/latest/", None),
}
.. include:: ../src/ZODB/cross-database-references.rst
.. include:: ../DEVELOPERS.rst
=============
Event support
=============
Sometimes, you want to react when ZODB does certain things. In the
past, ZODB provided ad hoc hook functions for this. Going forward,
......
.. include:: ../src/ZODB/historical_connections.rst
......@@ -21,192 +21,26 @@ Check out the :doc:`tutorial`!
ZODB runs on Python 2.7 or Python 3.4 and above. It also runs on PyPy.
Transactions
============
Transactions make programs easier to reason about.
Transactions are atomic
Changes made in a transaction are either saved in their entirety or
not at all.
This makes error handling a lot easier. If you have an error, you
just abort the current transaction. You don't have to worry about
undoing previous database changes.
Transactions provide isolation
Transactions allow multiple logical threads (threads or processes)
to access databases and the database prevents the threads from
making conflicting changes.
This allows you to scale your application across multiple threads,
processes or machines without having to use low-level locking
primitives.
You still have to deal with concurrency on some level. For
timestamp-based systems like ZODB, you may have to retry conflicting
transactions. With locking-based systems, you have to deal with
possible deadlocks.
Transactions affect multiple objects
Most NoSQL databases don't have transactions. Their notions of
consistency are much weaker, typically applying to single documents.
There can be good reasons to use NoSQL databases for their extreme
scalability, but otherwise, think hard about giving up the benefits
of transactions.
ZODB transaction support:
- `ACID <https://en.wikipedia.org/wiki/ACID>`_ transactions with
`snapshot isolation
<https://en.wikipedia.org/wiki/Snapshot_isolation>`_
- Distributed transaction support using two-phase commit
This allows transactions to span multiple ZODB databases and to span
ZODB and non-ZODB databases.
Other notable ZODB features
===========================
Database caching with invalidation
Every database connection has a cache that is a consistent partial database
replica. When accessing database objects, data already in the cache
is accessed without any database interactions. When data are
modified, invalidations are sent to clients causing cached objects
to be invalidated. The next time invalidated objects are accessed
they'll be loaded from the database.
Applications don't have to invalidate cache entries. The database
invalidates cache entries automatically.
Pluggable layered storage
ZODB has a pluggable storage architecture. This allows a variety of
storage schemes including memory-based, file-based and distributed
(client-server) storage. Through storage layering, storage
components provide compression, encryption, replication and more.
Easy testing
Because application code rarely has database logic, it can
usually be unit tested without a database.
ZODB provides in-memory storage implementations as well as
copy-on-write layered "demo storage" implementations that make testing
database-related code very easy.
Garbage collection
Removal of unused objects is automatic, so application developers
don't have to worry about referential integrity.
Binary large objects, Blobs
ZODB blobs are database-managed files. This can be especially
useful when serving media. If you use AWS, there's a Blob
implementation that stores blobs in S3 and caches them on disk.
Time travel
ZODB storages typically add new records on write and remove old
records on "pack" operations. This allows limited time travel, back
to the last pack time. This can be very useful for forensic
analysis.
When should you use ZODB?
=========================
You want to focus on your application without writing a lot of database code.
ZODB provides highly transparent persistence.
Your application has complex relationships and data structures.
In relational databases you have to join tables to model complex
data structures and these joins can be tedious and expensive. You
can mitigate this to some extent in databases like Postgres by using
more powerful data types like arrays and JSON columns, but when
relationships extend across rows, you still have to do joins.
In NoSQL databases, you can model complex data structures with
documents, but if you have relationships across documents, then you
have to do joins and join capabilities in NoSQL databases are
typically far less powerful and transactional semantics typically don't
cross documents, if they exist at all.
In ZODB, you can make objects as complex as you want and cross
object relationships are handled with Python object references.
You access data through object attributes and methods.
If your primary object access is search, then other database
technologies might be a better fit.
ZODB has no query language other than Python. It's primary support
for search is through mapping objects called BTrees. People have
build higher-level search APIs on top of ZODB. These work well
enough to support some search.
You read data a lot more than you write it.
ZODB caches aggressively, and if your working set fits (or mostly
fits) in memory, performance is very good because it rarely has to
touch the database server.
If your application is very write heavy (e.g. logging), then you're
better off using something else. Sometimes, you can use a database
suitable for heavy writes in combination with ZODB.
Need to test logic that uses your database.
ZODB has a number of storage implementations, including layered
in-memory implementations that make testing very easy.
A database without an in-memory storage option can make testing very
complicated.
When should you *not* use ZODB?
===============================
- You have very high write volume.
ZODB can commit thousands of transactions per second with suitable
storage configuration and without conflicting changes.
Internal search indexes can lead to lots of conflicts, and can
therefore limit write capacity. If you need high write volume and
search beyond mapping access, consider using external indexes.
- You need to use non-Python tools to access your database.
especially tools designed to work with relational databases
Newt DB addresses these issues to a significant degree. See
http://newtdb.org.
How does ZODB scale?
====================
Not as well as many technologies, but some fairly large applications
have been built on ZODB.
At Zope Corporation, several hundred newspaper content-management
systems and web sites were hosted using a multi-database configuration
with most data in a main database and a catalog database. The
databases had several hundred gigabytes of ordinary database records
plus multiple terabytes of blob data.
ZODB is mature
==============
ZODB is very mature. Development started in 1996 and it has been used
in production in thousands of applications for many years.
ZODB is in heavy use in the `Pyramid <http://www.pylonsproject.org/>`_
and `Plone <https://plone.org/>`_ communities and in many other
applications.
Learning more
=============
.. toctree::
:maxdepth: 1
introduction
tutorial
guide/index
reference/index
articles/index
ConflictResolution
collaborations
cross-database-references
event
historical_connections
persistentclass
utils
developers
changelog
reference/index
* `The ZODB Book (in progress) <http://zodb.readthedocs.org/en/latest/>`_
......@@ -231,15 +65,23 @@ transaction system on the `transaction list
Bug reporting and feature requests are submitted through github issue
trackers for various ZODB components:
- `ZODB <https://github.com/zopefoundation/zodb>`_
- ZODB `repository <https://github.com/zopefoundation/zodb>`_
- persistent `documentation <https://persistent.readthedocs.io/en/stable/>`_ and its `repository <https://github.com/zopefoundation/persistent>`_.
- transaction `documentation <https://transaction.readthedocs.io/en/stable/>`_ and its `repository <https://github.com/zopefoundation/transaction>`_
- BTrees `documentation <https://btrees.readthedocs.io/en/stable/>`_ and their `repository <https://github.com/zopefoundation/BTrees>`_
- ZEO (client-server framework) `documentation <https://zeo.readthedocs.io/en/stable/>`_ and its `repository <https://github.com/zopefoundation/ZEO>`_
- `persistent <https://github.com/zopefoundation/persistent>`_
- relstorage `documentation <https://relstorage.readthedocs.io/en/latest/>`_ and its `repository <https://github.com/zodb/relstorage/>`_
- `transaction <https://github.com/zopefoundation/transaction>`_
- zodburi `documentation <https://docs.pylonsproject.org/projects/zodburi/en/latest/>`_ and its `repository <https://github.com/Pylons/zodburi>`_
- `BTrees <https://github.com/zopefoundation/BTrees>`_
- NEO `documentation <https://neo.nexedi.com/>`_ and its `repository <https://lab.nexedi.com/nexedi/neoppod/>`_
- `ZEO (client-server framework) <https://github.com/zopefoundation/ZEO>`_
- readonlystorage `repository <https://gitlab.com/yaal/readonlystorage>`_
If you'd like to contribute then we'll gladly accept work on documentation,
helping out other developers and users at the mailing list, submitting bugs,
......
============
Introduction
============
Transactions
============
Transactions make programs easier to reason about.
Transactions are atomic
Changes made in a transaction are either saved in their entirety or
not at all.
This makes error handling a lot easier. If you have an error, you
just abort the current transaction. You don't have to worry about
undoing previous database changes.
Transactions provide isolation
Transactions allow multiple logical threads (threads or processes)
to access databases and the database prevents the threads from
making conflicting changes.
This allows you to scale your application across multiple threads,
processes or machines without having to use low-level locking
primitives.
You still have to deal with concurrency on some level. For
timestamp-based systems like ZODB, you may have to retry conflicting
transactions. With locking-based systems, you have to deal with
possible deadlocks.
Transactions affect multiple objects
Most NoSQL databases don't have transactions. Their notions of
consistency are much weaker, typically applying to single documents.
There can be good reasons to use NoSQL databases for their extreme
scalability, but otherwise, think hard about giving up the benefits
of transactions.
ZODB transaction support:
- `ACID <https://en.wikipedia.org/wiki/ACID>`_ transactions with
`snapshot isolation
<https://en.wikipedia.org/wiki/Snapshot_isolation>`_
- Distributed transaction support using two-phase commit
This allows transactions to span multiple ZODB databases and to span
ZODB and non-ZODB databases.
Other notable ZODB features
===========================
Database caching with invalidation
Every database connection has a cache that is a consistent partial database
replica. When accessing database objects, data already in the cache
is accessed without any database interactions. When data are
modified, invalidations are sent to clients causing cached objects
to be invalidated. The next time invalidated objects are accessed
they'll be loaded from the database.
Applications don't have to invalidate cache entries. The database
invalidates cache entries automatically.
Pluggable layered storage
ZODB has a pluggable storage architecture. This allows a variety of
storage schemes including memory-based, file-based and distributed
(client-server) storage. Through storage layering, storage
components provide compression, encryption, replication and more.
Easy testing
Because application code rarely has database logic, it can
usually be unit tested without a database.
ZODB provides in-memory storage implementations as well as
copy-on-write layered "demo storage" implementations that make testing
database-related code very easy.
Garbage collection
Removal of unused objects is automatic, so application developers
don't have to worry about referential integrity.
Binary large objects, Blobs
ZODB blobs are database-managed files. This can be especially
useful when serving media. If you use AWS, there's a Blob
implementation that stores blobs in S3 and caches them on disk.
Time travel
ZODB storages typically add new records on write and remove old
records on "pack" operations. This allows limited time travel, back
to the last pack time. This can be very useful for forensic
analysis.
When should you use ZODB?
=========================
You want to focus on your application without writing a lot of database code.
ZODB provides highly transparent persistence.
Your application has complex relationships and data structures.
In relational databases you have to join tables to model complex
data structures and these joins can be tedious and expensive. You
can mitigate this to some extent in databases like Postgres by using
more powerful data types like arrays and JSON columns, but when
relationships extend across rows, you still have to do joins.
In NoSQL databases, you can model complex data structures with
documents, but if you have relationships across documents, then you
have to do joins and join capabilities in NoSQL databases are
typically far less powerful and transactional semantics typically don't
cross documents, if they exist at all.
In ZODB, you can make objects as complex as you want and cross
object relationships are handled with Python object references.
You access data through object attributes and methods.
If your primary object access is search, then other database
technologies might be a better fit.
ZODB has no query language other than Python. It's primary support
for search is through mapping objects called BTrees. People have
build higher-level search APIs on top of ZODB. These work well
enough to support some search.
You read data a lot more than you write it.
ZODB caches aggressively, and if your working set fits (or mostly
fits) in memory, performance is very good because it rarely has to
touch the database server.
If your application is very write heavy (e.g. logging), then you're
better off using something else. Sometimes, you can use a database
suitable for heavy writes in combination with ZODB.
Need to test logic that uses your database.
ZODB has a number of storage implementations, including layered
in-memory implementations that make testing very easy.
A database without an in-memory storage option can make testing very
complicated.
When should you *not* use ZODB?
===============================
- You have very high write volume.
ZODB can commit thousands of transactions per second with suitable
storage configuration and without conflicting changes.
Internal search indexes can lead to lots of conflicts, and can
therefore limit write capacity. If you need high write volume and
search beyond mapping access, consider using external indexes.
- You need to use non-Python tools to access your database.
especially tools designed to work with relational databases
Newt DB addresses these issues to a significant degree. See
http://newtdb.org.
How does ZODB scale?
====================
Not as well as many technologies, but some fairly large applications
have been built on ZODB.
At Zope Corporation, several hundred newspaper content-management
systems and web sites were hosted using a multi-database configuration
with most data in a main database and a catalog database. The
databases had several hundred gigabytes of ordinary database records
plus multiple terabytes of blob data.
ZODB is mature
==============
ZODB is very mature. Development started in 1996 and it has been used
in production in thousands of applications for many years.
ZODB is in heavy use in the `Pyramid <http://www.pylonsproject.org/>`_
and `Plone <https://plone.org/>`_ communities and in many other
applications.
.. include:: ../src/ZODB/persistentclass.rst
Sphinx
# pygments 2.6 stops the support for python2
pygments<2.6
docutils
ZODB
j1m.sphinxautointerface
j1m.sphinxautozconfig
sphinx_rtd_theme
.. include:: ../src/ZODB/utils.rst
......@@ -58,7 +58,7 @@ setup(
keywords="database nosql python zope",
packages=find_packages('src'),
package_dir={'': 'src'},
url='http://www.zodb.org/',
url='http://zodb-docs.readthedocs.io',
license="ZPL 2.1",
platforms=["any"],
classifiers=list(filter(None, classifiers.split("\n"))),
......
......@@ -69,7 +69,7 @@ It isn't valid to create references outside a multi database:
>>> tm.abort()
Databases for new objects
-------------------------
=========================
Objects are normally added to a database by making them reachable from
an object already in the database. This is unambiguous when there is
......@@ -142,7 +142,7 @@ This the most explicit and thus the best way, when practical, to avoid
the ambiguity.
Dissallowing implicit cross-database references
-----------------------------------------------
===============================================
The database contructor accepts a xrefs keyword argument that defaults
to True. If False is passed, the implicit cross database references
......@@ -173,7 +173,7 @@ the other way around.
>>> transaction.abort()
NOTE
----
====
This implementation is incomplete. It allows creating and using
cross-database references, however, there are a number of facilities
......
......@@ -177,7 +177,7 @@ until we sync:
'red'
Instances of Persistent Classes
-------------------------------
===============================
We can, of course, store instances of persistent classes in the
database:
......@@ -196,7 +196,7 @@ NOTE: If a non-persistent instance of a persistent class is copied,
Persistent instances of persistent classes
------------------------------------------
==========================================
Persistent instances of persistent classes are handled differently
than normal instances. When we copy a persistent instances of a
......@@ -267,7 +267,7 @@ Now, we can read the object:
'blue'
Copying
-------
=======
If we copy an instance via export/import, the copy and the original
share the same class:
......
=========================
Subtransactions in ZODB 3
=========================
ZODB 3 provides limited support for subtransactions. Subtransactions
are nested to *one* level. There are top-level transactions and
subtransactions. When a transaction is committed, a flag is passed
indicating whether it is a subtransaction or a top-level transaction.
Consider the following exampler commit calls:
- ``commit()``
A regular top-level transaction is committed.
- ``commit(1)``
A subtransaction is committed. There is now one subtransaction of
the current top-level transaction.
- ``commit(1)``
A subtransaction is committed. There are now two subtransactions of
the current top-level transaction.
- ``abort(1)``
A subtransaction is aborted. There are still two subtransactions of
the current top-level transaction; work done since the last
``commit(1)`` call is discarded.
- ``commit()``
We now commit a top-level transaction. The work done in the previous
two subtransactions *plus* work done since the last ``abort(1)`` call
is saved.
- ``commit(1)``
A subtransaction is committed. There is now one subtransaction of
the current top-level transaction.
- ``commit(1)``
A subtransaction is committed. There are now two subtransactions of
the current top-level transaction.
- ``abort()``
We now abort a top-level transaction. We discard the work done in
the previous two subtransactions *plus* work done since the last
``commit(1)`` call.
......@@ -156,6 +156,6 @@ class ExampleClass(object):
def test_suite():
suite = unittest.defaultTestLoader.loadTestsFromName(__name__)
suite.addTest(
doctest.DocFileSuite('../utils.txt', checker=checker)
doctest.DocFileSuite('../utils.rst', checker=checker)
)
return suite
......@@ -394,7 +394,7 @@ def test_suite():
manuel.doctest.Manuel(checker=ZODB.tests.util.checker)
+ manuel.footnote.Manuel()
+ manuel.capture.Manuel(),
'../ConflictResolution.txt',
'../ConflictResolution.rst',
setUp=setUp, tearDown=tearDown
),
doctest.DocTestSuite(
......
......@@ -183,13 +183,13 @@ def tearDownDbs(test):
def test_suite():
return unittest.TestSuite((
doctest.DocFileSuite(
'../cross-database-references.txt',
'../cross-database-references.rst',
globs=dict(MyClass=MyClass),
tearDown=tearDownDbs,
checker=ZODB.tests.util.checker,
),
doctest.DocFileSuite(
'../cross-database-references.txt',
'../cross-database-references.rst',
globs=dict(MyClass=MyClass_w_getnewargs),
tearDown=tearDownDbs,
checker=ZODB.tests.util.checker,
......
......@@ -20,6 +20,6 @@ def test_suite():
return manuel.testing.TestSuite(
manuel.doctest.Manuel(checker=ZODB.tests.util.checker) +
manuel.footnote.Manuel(),
'../historical_connections.txt',
'../historical_connections.rst',
setUp=ZODB.tests.util.setUp, tearDown=ZODB.tests.util.tearDown,
)
......@@ -87,7 +87,7 @@ def tearDown(test):
def test_suite():
return unittest.TestSuite((
doctest.DocFileSuite(
"../persistentclass.txt",
"../persistentclass.rst",
setUp=setUp, tearDown=tearDown,
checker=ZODB.tests.util.checker),
doctest.DocTestSuite(setUp=setUp, tearDown=tearDown),
......
ZODB Utilits Module
===================
=====================
ZODB Utilities Module
=====================
The ZODB.utils module provides a number of helpful, somewhat random
:), utility functions.
......@@ -10,7 +11,7 @@ This document documents a few of them. Over time, it may document
more.
64-bit integers and strings
---------------------------------
===========================
ZODB uses 64-bit transaction ids that are typically represented as
strings, but are sometimes manipulated as integers. Object ids are
......@@ -31,7 +32,7 @@ The contant z64 has zero packed as a 64-bit string:
'\x00\x00\x00\x00\x00\x00\x00\x00'
Transaction id generation
-------------------------
=========================
Storages assign transaction ids as transactions are committed. These
are based on UTC time, but must be strictly increasing. The
......@@ -86,7 +87,7 @@ time, the time stamp we get will be based on the time:
Locking support
---------------
===============
Storages are required to be thread safe. The locking descriptor helps
automate that. It arranges for a lock to be acquired when a function
......@@ -147,7 +148,7 @@ attribute.
#as first argument (got C2 instance instead)
Preconditions
-------------
=============
Often, we want to supply method preconditions. The locking descriptor
supports optional method preconditions [1]_.
......
[tox]
envlist = py27,py35,py36,py37,py38,pypy,pypy3,py38-pure
envlist = py27,py35,py36,py37,py38,pypy,pypy3,py38-pure,docs
[testenv]
# ZODB.tests.testdocumentation needs to find
......@@ -28,3 +28,12 @@ basepython =
python3.8
setenv =
PURE_PYTHON = 1
[testenv:docs]
basepython =
python3.7
commands =
sphinx-build -b html -d doc/_build/doctrees doc doc/_build/html
sphinx-build -d doc/_build/doctrees doc doc/_build/doctest
deps =
--requirement doc/requirements.txt
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment