Merge lp:~lvh/divmod.org/modernize-axiom-packaging into lp:divmod.org

Proposed by Laurens Van Houtven
Status: Merged
Merged at revision: 2718
Proposed branch: lp:~lvh/divmod.org/modernize-axiom-packaging
Merge into: lp:divmod.org
Diff against target: 167 lines (+95/-26)
7 files modified
Axiom/.coveragerc (+8/-0)
Axiom/axiom/__init__.py (+7/-4)
Axiom/axiom/_version.py (+1/-3)
Axiom/requirements-testing.txt (+2/-0)
Axiom/requirements.txt (+5/-7)
Axiom/setup.py (+48/-12)
Axiom/tox.ini (+24/-0)
To merge this branch: bzr merge lp:~lvh/divmod.org/modernize-axiom-packaging
Reviewer Review Type Date Requested Status
Tristan Seligmann Needs Fixing
Jean-Paul Calderone Needs Fixing
Review via email: mp+180311@code.launchpad.net

Description of the change

A number of changes to make Axiom a bit more present-day Python packaging compatible :)

setup.py now works without relying on non-stdlib projects. This makes it easier to pip install Axiom.

Also, tox has been configured to automagically make virtualenvs to run the tests in. This will hopefully make test runs more reproducible, instead of relying. It has the added benefit that our dependency specifications (previously DEPS.txt, now requirements.txt, for consistency with current packaging standards) are actually tested. As a free bonus, tox does coverage testing using coverage.py, too.

To post a comment you must log in.
Revision history for this message
Laurens Van Houtven (lvh) wrote :

Also, Axiom's version is now also available as __version__. This appears to be the more common place to specify the version. axiom.version still works, so this is not a backwards incompatible change. I still need to write a test to check that the two are identical, but I'm not sure where to put it: test_listversions seems the closest one, but not exactly a match :)

Revision history for this message
Laurens Van Houtven (lvh) wrote :

(Also, this patch doesn't actually make Axiom pip installable yet on a clean machine, as you can tell from tox.ini. To do that, I'm going to have to give Epsilon similar treatment.)

2728. By Laurens Van Houtven

Don't use setuptools

Revision history for this message
Jean-Paul Calderone (exarkun) wrote :

Thanks.

  1) The way setup.py and axiom/_version.py interact to convey version information seems more complex than necessary. This is of particular interest since there is no test coverage of setup.py's idea of the project version, I think. Since assembling strings is easier than parsing them, perhaps the version information should be available as a string literal in _version.py - whatever form is the absolute simplest for setup.py to get at - and it can be parsed into the Version object in _version.py (by unit tested code).

Oh, apparently that's my only comment. Please address, verify the result is sufficiently successful on continuous integration, and then merge.

Thanks again!

review: Needs Fixing
Revision history for this message
Laurens Van Houtven (lvh) wrote :

I'm a bit at a loss of how to make this better. A version number tuple (i.e. (1, 2, 3)) makes the regex essentially the same. An explicit version string would require parsing the string again (granted, *map(int, v.split(".") isn't the hardest parse in the world).

Revision history for this message
Tristan Seligmann (mithrandi) wrote :

+1 on this branch, aside from the issue exarkun raised above, but I'd like to suggest something else: dropping support for Python 2.4 and Python 2.5. This is probably a little out of scope for this branch, but since you're adding a requirements-py24.txt in this branch, I thought it might be easier just to get rid of it.

We don't actually seem to have an explict statement of Python version requirement anywhere now that DEPS.txt is gone; I guess the way to indicate this is with the appropriate classifiers?

review: Needs Fixing
2729. By Laurens Van Houtven

Address JP's version issues

2730. By Laurens Van Houtven

Remove Python 2.4 requirements file

2731. By Laurens Van Houtven

Update existing requirements file

2732. By Laurens Van Houtven

Test against Twisted trunk

2733. By Laurens Van Houtven

Add trove classifiers for supported Python versions

Revision history for this message
Laurens Van Houtven (lvh) wrote :

