Merge lp:~allenap/storm/go-setuptools into lp:storm

Proposed by Gavin Panella
Status: Merged
Approved by: Free Ekanayaka
Approved revision: 417
Merged at revision: 421
Proposed branch: lp:~allenap/storm/go-setuptools
Merge into: lp:storm
Diff against target: 661 lines (+446/-94)
7 files modified
.bzrignore (+3/-0)
Makefile (+9/-4)
README (+19/-2)
ez_setup.py (+284/-0)
setup.py (+33/-20)
test (+11/-68)
tests/__init__.py (+87/-0)
To merge this branch: bzr merge lp:~allenap/storm/go-setuptools
Reviewer Review Type Date Requested Status
Free Ekanayaka (community) Approve
Raphaël Badin code* Approve
Review via email: mp+79891@code.launchpad.net

Description of the change

Switch everything to use setuptools, and define all test dependencies
in setup.py.

I've switched make check over to use setuptools' testing support, and
I've also made the ./test script use the eggs that setuptools
downloads. There's also an additional develop Makefile target that
downloads the test dependencies without running the tests.

This means you can do make check from a fresh branch of Storm and run
*all* the tests without further intervention (other than doing the
one-off package installations and database set-up).

This is here to support the follow-up branch:

  lp:~allenap/storm/oneiric-admin-shutdown-bug-871596

I think this branch is really useful because it makes it trivial to
run the full test suite from a freshly checked out branch. With some
additional work to use van.pg for PostgreSQL cluster set-up (and
hopefully something similar for MySQL) it seriously lowers the barrier
for contribution.

This branch will not be landed independently; this split is solely to
aid review.

To post a comment you must log in.
Revision history for this message
Raphaël Badin (rvb) wrote :

I'm no setuptools expert but this branch looks good and will definitely lower the barrier for contribution. Most of the added code is simply boilerplate setuptools code (ez_setup.py).

[1]

As discussed on IRC, I think the python-mysqldb package can also be "eggified".

review: Approve (code*)
lp:~allenap/storm/go-setuptools updated
417. By Gavin Panella

Move python-mysqldb into the test dependencies section in README.

Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

This is indeed helpful, +1.

review: Approve
lp:~allenap/storm/go-setuptools updated
418. By Gavin Panella

Revert r417; MySQLdb-python is not reliably installable from PyPI.

419. By Gavin Panella

