launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #25598
[Merge] ~cjwatson/launchpad:make-requirements into launchpad:master
Colin Watson has proposed merging ~cjwatson/launchpad:make-requirements into launchpad:master.
Commit message:
Build constraints file dynamically
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/393170
The constraints file used to specify Launchpad's Python dependencies is logically built by combining a particular version of the Zope Toolkit's ztk-versions.cfg file (which is published as a buildout configuration file) with our own requirements, which may either override the ZTK version of a particular package or add additional packages. Since we ported our build system from buildout to pip, we've been maintaining this as a single file in PEP 508 requirements syntax with overrides expressed by commenting out the original and adding another version immediately afterwards; this was functional, but cumbersome and confusing.
Instead, keep an unmodified copy of the relevant version of ztk-versions.cfg, and apply local overrides to it dynamically. This should be much easier to maintain: we can upgrade to newer ZTK releases just by dropping in a new version of ztk-versions.cfg (and perhaps removing overrides that are no longer necessary), and we can upgrade our local requirements without needing to worry about whether doing so overrides ZTK-specified versions.
When building on Python 2, there is no functional change.
When building on Python 3, Pygments, Sphinx, and sphinxcontrib-websupport get upgraded versions. This was already specified in ztk-versions.cfg, but previously went unnoticed in the manual conversion to PEP 508 requirements syntax.
Dependencies MP: https://code.launchpad.net/~cjwatson/lp-source-dependencies/+git/lp-source-dependencies/+merge/393169
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:make-requirements into launchpad:master.
diff --git a/.gitignore b/.gitignore
index a2691e1..0f9be64 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,3 +75,4 @@ callgrind.out.*
/logs
yarn/node_modules
/wheelhouse
+requirements/combined.txt
diff --git a/Makefile b/Makefile
index 1b84b8d..8d9c527 100644
--- a/Makefile
+++ b/Makefile
@@ -240,6 +240,16 @@ jsbuild: $(LP_JS_BUILD) $(YUI_SYMLINK)
build/js/lp/meta.js >/dev/null
utilities/check-js-deps
+requirements/combined.txt: \
+ requirements/setup.txt \
+ requirements/ztk-versions.cfg \
+ requirements/launchpad.txt
+ $(PYTHON) utilities/make-requirements.py \
+ --exclude requirements/setup.txt \
+ --buildout requirements/ztk-versions.cfg \
+ --include requirements/launchpad.txt \
+ >"$@"
+
# This target is used by LOSAs to prepare a build to be pushed out to
# destination machines. We only want wheels: they are the expensive bits,
# and the other bits might run into problems like bug 575037. This target
@@ -250,10 +260,11 @@ jsbuild: $(LP_JS_BUILD) $(YUI_SYMLINK)
# It doesn't seem to be straightforward to build a wheelhouse of all our
# dependencies without also building a useless wheel of Launchpad itself;
# fortunately that doesn't take too long, and we just remove it afterwards.
-build_wheels: $(PIP_BIN)
+build_wheels: $(PIP_BIN) requirements/combined.txt
$(RM) -r wheelhouse
$(SHHH) $(PIP) wheel \
- -c setup-requirements.txt -c constraints.txt -w wheelhouse .
+ -c requirements/setup.txt -c requirements/combined.txt \
+ -w wheelhouse .
$(RM) wheelhouse/lp-[0-9]*.whl
$(MAKE) clean_pip
@@ -267,7 +278,7 @@ build_eggs: build_wheels
# If we listed every target on the left-hand side, a parallel make would try
# multiple copies of this rule to build them all. Instead, we nominally build
# just $(PY), and everything else is implicitly updated by that.
-$(PY): download-cache constraints.txt setup.py
+$(PY): download-cache requirements/combined.txt setup.py
rm -rf env
mkdir -p env
$(VIRTUALENV) \
@@ -276,10 +287,10 @@ $(PY): download-cache constraints.txt setup.py
--extra-search-dir=$(WD)/wheelhouse/ \
env
ln -sfn env/bin bin
- $(SHHH) $(PIP) install -r setup-requirements.txt
+ $(SHHH) $(PIP) install -r requirements/setup.txt
$(SHHH) LPCONFIG=$(LPCONFIG) $(PIP) \
install \
- -c setup-requirements.txt -c constraints.txt -e . \
+ -c requirements/setup.txt -c requirements/combined.txt -e . \
|| { code=$$?; rm -f $@; exit $$code; }
touch $@
@@ -422,6 +433,7 @@ lxc-clean: clean_js clean_pip clean_logs
# referenced bug is fixed, this target may be reunited with
# the 'clean' target.
$(RM) -r env wheelhouse
+ $(RM) requirements/combined.txt
$(RM) -r $(LP_BUILT_JS_ROOT)/*
$(RM) -r $(CODEHOSTING_ROOT)/*
$(RM) -r $(APIDOC_DIR)
diff --git a/constraints.txt b/constraints.txt
deleted file mode 100644
index 116d95e..0000000
--- a/constraints.txt
+++ /dev/null
@@ -1,342 +0,0 @@
-# ztk-versions.cfg from ZTK a9eb2093b5 (2019-10-23), with some upgrades
-# ---------------------------------------------------------------------
-
-# XXX cjwatson 2020-02-21: Cases where we've upgraded relative to
-# ztk-versions.cfg are denoted by the upstream version being commented out
-# and followed by the real version we want to use. This is a bit
-# cumbersome, and we should build this file dynamically instead.
-
-zope.annotation==4.7.0
-zope.applicationcontrol==4.2.0
-zope.authentication==4.4.0
-zope.browser==2.3
-zope.browsermenu==4.4
-zope.browserpage==4.4.0
-zope.browserresource==4.4
-zope.cachedescriptors==4.3.1
-zope.catalog==4.2.1
-zope.component==4.6.1
-zope.componentvocabulary==2.2.0
-zope.configuration==4.4.0
-zope.container==4.4.0
-zope.contentprovider==4.2.1
-zope.contenttype==4.5.0
-zope.copy==4.2
-zope.copypastemove==4.1.0
-zope.datetime==4.2.0
-zope.deferredimport==4.3.1
-zope.deprecation==4.4.0
-zope.dottedname==4.3
-zope.dublincore==4.2.0
-zope.error==4.5.0
-zope.event==4.4
-zope.exceptions==4.3
-zope.filerepresentation==5.0.0
-zope.formlib==4.7.1
-zope.hookable==5.0.1
-zope.i18n==4.7.0
-zope.i18nmessageid==5.0.1
-zope.index==5.0.0
-zope.interface==5.0.2
-zope.intid==4.3.0
-zope.keyreference==4.2.0
-zope.lifecycleevent==4.3
-zope.location==4.2
-zope.login==2.1.0
-zope.mimetype==2.5.0
-zope.minmax==2.2.0
-zope.pagetemplate==4.5.0
-zope.password==4.3.1
-zope.pluggableauth==2.3.0
-zope.principalannotation==4.3.0
-zope.principalregistry==4.2.0
-zope.processlifetime==2.3.0
-zope.proxy==4.3.5
-zope.ptresource==4.2.0
-zope.publisher==5.2.0
-zope.ramcache==2.3
-zope.schema==6.0.0
-zope.security==5.1.1
-zope.securitypolicy==4.3.1
-zope.sendmail==5.0
-#zope.session==4.3.0
-# lp:~launchpad-committers/zope.session:launchpad
-zope.session==4.3.0+lp1
-zope.site==4.3.0
-zope.size==4.3
-zope.structuredtext==4.3
-zope.tal==4.4
-zope.tales==5.0.2
-zope.testing==4.7
-#zope.testrunner==5.1
-# lp:~launchpad-committers/zope.testrunner:launchpad
-zope.testrunner==5.2+lp1
-zope.traversing==4.4.1
-zope.viewlet==4.2.1
-
-# Direct dependencies
-BTrees==4.7.1
-persistent==4.6.4
-python-gettext==4.0
-pytz==2019.3
-# Handled in setup-requirements.txt instead.
-#setuptools==44.0.0
-six==1.15.0
-transaction==3.0.0
-
-# zope.password needs these
-bcrypt==3.1.7
-cffi==1.14.0
-pycparser==2.20
-
-# Python2-only
-zope.untrustedpython==4.0.0
-# Required by zope.untrustedpython
-RestrictedPython==3.6.0
-
-# Testing dependencies
-ZConfig==3.5.0
-ZODB==5.5.1
-argparse==1.4.0
-colorama==0.4.3
-extras==1.0.0
-fixtures==3.0.0
-linecache2==1.0.0
-manuel==1.10.1
-pbr==5.4.4
-pyparsing==2.4.6
-python-mimeparse==1.6.0
-python-subunit==1.4.0
-testtools==2.4.0
-traceback2==1.4.0
-#unittest2==1.1.0
-# lp1 Set version directly in setup.py to fix wheel building.
-unittest2==1.1.0+lp1
-zc.lockfile==2.0
-zdaemon==4.3
-zodbpickle==2.0.0
-
-
-# Testing tools
-coverage==5.0.4
-nose==1.3.7
-
-# Documentation dependencies
-Sphinx==1.8.5
-docutils==0.16
-imagesize==1.2.0
-alabaster==0.7.12
-babel==2.8.0
-Jinja2==2.11.1
-MarkupSafe==1.1.1
-Pygments==2.5.2
-snowballstemmer==2.0.0
-lxml==4.5.0
-repoze.sphinx.autointerface==0.8
-requests==2.23.0
-certifi==2019.11.28
-urllib3==1.25.11
-idna==2.9
-chardet==3.0.4
-sphinxcontrib-applehelp==1.0.2
-sphinxcontrib-devhelp==1.0.2
-sphinxcontrib-htmlhelp==1.0.3
-sphinxcontrib-jsmath==1.0.1
-sphinxcontrib-programoutput==0.16
-sphinxcontrib-qthelp==1.0.3
-sphinxcontrib-serializinghtml==1.1.4
-sphinxcontrib-websupport==1.1.2
-sphinx-rtd-theme==0.4.3
-packaging==20.3
-typing==3.7.4.1
-z3c.recipe.sphinxdoc==1.1.0
-
-# ZTK buildout dependencies
-collective.recipe.cmd==0.11
-mr.developer==2.0.0
-z3c.checkversions==1.1
-z3c.recipe.compattest==1.0
-zc.buildout==2.13.3
-zc.recipe.egg==2.0.7
-zc.recipe.testrunner==2.1
-
-# Launchpad
-# ---------
-
-# Alphabetical, case-insensitive, please! :-)
-
-ampoule==19.12.0
-amqp==2.4.2
-anyjson==0.3.3
-appdirs==1.4.3
-asn1crypto==0.23.0
-attrs==19.1.0
-Automat==0.6.0
-backports.functools-lru-cache==1.5
-backports.lzma==0.0.3
-beautifulsoup4==4.7.1
-billiard==3.5.0.5
-bleach==3.1.0
-breezy==3.0.1
-bson==0.5.9
-boto3==1.16.2
-botocore==1.19.2
-# lp:~launchpad/bzr/lp
-bzr==2.6.0.lp.4
-celery==4.1.1
-Chameleon==3.6.2
-configobj==5.0.6
-constantly==15.1.0
-contextlib2==0.6.0.post1
-cookies==2.2.1
-cryptography==2.7
-cssselect==0.9.1
-cssutils==1.0.2
-Cython==0.29.17
-d2to1==0.2.12
-defusedxml==0.6.0
-distro==1.4.0
-dkimpy==1.0.4
-dnspython==1.16.0
-dulwich==0.18.6
-eggtestinfo==0.3
-elementtree==1.2.6-20050316
-enum34==1.1.6
-fastimport==0.9.8
-feedparser==5.2.1
-feedvalidator==0.0.0DEV-r1049
-FormEncode==1.3.1
-futures==3.3.0
-geoip2==2.9.0
-grokcore.component==3.1
-gunicorn==19.8.1
-httplib2==0.8
-hyperlink==18.0.0
-importlib==1.0.2
-importlib-resources==0.5
-incremental==17.5.0
-ipaddress==1.0.18
-ipython==0.13.2
-iso8601==0.1.12
-jmespath==0.10.0
-jsautobuild==0.2
-keyring==0.6.2
-kombu==4.4.0
-launchpad-buildd==193
-launchpadlib==1.10.9
-lazr.authentication==0.1.1
-lazr.batchnavigator==1.3.0
-lazr.config==2.2.2
-lazr.delegates==2.0.4
-lazr.enum==1.2
-lazr.jobrunner==0.16
-lazr.lifecycle==1.2
-lazr.restful==0.23.0
-lazr.restfulclient==0.14.3
-lazr.sshserver==0.1.10
-lazr.uri==1.0.5
-libnacl==1.3.6
-lpjsmin==0.6
-m2r==0.1.13
-Markdown==2.3.1
-martian==1.3.post1
-maxminddb==1.5.1
-meliae==0.5.1
-mistune==0.8.3
-mock==1.0.1
-mocker==1.1.1
-monotonic==1.5
-netaddr==0.7.19
-oauth==1.0
-oauthlib==3.1.0
-oops==0.0.14
-oops-amqp==0.1.0
-oops-datedir-repo==0.0.24
-oops-timeline==0.0.3
-oops-twisted==0.0.8
-oops-wsgi==0.0.14
-ordereddict==1.1
-oslo.config==1.3.0
-paramiko==2.4.1
-Paste==3.3.0
-PasteDeploy==2.1.0
-PasteScript==3.2.0
-pathlib2==2.3.2
-pgbouncer==0.0.9
-prettytable==0.7.2
-psutil==5.4.2
-psycopg2==2.7.4
-pyasn1==0.4.2
-pyasn1-modules==0.2.1
-pygettextpo==0.2
-# lp:~launchpad/pygpgme/pthread
-# lp1 Link against libgpgme-pthread for thread-safety.
-pygpgme==0.3+lp1
-PyHamcrest==1.9.0
-pyinotify==0.9.4
-pymacaroons==0.13.0
-PyNaCl==1.3.0
-pyOpenSSL==17.5.0
-pystache==0.5.3
-python-dateutil==2.8.1
-python-debian==0.1.32
-python-keystoneclient==0.7.1
-python-memcached==1.58
-# https://github.com/ziima/python-openid/pull/47
-# lp1 Fix normalization of non-ASCII query strings on Python 2.
-python-openid2==3.2+lp1
-python-swiftclient==3.8.1
-PyYAML==3.10
-rabbitfixture==0.4.2
-requests-file==1.4.3
-requests-toolbelt==0.9.1
-responses==0.9.0
-s3transfer==0.3.3
-scandir==1.7
-service-identity==18.1.0
-setproctitle==1.1.7
-setuptools-git==1.2
-setuptools-scm==3.4.3
-simplejson==3.8.2
-SimpleTAL==4.3; python_version < "3"
-SimpleTAL==5.2; python_version >= "3"
-soupmatchers==0.4
-soupsieve==1.9
-statsd==3.3.0
-# lp:~launchpad-committers/storm/lp
-storm==0.24+lp416
-subprocess32==3.2.6
-subvertpy==0.9.1
-tenacity==6.1.0
-testresources==0.2.7
-testscenarios==0.4
-timeline==0.0.7
-treq==18.6.0
-Twisted==19.2.1
-txAMQP==0.6.2
-txfixtures==0.4.3
-txpkgupload==0.2
-van.testing==3.0.0
-vine==1.1.4
-virtualenv-tools3==2.0.0
-wadllib==1.3.2
-waitress==1.3.1
-webencodings==0.5.1
-WebOb==1.8.5
-WebTest==2.0.35
-wsgi-intercept==1.9.2
-WSGIProxy2==0.4.6
-wsgiref==0.1.2
-z3c.pt==3.2.0
-z3c.ptcompat==2.2.0
-zc.zservertracelog==2.0.0
-zope.app.applicationcontrol==4.0.0
-zope.app.appsetup==4.1.0
-zope.app.http==4.0.1
-zope.app.publication==4.3.1
-zope.app.publisher==4.2.0
-zope.app.server==4.0.0
-zope.app.wsgi==4.3.0
-zope.server==4.0.2
-zope.testbrowser==5.5.1
-zope.vocabularyregistry==1.1.1
diff --git a/requirements/launchpad.txt b/requirements/launchpad.txt
new file mode 100644
index 0000000..045e778
--- /dev/null
+++ b/requirements/launchpad.txt
@@ -0,0 +1,190 @@
+# Please keep requirements in this file sorted alphabetically and
+# case-insensitively. Local forks should use +lpN suffixes and be annotated
+# with information about where to find the code for the fork.
+#
+# Don't list entries from ztk-versions.cfg here unless overriding their
+# versions; they will be included automatically.
+
+ampoule==19.12.0
+amqp==2.4.2
+anyjson==0.3.3
+appdirs==1.4.3
+asn1crypto==0.23.0
+attrs==19.1.0
+Automat==0.6.0
+backports.functools-lru-cache==1.5
+backports.lzma==0.0.3
+beautifulsoup4==4.7.1
+billiard==3.5.0.5
+bleach==3.1.0
+breezy==3.0.1
+bson==0.5.9
+boto3==1.16.2
+botocore==1.19.2
+# lp:~launchpad/bzr/lp
+bzr==2.6.0.lp.4
+celery==4.1.1
+Chameleon==3.6.2
+configobj==5.0.6
+constantly==15.1.0
+contextlib2==0.6.0.post1
+cookies==2.2.1
+cryptography==2.7
+cssselect==0.9.1
+cssutils==1.0.2
+Cython==0.29.17
+d2to1==0.2.12
+defusedxml==0.6.0
+distro==1.4.0
+dkimpy==1.0.4
+dnspython==1.16.0
+dulwich==0.18.6
+eggtestinfo==0.3
+elementtree==1.2.6-20050316
+enum34==1.1.6
+fastimport==0.9.8
+feedparser==5.2.1
+feedvalidator==0.0.0DEV-r1049
+FormEncode==1.3.1
+futures==3.3.0
+geoip2==2.9.0
+grokcore.component==3.1
+gunicorn==19.8.1
+httplib2==0.8
+hyperlink==18.0.0
+importlib==1.0.2
+importlib-resources==0.5
+incremental==17.5.0
+ipaddress==1.0.18
+ipython==0.13.2
+iso8601==0.1.12
+jmespath==0.10.0
+jsautobuild==0.2
+keyring==0.6.2
+kombu==4.4.0
+launchpad-buildd==193
+launchpadlib==1.10.9
+lazr.authentication==0.1.1
+lazr.batchnavigator==1.3.0
+lazr.config==2.2.2
+lazr.delegates==2.0.4
+lazr.enum==1.2
+lazr.jobrunner==0.16
+lazr.lifecycle==1.2
+lazr.restful==0.23.0
+lazr.restfulclient==0.14.3
+lazr.sshserver==0.1.10
+lazr.uri==1.0.5
+libnacl==1.3.6
+lpjsmin==0.6
+m2r==0.1.13
+Markdown==2.3.1
+martian==1.3.post1
+maxminddb==1.5.1
+meliae==0.5.1
+mistune==0.8.3
+mock==1.0.1
+mocker==1.1.1
+monotonic==1.5
+netaddr==0.7.19
+oauth==1.0
+oauthlib==3.1.0
+oops==0.0.14
+oops-amqp==0.1.0
+oops-datedir-repo==0.0.24
+oops-timeline==0.0.3
+oops-twisted==0.0.8
+oops-wsgi==0.0.14
+ordereddict==1.1
+oslo.config==1.3.0
+paramiko==2.4.1
+Paste==3.3.0
+PasteDeploy==2.1.0
+PasteScript==3.2.0
+pathlib2==2.3.2
+pgbouncer==0.0.9
+prettytable==0.7.2
+psutil==5.4.2
+psycopg2==2.7.4
+pyasn1==0.4.2
+pyasn1-modules==0.2.1
+pygettextpo==0.2
+# lp:~launchpad/pygpgme/pthread
+# lp1 Link against libgpgme-pthread for thread-safety.
+pygpgme==0.3+lp1
+PyHamcrest==1.9.0
+pyinotify==0.9.4
+pymacaroons==0.13.0
+PyNaCl==1.3.0
+pyOpenSSL==17.5.0
+pystache==0.5.3
+python-dateutil==2.8.1
+python-debian==0.1.32
+python-keystoneclient==0.7.1
+python-memcached==1.58
+# https://github.com/ziima/python-openid/pull/47
+# lp1 Fix normalization of non-ASCII query strings on Python 2.
+python-openid2==3.2+lp1
+python-swiftclient==3.8.1
+PyYAML==3.10
+rabbitfixture==0.4.2
+requests-file==1.4.3
+requests-toolbelt==0.9.1
+responses==0.9.0
+s3transfer==0.3.3
+scandir==1.7
+service-identity==18.1.0
+setproctitle==1.1.7
+setuptools-git==1.2
+setuptools-scm==3.4.3
+simplejson==3.8.2
+SimpleTAL==4.3; python_version < "3"
+SimpleTAL==5.2; python_version >= "3"
+six==1.15.0
+soupmatchers==0.4
+soupsieve==1.9
+statsd==3.3.0
+# lp:~launchpad-committers/storm/lp
+storm==0.24+lp416
+subprocess32==3.2.6
+subvertpy==0.9.1
+tenacity==6.1.0
+testresources==0.2.7
+testscenarios==0.4
+timeline==0.0.7
+treq==18.6.0
+Twisted==19.2.1
+txAMQP==0.6.2
+txfixtures==0.4.3
+txpkgupload==0.2
+# lp1 Set version directly in setup.py to fix wheel building.
+unittest2==1.1.0+lp1
+urllib3==1.25.11
+van.testing==3.0.0
+vine==1.1.4
+virtualenv-tools3==2.0.0
+wadllib==1.3.2
+waitress==1.3.1
+webencodings==0.5.1
+WebOb==1.8.5
+WebTest==2.0.35
+wsgi-intercept==1.9.2
+WSGIProxy2==0.4.6
+wsgiref==0.1.2
+z3c.pt==3.2.0
+z3c.ptcompat==2.2.0
+zc.zservertracelog==2.0.0
+zope.app.applicationcontrol==4.0.0
+zope.app.appsetup==4.1.0
+zope.app.http==4.0.1
+zope.app.publication==4.3.1
+zope.app.publisher==4.2.0
+zope.app.server==4.0.0
+zope.app.wsgi==4.3.0
+zope.server==4.0.2
+# lp:~launchpad-committers/zope.session:launchpad
+zope.session==4.3.0+lp1
+zope.testbrowser==5.5.1
+# lp:~launchpad-committers/zope.testrunner:launchpad
+zope.testrunner==5.2+lp1
+zope.vocabularyregistry==1.1.1
diff --git a/setup-requirements.txt b/requirements/setup.txt
similarity index 100%
rename from setup-requirements.txt
rename to requirements/setup.txt
diff --git a/requirements/ztk-versions.cfg b/requirements/ztk-versions.cfg
new file mode 100644
index 0000000..37a3cfd
--- /dev/null
+++ b/requirements/ztk-versions.cfg
@@ -0,0 +1,154 @@
+[versions]
+# ZTK
+zope.annotation = 4.7.0
+zope.applicationcontrol = 4.2.0
+zope.authentication = 4.4.0
+zope.browser = 2.3
+zope.browsermenu = 4.4
+zope.browserpage = 4.4.0
+zope.browserresource = 4.4
+zope.cachedescriptors = 4.3.1
+zope.catalog = 4.2.1
+zope.component = 4.6.1
+zope.componentvocabulary = 2.2.0
+zope.configuration = 4.4.0
+zope.container = 4.4.0
+zope.contentprovider = 4.2.1
+zope.contenttype = 4.5.0
+zope.copy = 4.2
+zope.copypastemove = 4.1.0
+zope.datetime = 4.2.0
+zope.deferredimport = 4.3.1
+zope.deprecation = 4.4.0
+zope.dottedname = 4.3
+zope.dublincore = 4.2.0
+zope.error = 4.5.0
+zope.event = 4.4
+zope.exceptions = 4.3
+zope.filerepresentation = 5.0.0
+zope.formlib = 4.7.1
+zope.hookable = 5.0.1
+zope.i18n = 4.7.0
+zope.i18nmessageid = 5.0.1
+zope.index = 5.0.0
+zope.interface = 5.0.2
+zope.intid = 4.3.0
+zope.keyreference = 4.2.0
+zope.lifecycleevent = 4.3
+zope.location = 4.2
+zope.login = 2.1.0
+zope.mimetype = 2.5.0
+zope.minmax = 2.2.0
+zope.pagetemplate = 4.5.0
+zope.password = 4.3.1
+zope.pluggableauth = 2.3.0
+zope.principalannotation = 4.3.0
+zope.principalregistry = 4.2.0
+zope.processlifetime = 2.3.0
+zope.proxy = 4.3.5
+zope.ptresource = 4.2.0
+zope.publisher = 5.2.0
+zope.ramcache = 2.3
+zope.schema = 6.0.0
+zope.security = 5.1.1
+zope.securitypolicy = 4.3.1
+zope.sendmail = 5.0
+zope.session = 4.3.0
+zope.site = 4.3.0
+zope.size = 4.3
+zope.structuredtext = 4.3
+zope.tal = 4.4
+zope.tales = 5.0.2
+zope.testing = 4.7
+zope.testrunner = 5.1
+zope.traversing = 4.4.1
+zope.viewlet = 4.2.1
+
+# Direct dependencies
+BTrees = 4.7.1
+persistent = 4.6.4
+python-gettext = 4.0
+pytz = 2019.3
+setuptools = 46.1.3
+six = 1.14.0
+transaction = 3.0.0
+
+# zope.password needs these
+bcrypt = 3.1.7
+cffi = 1.14.0
+pycparser = 2.20
+
+# Python2-only
+zope.untrustedpython = 4.0.0
+# Required by zope.untrustedpython
+RestrictedPython = 3.6.0
+
+# Testing dependencies
+ZConfig = 3.5.0
+ZODB = 5.5.1
+argparse = 1.4.0
+colorama = 0.4.3
+extras = 1.0.0
+fixtures = 3.0.0
+linecache2 = 1.0.0
+manuel = 1.10.1
+pbr = 5.4.4
+pyparsing = 2.4.6
+python-mimeparse = 1.6.0
+python-subunit = 1.4.0
+testtools = 2.4.0
+traceback2 = 1.4.0
+unittest2 = 1.1.0
+zc.lockfile = 2.0
+zdaemon = 4.3
+zodbpickle = 2.0.0
+
+
+# Testing tools
+coverage = 5.0.4
+nose = 1.3.7
+
+# Documentation dependencies
+Sphinx = 2.4.4
+docutils = 0.16
+imagesize = 1.2.0
+alabaster = 0.7.12
+babel = 2.8.0
+Jinja2 = 2.11.1
+MarkupSafe = 1.1.1
+Pygments = 2.6.1
+snowballstemmer = 2.0.0
+lxml = 4.5.0
+repoze.sphinx.autointerface = 0.8
+requests = 2.23.0
+certifi = 2019.11.28
+urllib3 = 1.25.8
+idna = 2.9
+chardet = 3.0.4
+sphinxcontrib-applehelp = 1.0.2
+sphinxcontrib-devhelp = 1.0.2
+sphinxcontrib-htmlhelp = 1.0.3
+sphinxcontrib-jsmath = 1.0.1
+sphinxcontrib-programoutput = 0.16
+sphinxcontrib-qthelp = 1.0.3
+sphinxcontrib-serializinghtml = 1.1.4
+sphinxcontrib-websupport = 1.2.1
+sphinx-rtd-theme = 0.4.3
+packaging = 20.3
+typing = 3.7.4.1
+z3c.recipe.sphinxdoc = 1.1.0
+
+# ZTK buildout dependencies
+collective.recipe.cmd = 0.11
+mr.developer = 2.0.0
+z3c.checkversions = 1.1
+z3c.recipe.compattest = 1.0
+zc.buildout = 2.13.3
+zc.recipe.egg = 2.0.7
+zc.recipe.testrunner = 2.1
+
+[versions:python27]
+Pygments = 2.5.2
+Sphinx = 1.8.5
+setuptools = 44.0.0
+sphinxcontrib-websupport = 1.1.2
diff --git a/utilities/make-requirements.py b/utilities/make-requirements.py
new file mode 100755
index 0000000..00a7231
--- /dev/null
+++ b/utilities/make-requirements.py
@@ -0,0 +1,122 @@
+#! /usr/bin/python2
+
+# Copyright 2020 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Build a constraints.txt file from inputs."""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from argparse import ArgumentParser
+from collections import defaultdict
+try:
+ # Python 3
+ from configparser import ConfigParser
+except ImportError:
+ # Python 2
+ from ConfigParser import SafeConfigParser as ConfigParser
+import logging
+
+from pkg_resources import parse_requirements
+
+
+def read_buildout_versions(path):
+ """Parse versions from a buildout configuration file into requirements.
+
+ This isn't intended to parse everything buildout can do, but just enough
+ to parse Zope Toolkit versions files. Note that these are treated as
+ constraints (`pip install -c`) rather than requirements (`pip install
+ -r`), because we don't use the whole Zope Toolkit.
+ """
+ parser = ConfigParser()
+ parser.optionxform = str
+ parser.read(path)
+ versions = defaultdict(dict)
+ for section in parser.sections():
+ if section == "versions":
+ python_version = None
+ elif section.startswith("versions:python"):
+ python_suffix = section[len("versions:python"):]
+ if len(python_suffix) == 1 and python_suffix.isdigit():
+ python_version = "%s.*" % python_suffix
+ elif len(python_suffix) == 2 and python_suffix.isdigit():
+ python_version = "%s.%s.*" % tuple(python_suffix)
+ else:
+ raise ValueError("Unhandled section: %s" % section)
+ else:
+ raise ValueError("Unhandled section: %s" % section)
+ for name in parser.options(section):
+ versions[name][python_version] = parser.get(section, name)
+ requirements = []
+ for name in sorted(versions):
+ python_versions = list(versions[name])
+ if python_versions == [None]:
+ requirements.append("%s==%s" % (name, versions[name][None]))
+ else:
+ for python_version in python_versions:
+ if python_version is None:
+ continue
+ requirements.append(
+ '%s==%s; python_version == "%s"' %
+ (name, versions[name][python_version], python_version))
+ if None in python_versions:
+ marker = " and ".join(
+ 'python_version != "%s"' % python_version
+ for python_version in python_versions
+ if python_version is not None)
+ requirements.append(
+ "%s==%s; %s" % (name, versions[name][None], marker))
+ return list(parse_requirements(requirements))
+
+
+def read_requirements(path):
+ """Parse a PEP 508 requirements file."""
+ with open(path) as f:
+ return list(parse_requirements(f))
+
+
+def write_requirements(include_requirements, exclude_requirements):
+ """Write a combined requirements file to stdout."""
+ combined = {}
+ for requirements in include_requirements:
+ by_key = defaultdict(list)
+ for requirement in requirements:
+ by_key[requirement.key].append(requirement)
+ for key in by_key:
+ if by_key[key] == combined.get(key):
+ logging.warning("Duplicate requirement found: %s", by_key[key])
+ combined.update(by_key)
+ for requirements in exclude_requirements:
+ for requirement in requirements:
+ if requirement.key in combined:
+ del combined[requirement.key]
+
+ print("# THIS IS AN AUTOGENERATED FILE; DO NOT EDIT DIRECTLY.")
+ for key in sorted(combined):
+ for requirement in combined[key]:
+ print(requirement)
+
+
+def main():
+ parser = ArgumentParser()
+ parser.add_argument(
+ "--buildout", action="append", metavar="VERSIONS",
+ help="Include requirements from this buildout versions file")
+ parser.add_argument(
+ "--include", action="append", metavar="REQUIREMENTS",
+ help="Include requirements from this PEP 508 requirements file")
+ parser.add_argument(
+ "--exclude", action="append", metavar="REQUIREMENTS",
+ help="Exclude requirements from this PEP 508 requirements file")
+ args = parser.parse_args()
+
+ include_requirements = (
+ [read_buildout_versions(path) for path in args.buildout] +
+ [read_requirements(path) for path in args.include])
+ exclude_requirements = [read_requirements(path) for path in args.exclude]
+
+ write_requirements(include_requirements, exclude_requirements)
+
+
+if __name__ == "__main__":
+ main()