I've added the trove classifiers.

The tox.ini file now tests against py26 and py27, both for Twisted's latest release and Twisted trunk.

Revision history for this message
Tristan Seligmann (mithrandi) wrote :

I've noticed another issue with switching away from epsilon.setuphelper; it has a regeneratePluginCache hook to regenerate the Twisted plugin cache (actually, it looks for any directory of the form foo/plugins/ where foo/plugins/__init__.py does not exist). The new setup.py does not have this functionality; on the other hand, after reading https://twistedmatrix.com/trac/ticket/2410 I start to wonder if this functionality shouldn't just be dropped after all.

review: Needs Fixing
Revision history for this message
Tristan Seligmann (mithrandi) wrote :

To summarize: the new setup.py doesn't regenerate the Twisted plugin cache post-install the way epsilon.setuphelper does. This doesn't matter for distro packaging or user installs, but represents a regression for system-wide installs as the administrator will need to regenerate the plugin cache manually.

(Also, I don't know of a better way of doing this than the hack present in epsilon.setuphelper)

Revision history for this message
Jean-Paul Calderone (exarkun) wrote :

A better implementation than epsilon.setuphelper is probably to customize the `install` command. http://stackoverflow.com/questions/1321270/how-to-extend-distutils-with-a-simple-post-install-script shows how this can be done.

Revision history for this message
Laurens Van Houtven (lvh) wrote :

I'm trying to do this, but this section of distutils isn't really documented[1]. For one, epsilon.setuphelper.regeneratePluginCaches wants the distobj, but I'm not sure what the appropriate way to give it a reference to that is. Do I just do distobj = setup(...) and use that from in setup.py?

Either way, that thing seems pretty general, and I think it can all be boiled down to:

list(plugin.getPlugins(plugin.IPlugin, "axiom.plugins"))

maybe wrapped in a try except if modules have been removed. I'm not sure what happens if pip is dragging in Twisted as a dependency when installing Axiom.

[1]: http://docs.python.org/2/distutils/apiref.html#module-distutils.command

Revision history for this message
Laurens Van Houtven (lvh) wrote :

I am saddened:

https://gist.github.com/lvh/8ced76c8ba794e42fac3

I'm not entirely sure what the hell is going on there, but it appears that by default, setup.py install is using some *setuptools* stuff; that's the only way I can explain that that flag works, since it's something distutils doesn't know about.

Revision history for this message
Laurens Van Houtven (lvh) wrote :

Realized why. tox installs axiom into the virtualenv using pip, which uses setuptools. I guess when I said earlier that I could do this with distutils, that wasn't really true. Sorry.

2734. By Laurens Van Houtven

Explicitly use setuptools

2735. By Laurens Van Houtven

Post-install cache regeneration

2736. By Laurens Van Houtven

Merge trunk

2737. By Laurens Van Houtven

Merge new trunk (with removed performance tests that were causing failures on py27 + new sqlites)

Revision history for this message
Laurens Van Houtven (lvh) wrote :

I've fixed this by customizing the equivalent setuptools command.

This regenerates the cache using the command Tristan suggested. The only downside is that it now requires setuptools. On the plus side running the tests using tox now no longer relies on pip's deep distutils-masking magic :)

Please review.

Revision history for this message
Laurens Van Houtven (lvh) wrote :

Also, my new branch: https://code.launchpad.net/~lvh/divmod.org/axiom-docs adds a bunch of documentation and relies on this (or at least will) for automatically building and testing the documentation.

2738. By Laurens Van Houtven

Use Launchpad page as URL

Revision history for this message
Tristan Seligmann (mithrandi) wrote :

> list(plugin.getPlugins(plugin.IPlugin, "axiom.plugins"))

I haven't actually tried running this code yet, but I'm pretty sure it raises an exception. The second parameter to getPlugins() is an actual python module object, not a string. In other words, this code should be something like:

import axiom.plugins
list(plugin.getPlugins(plugin.IPlugin))
list(plugin.getPlugins(plugin.IPlugin, axiom.plugins))

(Note the two calls to getPlugins; we want to regenerate the cache for twisted.plugins as well as for axiom.plugins)

Alas, I suspect this approach may work fine when running 'setup.py install' as the old setup.py did, but fail during a pip installation (or during the building of a Debian/etc. package) as Axiom is not being installed into a location that is on the sys.path of the running Python interpreter.

review: Needs Fixing
2739. By Laurens Van Houtven

Fix cache regeneration logic

2740. By Laurens Van Houtven

Actually use the install class. Oops.

Revision history for this message
Laurens Van Houtven (lvh) wrote :

My bad. I've fixed it in the current version.

pip installs into new virtualenvs appear to work fine, now. I'm 100% sure the _regenerateCache function is being called (I know because I put a raise statement in there).

I'm trying now, rebuilding *all* the virtualenvs to make sure it's not a spuriour success due to a stale install of Axiom somewhere.

Revision history for this message
Laurens Van Houtven (lvh) wrote :

(tox -r succeeded too.)

2741. By Laurens Van Houtven

setup.py doesn't actually rely on the strip or triple-quoting anymore

2742. By Laurens Van Houtven

Add PyOpenSSL to install_requires

2743. By Laurens Van Houtven

Add note to eventually remove PyOpenSSL from install_requires

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'Axiom/.coveragerc'
2--- Axiom/.coveragerc 1970-01-01 00:00:00 +0000
3+++ Axiom/.coveragerc 2013-10-14 15:41:13 +0000
4@@ -0,0 +1,8 @@
5+[run]
6+branch = True
7+source =
8+ axiom
9+
10+[report]
11+exclude_lines =
12+ pragma: no cover
13
14=== modified file 'Axiom/axiom/__init__.py'
15--- Axiom/axiom/__init__.py 2008-03-09 21:03:23 +0000
16+++ Axiom/axiom/__init__.py 2013-10-14 15:41:13 +0000
17@@ -1,5 +1,8 @@
18 # -*- test-case-name: axiom.test -*-
19-
20-from axiom._version import version
21-version # tell pyflakes we're exporting it.
22-
23+from twisted.python import versions
24+from axiom._version import __version__
25+
26+def _asTwistedVersion(packageName, versionString):
27+ return versions.Version(packageName, *map(int, versionString.split(".")))
28+
29+version = _asTwistedVersion("axiom", __version__)
30
31=== modified file 'Axiom/axiom/_version.py'
32--- Axiom/axiom/_version.py 2009-11-30 01:08:55 +0000
33+++ Axiom/axiom/_version.py 2013-10-14 15:41:13 +0000
34@@ -1,3 +1,1 @@
35-# This is an auto-generated file. Use Epsilon/bin/release-divmod to update.
36-from twisted.python import versions
37-version = versions.Version(__name__[:__name__.rfind('.')], 0, 6, 0)
38+__version__ = "0.6.0"
39
40=== added file 'Axiom/requirements-testing.txt'
41--- Axiom/requirements-testing.txt 1970-01-01 00:00:00 +0000
42+++ Axiom/requirements-testing.txt 2013-10-14 15:41:13 +0000
43@@ -0,0 +1,2 @@
44+# Requirements for running the test suite using tox
45+coverage
46
47=== renamed file 'Axiom/DEPS.txt' => 'Axiom/requirements.txt'
48--- Axiom/DEPS.txt 2006-06-14 11:54:41 +0000
49+++ Axiom/requirements.txt 2013-10-14 15:41:13 +0000
50@@ -1,7 +1,5 @@
51-Python 2.4
52-SQLite 3.2.1
53-Twisted 2.4.0
54-PySQLite 2.0
55-NPTL 2.3.5 or later (LinuxThreads not supported: see
56- <http://twistedmatrix.com/bugs/issue1251>)
57-Epsilon 0.5.0
58+Twisted>=13.1.0
59+Epsilon>=0.6.0
60+PyOpenSSL>=0.13 # epsilon.juice depends on this
61+# You may also optionally install pysqlite2. If you don't, the stdlib
62+# sqlite module will be used.
63
64=== modified file 'Axiom/setup.py'
65--- Axiom/setup.py 2009-11-30 01:08:55 +0000
66+++ Axiom/setup.py 2013-10-14 15:41:13 +0000
67@@ -1,23 +1,59 @@
68-from epsilon.setuphelper import autosetup
69-
70-import axiom
71-
72-distobj = autosetup(
73+from setuptools import setup, find_packages
74+from setuptools.command.install import install as Install
75+import re
76+
77+versionPattern = re.compile(r"""^__version__ = ['"](.*?)['"]$""", re.M)
78+with open("axiom/_version.py", "rt") as f:
79+ version = versionPattern.search(f.read()).group(1)
80+
81+
82+
83+class InstallAndRegenerate(Install):
84+ def run(self):
85+ """
86+ Runs the usual install logic, then regenerates the plugin cache.
87+ """
88+ Install.run(self)
89+ _regenerateCache()
90+
91+
92+
93+def _regenerateCache():
94+ from twisted import plugin
95+ from axiom import plugins
96+ list(plugin.getPlugins(plugin.IPlugin)) # Twisted
97+ list(plugin.getPlugins(plugin.IPlugin, plugins)) # Axiom
98+
99+
100+setup(
101 name="Axiom",
102- version=axiom.version.short(),
103+ version=version,
104+ description="An in-process object-relational database",
105+ url="https://launchpad.net/divmod.org",
106+
107 maintainer="Divmod, Inc.",
108 maintainer_email="support@divmod.org",
109- url="http://divmod.org/trac/wiki/DivmodAxiom",
110+
111+ install_requires=["twisted", "epsilon", "PyOpenSSL"],
112+ # XXX: Remove PyOpenSSL install_requires as soon as epsilon
113+ # specifies this dependency
114+ packages=find_packages() + ['twisted.plugins'],
115+ scripts=['bin/axiomatic'],
116+ cmdclass={
117+ "install": InstallAndRegenerate,
118+ },
119+
120 license="MIT",
121 platforms=["any"],
122- description="An in-process object-relational database",
123+
124 classifiers=[
125 "Development Status :: 5 - Production/Stable",
126 "Framework :: Twisted",
127 "Intended Audience :: Developers",
128 "License :: OSI Approved :: MIT License",
129 "Programming Language :: Python",
130- "Topic :: Database"],
131-
132- scripts=['bin/axiomatic'])
133-
134+ "Programming Language :: Python :: 2",
135+ "Programming Language :: Python :: 2.6",
136+ "Programming Language :: Python :: 2.7",
137+ "Programming Language :: Python :: 2 :: Only",
138+ "Topic :: Database"])
139
140=== added file 'Axiom/tox.ini'
141--- Axiom/tox.ini 1970-01-01 00:00:00 +0000
142+++ Axiom/tox.ini 2013-10-14 15:41:13 +0000
143@@ -0,0 +1,24 @@
144+[tox]
145+envlist = py26,py27,py26-trunkdeps,py27-trunkdeps
146+
147+[testenv]
148+deps =
149+ Twisted
150+commands =
151+ pip install \
152+ -r {toxinidir}/requirements.txt \
153+ -r {toxinidir}/requirements-testing.txt
154+ coverage run {envdir}/bin/trial \
155+ --temp-directory={envdir}/_trial {posargs:axiom}
156+ coverage report --show-missing
157+ coverage html --directory {envdir}/_coverage
158+
159+[testenv:py26-trunkdeps]
160+basepython = python2.6
161+deps =
162+ --editable=svn+svn://svn.twistedmatrix.com/svn/Twisted/trunk
163+
164+[testenv:py27-trunkdeps]
165+basepython = python2.7
166+deps =
167+ --editable=svn+svn://svn.twistedmatrix.com/svn/Twisted/trunk

Subscribers

People subscribed via source and target branches