aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/api_tests.txt424
-rw-r--r--tests/manual_test.py108
-rw-r--r--tests/test_ez_setup.py63
-rw-r--r--tests/test_pkg_resources.py61
4 files changed, 656 insertions, 0 deletions
diff --git a/tests/api_tests.txt b/tests/api_tests.txt
new file mode 100644
index 00000000..d03da14e
--- /dev/null
+++ b/tests/api_tests.txt
@@ -0,0 +1,424 @@
+Pluggable Distributions of Python Software
+==========================================
+
+Distributions
+-------------
+
+A "Distribution" is a collection of files that represent a "Release" of a
+"Project" as of a particular point in time, denoted by a
+"Version"::
+
+ >>> import sys, pkg_resources
+ >>> from pkg_resources import Distribution
+ >>> Distribution(project_name="Foo", version="1.2")
+ Foo 1.2
+
+Distributions have a location, which can be a filename, URL, or really anything
+else you care to use::
+
+ >>> dist = Distribution(
+ ... location="http://example.com/something",
+ ... project_name="Bar", version="0.9"
+ ... )
+
+ >>> dist
+ Bar 0.9 (http://example.com/something)
+
+
+Distributions have various introspectable attributes::
+
+ >>> dist.location
+ 'http://example.com/something'
+
+ >>> dist.project_name
+ 'Bar'
+
+ >>> dist.version
+ '0.9'
+
+ >>> dist.py_version == sys.version[:3]
+ True
+
+ >>> print dist.platform
+ None
+
+Including various computed attributes::
+
+ >>> from pkg_resources import parse_version
+ >>> dist.parsed_version == parse_version(dist.version)
+ True
+
+ >>> dist.key # case-insensitive form of the project name
+ 'bar'
+
+Distributions are compared (and hashed) by version first::
+
+ >>> Distribution(version='1.0') == Distribution(version='1.0')
+ True
+ >>> Distribution(version='1.0') == Distribution(version='1.1')
+ False
+ >>> Distribution(version='1.0') < Distribution(version='1.1')
+ True
+
+but also by project name (case-insensitive), platform, Python version,
+location, etc.::
+
+ >>> Distribution(project_name="Foo",version="1.0") == \
+ ... Distribution(project_name="Foo",version="1.0")
+ True
+
+ >>> Distribution(project_name="Foo",version="1.0") == \
+ ... Distribution(project_name="foo",version="1.0")
+ True
+
+ >>> Distribution(project_name="Foo",version="1.0") == \
+ ... Distribution(project_name="Foo",version="1.1")
+ False
+
+ >>> Distribution(project_name="Foo",py_version="2.3",version="1.0") == \
+ ... Distribution(project_name="Foo",py_version="2.4",version="1.0")
+ False
+
+ >>> Distribution(location="spam",version="1.0") == \
+ ... Distribution(location="spam",version="1.0")
+ True
+
+ >>> Distribution(location="spam",version="1.0") == \
+ ... Distribution(location="baz",version="1.0")
+ False
+
+
+
+Hash and compare distribution by prio/plat
+
+Get version from metadata
+provider capabilities
+egg_name()
+as_requirement()
+from_location, from_filename (w/path normalization)
+
+Releases may have zero or more "Requirements", which indicate
+what releases of another project the release requires in order to
+function. A Requirement names the other project, expresses some criteria
+as to what releases of that project are acceptable, and lists any "Extras"
+that the requiring release may need from that project. (An Extra is an
+optional feature of a Release, that can only be used if its additional
+Requirements are satisfied.)
+
+
+
+The Working Set
+---------------
+
+A collection of active distributions is called a Working Set. Note that a
+Working Set can contain any importable distribution, not just pluggable ones.
+For example, the Python standard library is an importable distribution that
+will usually be part of the Working Set, even though it is not pluggable.
+Similarly, when you are doing development work on a project, the files you are
+editing are also a Distribution. (And, with a little attention to the
+directory names used, and including some additional metadata, such a
+"development distribution" can be made pluggable as well.)
+
+ >>> from pkg_resources import WorkingSet
+
+A working set's entries are the sys.path entries that correspond to the active
+distributions. By default, the working set's entries are the items on
+``sys.path``::
+
+ >>> ws = WorkingSet()
+ >>> ws.entries == sys.path
+ True
+
+But you can also create an empty working set explicitly, and add distributions
+to it::
+
+ >>> ws = WorkingSet([])
+ >>> ws.add(dist)
+ >>> ws.entries
+ ['http://example.com/something']
+ >>> dist in ws
+ True
+ >>> Distribution('foo',version="") in ws
+ False
+
+And you can iterate over its distributions::
+
+ >>> list(ws)
+ [Bar 0.9 (http://example.com/something)]
+
+Adding the same distribution more than once is a no-op::
+
+ >>> ws.add(dist)
+ >>> list(ws)
+ [Bar 0.9 (http://example.com/something)]
+
+For that matter, adding multiple distributions for the same project also does
+nothing, because a working set can only hold one active distribution per
+project -- the first one added to it::
+
+ >>> ws.add(
+ ... Distribution(
+ ... 'http://example.com/something', project_name="Bar",
+ ... version="7.2"
+ ... )
+ ... )
+ >>> list(ws)
+ [Bar 0.9 (http://example.com/something)]
+
+You can append a path entry to a working set using ``add_entry()``::
+
+ >>> ws.entries
+ ['http://example.com/something']
+ >>> ws.add_entry(pkg_resources.__file__)
+ >>> ws.entries
+ ['http://example.com/something', '...pkg_resources.py...']
+
+Multiple additions result in multiple entries, even if the entry is already in
+the working set (because ``sys.path`` can contain the same entry more than
+once)::
+
+ >>> ws.add_entry(pkg_resources.__file__)
+ >>> ws.entries
+ ['...example.com...', '...pkg_resources...', '...pkg_resources...']
+
+And you can specify the path entry a distribution was found under, using the
+optional second parameter to ``add()``::
+
+ >>> ws = WorkingSet([])
+ >>> ws.add(dist,"foo")
+ >>> ws.entries
+ ['foo']
+
+But even if a distribution is found under multiple path entries, it still only
+shows up once when iterating the working set:
+
+ >>> ws.add_entry(ws.entries[0])
+ >>> list(ws)
+ [Bar 0.9 (http://example.com/something)]
+
+You can ask a WorkingSet to ``find()`` a distribution matching a requirement::
+
+ >>> from pkg_resources import Requirement
+ >>> print ws.find(Requirement.parse("Foo==1.0")) # no match, return None
+ None
+
+ >>> ws.find(Requirement.parse("Bar==0.9")) # match, return distribution
+ Bar 0.9 (http://example.com/something)
+
+Note that asking for a conflicting version of a distribution already in a
+working set triggers a ``pkg_resources.VersionConflict`` error:
+
+ >>> try:
+ ... ws.find(Requirement.parse("Bar==1.0"))
+ ... except pkg_resources.VersionConflict:
+ ... exc = sys.exc_info()[1]
+ ... print(str(exc))
+ ... else:
+ ... raise AssertionError("VersionConflict was not raised")
+ (Bar 0.9 (http://example.com/something), Requirement.parse('Bar==1.0'))
+
+You can subscribe a callback function to receive notifications whenever a new
+distribution is added to a working set. The callback is immediately invoked
+once for each existing distribution in the working set, and then is called
+again for new distributions added thereafter::
+
+ >>> def added(dist): print "Added", dist
+ >>> ws.subscribe(added)
+ Added Bar 0.9
+ >>> foo12 = Distribution(project_name="Foo", version="1.2", location="f12")
+ >>> ws.add(foo12)
+ Added Foo 1.2
+
+Note, however, that only the first distribution added for a given project name
+will trigger a callback, even during the initial ``subscribe()`` callback::
+
+ >>> foo14 = Distribution(project_name="Foo", version="1.4", location="f14")
+ >>> ws.add(foo14) # no callback, because Foo 1.2 is already active
+
+ >>> ws = WorkingSet([])
+ >>> ws.add(foo12)
+ >>> ws.add(foo14)
+ >>> ws.subscribe(added)
+ Added Foo 1.2
+
+And adding a callback more than once has no effect, either::
+
+ >>> ws.subscribe(added) # no callbacks
+
+ # and no double-callbacks on subsequent additions, either
+ >>> just_a_test = Distribution(project_name="JustATest", version="0.99")
+ >>> ws.add(just_a_test)
+ Added JustATest 0.99
+
+
+Finding Plugins
+---------------
+
+``WorkingSet`` objects can be used to figure out what plugins in an
+``Environment`` can be loaded without any resolution errors::
+
+ >>> from pkg_resources import Environment
+
+ >>> plugins = Environment([]) # normally, a list of plugin directories
+ >>> plugins.add(foo12)
+ >>> plugins.add(foo14)
+ >>> plugins.add(just_a_test)
+
+In the simplest case, we just get the newest version of each distribution in
+the plugin environment::
+
+ >>> ws = WorkingSet([])
+ >>> ws.find_plugins(plugins)
+ ([JustATest 0.99, Foo 1.4 (f14)], {})
+
+But if there's a problem with a version conflict or missing requirements, the
+method falls back to older versions, and the error info dict will contain an
+exception instance for each unloadable plugin::
+
+ >>> ws.add(foo12) # this will conflict with Foo 1.4
+ >>> ws.find_plugins(plugins)
+ ([JustATest 0.99, Foo 1.2 (f12)], {Foo 1.4 (f14): VersionConflict(...)})
+
+But if you disallow fallbacks, the failed plugin will be skipped instead of
+trying older versions::
+
+ >>> ws.find_plugins(plugins, fallback=False)
+ ([JustATest 0.99], {Foo 1.4 (f14): VersionConflict(...)})
+
+
+
+Platform Compatibility Rules
+----------------------------
+
+On the Mac, there are potential compatibility issues for modules compiled
+on newer versions of Mac OS X than what the user is running. Additionally,
+Mac OS X will soon have two platforms to contend with: Intel and PowerPC.
+
+Basic equality works as on other platforms::
+
+ >>> from pkg_resources import compatible_platforms as cp
+ >>> reqd = 'macosx-10.4-ppc'
+ >>> cp(reqd, reqd)
+ True
+ >>> cp("win32", reqd)
+ False
+
+Distributions made on other machine types are not compatible::
+
+ >>> cp("macosx-10.4-i386", reqd)
+ False
+
+Distributions made on earlier versions of the OS are compatible, as
+long as they are from the same top-level version. The patchlevel version
+number does not matter::
+
+ >>> cp("macosx-10.4-ppc", reqd)
+ True
+ >>> cp("macosx-10.3-ppc", reqd)
+ True
+ >>> cp("macosx-10.5-ppc", reqd)
+ False
+ >>> cp("macosx-9.5-ppc", reqd)
+ False
+
+Backwards compatibility for packages made via earlier versions of
+setuptools is provided as well::
+
+ >>> cp("darwin-8.2.0-Power_Macintosh", reqd)
+ True
+ >>> cp("darwin-7.2.0-Power_Macintosh", reqd)
+ True
+ >>> cp("darwin-8.2.0-Power_Macintosh", "macosx-10.3-ppc")
+ False
+
+
+Environment Markers
+-------------------
+
+ >>> from pkg_resources import invalid_marker as im, evaluate_marker as em
+ >>> import os
+
+ >>> print(im("sys_platform"))
+ Comparison or logical expression expected
+
+ >>> print(im("sys_platform==")) # doctest: +ELLIPSIS
+ unexpected EOF while parsing (...line 1)
+
+ >>> print(im("sys_platform=='win32'"))
+ False
+
+ >>> print(im("sys=='x'"))
+ Unknown name 'sys'
+
+ >>> print(im("(extra)"))
+ Comparison or logical expression expected
+
+ >>> print(im("(extra")) # doctest: +ELLIPSIS
+ unexpected EOF while parsing (...line 1)
+
+ >>> print(im("os.open('foo')=='y'"))
+ Language feature not supported in environment markers
+
+ >>> print(im("'x'=='y' and os.open('foo')=='y'")) # no short-circuit!
+ Language feature not supported in environment markers
+
+ >>> print(im("'x'=='x' or os.open('foo')=='y'")) # no short-circuit!
+ Language feature not supported in environment markers
+
+ >>> print(im("'x' < 'y'"))
+ '<' operator not allowed in environment markers
+
+ >>> print(im("'x' < 'y' < 'z'"))
+ Chained comparison not allowed in environment markers
+
+ >>> print(im("r'x'=='x'"))
+ Only plain strings allowed in environment markers
+
+ >>> print(im("'''x'''=='x'"))
+ Only plain strings allowed in environment markers
+
+ >>> print(im('"""x"""=="x"'))
+ Only plain strings allowed in environment markers
+
+ >>> print(im(r"'x\n'=='x'"))
+ Only plain strings allowed in environment markers
+
+ >>> print(im("os.open=='y'"))
+ Language feature not supported in environment markers
+
+ >>> em('"x"=="x"')
+ True
+
+ >>> em('"x"=="y"')
+ False
+
+ >>> em('"x"=="y" and "x"=="x"')
+ False
+
+ >>> em('"x"=="y" or "x"=="x"')
+ True
+
+ >>> em('"x"=="y" and "x"=="q" or "z"=="z"')
+ True
+
+ >>> em('"x"=="y" and ("x"=="q" or "z"=="z")')
+ False
+
+ >>> em('"x"=="y" and "z"=="z" or "x"=="q"')
+ False
+
+ >>> em('"x"=="x" and "z"=="z" or "x"=="q"')
+ True
+
+ >>> em("sys_platform=='win32'") == (sys.platform=='win32')
+ True
+
+ >>> em("'x' in 'yx'")
+ True
+
+ >>> em("'yx' in 'x'")
+ False
+
+
+
+
diff --git a/tests/manual_test.py b/tests/manual_test.py
new file mode 100644
index 00000000..44cf2d06
--- /dev/null
+++ b/tests/manual_test.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+import sys
+
+if sys.version_info[0] >= 3:
+ raise NotImplementedError('Py3 not supported in this test yet')
+
+import os
+import shutil
+import tempfile
+from distutils.command.install import INSTALL_SCHEMES
+from string import Template
+from urllib2 import urlopen
+
+try:
+ import subprocess
+ def _system_call(*args):
+ assert subprocess.call(args) == 0
+except ImportError:
+ # Python 2.3
+ def _system_call(*args):
+ # quoting arguments if windows
+ if sys.platform == 'win32':
+ def quote(arg):
+ if ' ' in arg:
+ return '"%s"' % arg
+ return arg
+ args = [quote(arg) for arg in args]
+ assert os.system(' '.join(args)) == 0
+
+def tempdir(func):
+ def _tempdir(*args, **kwargs):
+ test_dir = tempfile.mkdtemp()
+ old_dir = os.getcwd()
+ os.chdir(test_dir)
+ try:
+ return func(*args, **kwargs)
+ finally:
+ os.chdir(old_dir)
+ shutil.rmtree(test_dir)
+ return _tempdir
+
+SIMPLE_BUILDOUT = """\
+[buildout]
+
+parts = eggs
+
+[eggs]
+recipe = zc.recipe.egg
+
+eggs =
+ extensions
+"""
+
+BOOTSTRAP = 'http://downloads.buildout.org/1/bootstrap.py'
+PYVER = sys.version.split()[0][:3]
+
+_VARS = {'base': '.',
+ 'py_version_short': PYVER}
+
+if sys.platform == 'win32':
+ PURELIB = INSTALL_SCHEMES['nt']['purelib']
+else:
+ PURELIB = INSTALL_SCHEMES['unix_prefix']['purelib']
+
+
+@tempdir
+def test_virtualenv():
+ """virtualenv with setuptools"""
+ purelib = os.path.abspath(Template(PURELIB).substitute(**_VARS))
+ _system_call('virtualenv', '--no-site-packages', '.')
+ _system_call('bin/easy_install', 'setuptools==dev')
+ # linux specific
+ site_pkg = os.listdir(purelib)
+ site_pkg.sort()
+ assert 'setuptools' in site_pkg[0]
+ easy_install = os.path.join(purelib, 'easy-install.pth')
+ with open(easy_install) as f:
+ res = f.read()
+ assert 'setuptools' in res
+
+@tempdir
+def test_full():
+ """virtualenv + pip + buildout"""
+ _system_call('virtualenv', '--no-site-packages', '.')
+ _system_call('bin/easy_install', '-q', 'setuptools==dev')
+ _system_call('bin/easy_install', '-qU', 'setuptools==dev')
+ _system_call('bin/easy_install', '-q', 'pip')
+ _system_call('bin/pip', 'install', '-q', 'zc.buildout')
+
+ with open('buildout.cfg', 'w') as f:
+ f.write(SIMPLE_BUILDOUT)
+
+ with open('bootstrap.py', 'w') as f:
+ f.write(urlopen(BOOTSTRAP).read())
+
+ _system_call('bin/python', 'bootstrap.py')
+ _system_call('bin/buildout', '-q')
+ eggs = os.listdir('eggs')
+ eggs.sort()
+ assert len(eggs) == 3
+ assert eggs[1].startswith('setuptools')
+ del eggs[1]
+ assert eggs == ['extensions-0.3-py2.6.egg',
+ 'zc.recipe.egg-1.2.2-py2.6.egg']
+
+if __name__ == '__main__':
+ test_virtualenv()
+ test_full()
diff --git a/tests/test_ez_setup.py b/tests/test_ez_setup.py
new file mode 100644
index 00000000..922bd884
--- /dev/null
+++ b/tests/test_ez_setup.py
@@ -0,0 +1,63 @@
+import sys
+import os
+import tempfile
+import unittest
+import shutil
+import copy
+
+CURDIR = os.path.abspath(os.path.dirname(__file__))
+TOPDIR = os.path.split(CURDIR)[0]
+sys.path.insert(0, TOPDIR)
+
+from ez_setup import (use_setuptools, _build_egg, _python_cmd, _do_download,
+ _install, DEFAULT_URL, DEFAULT_VERSION)
+import ez_setup
+
+class TestSetup(unittest.TestCase):
+
+ def urlopen(self, url):
+ return open(self.tarball)
+
+ def setUp(self):
+ self.old_sys_path = copy.copy(sys.path)
+ self.cwd = os.getcwd()
+ self.tmpdir = tempfile.mkdtemp()
+ os.chdir(TOPDIR)
+ _python_cmd("setup.py", "-q", "egg_info", "-RDb", "''", "sdist",
+ "--dist-dir", "%s" % self.tmpdir)
+ tarball = os.listdir(self.tmpdir)[0]
+ self.tarball = os.path.join(self.tmpdir, tarball)
+ import urllib2
+ urllib2.urlopen = self.urlopen
+
+ def tearDown(self):
+ shutil.rmtree(self.tmpdir)
+ os.chdir(self.cwd)
+ sys.path = copy.copy(self.old_sys_path)
+
+ def test_build_egg(self):
+ # making it an egg
+ egg = _build_egg(self.tarball, self.tmpdir)
+
+ # now trying to import it
+ sys.path[0] = egg
+ import setuptools
+ self.assertTrue(setuptools.__file__.startswith(egg))
+
+ def test_do_download(self):
+ tmpdir = tempfile.mkdtemp()
+ _do_download(DEFAULT_VERSION, DEFAULT_URL, tmpdir, 1)
+ import setuptools
+ self.assertTrue(setuptools.bootstrap_install_from.startswith(tmpdir))
+
+ def test_install(self):
+ def _faked(*args):
+ return True
+ ez_setup.python_cmd = _faked
+ _install(self.tarball)
+
+ def test_use_setuptools(self):
+ self.assertEqual(use_setuptools(), None)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tests/test_pkg_resources.py b/tests/test_pkg_resources.py
new file mode 100644
index 00000000..7009b4ab
--- /dev/null
+++ b/tests/test_pkg_resources.py
@@ -0,0 +1,61 @@
+import sys
+import tempfile
+import os
+import zipfile
+
+import pkg_resources
+
+class EggRemover(unicode):
+ def __call__(self):
+ if self in sys.path:
+ sys.path.remove(self)
+ if os.path.exists(self):
+ os.remove(self)
+
+class TestZipProvider(object):
+ finalizers = []
+
+ @classmethod
+ def setup_class(cls):
+ "create a zip egg and add it to sys.path"
+ egg = tempfile.NamedTemporaryFile(suffix='.egg', delete=False)
+ zip_egg = zipfile.ZipFile(egg, 'w')
+ zip_info = zipfile.ZipInfo()
+ zip_info.filename = 'mod.py'
+ zip_info.date_time = 2013, 5, 12, 13, 25, 0
+ zip_egg.writestr(zip_info, 'x = 3\n')
+ zip_info = zipfile.ZipInfo()
+ zip_info.filename = 'data.dat'
+ zip_info.date_time = 2013, 5, 12, 13, 25, 0
+ zip_egg.writestr(zip_info, 'hello, world!')
+ zip_egg.close()
+ egg.close()
+
+ sys.path.append(egg.name)
+ cls.finalizers.append(EggRemover(egg.name))
+
+ @classmethod
+ def teardown_class(cls):
+ for finalizer in cls.finalizers:
+ finalizer()
+
+ def test_resource_filename_rewrites_on_change(self):
+ """
+ If a previous call to get_resource_filename has saved the file, but
+ the file has been subsequently mutated with different file of the
+ same size and modification time, it should not be overwritten on a
+ subsequent call to get_resource_filename.
+ """
+ import mod
+ manager = pkg_resources.ResourceManager()
+ zp = pkg_resources.ZipProvider(mod)
+ filename = zp.get_resource_filename(manager, 'data.dat')
+ assert os.stat(filename).st_mtime == 1368379500
+ f = open(filename, 'wb')
+ f.write('hello, world?')
+ f.close()
+ os.utime(filename, (1368379500, 1368379500))
+ filename = zp.get_resource_filename(manager, 'data.dat')
+ f = open(filename)
+ assert f.read() == 'hello, world!'
+ manager.cleanup_resources()