Merge lp:storm.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2009-10-08 07:38:11 +0000
3+++ .bzrignore 2011-10-28 13:28:23 +0000
4@@ -11,3 +11,6 @@
5 debian/python-storm.substvars
6 apidoc
7 _trial_temp
8+*.egg
9+TAGS
10+tags
11
12=== modified file 'Makefile'
13--- Makefile 2009-11-24 18:34:34 +0000
14+++ Makefile 2011-10-28 13:28:23 +0000
15@@ -1,7 +1,7 @@
16 PYTHON ?= python
17 PYDOCTOR ?= pydoctor
18
19-TEST_COMMAND = $(PYTHON) test
20+TEST_COMMAND = $(PYTHON) setup.py test
21
22 STORM_POSTGRES_URI = postgres:storm_test
23 STORM_POSTGRES_HOST_URI = postgres://localhost/storm_test
24@@ -18,8 +18,11 @@
25 build:
26 $(PYTHON) setup.py build_ext -i
27
28-check: build
29- # Run the tests once with C extensions and once without them.
30+develop:
31+ $(TEST_COMMAND) --quiet --dry-run
32+
33+check:
34+ @ # Run the tests once with C extensions and once without them.
35 $(TEST_COMMAND) && STORM_CEXTENSIONS=0 $(TEST_COMMAND)
36
37 doc:
38@@ -36,8 +39,10 @@
39 rm -rf debian/files
40 rm -rf debian/python-storm
41 rm -rf debian/python-storm.*
42+ rm -rf *.egg
43+ rm -rf _trial_temp
44 find . -name "*.so" -type f -exec rm -f {} \;
45 find . -name "*.pyc" -type f -exec rm -f {} \;
46 find . -name "*~" -type f -exec rm -f {} \;
47
48-.PHONY: all build test
49+.PHONY: all build check clean develop doc release
50
51=== modified file 'README'
52--- README 2009-10-08 07:23:43 +0000
53+++ README 2011-10-28 13:28:23 +0000
54@@ -98,8 +98,9 @@
55 you will need to install MySQL and PostgreSQL, along with the
56 related Python database drivers:
57
58- $ sudo apt-get install python-mysqldb python-psycopg2 mysql-server \
59- postgresql build-essential
60+ $ sudo apt-get install \
61+ python-mysqldb mysql-server \
62+ postgresql build-essential
63
64 These will take a few minutes to download (its a bit under 200MB all
65 together). Once the download is complete, a screen called
66@@ -110,6 +111,22 @@
67 asked to enter a password multiple times. Leave it blank in each
68 case.
69
70+The Python dependencies for running tests can mostly be installed with
71+apt-get:
72+
73+ $ apt-get install \
74+ python-django python-psycopg2 python-testresources \
75+ python-transaction python-twisted python-zope.component \
76+ python-zope.security
77+
78+Alternatively, dependencies can be downloaded as eggs into the current
79+directory with:
80+
81+ $ make develop
82+
83+This ensures that all dependencies are available, downloading from
84+PyPI as appropriate.
85+
86 Setting up database users and access security
87 ---------------------------------------------
88
89
90=== added file 'ez_setup.py'
91--- ez_setup.py 1970-01-01 00:00:00 +0000
92+++ ez_setup.py 2011-10-28 13:28:23 +0000
93@@ -0,0 +1,284 @@
94+#!python
95+"""Bootstrap setuptools installation
96+
97+If you want to use setuptools in your package's setup.py, just include this
98+file in the same directory with it, and add this to the top of your setup.py::
99+
100+ from ez_setup import use_setuptools
101+ use_setuptools()
102+
103+If you want to require a specific version of setuptools, set a download
104+mirror, or use an alternate download directory, you can do so by supplying
105+the appropriate options to ``use_setuptools()``.
106+
107+This file can also be run as a script to install or upgrade setuptools.
108+"""
109+import sys
110+DEFAULT_VERSION = "0.6c11"
111+DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
112+
113+md5_data = {
114+ 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
115+ 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
116+ 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
117+ 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
118+ 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
119+ 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
120+ 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
121+ 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
122+ 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
123+ 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
124+ 'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090',
125+ 'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4',
126+ 'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7',
127+ 'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5',
128+ 'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de',
129+ 'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b',
130+ 'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2',
131+ 'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086',
132+ 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
133+ 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
134+ 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
135+ 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
136+ 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
137+ 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
138+ 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
139+ 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
140+ 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
141+ 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
142+ 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
143+ 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
144+ 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
145+ 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
146+ 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
147+ 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
148+ 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
149+ 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
150+ 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
151+ 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
152+ 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
153+ 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
154+ 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
155+ 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
156+}
157+
158+import sys, os
159+try: from hashlib import md5
160+except ImportError: from md5 import md5
161+
162+def _validate_md5(egg_name, data):
163+ if egg_name in md5_data:
164+ digest = md5(data).hexdigest()
165+ if digest != md5_data[egg_name]:
166+ print >>sys.stderr, (
167+ "md5 validation of %s failed! (Possible download problem?)"
168+ % egg_name
169+ )
170+ sys.exit(2)
171+ return data
172+
173+def use_setuptools(
174+ version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
175+ download_delay=15
176+):
177+ """Automatically find/download setuptools and make it available on sys.path
178+
179+ `version` should be a valid setuptools version number that is available
180+ as an egg for download under the `download_base` URL (which should end with
181+ a '/'). `to_dir` is the directory where setuptools will be downloaded, if
182+ it is not already available. If `download_delay` is specified, it should
183+ be the number of seconds that will be paused before initiating a download,
184+ should one be required. If an older version of setuptools is installed,
185+ this routine will print a message to ``sys.stderr`` and raise SystemExit in
186+ an attempt to abort the calling script.
187+ """
188+ was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
189+ def do_download():
190+ egg = download_setuptools(version, download_base, to_dir, download_delay)
191+ sys.path.insert(0, egg)
192+ import setuptools; setuptools.bootstrap_install_from = egg
193+ try:
194+ import pkg_resources
195+ except ImportError:
196+ return do_download()
197+ try:
198+ pkg_resources.require("setuptools>="+version); return
199+ except pkg_resources.VersionConflict, e:
200+ if was_imported:
201+ print >>sys.stderr, (
202+ "The required version of setuptools (>=%s) is not available, and\n"
203+ "can't be installed while this script is running. Please install\n"
204+ " a more recent version first, using 'easy_install -U setuptools'."
205+ "\n\n(Currently using %r)"
206+ ) % (version, e.args[0])
207+ sys.exit(2)
208+ except pkg_resources.DistributionNotFound:
209+ pass
210+
211+ del pkg_resources, sys.modules['pkg_resources'] # reload ok
212+ return do_download()
213+
214+def download_setuptools(
215+ version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
216+ delay = 15
217+):
218+ """Download setuptools from a specified location and return its filename
219+
220+ `version` should be a valid setuptools version number that is available
221+ as an egg for download under the `download_base` URL (which should end
222+ with a '/'). `to_dir` is the directory where the egg will be downloaded.
223+ `delay` is the number of seconds to pause before an actual download attempt.
224+ """
225+ import urllib2, shutil
226+ egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
227+ url = download_base + egg_name
228+ saveto = os.path.join(to_dir, egg_name)
229+ src = dst = None
230+ if not os.path.exists(saveto): # Avoid repeated downloads
231+ try:
232+ from distutils import log
233+ if delay:
234+ log.warn("""
235+---------------------------------------------------------------------------
236+This script requires setuptools version %s to run (even to display
237+help). I will attempt to download it for you (from
238+%s), but
239+you may need to enable firewall access for this script first.
240+I will start the download in %d seconds.
241+
242+(Note: if this machine does not have network access, please obtain the file
243+
244+ %s
245+
246+and place it in this directory before rerunning this script.)
247+---------------------------------------------------------------------------""",
248+ version, download_base, delay, url
249+ ); from time import sleep; sleep(delay)
250+ log.warn("Downloading %s", url)
251+ src = urllib2.urlopen(url)
252+ # Read/write all in one block, so we don't create a corrupt file
253+ # if the download is interrupted.
254+ data = _validate_md5(egg_name, src.read())
255+ dst = open(saveto,"wb"); dst.write(data)
256+ finally:
257+ if src: src.close()
258+ if dst: dst.close()
259+ return os.path.realpath(saveto)
260+
261+
262+
263+
264+
265+
266+
267+
268+
269+
270+
271+
272+
273+
274+
275+
276+
277+
278+
279+
280+
281+
282+
283+
284+
285+
286+
287+
288+
289+
290+
291+
292+
293+
294+
295+
296+def main(argv, version=DEFAULT_VERSION):
297+ """Install or upgrade setuptools and EasyInstall"""
298+ try:
299+ import setuptools
300+ except ImportError:
301+ egg = None
302+ try:
303+ egg = download_setuptools(version, delay=0)
304+ sys.path.insert(0,egg)
305+ from setuptools.command.easy_install import main
306+ return main(list(argv)+[egg]) # we're done here
307+ finally:
308+ if egg and os.path.exists(egg):
309+ os.unlink(egg)
310+ else:
311+ if setuptools.__version__ == '0.0.1':
312+ print >>sys.stderr, (
313+ "You have an obsolete version of setuptools installed. Please\n"
314+ "remove it from your system entirely before rerunning this script."
315+ )
316+ sys.exit(2)
317+
318+ req = "setuptools>="+version
319+ import pkg_resources
320+ try:
321+ pkg_resources.require(req)
322+ except pkg_resources.VersionConflict:
323+ try:
324+ from setuptools.command.easy_install import main
325+ except ImportError:
326+ from easy_install import main
327+ main(list(argv)+[download_setuptools(delay=0)])
328+ sys.exit(0) # try to force an exit
329+ else:
330+ if argv:
331+ from setuptools.command.easy_install import main
332+ main(argv)
333+ else:
334+ print "Setuptools version",version,"or greater has been installed."
335+ print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
336+
337+def update_md5(filenames):
338+ """Update our built-in md5 registry"""
339+
340+ import re
341+
342+ for name in filenames:
343+ base = os.path.basename(name)
344+ f = open(name,'rb')
345+ md5_data[base] = md5(f.read()).hexdigest()
346+ f.close()
347+
348+ data = [" %r: %r,\n" % it for it in md5_data.items()]
349+ data.sort()
350+ repl = "".join(data)
351+
352+ import inspect
353+ srcfile = inspect.getsourcefile(sys.modules[__name__])
354+ f = open(srcfile, 'rb'); src = f.read(); f.close()
355+
356+ match = re.search("\nmd5_data = {\n([^}]+)}", src)
357+ if not match:
358+ print >>sys.stderr, "Internal error!"
359+ sys.exit(2)
360+
361+ src = src[:match.start(1)] + repl + src[match.end(1):]
362+ f = open(srcfile,'w')
363+ f.write(src)
364+ f.close()
365+
366+
367+if __name__=='__main__':
368+ if len(sys.argv)>2 and sys.argv[1]=='--md5update':
369+ update_md5(sys.argv[2:])
370+ else:
371+ main(sys.argv[1:])
372+
373+
374+
375+
376+
377+
378
379=== modified file 'setup.py'
380--- setup.py 2011-08-31 17:04:09 +0000
381+++ setup.py 2011-10-28 13:28:23 +0000
382@@ -2,10 +2,10 @@
383 import os
384 import re
385
386-try:
387- from setuptools import setup, Extension
388-except ImportError:
389- from distutils.core import setup, Extension
390+import ez_setup
391+ez_setup.use_setuptools()
392+
393+from setuptools import setup, Extension, find_packages
394
395
396 if os.path.isfile("MANIFEST"):
397@@ -19,20 +19,12 @@
398 open("storm/__init__.py").read()).group(1)
399
400
401-def find_packages():
402- # implement a simple find_packages so we don't have to depend on
403- # setuptools
404- packages = []
405- for directory, subdirectories, files in os.walk("storm"):
406- if '__init__.py' in files:
407- packages.append(directory.replace(os.sep, '.'))
408- return packages
409-
410-
411 setup(
412 name="storm",
413 version=VERSION,
414- description="Storm is an object-relational mapper (ORM) for Python developed at Canonical.",
415+ description=(
416+ "Storm is an object-relational mapper (ORM) for Python "
417+ "developed at Canonical."),
418 author="Gustavo Niemeyer",
419 author_email="gustavo@niemeyer.net",
420 maintainer="Storm Developers",
421@@ -41,18 +33,39 @@
422 url="https://storm.canonical.com",
423 download_url="https://launchpad.net/storm/+download",
424 packages=find_packages(),
425- zip_safe=False,
426- include_package_data=True,
427 package_data={"": ["*.zcml"]},
428 classifiers=[
429 "Development Status :: 5 - Production/Stable",
430 "Intended Audience :: Developers",
431- "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
432+ ("License :: OSI Approved :: GNU Library or "
433+ "Lesser General Public License (LGPL)"),
434 "Programming Language :: Python",
435 "Topic :: Database",
436 "Topic :: Database :: Front-Ends",
437 "Topic :: Software Development :: Libraries :: Python Modules",
438 ],
439 ext_modules=(BUILD_CEXTENSIONS and
440- [Extension("storm.cextensions", ["storm/cextensions.c"])])
441-)
442+ [Extension("storm.cextensions", ["storm/cextensions.c"])]),
443+ # The following options are specific to setuptools but ignored (with a
444+ # warning) by distutils.
445+ include_package_data=True,
446+ zip_safe=False,
447+ test_suite = "tests.find_tests",
448+ tests_require=[
449+ # Versions based on Lucid, where packaged.
450+ "django >= 1.1.1",
451+ "psycopg2 >= 2.0.13",
452+ "testresources >= 0.2.4",
453+ # timeline is not yet packaged in Ubuntu.
454+ "timeline >= 0.0.2",
455+ "transaction >= 1.0.0",
456+ "twisted >= 10.0.0",
457+ "zope.component >= 3.8.0",
458+ # zope.component 3.11.0 requires a version of zope.interface that no
459+ # version of Ubuntu yet packages. The following rule exists for the
460+ # sake of convenience rather than necessity, for the situation where
461+ # zope.interface is installed via a package but zope.component is not.
462+ "zope.component < 3.11.0",
463+ "zope.security >= 3.7.2",
464+ ],
465+ )
466
467=== modified file 'test'
468--- test 2011-09-13 10:43:40 +0000
469+++ test 2011-10-28 13:28:23 +0000
470@@ -19,6 +19,7 @@
471 # You should have received a copy of the GNU Lesser General Public License
472 # along with this program. If not, see <http://www.gnu.org/licenses/>.
473 #
474+import glob
475 import optparse
476 import unittest
477 import doctest
478@@ -28,73 +29,11 @@
479 import tests
480
481
482-def find_tests(testpaths=()):
483- """Find all test paths, or test paths contained in the provided sequence.
484-
485- @param testpaths: If provided, only tests in the given sequence will
486- be considered. If not provided, all tests are
487- considered.
488- @return: a test suite containing the requested tests.
489- """
490- suite = unittest.TestSuite()
491- topdir = os.path.abspath(os.path.dirname(__file__))
492- testdir = os.path.dirname(tests.__file__)
493- testpaths = set(testpaths)
494- for root, dirnames, filenames in os.walk(testdir):
495- for filename in filenames:
496- filepath = os.path.join(root, filename)
497- relpath = filepath[len(topdir)+1:]
498-
499- if (filename == "__init__.py" or filename.endswith(".pyc") or
500- relpath == os.path.join("tests", "conftest.py")):
501- # Skip non-tests.
502- continue
503-
504- if testpaths:
505- # Skip any tests not in testpaths.
506- for testpath in testpaths:
507- if relpath.startswith(testpath):
508- break
509- else:
510- continue
511-
512- if filename.endswith(".py"):
513- modpath = relpath.replace(os.path.sep, ".")[:-3]
514- module = __import__(modpath, None, None, [""])
515- suite.addTest(
516- unittest.defaultTestLoader.loadTestsFromModule(module))
517- elif filename.endswith(".txt"):
518- load_test = True
519- if relpath == os.path.join("tests", "zope", "README.txt"):
520- # Special case the inclusion of the Zope-dependent
521- # ZStorm doctest.
522- import tests.zope as ztest
523- load_test = (
524- ztest.has_transaction and
525- ztest.has_zope_component and
526- ztest.has_zope_security)
527- if load_test:
528- parent_path = os.path.dirname(relpath).replace(
529- os.path.sep, ".")
530- parent_module = __import__(parent_path, None, None, [""])
531- suite.addTest(doctest.DocFileSuite(
532- os.path.basename(relpath),
533- module_relative=True,
534- package=parent_module,
535- optionflags=doctest.ELLIPSIS))
536-
537- return suite
538-
539-
540-def parse_sys_argv():
541- """Extract any arguments not starting with '-' from sys.argv."""
542- testpaths = []
543- for i in range(len(sys.argv)-1,0,-1):
544- arg = sys.argv[i]
545- if not arg.startswith("-"):
546- testpaths.append(arg)
547- del sys.argv[i]
548- return testpaths
549+def add_eggs_to_path():
550+ here = os.path.dirname(__file__)
551+ egg_paths = glob.glob(os.path.join(here, "*.egg"))
552+ sys.path[:0] = map(os.path.abspath, egg_paths)
553+
554
555 def test_with_runner(runner):
556 usage = "test.py [options] [<test filename>, ...]"
557@@ -107,7 +46,11 @@
558 if opts.verbose:
559 runner.verbosity = 2
560
561- suite = find_tests(args)
562+ # python setup.py test [--dry-run] puts $package.egg directories in the
563+ # top directory, so we add them to sys.path here for convenience.
564+ add_eggs_to_path()
565+
566+ suite = tests.find_tests(args)
567 result = runner.run(suite)
568 return not result.wasSuccessful()
569
570
571=== modified file 'tests/__init__.py'
572--- tests/__init__.py 2008-06-20 14:08:30 +0000
573+++ tests/__init__.py 2011-10-28 13:28:23 +0000
574@@ -0,0 +1,87 @@
575+#
576+# Copyright (c) 2011 Canonical
577+#
578+# Written by Gustavo Niemeyer <gustavo@niemeyer.net>
579+#
580+# This file is part of Storm Object Relational Mapper.
581+#
582+# Storm is free software; you can redistribute it and/or modify
583+# it under the terms of the GNU Lesser General Public License as
584+# published by the Free Software Foundation; either version 2.1 of
585+# the License, or (at your option) any later version.
586+#
587+# Storm is distributed in the hope that it will be useful,
588+# but WITHOUT ANY WARRANTY; without even the implied warranty of
589+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
590+# GNU Lesser General Public License for more details.
591+#
592+# You should have received a copy of the GNU Lesser General Public License
593+# along with this program. If not, see <http://www.gnu.org/licenses/>.
594+#
595+
596+__all__ = [
597+ 'find_tests',
598+ ]
599+
600+import doctest
601+import os
602+import unittest
603+
604+
605+def find_tests(testpaths=()):
606+ """Find all test paths, or test paths contained in the provided sequence.
607+
608+ @param testpaths: If provided, only tests in the given sequence will
609+ be considered. If not provided, all tests are
610+ considered.
611+ @return: a test suite containing the requested tests.
612+ """
613+ suite = unittest.TestSuite()
614+ topdir = os.path.abspath(
615+ os.path.join(os.path.dirname(__file__), os.pardir))
616+ testdir = os.path.dirname(__file__)
617+ testpaths = set(testpaths)
618+ for root, dirnames, filenames in os.walk(testdir):
619+ for filename in filenames:
620+ filepath = os.path.join(root, filename)
621+ relpath = filepath[len(topdir)+1:]
622+
623+ if (filename == "__init__.py" or filename.endswith(".pyc") or
624+ relpath == os.path.join("tests", "conftest.py")):
625+ # Skip non-tests.
626+ continue
627+
628+ if testpaths:
629+ # Skip any tests not in testpaths.
630+ for testpath in testpaths:
631+ if relpath.startswith(testpath):
632+ break
633+ else:
634+ continue
635+
636+ if filename.endswith(".py"):
637+ modpath = relpath.replace(os.path.sep, ".")[:-3]
638+ module = __import__(modpath, None, None, [""])
639+ suite.addTest(
640+ unittest.defaultTestLoader.loadTestsFromModule(module))
641+ elif filename.endswith(".txt"):
642+ load_test = True
643+ if relpath == os.path.join("tests", "zope", "README.txt"):
644+ # Special case the inclusion of the Zope-dependent
645+ # ZStorm doctest.
646+ import tests.zope as ztest
647+ load_test = (
648+ ztest.has_transaction and
649+ ztest.has_zope_component and
650+ ztest.has_zope_security)
651+ if load_test:
652+ parent_path = os.path.dirname(relpath).replace(
653+ os.path.sep, ".")
654+ parent_module = __import__(parent_path, None, None, [""])
655+ suite.addTest(doctest.DocFileSuite(
656+ os.path.basename(relpath),
657+ module_relative=True,
658+ package=parent_module,
659+ optionflags=doctest.ELLIPSIS))
660+
661+ return suite

Subscribers

People subscribed via source and target branches

to status/vote changes: