summaryrefslogtreecommitdiffstats
path: root/Lib/packaging/tests
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/packaging/tests')
-rw-r--r--Lib/packaging/tests/LONG_DESC.txt44
-rw-r--r--Lib/packaging/tests/PKG-INFO57
-rw-r--r--Lib/packaging/tests/SETUPTOOLS-PKG-INFO182
-rw-r--r--Lib/packaging/tests/SETUPTOOLS-PKG-INFO2183
-rw-r--r--Lib/packaging/tests/__init__.py133
-rw-r--r--Lib/packaging/tests/__main__.py20
-rw-r--r--Lib/packaging/tests/fake_dists/babar-0.1.dist-info/INSTALLER0
-rw-r--r--Lib/packaging/tests/fake_dists/babar-0.1.dist-info/METADATA4
-rw-r--r--Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RECORD0
-rw-r--r--Lib/packaging/tests/fake_dists/babar-0.1.dist-info/REQUESTED0
-rw-r--r--Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RESOURCES2
-rw-r--r--Lib/packaging/tests/fake_dists/babar.cfg1
-rw-r--r--Lib/packaging/tests/fake_dists/babar.png0
-rw-r--r--Lib/packaging/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO6
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO18
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt0
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt1
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt3
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe1
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt6
-rw-r--r--Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt0
-rw-r--r--Lib/packaging/tests/fake_dists/cheese-2.0.2.egg-info5
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER0
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA9
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD0
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED0
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py1
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py10
-rw-r--r--Lib/packaging/tests/fake_dists/choxie-2.0.0.9/truffles.py5
-rw-r--r--Lib/packaging/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO5
-rw-r--r--Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER0
-rw-r--r--Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/METADATA5
-rw-r--r--Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/RECORD0
-rw-r--r--Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED0
-rw-r--r--Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/__init__.py1
-rw-r--r--Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/utils.py8
-rw-r--r--Lib/packaging/tests/fake_dists/nut-funkyversion.egg-info3
-rw-r--r--Lib/packaging/tests/fake_dists/strawberry-0.6.eggbin0 -> 1402 bytes
-rw-r--r--Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER0
-rw-r--r--Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA7
-rw-r--r--Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD0
-rw-r--r--Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED0
-rw-r--r--Lib/packaging/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py18
-rw-r--r--Lib/packaging/tests/fake_dists/truffles-5.0.egg-info3
-rw-r--r--Lib/packaging/tests/fixer/__init__.py0
-rw-r--r--Lib/packaging/tests/fixer/fix_idioms.py134
-rw-r--r--Lib/packaging/tests/pypi_server.py444
-rw-r--r--Lib/packaging/tests/pypi_test_server.py59
-rw-r--r--Lib/packaging/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gzbin0 -> 110 bytes
-rw-r--r--Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz0
-rw-r--r--Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html3
-rw-r--r--Lib/packaging/tests/pypiserver/downloads_with_md5/simple/foobar/index.html3
-rw-r--r--Lib/packaging/tests/pypiserver/downloads_with_md5/simple/index.html2
-rw-r--r--Lib/packaging/tests/pypiserver/foo_bar_baz/simple/bar/index.html6
-rw-r--r--Lib/packaging/tests/pypiserver/foo_bar_baz/simple/baz/index.html6
-rw-r--r--Lib/packaging/tests/pypiserver/foo_bar_baz/simple/foo/index.html6
-rw-r--r--Lib/packaging/tests/pypiserver/foo_bar_baz/simple/index.html3
-rw-r--r--Lib/packaging/tests/pypiserver/project_list/simple/index.html5
-rw-r--r--Lib/packaging/tests/pypiserver/test_found_links/simple/foobar/index.html6
-rw-r--r--Lib/packaging/tests/pypiserver/test_found_links/simple/index.html1
-rw-r--r--Lib/packaging/tests/pypiserver/test_pypi_server/external/index.html1
-rw-r--r--Lib/packaging/tests/pypiserver/test_pypi_server/simple/index.html1
-rw-r--r--Lib/packaging/tests/pypiserver/with_externals/external/external.html3
-rw-r--r--Lib/packaging/tests/pypiserver/with_externals/simple/foobar/index.html4
-rw-r--r--Lib/packaging/tests/pypiserver/with_externals/simple/index.html1
-rw-r--r--Lib/packaging/tests/pypiserver/with_norel_links/external/homepage.html7
-rw-r--r--Lib/packaging/tests/pypiserver/with_norel_links/external/nonrel.html1
-rw-r--r--Lib/packaging/tests/pypiserver/with_norel_links/simple/foobar/index.html6
-rw-r--r--Lib/packaging/tests/pypiserver/with_norel_links/simple/index.html1
-rw-r--r--Lib/packaging/tests/pypiserver/with_real_externals/simple/foobar/index.html4
-rw-r--r--Lib/packaging/tests/pypiserver/with_real_externals/simple/index.html1
-rw-r--r--Lib/packaging/tests/support.py259
-rw-r--r--Lib/packaging/tests/test_ccompiler.py15
-rw-r--r--Lib/packaging/tests/test_command_bdist.py77
-rw-r--r--Lib/packaging/tests/test_command_bdist_dumb.py103
-rw-r--r--Lib/packaging/tests/test_command_bdist_msi.py25
-rw-r--r--Lib/packaging/tests/test_command_bdist_wininst.py32
-rw-r--r--Lib/packaging/tests/test_command_build.py55
-rw-r--r--Lib/packaging/tests/test_command_build_clib.py141
-rw-r--r--Lib/packaging/tests/test_command_build_ext.py353
-rw-r--r--Lib/packaging/tests/test_command_build_py.py124
-rw-r--r--Lib/packaging/tests/test_command_build_scripts.py112
-rw-r--r--Lib/packaging/tests/test_command_check.py131
-rw-r--r--Lib/packaging/tests/test_command_clean.py48
-rw-r--r--Lib/packaging/tests/test_command_cmd.py101
-rw-r--r--Lib/packaging/tests/test_command_config.py76
-rw-r--r--Lib/packaging/tests/test_command_install_data.py80
-rw-r--r--Lib/packaging/tests/test_command_install_dist.py210
-rw-r--r--Lib/packaging/tests/test_command_install_distinfo.py192
-rw-r--r--Lib/packaging/tests/test_command_install_headers.py38
-rw-r--r--Lib/packaging/tests/test_command_install_lib.py111
-rw-r--r--Lib/packaging/tests/test_command_install_scripts.py78
-rw-r--r--Lib/packaging/tests/test_command_register.py259
-rw-r--r--Lib/packaging/tests/test_command_sdist.py407
-rw-r--r--Lib/packaging/tests/test_command_test.py225
-rw-r--r--Lib/packaging/tests/test_command_upload.py157
-rw-r--r--Lib/packaging/tests/test_command_upload_docs.py205
-rw-r--r--Lib/packaging/tests/test_compiler.py66
-rw-r--r--Lib/packaging/tests/test_config.py424
-rw-r--r--Lib/packaging/tests/test_create.py235
-rw-r--r--Lib/packaging/tests/test_cygwinccompiler.py88
-rw-r--r--Lib/packaging/tests/test_database.py506
-rw-r--r--Lib/packaging/tests/test_depgraph.py301
-rw-r--r--Lib/packaging/tests/test_dist.py445
-rw-r--r--Lib/packaging/tests/test_extension.py15
-rw-r--r--Lib/packaging/tests/test_install.py353
-rw-r--r--Lib/packaging/tests/test_manifest.py72
-rw-r--r--Lib/packaging/tests/test_markers.py71
-rw-r--r--Lib/packaging/tests/test_metadata.py279
-rw-r--r--Lib/packaging/tests/test_mixin2to3.py75
-rw-r--r--Lib/packaging/tests/test_msvc9compiler.py140
-rw-r--r--Lib/packaging/tests/test_pypi_dist.py277
-rw-r--r--Lib/packaging/tests/test_pypi_server.py81
-rw-r--r--Lib/packaging/tests/test_pypi_simple.py326
-rw-r--r--Lib/packaging/tests/test_pypi_xmlrpc.py93
-rw-r--r--Lib/packaging/tests/test_resources.py168
-rw-r--r--Lib/packaging/tests/test_run.py62
-rw-r--r--Lib/packaging/tests/test_uninstall.py99
-rw-r--r--Lib/packaging/tests/test_unixccompiler.py132
-rw-r--r--Lib/packaging/tests/test_util.py928
-rw-r--r--Lib/packaging/tests/test_version.py252
121 files changed, 10551 insertions, 0 deletions
diff --git a/Lib/packaging/tests/LONG_DESC.txt b/Lib/packaging/tests/LONG_DESC.txt
new file mode 100644
index 0000000..2b4358a
--- /dev/null
+++ b/Lib/packaging/tests/LONG_DESC.txt
@@ -0,0 +1,44 @@
+CLVault
+=======
+
+CLVault uses Keyring to provide a command-line utility to safely store
+and retrieve passwords.
+
+Install it using pip or the setup.py script::
+
+ $ python setup.py install
+
+ $ pip install clvault
+
+Once it's installed, you will have three scripts installed in your
+Python scripts folder, you can use to list, store and retrieve passwords::
+
+ $ clvault-set blog
+ Set your password:
+ Set the associated username (can be blank): tarek
+ Set a description (can be blank): My blog password
+ Password set.
+
+ $ clvault-get blog
+ The username is "tarek"
+ The password has been copied in your clipboard
+
+ $ clvault-list
+ Registered services:
+ blog My blog password
+
+
+*clvault-set* takes a service name then prompt you for a password, and some
+optional information about your service. The password is safely stored in
+a keyring while the description is saved in a ``.clvault`` file in your
+home directory. This file is created automatically the first time the command
+is used.
+
+*clvault-get* copies the password for a given service in your clipboard, and
+displays the associated user if any.
+
+*clvault-list* lists all registered services, with their description when
+given.
+
+
+Project page: http://bitbucket.org/tarek/clvault
diff --git a/Lib/packaging/tests/PKG-INFO b/Lib/packaging/tests/PKG-INFO
new file mode 100644
index 0000000..f48546e
--- /dev/null
+++ b/Lib/packaging/tests/PKG-INFO
@@ -0,0 +1,57 @@
+Metadata-Version: 1.2
+Name: CLVault
+Version: 0.5
+Summary: Command-Line utility to store and retrieve passwords
+Home-page: http://bitbucket.org/tarek/clvault
+Author: Tarek Ziade
+Author-email: tarek@ziade.org
+License: PSF
+Keywords: keyring,password,crypt
+Requires-Dist: foo; sys.platform == 'okook'
+Requires-Dist: bar; sys.platform == '%s'
+Platform: UNKNOWN
+Description: CLVault
+ |=======
+ |
+ |CLVault uses Keyring to provide a command-line utility to safely store
+ |and retrieve passwords.
+ |
+ |Install it using pip or the setup.py script::
+ |
+ | $ python setup.py install
+ |
+ | $ pip install clvault
+ |
+ |Once it's installed, you will have three scripts installed in your
+ |Python scripts folder, you can use to list, store and retrieve passwords::
+ |
+ | $ clvault-set blog
+ | Set your password:
+ | Set the associated username (can be blank): tarek
+ | Set a description (can be blank): My blog password
+ | Password set.
+ |
+ | $ clvault-get blog
+ | The username is "tarek"
+ | The password has been copied in your clipboard
+ |
+ | $ clvault-list
+ | Registered services:
+ | blog My blog password
+ |
+ |
+ |*clvault-set* takes a service name then prompt you for a password, and some
+ |optional information about your service. The password is safely stored in
+ |a keyring while the description is saved in a ``.clvault`` file in your
+ |home directory. This file is created automatically the first time the command
+ |is used.
+ |
+ |*clvault-get* copies the password for a given service in your clipboard, and
+ |displays the associated user if any.
+ |
+ |*clvault-list* lists all registered services, with their description when
+ |given.
+ |
+ |
+ |Project page: http://bitbucket.org/tarek/clvault
+ |
diff --git a/Lib/packaging/tests/SETUPTOOLS-PKG-INFO b/Lib/packaging/tests/SETUPTOOLS-PKG-INFO
new file mode 100644
index 0000000..dff8d00
--- /dev/null
+++ b/Lib/packaging/tests/SETUPTOOLS-PKG-INFO
@@ -0,0 +1,182 @@
+Metadata-Version: 1.0
+Name: setuptools
+Version: 0.6c9
+Summary: Download, build, install, upgrade, and uninstall Python packages -- easily!
+Home-page: http://pypi.python.org/pypi/setuptools
+Author: Phillip J. Eby
+Author-email: distutils-sig@python.org
+License: PSF or ZPL
+Description: ===============================
+ Installing and Using Setuptools
+ ===============================
+
+ .. contents:: **Table of Contents**
+
+
+ -------------------------
+ Installation Instructions
+ -------------------------
+
+ Windows
+ =======
+
+ Install setuptools using the provided ``.exe`` installer. If you've previously
+ installed older versions of setuptools, please delete all ``setuptools*.egg``
+ and ``setuptools.pth`` files from your system's ``site-packages`` directory
+ (and any other ``sys.path`` directories) FIRST.
+
+ If you are upgrading a previous version of setuptools that was installed using
+ an ``.exe`` installer, please be sure to also *uninstall that older version*
+ via your system's "Add/Remove Programs" feature, BEFORE installing the newer
+ version.
+
+ Once installation is complete, you will find an ``easy_install.exe`` program in
+ your Python ``Scripts`` subdirectory. Be sure to add this directory to your
+ ``PATH`` environment variable, if you haven't already done so.
+
+
+ RPM-Based Systems
+ =================
+
+ Install setuptools using the provided source RPM. The included ``.spec`` file
+ assumes you are installing using the default ``python`` executable, and is not
+ specific to a particular Python version. The ``easy_install`` executable will
+ be installed to a system ``bin`` directory such as ``/usr/bin``.
+
+ If you wish to install to a location other than the default Python
+ installation's default ``site-packages`` directory (and ``$prefix/bin`` for
+ scripts), please use the ``.egg``-based installation approach described in the
+ following section.
+
+
+ Cygwin, Mac OS X, Linux, Other
+ ==============================
+
+ 1. Download the appropriate egg for your version of Python (e.g.
+ ``setuptools-0.6c9-py2.4.egg``). Do NOT rename it.
+
+ 2. Run it as if it were a shell script, e.g. ``sh setuptools-0.6c9-py2.4.egg``.
+ Setuptools will install itself using the matching version of Python (e.g.
+ ``python2.4``), and will place the ``easy_install`` executable in the
+ default location for installing Python scripts (as determined by the
+ standard distutils configuration files, or by the Python installation).
+
+ If you want to install setuptools to somewhere other than ``site-packages`` or
+ your default distutils installation locations for libraries and scripts, you
+ may include EasyInstall command-line options such as ``--prefix``,
+ ``--install-dir``, and so on, following the ``.egg`` filename on the same
+ command line. For example::
+
+ sh setuptools-0.6c9-py2.4.egg --prefix=~
+
+ You can use ``--help`` to get a full options list, but we recommend consulting
+ the `EasyInstall manual`_ for detailed instructions, especially `the section
+ on custom installation locations`_.
+
+ .. _EasyInstall manual: http://peak.telecommunity.com/DevCenter/EasyInstall
+ .. _the section on custom installation locations: http://peak.telecommunity.com/DevCenter/EasyInstall#custom-installation-locations
+
+
+ Cygwin Note
+ -----------
+
+ If you are trying to install setuptools for the **Windows** version of Python
+ (as opposed to the Cygwin version that lives in ``/usr/bin``), you must make
+ sure that an appropriate executable (``python2.3``, ``python2.4``, or
+ ``python2.5``) is on your **Cygwin** ``PATH`` when invoking the egg. For
+ example, doing the following at a Cygwin bash prompt will install setuptools
+ for the **Windows** Python found at ``C:\\Python24``::
+
+ ln -s /cygdrive/c/Python24/python.exe python2.4
+ PATH=.:$PATH sh setuptools-0.6c9-py2.4.egg
+ rm python2.4
+
+
+ Downloads
+ =========
+
+ All setuptools downloads can be found at `the project's home page in the Python
+ Package Index`_. Scroll to the very bottom of the page to find the links.
+
+ .. _the project's home page in the Python Package Index: http://pypi.python.org/pypi/setuptools
+
+ In addition to the PyPI downloads, the development version of ``setuptools``
+ is available from the `Python SVN sandbox`_, and in-development versions of the
+ `0.6 branch`_ are available as well.
+
+ .. _0.6 branch: http://svn.python.org/projects/sandbox/branches/setuptools-0.6/#egg=setuptools-dev06
+
+ .. _Python SVN sandbox: http://svn.python.org/projects/sandbox/trunk/setuptools/#egg=setuptools-dev
+
+ --------------------------------
+ Using Setuptools and EasyInstall
+ --------------------------------
+
+ Here are some of the available manuals, tutorials, and other resources for
+ learning about Setuptools, Python Eggs, and EasyInstall:
+
+ * `The EasyInstall user's guide and reference manual`_
+ * `The setuptools Developer's Guide`_
+ * `The pkg_resources API reference`_
+ * `Package Compatibility Notes`_ (user-maintained)
+ * `The Internal Structure of Python Eggs`_
+
+ Questions, comments, and bug reports should be directed to the `distutils-sig
+ mailing list`_. If you have written (or know of) any tutorials, documentation,
+ plug-ins, or other resources for setuptools users, please let us know about
+ them there, so this reference list can be updated. If you have working,
+ *tested* patches to correct problems or add features, you may submit them to
+ the `setuptools bug tracker`_.
+
+ .. _setuptools bug tracker: http://bugs.python.org/setuptools/
+ .. _Package Compatibility Notes: http://peak.telecommunity.com/DevCenter/PackageNotes
+ .. _The Internal Structure of Python Eggs: http://peak.telecommunity.com/DevCenter/EggFormats
+ .. _The setuptools Developer's Guide: http://peak.telecommunity.com/DevCenter/setuptools
+ .. _The pkg_resources API reference: http://peak.telecommunity.com/DevCenter/PkgResources
+ .. _The EasyInstall user's guide and reference manual: http://peak.telecommunity.com/DevCenter/EasyInstall
+ .. _distutils-sig mailing list: http://mail.python.org/pipermail/distutils-sig/
+
+
+ -------
+ Credits
+ -------
+
+ * The original design for the ``.egg`` format and the ``pkg_resources`` API was
+ co-created by Phillip Eby and Bob Ippolito. Bob also implemented the first
+ version of ``pkg_resources``, and supplied the OS X operating system version
+ compatibility algorithm.
+
+ * Ian Bicking implemented many early "creature comfort" features of
+ easy_install, including support for downloading via Sourceforge and
+ Subversion repositories. Ian's comments on the Web-SIG about WSGI
+ application deployment also inspired the concept of "entry points" in eggs,
+ and he has given talks at PyCon and elsewhere to inform and educate the
+ community about eggs and setuptools.
+
+ * Jim Fulton contributed time and effort to build automated tests of various
+ aspects of ``easy_install``, and supplied the doctests for the command-line
+ ``.exe`` wrappers on Windows.
+
+ * Phillip J. Eby is the principal author and maintainer of setuptools, and
+ first proposed the idea of an importable binary distribution format for
+ Python application plug-ins.
+
+ * Significant parts of the implementation of setuptools were funded by the Open
+ Source Applications Foundation, to provide a plug-in infrastructure for the
+ Chandler PIM application. In addition, many OSAF staffers (such as Mike
+ "Code Bear" Taylor) contributed their time and stress as guinea pigs for the
+ use of eggs and setuptools, even before eggs were "cool". (Thanks, guys!)
+
+
+Keywords: CPAN PyPI distutils eggs package management
+Platform: UNKNOWN
+Classifier: Development Status :: 3 - Alpha
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Python Software Foundation License
+Classifier: License :: OSI Approved :: Zope Public License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Archiving :: Packaging
+Classifier: Topic :: System :: Systems Administration
+Classifier: Topic :: Utilities
diff --git a/Lib/packaging/tests/SETUPTOOLS-PKG-INFO2 b/Lib/packaging/tests/SETUPTOOLS-PKG-INFO2
new file mode 100644
index 0000000..4b3906a
--- /dev/null
+++ b/Lib/packaging/tests/SETUPTOOLS-PKG-INFO2
@@ -0,0 +1,183 @@
+Metadata-Version: 1.1
+Name: setuptools
+Version: 0.6c9
+Summary: Download, build, install, upgrade, and uninstall Python packages -- easily!
+Home-page: http://pypi.python.org/pypi/setuptools
+Author: Phillip J. Eby
+Author-email: distutils-sig@python.org
+License: PSF or ZPL
+Description: ===============================
+ Installing and Using Setuptools
+ ===============================
+
+ .. contents:: **Table of Contents**
+
+
+ -------------------------
+ Installation Instructions
+ -------------------------
+
+ Windows
+ =======
+
+ Install setuptools using the provided ``.exe`` installer. If you've previously
+ installed older versions of setuptools, please delete all ``setuptools*.egg``
+ and ``setuptools.pth`` files from your system's ``site-packages`` directory
+ (and any other ``sys.path`` directories) FIRST.
+
+ If you are upgrading a previous version of setuptools that was installed using
+ an ``.exe`` installer, please be sure to also *uninstall that older version*
+ via your system's "Add/Remove Programs" feature, BEFORE installing the newer
+ version.
+
+ Once installation is complete, you will find an ``easy_install.exe`` program in
+ your Python ``Scripts`` subdirectory. Be sure to add this directory to your
+ ``PATH`` environment variable, if you haven't already done so.
+
+
+ RPM-Based Systems
+ =================
+
+ Install setuptools using the provided source RPM. The included ``.spec`` file
+ assumes you are installing using the default ``python`` executable, and is not
+ specific to a particular Python version. The ``easy_install`` executable will
+ be installed to a system ``bin`` directory such as ``/usr/bin``.
+
+ If you wish to install to a location other than the default Python
+ installation's default ``site-packages`` directory (and ``$prefix/bin`` for
+ scripts), please use the ``.egg``-based installation approach described in the
+ following section.
+
+
+ Cygwin, Mac OS X, Linux, Other
+ ==============================
+
+ 1. Download the appropriate egg for your version of Python (e.g.
+ ``setuptools-0.6c9-py2.4.egg``). Do NOT rename it.
+
+ 2. Run it as if it were a shell script, e.g. ``sh setuptools-0.6c9-py2.4.egg``.
+ Setuptools will install itself using the matching version of Python (e.g.
+ ``python2.4``), and will place the ``easy_install`` executable in the
+ default location for installing Python scripts (as determined by the
+ standard distutils configuration files, or by the Python installation).
+
+ If you want to install setuptools to somewhere other than ``site-packages`` or
+ your default distutils installation locations for libraries and scripts, you
+ may include EasyInstall command-line options such as ``--prefix``,
+ ``--install-dir``, and so on, following the ``.egg`` filename on the same
+ command line. For example::
+
+ sh setuptools-0.6c9-py2.4.egg --prefix=~
+
+ You can use ``--help`` to get a full options list, but we recommend consulting
+ the `EasyInstall manual`_ for detailed instructions, especially `the section
+ on custom installation locations`_.
+
+ .. _EasyInstall manual: http://peak.telecommunity.com/DevCenter/EasyInstall
+ .. _the section on custom installation locations: http://peak.telecommunity.com/DevCenter/EasyInstall#custom-installation-locations
+
+
+ Cygwin Note
+ -----------
+
+ If you are trying to install setuptools for the **Windows** version of Python
+ (as opposed to the Cygwin version that lives in ``/usr/bin``), you must make
+ sure that an appropriate executable (``python2.3``, ``python2.4``, or
+ ``python2.5``) is on your **Cygwin** ``PATH`` when invoking the egg. For
+ example, doing the following at a Cygwin bash prompt will install setuptools
+ for the **Windows** Python found at ``C:\\Python24``::
+
+ ln -s /cygdrive/c/Python24/python.exe python2.4
+ PATH=.:$PATH sh setuptools-0.6c9-py2.4.egg
+ rm python2.4
+
+
+ Downloads
+ =========
+
+ All setuptools downloads can be found at `the project's home page in the Python
+ Package Index`_. Scroll to the very bottom of the page to find the links.
+
+ .. _the project's home page in the Python Package Index: http://pypi.python.org/pypi/setuptools
+
+ In addition to the PyPI downloads, the development version of ``setuptools``
+ is available from the `Python SVN sandbox`_, and in-development versions of the
+ `0.6 branch`_ are available as well.
+
+ .. _0.6 branch: http://svn.python.org/projects/sandbox/branches/setuptools-0.6/#egg=setuptools-dev06
+
+ .. _Python SVN sandbox: http://svn.python.org/projects/sandbox/trunk/setuptools/#egg=setuptools-dev
+
+ --------------------------------
+ Using Setuptools and EasyInstall
+ --------------------------------
+
+ Here are some of the available manuals, tutorials, and other resources for
+ learning about Setuptools, Python Eggs, and EasyInstall:
+
+ * `The EasyInstall user's guide and reference manual`_
+ * `The setuptools Developer's Guide`_
+ * `The pkg_resources API reference`_
+ * `Package Compatibility Notes`_ (user-maintained)
+ * `The Internal Structure of Python Eggs`_
+
+ Questions, comments, and bug reports should be directed to the `distutils-sig
+ mailing list`_. If you have written (or know of) any tutorials, documentation,
+ plug-ins, or other resources for setuptools users, please let us know about
+ them there, so this reference list can be updated. If you have working,
+ *tested* patches to correct problems or add features, you may submit them to
+ the `setuptools bug tracker`_.
+
+ .. _setuptools bug tracker: http://bugs.python.org/setuptools/
+ .. _Package Compatibility Notes: http://peak.telecommunity.com/DevCenter/PackageNotes
+ .. _The Internal Structure of Python Eggs: http://peak.telecommunity.com/DevCenter/EggFormats
+ .. _The setuptools Developer's Guide: http://peak.telecommunity.com/DevCenter/setuptools
+ .. _The pkg_resources API reference: http://peak.telecommunity.com/DevCenter/PkgResources
+ .. _The EasyInstall user's guide and reference manual: http://peak.telecommunity.com/DevCenter/EasyInstall
+ .. _distutils-sig mailing list: http://mail.python.org/pipermail/distutils-sig/
+
+
+ -------
+ Credits
+ -------
+
+ * The original design for the ``.egg`` format and the ``pkg_resources`` API was
+ co-created by Phillip Eby and Bob Ippolito. Bob also implemented the first
+ version of ``pkg_resources``, and supplied the OS X operating system version
+ compatibility algorithm.
+
+ * Ian Bicking implemented many early "creature comfort" features of
+ easy_install, including support for downloading via Sourceforge and
+ Subversion repositories. Ian's comments on the Web-SIG about WSGI
+ application deployment also inspired the concept of "entry points" in eggs,
+ and he has given talks at PyCon and elsewhere to inform and educate the
+ community about eggs and setuptools.
+
+ * Jim Fulton contributed time and effort to build automated tests of various
+ aspects of ``easy_install``, and supplied the doctests for the command-line
+ ``.exe`` wrappers on Windows.
+
+ * Phillip J. Eby is the principal author and maintainer of setuptools, and
+ first proposed the idea of an importable binary distribution format for
+ Python application plug-ins.
+
+ * Significant parts of the implementation of setuptools were funded by the Open
+ Source Applications Foundation, to provide a plug-in infrastructure for the
+ Chandler PIM application. In addition, many OSAF staffers (such as Mike
+ "Code Bear" Taylor) contributed their time and stress as guinea pigs for the
+ use of eggs and setuptools, even before eggs were "cool". (Thanks, guys!)
+
+
+Keywords: CPAN PyPI distutils eggs package management
+Platform: UNKNOWN
+Classifier: Development Status :: 3 - Alpha
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Python Software Foundation License
+Classifier: License :: OSI Approved :: Zope Public License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: System :: Archiving :: Packaging
+Classifier: Topic :: System :: Systems Administration
+Classifier: Topic :: Utilities
+Requires: Foo
diff --git a/Lib/packaging/tests/__init__.py b/Lib/packaging/tests/__init__.py
new file mode 100644
index 0000000..0b0e3c5
--- /dev/null
+++ b/Lib/packaging/tests/__init__.py
@@ -0,0 +1,133 @@
+"""Test suite for packaging.
+
+This test suite consists of a collection of test modules in the
+packaging.tests package. Each test module has a name starting with
+'test' and contains a function test_suite(). The function is expected
+to return an initialized unittest.TestSuite instance.
+
+Utility code is included in packaging.tests.support.
+"""
+
+# Put this text back for the backport
+#Always import unittest from this module, it will be the right version
+#(standard library unittest for 3.2 and higher, third-party unittest2
+#elease for older versions).
+
+import os
+import sys
+import unittest
+from test.support import TESTFN
+
+# XXX move helpers to support, add tests for them, remove things that
+# duplicate test.support (or keep them for the backport; needs thinking)
+
+here = os.path.dirname(__file__) or os.curdir
+verbose = 1
+
+def test_suite():
+ suite = unittest.TestSuite()
+ for fn in os.listdir(here):
+ if fn.startswith("test") and fn.endswith(".py"):
+ modname = "packaging.tests." + fn[:-3]
+ __import__(modname)
+ module = sys.modules[modname]
+ suite.addTest(module.test_suite())
+ return suite
+
+
+class Error(Exception):
+ """Base class for regression test exceptions."""
+
+
+class TestFailed(Error):
+ """Test failed."""
+
+
+class BasicTestRunner:
+ def run(self, test):
+ result = unittest.TestResult()
+ test(result)
+ return result
+
+
+def _run_suite(suite, verbose_=1):
+ """Run tests from a unittest.TestSuite-derived class."""
+ global verbose
+ verbose = verbose_
+ if verbose_:
+ runner = unittest.TextTestRunner(sys.stdout, verbosity=2)
+ else:
+ runner = BasicTestRunner()
+
+ result = runner.run(suite)
+ if not result.wasSuccessful():
+ if len(result.errors) == 1 and not result.failures:
+ err = result.errors[0][1]
+ elif len(result.failures) == 1 and not result.errors:
+ err = result.failures[0][1]
+ else:
+ err = "errors occurred; run in verbose mode for details"
+ raise TestFailed(err)
+
+
+def run_unittest(classes, verbose_=1):
+ """Run tests from unittest.TestCase-derived classes.
+
+ Originally extracted from stdlib test.test_support and modified to
+ support unittest2.
+ """
+ valid_types = (unittest.TestSuite, unittest.TestCase)
+ suite = unittest.TestSuite()
+ for cls in classes:
+ if isinstance(cls, str):
+ if cls in sys.modules:
+ suite.addTest(unittest.findTestCases(sys.modules[cls]))
+ else:
+ raise ValueError("str arguments must be keys in sys.modules")
+ elif isinstance(cls, valid_types):
+ suite.addTest(cls)
+ else:
+ suite.addTest(unittest.makeSuite(cls))
+ _run_suite(suite, verbose_)
+
+
+def reap_children():
+ """Use this function at the end of test_main() whenever sub-processes
+ are started. This will help ensure that no extra children (zombies)
+ stick around to hog resources and create problems when looking
+ for refleaks.
+
+ Extracted from stdlib test.support.
+ """
+
+ # Reap all our dead child processes so we don't leave zombies around.
+ # These hog resources and might be causing some of the buildbots to die.
+ if hasattr(os, 'waitpid'):
+ any_process = -1
+ while True:
+ try:
+ # This will raise an exception on Windows. That's ok.
+ pid, status = os.waitpid(any_process, os.WNOHANG)
+ if pid == 0:
+ break
+ except:
+ break
+
+
+def captured_stdout(func, *args, **kw):
+ import io
+ orig_stdout = getattr(sys, 'stdout')
+ setattr(sys, 'stdout', io.StringIO())
+ try:
+ res = func(*args, **kw)
+ sys.stdout.seek(0)
+ return res, sys.stdout.read()
+ finally:
+ setattr(sys, 'stdout', orig_stdout)
+
+
+def unload(name):
+ try:
+ del sys.modules[name]
+ except KeyError:
+ pass
diff --git a/Lib/packaging/tests/__main__.py b/Lib/packaging/tests/__main__.py
new file mode 100644
index 0000000..68ee229
--- /dev/null
+++ b/Lib/packaging/tests/__main__.py
@@ -0,0 +1,20 @@
+"""Packaging test suite runner."""
+
+# Ripped from importlib tests, thanks Brett!
+
+import os
+import sys
+import unittest
+from test.support import run_unittest, reap_children
+
+
+def test_main():
+ start_dir = os.path.dirname(__file__)
+ top_dir = os.path.dirname(os.path.dirname(start_dir))
+ test_loader = unittest.TestLoader()
+ run_unittest(test_loader.discover(start_dir, top_level_dir=top_dir))
+ reap_children()
+
+
+if __name__ == '__main__':
+ test_main()
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/INSTALLER b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/INSTALLER
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/INSTALLER
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/METADATA b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/METADATA
new file mode 100644
index 0000000..65e839a
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/METADATA
@@ -0,0 +1,4 @@
+Metadata-version: 1.2
+Name: babar
+Version: 0.1
+Author: FELD Boris \ No newline at end of file
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RECORD b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RECORD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RECORD
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/REQUESTED b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/REQUESTED
diff --git a/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RESOURCES b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RESOURCES
new file mode 100644
index 0000000..5d0da49
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar-0.1.dist-info/RESOURCES
@@ -0,0 +1,2 @@
+babar.png,babar.png
+babar.cfg,babar.cfg \ No newline at end of file
diff --git a/Lib/packaging/tests/fake_dists/babar.cfg b/Lib/packaging/tests/fake_dists/babar.cfg
new file mode 100644
index 0000000..ecd6efe
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar.cfg
@@ -0,0 +1 @@
+Config \ No newline at end of file
diff --git a/Lib/packaging/tests/fake_dists/babar.png b/Lib/packaging/tests/fake_dists/babar.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/babar.png
diff --git a/Lib/packaging/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO b/Lib/packaging/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
new file mode 100644
index 0000000..a176dfd
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/bacon-0.1.egg-info/PKG-INFO
@@ -0,0 +1,6 @@
+Metadata-Version: 1.2
+Name: bacon
+Version: 0.1
+Provides-Dist: truffles (2.0)
+Provides-Dist: bacon (0.1)
+Obsoletes-Dist: truffles (>=0.9,<=1.5)
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO
new file mode 100644
index 0000000..a7e118a
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/PKG-INFO
@@ -0,0 +1,18 @@
+Metadata-Version: 1.0
+Name: banana
+Version: 0.4
+Summary: A yellow fruit
+Home-page: http://en.wikipedia.org/wiki/Banana
+Author: Josip Djolonga
+Author-email: foo@nbar.com
+License: BSD
+Description: A fruit
+Keywords: foo bar
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Science/Research
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Topic :: Scientific/Engineering :: GIS
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/SOURCES.txt
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt
new file mode 100644
index 0000000..5d3e5f6
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/entry_points.txt
@@ -0,0 +1,3 @@
+
+ # -*- Entry points: -*-
+ \ No newline at end of file
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/not-zip-safe
@@ -0,0 +1 @@
+
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt
new file mode 100644
index 0000000..4354305
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/requires.txt
@@ -0,0 +1,6 @@
+# this should be ignored
+
+strawberry >=0.5
+
+[section ignored]
+foo ==0.5
diff --git a/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/banana-0.4.egg/EGG-INFO/top_level.txt
diff --git a/Lib/packaging/tests/fake_dists/cheese-2.0.2.egg-info b/Lib/packaging/tests/fake_dists/cheese-2.0.2.egg-info
new file mode 100644
index 0000000..27cbe30
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/cheese-2.0.2.egg-info
@@ -0,0 +1,5 @@
+Metadata-Version: 1.2
+Name: cheese
+Version: 2.0.2
+Provides-Dist: truffles (1.0.2)
+Obsoletes-Dist: truffles (!=1.2,<=2.0)
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA
new file mode 100644
index 0000000..418929e
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA
@@ -0,0 +1,9 @@
+Metadata-Version: 1.2
+Name: choxie
+Version: 2.0.0.9
+Summary: Chocolate with a kick!
+Requires-Dist: towel-stuff (0.1)
+Requires-Dist: nut
+Provides-Dist: truffles (1.0)
+Obsoletes-Dist: truffles (<=0.8,>=0.5)
+Obsoletes-Dist: truffles (<=0.9,>=0.6)
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py
new file mode 100644
index 0000000..40a96af
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py
new file mode 100644
index 0000000..c4027f3
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+from towel_stuff import Towel
+
+class Chocolate(object):
+ """A piece of chocolate."""
+
+ def wrap_with_towel(self):
+ towel = Towel()
+ towel.wrap(self)
+ return towel
diff --git a/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/truffles.py b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/truffles.py
new file mode 100644
index 0000000..342b8ea
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/choxie-2.0.0.9/truffles.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+from choxie.chocolate import Chocolate
+
+class Truffle(Chocolate):
+ """A truffle."""
diff --git a/Lib/packaging/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO b/Lib/packaging/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO
new file mode 100644
index 0000000..499a083
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/coconuts-aster-10.3.egg-info/PKG-INFO
@@ -0,0 +1,5 @@
+Metadata-Version: 1.2
+Name: coconuts-aster
+Version: 10.3
+Provides-Dist: strawberry (0.6)
+Provides-Dist: banana (0.4)
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/INSTALLER
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/METADATA b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/METADATA
new file mode 100644
index 0000000..0b99f52
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/METADATA
@@ -0,0 +1,5 @@
+Metadata-Version: 1.2
+Name: grammar
+Version: 1.0a4
+Requires-Dist: truffles (>=1.2)
+Author: Sherlock Holmes
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/RECORD b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/RECORD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/RECORD
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4.dist-info/REQUESTED
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/__init__.py b/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/__init__.py
new file mode 100644
index 0000000..40a96af
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/__init__.py
@@ -0,0 +1 @@
+# -*- coding: utf-8 -*-
diff --git a/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/utils.py b/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/utils.py
new file mode 100644
index 0000000..66ba796
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/grammar-1.0a4/grammar/utils.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+from random import randint
+
+def is_valid_grammar(sentence):
+ if randint(0, 10) < 2:
+ return False
+ else:
+ return True
diff --git a/Lib/packaging/tests/fake_dists/nut-funkyversion.egg-info b/Lib/packaging/tests/fake_dists/nut-funkyversion.egg-info
new file mode 100644
index 0000000..0c58ec1
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/nut-funkyversion.egg-info
@@ -0,0 +1,3 @@
+Metadata-Version: 1.2
+Name: nut
+Version: funkyversion
diff --git a/Lib/packaging/tests/fake_dists/strawberry-0.6.egg b/Lib/packaging/tests/fake_dists/strawberry-0.6.egg
new file mode 100644
index 0000000..6d160e8
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/strawberry-0.6.egg
Binary files differ
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/INSTALLER
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
new file mode 100644
index 0000000..ca46d0a
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/METADATA
@@ -0,0 +1,7 @@
+Metadata-Version: 1.2
+Name: towel-stuff
+Version: 0.1
+Provides-Dist: truffles (1.1.2)
+Provides-Dist: towel-stuff (0.1)
+Obsoletes-Dist: truffles (!=0.8,<1.0)
+Requires-Dist: bacon (<=0.2)
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/RECORD
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1.dist-info/REQUESTED
diff --git a/Lib/packaging/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py b/Lib/packaging/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py
new file mode 100644
index 0000000..191f895
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/towel_stuff-0.1/towel_stuff/__init__.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+
+class Towel(object):
+ """A towel, that one should never be without."""
+
+ def __init__(self, color='tie-dye'):
+ self.color = color
+ self.wrapped_obj = None
+
+ def wrap(self, obj):
+ """Wrap an object up in our towel."""
+ self.wrapped_obj = obj
+
+ def unwrap(self):
+ """Unwrap whatever is in our towel and return whatever it is."""
+ obj = self.wrapped_obj
+ self.wrapped_obj = None
+ return obj
diff --git a/Lib/packaging/tests/fake_dists/truffles-5.0.egg-info b/Lib/packaging/tests/fake_dists/truffles-5.0.egg-info
new file mode 100644
index 0000000..45f0cf8
--- /dev/null
+++ b/Lib/packaging/tests/fake_dists/truffles-5.0.egg-info
@@ -0,0 +1,3 @@
+Metadata-Version: 1.2
+Name: truffles
+Version: 5.0
diff --git a/Lib/packaging/tests/fixer/__init__.py b/Lib/packaging/tests/fixer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/fixer/__init__.py
diff --git a/Lib/packaging/tests/fixer/fix_idioms.py b/Lib/packaging/tests/fixer/fix_idioms.py
new file mode 100644
index 0000000..64f5ea0
--- /dev/null
+++ b/Lib/packaging/tests/fixer/fix_idioms.py
@@ -0,0 +1,134 @@
+"""Adjust some old Python 2 idioms to their modern counterparts.
+
+* Change some type comparisons to isinstance() calls:
+ type(x) == T -> isinstance(x, T)
+ type(x) is T -> isinstance(x, T)
+ type(x) != T -> not isinstance(x, T)
+ type(x) is not T -> not isinstance(x, T)
+
+* Change "while 1:" into "while True:".
+
+* Change both
+
+ v = list(EXPR)
+ v.sort()
+ foo(v)
+
+and the more general
+
+ v = EXPR
+ v.sort()
+ foo(v)
+
+into
+
+ v = sorted(EXPR)
+ foo(v)
+"""
+# Author: Jacques Frechet, Collin Winter
+
+# Local imports
+from lib2to3 import fixer_base
+from lib2to3.fixer_util import Call, Comma, Name, Node, syms
+
+CMP = "(n='!=' | '==' | 'is' | n=comp_op< 'is' 'not' >)"
+TYPE = "power< 'type' trailer< '(' x=any ')' > >"
+
+class FixIdioms(fixer_base.BaseFix):
+
+ explicit = False # The user must ask for this fixer
+
+ PATTERN = r"""
+ isinstance=comparison< %s %s T=any >
+ |
+ isinstance=comparison< T=any %s %s >
+ |
+ while_stmt< 'while' while='1' ':' any+ >
+ |
+ sorted=any<
+ any*
+ simple_stmt<
+ expr_stmt< id1=any '='
+ power< list='list' trailer< '(' (not arglist<any+>) any ')' > >
+ >
+ '\n'
+ >
+ sort=
+ simple_stmt<
+ power< id2=any
+ trailer< '.' 'sort' > trailer< '(' ')' >
+ >
+ '\n'
+ >
+ next=any*
+ >
+ |
+ sorted=any<
+ any*
+ simple_stmt< expr_stmt< id1=any '=' expr=any > '\n' >
+ sort=
+ simple_stmt<
+ power< id2=any
+ trailer< '.' 'sort' > trailer< '(' ')' >
+ >
+ '\n'
+ >
+ next=any*
+ >
+ """ % (TYPE, CMP, CMP, TYPE)
+
+ def match(self, node):
+ r = super(FixIdioms, self).match(node)
+ # If we've matched one of the sort/sorted subpatterns above, we
+ # want to reject matches where the initial assignment and the
+ # subsequent .sort() call involve different identifiers.
+ if r and "sorted" in r:
+ if r["id1"] == r["id2"]:
+ return r
+ return None
+ return r
+
+ def transform(self, node, results):
+ if "isinstance" in results:
+ return self.transform_isinstance(node, results)
+ elif "while" in results:
+ return self.transform_while(node, results)
+ elif "sorted" in results:
+ return self.transform_sort(node, results)
+ else:
+ raise RuntimeError("Invalid match")
+
+ def transform_isinstance(self, node, results):
+ x = results["x"].clone() # The thing inside of type()
+ T = results["T"].clone() # The type being compared against
+ x.prefix = ""
+ T.prefix = " "
+ test = Call(Name("isinstance"), [x, Comma(), T])
+ if "n" in results:
+ test.prefix = " "
+ test = Node(syms.not_test, [Name("not"), test])
+ test.prefix = node.prefix
+ return test
+
+ def transform_while(self, node, results):
+ one = results["while"]
+ one.replace(Name("True", prefix=one.prefix))
+
+ def transform_sort(self, node, results):
+ sort_stmt = results["sort"]
+ next_stmt = results["next"]
+ list_call = results.get("list")
+ simple_expr = results.get("expr")
+
+ if list_call:
+ list_call.replace(Name("sorted", prefix=list_call.prefix))
+ elif simple_expr:
+ new = simple_expr.clone()
+ new.prefix = ""
+ simple_expr.replace(Call(Name("sorted"), [new],
+ prefix=simple_expr.prefix))
+ else:
+ raise RuntimeError("should not have reached here")
+ sort_stmt.remove()
+ if next_stmt:
+ next_stmt[0].prefix = sort_stmt._prefix
diff --git a/Lib/packaging/tests/pypi_server.py b/Lib/packaging/tests/pypi_server.py
new file mode 100644
index 0000000..cc5fcca
--- /dev/null
+++ b/Lib/packaging/tests/pypi_server.py
@@ -0,0 +1,444 @@
+"""Mock PyPI Server implementation, to use in tests.
+
+This module also provides a simple test case to extend if you need to use
+the PyPIServer all along your test case. Be sure to read the documentation
+before any use.
+
+XXX TODO:
+
+The mock server can handle simple HTTP request (to simulate a simple index) or
+XMLRPC requests, over HTTP. Both does not have the same intergface to deal
+with, and I think it's a pain.
+
+A good idea could be to re-think a bit the way dstributions are handled in the
+mock server. As it should return malformed HTML pages, we need to keep the
+static behavior.
+
+I think of something like that:
+
+ >>> server = PyPIMockServer()
+ >>> server.startHTTP()
+ >>> server.startXMLRPC()
+
+Then, the server must have only one port to rely on, eg.
+
+ >>> server.fulladress()
+ "http://ip:port/"
+
+It could be simple to have one HTTP server, relaying the requests to the two
+implementations (static HTTP and XMLRPC over HTTP).
+"""
+
+import os
+import queue
+import select
+import socket
+import threading
+import socketserver
+from functools import wraps
+from http.server import HTTPServer, SimpleHTTPRequestHandler
+from xmlrpc.server import SimpleXMLRPCServer
+
+from packaging.tests import unittest
+
+PYPI_DEFAULT_STATIC_PATH = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)), 'pypiserver')
+
+
+def use_xmlrpc_server(*server_args, **server_kwargs):
+ server_kwargs['serve_xmlrpc'] = True
+ return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_http_server(*server_args, **server_kwargs):
+ server_kwargs['serve_xmlrpc'] = False
+ return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_pypi_server(*server_args, **server_kwargs):
+ """Decorator to make use of the PyPIServer for test methods,
+ just when needed, and not for the entire duration of the testcase.
+ """
+ def wrapper(func):
+ @wraps(func)
+ def wrapped(*args, **kwargs):
+ server = PyPIServer(*server_args, **server_kwargs)
+ server.start()
+ try:
+ func(server=server, *args, **kwargs)
+ finally:
+ server.stop()
+ return wrapped
+ return wrapper
+
+
+class PyPIServerTestCase(unittest.TestCase):
+
+ def setUp(self):
+ super(PyPIServerTestCase, self).setUp()
+ self.pypi = PyPIServer()
+ self.pypi.start()
+ self.addCleanup(self.pypi.stop)
+
+
+class PyPIServer(threading.Thread):
+ """PyPI Mocked server.
+ Provides a mocked version of the PyPI API's, to ease tests.
+
+ Support serving static content and serving previously given text.
+ """
+
+ def __init__(self, test_static_path=None,
+ static_filesystem_paths=["default"],
+ static_uri_paths=["simple", "packages"], serve_xmlrpc=False):
+ """Initialize the server.
+
+ Default behavior is to start the HTTP server. You can either start the
+ xmlrpc server by setting xmlrpc to True. Caution: Only one server will
+ be started.
+
+ static_uri_paths and static_base_path are parameters used to provides
+ respectively the http_paths to serve statically, and where to find the
+ matching files on the filesystem.
+ """
+ # we want to launch the server in a new dedicated thread, to not freeze
+ # tests.
+ threading.Thread.__init__(self)
+ self._run = True
+ self._serve_xmlrpc = serve_xmlrpc
+
+ #TODO allow to serve XMLRPC and HTTP static files at the same time.
+ if not self._serve_xmlrpc:
+ self.server = HTTPServer(('127.0.0.1', 0), PyPIRequestHandler)
+ self.server.RequestHandlerClass.pypi_server = self
+
+ self.request_queue = queue.Queue()
+ self._requests = []
+ self.default_response_status = 404
+ self.default_response_headers = [('Content-type', 'text/plain')]
+ self.default_response_data = "The page does not exists"
+
+ # initialize static paths / filesystems
+ self.static_uri_paths = static_uri_paths
+
+ # append the static paths defined locally
+ if test_static_path is not None:
+ static_filesystem_paths.append(test_static_path)
+ self.static_filesystem_paths = [
+ PYPI_DEFAULT_STATIC_PATH + "/" + path
+ for path in static_filesystem_paths]
+ else:
+ # XMLRPC server
+ self.server = PyPIXMLRPCServer(('127.0.0.1', 0))
+ self.xmlrpc = XMLRPCMockIndex()
+ # register the xmlrpc methods
+ self.server.register_introspection_functions()
+ self.server.register_instance(self.xmlrpc)
+
+ self.address = (self.server.server_name, self.server.server_port)
+ # to not have unwanted outputs.
+ self.server.RequestHandlerClass.log_request = lambda *_: None
+
+ def run(self):
+ # loop because we can't stop it otherwise, for python < 2.6
+ while self._run:
+ r, w, e = select.select([self.server], [], [], 0.5)
+ if r:
+ self.server.handle_request()
+
+ def stop(self):
+ """self shutdown is not supported for python < 2.6"""
+ self._run = False
+
+ def get_next_response(self):
+ return (self.default_response_status,
+ self.default_response_headers,
+ self.default_response_data)
+
+ @property
+ def requests(self):
+ """Use this property to get all requests that have been made
+ to the server
+ """
+ while True:
+ try:
+ self._requests.append(self.request_queue.get_nowait())
+ except queue.Empty:
+ break
+ return self._requests
+
+ @property
+ def full_address(self):
+ return "http://%s:%s" % self.address
+
+
+class PyPIRequestHandler(SimpleHTTPRequestHandler):
+ # we need to access the pypi server while serving the content
+ pypi_server = None
+
+ def serve_request(self):
+ """Serve the content.
+
+ Also record the requests to be accessed later. If trying to access an
+ url matching a static uri, serve static content, otherwise serve
+ what is provided by the `get_next_response` method.
+
+ If nothing is defined there, return a 404 header.
+ """
+ # record the request. Read the input only on PUT or POST requests
+ if self.command in ("PUT", "POST"):
+ if 'content-length' in self.headers:
+ request_data = self.rfile.read(
+ int(self.headers['content-length']))
+ else:
+ request_data = self.rfile.read()
+
+ elif self.command in ("GET", "DELETE"):
+ request_data = ''
+
+ self.pypi_server.request_queue.put((self, request_data))
+
+ # serve the content from local disc if we request an URL beginning
+ # by a pattern defined in `static_paths`
+ url_parts = self.path.split("/")
+ if (len(url_parts) > 1 and
+ url_parts[1] in self.pypi_server.static_uri_paths):
+ data = None
+ # always take the last first.
+ fs_paths = []
+ fs_paths.extend(self.pypi_server.static_filesystem_paths)
+ fs_paths.reverse()
+ relative_path = self.path
+ for fs_path in fs_paths:
+ try:
+ if self.path.endswith("/"):
+ relative_path += "index.html"
+
+ if relative_path.endswith('.tar.gz'):
+ with open(fs_path + relative_path, 'br') as file:
+ data = file.read()
+ headers = [('Content-type', 'application/x-gtar')]
+ else:
+ with open(fs_path + relative_path) as file:
+ data = file.read().encode()
+ headers = [('Content-type', 'text/html')]
+
+ self.make_response(data, headers=headers)
+
+ except IOError:
+ pass
+
+ if data is None:
+ self.make_response("Not found", 404)
+
+ # otherwise serve the content from get_next_response
+ else:
+ # send back a response
+ status, headers, data = self.pypi_server.get_next_response()
+ self.make_response(data, status, headers)
+
+ do_POST = do_GET = do_DELETE = do_PUT = serve_request
+
+ def make_response(self, data, status=200,
+ headers=[('Content-type', 'text/html')]):
+ """Send the response to the HTTP client"""
+ if not isinstance(status, int):
+ try:
+ status = int(status)
+ except ValueError:
+ # we probably got something like YYY Codename.
+ # Just get the first 3 digits
+ status = int(status[:3])
+
+ self.send_response(status)
+ for header, value in headers:
+ self.send_header(header, value)
+ self.end_headers()
+
+ if type(data) is str:
+ data = data.encode()
+
+ self.wfile.write(data)
+
+
+class PyPIXMLRPCServer(SimpleXMLRPCServer):
+ def server_bind(self):
+ """Override server_bind to store the server name."""
+ socketserver.TCPServer.server_bind(self)
+ host, port = self.socket.getsockname()[:2]
+ self.server_name = socket.getfqdn(host)
+ self.server_port = port
+
+
+class MockDist:
+ """Fake distribution, used in the Mock PyPI Server"""
+
+ def __init__(self, name, version="1.0", hidden=False, url="http://url/",
+ type="sdist", filename="", size=10000,
+ digest="123456", downloads=7, has_sig=False,
+ python_version="source", comment="comment",
+ author="John Doe", author_email="john@doe.name",
+ maintainer="Main Tayner", maintainer_email="maintainer_mail",
+ project_url="http://project_url/", homepage="http://homepage/",
+ keywords="", platform="UNKNOWN", classifiers=[], licence="",
+ description="Description", summary="Summary", stable_version="",
+ ordering="", documentation_id="", code_kwalitee_id="",
+ installability_id="", obsoletes=[], obsoletes_dist=[],
+ provides=[], provides_dist=[], requires=[], requires_dist=[],
+ requires_external=[], requires_python=""):
+
+ # basic fields
+ self.name = name
+ self.version = version
+ self.hidden = hidden
+
+ # URL infos
+ self.url = url
+ self.digest = digest
+ self.downloads = downloads
+ self.has_sig = has_sig
+ self.python_version = python_version
+ self.comment = comment
+ self.type = type
+
+ # metadata
+ self.author = author
+ self.author_email = author_email
+ self.maintainer = maintainer
+ self.maintainer_email = maintainer_email
+ self.project_url = project_url
+ self.homepage = homepage
+ self.keywords = keywords
+ self.platform = platform
+ self.classifiers = classifiers
+ self.licence = licence
+ self.description = description
+ self.summary = summary
+ self.stable_version = stable_version
+ self.ordering = ordering
+ self.cheesecake_documentation_id = documentation_id
+ self.cheesecake_code_kwalitee_id = code_kwalitee_id
+ self.cheesecake_installability_id = installability_id
+
+ self.obsoletes = obsoletes
+ self.obsoletes_dist = obsoletes_dist
+ self.provides = provides
+ self.provides_dist = provides_dist
+ self.requires = requires
+ self.requires_dist = requires_dist
+ self.requires_external = requires_external
+ self.requires_python = requires_python
+
+ def url_infos(self):
+ return {
+ 'url': self.url,
+ 'packagetype': self.type,
+ 'filename': 'filename.tar.gz',
+ 'size': '6000',
+ 'md5_digest': self.digest,
+ 'downloads': self.downloads,
+ 'has_sig': self.has_sig,
+ 'python_version': self.python_version,
+ 'comment_text': self.comment,
+ }
+
+ def metadata(self):
+ return {
+ 'maintainer': self.maintainer,
+ 'project_url': [self.project_url],
+ 'maintainer_email': self.maintainer_email,
+ 'cheesecake_code_kwalitee_id': self.cheesecake_code_kwalitee_id,
+ 'keywords': self.keywords,
+ 'obsoletes_dist': self.obsoletes_dist,
+ 'requires_external': self.requires_external,
+ 'author': self.author,
+ 'author_email': self.author_email,
+ 'download_url': self.url,
+ 'platform': self.platform,
+ 'version': self.version,
+ 'obsoletes': self.obsoletes,
+ 'provides': self.provides,
+ 'cheesecake_documentation_id': self.cheesecake_documentation_id,
+ '_pypi_hidden': self.hidden,
+ 'description': self.description,
+ '_pypi_ordering': 19,
+ 'requires_dist': self.requires_dist,
+ 'requires_python': self.requires_python,
+ 'classifiers': [],
+ 'name': self.name,
+ 'licence': self.licence,
+ 'summary': self.summary,
+ 'home_page': self.homepage,
+ 'stable_version': self.stable_version,
+ 'provides_dist': self.provides_dist or "%s (%s)" % (self.name,
+ self.version),
+ 'requires': self.requires,
+ 'cheesecake_installability_id': self.cheesecake_installability_id,
+ }
+
+ def search_result(self):
+ return {
+ '_pypi_ordering': 0,
+ 'version': self.version,
+ 'name': self.name,
+ 'summary': self.summary,
+ }
+
+
+class XMLRPCMockIndex:
+ """Mock XMLRPC server"""
+
+ def __init__(self, dists=[]):
+ self._dists = dists
+ self._search_result = []
+
+ def add_distributions(self, dists):
+ for dist in dists:
+ self._dists.append(MockDist(**dist))
+
+ def set_distributions(self, dists):
+ self._dists = []
+ self.add_distributions(dists)
+
+ def set_search_result(self, result):
+ """set a predefined search result"""
+ self._search_result = result
+
+ def _get_search_results(self):
+ results = []
+ for name in self._search_result:
+ found_dist = [d for d in self._dists if d.name == name]
+ if found_dist:
+ results.append(found_dist[0])
+ else:
+ dist = MockDist(name)
+ results.append(dist)
+ self._dists.append(dist)
+ return [r.search_result() for r in results]
+
+ def list_packages(self):
+ return [d.name for d in self._dists]
+
+ def package_releases(self, package_name, show_hidden=False):
+ if show_hidden:
+ # return all
+ return [d.version for d in self._dists if d.name == package_name]
+ else:
+ # return only un-hidden
+ return [d.version for d in self._dists if d.name == package_name
+ and not d.hidden]
+
+ def release_urls(self, package_name, version):
+ return [d.url_infos() for d in self._dists
+ if d.name == package_name and d.version == version]
+
+ def release_data(self, package_name, version):
+ release = [d for d in self._dists
+ if d.name == package_name and d.version == version]
+ if release:
+ return release[0].metadata()
+ else:
+ return {}
+
+ def search(self, spec, operator="and"):
+ return self._get_search_results()
diff --git a/Lib/packaging/tests/pypi_test_server.py b/Lib/packaging/tests/pypi_test_server.py
new file mode 100644
index 0000000..8c8c641
--- /dev/null
+++ b/Lib/packaging/tests/pypi_test_server.py
@@ -0,0 +1,59 @@
+"""Test PyPI Server implementation at testpypi.python.org, to use in tests.
+
+This is a drop-in replacement for the mock pypi server for testing against a
+real pypi server hosted by python.org especially for testing against.
+"""
+
+import unittest
+
+PYPI_DEFAULT_STATIC_PATH = None
+
+
+def use_xmlrpc_server(*server_args, **server_kwargs):
+ server_kwargs['serve_xmlrpc'] = True
+ return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_http_server(*server_args, **server_kwargs):
+ server_kwargs['serve_xmlrpc'] = False
+ return use_pypi_server(*server_args, **server_kwargs)
+
+
+def use_pypi_server(*server_args, **server_kwargs):
+ """Decorator to make use of the PyPIServer for test methods,
+ just when needed, and not for the entire duration of the testcase.
+ """
+ def wrapper(func):
+ def wrapped(*args, **kwargs):
+ server = PyPIServer(*server_args, **server_kwargs)
+ func(server=server, *args, **kwargs)
+ return wrapped
+ return wrapper
+
+
+class PyPIServerTestCase(unittest.TestCase):
+
+ def setUp(self):
+ super(PyPIServerTestCase, self).setUp()
+ self.pypi = PyPIServer()
+ self.pypi.start()
+ self.addCleanup(self.pypi.stop)
+
+
+class PyPIServer:
+ """Shim to access testpypi.python.org, for testing a real server."""
+
+ def __init__(self, test_static_path=None,
+ static_filesystem_paths=["default"],
+ static_uri_paths=["simple"], serve_xmlrpc=False):
+ self.address = ('testpypi.python.org', '80')
+
+ def start(self):
+ pass
+
+ def stop(self):
+ pass
+
+ @property
+ def full_address(self):
+ return "http://%s:%s" % self.address
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gz b/Lib/packaging/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gz
new file mode 100644
index 0000000..333961e
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/packages/source/f/foobar/foobar-0.1.tar.gz
Binary files differ
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html
new file mode 100644
index 0000000..b89f1bd
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html
@@ -0,0 +1,3 @@
+<html><body>
+<a href="badmd5-0.1.tar.gz#md5=3e3d86693d6564c807272b11b3069dfe" rel="download">badmd5-0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/foobar/index.html
new file mode 100644
index 0000000..9e42b16
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/foobar/index.html
@@ -0,0 +1,3 @@
+<html><body>
+<a href="foobar-0.1.tar.gz#md5=fe18804c5b722ff024cabdf514924fc4" rel="download">foobar-0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/index.html b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/index.html
new file mode 100644
index 0000000..9baee04
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/downloads_with_md5/simple/index.html
@@ -0,0 +1,2 @@
+<a href="foobar/">foobar/</a>
+<a href="badmd5/">badmd5/</a>
diff --git a/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/bar/index.html b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/bar/index.html
new file mode 100644
index 0000000..c3d42c5
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/bar/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for bar</title></head><body><h1>Links for bar</h1>
+<a rel="download" href="../../packages/source/F/bar/bar-1.0.tar.gz">bar-1.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/bar/bar-1.0.1.tar.gz">bar-1.0.1.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/bar/bar-2.0.tar.gz">bar-2.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/bar/bar-2.0.1.tar.gz">bar-2.0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/baz/index.html b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/baz/index.html
new file mode 100644
index 0000000..4f34312
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/baz/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for baz</title></head><body><h1>Links for baz</h1>
+<a rel="download" href="../../packages/source/F/baz/baz-1.0.tar.gz">baz-1.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/baz/baz-1.0.1.tar.gz">baz-1.0.1.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/baz/baz-2.0.tar.gz">baz-2.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/baz/baz-2.0.1.tar.gz">baz-2.0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/foo/index.html b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/foo/index.html
new file mode 100644
index 0000000..0565e11
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/foo/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for foo</title></head><body><h1>Links for foo</h1>
+<a rel="download" href="../../packages/source/F/foo/foo-1.0.tar.gz">foo-1.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/foo/foo-1.0.1.tar.gz">foo-1.0.1.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/foo/foo-2.0.tar.gz">foo-2.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/foo/foo-2.0.1.tar.gz">foo-2.0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/index.html b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/index.html
new file mode 100644
index 0000000..a70cfd3
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/foo_bar_baz/simple/index.html
@@ -0,0 +1,3 @@
+<a href="foo/">foo/</a>
+<a href="bar/">bar/</a>
+<a href="baz/">baz/</a>
diff --git a/Lib/packaging/tests/pypiserver/project_list/simple/index.html b/Lib/packaging/tests/pypiserver/project_list/simple/index.html
new file mode 100644
index 0000000..b36d728
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/project_list/simple/index.html
@@ -0,0 +1,5 @@
+<a class="test" href="yeah">FooBar-bar</a>
+<a class="test" href="yeah">Foobar-baz</a>
+<a class="test" href="yeah">Baz-FooBar</a>
+<a class="test" href="yeah">Baz</a>
+<a class="test" href="yeah">Foo</a>
diff --git a/Lib/packaging/tests/pypiserver/test_found_links/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/test_found_links/simple/foobar/index.html
new file mode 100644
index 0000000..a282a4e
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/test_found_links/simple/foobar/index.html
@@ -0,0 +1,6 @@
+<html><head><title>Links for Foobar</title></head><body><h1>Links for Foobar</h1>
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-1.0.tar.gz#md5=98fa833fdabcdd78d00245aead66c174">Foobar-1.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-1.0.1.tar.gz#md5=2351efb20f6b7b5d9ce80fa4cb1bd9ca">Foobar-1.0.1.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-2.0.tar.gz#md5=98fa833fdabcdd78d00245aead66c274">Foobar-2.0.tar.gz</a><br/>
+<a rel="download" href="../../packages/source/F/Foobar/Foobar-2.0.1.tar.gz#md5=2352efb20f6b7b5d9ce80fa4cb2bd9ca">Foobar-2.0.1.tar.gz</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/test_found_links/simple/index.html b/Lib/packaging/tests/pypiserver/test_found_links/simple/index.html
new file mode 100644
index 0000000..a1a7bb7
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/test_found_links/simple/index.html
@@ -0,0 +1 @@
+<a href="foobar/">foobar/</a>
diff --git a/Lib/packaging/tests/pypiserver/test_pypi_server/external/index.html b/Lib/packaging/tests/pypiserver/test_pypi_server/external/index.html
new file mode 100644
index 0000000..265ee0a
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/test_pypi_server/external/index.html
@@ -0,0 +1 @@
+index.html from external server
diff --git a/Lib/packaging/tests/pypiserver/test_pypi_server/simple/index.html b/Lib/packaging/tests/pypiserver/test_pypi_server/simple/index.html
new file mode 100644
index 0000000..6f97667
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/test_pypi_server/simple/index.html
@@ -0,0 +1 @@
+Yeah
diff --git a/Lib/packaging/tests/pypiserver/with_externals/external/external.html b/Lib/packaging/tests/pypiserver/with_externals/external/external.html
new file mode 100644
index 0000000..92e4702
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_externals/external/external.html
@@ -0,0 +1,3 @@
+<html><body>
+<a href="/foobar-0.1.tar.gz#md5=1__bad_md5___">bad old link</a>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/with_externals/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/with_externals/simple/foobar/index.html
new file mode 100644
index 0000000..b100a26
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_externals/simple/foobar/index.html
@@ -0,0 +1,4 @@
+<html><body>
+<a rel ="download" href="/foobar-0.1.tar.gz#md5=12345678901234567">foobar-0.1.tar.gz</a><br/>
+<a href="../../external/external.html" rel="homepage">external homepage</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/with_externals/simple/index.html b/Lib/packaging/tests/pypiserver/with_externals/simple/index.html
new file mode 100644
index 0000000..a1a7bb7
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_externals/simple/index.html
@@ -0,0 +1 @@
+<a href="foobar/">foobar/</a>
diff --git a/Lib/packaging/tests/pypiserver/with_norel_links/external/homepage.html b/Lib/packaging/tests/pypiserver/with_norel_links/external/homepage.html
new file mode 100644
index 0000000..1cc0c32
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_norel_links/external/homepage.html
@@ -0,0 +1,7 @@
+<html>
+<body>
+<p>a rel=homepage HTML page</p>
+<a href="/foobar-2.0.tar.gz">foobar 2.0</a>
+</body>
+</html>
+
diff --git a/Lib/packaging/tests/pypiserver/with_norel_links/external/nonrel.html b/Lib/packaging/tests/pypiserver/with_norel_links/external/nonrel.html
new file mode 100644
index 0000000..f6ace22
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_norel_links/external/nonrel.html
@@ -0,0 +1 @@
+A page linked without rel="download" or rel="homepage" link.
diff --git a/Lib/packaging/tests/pypiserver/with_norel_links/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/with_norel_links/simple/foobar/index.html
new file mode 100644
index 0000000..171df93
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_norel_links/simple/foobar/index.html
@@ -0,0 +1,6 @@
+<html><body>
+<a rel="download" href="/foobar-0.1.tar.gz" rel="download">foobar-0.1.tar.gz</a><br/>
+<a href="../../external/homepage.html" rel="homepage">external homepage</a><br/>
+<a href="../../external/nonrel.html">unrelated link</a><br/>
+<a href="/unrelated-0.2.tar.gz">unrelated download</a></br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/with_norel_links/simple/index.html b/Lib/packaging/tests/pypiserver/with_norel_links/simple/index.html
new file mode 100644
index 0000000..a1a7bb7
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_norel_links/simple/index.html
@@ -0,0 +1 @@
+<a href="foobar/">foobar/</a>
diff --git a/Lib/packaging/tests/pypiserver/with_real_externals/simple/foobar/index.html b/Lib/packaging/tests/pypiserver/with_real_externals/simple/foobar/index.html
new file mode 100644
index 0000000..b2885ae
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_real_externals/simple/foobar/index.html
@@ -0,0 +1,4 @@
+<html><body>
+<a rel="download" href="/foobar-0.1.tar.gz#md5=0_correct_md5">foobar-0.1.tar.gz</a><br/>
+<a href="http://a-really-external-website/external/external.html" rel="homepage">external homepage</a><br/>
+</body></html>
diff --git a/Lib/packaging/tests/pypiserver/with_real_externals/simple/index.html b/Lib/packaging/tests/pypiserver/with_real_externals/simple/index.html
new file mode 100644
index 0000000..a1a7bb7
--- /dev/null
+++ b/Lib/packaging/tests/pypiserver/with_real_externals/simple/index.html
@@ -0,0 +1 @@
+<a href="foobar/">foobar/</a>
diff --git a/Lib/packaging/tests/support.py b/Lib/packaging/tests/support.py
new file mode 100644
index 0000000..cf5d788
--- /dev/null
+++ b/Lib/packaging/tests/support.py
@@ -0,0 +1,259 @@
+"""Support code for packaging test cases.
+
+A few helper classes are provided: LoggingCatcher, TempdirManager and
+EnvironRestorer. They are written to be used as mixins::
+
+ from packaging.tests import unittest
+ from packaging.tests.support import LoggingCatcher
+
+ class SomeTestCase(LoggingCatcher, unittest.TestCase):
+
+If you need to define a setUp method on your test class, you have to
+call the mixin class' setUp method or it won't work (same thing for
+tearDown):
+
+ def setUp(self):
+ super(SomeTestCase, self).setUp()
+ ... # other setup code
+
+Also provided is a DummyCommand class, useful to mock commands in the
+tests of another command that needs them, a create_distribution function
+and a skip_unless_symlink decorator.
+
+Also provided is a DummyCommand class, useful to mock commands in the
+tests of another command that needs them, a create_distribution function
+and a skip_unless_symlink decorator.
+
+Each class or function has a docstring to explain its purpose and usage.
+"""
+
+import os
+import errno
+import shutil
+import logging
+import weakref
+import tempfile
+
+from packaging import logger
+from packaging.dist import Distribution
+from packaging.tests import unittest
+
+__all__ = ['LoggingCatcher', 'TempdirManager', 'EnvironRestorer',
+ 'DummyCommand', 'unittest', 'create_distribution',
+ 'skip_unless_symlink']
+
+
+class _TestHandler(logging.handlers.BufferingHandler):
+ # stolen and adapted from test.support
+
+ def __init__(self):
+ logging.handlers.BufferingHandler.__init__(self, 0)
+ self.setLevel(logging.DEBUG)
+
+ def shouldFlush(self):
+ return False
+
+ def emit(self, record):
+ self.buffer.append(record)
+
+
+class LoggingCatcher:
+ """TestCase-compatible mixin to receive logging calls.
+
+ Upon setUp, instances of this classes get a BufferingHandler that's
+ configured to record all messages logged to the 'packaging' logger.
+
+ Use get_logs to retrieve messages and self.loghandler.flush to discard
+ them.
+ """
+
+ def setUp(self):
+ super(LoggingCatcher, self).setUp()
+ self.loghandler = handler = _TestHandler()
+ logger.addHandler(handler)
+ self.addCleanup(logger.setLevel, logger.level)
+ logger.setLevel(logging.DEBUG) # we want all messages
+
+ def tearDown(self):
+ handler = self.loghandler
+ # All this is necessary to properly shut down the logging system and
+ # avoid a regrtest complaint. Thanks to Vinay Sajip for the help.
+ handler.close()
+ logger.removeHandler(handler)
+ for ref in weakref.getweakrefs(handler):
+ logging._removeHandlerRef(ref)
+ del self.loghandler
+ super(LoggingCatcher, self).tearDown()
+
+ def get_logs(self, *levels):
+ """Return all log messages with level in *levels*.
+
+ Without explicit levels given, returns all messages.
+ *levels* defaults to all levels. For log calls with arguments (i.e.
+ logger.info('bla bla %s', arg)), the messages
+ Returns a list.
+
+ Example: self.get_logs(logging.WARN, logging.DEBUG).
+ """
+ if not levels:
+ return [log.getMessage() for log in self.loghandler.buffer]
+ return [log.getMessage() for log in self.loghandler.buffer
+ if log.levelno in levels]
+
+
+class TempdirManager:
+ """TestCase-compatible mixin to create temporary directories and files.
+
+ Directories and files created in a test_* method will be removed after it
+ has run.
+ """
+
+ def setUp(self):
+ super(TempdirManager, self).setUp()
+ self._basetempdir = tempfile.mkdtemp()
+
+ def tearDown(self):
+ shutil.rmtree(self._basetempdir, os.name in ('nt', 'cygwin'))
+ super(TempdirManager, self).tearDown()
+
+ def mktempfile(self):
+ """Create a read-write temporary file and return it."""
+
+ def _delete_file(filename):
+ try:
+ os.remove(filename)
+ except OSError as exc:
+ if exc.errno != errno.ENOENT:
+ raise
+
+ fd, fn = tempfile.mkstemp(dir=self._basetempdir)
+ os.close(fd)
+ fp = open(fn, 'w+')
+ self.addCleanup(fp.close)
+ self.addCleanup(_delete_file, fn)
+ return fp
+
+ def mkdtemp(self):
+ """Create a temporary directory and return its path."""
+ d = tempfile.mkdtemp(dir=self._basetempdir)
+ return d
+
+ def write_file(self, path, content='xxx'):
+ """Write a file at the given path.
+
+ path can be a string, a tuple or a list; if it's a tuple or list,
+ os.path.join will be used to produce a path.
+ """
+ if isinstance(path, (list, tuple)):
+ path = os.path.join(*path)
+ f = open(path, 'w')
+ try:
+ f.write(content)
+ finally:
+ f.close()
+
+ def create_dist(self, **kw):
+ """Create a stub distribution object and files.
+
+ This function creates a Distribution instance (use keyword arguments
+ to customize it) and a temporary directory with a project structure
+ (currently an empty directory).
+
+ It returns the path to the directory and the Distribution instance.
+ You can use self.write_file to write any file in that
+ directory, e.g. setup scripts or Python modules.
+ """
+ if 'name' not in kw:
+ kw['name'] = 'foo'
+ tmp_dir = self.mkdtemp()
+ project_dir = os.path.join(tmp_dir, kw['name'])
+ os.mkdir(project_dir)
+ dist = Distribution(attrs=kw)
+ return project_dir, dist
+
+ def assertIsFile(self, *args):
+ path = os.path.join(*args)
+ dirname = os.path.dirname(path)
+ file = os.path.basename(path)
+ if os.path.isdir(dirname):
+ files = os.listdir(dirname)
+ msg = "%s not found in %s: %s" % (file, dirname, files)
+ assert os.path.isfile(path), msg
+ else:
+ raise AssertionError(
+ '%s not found. %s does not exist' % (file, dirname))
+
+ def assertIsNotFile(self, *args):
+ path = os.path.join(*args)
+ self.assertFalse(os.path.isfile(path), "%r exists" % path)
+
+
+class EnvironRestorer:
+ """TestCase-compatible mixin to restore or delete environment variables.
+
+ The variables to restore (or delete if they were not originally present)
+ must be explicitly listed in self.restore_environ. It's better to be
+ aware of what we're modifying instead of saving and restoring the whole
+ environment.
+ """
+
+ def setUp(self):
+ super(EnvironRestorer, self).setUp()
+ self._saved = []
+ self._added = []
+ for key in self.restore_environ:
+ if key in os.environ:
+ self._saved.append((key, os.environ[key]))
+ else:
+ self._added.append(key)
+
+ def tearDown(self):
+ for key, value in self._saved:
+ os.environ[key] = value
+ for key in self._added:
+ os.environ.pop(key, None)
+ super(EnvironRestorer, self).tearDown()
+
+
+class DummyCommand:
+ """Class to store options for retrieval via set_undefined_options().
+
+ Useful for mocking one dependency command in the tests for another
+ command, see e.g. the dummy build command in test_build_scripts.
+ """
+
+ def __init__(self, **kwargs):
+ for kw, val in kwargs.items():
+ setattr(self, kw, val)
+
+ def ensure_finalized(self):
+ pass
+
+
+class TestDistribution(Distribution):
+ """Distribution subclasses that avoids the default search for
+ configuration files.
+
+ The ._config_files attribute must be set before
+ .parse_config_files() is called.
+ """
+
+ def find_config_files(self):
+ return self._config_files
+
+
+def create_distribution(configfiles=()):
+ """Prepares a distribution with given config files parsed."""
+ d = TestDistribution()
+ d.config.find_config_files = d.find_config_files
+ d._config_files = configfiles
+ d.parse_config_files()
+ d.parse_command_line()
+ return d
+
+
+try:
+ from test.support import skip_unless_symlink
+except ImportError:
+ skip_unless_symlink = unittest.skip(
+ 'requires test.support.skip_unless_symlink')
diff --git a/Lib/packaging/tests/test_ccompiler.py b/Lib/packaging/tests/test_ccompiler.py
new file mode 100644
index 0000000..dd4bdd9
--- /dev/null
+++ b/Lib/packaging/tests/test_ccompiler.py
@@ -0,0 +1,15 @@
+"""Tests for distutils.compiler.ccompiler."""
+
+from packaging.compiler import ccompiler
+from packaging.tests import unittest, support
+
+
+class CCompilerTestCase(unittest.TestCase):
+ pass # XXX need some tests on CCompiler
+
+
+def test_suite():
+ return unittest.makeSuite(CCompilerTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_bdist.py b/Lib/packaging/tests/test_command_bdist.py
new file mode 100644
index 0000000..1522b7e
--- /dev/null
+++ b/Lib/packaging/tests/test_command_bdist.py
@@ -0,0 +1,77 @@
+"""Tests for distutils.command.bdist."""
+
+from packaging import util
+from packaging.command.bdist import bdist, show_formats
+
+from packaging.tests import unittest, support, captured_stdout
+
+
+class BuildTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def _mock_get_platform(self):
+ self._get_platform_called = True
+ return self._get_platform()
+
+ def setUp(self):
+ super(BuildTestCase, self).setUp()
+
+ # mock util.get_platform
+ self._get_platform_called = False
+ self._get_platform = util.get_platform
+ util.get_platform = self._mock_get_platform
+
+ def tearDown(self):
+ super(BuildTestCase, self).tearDown()
+ util.get_platform = self._get_platform
+
+ def test_formats(self):
+
+ # let's create a command and make sure
+ # we can fix the format
+ pkg_pth, dist = self.create_dist()
+ cmd = bdist(dist)
+ cmd.formats = ['msi']
+ cmd.ensure_finalized()
+ self.assertEqual(cmd.formats, ['msi'])
+
+ # what format bdist offers ?
+ # XXX an explicit list in bdist is
+ # not the best way to bdist_* commands
+ # we should add a registry
+ formats = sorted(('zip', 'gztar', 'bztar', 'ztar',
+ 'tar', 'wininst', 'msi'))
+ found = sorted(cmd.format_command)
+ self.assertEqual(found, formats)
+
+ def test_skip_build(self):
+ pkg_pth, dist = self.create_dist()
+ cmd = bdist(dist)
+ cmd.skip_build = False
+ cmd.formats = ['ztar']
+ cmd.ensure_finalized()
+ self.assertFalse(self._get_platform_called)
+
+ pkg_pth, dist = self.create_dist()
+ cmd = bdist(dist)
+ cmd.skip_build = True
+ cmd.formats = ['ztar']
+ cmd.ensure_finalized()
+ self.assertTrue(self._get_platform_called)
+
+ def test_show_formats(self):
+ __, stdout = captured_stdout(show_formats)
+
+ # the output should be a header line + one line per format
+ num_formats = len(bdist.format_commands)
+ output = [line for line in stdout.split('\n')
+ if line.strip().startswith('--formats=')]
+ self.assertEqual(len(output), num_formats)
+
+
+def test_suite():
+ return unittest.makeSuite(BuildTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_bdist_dumb.py b/Lib/packaging/tests/test_command_bdist_dumb.py
new file mode 100644
index 0000000..ce1563f
--- /dev/null
+++ b/Lib/packaging/tests/test_command_bdist_dumb.py
@@ -0,0 +1,103 @@
+"""Tests for distutils.command.bdist_dumb."""
+
+import sys
+import os
+
+# zlib is not used here, but if it's not available
+# test_simple_built will fail
+try:
+ import zlib
+except ImportError:
+ zlib = None
+
+from packaging.dist import Distribution
+from packaging.command.bdist_dumb import bdist_dumb
+from packaging.tests import unittest, support
+
+
+SETUP_PY = """\
+from distutils.run import setup
+import foo
+
+setup(name='foo', version='0.1', py_modules=['foo'],
+ url='xxx', author='xxx', author_email='xxx')
+"""
+
+
+class BuildDumbTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def setUp(self):
+ super(BuildDumbTestCase, self).setUp()
+ self.old_location = os.getcwd()
+ self.old_sys_argv = sys.argv, sys.argv[:]
+
+ def tearDown(self):
+ os.chdir(self.old_location)
+ sys.argv = self.old_sys_argv[0]
+ sys.argv[:] = self.old_sys_argv[1]
+ super(BuildDumbTestCase, self).tearDown()
+
+ @unittest.skipUnless(zlib, "requires zlib")
+ def test_simple_built(self):
+
+ # let's create a simple package
+ tmp_dir = self.mkdtemp()
+ pkg_dir = os.path.join(tmp_dir, 'foo')
+ os.mkdir(pkg_dir)
+ self.write_file((pkg_dir, 'setup.py'), SETUP_PY)
+ self.write_file((pkg_dir, 'foo.py'), '#')
+ self.write_file((pkg_dir, 'MANIFEST.in'), 'include foo.py')
+ self.write_file((pkg_dir, 'README'), '')
+
+ dist = Distribution({'name': 'foo', 'version': '0.1',
+ 'py_modules': ['foo'],
+ 'url': 'xxx', 'author': 'xxx',
+ 'author_email': 'xxx'})
+ dist.script_name = 'setup.py'
+ os.chdir(pkg_dir)
+
+ sys.argv[:] = ['setup.py']
+ cmd = bdist_dumb(dist)
+
+ # so the output is the same no matter
+ # what is the platform
+ cmd.format = 'zip'
+
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # see what we have
+ dist_created = os.listdir(os.path.join(pkg_dir, 'dist'))
+ base = "%s.%s" % (dist.get_fullname(), cmd.plat_name)
+ if os.name == 'os2':
+ base = base.replace(':', '-')
+
+ wanted = ['%s.zip' % base]
+ self.assertEqual(dist_created, wanted)
+
+ # now let's check what we have in the zip file
+ # XXX to be done
+
+ def test_finalize_options(self):
+ pkg_dir, dist = self.create_dist()
+ os.chdir(pkg_dir)
+ cmd = bdist_dumb(dist)
+ self.assertEqual(cmd.bdist_dir, None)
+ cmd.finalize_options()
+
+ # bdist_dir is initialized to bdist_base/dumb if not set
+ base = cmd.get_finalized_command('bdist').bdist_base
+ self.assertEqual(cmd.bdist_dir, os.path.join(base, 'dumb'))
+
+ # the format is set to a default value depending on the os.name
+ default = cmd.default_format[os.name]
+ self.assertEqual(cmd.format, default)
+
+
+def test_suite():
+ return unittest.makeSuite(BuildDumbTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_bdist_msi.py b/Lib/packaging/tests/test_command_bdist_msi.py
new file mode 100644
index 0000000..fded962
--- /dev/null
+++ b/Lib/packaging/tests/test_command_bdist_msi.py
@@ -0,0 +1,25 @@
+"""Tests for distutils.command.bdist_msi."""
+import sys
+
+from packaging.tests import unittest, support
+
+
+class BDistMSITestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ @unittest.skipUnless(sys.platform == "win32", "runs only on win32")
+ def test_minimal(self):
+ # minimal test XXX need more tests
+ from packaging.command.bdist_msi import bdist_msi
+ pkg_pth, dist = self.create_dist()
+ cmd = bdist_msi(dist)
+ cmd.ensure_finalized()
+
+
+def test_suite():
+ return unittest.makeSuite(BDistMSITestCase)
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_bdist_wininst.py b/Lib/packaging/tests/test_command_bdist_wininst.py
new file mode 100644
index 0000000..09bdaad
--- /dev/null
+++ b/Lib/packaging/tests/test_command_bdist_wininst.py
@@ -0,0 +1,32 @@
+"""Tests for distutils.command.bdist_wininst."""
+
+from packaging.command.bdist_wininst import bdist_wininst
+from packaging.tests import unittest, support
+
+
+class BuildWinInstTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_get_exe_bytes(self):
+
+ # issue5731: command was broken on non-windows platforms
+ # this test makes sure it works now for every platform
+ # let's create a command
+ pkg_pth, dist = self.create_dist()
+ cmd = bdist_wininst(dist)
+ cmd.ensure_finalized()
+
+ # let's run the code that finds the right wininst*.exe file
+ # and make sure it finds it and returns its content
+ # no matter what platform we have
+ exe_file = cmd.get_exe_bytes()
+ self.assertGreater(len(exe_file), 10)
+
+
+def test_suite():
+ return unittest.makeSuite(BuildWinInstTestCase)
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_build.py b/Lib/packaging/tests/test_command_build.py
new file mode 100644
index 0000000..91fbe42
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build.py
@@ -0,0 +1,55 @@
+"""Tests for distutils.command.build."""
+import os
+import sys
+
+from packaging.command.build import build
+from sysconfig import get_platform
+from packaging.tests import unittest, support
+
+
+class BuildTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_finalize_options(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = build(dist)
+ cmd.finalize_options()
+
+ # if not specified, plat_name gets the current platform
+ self.assertEqual(cmd.plat_name, get_platform())
+
+ # build_purelib is build + lib
+ wanted = os.path.join(cmd.build_base, 'lib')
+ self.assertEqual(cmd.build_purelib, wanted)
+
+ # build_platlib is 'build/lib.platform-x.x[-pydebug]'
+ # examples:
+ # build/lib.macosx-10.3-i386-2.7
+ plat_spec = '.%s-%s' % (cmd.plat_name, sys.version[0:3])
+ if hasattr(sys, 'gettotalrefcount'):
+ self.assertTrue(cmd.build_platlib.endswith('-pydebug'))
+ plat_spec += '-pydebug'
+ wanted = os.path.join(cmd.build_base, 'lib' + plat_spec)
+ self.assertEqual(cmd.build_platlib, wanted)
+
+ # by default, build_lib = build_purelib
+ self.assertEqual(cmd.build_lib, cmd.build_purelib)
+
+ # build_temp is build/temp.<plat>
+ wanted = os.path.join(cmd.build_base, 'temp' + plat_spec)
+ self.assertEqual(cmd.build_temp, wanted)
+
+ # build_scripts is build/scripts-x.x
+ wanted = os.path.join(cmd.build_base, 'scripts-' + sys.version[0:3])
+ self.assertEqual(cmd.build_scripts, wanted)
+
+ # executable is os.path.normpath(sys.executable)
+ self.assertEqual(cmd.executable, os.path.normpath(sys.executable))
+
+
+def test_suite():
+ return unittest.makeSuite(BuildTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_build_clib.py b/Lib/packaging/tests/test_command_build_clib.py
new file mode 100644
index 0000000..a2a8583
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build_clib.py
@@ -0,0 +1,141 @@
+"""Tests for distutils.command.build_clib."""
+import os
+import sys
+
+from packaging.util import find_executable
+from packaging.command.build_clib import build_clib
+from packaging.errors import PackagingSetupError
+from packaging.tests import unittest, support
+
+
+class BuildCLibTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_check_library_dist(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = build_clib(dist)
+
+ # 'libraries' option must be a list
+ self.assertRaises(PackagingSetupError, cmd.check_library_list, 'foo')
+
+ # each element of 'libraries' must a 2-tuple
+ self.assertRaises(PackagingSetupError, cmd.check_library_list,
+ ['foo1', 'foo2'])
+
+ # first element of each tuple in 'libraries'
+ # must be a string (the library name)
+ self.assertRaises(PackagingSetupError, cmd.check_library_list,
+ [(1, 'foo1'), ('name', 'foo2')])
+
+ # library name may not contain directory separators
+ self.assertRaises(PackagingSetupError, cmd.check_library_list,
+ [('name', 'foo1'),
+ ('another/name', 'foo2')])
+
+ # second element of each tuple must be a dictionary (build info)
+ self.assertRaises(PackagingSetupError, cmd.check_library_list,
+ [('name', {}),
+ ('another', 'foo2')])
+
+ # those work
+ libs = [('name', {}), ('name', {'ok': 'good'})]
+ cmd.check_library_list(libs)
+
+ def test_get_source_files(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = build_clib(dist)
+
+ # "in 'libraries' option 'sources' must be present and must be
+ # a list of source filenames
+ cmd.libraries = [('name', {})]
+ self.assertRaises(PackagingSetupError, cmd.get_source_files)
+
+ cmd.libraries = [('name', {'sources': 1})]
+ self.assertRaises(PackagingSetupError, cmd.get_source_files)
+
+ cmd.libraries = [('name', {'sources': ['a', 'b']})]
+ self.assertEqual(cmd.get_source_files(), ['a', 'b'])
+
+ cmd.libraries = [('name', {'sources': ('a', 'b')})]
+ self.assertEqual(cmd.get_source_files(), ['a', 'b'])
+
+ cmd.libraries = [('name', {'sources': ('a', 'b')}),
+ ('name2', {'sources': ['c', 'd']})]
+ self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd'])
+
+ def test_build_libraries(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = build_clib(dist)
+
+ class FakeCompiler:
+ def compile(*args, **kw):
+ pass
+ create_static_lib = compile
+
+ cmd.compiler = FakeCompiler()
+
+ # build_libraries is also doing a bit of type checking
+ lib = [('name', {'sources': 'notvalid'})]
+ self.assertRaises(PackagingSetupError, cmd.build_libraries, lib)
+
+ lib = [('name', {'sources': []})]
+ cmd.build_libraries(lib)
+
+ lib = [('name', {'sources': ()})]
+ cmd.build_libraries(lib)
+
+ def test_finalize_options(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = build_clib(dist)
+
+ cmd.include_dirs = 'one-dir'
+ cmd.finalize_options()
+ self.assertEqual(cmd.include_dirs, ['one-dir'])
+
+ cmd.include_dirs = None
+ cmd.finalize_options()
+ self.assertEqual(cmd.include_dirs, [])
+
+ cmd.distribution.libraries = 'WONTWORK'
+ self.assertRaises(PackagingSetupError, cmd.finalize_options)
+
+ @unittest.skipIf(sys.platform == 'win32', 'disabled on win32')
+ def test_run(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = build_clib(dist)
+
+ foo_c = os.path.join(pkg_dir, 'foo.c')
+ self.write_file(foo_c, 'int main(void) { return 1;}\n')
+ cmd.libraries = [('foo', {'sources': [foo_c]})]
+
+ build_temp = os.path.join(pkg_dir, 'build')
+ os.mkdir(build_temp)
+ cmd.build_temp = build_temp
+ cmd.build_clib = build_temp
+
+ # before we run the command, we want to make sure
+ # all commands are present on the system
+ # by creating a compiler and checking its executables
+ from packaging.compiler import new_compiler, customize_compiler
+
+ compiler = new_compiler()
+ customize_compiler(compiler)
+ for ccmd in compiler.executables.values():
+ if ccmd is None:
+ continue
+ if find_executable(ccmd[0]) is None:
+ raise unittest.SkipTest("can't test")
+
+ # this should work
+ cmd.run()
+
+ # let's check the result
+ self.assertIn('libfoo.a', os.listdir(build_temp))
+
+
+def test_suite():
+ return unittest.makeSuite(BuildCLibTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_build_ext.py b/Lib/packaging/tests/test_command_build_ext.py
new file mode 100644
index 0000000..2d79842
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build_ext.py
@@ -0,0 +1,353 @@
+import os
+import sys
+import site
+import shutil
+import sysconfig
+from io import StringIO
+from packaging.dist import Distribution
+from packaging.errors import UnknownFileError, CompileError
+from packaging.command.build_ext import build_ext
+from packaging.compiler.extension import Extension
+
+from packaging.tests import support, unittest, verbose, unload
+
+# http://bugs.python.org/issue4373
+# Don't load the xx module more than once.
+ALREADY_TESTED = False
+
+
+def _get_source_filename():
+ srcdir = sysconfig.get_config_var('srcdir')
+ return os.path.join(srcdir, 'Modules', 'xxmodule.c')
+
+
+class BuildExtTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+ def setUp(self):
+ # Create a simple test environment
+ # Note that we're making changes to sys.path
+ super(BuildExtTestCase, self).setUp()
+ self.tmp_dir = self.mkdtemp()
+ self.sys_path = sys.path, sys.path[:]
+ sys.path.append(self.tmp_dir)
+ shutil.copy(_get_source_filename(), self.tmp_dir)
+ self.old_user_base = site.USER_BASE
+ site.USER_BASE = self.mkdtemp()
+ build_ext.USER_BASE = site.USER_BASE
+
+ def test_build_ext(self):
+ global ALREADY_TESTED
+ xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
+ xx_ext = Extension('xx', [xx_c])
+ dist = Distribution({'name': 'xx', 'ext_modules': [xx_ext]})
+ dist.package_dir = self.tmp_dir
+ cmd = build_ext(dist)
+ if os.name == "nt":
+ # On Windows, we must build a debug version iff running
+ # a debug build of Python
+ cmd.debug = sys.executable.endswith("_d.exe")
+ cmd.build_lib = self.tmp_dir
+ cmd.build_temp = self.tmp_dir
+
+ old_stdout = sys.stdout
+ if not verbose:
+ # silence compiler output
+ sys.stdout = StringIO()
+ try:
+ cmd.ensure_finalized()
+ cmd.run()
+ finally:
+ sys.stdout = old_stdout
+
+ if ALREADY_TESTED:
+ return
+ else:
+ ALREADY_TESTED = True
+
+ import xx
+
+ for attr in ('error', 'foo', 'new', 'roj'):
+ self.assertTrue(hasattr(xx, attr))
+
+ self.assertEqual(xx.foo(2, 5), 7)
+ self.assertEqual(xx.foo(13, 15), 28)
+ self.assertEqual(xx.new().demo(), None)
+ doc = 'This is a template module just for instruction.'
+ self.assertEqual(xx.__doc__, doc)
+ self.assertTrue(isinstance(xx.Null(), xx.Null))
+ self.assertTrue(isinstance(xx.Str(), xx.Str))
+
+ def tearDown(self):
+ # Get everything back to normal
+ unload('xx')
+ sys.path = self.sys_path[0]
+ sys.path[:] = self.sys_path[1]
+ if sys.version > "2.6":
+ site.USER_BASE = self.old_user_base
+ build_ext.USER_BASE = self.old_user_base
+
+ super(BuildExtTestCase, self).tearDown()
+
+ def test_solaris_enable_shared(self):
+ dist = Distribution({'name': 'xx'})
+ cmd = build_ext(dist)
+ old = sys.platform
+
+ sys.platform = 'sunos' # fooling finalize_options
+ from sysconfig import _CONFIG_VARS
+
+ old_var = _CONFIG_VARS.get('Py_ENABLE_SHARED')
+ _CONFIG_VARS['Py_ENABLE_SHARED'] = 1
+ try:
+ cmd.ensure_finalized()
+ finally:
+ sys.platform = old
+ if old_var is None:
+ del _CONFIG_VARS['Py_ENABLE_SHARED']
+ else:
+ _CONFIG_VARS['Py_ENABLE_SHARED'] = old_var
+
+ # make sure we get some library dirs under solaris
+ self.assertGreater(len(cmd.library_dirs), 0)
+
+ @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+ def test_user_site(self):
+ dist = Distribution({'name': 'xx'})
+ cmd = build_ext(dist)
+
+ # making sure the user option is there
+ options = [name for name, short, label in
+ cmd.user_options]
+ self.assertIn('user', options)
+
+ # setting a value
+ cmd.user = True
+
+ # setting user based lib and include
+ lib = os.path.join(site.USER_BASE, 'lib')
+ incl = os.path.join(site.USER_BASE, 'include')
+ os.mkdir(lib)
+ os.mkdir(incl)
+
+ # let's run finalize
+ cmd.ensure_finalized()
+
+ # see if include_dirs and library_dirs
+ # were set
+ self.assertIn(lib, cmd.library_dirs)
+ self.assertIn(lib, cmd.rpath)
+ self.assertIn(incl, cmd.include_dirs)
+
+ def test_optional_extension(self):
+
+ # this extension will fail, but let's ignore this failure
+ # with the optional argument.
+ modules = [Extension('foo', ['xxx'], optional=False)]
+ dist = Distribution({'name': 'xx', 'ext_modules': modules})
+ cmd = build_ext(dist)
+ cmd.ensure_finalized()
+ self.assertRaises((UnknownFileError, CompileError),
+ cmd.run) # should raise an error
+
+ modules = [Extension('foo', ['xxx'], optional=True)]
+ dist = Distribution({'name': 'xx', 'ext_modules': modules})
+ cmd = build_ext(dist)
+ cmd.ensure_finalized()
+ cmd.run() # should pass
+
+ def test_finalize_options(self):
+ # Make sure Python's include directories (for Python.h, pyconfig.h,
+ # etc.) are in the include search path.
+ modules = [Extension('foo', ['xxx'], optional=False)]
+ dist = Distribution({'name': 'xx', 'ext_modules': modules})
+ cmd = build_ext(dist)
+ cmd.finalize_options()
+
+ py_include = sysconfig.get_path('include')
+ self.assertIn(py_include, cmd.include_dirs)
+
+ plat_py_include = sysconfig.get_path('platinclude')
+ self.assertIn(plat_py_include, cmd.include_dirs)
+
+ # make sure cmd.libraries is turned into a list
+ # if it's a string
+ cmd = build_ext(dist)
+ cmd.libraries = 'my_lib'
+ cmd.finalize_options()
+ self.assertEqual(cmd.libraries, ['my_lib'])
+
+ # make sure cmd.library_dirs is turned into a list
+ # if it's a string
+ cmd = build_ext(dist)
+ cmd.library_dirs = 'my_lib_dir'
+ cmd.finalize_options()
+ self.assertIn('my_lib_dir', cmd.library_dirs)
+
+ # make sure rpath is turned into a list
+ # if it's a list of os.pathsep's paths
+ cmd = build_ext(dist)
+ cmd.rpath = os.pathsep.join(['one', 'two'])
+ cmd.finalize_options()
+ self.assertEqual(cmd.rpath, ['one', 'two'])
+
+ # XXX more tests to perform for win32
+
+ # make sure define is turned into 2-tuples
+ # strings if they are ','-separated strings
+ cmd = build_ext(dist)
+ cmd.define = 'one,two'
+ cmd.finalize_options()
+ self.assertEqual(cmd.define, [('one', '1'), ('two', '1')])
+
+ # make sure undef is turned into a list of
+ # strings if they are ','-separated strings
+ cmd = build_ext(dist)
+ cmd.undef = 'one,two'
+ cmd.finalize_options()
+ self.assertEqual(cmd.undef, ['one', 'two'])
+
+ # make sure swig_opts is turned into a list
+ cmd = build_ext(dist)
+ cmd.swig_opts = None
+ cmd.finalize_options()
+ self.assertEqual(cmd.swig_opts, [])
+
+ cmd = build_ext(dist)
+ cmd.swig_opts = '1 2'
+ cmd.finalize_options()
+ self.assertEqual(cmd.swig_opts, ['1', '2'])
+
+ def test_get_source_files(self):
+ modules = [Extension('foo', ['xxx'], optional=False)]
+ dist = Distribution({'name': 'xx', 'ext_modules': modules})
+ cmd = build_ext(dist)
+ cmd.ensure_finalized()
+ self.assertEqual(cmd.get_source_files(), ['xxx'])
+
+ def test_compiler_option(self):
+ # cmd.compiler is an option and
+ # should not be overriden by a compiler instance
+ # when the command is run
+ dist = Distribution()
+ cmd = build_ext(dist)
+ cmd.compiler = 'unix'
+ cmd.ensure_finalized()
+ cmd.run()
+ self.assertEqual(cmd.compiler, 'unix')
+
+ def test_get_outputs(self):
+ tmp_dir = self.mkdtemp()
+ c_file = os.path.join(tmp_dir, 'foo.c')
+ self.write_file(c_file, 'void initfoo(void) {};\n')
+ ext = Extension('foo', [c_file], optional=False)
+ dist = Distribution({'name': 'xx',
+ 'ext_modules': [ext]})
+ cmd = build_ext(dist)
+ cmd.ensure_finalized()
+ self.assertEqual(len(cmd.get_outputs()), 1)
+
+ if os.name == "nt":
+ cmd.debug = sys.executable.endswith("_d.exe")
+
+ cmd.build_lib = os.path.join(self.tmp_dir, 'build')
+ cmd.build_temp = os.path.join(self.tmp_dir, 'tempt')
+
+ # issue #5977 : distutils build_ext.get_outputs
+ # returns wrong result with --inplace
+ other_tmp_dir = os.path.realpath(self.mkdtemp())
+ old_wd = os.getcwd()
+ os.chdir(other_tmp_dir)
+ try:
+ cmd.inplace = True
+ cmd.run()
+ so_file = cmd.get_outputs()[0]
+ finally:
+ os.chdir(old_wd)
+ self.assertTrue(os.path.exists(so_file))
+ so_ext = sysconfig.get_config_var('SO')
+ self.assertTrue(so_file.endswith(so_ext))
+ so_dir = os.path.dirname(so_file)
+ self.assertEqual(so_dir, other_tmp_dir)
+
+ cmd.inplace = False
+ cmd.run()
+ so_file = cmd.get_outputs()[0]
+ self.assertTrue(os.path.exists(so_file))
+ self.assertTrue(so_file.endswith(so_ext))
+ so_dir = os.path.dirname(so_file)
+ self.assertEqual(so_dir, cmd.build_lib)
+
+ # inplace = False, cmd.package = 'bar'
+ build_py = cmd.get_finalized_command('build_py')
+ build_py.package_dir = 'bar'
+ path = cmd.get_ext_fullpath('foo')
+ # checking that the last directory is the build_dir
+ path = os.path.split(path)[0]
+ self.assertEqual(path, cmd.build_lib)
+
+ # inplace = True, cmd.package = 'bar'
+ cmd.inplace = True
+ other_tmp_dir = os.path.realpath(self.mkdtemp())
+ old_wd = os.getcwd()
+ os.chdir(other_tmp_dir)
+ try:
+ path = cmd.get_ext_fullpath('foo')
+ finally:
+ os.chdir(old_wd)
+ # checking that the last directory is bar
+ path = os.path.split(path)[0]
+ lastdir = os.path.split(path)[-1]
+ self.assertEqual(lastdir, 'bar')
+
+ def test_ext_fullpath(self):
+ ext = sysconfig.get_config_vars()['SO']
+ # building lxml.etree inplace
+ #etree_c = os.path.join(self.tmp_dir, 'lxml.etree.c')
+ #etree_ext = Extension('lxml.etree', [etree_c])
+ #dist = Distribution({'name': 'lxml', 'ext_modules': [etree_ext]})
+ dist = Distribution()
+ cmd = build_ext(dist)
+ cmd.inplace = True
+ cmd.distribution.package_dir = 'src'
+ cmd.distribution.packages = ['lxml', 'lxml.html']
+ curdir = os.getcwd()
+ wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
+ path = cmd.get_ext_fullpath('lxml.etree')
+ self.assertEqual(wanted, path)
+
+ # building lxml.etree not inplace
+ cmd.inplace = False
+ cmd.build_lib = os.path.join(curdir, 'tmpdir')
+ wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext)
+ path = cmd.get_ext_fullpath('lxml.etree')
+ self.assertEqual(wanted, path)
+
+ # building twisted.runner.portmap not inplace
+ build_py = cmd.get_finalized_command('build_py')
+ build_py.package_dir = None
+ cmd.distribution.packages = ['twisted', 'twisted.runner.portmap']
+ path = cmd.get_ext_fullpath('twisted.runner.portmap')
+ wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner',
+ 'portmap' + ext)
+ self.assertEqual(wanted, path)
+
+ # building twisted.runner.portmap inplace
+ cmd.inplace = True
+ path = cmd.get_ext_fullpath('twisted.runner.portmap')
+ wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
+ self.assertEqual(wanted, path)
+
+
+def test_suite():
+ src = _get_source_filename()
+ if not os.path.exists(src):
+ if verbose:
+ print ('test_build_ext: Cannot find source code (test'
+ ' must run in python build dir)')
+ return unittest.TestSuite()
+ else:
+ return unittest.makeSuite(BuildExtTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_build_py.py b/Lib/packaging/tests/test_command_build_py.py
new file mode 100644
index 0000000..9b40e6d
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build_py.py
@@ -0,0 +1,124 @@
+"""Tests for distutils.command.build_py."""
+
+import os
+import sys
+
+from packaging.command.build_py import build_py
+from packaging.dist import Distribution
+from packaging.errors import PackagingFileError
+
+from packaging.tests import unittest, support
+
+
+class BuildPyTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_package_data(self):
+ sources = self.mkdtemp()
+ pkg_dir = os.path.join(sources, 'pkg')
+ os.mkdir(pkg_dir)
+ f = open(os.path.join(pkg_dir, "__init__.py"), "w")
+ try:
+ f.write("# Pretend this is a package.")
+ finally:
+ f.close()
+ f = open(os.path.join(pkg_dir, "README.txt"), "w")
+ try:
+ f.write("Info about this package")
+ finally:
+ f.close()
+
+ destination = self.mkdtemp()
+
+ dist = Distribution({"packages": ["pkg"],
+ "package_dir": sources})
+ # script_name need not exist, it just need to be initialized
+
+ dist.script_name = os.path.join(sources, "setup.py")
+ dist.command_obj["build"] = support.DummyCommand(
+ force=False,
+ build_lib=destination,
+ use_2to3_fixers=None,
+ convert_2to3_doctests=None,
+ use_2to3=False)
+ dist.packages = ["pkg"]
+ dist.package_data = {"pkg": ["README.txt"]}
+ dist.package_dir = sources
+
+ cmd = build_py(dist)
+ cmd.compile = True
+ cmd.ensure_finalized()
+ self.assertEqual(cmd.package_data, dist.package_data)
+
+ cmd.run()
+
+ # This makes sure the list of outputs includes byte-compiled
+ # files for Python modules but not for package data files
+ # (there shouldn't *be* byte-code files for those!).
+ #
+ self.assertEqual(len(cmd.get_outputs()), 3)
+ pkgdest = os.path.join(destination, "pkg")
+ files = os.listdir(pkgdest)
+ self.assertIn("__init__.py", files)
+ self.assertIn("__init__.pyc", files)
+ self.assertIn("README.txt", files)
+
+ def test_empty_package_dir(self):
+ # See SF 1668596/1720897.
+ cwd = os.getcwd()
+
+ # create the distribution files.
+ sources = self.mkdtemp()
+ pkg = os.path.join(sources, 'pkg')
+ os.mkdir(pkg)
+ open(os.path.join(pkg, "__init__.py"), "w").close()
+ testdir = os.path.join(pkg, "doc")
+ os.mkdir(testdir)
+ open(os.path.join(testdir, "testfile"), "w").close()
+
+ os.chdir(sources)
+ old_stdout = sys.stdout
+ #sys.stdout = StringIO.StringIO()
+
+ try:
+ dist = Distribution({"packages": ["pkg"],
+ "package_dir": sources,
+ "package_data": {"pkg": ["doc/*"]}})
+ # script_name need not exist, it just need to be initialized
+ dist.script_name = os.path.join(sources, "setup.py")
+ dist.script_args = ["build"]
+ dist.parse_command_line()
+
+ try:
+ dist.run_commands()
+ except PackagingFileError as e:
+ self.fail("failed package_data test when package_dir is ''")
+ finally:
+ # Restore state.
+ os.chdir(cwd)
+ sys.stdout = old_stdout
+
+ @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'),
+ 'sys.dont_write_bytecode not supported')
+ def test_dont_write_bytecode(self):
+ # makes sure byte_compile is not used
+ pkg_dir, dist = self.create_dist()
+ cmd = build_py(dist)
+ cmd.compile = True
+ cmd.optimize = 1
+
+ old_dont_write_bytecode = sys.dont_write_bytecode
+ sys.dont_write_bytecode = True
+ try:
+ cmd.byte_compile([])
+ finally:
+ sys.dont_write_bytecode = old_dont_write_bytecode
+
+ self.assertIn('byte-compiling is disabled', self.get_logs()[0])
+
+def test_suite():
+ return unittest.makeSuite(BuildPyTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_build_scripts.py b/Lib/packaging/tests/test_command_build_scripts.py
new file mode 100644
index 0000000..60d8b68
--- /dev/null
+++ b/Lib/packaging/tests/test_command_build_scripts.py
@@ -0,0 +1,112 @@
+"""Tests for distutils.command.build_scripts."""
+
+import os
+import sys
+import sysconfig
+from packaging.dist import Distribution
+from packaging.command.build_scripts import build_scripts
+
+from packaging.tests import unittest, support
+
+
+class BuildScriptsTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_default_settings(self):
+ cmd = self.get_build_scripts_cmd("/foo/bar", [])
+ self.assertFalse(cmd.force)
+ self.assertIs(cmd.build_dir, None)
+
+ cmd.finalize_options()
+
+ self.assertTrue(cmd.force)
+ self.assertEqual(cmd.build_dir, "/foo/bar")
+
+ def test_build(self):
+ source = self.mkdtemp()
+ target = self.mkdtemp()
+ expected = self.write_sample_scripts(source)
+
+ cmd = self.get_build_scripts_cmd(target,
+ [os.path.join(source, fn)
+ for fn in expected])
+ cmd.finalize_options()
+ cmd.run()
+
+ built = os.listdir(target)
+ for name in expected:
+ self.assertIn(name, built)
+
+ def get_build_scripts_cmd(self, target, scripts):
+ dist = Distribution()
+ dist.scripts = scripts
+ dist.command_obj["build"] = support.DummyCommand(
+ build_scripts=target,
+ force=True,
+ executable=sys.executable,
+ use_2to3=False,
+ use_2to3_fixers=None,
+ convert_2to3_doctests=None
+ )
+ return build_scripts(dist)
+
+ def write_sample_scripts(self, dir):
+ expected = []
+ expected.append("script1.py")
+ self.write_script(dir, "script1.py",
+ ("#! /usr/bin/env python2.3\n"
+ "# bogus script w/ Python sh-bang\n"
+ "pass\n"))
+ expected.append("script2.py")
+ self.write_script(dir, "script2.py",
+ ("#!/usr/bin/python\n"
+ "# bogus script w/ Python sh-bang\n"
+ "pass\n"))
+ expected.append("shell.sh")
+ self.write_script(dir, "shell.sh",
+ ("#!/bin/sh\n"
+ "# bogus shell script w/ sh-bang\n"
+ "exit 0\n"))
+ return expected
+
+ def write_script(self, dir, name, text):
+ f = open(os.path.join(dir, name), "w")
+ try:
+ f.write(text)
+ finally:
+ f.close()
+
+ def test_version_int(self):
+ source = self.mkdtemp()
+ target = self.mkdtemp()
+ expected = self.write_sample_scripts(source)
+
+
+ cmd = self.get_build_scripts_cmd(target,
+ [os.path.join(source, fn)
+ for fn in expected])
+ cmd.finalize_options()
+
+ # http://bugs.python.org/issue4524
+ #
+ # On linux-g++-32 with command line `./configure --enable-ipv6
+ # --with-suffix=3`, python is compiled okay but the build scripts
+ # failed when writing the name of the executable
+ old = sysconfig.get_config_vars().get('VERSION')
+ sysconfig._CONFIG_VARS['VERSION'] = 4
+ try:
+ cmd.run()
+ finally:
+ if old is not None:
+ sysconfig._CONFIG_VARS['VERSION'] = old
+
+ built = os.listdir(target)
+ for name in expected:
+ self.assertIn(name, built)
+
+def test_suite():
+ return unittest.makeSuite(BuildScriptsTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_check.py b/Lib/packaging/tests/test_command_check.py
new file mode 100644
index 0000000..8b32673
--- /dev/null
+++ b/Lib/packaging/tests/test_command_check.py
@@ -0,0 +1,131 @@
+"""Tests for distutils.command.check."""
+
+import logging
+from packaging.command.check import check
+from packaging.metadata import _HAS_DOCUTILS
+from packaging.errors import PackagingSetupError, MetadataMissingError
+from packaging.tests import unittest, support
+
+
+class CheckTestCase(support.LoggingCatcher,
+ support.TempdirManager,
+ unittest.TestCase):
+
+ def _run(self, metadata=None, **options):
+ if metadata is None:
+ metadata = {'name': 'xxx', 'version': '1.2'}
+ pkg_info, dist = self.create_dist(**metadata)
+ cmd = check(dist)
+ cmd.initialize_options()
+ for name, value in options.items():
+ setattr(cmd, name, value)
+ cmd.ensure_finalized()
+ cmd.run()
+ return cmd
+
+ def test_check_metadata(self):
+ # let's run the command with no metadata at all
+ # by default, check is checking the metadata
+ # should have some warnings
+ cmd = self._run()
+ # trick: using assertNotEqual with an empty list will give us a more
+ # useful error message than assertGreater(.., 0) when the code change
+ # and the test fails
+ self.assertNotEqual([], self.get_logs(logging.WARNING))
+
+ # now let's add the required fields
+ # and run it again, to make sure we don't get
+ # any warning anymore
+ self.loghandler.flush()
+ metadata = {'home_page': 'xxx', 'author': 'xxx',
+ 'author_email': 'xxx',
+ 'name': 'xxx', 'version': '4.2',
+ }
+ cmd = self._run(metadata)
+ self.assertEqual([], self.get_logs(logging.WARNING))
+
+ # now with the strict mode, we should
+ # get an error if there are missing metadata
+ self.assertRaises(MetadataMissingError, self._run, {}, **{'strict': 1})
+ self.assertRaises(PackagingSetupError, self._run,
+ {'name': 'xxx', 'version': 'xxx'}, **{'strict': 1})
+
+ # and of course, no error when all metadata fields are present
+ self.loghandler.flush()
+ cmd = self._run(metadata, strict=True)
+ self.assertEqual([], self.get_logs(logging.WARNING))
+
+ def test_check_metadata_1_2(self):
+ # let's run the command with no metadata at all
+ # by default, check is checking the metadata
+ # should have some warnings
+ cmd = self._run()
+ self.assertNotEqual([], self.get_logs(logging.WARNING))
+
+ # now let's add the required fields and run it again, to make sure we
+ # don't get any warning anymore let's use requires_python as a marker
+ # to enforce Metadata-Version 1.2
+ metadata = {'home_page': 'xxx', 'author': 'xxx',
+ 'author_email': 'xxx',
+ 'name': 'xxx', 'version': '4.2',
+ 'requires_python': '2.4',
+ }
+ self.loghandler.flush()
+ cmd = self._run(metadata)
+ self.assertEqual([], self.get_logs(logging.WARNING))
+
+ # now with the strict mode, we should
+ # get an error if there are missing metadata
+ self.assertRaises(MetadataMissingError, self._run, {}, **{'strict': 1})
+ self.assertRaises(PackagingSetupError, self._run,
+ {'name': 'xxx', 'version': 'xxx'}, **{'strict': 1})
+
+ # complain about version format
+ metadata['version'] = 'xxx'
+ self.assertRaises(PackagingSetupError, self._run, metadata,
+ **{'strict': 1})
+
+ # now with correct version format again
+ metadata['version'] = '4.2'
+ self.loghandler.flush()
+ cmd = self._run(metadata, strict=True)
+ self.assertEqual([], self.get_logs(logging.WARNING))
+
+ @unittest.skipUnless(_HAS_DOCUTILS, "requires docutils")
+ def test_check_restructuredtext(self):
+ # let's see if it detects broken rest in long_description
+ broken_rest = 'title\n===\n\ntest'
+ pkg_info, dist = self.create_dist(description=broken_rest)
+ cmd = check(dist)
+ cmd.check_restructuredtext()
+ self.assertEqual(len(self.get_logs(logging.WARNING)), 1)
+
+ self.loghandler.flush()
+ pkg_info, dist = self.create_dist(description='title\n=====\n\ntest')
+ cmd = check(dist)
+ cmd.check_restructuredtext()
+ self.assertEqual([], self.get_logs(logging.WARNING))
+
+ def test_check_all(self):
+ self.assertRaises(PackagingSetupError, self._run,
+ {'name': 'xxx', 'version': 'xxx'}, **{'strict': 1,
+ 'all': 1})
+ self.assertRaises(MetadataMissingError, self._run,
+ {}, **{'strict': 1,
+ 'all': 1})
+
+ def test_check_hooks(self):
+ pkg_info, dist = self.create_dist()
+ dist.command_options['install_dist'] = {
+ 'pre_hook': ('file', {"a": 'some.nonextistant.hook.ghrrraarrhll'}),
+ }
+ cmd = check(dist)
+ cmd.check_hooks_resolvable()
+ self.assertEqual(len(self.get_logs(logging.WARNING)), 1)
+
+
+def test_suite():
+ return unittest.makeSuite(CheckTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_clean.py b/Lib/packaging/tests/test_command_clean.py
new file mode 100644
index 0000000..8d29e4d
--- /dev/null
+++ b/Lib/packaging/tests/test_command_clean.py
@@ -0,0 +1,48 @@
+"""Tests for distutils.command.clean."""
+import os
+
+from packaging.command.clean import clean
+from packaging.tests import unittest, support
+
+
+class cleanTestCase(support.TempdirManager, support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_simple_run(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = clean(dist)
+
+ # let's add some elements clean should remove
+ dirs = [(d, os.path.join(pkg_dir, d))
+ for d in ('build_temp', 'build_lib', 'bdist_base',
+ 'build_scripts', 'build_base')]
+
+ for name, path in dirs:
+ os.mkdir(path)
+ setattr(cmd, name, path)
+ if name == 'build_base':
+ continue
+ for f in ('one', 'two', 'three'):
+ self.write_file(os.path.join(path, f))
+
+ # let's run the command
+ cmd.all = True
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # make sure the files where removed
+ for name, path in dirs:
+ self.assertFalse(os.path.exists(path),
+ '%r was not removed' % path)
+
+ # let's run the command again (should spit warnings but succeed)
+ cmd.all = True
+ cmd.ensure_finalized()
+ cmd.run()
+
+
+def test_suite():
+ return unittest.makeSuite(cleanTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_cmd.py b/Lib/packaging/tests/test_command_cmd.py
new file mode 100644
index 0000000..8ac9dce
--- /dev/null
+++ b/Lib/packaging/tests/test_command_cmd.py
@@ -0,0 +1,101 @@
+"""Tests for distutils.cmd."""
+import os
+
+from packaging.command.cmd import Command
+from packaging.dist import Distribution
+from packaging.errors import PackagingOptionError
+from packaging.tests import support, unittest
+
+
+class MyCmd(Command):
+ def initialize_options(self):
+ pass
+
+
+class CommandTestCase(support.LoggingCatcher,
+ unittest.TestCase):
+
+ def setUp(self):
+ super(CommandTestCase, self).setUp()
+ dist = Distribution()
+ self.cmd = MyCmd(dist)
+
+ def test_make_file(self):
+ cmd = self.cmd
+
+ # making sure it raises when infiles is not a string or a list/tuple
+ self.assertRaises(TypeError, cmd.make_file,
+ infiles=1, outfile='', func='func', args=())
+
+ # making sure execute gets called properly
+ def _execute(func, args, exec_msg, level):
+ self.assertEqual(exec_msg, 'generating out from in')
+ cmd.force = True
+ cmd.execute = _execute
+ cmd.make_file(infiles='in', outfile='out', func='func', args=())
+
+ def test_dump_options(self):
+ cmd = self.cmd
+ cmd.option1 = 1
+ cmd.option2 = 1
+ cmd.user_options = [('option1', '', ''), ('option2', '', '')]
+ cmd.dump_options()
+
+ wanted = ["command options for 'MyCmd':", ' option1 = 1',
+ ' option2 = 1']
+ msgs = self.get_logs()
+ self.assertEqual(msgs, wanted)
+
+ def test_ensure_string(self):
+ cmd = self.cmd
+ cmd.option1 = 'ok'
+ cmd.ensure_string('option1')
+
+ cmd.option2 = None
+ cmd.ensure_string('option2', 'xxx')
+ self.assertTrue(hasattr(cmd, 'option2'))
+
+ cmd.option3 = 1
+ self.assertRaises(PackagingOptionError, cmd.ensure_string, 'option3')
+
+ def test_ensure_string_list(self):
+ cmd = self.cmd
+ cmd.option1 = 'ok,dok'
+ cmd.ensure_string_list('option1')
+ self.assertEqual(cmd.option1, ['ok', 'dok'])
+
+ cmd.yes_string_list = ['one', 'two', 'three']
+ cmd.yes_string_list2 = 'ok'
+ cmd.ensure_string_list('yes_string_list')
+ cmd.ensure_string_list('yes_string_list2')
+ self.assertEqual(cmd.yes_string_list, ['one', 'two', 'three'])
+ self.assertEqual(cmd.yes_string_list2, ['ok'])
+
+ cmd.not_string_list = ['one', 2, 'three']
+ cmd.not_string_list2 = object()
+ self.assertRaises(PackagingOptionError,
+ cmd.ensure_string_list, 'not_string_list')
+
+ self.assertRaises(PackagingOptionError,
+ cmd.ensure_string_list, 'not_string_list2')
+
+ def test_ensure_filename(self):
+ cmd = self.cmd
+ cmd.option1 = __file__
+ cmd.ensure_filename('option1')
+ cmd.option2 = 'xxx'
+ self.assertRaises(PackagingOptionError, cmd.ensure_filename, 'option2')
+
+ def test_ensure_dirname(self):
+ cmd = self.cmd
+ cmd.option1 = os.path.dirname(__file__) or os.curdir
+ cmd.ensure_dirname('option1')
+ cmd.option2 = 'xxx'
+ self.assertRaises(PackagingOptionError, cmd.ensure_dirname, 'option2')
+
+
+def test_suite():
+ return unittest.makeSuite(CommandTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_command_config.py b/Lib/packaging/tests/test_command_config.py
new file mode 100644
index 0000000..6d780c5
--- /dev/null
+++ b/Lib/packaging/tests/test_command_config.py
@@ -0,0 +1,76 @@
+"""Tests for distutils.command.config."""
+import os
+import sys
+import logging
+
+from packaging.command.config import dump_file, config
+from packaging.tests import unittest, support
+
+
+class ConfigTestCase(support.LoggingCatcher,
+ support.TempdirManager,
+ unittest.TestCase):
+
+ def test_dump_file(self):
+ this_file = __file__.rstrip('co')
+ with open(this_file) as f:
+ numlines = len(f.readlines())
+
+ dump_file(this_file, 'I am the header')
+
+ logs = []
+ for log in self.get_logs(logging.INFO):
+ logs.extend(line for line in log.split('\n'))
+ self.assertEqual(len(logs), numlines + 2)
+
+ @unittest.skipIf(sys.platform == 'win32', 'disabled on win32')
+ def test_search_cpp(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = config(dist)
+
+ # simple pattern searches
+ match = cmd.search_cpp(pattern='xxx', body='// xxx')
+ self.assertEqual(match, 0)
+
+ match = cmd.search_cpp(pattern='_configtest', body='// xxx')
+ self.assertEqual(match, 1)
+
+ def test_finalize_options(self):
+ # finalize_options does a bit of transformation
+ # on options
+ pkg_dir, dist = self.create_dist()
+ cmd = config(dist)
+ cmd.include_dirs = 'one%stwo' % os.pathsep
+ cmd.libraries = 'one'
+ cmd.library_dirs = 'three%sfour' % os.pathsep
+ cmd.ensure_finalized()
+
+ self.assertEqual(cmd.include_dirs, ['one', 'two'])
+ self.assertEqual(cmd.libraries, ['one'])
+ self.assertEqual(cmd.library_dirs, ['three', 'four'])
+
+ def test_clean(self):
+ # _clean removes files
+ tmp_dir = self.mkdtemp()
+ f1 = os.path.join(tmp_dir, 'one')
+ f2 = os.path.join(tmp_dir, 'two')
+
+ self.write_file(f1, 'xxx')
+ self.write_file(f2, 'xxx')
+
+ for f in (f1, f2):
+ self.assertTrue(os.path.exists(f))
+
+ pkg_dir, dist = self.create_dist()
+ cmd = config(dist)
+ cmd._clean(f1, f2)
+
+ for f in (f1, f2):
+ self.assertFalse(os.path.exists(f))
+
+
+def test_suite():
+ return unittest.makeSuite(ConfigTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_data.py b/Lib/packaging/tests/test_command_install_data.py
new file mode 100644
index 0000000..8b8bbac
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_data.py
@@ -0,0 +1,80 @@
+"""Tests for packaging.command.install_data."""
+import os
+import sysconfig
+from sysconfig import _get_default_scheme
+from packaging.tests import unittest, support
+from packaging.command.install_data import install_data
+
+
+class InstallDataTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_simple_run(self):
+ self.addCleanup(setattr, sysconfig, '_SCHEMES', sysconfig._SCHEMES)
+
+ pkg_dir, dist = self.create_dist()
+ cmd = install_data(dist)
+ cmd.install_dir = inst = os.path.join(pkg_dir, 'inst')
+
+ sysconfig._SCHEMES.set(_get_default_scheme(), 'inst',
+ os.path.join(pkg_dir, 'inst'))
+ sysconfig._SCHEMES.set(_get_default_scheme(), 'inst2',
+ os.path.join(pkg_dir, 'inst2'))
+
+ one = os.path.join(pkg_dir, 'one')
+ self.write_file(one, 'xxx')
+ inst2 = os.path.join(pkg_dir, 'inst2')
+ two = os.path.join(pkg_dir, 'two')
+ self.write_file(two, 'xxx')
+
+ cmd.data_files = {one: '{inst}/one', two: '{inst2}/two'}
+ self.assertCountEqual(cmd.get_inputs(), [one, two])
+
+ # let's run the command
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # let's check the result
+ self.assertEqual(len(cmd.get_outputs()), 2)
+ rtwo = os.path.split(two)[-1]
+ self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
+ rone = os.path.split(one)[-1]
+ self.assertTrue(os.path.exists(os.path.join(inst, rone)))
+ cmd.outfiles = []
+
+ # let's try with warn_dir one
+ cmd.warn_dir = True
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # let's check the result
+ self.assertEqual(len(cmd.get_outputs()), 2)
+ self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
+ self.assertTrue(os.path.exists(os.path.join(inst, rone)))
+ cmd.outfiles = []
+
+ # now using root and empty dir
+ cmd.root = os.path.join(pkg_dir, 'root')
+ three = os.path.join(cmd.install_dir, 'three')
+ self.write_file(three, 'xx')
+
+ sysconfig._SCHEMES.set(_get_default_scheme(), 'inst3',
+ cmd.install_dir)
+
+ cmd.data_files = {one: '{inst}/one', two: '{inst2}/two',
+ three: '{inst3}/three'}
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # let's check the result
+ self.assertEqual(len(cmd.get_outputs()), 3)
+ self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
+ self.assertTrue(os.path.exists(os.path.join(inst, rone)))
+
+
+def test_suite():
+ return unittest.makeSuite(InstallDataTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_dist.py b/Lib/packaging/tests/test_command_install_dist.py
new file mode 100644
index 0000000..a06d1f6
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_dist.py
@@ -0,0 +1,210 @@
+"""Tests for packaging.command.install."""
+
+import os
+import sys
+
+from sysconfig import (get_scheme_names, get_config_vars,
+ _SCHEMES, get_config_var, get_path)
+
+_CONFIG_VARS = get_config_vars()
+
+from packaging.tests import captured_stdout
+
+from packaging.command.install_dist import install_dist
+from packaging.command import install_dist as install_module
+from packaging.dist import Distribution
+from packaging.errors import PackagingOptionError
+
+from packaging.tests import unittest, support
+
+
+class InstallTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_home_installation_scheme(self):
+ # This ensure two things:
+ # - that --home generates the desired set of directory names
+ # - test --home is supported on all platforms
+ builddir = self.mkdtemp()
+ destination = os.path.join(builddir, "installation")
+
+ dist = Distribution({"name": "foopkg"})
+ # script_name need not exist, it just need to be initialized
+ dist.script_name = os.path.join(builddir, "setup.py")
+ dist.command_obj["build"] = support.DummyCommand(
+ build_base=builddir,
+ build_lib=os.path.join(builddir, "lib"),
+ )
+
+ old_posix_prefix = _SCHEMES.get('posix_prefix', 'platinclude')
+ old_posix_home = _SCHEMES.get('posix_home', 'platinclude')
+
+ new_path = '{platbase}/include/python{py_version_short}'
+ _SCHEMES.set('posix_prefix', 'platinclude', new_path)
+ _SCHEMES.set('posix_home', 'platinclude', '{platbase}/include/python')
+
+ try:
+ cmd = install_dist(dist)
+ cmd.home = destination
+ cmd.ensure_finalized()
+ finally:
+ _SCHEMES.set('posix_prefix', 'platinclude', old_posix_prefix)
+ _SCHEMES.set('posix_home', 'platinclude', old_posix_home)
+
+ self.assertEqual(cmd.install_base, destination)
+ self.assertEqual(cmd.install_platbase, destination)
+
+ def check_path(got, expected):
+ got = os.path.normpath(got)
+ expected = os.path.normpath(expected)
+ self.assertEqual(got, expected)
+
+ libdir = os.path.join(destination, "lib", "python")
+ check_path(cmd.install_lib, libdir)
+ check_path(cmd.install_platlib, libdir)
+ check_path(cmd.install_purelib, libdir)
+ check_path(cmd.install_headers,
+ os.path.join(destination, "include", "python", "foopkg"))
+ check_path(cmd.install_scripts, os.path.join(destination, "bin"))
+ check_path(cmd.install_data, destination)
+
+ @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+ def test_user_site(self):
+ # test install with --user
+ # preparing the environment for the test
+ self.old_user_base = get_config_var('userbase')
+ self.old_user_site = get_path('purelib', '%s_user' % os.name)
+ self.tmpdir = self.mkdtemp()
+ self.user_base = os.path.join(self.tmpdir, 'B')
+ self.user_site = os.path.join(self.tmpdir, 'S')
+ _CONFIG_VARS['userbase'] = self.user_base
+ scheme = '%s_user' % os.name
+ _SCHEMES.set(scheme, 'purelib', self.user_site)
+
+ def _expanduser(path):
+ if path[0] == '~':
+ path = os.path.normpath(self.tmpdir) + path[1:]
+ return path
+
+ self.old_expand = os.path.expanduser
+ os.path.expanduser = _expanduser
+
+ try:
+ # this is the actual test
+ self._test_user_site()
+ finally:
+ _CONFIG_VARS['userbase'] = self.old_user_base
+ _SCHEMES.set(scheme, 'purelib', self.old_user_site)
+ os.path.expanduser = self.old_expand
+
+ def _test_user_site(self):
+ schemes = get_scheme_names()
+ for key in ('nt_user', 'posix_user', 'os2_home'):
+ self.assertIn(key, schemes)
+
+ dist = Distribution({'name': 'xx'})
+ cmd = install_dist(dist)
+ # making sure the user option is there
+ options = [name for name, short, lable in
+ cmd.user_options]
+ self.assertIn('user', options)
+
+ # setting a value
+ cmd.user = True
+
+ # user base and site shouldn't be created yet
+ self.assertFalse(os.path.exists(self.user_base))
+ self.assertFalse(os.path.exists(self.user_site))
+
+ # let's run finalize
+ cmd.ensure_finalized()
+
+ # now they should
+ self.assertTrue(os.path.exists(self.user_base))
+ self.assertTrue(os.path.exists(self.user_site))
+
+ self.assertIn('userbase', cmd.config_vars)
+ self.assertIn('usersite', cmd.config_vars)
+
+ def test_handle_extra_path(self):
+ dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'})
+ cmd = install_dist(dist)
+
+ # two elements
+ cmd.handle_extra_path()
+ self.assertEqual(cmd.extra_path, ['path', 'dirs'])
+ self.assertEqual(cmd.extra_dirs, 'dirs')
+ self.assertEqual(cmd.path_file, 'path')
+
+ # one element
+ cmd.extra_path = ['path']
+ cmd.handle_extra_path()
+ self.assertEqual(cmd.extra_path, ['path'])
+ self.assertEqual(cmd.extra_dirs, 'path')
+ self.assertEqual(cmd.path_file, 'path')
+
+ # none
+ dist.extra_path = cmd.extra_path = None
+ cmd.handle_extra_path()
+ self.assertEqual(cmd.extra_path, None)
+ self.assertEqual(cmd.extra_dirs, '')
+ self.assertEqual(cmd.path_file, None)
+
+ # three elements (no way !)
+ cmd.extra_path = 'path,dirs,again'
+ self.assertRaises(PackagingOptionError, cmd.handle_extra_path)
+
+ def test_finalize_options(self):
+ dist = Distribution({'name': 'xx'})
+ cmd = install_dist(dist)
+
+ # must supply either prefix/exec-prefix/home or
+ # install-base/install-platbase -- not both
+ cmd.prefix = 'prefix'
+ cmd.install_base = 'base'
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+ # must supply either home or prefix/exec-prefix -- not both
+ cmd.install_base = None
+ cmd.home = 'home'
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+ if sys.version >= '2.6':
+ # can't combine user with with prefix/exec_prefix/home or
+ # install_(plat)base
+ cmd.prefix = None
+ cmd.user = 'user'
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+ def test_old_record(self):
+ # test pre-PEP 376 --record option (outside dist-info dir)
+ install_dir = self.mkdtemp()
+ pkgdir, dist = self.create_dist()
+
+ dist = Distribution()
+ cmd = install_dist(dist)
+ dist.command_obj['install_dist'] = cmd
+ cmd.root = install_dir
+ cmd.record = os.path.join(pkgdir, 'filelist')
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # let's check the record file was created with four
+ # lines, one for each .dist-info entry: METADATA,
+ # INSTALLER, REQUSTED, RECORD
+ f = open(cmd.record)
+ try:
+ self.assertEqual(len(f.readlines()), 4)
+ finally:
+ f.close()
+
+ # XXX test that fancy_getopt is okay with options named
+ # record and no-record but unrelated
+
+
+def test_suite():
+ return unittest.makeSuite(InstallTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_distinfo.py b/Lib/packaging/tests/test_command_install_distinfo.py
new file mode 100644
index 0000000..3d33691
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_distinfo.py
@@ -0,0 +1,192 @@
+"""Tests for ``packaging.command.install_distinfo``. """
+
+import os
+import csv
+import hashlib
+import sys
+
+from packaging.command.install_distinfo import install_distinfo
+from packaging.command.cmd import Command
+from packaging.metadata import Metadata
+from packaging.tests import unittest, support
+
+
+class DummyInstallCmd(Command):
+
+ def __init__(self, dist=None):
+ self.outputs = []
+ self.distribution = dist
+
+ def __getattr__(self, name):
+ return None
+
+ def ensure_finalized(self):
+ pass
+
+ def get_outputs(self):
+ return (self.outputs +
+ self.get_finalized_command('install_distinfo').get_outputs())
+
+
+class InstallDistinfoTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ checkLists = lambda self, x, y: self.assertListEqual(sorted(x), sorted(y))
+
+ def test_empty_install(self):
+ pkg_dir, dist = self.create_dist(name='foo',
+ version='1.0')
+ install_dir = self.mkdtemp()
+
+ install = DummyInstallCmd(dist)
+ dist.command_obj['install_dist'] = install
+
+ cmd = install_distinfo(dist)
+ dist.command_obj['install_distinfo'] = cmd
+
+ cmd.initialize_options()
+ cmd.distinfo_dir = install_dir
+ cmd.ensure_finalized()
+ cmd.run()
+
+ self.checkLists(os.listdir(install_dir), ['foo-1.0.dist-info'])
+
+ dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+ self.checkLists(os.listdir(dist_info),
+ ['METADATA', 'RECORD', 'REQUESTED', 'INSTALLER'])
+ with open(os.path.join(dist_info, 'INSTALLER')) as fp:
+ self.assertEqual(fp.read(), 'distutils')
+ with open(os.path.join(dist_info, 'REQUESTED')) as fp:
+ self.assertEqual(fp.read(), '')
+ meta_path = os.path.join(dist_info, 'METADATA')
+ self.assertTrue(Metadata(path=meta_path).check())
+
+ def test_installer(self):
+ pkg_dir, dist = self.create_dist(name='foo',
+ version='1.0')
+ install_dir = self.mkdtemp()
+
+ install = DummyInstallCmd(dist)
+ dist.command_obj['install_dist'] = install
+
+ cmd = install_distinfo(dist)
+ dist.command_obj['install_distinfo'] = cmd
+
+ cmd.initialize_options()
+ cmd.distinfo_dir = install_dir
+ cmd.installer = 'bacon-python'
+ cmd.ensure_finalized()
+ cmd.run()
+
+ dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+ with open(os.path.join(dist_info, 'INSTALLER')) as fp:
+ self.assertEqual(fp.read(), 'bacon-python')
+
+ def test_requested(self):
+ pkg_dir, dist = self.create_dist(name='foo',
+ version='1.0')
+ install_dir = self.mkdtemp()
+
+ install = DummyInstallCmd(dist)
+ dist.command_obj['install_dist'] = install
+
+ cmd = install_distinfo(dist)
+ dist.command_obj['install_distinfo'] = cmd
+
+ cmd.initialize_options()
+ cmd.distinfo_dir = install_dir
+ cmd.requested = False
+ cmd.ensure_finalized()
+ cmd.run()
+
+ dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+ self.checkLists(os.listdir(dist_info),
+ ['METADATA', 'RECORD', 'INSTALLER'])
+
+ def test_no_record(self):
+ pkg_dir, dist = self.create_dist(name='foo',
+ version='1.0')
+ install_dir = self.mkdtemp()
+
+ install = DummyInstallCmd(dist)
+ dist.command_obj['install_dist'] = install
+
+ cmd = install_distinfo(dist)
+ dist.command_obj['install_distinfo'] = cmd
+
+ cmd.initialize_options()
+ cmd.distinfo_dir = install_dir
+ cmd.no_record = True
+ cmd.ensure_finalized()
+ cmd.run()
+
+ dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+ self.checkLists(os.listdir(dist_info),
+ ['METADATA', 'REQUESTED', 'INSTALLER'])
+
+ def test_record(self):
+ pkg_dir, dist = self.create_dist(name='foo',
+ version='1.0')
+ install_dir = self.mkdtemp()
+
+ install = DummyInstallCmd(dist)
+ dist.command_obj['install_dist'] = install
+
+ fake_dists = os.path.join(os.path.dirname(__file__), 'fake_dists')
+ fake_dists = os.path.realpath(fake_dists)
+
+ # for testing, we simply add all files from _backport's fake_dists
+ dirs = []
+ for dir in os.listdir(fake_dists):
+ full_path = os.path.join(fake_dists, dir)
+ if (not dir.endswith('.egg') or dir.endswith('.egg-info') or
+ dir.endswith('.dist-info')) and os.path.isdir(full_path):
+ dirs.append(full_path)
+
+ for dir in dirs:
+ for path, subdirs, files in os.walk(dir):
+ install.outputs += [os.path.join(path, f) for f in files]
+ install.outputs += [os.path.join('path', f + 'c')
+ for f in files if f.endswith('.py')]
+
+ cmd = install_distinfo(dist)
+ dist.command_obj['install_distinfo'] = cmd
+
+ cmd.initialize_options()
+ cmd.distinfo_dir = install_dir
+ cmd.ensure_finalized()
+ cmd.run()
+
+ dist_info = os.path.join(install_dir, 'foo-1.0.dist-info')
+
+ expected = []
+ for f in install.get_outputs():
+ if (f.endswith('.pyc') or f == os.path.join(
+ install_dir, 'foo-1.0.dist-info', 'RECORD')):
+ expected.append([f, '', ''])
+ else:
+ size = os.path.getsize(f)
+ md5 = hashlib.md5()
+ with open(f) as fp:
+ md5.update(fp.read().encode())
+ hash = md5.hexdigest()
+ expected.append([f, hash, str(size)])
+
+ parsed = []
+ with open(os.path.join(dist_info, 'RECORD'), 'r') as f:
+ reader = csv.reader(f, delimiter=',',
+ lineterminator=os.linesep,
+ quotechar='"')
+ parsed = list(reader)
+
+ self.maxDiff = None
+ self.checkLists(parsed, expected)
+
+
+def test_suite():
+ return unittest.makeSuite(InstallDistinfoTestCase)
+
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_headers.py b/Lib/packaging/tests/test_command_install_headers.py
new file mode 100644
index 0000000..f2906a7
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_headers.py
@@ -0,0 +1,38 @@
+"""Tests for packaging.command.install_headers."""
+import os
+
+from packaging.command.install_headers import install_headers
+from packaging.tests import unittest, support
+
+
+class InstallHeadersTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_simple_run(self):
+ # we have two headers
+ header_list = self.mkdtemp()
+ header1 = os.path.join(header_list, 'header1')
+ header2 = os.path.join(header_list, 'header2')
+ self.write_file(header1)
+ self.write_file(header2)
+ headers = [header1, header2]
+
+ pkg_dir, dist = self.create_dist(headers=headers)
+ cmd = install_headers(dist)
+ self.assertEqual(cmd.get_inputs(), headers)
+
+ # let's run the command
+ cmd.install_dir = os.path.join(pkg_dir, 'inst')
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # let's check the results
+ self.assertEqual(len(cmd.get_outputs()), 2)
+
+
+def test_suite():
+ return unittest.makeSuite(InstallHeadersTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_lib.py b/Lib/packaging/tests/test_command_install_lib.py
new file mode 100644
index 0000000..99d47dd
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_lib.py
@@ -0,0 +1,111 @@
+"""Tests for packaging.command.install_data."""
+import sys
+import os
+
+from packaging.tests import unittest, support
+from packaging.command.install_lib import install_lib
+from packaging.compiler.extension import Extension
+from packaging.errors import PackagingOptionError
+
+try:
+ no_bytecode = sys.dont_write_bytecode
+ bytecode_support = True
+except AttributeError:
+ no_bytecode = False
+ bytecode_support = False
+
+
+class InstallLibTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ support.EnvironRestorer,
+ unittest.TestCase):
+
+ restore_environ = ['PYTHONPATH']
+
+ def test_finalize_options(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = install_lib(dist)
+
+ cmd.finalize_options()
+ self.assertTrue(cmd.compile)
+ self.assertEqual(cmd.optimize, 0)
+
+ # optimize must be 0, 1, or 2
+ cmd.optimize = 'foo'
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+ cmd.optimize = '4'
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+ cmd.optimize = '2'
+ cmd.finalize_options()
+ self.assertEqual(cmd.optimize, 2)
+
+ @unittest.skipIf(no_bytecode, 'byte-compile not supported')
+ def test_byte_compile(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = install_lib(dist)
+ cmd.compile = True
+ cmd.optimize = 1
+
+ f = os.path.join(pkg_dir, 'foo.py')
+ self.write_file(f, '# python file')
+ cmd.byte_compile([f])
+ self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyc')))
+ self.assertTrue(os.path.exists(os.path.join(pkg_dir, 'foo.pyo')))
+
+ def test_get_outputs(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = install_lib(dist)
+
+ # setting up a dist environment
+ cmd.compile = True
+ cmd.optimize = 1
+ cmd.install_dir = pkg_dir
+ f = os.path.join(pkg_dir, '__init__.py')
+ self.write_file(f, '# python package')
+ cmd.distribution.ext_modules = [Extension('foo', ['xxx'])]
+ cmd.distribution.packages = [pkg_dir]
+ cmd.distribution.script_name = 'setup.py'
+
+ # get_output should return 4 elements
+ self.assertEqual(len(cmd.get_outputs()), 4)
+
+ def test_get_inputs(self):
+ pkg_dir, dist = self.create_dist()
+ cmd = install_lib(dist)
+
+ # setting up a dist environment
+ cmd.compile = True
+ cmd.optimize = 1
+ cmd.install_dir = pkg_dir
+ f = os.path.join(pkg_dir, '__init__.py')
+ self.write_file(f, '# python package')
+ cmd.distribution.ext_modules = [Extension('foo', ['xxx'])]
+ cmd.distribution.packages = [pkg_dir]
+ cmd.distribution.script_name = 'setup.py'
+
+ # get_input should return 2 elements
+ self.assertEqual(len(cmd.get_inputs()), 2)
+
+ @unittest.skipUnless(bytecode_support,
+ 'sys.dont_write_bytecode not supported')
+ def test_dont_write_bytecode(self):
+ # makes sure byte_compile is not used
+ pkg_dir, dist = self.create_dist()
+ cmd = install_lib(dist)
+ cmd.compile = True
+ cmd.optimize = 1
+
+ self.addCleanup(setattr, sys, 'dont_write_bytecode',
+ sys.dont_write_bytecode)
+ sys.dont_write_bytecode = True
+ cmd.byte_compile([])
+
+ self.assertIn('byte-compiling is disabled', self.get_logs()[0])
+
+
+def test_suite():
+ return unittest.makeSuite(InstallLibTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_install_scripts.py b/Lib/packaging/tests/test_command_install_scripts.py
new file mode 100644
index 0000000..08c7338
--- /dev/null
+++ b/Lib/packaging/tests/test_command_install_scripts.py
@@ -0,0 +1,78 @@
+"""Tests for packaging.command.install_scripts."""
+import os
+
+from packaging.tests import unittest, support
+from packaging.command.install_scripts import install_scripts
+from packaging.dist import Distribution
+
+
+class InstallScriptsTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_default_settings(self):
+ dist = Distribution()
+ dist.command_obj["build"] = support.DummyCommand(
+ build_scripts="/foo/bar")
+ dist.command_obj["install_dist"] = support.DummyCommand(
+ install_scripts="/splat/funk",
+ force=True,
+ skip_build=True,
+ )
+ cmd = install_scripts(dist)
+ self.assertFalse(cmd.force)
+ self.assertFalse(cmd.skip_build)
+ self.assertIs(cmd.build_dir, None)
+ self.assertIs(cmd.install_dir, None)
+
+ cmd.finalize_options()
+
+ self.assertTrue(cmd.force)
+ self.assertTrue(cmd.skip_build)
+ self.assertEqual(cmd.build_dir, "/foo/bar")
+ self.assertEqual(cmd.install_dir, "/splat/funk")
+
+ def test_installation(self):
+ source = self.mkdtemp()
+ expected = []
+
+ def write_script(name, text):
+ expected.append(name)
+ f = open(os.path.join(source, name), "w")
+ try:
+ f.write(text)
+ finally:
+ f.close()
+
+ write_script("script1.py", ("#! /usr/bin/env python2.3\n"
+ "# bogus script w/ Python sh-bang\n"
+ "pass\n"))
+ write_script("script2.py", ("#!/usr/bin/python\n"
+ "# bogus script w/ Python sh-bang\n"
+ "pass\n"))
+ write_script("shell.sh", ("#!/bin/sh\n"
+ "# bogus shell script w/ sh-bang\n"
+ "exit 0\n"))
+
+ target = self.mkdtemp()
+ dist = Distribution()
+ dist.command_obj["build"] = support.DummyCommand(build_scripts=source)
+ dist.command_obj["install_dist"] = support.DummyCommand(
+ install_scripts=target,
+ force=True,
+ skip_build=True,
+ )
+ cmd = install_scripts(dist)
+ cmd.finalize_options()
+ cmd.run()
+
+ installed = os.listdir(target)
+ for name in expected:
+ self.assertIn(name, installed)
+
+
+def test_suite():
+ return unittest.makeSuite(InstallScriptsTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_register.py b/Lib/packaging/tests/test_command_register.py
new file mode 100644
index 0000000..7aa487a
--- /dev/null
+++ b/Lib/packaging/tests/test_command_register.py
@@ -0,0 +1,259 @@
+"""Tests for packaging.command.register."""
+import os
+import getpass
+import urllib.request
+import urllib.error
+import urllib.parse
+
+try:
+ import docutils
+ DOCUTILS_SUPPORT = True
+except ImportError:
+ DOCUTILS_SUPPORT = False
+
+from packaging.tests import unittest, support
+from packaging.command import register as register_module
+from packaging.command.register import register
+from packaging.errors import PackagingSetupError
+
+
+PYPIRC_NOPASSWORD = """\
+[distutils]
+
+index-servers =
+ server1
+
+[server1]
+username:me
+"""
+
+WANTED_PYPIRC = """\
+[distutils]
+index-servers =
+ pypi
+
+[pypi]
+username:tarek
+password:password
+"""
+
+
+class Inputs:
+ """Fakes user inputs."""
+ def __init__(self, *answers):
+ self.answers = answers
+ self.index = 0
+
+ def __call__(self, prompt=''):
+ try:
+ return self.answers[self.index]
+ finally:
+ self.index += 1
+
+
+class FakeOpener:
+ """Fakes a PyPI server"""
+ def __init__(self):
+ self.reqs = []
+
+ def __call__(self, *args):
+ return self
+
+ def open(self, req):
+ self.reqs.append(req)
+ return self
+
+ def read(self):
+ return 'xxx'
+
+
+class RegisterTestCase(support.TempdirManager,
+ support.EnvironRestorer,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ restore_environ = ['HOME']
+
+ def setUp(self):
+ super(RegisterTestCase, self).setUp()
+ self.tmp_dir = self.mkdtemp()
+ self.rc = os.path.join(self.tmp_dir, '.pypirc')
+ os.environ['HOME'] = self.tmp_dir
+
+ # patching the password prompt
+ self._old_getpass = getpass.getpass
+
+ def _getpass(prompt):
+ return 'password'
+
+ getpass.getpass = _getpass
+ self.old_opener = urllib.request.build_opener
+ self.conn = urllib.request.build_opener = FakeOpener()
+
+ def tearDown(self):
+ getpass.getpass = self._old_getpass
+ urllib.request.build_opener = self.old_opener
+ if hasattr(register_module, 'input'):
+ del register_module.input
+ super(RegisterTestCase, self).tearDown()
+
+ def _get_cmd(self, metadata=None):
+ if metadata is None:
+ metadata = {'url': 'xxx', 'author': 'xxx',
+ 'author_email': 'xxx',
+ 'name': 'xxx', 'version': 'xxx'}
+ pkg_info, dist = self.create_dist(**metadata)
+ return register(dist)
+
+ def test_create_pypirc(self):
+ # this test makes sure a .pypirc file
+ # is created when requested.
+
+ # let's create a register instance
+ cmd = self._get_cmd()
+
+ # we shouldn't have a .pypirc file yet
+ self.assertFalse(os.path.exists(self.rc))
+
+ # patching input and getpass.getpass
+ # so register gets happy
+ # Here's what we are faking :
+ # use your existing login (choice 1.)
+ # Username : 'tarek'
+ # Password : 'password'
+ # Save your login (y/N)? : 'y'
+ inputs = Inputs('1', 'tarek', 'y')
+ register_module.input = inputs
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # we should have a brand new .pypirc file
+ self.assertTrue(os.path.exists(self.rc))
+
+ # with the content similar to WANTED_PYPIRC
+ with open(self.rc) as fp:
+ content = fp.read()
+ self.assertEqual(content, WANTED_PYPIRC)
+
+ # now let's make sure the .pypirc file generated
+ # really works : we shouldn't be asked anything
+ # if we run the command again
+ def _no_way(prompt=''):
+ raise AssertionError(prompt)
+
+ register_module.input = _no_way
+ cmd.show_response = True
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # let's see what the server received : we should
+ # have 2 similar requests
+ self.assertEqual(len(self.conn.reqs), 2)
+ req1 = dict(self.conn.reqs[0].headers)
+ req2 = dict(self.conn.reqs[1].headers)
+ self.assertEqual(req2['Content-length'], req1['Content-length'])
+ self.assertIn('xxx', self.conn.reqs[1].data)
+
+ def test_password_not_in_file(self):
+
+ self.write_file(self.rc, PYPIRC_NOPASSWORD)
+ cmd = self._get_cmd()
+ cmd.finalize_options()
+ cmd._set_config()
+ cmd.send_metadata()
+
+ # dist.password should be set
+ # therefore used afterwards by other commands
+ self.assertEqual(cmd.distribution.password, 'password')
+
+ def test_registration(self):
+ # this test runs choice 2
+ cmd = self._get_cmd()
+ inputs = Inputs('2', 'tarek', 'tarek@ziade.org')
+ register_module.input = inputs
+ # let's run the command
+ # FIXME does this send a real request? use a mock server
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # we should have send a request
+ self.assertEqual(len(self.conn.reqs), 1)
+ req = self.conn.reqs[0]
+ headers = dict(req.headers)
+ self.assertEqual(headers['Content-length'], '608')
+ self.assertIn('tarek', req.data)
+
+ def test_password_reset(self):
+ # this test runs choice 3
+ cmd = self._get_cmd()
+ inputs = Inputs('3', 'tarek@ziade.org')
+ register_module.input = inputs
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # we should have send a request
+ self.assertEqual(len(self.conn.reqs), 1)
+ req = self.conn.reqs[0]
+ headers = dict(req.headers)
+ self.assertEqual(headers['Content-length'], '290')
+ self.assertIn('tarek', req.data)
+
+ @unittest.skipUnless(DOCUTILS_SUPPORT, 'needs docutils')
+ def test_strict(self):
+ # testing the script option
+ # when on, the register command stops if
+ # the metadata is incomplete or if
+ # long_description is not reSt compliant
+
+ # empty metadata
+ cmd = self._get_cmd({'name': 'xxx', 'version': 'xxx'})
+ cmd.ensure_finalized()
+ cmd.strict = True
+ inputs = Inputs('1', 'tarek', 'y')
+ register_module.input = inputs
+ self.assertRaises(PackagingSetupError, cmd.run)
+
+ # metadata is OK but long_description is broken
+ metadata = {'home_page': 'xxx', 'author': 'xxx',
+ 'author_email': 'éxéxé',
+ 'name': 'xxx', 'version': 'xxx',
+ 'description': 'title\n==\n\ntext'}
+
+ cmd = self._get_cmd(metadata)
+ cmd.ensure_finalized()
+ cmd.strict = True
+
+ self.assertRaises(PackagingSetupError, cmd.run)
+
+ # now something that works
+ metadata['description'] = 'title\n=====\n\ntext'
+ cmd = self._get_cmd(metadata)
+ cmd.ensure_finalized()
+ cmd.strict = True
+ inputs = Inputs('1', 'tarek', 'y')
+ register_module.input = inputs
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # strict is not by default
+ cmd = self._get_cmd()
+ cmd.ensure_finalized()
+ inputs = Inputs('1', 'tarek', 'y')
+ register_module.input = inputs
+ cmd.ensure_finalized()
+ cmd.run()
+
+ def test_register_pep345(self):
+ cmd = self._get_cmd({})
+ cmd.ensure_finalized()
+ cmd.distribution.metadata['Requires-Dist'] = ['lxml']
+ data = cmd.build_post_data('submit')
+ self.assertEqual(data['metadata_version'], '1.2')
+ self.assertEqual(data['requires_dist'], ['lxml'])
+
+
+def test_suite():
+ return unittest.makeSuite(RegisterTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_sdist.py b/Lib/packaging/tests/test_command_sdist.py
new file mode 100644
index 0000000..956e258
--- /dev/null
+++ b/Lib/packaging/tests/test_command_sdist.py
@@ -0,0 +1,407 @@
+"""Tests for packaging.command.sdist."""
+import os
+import zipfile
+import tarfile
+import logging
+
+# zlib is not used here, but if it's not available
+# the tests that use zipfile may fail
+try:
+ import zlib
+except ImportError:
+ zlib = None
+
+try:
+ import grp
+ import pwd
+ UID_GID_SUPPORT = True
+except ImportError:
+ UID_GID_SUPPORT = False
+
+from os.path import join
+from packaging.tests import captured_stdout
+from packaging.command.sdist import sdist
+from packaging.command.sdist import show_formats
+from packaging.dist import Distribution
+from packaging.tests import unittest
+from packaging.errors import PackagingOptionError
+from packaging.util import find_executable
+from packaging.tests import support
+from shutil import get_archive_formats
+
+SETUP_PY = """
+from packaging.core import setup
+import somecode
+
+setup(name='fake')
+"""
+
+MANIFEST = """\
+# file GENERATED by packaging, do NOT edit
+README
+inroot.txt
+data%(sep)sdata.dt
+scripts%(sep)sscript.py
+some%(sep)sfile.txt
+some%(sep)sother_file.txt
+somecode%(sep)s__init__.py
+somecode%(sep)sdoc.dat
+somecode%(sep)sdoc.txt
+"""
+
+
+def builder(dist, filelist):
+ filelist.append('bah')
+
+
+class SDistTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ support.EnvironRestorer,
+ unittest.TestCase):
+
+ restore_environ = ['HOME']
+
+ def setUp(self):
+ # PyPIRCCommandTestCase creates a temp dir already
+ # and put it in self.tmp_dir
+ super(SDistTestCase, self).setUp()
+ self.tmp_dir = self.mkdtemp()
+ os.environ['HOME'] = self.tmp_dir
+ # setting up an environment
+ self.old_path = os.getcwd()
+ os.mkdir(join(self.tmp_dir, 'somecode'))
+ os.mkdir(join(self.tmp_dir, 'dist'))
+ # a package, and a README
+ self.write_file((self.tmp_dir, 'README'), 'xxx')
+ self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#')
+ self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY)
+ os.chdir(self.tmp_dir)
+
+ def tearDown(self):
+ # back to normal
+ os.chdir(self.old_path)
+ super(SDistTestCase, self).tearDown()
+
+ def get_cmd(self, metadata=None):
+ """Returns a cmd"""
+ if metadata is None:
+ metadata = {'name': 'fake', 'version': '1.0',
+ 'url': 'xxx', 'author': 'xxx',
+ 'author_email': 'xxx'}
+ dist = Distribution(metadata)
+ dist.script_name = 'setup.py'
+ dist.packages = ['somecode']
+ dist.include_package_data = True
+ cmd = sdist(dist)
+ cmd.dist_dir = 'dist'
+ return dist, cmd
+
+ @unittest.skipUnless(zlib, "requires zlib")
+ def test_prune_file_list(self):
+ # this test creates a package with some vcs dirs in it
+ # and launch sdist to make sure they get pruned
+ # on all systems
+
+ # creating VCS directories with some files in them
+ os.mkdir(join(self.tmp_dir, 'somecode', '.svn'))
+
+ self.write_file((self.tmp_dir, 'somecode', '.svn', 'ok.py'), 'xxx')
+
+ os.mkdir(join(self.tmp_dir, 'somecode', '.hg'))
+ self.write_file((self.tmp_dir, 'somecode', '.hg',
+ 'ok'), 'xxx')
+
+ os.mkdir(join(self.tmp_dir, 'somecode', '.git'))
+ self.write_file((self.tmp_dir, 'somecode', '.git',
+ 'ok'), 'xxx')
+
+ # now building a sdist
+ dist, cmd = self.get_cmd()
+
+ # zip is available universally
+ # (tar might not be installed under win32)
+ cmd.formats = ['zip']
+
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # now let's check what we have
+ dist_folder = join(self.tmp_dir, 'dist')
+ files = os.listdir(dist_folder)
+ self.assertEqual(files, ['fake-1.0.zip'])
+
+ with zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) as zip_file:
+ content = zip_file.namelist()
+
+ # making sure everything has been pruned correctly
+ self.assertEqual(len(content), 3)
+
+ @unittest.skipUnless(zlib, "requires zlib")
+ @unittest.skipIf(find_executable('tar') is None or
+ find_executable('gzip') is None,
+ 'requires tar and gzip programs')
+ def test_make_distribution(self):
+ # building a sdist
+ dist, cmd = self.get_cmd()
+
+ # creating a gztar then a tar
+ cmd.formats = ['gztar', 'tar']
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # making sure we have two files
+ dist_folder = join(self.tmp_dir, 'dist')
+ result = sorted(os.listdir(dist_folder))
+ self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
+
+ os.remove(join(dist_folder, 'fake-1.0.tar'))
+ os.remove(join(dist_folder, 'fake-1.0.tar.gz'))
+
+ # now trying a tar then a gztar
+ cmd.formats = ['tar', 'gztar']
+
+ cmd.ensure_finalized()
+ cmd.run()
+
+ result = sorted(os.listdir(dist_folder))
+ self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
+
+ @unittest.skipUnless(zlib, "requires zlib")
+ def test_add_defaults(self):
+
+ # http://bugs.python.org/issue2279
+
+ # add_default should also include
+ # data_files and package_data
+ dist, cmd = self.get_cmd()
+
+ # filling data_files by pointing files
+ # in package_data
+ dist.package_data = {'': ['*.cfg', '*.dat'],
+ 'somecode': ['*.txt']}
+ self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
+ self.write_file((self.tmp_dir, 'somecode', 'doc.dat'), '#')
+
+ # adding some data in data_files
+ data_dir = join(self.tmp_dir, 'data')
+ os.mkdir(data_dir)
+ self.write_file((data_dir, 'data.dt'), '#')
+ some_dir = join(self.tmp_dir, 'some')
+ os.mkdir(some_dir)
+ self.write_file((self.tmp_dir, 'inroot.txt'), '#')
+ self.write_file((some_dir, 'file.txt'), '#')
+ self.write_file((some_dir, 'other_file.txt'), '#')
+
+ dist.data_files = {'data/data.dt': '{appdata}/data.dt',
+ 'inroot.txt': '{appdata}/inroot.txt',
+ 'some/file.txt': '{appdata}/file.txt',
+ 'some/other_file.txt': '{appdata}/other_file.txt'}
+
+ # adding a script
+ script_dir = join(self.tmp_dir, 'scripts')
+ os.mkdir(script_dir)
+ self.write_file((script_dir, 'script.py'), '#')
+ dist.scripts = [join('scripts', 'script.py')]
+
+ cmd.formats = ['zip']
+ cmd.use_defaults = True
+
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # now let's check what we have
+ dist_folder = join(self.tmp_dir, 'dist')
+ files = os.listdir(dist_folder)
+ self.assertEqual(files, ['fake-1.0.zip'])
+
+ with zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) as zip_file:
+ content = zip_file.namelist()
+
+ # Making sure everything was added. This includes 9 code and data
+ # files in addition to PKG-INFO.
+ self.assertEqual(len(content), 10)
+
+ # Checking the MANIFEST
+ with open(join(self.tmp_dir, 'MANIFEST')) as fp:
+ manifest = fp.read()
+ self.assertEqual(manifest, MANIFEST % {'sep': os.sep})
+
+ @unittest.skipUnless(zlib, "requires zlib")
+ def test_metadata_check_option(self):
+ # testing the `check-metadata` option
+ dist, cmd = self.get_cmd(metadata={'name': 'xxx', 'version': 'xxx'})
+
+ # this should raise some warnings
+ # with the check subcommand
+ cmd.ensure_finalized()
+ cmd.run()
+ warnings = self.get_logs(logging.WARN)
+ self.assertEqual(len(warnings), 3)
+
+ # trying with a complete set of metadata
+ self.loghandler.flush()
+ dist, cmd = self.get_cmd()
+ cmd.ensure_finalized()
+ cmd.metadata_check = False
+ cmd.run()
+ warnings = self.get_logs(logging.WARN)
+ # removing manifest generated warnings
+ warnings = [warn for warn in warnings if
+ not warn.endswith('-- skipping')]
+ # the remaining warning is about the use of the default file list
+ self.assertEqual(len(warnings), 1)
+
+ def test_show_formats(self):
+ __, stdout = captured_stdout(show_formats)
+
+ # the output should be a header line + one line per format
+ num_formats = len(get_archive_formats())
+ output = [line for line in stdout.split('\n')
+ if line.strip().startswith('--formats=')]
+ self.assertEqual(len(output), num_formats)
+
+ def test_finalize_options(self):
+
+ dist, cmd = self.get_cmd()
+ cmd.finalize_options()
+
+ # default options set by finalize
+ self.assertEqual(cmd.manifest, 'MANIFEST')
+ self.assertEqual(cmd.dist_dir, 'dist')
+
+ # formats has to be a string splitable on (' ', ',') or
+ # a stringlist
+ cmd.formats = 1
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+ cmd.formats = ['zip']
+ cmd.finalize_options()
+
+ # formats has to be known
+ cmd.formats = 'supazipa'
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+ @unittest.skipUnless(zlib, "requires zlib")
+ @unittest.skipUnless(UID_GID_SUPPORT, "requires grp and pwd support")
+ @unittest.skipIf(find_executable('tar') is None or
+ find_executable('gzip') is None,
+ 'requires tar and gzip programs')
+ def test_make_distribution_owner_group(self):
+ # building a sdist
+ dist, cmd = self.get_cmd()
+
+ # creating a gztar and specifying the owner+group
+ cmd.formats = ['gztar']
+ cmd.owner = pwd.getpwuid(0)[0]
+ cmd.group = grp.getgrgid(0)[0]
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # making sure we have the good rights
+ archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
+ with tarfile.open(archive_name) as archive:
+ for member in archive.getmembers():
+ self.assertEqual(member.uid, 0)
+ self.assertEqual(member.gid, 0)
+
+ # building a sdist again
+ dist, cmd = self.get_cmd()
+
+ # creating a gztar
+ cmd.formats = ['gztar']
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # making sure we have the good rights
+ archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
+ with tarfile.open(archive_name) as archive:
+
+ # note that we are not testing the group ownership here
+ # because, depending on the platforms and the container
+ # rights (see #7408)
+ for member in archive.getmembers():
+ self.assertEqual(member.uid, os.getuid())
+
+ def test_get_file_list(self):
+ # make sure MANIFEST is recalculated
+ dist, cmd = self.get_cmd()
+ # filling data_files by pointing files in package_data
+ dist.package_data = {'somecode': ['*.txt']}
+ self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#')
+ cmd.ensure_finalized()
+ cmd.run()
+
+ # Should produce four lines. Those lines are one comment, one default
+ # (README) and two package files.
+ with open(cmd.manifest) as f:
+ manifest = [line.strip() for line in f.read().split('\n')
+ if line.strip() != '']
+ self.assertEqual(len(manifest), 4)
+
+ # Adding a file
+ self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#')
+
+ # make sure build_py is reinitialized, like a fresh run
+ build_py = dist.get_command_obj('build_py')
+ build_py.finalized = False
+ build_py.ensure_finalized()
+
+ cmd.run()
+
+ with open(cmd.manifest) as f:
+ manifest2 = [line.strip() for line in f.read().split('\n')
+ if line.strip() != '']
+
+ # Do we have the new file in MANIFEST?
+ self.assertEqual(len(manifest2), 5)
+ self.assertIn('doc2.txt', manifest2[-1])
+
+ def test_manifest_marker(self):
+ # check that autogenerated MANIFESTs have a marker
+ dist, cmd = self.get_cmd()
+ cmd.ensure_finalized()
+ cmd.run()
+
+ with open(cmd.manifest) as f:
+ manifest = [line.strip() for line in f.read().split('\n')
+ if line.strip() != '']
+
+ self.assertEqual(manifest[0],
+ '# file GENERATED by packaging, do NOT edit')
+
+ def test_manual_manifest(self):
+ # check that a MANIFEST without a marker is left alone
+ dist, cmd = self.get_cmd()
+ cmd.ensure_finalized()
+ self.write_file((self.tmp_dir, cmd.manifest), 'README.manual')
+ cmd.run()
+
+ with open(cmd.manifest) as f:
+ manifest = [line.strip() for line in f.read().split('\n')
+ if line.strip() != '']
+
+ self.assertEqual(manifest, ['README.manual'])
+
+ def test_template(self):
+ dist, cmd = self.get_cmd()
+ dist.extra_files = ['include yeah']
+ cmd.ensure_finalized()
+ self.write_file((self.tmp_dir, 'yeah'), 'xxx')
+ cmd.run()
+ with open(cmd.manifest) as f:
+ content = f.read()
+
+ self.assertIn('yeah', content)
+
+ def test_manifest_builder(self):
+ dist, cmd = self.get_cmd()
+ cmd.manifest_builders = 'packaging.tests.test_command_sdist.builder'
+ cmd.ensure_finalized()
+ cmd.run()
+ self.assertIn('bah', cmd.filelist.files)
+
+
+def test_suite():
+ return unittest.makeSuite(SDistTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_test.py b/Lib/packaging/tests/test_command_test.py
new file mode 100644
index 0000000..4fd8452
--- /dev/null
+++ b/Lib/packaging/tests/test_command_test.py
@@ -0,0 +1,225 @@
+import os
+import re
+import sys
+import shutil
+import logging
+import unittest as ut1
+import packaging.database
+
+from os.path import join
+from operator import getitem, setitem, delitem
+from packaging.command.build import build
+from packaging.tests import unittest
+from packaging.tests.support import (TempdirManager, EnvironRestorer,
+ LoggingCatcher)
+from packaging.command.test import test
+from packaging.command import set_command
+from packaging.dist import Distribution
+
+
+EXPECTED_OUTPUT_RE = r'''FAIL: test_blah \(myowntestmodule.SomeTest\)
+----------------------------------------------------------------------
+Traceback \(most recent call last\):
+ File ".+/myowntestmodule.py", line \d+, in test_blah
+ self.fail\("horribly"\)
+AssertionError: horribly
+'''
+
+here = os.path.dirname(os.path.abspath(__file__))
+
+
+class MockBuildCmd(build):
+ build_lib = "mock build lib"
+ command_name = 'build'
+ plat_name = 'whatever'
+
+ def initialize_options(self):
+ pass
+
+ def finalize_options(self):
+ pass
+
+ def run(self):
+ self._record.append("build has run")
+
+
+class TestTest(TempdirManager,
+ EnvironRestorer,
+ LoggingCatcher,
+ unittest.TestCase):
+
+ restore_environ = ['PYTHONPATH']
+
+ def setUp(self):
+ super(TestTest, self).setUp()
+ self.addCleanup(packaging.database.clear_cache)
+ new_pythonpath = os.path.dirname(os.path.dirname(here))
+ pythonpath = os.environ.get('PYTHONPATH')
+ if pythonpath is not None:
+ new_pythonpath = os.pathsep.join((new_pythonpath, pythonpath))
+ os.environ['PYTHONPATH'] = new_pythonpath
+
+ def assert_re_match(self, pattern, string):
+ def quote(s):
+ lines = ['## ' + line for line in s.split('\n')]
+ sep = ["#" * 60]
+ return [''] + sep + lines + sep
+ msg = quote(pattern) + ["didn't match"] + quote(string)
+ msg = "\n".join(msg)
+ if not re.search(pattern, string):
+ self.fail(msg)
+
+ def prepare_dist(self, dist_name):
+ pkg_dir = join(os.path.dirname(__file__), "dists", dist_name)
+ temp_pkg_dir = join(self.mkdtemp(), dist_name)
+ shutil.copytree(pkg_dir, temp_pkg_dir)
+ return temp_pkg_dir
+
+ def safely_replace(self, obj, attr,
+ new_val=None, delete=False, dictionary=False):
+ """Replace a object's attribute returning to its original state at the
+ end of the test run. Creates the attribute if not present before
+ (deleting afterwards). When delete=True, makes sure the value is del'd
+ for the test run. If dictionary is set to True, operates of its items
+ rather than attributes."""
+ if dictionary:
+ _setattr, _getattr, _delattr = setitem, getitem, delitem
+
+ def _hasattr(_dict, value):
+ return value in _dict
+ else:
+ _setattr, _getattr, _delattr, _hasattr = (setattr, getattr,
+ delattr, hasattr)
+
+ orig_has_attr = _hasattr(obj, attr)
+ if orig_has_attr:
+ orig_val = _getattr(obj, attr)
+
+ if delete is False:
+ _setattr(obj, attr, new_val)
+ elif orig_has_attr:
+ _delattr(obj, attr)
+
+ def do_cleanup():
+ if orig_has_attr:
+ _setattr(obj, attr, orig_val)
+ elif _hasattr(obj, attr):
+ _delattr(obj, attr)
+
+ self.addCleanup(do_cleanup)
+
+ def test_runs_unittest(self):
+ module_name, a_module = self.prepare_a_module()
+ record = []
+ a_module.recorder = lambda *args: record.append("suite")
+
+ class MockTextTestRunner:
+ def __init__(*_, **__):
+ pass
+
+ def run(_self, suite):
+ record.append("run")
+
+ self.safely_replace(ut1, "TextTestRunner", MockTextTestRunner)
+
+ dist = Distribution()
+ cmd = test(dist)
+ cmd.suite = "%s.recorder" % module_name
+ cmd.run()
+ self.assertEqual(record, ["suite", "run"])
+
+ def test_builds_before_running_tests(self):
+ self.addCleanup(set_command, 'packaging.command.build.build')
+ set_command('packaging.tests.test_command_test.MockBuildCmd')
+
+ dist = Distribution()
+ dist.get_command_obj('build')._record = record = []
+ cmd = test(dist)
+ cmd.runner = self.prepare_named_function(lambda: None)
+ cmd.ensure_finalized()
+ cmd.run()
+ self.assertEqual(['build has run'], record)
+
+ def _test_works_with_2to3(self):
+ pass
+
+ def test_checks_requires(self):
+ dist = Distribution()
+ cmd = test(dist)
+ phony_project = 'ohno_ohno-impossible_1234-name_stop-that!'
+ cmd.tests_require = [phony_project]
+ cmd.ensure_finalized()
+ logs = self.get_logs(logging.WARNING)
+ self.assertEqual(1, len(logs))
+ self.assertIn(phony_project, logs[0])
+
+ def prepare_a_module(self):
+ tmp_dir = self.mkdtemp()
+ sys.path.append(tmp_dir)
+ self.addCleanup(sys.path.remove, tmp_dir)
+
+ self.write_file((tmp_dir, 'packaging_tests_a.py'), '')
+ import packaging_tests_a as a_module
+ return "packaging_tests_a", a_module
+
+ def prepare_named_function(self, func):
+ module_name, a_module = self.prepare_a_module()
+ a_module.recorder = func
+ return "%s.recorder" % module_name
+
+ def test_custom_runner(self):
+ dist = Distribution()
+ cmd = test(dist)
+ record = []
+ cmd.runner = self.prepare_named_function(
+ lambda: record.append("runner called"))
+ cmd.ensure_finalized()
+ cmd.run()
+ self.assertEqual(["runner called"], record)
+
+ def prepare_mock_ut2(self):
+ class MockUTClass:
+ def __init__(*_, **__):
+ pass
+
+ def discover(self):
+ pass
+
+ def run(self, _):
+ pass
+
+ class MockUTModule:
+ TestLoader = MockUTClass
+ TextTestRunner = MockUTClass
+
+ mock_ut2 = MockUTModule()
+ self.safely_replace(sys.modules, "unittest2",
+ mock_ut2, dictionary=True)
+ return mock_ut2
+
+ def test_gets_unittest_discovery(self):
+ mock_ut2 = self.prepare_mock_ut2()
+ dist = Distribution()
+ cmd = test(dist)
+ self.safely_replace(ut1.TestLoader, "discover", lambda: None)
+ self.assertEqual(cmd.get_ut_with_discovery(), ut1)
+
+ del ut1.TestLoader.discover
+ self.assertEqual(cmd.get_ut_with_discovery(), mock_ut2)
+
+ def test_calls_discover(self):
+ self.safely_replace(ut1.TestLoader, "discover", delete=True)
+ mock_ut2 = self.prepare_mock_ut2()
+ record = []
+ mock_ut2.TestLoader.discover = lambda self, path: record.append(path)
+ dist = Distribution()
+ cmd = test(dist)
+ cmd.run()
+ self.assertEqual([os.curdir], record)
+
+
+def test_suite():
+ return unittest.makeSuite(TestTest)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_upload.py b/Lib/packaging/tests/test_command_upload.py
new file mode 100644
index 0000000..f2e338b
--- /dev/null
+++ b/Lib/packaging/tests/test_command_upload.py
@@ -0,0 +1,157 @@
+"""Tests for packaging.command.upload."""
+import os
+import sys
+
+from packaging.command.upload import upload
+from packaging.dist import Distribution
+from packaging.errors import PackagingOptionError
+
+from packaging.tests import unittest, support
+from packaging.tests.pypi_server import PyPIServer, PyPIServerTestCase
+
+
+PYPIRC_NOPASSWORD = """\
+[distutils]
+
+index-servers =
+ server1
+
+[server1]
+username:me
+"""
+
+PYPIRC = """\
+[distutils]
+
+index-servers =
+ server1
+ server2
+
+[server1]
+username:me
+password:secret
+
+[server2]
+username:meagain
+password: secret
+realm:acme
+repository:http://another.pypi/
+"""
+
+
+class UploadTestCase(support.TempdirManager, support.EnvironRestorer,
+ support.LoggingCatcher, PyPIServerTestCase):
+
+ restore_environ = ['HOME']
+
+ def setUp(self):
+ super(UploadTestCase, self).setUp()
+ self.tmp_dir = self.mkdtemp()
+ self.rc = os.path.join(self.tmp_dir, '.pypirc')
+ os.environ['HOME'] = self.tmp_dir
+
+ def test_finalize_options(self):
+ # new format
+ self.write_file(self.rc, PYPIRC)
+ dist = Distribution()
+ cmd = upload(dist)
+ cmd.finalize_options()
+ for attr, expected in (('username', 'me'), ('password', 'secret'),
+ ('realm', 'pypi'),
+ ('repository', 'http://pypi.python.org/pypi')):
+ self.assertEqual(getattr(cmd, attr), expected)
+
+ def test_finalize_options_unsigned_identity_raises_exception(self):
+ self.write_file(self.rc, PYPIRC)
+ dist = Distribution()
+ cmd = upload(dist)
+ cmd.identity = True
+ cmd.sign = False
+ self.assertRaises(PackagingOptionError, cmd.finalize_options)
+
+ def test_saved_password(self):
+ # file with no password
+ self.write_file(self.rc, PYPIRC_NOPASSWORD)
+
+ # make sure it passes
+ dist = Distribution()
+ cmd = upload(dist)
+ cmd.ensure_finalized()
+ self.assertEqual(cmd.password, None)
+
+ # make sure we get it as well, if another command
+ # initialized it at the dist level
+ dist.password = 'xxx'
+ cmd = upload(dist)
+ cmd.finalize_options()
+ self.assertEqual(cmd.password, 'xxx')
+
+ def test_upload_without_files_raises_exception(self):
+ dist = Distribution()
+ cmd = upload(dist)
+ self.assertRaises(PackagingOptionError, cmd.run)
+
+ def test_upload(self):
+ path = os.path.join(self.tmp_dir, 'xxx')
+ self.write_file(path)
+ command, pyversion, filename = 'xxx', '3.3', path
+ dist_files = [(command, pyversion, filename)]
+
+ # lets run it
+ pkg_dir, dist = self.create_dist(dist_files=dist_files, author='dédé')
+ cmd = upload(dist)
+ cmd.ensure_finalized()
+ cmd.repository = self.pypi.full_address
+ cmd.run()
+
+ # what did we send ?
+ handler, request_data = self.pypi.requests[-1]
+ headers = handler.headers
+ #self.assertIn('dédé', str(request_data))
+ self.assertIn(b'xxx', request_data)
+
+ self.assertEqual(int(headers['content-length']), len(request_data))
+ self.assertLess(int(headers['content-length']), 2500)
+ self.assertTrue(headers['content-type'].startswith('multipart/form-data'))
+ self.assertEqual(handler.command, 'POST')
+ self.assertNotIn('\n', headers['authorization'])
+
+ def test_upload_docs(self):
+ path = os.path.join(self.tmp_dir, 'xxx')
+ self.write_file(path)
+ command, pyversion, filename = 'xxx', '3.3', path
+ dist_files = [(command, pyversion, filename)]
+ docs_path = os.path.join(self.tmp_dir, "build", "docs")
+ os.makedirs(docs_path)
+ self.write_file(os.path.join(docs_path, "index.html"), "yellow")
+ self.write_file(self.rc, PYPIRC)
+
+ # lets run it
+ pkg_dir, dist = self.create_dist(dist_files=dist_files, author='dédé')
+
+ cmd = upload(dist)
+ cmd.get_finalized_command("build").run()
+ cmd.upload_docs = True
+ cmd.ensure_finalized()
+ cmd.repository = self.pypi.full_address
+ try:
+ prev_dir = os.getcwd()
+ os.chdir(self.tmp_dir)
+ cmd.run()
+ finally:
+ os.chdir(prev_dir)
+
+ handler, request_data = self.pypi.requests[-1]
+ action, name, content = request_data.split(
+ "----------------GHSKFJDLGDS7543FJKLFHRE75642756743254"
+ .encode())[1:4]
+
+ self.assertIn(b'name=":action"', action)
+ self.assertIn(b'doc_upload', action)
+
+
+def test_suite():
+ return unittest.makeSuite(UploadTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_command_upload_docs.py b/Lib/packaging/tests/test_command_upload_docs.py
new file mode 100644
index 0000000..b103894
--- /dev/null
+++ b/Lib/packaging/tests/test_command_upload_docs.py
@@ -0,0 +1,205 @@
+"""Tests for packaging.command.upload_docs."""
+import os
+import sys
+import shutil
+import zipfile
+
+from packaging.command import upload_docs as upload_docs_mod
+from packaging.command.upload_docs import (upload_docs, zip_dir,
+ encode_multipart)
+from packaging.dist import Distribution
+from packaging.errors import PackagingFileError, PackagingOptionError
+
+from packaging.tests import unittest, support
+from packaging.tests.pypi_server import PyPIServerTestCase
+
+
+EXPECTED_MULTIPART_OUTPUT = [
+ b'---x',
+ b'Content-Disposition: form-data; name="username"',
+ b'',
+ b'wok',
+ b'---x',
+ b'Content-Disposition: form-data; name="password"',
+ b'',
+ b'secret',
+ b'---x',
+ b'Content-Disposition: form-data; name="picture"; filename="wok.png"',
+ b'',
+ b'PNG89',
+ b'---x--',
+ b'',
+]
+
+PYPIRC = """\
+[distutils]
+index-servers = server1
+
+[server1]
+repository = %s
+username = real_slim_shady
+password = long_island
+"""
+
+class UploadDocsTestCase(support.TempdirManager,
+ support.EnvironRestorer,
+ support.LoggingCatcher,
+ PyPIServerTestCase):
+
+ restore_environ = ['HOME']
+
+ def setUp(self):
+ super(UploadDocsTestCase, self).setUp()
+ self.tmp_dir = self.mkdtemp()
+ self.rc = os.path.join(self.tmp_dir, '.pypirc')
+ os.environ['HOME'] = self.tmp_dir
+ self.dist = Distribution()
+ self.dist.metadata['Name'] = "distr-name"
+ self.cmd = upload_docs(self.dist)
+
+ def test_default_uploaddir(self):
+ sandbox = self.mkdtemp()
+ previous = os.getcwd()
+ os.chdir(sandbox)
+ try:
+ os.mkdir("build")
+ self.prepare_sample_dir("build")
+ self.cmd.ensure_finalized()
+ self.assertEqual(self.cmd.upload_dir, os.path.join("build", "docs"))
+ finally:
+ os.chdir(previous)
+
+ def test_default_uploaddir_looks_for_doc_also(self):
+ sandbox = self.mkdtemp()
+ previous = os.getcwd()
+ os.chdir(sandbox)
+ try:
+ os.mkdir("build")
+ self.prepare_sample_dir("build")
+ os.rename(os.path.join("build", "docs"), os.path.join("build", "doc"))
+ self.cmd.ensure_finalized()
+ self.assertEqual(self.cmd.upload_dir, os.path.join("build", "doc"))
+ finally:
+ os.chdir(previous)
+
+ def prepare_sample_dir(self, sample_dir=None):
+ if sample_dir is None:
+ sample_dir = self.mkdtemp()
+ os.mkdir(os.path.join(sample_dir, "docs"))
+ self.write_file(os.path.join(sample_dir, "docs", "index.html"), "Ce mortel ennui")
+ self.write_file(os.path.join(sample_dir, "index.html"), "Oh la la")
+ return sample_dir
+
+ def test_zip_dir(self):
+ source_dir = self.prepare_sample_dir()
+ compressed = zip_dir(source_dir)
+
+ zip_f = zipfile.ZipFile(compressed)
+ self.assertEqual(zip_f.namelist(), ['index.html', 'docs/index.html'])
+
+ def test_encode_multipart(self):
+ fields = [('username', 'wok'), ('password', 'secret')]
+ files = [('picture', 'wok.png', b'PNG89')]
+ content_type, body = encode_multipart(fields, files, b'-x')
+ self.assertEqual(b'multipart/form-data; boundary=-x', content_type)
+ self.assertEqual(EXPECTED_MULTIPART_OUTPUT, body.split(b'\r\n'))
+
+ def prepare_command(self):
+ self.cmd.upload_dir = self.prepare_sample_dir()
+ self.cmd.ensure_finalized()
+ self.cmd.repository = self.pypi.full_address
+ self.cmd.username = "username"
+ self.cmd.password = "password"
+
+ def test_upload(self):
+ self.prepare_command()
+ self.cmd.run()
+
+ self.assertEqual(len(self.pypi.requests), 1)
+ handler, request_data = self.pypi.requests[-1]
+ self.assertIn(b"content", request_data)
+ self.assertIn("Basic", handler.headers['authorization'])
+ self.assertTrue(handler.headers['content-type']
+ .startswith('multipart/form-data;'))
+
+ action, name, version, content =\
+ request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254".encode())[1:5]
+
+
+ # check that we picked the right chunks
+ self.assertIn(b'name=":action"', action)
+ self.assertIn(b'name="name"', name)
+ self.assertIn(b'name="version"', version)
+ self.assertIn(b'name="content"', content)
+
+ # check their contents
+ self.assertIn(b'doc_upload', action)
+ self.assertIn(b'distr-name', name)
+ self.assertIn(b'docs/index.html', content)
+ self.assertIn(b'Ce mortel ennui', content)
+
+ def test_https_connection(self):
+ https_called = False
+
+ orig_https = upload_docs_mod.http.client.HTTPConnection
+
+ def https_conn_wrapper(*args):
+ nonlocal https_called
+ https_called = True
+ # the testing server is http
+ return upload_docs_mod.http.client.HTTPConnection(*args)
+
+ upload_docs_mod.http.client.HTTPSConnection = https_conn_wrapper
+ try:
+ self.prepare_command()
+ self.cmd.run()
+ self.assertFalse(https_called)
+
+ self.cmd.repository = self.cmd.repository.replace("http", "https")
+ self.cmd.run()
+ self.assertTrue(https_called)
+ finally:
+ upload_docs_mod.http.client.HTTPConnection = orig_https
+
+ def test_handling_response(self):
+ self.pypi.default_response_status = '403 Forbidden'
+ self.prepare_command()
+ self.cmd.run()
+ self.assertIn('Upload failed (403): Forbidden', self.get_logs()[-1])
+
+ self.pypi.default_response_status = '301 Moved Permanently'
+ self.pypi.default_response_headers.append(("Location", "brand_new_location"))
+ self.cmd.run()
+ self.assertIn('brand_new_location', self.get_logs()[-1])
+
+ def test_reads_pypirc_data(self):
+ self.write_file(self.rc, PYPIRC % self.pypi.full_address)
+ self.cmd.repository = self.pypi.full_address
+ self.cmd.upload_dir = self.prepare_sample_dir()
+ self.cmd.ensure_finalized()
+ self.assertEqual(self.cmd.username, "real_slim_shady")
+ self.assertEqual(self.cmd.password, "long_island")
+
+ def test_checks_index_html_presence(self):
+ self.cmd.upload_dir = self.prepare_sample_dir()
+ os.remove(os.path.join(self.cmd.upload_dir, "index.html"))
+ self.assertRaises(PackagingFileError, self.cmd.ensure_finalized)
+
+ def test_checks_upload_dir(self):
+ self.cmd.upload_dir = self.prepare_sample_dir()
+ shutil.rmtree(os.path.join(self.cmd.upload_dir))
+ self.assertRaises(PackagingOptionError, self.cmd.ensure_finalized)
+
+ def test_show_response(self):
+ self.prepare_command()
+ self.cmd.show_response = True
+ self.cmd.run()
+ record = self.get_logs()[-1]
+ self.assertTrue(record, "should report the response")
+ self.assertIn(self.pypi.default_response_data, record)
+
+def test_suite():
+ return unittest.makeSuite(UploadDocsTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_compiler.py b/Lib/packaging/tests/test_compiler.py
new file mode 100644
index 0000000..2c620cb
--- /dev/null
+++ b/Lib/packaging/tests/test_compiler.py
@@ -0,0 +1,66 @@
+"""Tests for distutils.compiler."""
+import os
+
+from packaging.compiler import (get_default_compiler, customize_compiler,
+ gen_lib_options)
+from packaging.tests import unittest, support
+
+
+class FakeCompiler:
+
+ name = 'fake'
+ description = 'Fake'
+
+ def library_dir_option(self, dir):
+ return "-L" + dir
+
+ def runtime_library_dir_option(self, dir):
+ return ["-cool", "-R" + dir]
+
+ def find_library_file(self, dirs, lib, debug=False):
+ return 'found'
+
+ def library_option(self, lib):
+ return "-l" + lib
+
+
+class CompilerTestCase(support.EnvironRestorer, unittest.TestCase):
+
+ restore_environ = ['AR', 'ARFLAGS']
+
+ @unittest.skipUnless(get_default_compiler() == 'unix',
+ 'irrelevant if default compiler is not unix')
+ def test_customize_compiler(self):
+
+ os.environ['AR'] = 'my_ar'
+ os.environ['ARFLAGS'] = '-arflags'
+
+ # make sure AR gets caught
+ class compiler:
+ name = 'unix'
+
+ def set_executables(self, **kw):
+ self.exes = kw
+
+ comp = compiler()
+ customize_compiler(comp)
+ self.assertEqual(comp.exes['archiver'], 'my_ar -arflags')
+
+ def test_gen_lib_options(self):
+ compiler = FakeCompiler()
+ libdirs = ['lib1', 'lib2']
+ runlibdirs = ['runlib1']
+ libs = [os.path.join('dir', 'name'), 'name2']
+
+ opts = gen_lib_options(compiler, libdirs, runlibdirs, libs)
+ wanted = ['-Llib1', '-Llib2', '-cool', '-Rrunlib1', 'found',
+ '-lname2']
+ self.assertEqual(opts, wanted)
+
+
+def test_suite():
+ return unittest.makeSuite(CompilerTestCase)
+
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_config.py b/Lib/packaging/tests/test_config.py
new file mode 100644
index 0000000..8908c4f
--- /dev/null
+++ b/Lib/packaging/tests/test_config.py
@@ -0,0 +1,424 @@
+"""Tests for packaging.config."""
+import os
+import sys
+import logging
+from io import StringIO
+
+from packaging import command
+from packaging.dist import Distribution
+from packaging.errors import PackagingFileError
+from packaging.compiler import new_compiler, _COMPILERS
+from packaging.command.sdist import sdist
+
+from packaging.tests import unittest, support
+
+
+SETUP_CFG = """
+[metadata]
+name = RestingParrot
+version = 0.6.4
+author = Carl Meyer
+author_email = carl@oddbird.net
+maintainer = Éric Araujo
+maintainer_email = merwok@netwok.org
+summary = A sample project demonstrating packaging
+description-file = %(description-file)s
+keywords = packaging, sample project
+
+classifier =
+ Development Status :: 4 - Beta
+ Environment :: Console (Text Based)
+ Environment :: X11 Applications :: GTK; python_version < '3'
+ License :: OSI Approved :: MIT License
+ Programming Language :: Python
+ Programming Language :: Python :: 2
+ Programming Language :: Python :: 3
+
+requires_python = >=2.4, <3.2
+
+requires_dist =
+ PetShoppe
+ MichaelPalin (> 1.1)
+ pywin32; sys.platform == 'win32'
+ pysqlite2; python_version < '2.5'
+ inotify (0.0.1); sys.platform == 'linux2'
+
+requires_external = libxml2
+
+provides_dist = packaging-sample-project (0.2)
+ unittest2-sample-project
+
+project_url =
+ Main repository, http://bitbucket.org/carljm/sample-distutils2-project
+ Fork in progress, http://bitbucket.org/Merwok/sample-distutils2-project
+
+[files]
+packages_root = src
+
+packages = one
+ two
+ three
+
+modules = haven
+
+scripts =
+ script1.py
+ scripts/find-coconuts
+ bin/taunt
+
+package_data =
+ cheese = data/templates/*
+
+extra_files = %(extra-files)s
+
+# Replaces MANIFEST.in
+sdist_extra =
+ include THANKS HACKING
+ recursive-include examples *.txt *.py
+ prune examples/sample?/build
+
+resources=
+ bm/ {b1,b2}.gif = {icon}
+ Cf*/ *.CFG = {config}/baBar/
+ init_script = {script}/JunGle/
+
+[global]
+commands =
+ packaging.tests.test_config.FooBarBazTest
+
+compilers =
+ packaging.tests.test_config.DCompiler
+
+setup_hook = %(setup-hook)s
+
+
+
+[install_dist]
+sub_commands = foo
+"""
+
+# Can not be merged with SETUP_CFG else install_dist
+# command will fail when trying to compile C sources
+EXT_SETUP_CFG = """
+[files]
+packages = one
+ two
+
+[extension=speed_coconuts]
+name = one.speed_coconuts
+sources = c_src/speed_coconuts.c
+extra_link_args = "`gcc -print-file-name=libgcc.a`" -shared
+define_macros = HAVE_CAIRO HAVE_GTK2
+libraries = gecodeint gecodekernel -- sys.platform != 'win32'
+ GecodeInt GecodeKernel -- sys.platform == 'win32'
+
+[extension=fast_taunt]
+name = three.fast_taunt
+sources = cxx_src/utils_taunt.cxx
+ cxx_src/python_module.cxx
+include_dirs = /usr/include/gecode
+ /usr/include/blitz
+extra_compile_args = -fPIC -O2
+ -DGECODE_VERSION=$(./gecode_version) -- sys.platform != 'win32'
+ /DGECODE_VERSION='win32' -- sys.platform == 'win32'
+language = cxx
+
+"""
+
+
+class DCompiler:
+ name = 'd'
+ description = 'D Compiler'
+
+ def __init__(self, *args):
+ pass
+
+
+def hook(content):
+ content['metadata']['version'] += '.dev1'
+
+
+class FooBarBazTest:
+
+ def __init__(self, dist):
+ self.distribution = dist
+
+ @classmethod
+ def get_command_name(cls):
+ return 'foo'
+
+ def run(self):
+ self.distribution.foo_was_here = True
+
+ def nothing(self):
+ pass
+
+ def get_source_files(self):
+ return []
+
+ ensure_finalized = finalize_options = initialize_options = nothing
+
+
+class ConfigTestCase(support.TempdirManager,
+ support.EnvironRestorer,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ restore_environ = ['PLAT']
+
+ def setUp(self):
+ super(ConfigTestCase, self).setUp()
+ self.addCleanup(setattr, sys, 'stdout', sys.stdout)
+ self.addCleanup(setattr, sys, 'stderr', sys.stderr)
+ sys.stdout = StringIO()
+ sys.stderr = StringIO()
+
+ self.addCleanup(os.chdir, os.getcwd())
+ tempdir = self.mkdtemp()
+ os.chdir(tempdir)
+ self.tempdir = tempdir
+
+ def write_setup(self, kwargs=None):
+ opts = {'description-file': 'README', 'extra-files': '',
+ 'setup-hook': 'packaging.tests.test_config.hook'}
+ if kwargs:
+ opts.update(kwargs)
+ self.write_file('setup.cfg', SETUP_CFG % opts)
+
+ def get_dist(self):
+ dist = Distribution()
+ dist.parse_config_files()
+ return dist
+
+ def test_config(self):
+ self.write_setup()
+ self.write_file('README', 'yeah')
+ os.mkdir('bm')
+ self.write_file(('bm', 'b1.gif'), '')
+ self.write_file(('bm', 'b2.gif'), '')
+ os.mkdir('Cfg')
+ self.write_file(('Cfg', 'data.CFG'), '')
+ self.write_file('init_script', '')
+
+ # try to load the metadata now
+ dist = self.get_dist()
+
+ # check what was done
+ self.assertEqual(dist.metadata['Author'], 'Carl Meyer')
+ self.assertEqual(dist.metadata['Author-Email'], 'carl@oddbird.net')
+
+ # the hook adds .dev1
+ self.assertEqual(dist.metadata['Version'], '0.6.4.dev1')
+
+ wanted = [
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Console (Text Based)',
+ "Environment :: X11 Applications :: GTK; python_version < '3'",
+ 'License :: OSI Approved :: MIT License',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 3']
+ self.assertEqual(dist.metadata['Classifier'], wanted)
+
+ wanted = ['packaging', 'sample project']
+ self.assertEqual(dist.metadata['Keywords'], wanted)
+
+ self.assertEqual(dist.metadata['Requires-Python'], '>=2.4, <3.2')
+
+ wanted = ['PetShoppe',
+ 'MichaelPalin (> 1.1)',
+ "pywin32; sys.platform == 'win32'",
+ "pysqlite2; python_version < '2.5'",
+ "inotify (0.0.1); sys.platform == 'linux2'"]
+
+ self.assertEqual(dist.metadata['Requires-Dist'], wanted)
+ urls = [('Main repository',
+ 'http://bitbucket.org/carljm/sample-distutils2-project'),
+ ('Fork in progress',
+ 'http://bitbucket.org/Merwok/sample-distutils2-project')]
+ self.assertEqual(dist.metadata['Project-Url'], urls)
+
+ self.assertEqual(dist.packages, ['one', 'two', 'three'])
+ self.assertEqual(dist.py_modules, ['haven'])
+ self.assertEqual(dist.package_data, {'cheese': 'data/templates/*'})
+ self.assertEqual(
+ {'bm/b1.gif': '{icon}/b1.gif',
+ 'bm/b2.gif': '{icon}/b2.gif',
+ 'Cfg/data.CFG': '{config}/baBar/data.CFG',
+ 'init_script': '{script}/JunGle/init_script'},
+ dist.data_files)
+
+ self.assertEqual(dist.package_dir, 'src')
+
+ # Make sure we get the foo command loaded. We use a string comparison
+ # instead of assertIsInstance because the class is not the same when
+ # this test is run directly: foo is packaging.tests.test_config.Foo
+ # because get_command_class uses the full name, but a bare "Foo" in
+ # this file would be __main__.Foo when run as "python test_config.py".
+ # The name FooBarBazTest should be unique enough to prevent
+ # collisions.
+ self.assertEqual('FooBarBazTest',
+ dist.get_command_obj('foo').__class__.__name__)
+
+ # did the README got loaded ?
+ self.assertEqual(dist.metadata['description'], 'yeah')
+
+ # do we have the D Compiler enabled ?
+ self.assertIn('d', _COMPILERS)
+ d = new_compiler(compiler='d')
+ self.assertEqual(d.description, 'D Compiler')
+
+ def test_multiple_description_file(self):
+ self.write_setup({'description-file': 'README CHANGES'})
+ self.write_file('README', 'yeah')
+ self.write_file('CHANGES', 'changelog2')
+ dist = self.get_dist()
+ self.assertEqual(dist.metadata.requires_files, ['README', 'CHANGES'])
+
+ def test_multiline_description_file(self):
+ self.write_setup({'description-file': 'README\n CHANGES'})
+ self.write_file('README', 'yeah')
+ self.write_file('CHANGES', 'changelog')
+ dist = self.get_dist()
+ self.assertEqual(dist.metadata['description'], 'yeah\nchangelog')
+ self.assertEqual(dist.metadata.requires_files, ['README', 'CHANGES'])
+
+ def test_parse_extensions_in_config(self):
+ self.write_file('setup.cfg', EXT_SETUP_CFG)
+ dist = self.get_dist()
+
+ ext_modules = dict((mod.name, mod) for mod in dist.ext_modules)
+ self.assertEqual(len(ext_modules), 2)
+ ext = ext_modules.get('one.speed_coconuts')
+ self.assertEqual(ext.sources, ['c_src/speed_coconuts.c'])
+ self.assertEqual(ext.define_macros, ['HAVE_CAIRO', 'HAVE_GTK2'])
+ libs = ['gecodeint', 'gecodekernel']
+ if sys.platform == 'win32':
+ libs = ['GecodeInt', 'GecodeKernel']
+ self.assertEqual(ext.libraries, libs)
+ self.assertEqual(ext.extra_link_args,
+ ['`gcc -print-file-name=libgcc.a`', '-shared'])
+
+ ext = ext_modules.get('three.fast_taunt')
+ self.assertEqual(ext.sources,
+ ['cxx_src/utils_taunt.cxx', 'cxx_src/python_module.cxx'])
+ self.assertEqual(ext.include_dirs,
+ ['/usr/include/gecode', '/usr/include/blitz'])
+ cargs = ['-fPIC', '-O2']
+ if sys.platform == 'win32':
+ cargs.append("/DGECODE_VERSION='win32'")
+ else:
+ cargs.append('-DGECODE_VERSION=$(./gecode_version)')
+ self.assertEqual(ext.extra_compile_args, cargs)
+ self.assertEqual(ext.language, 'cxx')
+
+ def test_missing_setuphook_warns(self):
+ self.write_setup({'setup-hook': 'this.does._not.exist'})
+ self.write_file('README', 'yeah')
+ dist = self.get_dist()
+ logs = self.get_logs(logging.WARNING)
+ self.assertEqual(1, len(logs))
+ self.assertIn('could not import setup_hook', logs[0])
+
+ def test_metadata_requires_description_files_missing(self):
+ self.write_setup({'description-file': 'README\n README2'})
+ self.write_file('README', 'yeah')
+ self.write_file('README2', 'yeah')
+ os.mkdir('src')
+ self.write_file(('src', 'haven.py'), '#')
+ self.write_file('script1.py', '#')
+ os.mkdir('scripts')
+ self.write_file(('scripts', 'find-coconuts'), '#')
+ os.mkdir('bin')
+ self.write_file(('bin', 'taunt'), '#')
+
+ for pkg in ('one', 'two', 'three'):
+ pkg = os.path.join('src', pkg)
+ os.mkdir(pkg)
+ self.write_file((pkg, '__init__.py'), '#')
+
+ dist = self.get_dist()
+ cmd = sdist(dist)
+ cmd.finalize_options()
+ cmd.get_file_list()
+ self.assertRaises(PackagingFileError, cmd.make_distribution)
+
+ def test_metadata_requires_description_files(self):
+ # Create the following file structure:
+ # README
+ # README2
+ # script1.py
+ # scripts/
+ # find-coconuts
+ # bin/
+ # taunt
+ # src/
+ # haven.py
+ # one/__init__.py
+ # two/__init__.py
+ # three/__init__.py
+
+ self.write_setup({'description-file': 'README\n README2',
+ 'extra-files': '\n README3'})
+ self.write_file('README', 'yeah 1')
+ self.write_file('README2', 'yeah 2')
+ self.write_file('README3', 'yeah 3')
+ os.mkdir('src')
+ self.write_file(('src', 'haven.py'), '#')
+ self.write_file('script1.py', '#')
+ os.mkdir('scripts')
+ self.write_file(('scripts', 'find-coconuts'), '#')
+ os.mkdir('bin')
+ self.write_file(('bin', 'taunt'), '#')
+
+ for pkg in ('one', 'two', 'three'):
+ pkg = os.path.join('src', pkg)
+ os.mkdir(pkg)
+ self.write_file((pkg, '__init__.py'), '#')
+
+ dist = self.get_dist()
+ self.assertIn('yeah 1\nyeah 2', dist.metadata['description'])
+
+ cmd = sdist(dist)
+ cmd.finalize_options()
+ cmd.get_file_list()
+ self.assertRaises(PackagingFileError, cmd.make_distribution)
+
+ self.write_setup({'description-file': 'README\n README2',
+ 'extra-files': '\n README2\n README'})
+ dist = self.get_dist()
+ cmd = sdist(dist)
+ cmd.finalize_options()
+ cmd.get_file_list()
+ cmd.make_distribution()
+ with open('MANIFEST') as fp:
+ self.assertIn('README\nREADME2\n', fp.read())
+
+ def test_sub_commands(self):
+ self.write_setup()
+ self.write_file('README', 'yeah')
+ os.mkdir('src')
+ self.write_file(('src', 'haven.py'), '#')
+ self.write_file('script1.py', '#')
+ os.mkdir('scripts')
+ self.write_file(('scripts', 'find-coconuts'), '#')
+ os.mkdir('bin')
+ self.write_file(('bin', 'taunt'), '#')
+
+ for pkg in ('one', 'two', 'three'):
+ pkg = os.path.join('src', pkg)
+ os.mkdir(pkg)
+ self.write_file((pkg, '__init__.py'), '#')
+
+ # try to run the install command to see if foo is called
+ dist = self.get_dist()
+ self.assertIn('foo', command.get_command_names())
+ self.assertEqual('FooBarBazTest',
+ dist.get_command_obj('foo').__class__.__name__)
+
+
+def test_suite():
+ return unittest.makeSuite(ConfigTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_create.py b/Lib/packaging/tests/test_create.py
new file mode 100644
index 0000000..99ab063
--- /dev/null
+++ b/Lib/packaging/tests/test_create.py
@@ -0,0 +1,235 @@
+"""Tests for packaging.create."""
+import io
+import os
+import sys
+import sysconfig
+from textwrap import dedent
+from packaging.create import MainProgram, ask_yn, ask, main
+
+from packaging.tests import support, unittest
+
+
+class CreateTestCase(support.TempdirManager,
+ support.EnvironRestorer,
+ unittest.TestCase):
+
+ restore_environ = ['PLAT']
+
+ def setUp(self):
+ super(CreateTestCase, self).setUp()
+ self._stdin = sys.stdin # TODO use Inputs
+ self._stdout = sys.stdout
+ sys.stdin = io.StringIO()
+ sys.stdout = io.StringIO()
+ self._cwd = os.getcwd()
+ self.wdir = self.mkdtemp()
+ os.chdir(self.wdir)
+ # patch sysconfig
+ self._old_get_paths = sysconfig.get_paths
+ sysconfig.get_paths = lambda *args, **kwargs: {
+ 'man': sys.prefix + '/share/man',
+ 'doc': sys.prefix + '/share/doc/pyxfoil', }
+
+ def tearDown(self):
+ super(CreateTestCase, self).tearDown()
+ sys.stdin = self._stdin
+ sys.stdout = self._stdout
+ os.chdir(self._cwd)
+ sysconfig.get_paths = self._old_get_paths
+
+ def test_ask_yn(self):
+ sys.stdin.write('y\n')
+ sys.stdin.seek(0)
+ self.assertEqual('y', ask_yn('is this a test'))
+
+ def test_ask(self):
+ sys.stdin.write('a\n')
+ sys.stdin.write('b\n')
+ sys.stdin.seek(0)
+ self.assertEqual('a', ask('is this a test'))
+ self.assertEqual('b', ask(str(list(range(0, 70))), default='c',
+ lengthy=True))
+
+ def test_set_multi(self):
+ mainprogram = MainProgram()
+ sys.stdin.write('aaaaa\n')
+ sys.stdin.seek(0)
+ mainprogram.data['author'] = []
+ mainprogram._set_multi('_set_multi test', 'author')
+ self.assertEqual(['aaaaa'], mainprogram.data['author'])
+
+ def test_find_files(self):
+ # making sure we scan a project dir correctly
+ mainprogram = MainProgram()
+
+ # building the structure
+ tempdir = self.wdir
+ dirs = ['pkg1', 'data', 'pkg2', 'pkg2/sub']
+ files = ['README', 'setup.cfg', 'foo.py',
+ 'pkg1/__init__.py', 'pkg1/bar.py',
+ 'data/data1', 'pkg2/__init__.py',
+ 'pkg2/sub/__init__.py']
+
+ for dir_ in dirs:
+ os.mkdir(os.path.join(tempdir, dir_))
+
+ for file_ in files:
+ path = os.path.join(tempdir, file_)
+ self.write_file(path, 'xxx')
+
+ mainprogram._find_files()
+ mainprogram.data['packages'].sort()
+
+ # do we have what we want?
+ self.assertEqual(mainprogram.data['packages'],
+ ['pkg1', 'pkg2', 'pkg2.sub'])
+ self.assertEqual(mainprogram.data['modules'], ['foo'])
+ data_fn = os.path.join('data', 'data1')
+ self.assertEqual(set(mainprogram.data['extra_files']),
+ set(['setup.cfg', 'README', data_fn]))
+
+ def test_convert_setup_py_to_cfg(self):
+ self.write_file((self.wdir, 'setup.py'),
+ dedent("""
+ # -*- coding: utf-8 -*-
+ from distutils.core import setup
+
+ long_description = '''My super Death-scription
+ barbar is now on the public domain,
+ ho, baby !'''
+
+ setup(name='pyxfoil',
+ version='0.2',
+ description='Python bindings for the Xfoil engine',
+ long_description=long_description,
+ maintainer='André Espaze',
+ maintainer_email='andre.espaze@logilab.fr',
+ url='http://www.python-science.org/project/pyxfoil',
+ license='GPLv2',
+ packages=['pyxfoil', 'babar', 'me'],
+ data_files=[
+ ('share/doc/pyxfoil', ['README.rst']),
+ ('share/man', ['pyxfoil.1']),
+ ],
+ py_modules=['my_lib', 'mymodule'],
+ package_dir={
+ 'babar': '',
+ 'me': 'Martinique/Lamentin',
+ },
+ package_data={
+ 'babar': ['Pom', 'Flora', 'Alexander'],
+ 'me': ['dady', 'mumy', 'sys', 'bro'],
+ '': ['setup.py', 'README'],
+ 'pyxfoil': ['fengine.so'],
+ },
+ scripts=['my_script', 'bin/run'],
+ )
+ """))
+ sys.stdin.write('y\n')
+ sys.stdin.seek(0)
+ main()
+
+ with open(os.path.join(self.wdir, 'setup.cfg')) as fp:
+ lines = set(line.rstrip() for line in fp)
+
+ # FIXME don't use sets
+ self.assertEqual(lines, set(['',
+ '[metadata]',
+ 'version = 0.2',
+ 'name = pyxfoil',
+ 'maintainer = André Espaze',
+ 'description = My super Death-scription',
+ ' |barbar is now on the public domain,',
+ ' |ho, baby !',
+ 'maintainer_email = andre.espaze@logilab.fr',
+ 'home_page = http://www.python-science.org/project/pyxfoil',
+ 'download_url = UNKNOWN',
+ 'summary = Python bindings for the Xfoil engine',
+ '[files]',
+ 'modules = my_lib',
+ ' mymodule',
+ 'packages = pyxfoil',
+ ' babar',
+ ' me',
+ 'extra_files = Martinique/Lamentin/dady',
+ ' Martinique/Lamentin/mumy',
+ ' Martinique/Lamentin/sys',
+ ' Martinique/Lamentin/bro',
+ ' Pom',
+ ' Flora',
+ ' Alexander',
+ ' setup.py',
+ ' README',
+ ' pyxfoil/fengine.so',
+ 'scripts = my_script',
+ ' bin/run',
+ 'resources =',
+ ' README.rst = {doc}',
+ ' pyxfoil.1 = {man}',
+ ]))
+
+ def test_convert_setup_py_to_cfg_with_description_in_readme(self):
+ self.write_file((self.wdir, 'setup.py'),
+ dedent("""
+ # -*- coding: utf-8 -*-
+ from distutils.core import setup
+ fp = open('README.txt')
+ try:
+ long_description = fp.read()
+ finally:
+ fp.close()
+
+ setup(name='pyxfoil',
+ version='0.2',
+ description='Python bindings for the Xfoil engine',
+ long_description=long_description,
+ maintainer='André Espaze',
+ maintainer_email='andre.espaze@logilab.fr',
+ url='http://www.python-science.org/project/pyxfoil',
+ license='GPLv2',
+ packages=['pyxfoil'],
+ package_data={'pyxfoil': ['fengine.so', 'babar.so']},
+ data_files=[
+ ('share/doc/pyxfoil', ['README.rst']),
+ ('share/man', ['pyxfoil.1']),
+ ],
+ )
+ """))
+ self.write_file((self.wdir, 'README.txt'),
+ dedent('''
+My super Death-scription
+barbar is now in the public domain,
+ho, baby!
+ '''))
+ sys.stdin.write('y\n')
+ sys.stdin.seek(0)
+ # FIXME Out of memory error.
+ main()
+ with open(os.path.join(self.wdir, 'setup.cfg')) as fp:
+ lines = set(line.rstrip() for line in fp)
+
+ self.assertEqual(lines, set(['',
+ '[metadata]',
+ 'version = 0.2',
+ 'name = pyxfoil',
+ 'maintainer = André Espaze',
+ 'maintainer_email = andre.espaze@logilab.fr',
+ 'home_page = http://www.python-science.org/project/pyxfoil',
+ 'download_url = UNKNOWN',
+ 'summary = Python bindings for the Xfoil engine',
+ 'description-file = README.txt',
+ '[files]',
+ 'packages = pyxfoil',
+ 'extra_files = pyxfoil/fengine.so',
+ ' pyxfoil/babar.so',
+ 'resources =',
+ ' README.rst = {doc}',
+ ' pyxfoil.1 = {man}',
+ ]))
+
+
+def test_suite():
+ return unittest.makeSuite(CreateTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_cygwinccompiler.py b/Lib/packaging/tests/test_cygwinccompiler.py
new file mode 100644
index 0000000..17c43cd
--- /dev/null
+++ b/Lib/packaging/tests/test_cygwinccompiler.py
@@ -0,0 +1,88 @@
+"""Tests for packaging.cygwinccompiler."""
+import os
+import sys
+import sysconfig
+from packaging.compiler.cygwinccompiler import (
+ check_config_h, get_msvcr,
+ CONFIG_H_OK, CONFIG_H_NOTOK, CONFIG_H_UNCERTAIN)
+
+from packaging.tests import unittest, support
+
+
+class CygwinCCompilerTestCase(support.TempdirManager,
+ unittest.TestCase):
+
+ def setUp(self):
+ super(CygwinCCompilerTestCase, self).setUp()
+ self.version = sys.version
+ self.python_h = os.path.join(self.mkdtemp(), 'python.h')
+ self.old_get_config_h_filename = sysconfig.get_config_h_filename
+ sysconfig.get_config_h_filename = self._get_config_h_filename
+
+ def tearDown(self):
+ sys.version = self.version
+ sysconfig.get_config_h_filename = self.old_get_config_h_filename
+ super(CygwinCCompilerTestCase, self).tearDown()
+
+ def _get_config_h_filename(self):
+ return self.python_h
+
+ def test_check_config_h(self):
+ # check_config_h looks for "GCC" in sys.version first
+ # returns CONFIG_H_OK if found
+ sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC '
+ '4.0.1 (Apple Computer, Inc. build 5370)]')
+
+ self.assertEqual(check_config_h()[0], CONFIG_H_OK)
+
+ # then it tries to see if it can find "__GNUC__" in pyconfig.h
+ sys.version = 'something without the *CC word'
+
+ # if the file doesn't exist it returns CONFIG_H_UNCERTAIN
+ self.assertEqual(check_config_h()[0], CONFIG_H_UNCERTAIN)
+
+ # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK
+ self.write_file(self.python_h, 'xxx')
+ self.assertEqual(check_config_h()[0], CONFIG_H_NOTOK)
+
+ # and CONFIG_H_OK if __GNUC__ is found
+ self.write_file(self.python_h, 'xxx __GNUC__ xxx')
+ self.assertEqual(check_config_h()[0], CONFIG_H_OK)
+
+ def test_get_msvcr(self):
+ # none
+ sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) '
+ '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]')
+ self.assertEqual(get_msvcr(), None)
+
+ # MSVC 7.0
+ sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
+ '[MSC v.1300 32 bits (Intel)]')
+ self.assertEqual(get_msvcr(), ['msvcr70'])
+
+ # MSVC 7.1
+ sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
+ '[MSC v.1310 32 bits (Intel)]')
+ self.assertEqual(get_msvcr(), ['msvcr71'])
+
+ # VS2005 / MSVC 8.0
+ sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
+ '[MSC v.1400 32 bits (Intel)]')
+ self.assertEqual(get_msvcr(), ['msvcr80'])
+
+ # VS2008 / MSVC 9.0
+ sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
+ '[MSC v.1500 32 bits (Intel)]')
+ self.assertEqual(get_msvcr(), ['msvcr90'])
+
+ # unknown
+ sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
+ '[MSC v.1999 32 bits (Intel)]')
+ self.assertRaises(ValueError, get_msvcr)
+
+
+def test_suite():
+ return unittest.makeSuite(CygwinCCompilerTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_database.py b/Lib/packaging/tests/test_database.py
new file mode 100644
index 0000000..c8d9415
--- /dev/null
+++ b/Lib/packaging/tests/test_database.py
@@ -0,0 +1,506 @@
+import os
+import io
+import csv
+import imp
+import sys
+import shutil
+import zipfile
+import tempfile
+from os.path import relpath # separate import for backport concerns
+from hashlib import md5
+
+from packaging.errors import PackagingError
+from packaging.metadata import Metadata
+from packaging.tests import unittest, run_unittest, support, TESTFN
+
+from packaging.database import (
+ Distribution, EggInfoDistribution, get_distribution, get_distributions,
+ provides_distribution, obsoletes_distribution, get_file_users,
+ enable_cache, disable_cache, distinfo_dirname, _yield_distributions)
+
+# TODO Add a test for getting a distribution provided by another distribution
+# TODO Add a test for absolute pathed RECORD items (e.g. /etc/myapp/config.ini)
+# TODO Add tests from the former pep376 project (zipped site-packages, etc.)
+
+
+def get_hexdigest(filename):
+ with open(filename, 'rb') as file:
+ checksum = md5(file.read())
+ return checksum.hexdigest()
+
+
+def record_pieces(file):
+ path = relpath(file, sys.prefix)
+ digest = get_hexdigest(file)
+ size = os.path.getsize(file)
+ return [path, digest, size]
+
+
+class CommonDistributionTests:
+ """Mixin used to test the interface common to both Distribution classes.
+
+ Derived classes define cls, sample_dist, dirs and records. These
+ attributes are used in test methods. See source code for details.
+ """
+
+ def setUp(self):
+ super(CommonDistributionTests, self).setUp()
+ self.addCleanup(enable_cache)
+ disable_cache()
+ self.fake_dists_path = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), 'fake_dists'))
+
+ def test_instantiation(self):
+ # check that useful attributes are here
+ name, version, distdir = self.sample_dist
+ here = os.path.abspath(os.path.dirname(__file__))
+ dist_path = os.path.join(here, 'fake_dists', distdir)
+
+ dist = self.dist = self.cls(dist_path)
+ self.assertEqual(dist.path, dist_path)
+ self.assertEqual(dist.name, name)
+ self.assertEqual(dist.metadata['Name'], name)
+ self.assertIsInstance(dist.metadata, Metadata)
+ self.assertEqual(dist.version, version)
+ self.assertEqual(dist.metadata['Version'], version)
+
+ def test_repr(self):
+ dist = self.cls(self.dirs[0])
+ # just check that the class name is in the repr
+ self.assertIn(self.cls.__name__, repr(dist))
+
+ def test_comparison(self):
+ # tests for __eq__ and __hash__
+ dist = self.cls(self.dirs[0])
+ dist2 = self.cls(self.dirs[0])
+ dist3 = self.cls(self.dirs[1])
+ self.assertIn(dist, {dist: True})
+ self.assertEqual(dist, dist)
+
+ self.assertIsNot(dist, dist2)
+ self.assertEqual(dist, dist2)
+ self.assertNotEqual(dist, dist3)
+ self.assertNotEqual(dist, ())
+
+ def test_list_installed_files(self):
+ for dir_ in self.dirs:
+ dist = self.cls(dir_)
+ for path, md5_, size in dist.list_installed_files():
+ record_data = self.records[dist.path]
+ self.assertIn(path, record_data)
+ self.assertEqual(md5_, record_data[path][0])
+ self.assertEqual(size, record_data[path][1])
+
+
+class TestDistribution(CommonDistributionTests, unittest.TestCase):
+
+ cls = Distribution
+ sample_dist = 'choxie', '2.0.0.9', 'choxie-2.0.0.9.dist-info'
+
+ def setUp(self):
+ super(TestDistribution, self).setUp()
+ self.dirs = [os.path.join(self.fake_dists_path, f)
+ for f in os.listdir(self.fake_dists_path)
+ if f.endswith('.dist-info')]
+
+ self.records = {}
+ for distinfo_dir in self.dirs:
+ record_file = os.path.join(distinfo_dir, 'RECORD')
+ with open(record_file, 'w') as file:
+ record_writer = csv.writer(
+ file, delimiter=',', quoting=csv.QUOTE_NONE)
+
+ dist_location = distinfo_dir.replace('.dist-info', '')
+
+ for path, dirs, files in os.walk(dist_location):
+ for f in files:
+ record_writer.writerow(record_pieces(
+ os.path.join(path, f)))
+ for file in ('INSTALLER', 'METADATA', 'REQUESTED'):
+ record_writer.writerow(record_pieces(
+ os.path.join(distinfo_dir, file)))
+ record_writer.writerow([relpath(record_file, sys.prefix)])
+
+ with open(record_file) as file:
+ record_reader = csv.reader(file)
+ record_data = {}
+ for row in record_reader:
+ path, md5_, size = (row[:] +
+ [None for i in range(len(row), 3)])
+ record_data[path] = md5_, size
+ self.records[distinfo_dir] = record_data
+
+ def tearDown(self):
+ for distinfo_dir in self.dirs:
+ record_file = os.path.join(distinfo_dir, 'RECORD')
+ open(record_file, 'w').close()
+ super(TestDistribution, self).tearDown()
+
+ def test_instantiation(self):
+ super(TestDistribution, self).test_instantiation()
+ self.assertIsInstance(self.dist.requested, bool)
+
+ def test_uses(self):
+ # Test to determine if a distribution uses a specified file.
+ # Criteria to test against
+ distinfo_name = 'grammar-1.0a4'
+ distinfo_dir = os.path.join(self.fake_dists_path,
+ distinfo_name + '.dist-info')
+ true_path = [self.fake_dists_path, distinfo_name,
+ 'grammar', 'utils.py']
+ true_path = relpath(os.path.join(*true_path), sys.prefix)
+ false_path = [self.fake_dists_path, 'towel_stuff-0.1', 'towel_stuff',
+ '__init__.py']
+ false_path = relpath(os.path.join(*false_path), sys.prefix)
+
+ # Test if the distribution uses the file in question
+ dist = Distribution(distinfo_dir)
+ self.assertTrue(dist.uses(true_path))
+ self.assertFalse(dist.uses(false_path))
+
+ def test_get_distinfo_file(self):
+ # Test the retrieval of dist-info file objects.
+ distinfo_name = 'choxie-2.0.0.9'
+ other_distinfo_name = 'grammar-1.0a4'
+ distinfo_dir = os.path.join(self.fake_dists_path,
+ distinfo_name + '.dist-info')
+ dist = Distribution(distinfo_dir)
+ # Test for known good file matches
+ distinfo_files = [
+ # Relative paths
+ 'INSTALLER', 'METADATA',
+ # Absolute paths
+ os.path.join(distinfo_dir, 'RECORD'),
+ os.path.join(distinfo_dir, 'REQUESTED'),
+ ]
+
+ for distfile in distinfo_files:
+ with dist.get_distinfo_file(distfile) as value:
+ self.assertIsInstance(value, io.TextIOWrapper)
+ # Is it the correct file?
+ self.assertEqual(value.name,
+ os.path.join(distinfo_dir, distfile))
+
+ # Test an absolute path that is part of another distributions dist-info
+ other_distinfo_file = os.path.join(
+ self.fake_dists_path, other_distinfo_name + '.dist-info',
+ 'REQUESTED')
+ self.assertRaises(PackagingError, dist.get_distinfo_file,
+ other_distinfo_file)
+ # Test for a file that should not exist
+ self.assertRaises(PackagingError, dist.get_distinfo_file,
+ 'MAGICFILE')
+
+ def test_list_distinfo_files(self):
+ # Test for the iteration of RECORD path entries.
+ distinfo_name = 'towel_stuff-0.1'
+ distinfo_dir = os.path.join(self.fake_dists_path,
+ distinfo_name + '.dist-info')
+ dist = Distribution(distinfo_dir)
+ # Test for the iteration of the raw path
+ distinfo_record_paths = self.records[distinfo_dir].keys()
+ found = dist.list_distinfo_files()
+ self.assertEqual(sorted(found), sorted(distinfo_record_paths))
+ # Test for the iteration of local absolute paths
+ distinfo_record_paths = [os.path.join(sys.prefix, path)
+ for path in self.records[distinfo_dir]]
+ found = dist.list_distinfo_files(local=True)
+ self.assertEqual(sorted(found), sorted(distinfo_record_paths))
+
+ def test_get_resources_path(self):
+ distinfo_name = 'babar-0.1'
+ distinfo_dir = os.path.join(self.fake_dists_path,
+ distinfo_name + '.dist-info')
+ dist = Distribution(distinfo_dir)
+ resource_path = dist.get_resource_path('babar.png')
+ self.assertEqual(resource_path, 'babar.png')
+ self.assertRaises(KeyError, dist.get_resource_path, 'notexist')
+
+
+class TestEggInfoDistribution(CommonDistributionTests,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ cls = EggInfoDistribution
+ sample_dist = 'bacon', '0.1', 'bacon-0.1.egg-info'
+
+ def setUp(self):
+ super(TestEggInfoDistribution, self).setUp()
+
+ self.dirs = [os.path.join(self.fake_dists_path, f)
+ for f in os.listdir(self.fake_dists_path)
+ if f.endswith('.egg') or f.endswith('.egg-info')]
+
+ self.records = {}
+
+ @unittest.skip('not implemented yet')
+ def test_list_installed_files(self):
+ # EggInfoDistribution defines list_installed_files but there is no
+ # test for it yet; someone with setuptools expertise needs to add a
+ # file with the list of installed files for one of the egg fake dists
+ # and write the support code to populate self.records (and then delete
+ # this method)
+ pass
+
+
+class TestDatabase(support.LoggingCatcher,
+ unittest.TestCase):
+
+ def setUp(self):
+ super(TestDatabase, self).setUp()
+ disable_cache()
+ # Setup the path environment with our fake distributions
+ current_path = os.path.abspath(os.path.dirname(__file__))
+ self.sys_path = sys.path[:]
+ self.fake_dists_path = os.path.join(current_path, 'fake_dists')
+ sys.path.insert(0, self.fake_dists_path)
+
+ def tearDown(self):
+ sys.path[:] = self.sys_path
+ enable_cache()
+ super(TestDatabase, self).tearDown()
+
+ def test_distinfo_dirname(self):
+ # Given a name and a version, we expect the distinfo_dirname function
+ # to return a standard distribution information directory name.
+
+ items = [
+ # (name, version, standard_dirname)
+ # Test for a very simple single word name and decimal version
+ # number
+ ('docutils', '0.5', 'docutils-0.5.dist-info'),
+ # Test for another except this time with a '-' in the name, which
+ # needs to be transformed during the name lookup
+ ('python-ldap', '2.5', 'python_ldap-2.5.dist-info'),
+ # Test for both '-' in the name and a funky version number
+ ('python-ldap', '2.5 a---5', 'python_ldap-2.5 a---5.dist-info'),
+ ]
+
+ # Loop through the items to validate the results
+ for name, version, standard_dirname in items:
+ dirname = distinfo_dirname(name, version)
+ self.assertEqual(dirname, standard_dirname)
+
+ def test_get_distributions(self):
+ # Lookup all distributions found in the ``sys.path``.
+ # This test could potentially pick up other installed distributions
+ fake_dists = [('grammar', '1.0a4'), ('choxie', '2.0.0.9'),
+ ('towel-stuff', '0.1'), ('babar', '0.1')]
+ found_dists = []
+
+ # Verify the fake dists have been found.
+ dists = [dist for dist in get_distributions()]
+ for dist in dists:
+ self.assertIsInstance(dist, Distribution)
+ if (dist.name in dict(fake_dists) and
+ dist.path.startswith(self.fake_dists_path)):
+ found_dists.append((dist.name, dist.metadata['version'], ))
+ else:
+ # check that it doesn't find anything more than this
+ self.assertFalse(dist.path.startswith(self.fake_dists_path))
+ # otherwise we don't care what other distributions are found
+
+ # Finally, test that we found all that we were looking for
+ self.assertEqual(sorted(found_dists), sorted(fake_dists))
+
+ # Now, test if the egg-info distributions are found correctly as well
+ fake_dists += [('bacon', '0.1'), ('cheese', '2.0.2'),
+ ('coconuts-aster', '10.3'),
+ ('banana', '0.4'), ('strawberry', '0.6'),
+ ('truffles', '5.0'), ('nut', 'funkyversion')]
+ found_dists = []
+
+ dists = [dist for dist in get_distributions(use_egg_info=True)]
+ for dist in dists:
+ self.assertIsInstance(dist, (Distribution, EggInfoDistribution))
+ if (dist.name in dict(fake_dists) and
+ dist.path.startswith(self.fake_dists_path)):
+ found_dists.append((dist.name, dist.metadata['version']))
+ else:
+ self.assertFalse(dist.path.startswith(self.fake_dists_path))
+
+ self.assertEqual(sorted(fake_dists), sorted(found_dists))
+
+ def test_get_distribution(self):
+ # Test for looking up a distribution by name.
+ # Test the lookup of the towel-stuff distribution
+ name = 'towel-stuff' # Note: This is different from the directory name
+
+ # Lookup the distribution
+ dist = get_distribution(name)
+ self.assertIsInstance(dist, Distribution)
+ self.assertEqual(dist.name, name)
+
+ # Verify that an unknown distribution returns None
+ self.assertIsNone(get_distribution('bogus'))
+
+ # Verify partial name matching doesn't work
+ self.assertIsNone(get_distribution('towel'))
+
+ # Verify that it does not find egg-info distributions, when not
+ # instructed to
+ self.assertIsNone(get_distribution('bacon'))
+ self.assertIsNone(get_distribution('cheese'))
+ self.assertIsNone(get_distribution('strawberry'))
+ self.assertIsNone(get_distribution('banana'))
+
+ # Now check that it works well in both situations, when egg-info
+ # is a file and directory respectively.
+ dist = get_distribution('cheese', use_egg_info=True)
+ self.assertIsInstance(dist, EggInfoDistribution)
+ self.assertEqual(dist.name, 'cheese')
+
+ dist = get_distribution('bacon', use_egg_info=True)
+ self.assertIsInstance(dist, EggInfoDistribution)
+ self.assertEqual(dist.name, 'bacon')
+
+ dist = get_distribution('banana', use_egg_info=True)
+ self.assertIsInstance(dist, EggInfoDistribution)
+ self.assertEqual(dist.name, 'banana')
+
+ dist = get_distribution('strawberry', use_egg_info=True)
+ self.assertIsInstance(dist, EggInfoDistribution)
+ self.assertEqual(dist.name, 'strawberry')
+
+ def test_get_file_users(self):
+ # Test the iteration of distributions that use a file.
+ name = 'towel_stuff-0.1'
+ path = os.path.join(self.fake_dists_path, name,
+ 'towel_stuff', '__init__.py')
+ for dist in get_file_users(path):
+ self.assertIsInstance(dist, Distribution)
+ self.assertEqual(dist.name, name)
+
+ def test_provides(self):
+ # Test for looking up distributions by what they provide
+ checkLists = lambda x, y: self.assertEqual(sorted(x), sorted(y))
+
+ l = [dist.name for dist in provides_distribution('truffles')]
+ checkLists(l, ['choxie', 'towel-stuff'])
+
+ l = [dist.name for dist in provides_distribution('truffles', '1.0')]
+ checkLists(l, ['choxie'])
+
+ l = [dist.name for dist in provides_distribution('truffles', '1.0',
+ use_egg_info=True)]
+ checkLists(l, ['choxie', 'cheese'])
+
+ l = [dist.name for dist in provides_distribution('truffles', '1.1.2')]
+ checkLists(l, ['towel-stuff'])
+
+ l = [dist.name for dist in provides_distribution('truffles', '1.1')]
+ checkLists(l, ['towel-stuff'])
+
+ l = [dist.name for dist in provides_distribution('truffles',
+ '!=1.1,<=2.0')]
+ checkLists(l, ['choxie'])
+
+ l = [dist.name for dist in provides_distribution('truffles',
+ '!=1.1,<=2.0',
+ use_egg_info=True)]
+ checkLists(l, ['choxie', 'bacon', 'cheese'])
+
+ l = [dist.name for dist in provides_distribution('truffles', '>1.0')]
+ checkLists(l, ['towel-stuff'])
+
+ l = [dist.name for dist in provides_distribution('truffles', '>1.5')]
+ checkLists(l, [])
+
+ l = [dist.name for dist in provides_distribution('truffles', '>1.5',
+ use_egg_info=True)]
+ checkLists(l, ['bacon'])
+
+ l = [dist.name for dist in provides_distribution('truffles', '>=1.0')]
+ checkLists(l, ['choxie', 'towel-stuff'])
+
+ l = [dist.name for dist in provides_distribution('strawberry', '0.6',
+ use_egg_info=True)]
+ checkLists(l, ['coconuts-aster'])
+
+ l = [dist.name for dist in provides_distribution('strawberry', '>=0.5',
+ use_egg_info=True)]
+ checkLists(l, ['coconuts-aster'])
+
+ l = [dist.name for dist in provides_distribution('strawberry', '>0.6',
+ use_egg_info=True)]
+ checkLists(l, [])
+
+ l = [dist.name for dist in provides_distribution('banana', '0.4',
+ use_egg_info=True)]
+ checkLists(l, ['coconuts-aster'])
+
+ l = [dist.name for dist in provides_distribution('banana', '>=0.3',
+ use_egg_info=True)]
+ checkLists(l, ['coconuts-aster'])
+
+ l = [dist.name for dist in provides_distribution('banana', '!=0.4',
+ use_egg_info=True)]
+ checkLists(l, [])
+
+ def test_obsoletes(self):
+ # Test looking for distributions based on what they obsolete
+ checkLists = lambda x, y: self.assertEqual(sorted(x), sorted(y))
+
+ l = [dist.name for dist in obsoletes_distribution('truffles', '1.0')]
+ checkLists(l, [])
+
+ l = [dist.name for dist in obsoletes_distribution('truffles', '1.0',
+ use_egg_info=True)]
+ checkLists(l, ['cheese', 'bacon'])
+
+ l = [dist.name for dist in obsoletes_distribution('truffles', '0.8')]
+ checkLists(l, ['choxie'])
+
+ l = [dist.name for dist in obsoletes_distribution('truffles', '0.8',
+ use_egg_info=True)]
+ checkLists(l, ['choxie', 'cheese'])
+
+ l = [dist.name for dist in obsoletes_distribution('truffles', '0.9.6')]
+ checkLists(l, ['choxie', 'towel-stuff'])
+
+ l = [dist.name for dist in obsoletes_distribution('truffles',
+ '0.5.2.3')]
+ checkLists(l, ['choxie', 'towel-stuff'])
+
+ l = [dist.name for dist in obsoletes_distribution('truffles', '0.2')]
+ checkLists(l, ['towel-stuff'])
+
+ def test_yield_distribution(self):
+ # tests the internal function _yield_distributions
+ checkLists = lambda x, y: self.assertEqual(sorted(x), sorted(y))
+
+ eggs = [('bacon', '0.1'), ('banana', '0.4'), ('strawberry', '0.6'),
+ ('truffles', '5.0'), ('cheese', '2.0.2'),
+ ('coconuts-aster', '10.3'), ('nut', 'funkyversion')]
+ dists = [('choxie', '2.0.0.9'), ('grammar', '1.0a4'),
+ ('towel-stuff', '0.1'), ('babar', '0.1')]
+
+ checkLists([], _yield_distributions(False, False))
+
+ found = [(dist.name, dist.metadata['Version'])
+ for dist in _yield_distributions(False, True)
+ if dist.path.startswith(self.fake_dists_path)]
+ checkLists(eggs, found)
+
+ found = [(dist.name, dist.metadata['Version'])
+ for dist in _yield_distributions(True, False)
+ if dist.path.startswith(self.fake_dists_path)]
+ checkLists(dists, found)
+
+ found = [(dist.name, dist.metadata['Version'])
+ for dist in _yield_distributions(True, True)
+ if dist.path.startswith(self.fake_dists_path)]
+ checkLists(dists + eggs, found)
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ load = unittest.defaultTestLoader.loadTestsFromTestCase
+ suite.addTest(load(TestDistribution))
+ suite.addTest(load(TestEggInfoDistribution))
+ suite.addTest(load(TestDatabase))
+ return suite
+
+
+if __name__ == "__main__":
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_depgraph.py b/Lib/packaging/tests/test_depgraph.py
new file mode 100644
index 0000000..9271a7b
--- /dev/null
+++ b/Lib/packaging/tests/test_depgraph.py
@@ -0,0 +1,301 @@
+"""Tests for packaging.depgraph """
+import io
+import os
+import re
+import sys
+import packaging.database
+from packaging import depgraph
+
+from packaging.tests import unittest, support
+
+
+class DepGraphTestCase(support.LoggingCatcher,
+ unittest.TestCase):
+
+ DISTROS_DIST = ('choxie', 'grammar', 'towel-stuff')
+ DISTROS_EGG = ('bacon', 'banana', 'strawberry', 'cheese')
+ BAD_EGGS = ('nut',)
+
+ EDGE = re.compile(
+ r'"(?P<from>.*)" -> "(?P<to>.*)" \[label="(?P<label>.*)"\]')
+
+ def checkLists(self, l1, l2):
+ """ Compare two lists without taking the order into consideration """
+ self.assertListEqual(sorted(l1), sorted(l2))
+
+ def setUp(self):
+ super(DepGraphTestCase, self).setUp()
+ path = os.path.join(os.path.dirname(__file__), 'fake_dists')
+ path = os.path.abspath(path)
+ sys.path.insert(0, path)
+ self.addCleanup(sys.path.remove, path)
+ self.addCleanup(packaging.database.enable_cache)
+ packaging.database.disable_cache()
+
+ def test_generate_graph(self):
+ dists = []
+ for name in self.DISTROS_DIST:
+ dist = packaging.database.get_distribution(name)
+ self.assertNotEqual(dist, None)
+ dists.append(dist)
+
+ choxie, grammar, towel = dists
+
+ graph = depgraph.generate_graph(dists)
+
+ deps = [(x.name, y) for x, y in graph.adjacency_list[choxie]]
+ self.checkLists([('towel-stuff', 'towel-stuff (0.1)')], deps)
+ self.assertIn(choxie, graph.reverse_list[towel])
+ self.checkLists(graph.missing[choxie], ['nut'])
+
+ deps = [(x.name, y) for x, y in graph.adjacency_list[grammar]]
+ self.checkLists([], deps)
+ self.checkLists(graph.missing[grammar], ['truffles (>=1.2)'])
+
+ deps = [(x.name, y) for x, y in graph.adjacency_list[towel]]
+ self.checkLists([], deps)
+ self.checkLists(graph.missing[towel], ['bacon (<=0.2)'])
+
+ def test_generate_graph_egg(self):
+ dists = []
+ for name in self.DISTROS_DIST + self.DISTROS_EGG:
+ dist = packaging.database.get_distribution(name, use_egg_info=True)
+ self.assertNotEqual(dist, None)
+ dists.append(dist)
+
+ choxie, grammar, towel, bacon, banana, strawberry, cheese = dists
+
+ graph = depgraph.generate_graph(dists)
+
+ deps = [(x.name, y) for x, y in graph.adjacency_list[choxie]]
+ self.checkLists([('towel-stuff', 'towel-stuff (0.1)')], deps)
+ self.assertIn(choxie, graph.reverse_list[towel])
+ self.checkLists(graph.missing[choxie], ['nut'])
+
+ deps = [(x.name, y) for x, y in graph.adjacency_list[grammar]]
+ self.checkLists([('bacon', 'truffles (>=1.2)')], deps)
+ self.checkLists(graph.missing[grammar], [])
+ self.assertIn(grammar, graph.reverse_list[bacon])
+
+ deps = [(x.name, y) for x, y in graph.adjacency_list[towel]]
+ self.checkLists([('bacon', 'bacon (<=0.2)')], deps)
+ self.checkLists(graph.missing[towel], [])
+ self.assertIn(towel, graph.reverse_list[bacon])
+
+ deps = [(x.name, y) for x, y in graph.adjacency_list[bacon]]
+ self.checkLists([], deps)
+ self.checkLists(graph.missing[bacon], [])
+
+ deps = [(x.name, y) for x, y in graph.adjacency_list[banana]]
+ self.checkLists([('strawberry', 'strawberry (>=0.5)')], deps)
+ self.checkLists(graph.missing[banana], [])
+ self.assertIn(banana, graph.reverse_list[strawberry])
+
+ deps = [(x.name, y) for x, y in graph.adjacency_list[strawberry]]
+ self.checkLists([], deps)
+ self.checkLists(graph.missing[strawberry], [])
+
+ deps = [(x.name, y) for x, y in graph.adjacency_list[cheese]]
+ self.checkLists([], deps)
+ self.checkLists(graph.missing[cheese], [])
+
+ def test_dependent_dists(self):
+ dists = []
+ for name in self.DISTROS_DIST:
+ dist = packaging.database.get_distribution(name)
+ self.assertNotEqual(dist, None)
+ dists.append(dist)
+
+ choxie, grammar, towel = dists
+
+ deps = [d.name for d in depgraph.dependent_dists(dists, choxie)]
+ self.checkLists([], deps)
+
+ deps = [d.name for d in depgraph.dependent_dists(dists, grammar)]
+ self.checkLists([], deps)
+
+ deps = [d.name for d in depgraph.dependent_dists(dists, towel)]
+ self.checkLists(['choxie'], deps)
+
+ def test_dependent_dists_egg(self):
+ dists = []
+ for name in self.DISTROS_DIST + self.DISTROS_EGG:
+ dist = packaging.database.get_distribution(name, use_egg_info=True)
+ self.assertNotEqual(dist, None)
+ dists.append(dist)
+
+ choxie, grammar, towel, bacon, banana, strawberry, cheese = dists
+
+ deps = [d.name for d in depgraph.dependent_dists(dists, choxie)]
+ self.checkLists([], deps)
+
+ deps = [d.name for d in depgraph.dependent_dists(dists, grammar)]
+ self.checkLists([], deps)
+
+ deps = [d.name for d in depgraph.dependent_dists(dists, towel)]
+ self.checkLists(['choxie'], deps)
+
+ deps = [d.name for d in depgraph.dependent_dists(dists, bacon)]
+ self.checkLists(['choxie', 'towel-stuff', 'grammar'], deps)
+
+ deps = [d.name for d in depgraph.dependent_dists(dists, strawberry)]
+ self.checkLists(['banana'], deps)
+
+ deps = [d.name for d in depgraph.dependent_dists(dists, cheese)]
+ self.checkLists([], deps)
+
+ def test_graph_to_dot(self):
+ expected = (
+ ('towel-stuff', 'bacon', 'bacon (<=0.2)'),
+ ('grammar', 'bacon', 'truffles (>=1.2)'),
+ ('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
+ ('banana', 'strawberry', 'strawberry (>=0.5)'),
+ )
+
+ dists = []
+ for name in self.DISTROS_DIST + self.DISTROS_EGG:
+ dist = packaging.database.get_distribution(name, use_egg_info=True)
+ self.assertNotEqual(dist, None)
+ dists.append(dist)
+
+ graph = depgraph.generate_graph(dists)
+ buf = io.StringIO()
+ depgraph.graph_to_dot(graph, buf)
+ buf.seek(0)
+ matches = []
+ lines = buf.readlines()
+ for line in lines[1:-1]: # skip the first and the last lines
+ if line[-1] == '\n':
+ line = line[:-1]
+ match = self.EDGE.match(line.strip())
+ self.assertIsNot(match, None)
+ matches.append(match.groups())
+
+ self.checkLists(matches, expected)
+
+ def test_graph_disconnected_to_dot(self):
+ dependencies_expected = (
+ ('towel-stuff', 'bacon', 'bacon (<=0.2)'),
+ ('grammar', 'bacon', 'truffles (>=1.2)'),
+ ('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
+ ('banana', 'strawberry', 'strawberry (>=0.5)'),
+ )
+ disconnected_expected = ('cheese', 'bacon', 'strawberry')
+
+ dists = []
+ for name in self.DISTROS_DIST + self.DISTROS_EGG:
+ dist = packaging.database.get_distribution(name, use_egg_info=True)
+ self.assertNotEqual(dist, None)
+ dists.append(dist)
+
+ graph = depgraph.generate_graph(dists)
+ buf = io.StringIO()
+ depgraph.graph_to_dot(graph, buf, skip_disconnected=False)
+ buf.seek(0)
+ lines = buf.readlines()
+
+ dependencies_lines = []
+ disconnected_lines = []
+
+ # First sort output lines into dependencies and disconnected lines.
+ # We also skip the attribute lines, and don't include the "{" and "}"
+ # lines.
+ disconnected_active = False
+ for line in lines[1:-1]: # Skip first and last line
+ if line.startswith('subgraph disconnected'):
+ disconnected_active = True
+ continue
+ if line.startswith('}') and disconnected_active:
+ disconnected_active = False
+ continue
+
+ if disconnected_active:
+ # Skip the 'label = "Disconnected"', etc. attribute lines.
+ if ' = ' not in line:
+ disconnected_lines.append(line)
+ else:
+ dependencies_lines.append(line)
+
+ dependencies_matches = []
+ for line in dependencies_lines:
+ if line[-1] == '\n':
+ line = line[:-1]
+ match = self.EDGE.match(line.strip())
+ self.assertIsNot(match, None)
+ dependencies_matches.append(match.groups())
+
+ disconnected_matches = []
+ for line in disconnected_lines:
+ if line[-1] == '\n':
+ line = line[:-1]
+ line = line.strip('"')
+ disconnected_matches.append(line)
+
+ self.checkLists(dependencies_matches, dependencies_expected)
+ self.checkLists(disconnected_matches, disconnected_expected)
+
+ def test_graph_bad_version_to_dot(self):
+ expected = (
+ ('towel-stuff', 'bacon', 'bacon (<=0.2)'),
+ ('grammar', 'bacon', 'truffles (>=1.2)'),
+ ('choxie', 'towel-stuff', 'towel-stuff (0.1)'),
+ ('banana', 'strawberry', 'strawberry (>=0.5)'),
+ )
+
+ dists = []
+ for name in self.DISTROS_DIST + self.DISTROS_EGG + self.BAD_EGGS:
+ dist = packaging.database.get_distribution(name, use_egg_info=True)
+ self.assertNotEqual(dist, None)
+ dists.append(dist)
+
+ graph = depgraph.generate_graph(dists)
+ buf = io.StringIO()
+ depgraph.graph_to_dot(graph, buf)
+ buf.seek(0)
+ matches = []
+ lines = buf.readlines()
+ for line in lines[1:-1]: # skip the first and the last lines
+ if line[-1] == '\n':
+ line = line[:-1]
+ match = self.EDGE.match(line.strip())
+ self.assertIsNot(match, None)
+ matches.append(match.groups())
+
+ self.checkLists(matches, expected)
+
+ def test_repr(self):
+ dists = []
+ for name in self.DISTROS_DIST + self.DISTROS_EGG + self.BAD_EGGS:
+ dist = packaging.database.get_distribution(name, use_egg_info=True)
+ self.assertNotEqual(dist, None)
+ dists.append(dist)
+
+ graph = depgraph.generate_graph(dists)
+ self.assertTrue(repr(graph))
+
+ def test_main(self):
+ tempout = io.StringIO()
+ old = sys.stdout
+ sys.stdout = tempout
+ oldargv = sys.argv[:]
+ sys.argv[:] = ['script.py']
+ try:
+ try:
+ depgraph.main()
+ except SystemExit:
+ pass
+ finally:
+ sys.stdout = old
+ sys.argv[:] = oldargv
+
+ # checks what main did XXX could do more here
+ tempout.seek(0)
+ res = tempout.read()
+ self.assertIn('towel', res)
+
+
+def test_suite():
+ return unittest.makeSuite(DepGraphTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_dist.py b/Lib/packaging/tests/test_dist.py
new file mode 100644
index 0000000..77dab71
--- /dev/null
+++ b/Lib/packaging/tests/test_dist.py
@@ -0,0 +1,445 @@
+"""Tests for packaging.dist."""
+import os
+import io
+import sys
+import logging
+import textwrap
+import packaging.dist
+
+from packaging.dist import Distribution
+from packaging.command import set_command
+from packaging.command.cmd import Command
+from packaging.errors import PackagingModuleError, PackagingOptionError
+from packaging.tests import TESTFN, captured_stdout
+from packaging.tests import support, unittest
+from packaging.tests.support import create_distribution
+
+
+class test_dist(Command):
+ """Sample packaging extension command."""
+
+ user_options = [
+ ("sample-option=", "S", "help text"),
+ ]
+
+ def initialize_options(self):
+ self.sample_option = None
+
+ def finalize_options(self):
+ pass
+
+
+class DistributionTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ support.EnvironRestorer,
+ unittest.TestCase):
+
+ restore_environ = ['HOME']
+
+ def setUp(self):
+ super(DistributionTestCase, self).setUp()
+ self.argv = sys.argv, sys.argv[:]
+ del sys.argv[1:]
+
+ def tearDown(self):
+ sys.argv = self.argv[0]
+ sys.argv[:] = self.argv[1]
+ super(DistributionTestCase, self).tearDown()
+
+ def test_debug_mode(self):
+ self.addCleanup(os.unlink, TESTFN)
+ with open(TESTFN, "w") as f:
+ f.write("[global]\n")
+ f.write("command_packages = foo.bar, splat")
+
+ files = [TESTFN]
+ sys.argv.append("build")
+ __, stdout = captured_stdout(create_distribution, files)
+ self.assertEqual(stdout, '')
+ packaging.dist.DEBUG = True
+ try:
+ __, stdout = captured_stdout(create_distribution, files)
+ self.assertEqual(stdout, '')
+ finally:
+ packaging.dist.DEBUG = False
+
+ def test_write_pkg_file(self):
+ # Check Metadata handling of Unicode fields
+ tmp_dir = self.mkdtemp()
+ my_file = os.path.join(tmp_dir, 'f')
+ cls = Distribution
+
+ dist = cls(attrs={'author': 'Mister Café',
+ 'name': 'my.package',
+ 'maintainer': 'Café Junior',
+ 'summary': 'Café torréfié',
+ 'description': 'Héhéhé'})
+
+ # let's make sure the file can be written
+ # with Unicode fields. they are encoded with
+ # PKG_INFO_ENCODING
+ with open(my_file, 'w') as fp:
+ dist.metadata.write_file(fp)
+
+ # regular ascii is of course always usable
+ dist = cls(attrs={'author': 'Mister Cafe',
+ 'name': 'my.package',
+ 'maintainer': 'Cafe Junior',
+ 'summary': 'Cafe torrefie',
+ 'description': 'Hehehe'})
+
+ with open(my_file, 'w') as fp:
+ dist.metadata.write_file(fp)
+
+ def test_bad_attr(self):
+ Distribution(attrs={'author': 'xxx',
+ 'name': 'xxx',
+ 'version': '1.2',
+ 'url': 'xxxx',
+ 'badoptname': 'xxx'})
+ logs = self.get_logs(logging.WARNING)
+ self.assertEqual(1, len(logs))
+ self.assertIn('unknown argument', logs[0])
+
+ def test_bad_version(self):
+ Distribution(attrs={'author': 'xxx',
+ 'name': 'xxx',
+ 'version': 'xxx',
+ 'url': 'xxxx'})
+ logs = self.get_logs(logging.WARNING)
+ self.assertEqual(1, len(logs))
+ self.assertIn('not a valid version', logs[0])
+
+ def test_empty_options(self):
+ # an empty options dictionary should not stay in the
+ # list of attributes
+ Distribution(attrs={'author': 'xxx',
+ 'name': 'xxx',
+ 'version': '1.2',
+ 'url': 'xxxx',
+ 'options': {}})
+
+ self.assertEqual([], self.get_logs(logging.WARNING))
+
+ def test_non_empty_options(self):
+ # TODO: how to actually use options is not documented except
+ # for a few cryptic comments in dist.py. If this is to stay
+ # in the public API, it deserves some better documentation.
+
+ # Here is an example of how it's used out there:
+ # http://svn.pythonmac.org/py2app/py2app/trunk/doc/
+ # index.html#specifying-customizations
+ dist = Distribution(attrs={'author': 'xxx',
+ 'name': 'xxx',
+ 'version': 'xxx',
+ 'url': 'xxxx',
+ 'options': {'sdist': {'owner': 'root'}}})
+
+ self.assertIn('owner', dist.get_option_dict('sdist'))
+
+ def test_finalize_options(self):
+
+ attrs = {'keywords': 'one,two',
+ 'platform': 'one,two'}
+
+ dist = Distribution(attrs=attrs)
+ dist.finalize_options()
+
+ # finalize_option splits platforms and keywords
+ self.assertEqual(dist.metadata['platform'], ['one', 'two'])
+ self.assertEqual(dist.metadata['keywords'], ['one', 'two'])
+
+ def test_find_config_files_disable(self):
+ # Bug #1180: Allow users to disable their own config file.
+ temp_home = self.mkdtemp()
+ if os.name == 'posix':
+ user_filename = os.path.join(temp_home, ".pydistutils.cfg")
+ else:
+ user_filename = os.path.join(temp_home, "pydistutils.cfg")
+
+ with open(user_filename, 'w') as f:
+ f.write('[distutils2]\n')
+
+ def _expander(path):
+ return temp_home
+
+ old_expander = os.path.expanduser
+ os.path.expanduser = _expander
+ try:
+ d = packaging.dist.Distribution()
+ all_files = d.find_config_files()
+
+ d = packaging.dist.Distribution(attrs={'script_args':
+ ['--no-user-cfg']})
+ files = d.find_config_files()
+ finally:
+ os.path.expanduser = old_expander
+
+ # make sure --no-user-cfg disables the user cfg file
+ self.assertEqual((len(all_files) - 1), len(files))
+
+ def test_special_hooks_parsing(self):
+ temp_home = self.mkdtemp()
+ config_files = [os.path.join(temp_home, "config1.cfg"),
+ os.path.join(temp_home, "config2.cfg")]
+
+ # Store two aliased hooks in config files
+ self.write_file((temp_home, "config1.cfg"),
+ '[test_dist]\npre-hook.a = type')
+ self.write_file((temp_home, "config2.cfg"),
+ '[test_dist]\npre-hook.b = type')
+
+ set_command('packaging.tests.test_dist.test_dist')
+ dist = create_distribution(config_files)
+ cmd = dist.get_command_obj("test_dist")
+ self.assertEqual(cmd.pre_hook, {"a": 'type', "b": 'type'})
+
+ def test_hooks_get_run(self):
+ temp_home = self.mkdtemp()
+ config_file = os.path.join(temp_home, "config1.cfg")
+ hooks_module = os.path.join(temp_home, "testhooks.py")
+
+ self.write_file(config_file, textwrap.dedent('''
+ [test_dist]
+ pre-hook.test = testhooks.log_pre_call
+ post-hook.test = testhooks.log_post_call'''))
+
+ self.write_file(hooks_module, textwrap.dedent('''
+ record = []
+
+ def log_pre_call(cmd):
+ record.append('pre-%s' % cmd.get_command_name())
+
+ def log_post_call(cmd):
+ record.append('post-%s' % cmd.get_command_name())
+ '''))
+
+ set_command('packaging.tests.test_dist.test_dist')
+ d = create_distribution([config_file])
+ cmd = d.get_command_obj("test_dist")
+
+ # prepare the call recorders
+ sys.path.append(temp_home)
+ self.addCleanup(sys.path.remove, temp_home)
+ from testhooks import record
+
+ cmd.run = lambda: record.append('run')
+ cmd.finalize_options = lambda: record.append('finalize')
+
+ d.run_command('test_dist')
+
+ self.assertEqual(record, ['finalize',
+ 'pre-test_dist',
+ 'run',
+ 'post-test_dist'])
+
+ def test_hooks_importable(self):
+ temp_home = self.mkdtemp()
+ config_file = os.path.join(temp_home, "config1.cfg")
+
+ self.write_file(config_file, textwrap.dedent('''
+ [test_dist]
+ pre-hook.test = nonexistent.dotted.name'''))
+
+ set_command('packaging.tests.test_dist.test_dist')
+ d = create_distribution([config_file])
+ cmd = d.get_command_obj("test_dist")
+ cmd.ensure_finalized()
+
+ self.assertRaises(PackagingModuleError, d.run_command, 'test_dist')
+
+ def test_hooks_callable(self):
+ temp_home = self.mkdtemp()
+ config_file = os.path.join(temp_home, "config1.cfg")
+
+ self.write_file(config_file, textwrap.dedent('''
+ [test_dist]
+ pre-hook.test = packaging.tests.test_dist.__doc__'''))
+
+ set_command('packaging.tests.test_dist.test_dist')
+ d = create_distribution([config_file])
+ cmd = d.get_command_obj("test_dist")
+ cmd.ensure_finalized()
+
+ self.assertRaises(PackagingOptionError, d.run_command, 'test_dist')
+
+
+class MetadataTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def setUp(self):
+ super(MetadataTestCase, self).setUp()
+ self.argv = sys.argv, sys.argv[:]
+
+ def tearDown(self):
+ sys.argv = self.argv[0]
+ sys.argv[:] = self.argv[1]
+ super(MetadataTestCase, self).tearDown()
+
+ def test_simple_metadata(self):
+ attrs = {"name": "package",
+ "version": "1.0"}
+ dist = Distribution(attrs)
+ meta = self.format_metadata(dist)
+ self.assertIn("Metadata-Version: 1.0", meta)
+ self.assertNotIn("provides:", meta.lower())
+ self.assertNotIn("requires:", meta.lower())
+ self.assertNotIn("obsoletes:", meta.lower())
+
+ def test_provides_dist(self):
+ attrs = {"name": "package",
+ "version": "1.0",
+ "provides_dist": ["package", "package.sub"]}
+ dist = Distribution(attrs)
+ self.assertEqual(dist.metadata['Provides-Dist'],
+ ["package", "package.sub"])
+ meta = self.format_metadata(dist)
+ self.assertIn("Metadata-Version: 1.2", meta)
+ self.assertNotIn("requires:", meta.lower())
+ self.assertNotIn("obsoletes:", meta.lower())
+
+ def _test_provides_illegal(self):
+ # XXX to do: check the versions
+ self.assertRaises(ValueError, Distribution,
+ {"name": "package",
+ "version": "1.0",
+ "provides_dist": ["my.pkg (splat)"]})
+
+ def test_requires_dist(self):
+ attrs = {"name": "package",
+ "version": "1.0",
+ "requires_dist": ["other", "another (==1.0)"]}
+ dist = Distribution(attrs)
+ self.assertEqual(dist.metadata['Requires-Dist'],
+ ["other", "another (==1.0)"])
+ meta = self.format_metadata(dist)
+ self.assertIn("Metadata-Version: 1.2", meta)
+ self.assertNotIn("provides:", meta.lower())
+ self.assertIn("Requires-Dist: other", meta)
+ self.assertIn("Requires-Dist: another (==1.0)", meta)
+ self.assertNotIn("obsoletes:", meta.lower())
+
+ def _test_requires_illegal(self):
+ # XXX
+ self.assertRaises(ValueError, Distribution,
+ {"name": "package",
+ "version": "1.0",
+ "requires": ["my.pkg (splat)"]})
+
+ def test_obsoletes_dist(self):
+ attrs = {"name": "package",
+ "version": "1.0",
+ "obsoletes_dist": ["other", "another (<1.0)"]}
+ dist = Distribution(attrs)
+ self.assertEqual(dist.metadata['Obsoletes-Dist'],
+ ["other", "another (<1.0)"])
+ meta = self.format_metadata(dist)
+ self.assertIn("Metadata-Version: 1.2", meta)
+ self.assertNotIn("provides:", meta.lower())
+ self.assertNotIn("requires:", meta.lower())
+ self.assertIn("Obsoletes-Dist: other", meta)
+ self.assertIn("Obsoletes-Dist: another (<1.0)", meta)
+
+ def _test_obsoletes_illegal(self):
+ # XXX
+ self.assertRaises(ValueError, Distribution,
+ {"name": "package",
+ "version": "1.0",
+ "obsoletes": ["my.pkg (splat)"]})
+
+ def format_metadata(self, dist):
+ sio = io.StringIO()
+ dist.metadata.write_file(sio)
+ return sio.getvalue()
+
+ def test_custom_pydistutils(self):
+ # fixes #2166
+ # make sure pydistutils.cfg is found
+ if os.name == 'posix':
+ user_filename = ".pydistutils.cfg"
+ else:
+ user_filename = "pydistutils.cfg"
+
+ temp_dir = self.mkdtemp()
+ user_filename = os.path.join(temp_dir, user_filename)
+ with open(user_filename, 'w') as f:
+ f.write('.')
+
+ dist = Distribution()
+
+ # linux-style
+ if sys.platform in ('linux', 'darwin'):
+ os.environ['HOME'] = temp_dir
+ files = dist.find_config_files()
+ self.assertIn(user_filename, files)
+
+ # win32-style
+ if sys.platform == 'win32':
+ # home drive should be found
+ os.environ['HOME'] = temp_dir
+ files = dist.find_config_files()
+ self.assertIn(user_filename, files)
+
+ def test_show_help(self):
+ # smoke test, just makes sure some help is displayed
+ dist = Distribution()
+ sys.argv = []
+ dist.help = True
+ dist.script_name = 'setup.py'
+ __, stdout = captured_stdout(dist.parse_command_line)
+ output = [line for line in stdout.split('\n')
+ if line.strip() != '']
+ self.assertGreater(len(output), 0)
+
+ def test_description(self):
+ desc = textwrap.dedent("""\
+ example::
+ We start here
+ and continue here
+ and end here.""")
+ attrs = {"name": "package",
+ "version": "1.0",
+ "description": desc}
+
+ dist = packaging.dist.Distribution(attrs)
+ meta = self.format_metadata(dist)
+ meta = meta.replace('\n' + 7 * ' ' + '|', '\n')
+ self.assertIn(desc, meta)
+
+ def test_read_metadata(self):
+ attrs = {"name": "package",
+ "version": "1.0",
+ "description": "desc",
+ "summary": "xxx",
+ "download_url": "http://example.com",
+ "keywords": ['one', 'two'],
+ "requires_dist": ['foo']}
+
+ dist = Distribution(attrs)
+ metadata = dist.metadata
+
+ # write it then reloads it
+ PKG_INFO = io.StringIO()
+ metadata.write_file(PKG_INFO)
+ PKG_INFO.seek(0)
+
+ metadata.read_file(PKG_INFO)
+ self.assertEqual(metadata['name'], "package")
+ self.assertEqual(metadata['version'], "1.0")
+ self.assertEqual(metadata['summary'], "xxx")
+ self.assertEqual(metadata['download_url'], 'http://example.com')
+ self.assertEqual(metadata['keywords'], ['one', 'two'])
+ self.assertEqual(metadata['platform'], [])
+ self.assertEqual(metadata['obsoletes'], [])
+ self.assertEqual(metadata['requires-dist'], ['foo'])
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(DistributionTestCase))
+ suite.addTest(unittest.makeSuite(MetadataTestCase))
+ return suite
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_extension.py b/Lib/packaging/tests/test_extension.py
new file mode 100644
index 0000000..41182e5
--- /dev/null
+++ b/Lib/packaging/tests/test_extension.py
@@ -0,0 +1,15 @@
+"""Tests for packaging.extension."""
+import os
+
+from packaging.compiler.extension import Extension
+from packaging.tests import unittest
+
+class ExtensionTestCase(unittest.TestCase):
+
+ pass
+
+def test_suite():
+ return unittest.makeSuite(ExtensionTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_install.py b/Lib/packaging/tests/test_install.py
new file mode 100644
index 0000000..2c51d19
--- /dev/null
+++ b/Lib/packaging/tests/test_install.py
@@ -0,0 +1,353 @@
+"""Tests for the packaging.install module."""
+
+import os
+from tempfile import mkstemp
+from packaging import install
+from packaging.pypi.xmlrpc import Client
+from packaging.metadata import Metadata
+
+from packaging.tests.support import LoggingCatcher, TempdirManager, unittest
+from packaging.tests.pypi_server import use_xmlrpc_server
+
+
+class InstalledDist:
+ """Distribution object, represent distributions currently installed on the
+ system"""
+ def __init__(self, name, version, deps):
+ self.metadata = Metadata()
+ self.name = name
+ self.version = version
+ self.metadata['Name'] = name
+ self.metadata['Version'] = version
+ self.metadata['Requires-Dist'] = deps
+
+ def __repr__(self):
+ return '<InstalledDist %s>' % self.metadata['Name']
+
+
+class ToInstallDist:
+ """Distribution that will be installed"""
+
+ def __init__(self, files=False):
+ self._files = files
+ self.install_called = False
+ self.install_called_with = {}
+ self.uninstall_called = False
+ self._real_files = []
+ self.name = "fake"
+ self.version = "fake"
+ if files:
+ for f in range(0, 3):
+ self._real_files.append(mkstemp())
+
+ def _unlink_installed_files(self):
+ if self._files:
+ for f in self._real_files:
+ os.unlink(f[1])
+
+ def list_installed_files(self, **args):
+ if self._files:
+ return [f[1] for f in self._real_files]
+
+ def get_install(self, **args):
+ return self.list_installed_files()
+
+
+class MagicMock:
+ def __init__(self, return_value=None, raise_exception=False):
+ self.called = False
+ self._times_called = 0
+ self._called_with = []
+ self._return_value = return_value
+ self._raise = raise_exception
+
+ def __call__(self, *args, **kwargs):
+ self.called = True
+ self._times_called = self._times_called + 1
+ self._called_with.append((args, kwargs))
+ iterable = hasattr(self._raise, '__iter__')
+ if self._raise:
+ if ((not iterable and self._raise)
+ or self._raise[self._times_called - 1]):
+ raise Exception
+ return self._return_value
+
+ def called_with(self, *args, **kwargs):
+ return (args, kwargs) in self._called_with
+
+
+def get_installed_dists(dists):
+ """Return a list of fake installed dists.
+ The list is name, version, deps"""
+ objects = []
+ for name, version, deps in dists:
+ objects.append(InstalledDist(name, version, deps))
+ return objects
+
+
+class TestInstall(LoggingCatcher, TempdirManager, unittest.TestCase):
+ def _get_client(self, server, *args, **kwargs):
+ return Client(server.full_address, *args, **kwargs)
+
+ def _get_results(self, output):
+ """return a list of results"""
+ installed = [(o.name, str(o.version)) for o in output['install']]
+ remove = [(o.name, str(o.version)) for o in output['remove']]
+ conflict = [(o.name, str(o.version)) for o in output['conflict']]
+ return installed, remove, conflict
+
+ @use_xmlrpc_server()
+ def test_existing_deps(self, server):
+ # Test that the installer get the dependencies from the metadatas
+ # and ask the index for this dependencies.
+ # In this test case, we have choxie that is dependent from towel-stuff
+ # 0.1, which is in-turn dependent on bacon <= 0.2:
+ # choxie -> towel-stuff -> bacon.
+ # Each release metadata is not provided in metadata 1.2.
+ client = self._get_client(server)
+ archive_path = '%s/distribution.tar.gz' % server.full_address
+ server.xmlrpc.set_distributions([
+ {'name': 'choxie',
+ 'version': '2.0.0.9',
+ 'requires_dist': ['towel-stuff (0.1)'],
+ 'url': archive_path},
+ {'name': 'towel-stuff',
+ 'version': '0.1',
+ 'requires_dist': ['bacon (<= 0.2)'],
+ 'url': archive_path},
+ {'name': 'bacon',
+ 'version': '0.1',
+ 'requires_dist': [],
+ 'url': archive_path},
+ ])
+ installed = get_installed_dists([('bacon', '0.1', [])])
+ output = install.get_infos("choxie", index=client,
+ installed=installed)
+
+ # we don't have installed bacon as it's already installed system-wide
+ self.assertEqual(0, len(output['remove']))
+ self.assertEqual(2, len(output['install']))
+ readable_output = [(o.name, str(o.version))
+ for o in output['install']]
+ self.assertIn(('towel-stuff', '0.1'), readable_output)
+ self.assertIn(('choxie', '2.0.0.9'), readable_output)
+
+ @use_xmlrpc_server()
+ def test_upgrade_existing_deps(self, server):
+ client = self._get_client(server)
+ archive_path = '%s/distribution.tar.gz' % server.full_address
+ server.xmlrpc.set_distributions([
+ {'name': 'choxie',
+ 'version': '2.0.0.9',
+ 'requires_dist': ['towel-stuff (0.1)'],
+ 'url': archive_path},
+ {'name': 'towel-stuff',
+ 'version': '0.1',
+ 'requires_dist': ['bacon (>= 0.2)'],
+ 'url': archive_path},
+ {'name': 'bacon',
+ 'version': '0.2',
+ 'requires_dist': [],
+ 'url': archive_path},
+ ])
+
+ output = install.get_infos("choxie", index=client,
+ installed=get_installed_dists([('bacon', '0.1', [])]))
+ installed = [(o.name, str(o.version)) for o in output['install']]
+
+ # we need bacon 0.2, but 0.1 is installed.
+ # So we expect to remove 0.1 and to install 0.2 instead.
+ remove = [(o.name, str(o.version)) for o in output['remove']]
+ self.assertIn(('choxie', '2.0.0.9'), installed)
+ self.assertIn(('towel-stuff', '0.1'), installed)
+ self.assertIn(('bacon', '0.2'), installed)
+ self.assertIn(('bacon', '0.1'), remove)
+ self.assertEqual(0, len(output['conflict']))
+
+ @use_xmlrpc_server()
+ def test_conflicts(self, server):
+ # Tests that conflicts are detected
+ client = self._get_client(server)
+ archive_path = '%s/distribution.tar.gz' % server.full_address
+
+ # choxie depends on towel-stuff, which depends on bacon.
+ server.xmlrpc.set_distributions([
+ {'name': 'choxie',
+ 'version': '2.0.0.9',
+ 'requires_dist': ['towel-stuff (0.1)'],
+ 'url': archive_path},
+ {'name': 'towel-stuff',
+ 'version': '0.1',
+ 'requires_dist': ['bacon (>= 0.2)'],
+ 'url': archive_path},
+ {'name': 'bacon',
+ 'version': '0.2',
+ 'requires_dist': [],
+ 'url': archive_path},
+ ])
+
+ # name, version, deps.
+ already_installed = [('bacon', '0.1', []),
+ ('chicken', '1.1', ['bacon (0.1)'])]
+ output = install.get_infos(
+ 'choxie', index=client,
+ installed=get_installed_dists(already_installed))
+
+ # we need bacon 0.2, but 0.1 is installed.
+ # So we expect to remove 0.1 and to install 0.2 instead.
+ installed, remove, conflict = self._get_results(output)
+ self.assertIn(('choxie', '2.0.0.9'), installed)
+ self.assertIn(('towel-stuff', '0.1'), installed)
+ self.assertIn(('bacon', '0.2'), installed)
+ self.assertIn(('bacon', '0.1'), remove)
+ self.assertIn(('chicken', '1.1'), conflict)
+
+ @use_xmlrpc_server()
+ def test_installation_unexisting_project(self, server):
+ # Test that the isntalled raises an exception if the project does not
+ # exists.
+ client = self._get_client(server)
+ self.assertRaises(install.InstallationException,
+ install.get_infos,
+ 'unexisting project', index=client)
+
+ def test_move_files(self):
+ # test that the files are really moved, and that the new path is
+ # returned.
+ path = self.mkdtemp()
+ newpath = self.mkdtemp()
+ files = [os.path.join(path, str(x)) for x in range(1, 20)]
+ for f in files:
+ open(f, 'a+').close()
+ output = [o for o in install._move_files(files, newpath)]
+
+ # check that output return the list of old/new places
+ for f in files:
+ self.assertIn((f, '%s%s' % (newpath, f)), output)
+
+ # remove the files
+ for f in [o[1] for o in output]: # o[1] is the new place
+ os.remove(f)
+
+ def test_update_infos(self):
+ tests = [[
+ {'foo': ['foobar', 'foo', 'baz'], 'baz': ['foo', 'foo']},
+ {'foo': ['additional_content', 'yeah'], 'baz': ['test', 'foo']},
+ {'foo': ['foobar', 'foo', 'baz', 'additional_content', 'yeah'],
+ 'baz': ['foo', 'foo', 'test', 'foo']},
+ ]]
+
+ for dict1, dict2, expect in tests:
+ install._update_infos(dict1, dict2)
+ for key in expect:
+ self.assertEqual(expect[key], dict1[key])
+
+ def test_install_dists_rollback(self):
+ # if one of the distribution installation fails, call uninstall on all
+ # installed distributions.
+
+ old_install_dist = install._install_dist
+ old_uninstall = getattr(install, 'uninstall', None)
+
+ install._install_dist = MagicMock(return_value=[],
+ raise_exception=(False, True))
+ install.remove = MagicMock()
+ try:
+ d1 = ToInstallDist()
+ d2 = ToInstallDist()
+ path = self.mkdtemp()
+ self.assertRaises(Exception, install.install_dists, [d1, d2], path)
+ self.assertTrue(install._install_dist.called_with(d1, path))
+ self.assertTrue(install.remove.called)
+ finally:
+ install._install_dist = old_install_dist
+ install.remove = old_uninstall
+
+ def test_install_dists_success(self):
+ old_install_dist = install._install_dist
+ install._install_dist = MagicMock(return_value=[])
+ try:
+ # test that the install method is called on each distributions
+ d1 = ToInstallDist()
+ d2 = ToInstallDist()
+
+ # should call install
+ path = self.mkdtemp()
+ install.install_dists([d1, d2], path)
+ for dist in (d1, d2):
+ self.assertTrue(install._install_dist.called_with(dist, path))
+ finally:
+ install._install_dist = old_install_dist
+
+ def test_install_from_infos_conflict(self):
+ # assert conflicts raise an exception
+ self.assertRaises(install.InstallationConflict,
+ install.install_from_infos,
+ conflicts=[ToInstallDist()])
+
+ def test_install_from_infos_remove_success(self):
+ old_install_dists = install.install_dists
+ install.install_dists = lambda x, y=None: None
+ try:
+ dists = []
+ for i in range(2):
+ dists.append(ToInstallDist(files=True))
+ install.install_from_infos(remove=dists)
+
+ # assert that the files have been removed
+ for dist in dists:
+ for f in dist.list_installed_files():
+ self.assertFalse(os.path.exists(f))
+ finally:
+ install.install_dists = old_install_dists
+
+ def test_install_from_infos_remove_rollback(self):
+ old_install_dist = install._install_dist
+ old_uninstall = getattr(install, 'uninstall', None)
+
+ install._install_dist = MagicMock(return_value=[],
+ raise_exception=(False, True))
+ install.uninstall = MagicMock()
+ try:
+ # assert that if an error occurs, the removed files are restored.
+ remove = []
+ for i in range(2):
+ remove.append(ToInstallDist(files=True))
+ to_install = [ToInstallDist(), ToInstallDist()]
+ temp_dir = self.mkdtemp()
+
+ self.assertRaises(Exception, install.install_from_infos,
+ install_path=temp_dir, install=to_install,
+ remove=remove)
+ # assert that the files are in the same place
+ # assert that the files have been removed
+ for dist in remove:
+ for f in dist.list_installed_files():
+ self.assertTrue(os.path.exists(f))
+ dist._unlink_installed_files()
+ finally:
+ install.install_dist = old_install_dist
+ install.uninstall = old_uninstall
+
+ def test_install_from_infos_install_succes(self):
+ old_install_dist = install._install_dist
+ install._install_dist = MagicMock([])
+ try:
+ # assert that the distribution can be installed
+ install_path = "my_install_path"
+ to_install = [ToInstallDist(), ToInstallDist()]
+
+ install.install_from_infos(install_path, install=to_install)
+ for dist in to_install:
+ install._install_dist.called_with(install_path)
+ finally:
+ install._install_dist = old_install_dist
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TestInstall))
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_manifest.py b/Lib/packaging/tests/test_manifest.py
new file mode 100644
index 0000000..21a42c3
--- /dev/null
+++ b/Lib/packaging/tests/test_manifest.py
@@ -0,0 +1,72 @@
+"""Tests for packaging.manifest."""
+import os
+import logging
+from io import StringIO
+from packaging.manifest import Manifest
+
+from packaging.tests import unittest, support
+
+_MANIFEST = """\
+recursive-include foo *.py # ok
+# nothing here
+
+#
+
+recursive-include bar \\
+ *.dat *.txt
+"""
+
+_MANIFEST2 = """\
+README
+file1
+"""
+
+
+class ManifestTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def test_manifest_reader(self):
+ tmpdir = self.mkdtemp()
+ MANIFEST = os.path.join(tmpdir, 'MANIFEST.in')
+ with open(MANIFEST, 'w') as f:
+ f.write(_MANIFEST)
+
+ manifest = Manifest()
+ manifest.read_template(MANIFEST)
+
+ warnings = self.get_logs(logging.WARNING)
+ # the manifest should have been read and 3 warnings issued
+ # (we didn't provide the files)
+ self.assertEqual(3, len(warnings))
+ for warning in warnings:
+ self.assertIn('no files found matching', warning)
+
+ # reset logs for the next assert
+ self.loghandler.flush()
+
+ # manifest also accepts file-like objects
+ with open(MANIFEST) as f:
+ manifest.read_template(f)
+
+ # the manifest should have been read and 3 warnings issued
+ # (we didn't provide the files)
+ self.assertEqual(3, len(warnings))
+
+ def test_default_actions(self):
+ tmpdir = self.mkdtemp()
+ self.addCleanup(os.chdir, os.getcwd())
+ os.chdir(tmpdir)
+ self.write_file('README', 'xxx')
+ self.write_file('file1', 'xxx')
+ content = StringIO(_MANIFEST2)
+ manifest = Manifest()
+ manifest.read_template(content)
+ self.assertEqual(['README', 'file1'], manifest.files)
+
+
+def test_suite():
+ return unittest.makeSuite(ManifestTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_markers.py b/Lib/packaging/tests/test_markers.py
new file mode 100644
index 0000000..dec0429
--- /dev/null
+++ b/Lib/packaging/tests/test_markers.py
@@ -0,0 +1,71 @@
+"""Tests for packaging.markers."""
+import os
+import sys
+import platform
+from packaging.markers import interpret
+
+from packaging.tests import unittest
+from packaging.tests.support import LoggingCatcher
+
+
+class MarkersTestCase(LoggingCatcher,
+ unittest.TestCase):
+
+ def test_interpret(self):
+ sys_platform = sys.platform
+ version = sys.version.split()[0]
+ os_name = os.name
+ platform_version = platform.version()
+ platform_machine = platform.machine()
+ platform_python_implementation = platform.python_implementation()
+
+ self.assertTrue(interpret("sys.platform == '%s'" % sys_platform))
+ self.assertTrue(interpret(
+ "sys.platform == '%s' or python_version == '2.4'" % sys_platform))
+ self.assertTrue(interpret(
+ "sys.platform == '%s' and python_full_version == '%s'" %
+ (sys_platform, version)))
+ self.assertTrue(interpret("'%s' == sys.platform" % sys_platform))
+ self.assertTrue(interpret('os.name == "%s"' % os_name))
+ self.assertTrue(interpret(
+ 'platform.version == "%s" and platform.machine == "%s"' %
+ (platform_version, platform_machine)))
+ self.assertTrue(interpret('platform.python_implementation == "%s"' %
+ platform_python_implementation))
+
+ # stuff that need to raise a syntax error
+ ops = ('os.name == os.name', 'os.name == 2', "'2' == '2'",
+ 'okpjonon', '', 'os.name ==', 'python_version == 2.4')
+ for op in ops:
+ self.assertRaises(SyntaxError, interpret, op)
+
+ # combined operations
+ OP = 'os.name == "%s"' % os_name
+ AND = ' and '
+ OR = ' or '
+ self.assertTrue(interpret(OP + AND + OP))
+ self.assertTrue(interpret(OP + AND + OP + AND + OP))
+ self.assertTrue(interpret(OP + OR + OP))
+ self.assertTrue(interpret(OP + OR + OP + OR + OP))
+
+ # other operators
+ self.assertTrue(interpret("os.name != 'buuuu'"))
+ self.assertTrue(interpret("python_version > '1.0'"))
+ self.assertTrue(interpret("python_version < '5.0'"))
+ self.assertTrue(interpret("python_version <= '5.0'"))
+ self.assertTrue(interpret("python_version >= '1.0'"))
+ self.assertTrue(interpret("'%s' in os.name" % os_name))
+ self.assertTrue(interpret("'buuuu' not in os.name"))
+ self.assertTrue(interpret(
+ "'buuuu' not in os.name and '%s' in os.name" % os_name))
+
+ # execution context
+ self.assertTrue(interpret('python_version == "0.1"',
+ {'python_version': '0.1'}))
+
+
+def test_suite():
+ return unittest.makeSuite(MarkersTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_metadata.py b/Lib/packaging/tests/test_metadata.py
new file mode 100644
index 0000000..b8dc5d8
--- /dev/null
+++ b/Lib/packaging/tests/test_metadata.py
@@ -0,0 +1,279 @@
+"""Tests for packaging.metadata."""
+import os
+import sys
+import logging
+from io import StringIO
+
+from packaging.errors import (MetadataConflictError, MetadataMissingError,
+ MetadataUnrecognizedVersionError)
+from packaging.metadata import Metadata, PKG_INFO_PREFERRED_VERSION
+
+from packaging.tests import unittest
+from packaging.tests.support import LoggingCatcher
+
+
+class MetadataTestCase(LoggingCatcher,
+ unittest.TestCase):
+
+ def test_instantiation(self):
+ PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
+ with open(PKG_INFO, 'r') as f:
+ contents = f.read()
+ fp = StringIO(contents)
+
+ m = Metadata()
+ self.assertRaises(MetadataUnrecognizedVersionError, m.items)
+
+ m = Metadata(PKG_INFO)
+ self.assertEqual(len(m.items()), 22)
+
+ m = Metadata(fileobj=fp)
+ self.assertEqual(len(m.items()), 22)
+
+ m = Metadata(mapping=dict(name='Test', version='1.0'))
+ self.assertEqual(len(m.items()), 11)
+
+ d = dict(m.items())
+ self.assertRaises(TypeError, Metadata,
+ PKG_INFO, fileobj=fp)
+ self.assertRaises(TypeError, Metadata,
+ PKG_INFO, mapping=d)
+ self.assertRaises(TypeError, Metadata,
+ fileobj=fp, mapping=d)
+ self.assertRaises(TypeError, Metadata,
+ PKG_INFO, mapping=m, fileobj=fp)
+
+ def test_metadata_read_write(self):
+ PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
+ metadata = Metadata(PKG_INFO)
+ out = StringIO()
+ metadata.write_file(out)
+ out.seek(0)
+ res = Metadata()
+ res.read_file(out)
+ for k in metadata:
+ self.assertEqual(metadata[k], res[k])
+
+ def test_metadata_markers(self):
+ # see if we can be platform-aware
+ PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
+ with open(PKG_INFO, 'r') as f:
+ content = f.read() % sys.platform
+ metadata = Metadata(platform_dependent=True)
+
+ metadata.read_file(StringIO(content))
+ self.assertEqual(metadata['Requires-Dist'], ['bar'])
+ metadata['Name'] = "baz; sys.platform == 'blah'"
+ # FIXME is None or 'UNKNOWN' correct here?
+ # where is that documented?
+ self.assertEqual(metadata['Name'], None)
+
+ # test with context
+ context = {'sys.platform': 'okook'}
+ metadata = Metadata(platform_dependent=True,
+ execution_context=context)
+ metadata.read_file(StringIO(content))
+ self.assertEqual(metadata['Requires-Dist'], ['foo'])
+
+ def test_description(self):
+ PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
+ with open(PKG_INFO, 'r') as f:
+ content = f.read() % sys.platform
+ metadata = Metadata()
+ metadata.read_file(StringIO(content))
+
+ # see if we can read the description now
+ DESC = os.path.join(os.path.dirname(__file__), 'LONG_DESC.txt')
+ with open(DESC) as f:
+ wanted = f.read()
+ self.assertEqual(wanted, metadata['Description'])
+
+ # save the file somewhere and make sure we can read it back
+ out = StringIO()
+ metadata.write_file(out)
+ out.seek(0)
+ metadata.read_file(out)
+ self.assertEqual(wanted, metadata['Description'])
+
+ def test_mapping_api(self):
+ PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO')
+ with open(PKG_INFO, 'r') as f:
+ content = f.read() % sys.platform
+ metadata = Metadata(fileobj=StringIO(content))
+ self.assertIn('Version', metadata.keys())
+ self.assertIn('0.5', metadata.values())
+ self.assertIn(('Version', '0.5'), metadata.items())
+
+ metadata.update({'version': '0.6'})
+ self.assertEqual(metadata['Version'], '0.6')
+ metadata.update([('version', '0.7')])
+ self.assertEqual(metadata['Version'], '0.7')
+
+ self.assertEqual(list(metadata), list(metadata.keys()))
+
+ def test_versions(self):
+ metadata = Metadata()
+ metadata['Obsoletes'] = 'ok'
+ self.assertEqual(metadata['Metadata-Version'], '1.1')
+
+ del metadata['Obsoletes']
+ metadata['Obsoletes-Dist'] = 'ok'
+ self.assertEqual(metadata['Metadata-Version'], '1.2')
+
+ self.assertRaises(MetadataConflictError, metadata.set,
+ 'Obsoletes', 'ok')
+
+ del metadata['Obsoletes']
+ del metadata['Obsoletes-Dist']
+ metadata['Version'] = '1'
+ self.assertEqual(metadata['Metadata-Version'], '1.0')
+
+ PKG_INFO = os.path.join(os.path.dirname(__file__),
+ 'SETUPTOOLS-PKG-INFO')
+ with open(PKG_INFO, 'r') as f:
+ content = f.read()
+ metadata.read_file(StringIO(content))
+ self.assertEqual(metadata['Metadata-Version'], '1.0')
+
+ PKG_INFO = os.path.join(os.path.dirname(__file__),
+ 'SETUPTOOLS-PKG-INFO2')
+ with open(PKG_INFO, 'r') as f:
+ content = f.read()
+ metadata.read_file(StringIO(content))
+ self.assertEqual(metadata['Metadata-Version'], '1.1')
+
+ # Update the _fields dict directly to prevent 'Metadata-Version'
+ # from being updated by the _set_best_version() method.
+ metadata._fields['Metadata-Version'] = '1.618'
+ self.assertRaises(MetadataUnrecognizedVersionError, metadata.keys)
+
+ def test_warnings(self):
+ metadata = Metadata()
+
+ # these should raise a warning
+ values = (('Requires-Dist', 'Funky (Groovie)'),
+ ('Requires-Python', '1-4'))
+
+ for name, value in values:
+ metadata.set(name, value)
+
+ # we should have a certain amount of warnings
+ self.assertEqual(len(self.get_logs()), 2)
+
+ def test_multiple_predicates(self):
+ metadata = Metadata()
+
+ # see for "3" instead of "3.0" ???
+ # its seems like the MINOR VERSION can be omitted
+ metadata['Requires-Python'] = '>=2.6, <3.0'
+ metadata['Requires-Dist'] = ['Foo (>=2.6, <3.0)']
+
+ self.assertEqual([], self.get_logs(logging.WARNING))
+
+ def test_project_url(self):
+ metadata = Metadata()
+ metadata['Project-URL'] = [('one', 'http://ok')]
+ self.assertEqual(metadata['Project-URL'],
+ [('one', 'http://ok')])
+ self.assertEqual(metadata['Metadata-Version'], '1.2')
+
+ def test_check_version(self):
+ metadata = Metadata()
+ metadata['Name'] = 'vimpdb'
+ metadata['Home-page'] = 'http://pypi.python.org'
+ metadata['Author'] = 'Monty Python'
+ metadata.docutils_support = False
+ missing, warnings = metadata.check()
+ self.assertEqual(missing, ['Version'])
+
+ def test_check_version_strict(self):
+ metadata = Metadata()
+ metadata['Name'] = 'vimpdb'
+ metadata['Home-page'] = 'http://pypi.python.org'
+ metadata['Author'] = 'Monty Python'
+ metadata.docutils_support = False
+ self.assertRaises(MetadataMissingError, metadata.check, strict=True)
+
+ def test_check_name(self):
+ metadata = Metadata()
+ metadata['Version'] = '1.0'
+ metadata['Home-page'] = 'http://pypi.python.org'
+ metadata['Author'] = 'Monty Python'
+ metadata.docutils_support = False
+ missing, warnings = metadata.check()
+ self.assertEqual(missing, ['Name'])
+
+ def test_check_name_strict(self):
+ metadata = Metadata()
+ metadata['Version'] = '1.0'
+ metadata['Home-page'] = 'http://pypi.python.org'
+ metadata['Author'] = 'Monty Python'
+ metadata.docutils_support = False
+ self.assertRaises(MetadataMissingError, metadata.check, strict=True)
+
+ def test_check_author(self):
+ metadata = Metadata()
+ metadata['Version'] = '1.0'
+ metadata['Name'] = 'vimpdb'
+ metadata['Home-page'] = 'http://pypi.python.org'
+ metadata.docutils_support = False
+ missing, warnings = metadata.check()
+ self.assertEqual(missing, ['Author'])
+
+ def test_check_homepage(self):
+ metadata = Metadata()
+ metadata['Version'] = '1.0'
+ metadata['Name'] = 'vimpdb'
+ metadata['Author'] = 'Monty Python'
+ metadata.docutils_support = False
+ missing, warnings = metadata.check()
+ self.assertEqual(missing, ['Home-page'])
+
+ def test_check_predicates(self):
+ metadata = Metadata()
+ metadata['Version'] = 'rr'
+ metadata['Name'] = 'vimpdb'
+ metadata['Home-page'] = 'http://pypi.python.org'
+ metadata['Author'] = 'Monty Python'
+ metadata['Requires-dist'] = ['Foo (a)']
+ metadata['Obsoletes-dist'] = ['Foo (a)']
+ metadata['Provides-dist'] = ['Foo (a)']
+ if metadata.docutils_support:
+ missing, warnings = metadata.check()
+ self.assertEqual(len(warnings), 4)
+ metadata.docutils_support = False
+ missing, warnings = metadata.check()
+ self.assertEqual(len(warnings), 4)
+
+ def test_best_choice(self):
+ metadata = Metadata()
+ metadata['Version'] = '1.0'
+ self.assertEqual(metadata['Metadata-Version'],
+ PKG_INFO_PREFERRED_VERSION)
+ metadata['Classifier'] = ['ok']
+ self.assertEqual(metadata['Metadata-Version'], '1.2')
+
+ def test_project_urls(self):
+ # project-url is a bit specific, make sure we write it
+ # properly in PKG-INFO
+ metadata = Metadata()
+ metadata['Version'] = '1.0'
+ metadata['Project-Url'] = [('one', 'http://ok')]
+ self.assertEqual(metadata['Project-Url'], [('one', 'http://ok')])
+ file_ = StringIO()
+ metadata.write_file(file_)
+ file_.seek(0)
+ res = file_.read().split('\n')
+ self.assertIn('Project-URL: one,http://ok', res)
+
+ file_.seek(0)
+ metadata = Metadata()
+ metadata.read_file(file_)
+ self.assertEqual(metadata['Project-Url'], [('one', 'http://ok')])
+
+
+def test_suite():
+ return unittest.makeSuite(MetadataTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_mixin2to3.py b/Lib/packaging/tests/test_mixin2to3.py
new file mode 100644
index 0000000..d7c83c2
--- /dev/null
+++ b/Lib/packaging/tests/test_mixin2to3.py
@@ -0,0 +1,75 @@
+"""Tests for packaging.command.build_py."""
+import sys
+
+from packaging.tests import unittest, support
+from packaging.compat import Mixin2to3
+
+
+class Mixin2to3TestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+ def test_convert_code_only(self):
+ # used to check if code gets converted properly.
+ code_content = "print 'test'\n"
+ code_handle = self.mktempfile()
+ code_name = code_handle.name
+
+ code_handle.write(code_content)
+ code_handle.flush()
+
+ mixin2to3 = Mixin2to3()
+ mixin2to3._run_2to3([code_name])
+ converted_code_content = "print('test')\n"
+ with open(code_name) as fp:
+ new_code_content = "".join(fp.readlines())
+
+ self.assertEqual(new_code_content, converted_code_content)
+
+ @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+ def test_doctests_only(self):
+ # used to check if doctests gets converted properly.
+ doctest_content = '"""\n>>> print test\ntest\n"""\nprint test\n\n'
+ doctest_handle = self.mktempfile()
+ doctest_name = doctest_handle.name
+
+ doctest_handle.write(doctest_content)
+ doctest_handle.flush()
+
+ mixin2to3 = Mixin2to3()
+ mixin2to3._run_2to3([doctest_name])
+
+ converted_doctest_content = ['"""', '>>> print(test)', 'test', '"""',
+ 'print(test)', '', '', '']
+ converted_doctest_content = '\n'.join(converted_doctest_content)
+ with open(doctest_name) as fp:
+ new_doctest_content = "".join(fp.readlines())
+
+ self.assertEqual(new_doctest_content, converted_doctest_content)
+
+ @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+ def test_additional_fixers(self):
+ # used to check if use_2to3_fixers works
+ code_content = "type(x) is T"
+ code_handle = self.mktempfile()
+ code_name = code_handle.name
+
+ code_handle.write(code_content)
+ code_handle.flush()
+
+ mixin2to3 = Mixin2to3()
+
+ mixin2to3._run_2to3(files=[code_name], doctests=[code_name],
+ fixers=['packaging.tests.fixer'])
+ converted_code_content = "isinstance(x, T)"
+ with open(code_name) as fp:
+ new_code_content = "".join(fp.readlines())
+ self.assertEqual(new_code_content, converted_code_content)
+
+
+def test_suite():
+ return unittest.makeSuite(Mixin2to3TestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_msvc9compiler.py b/Lib/packaging/tests/test_msvc9compiler.py
new file mode 100644
index 0000000..dc3ae65
--- /dev/null
+++ b/Lib/packaging/tests/test_msvc9compiler.py
@@ -0,0 +1,140 @@
+"""Tests for packaging.compiler.msvc9compiler."""
+import os
+import sys
+
+from packaging.errors import PackagingPlatformError
+
+from packaging.tests import unittest, support
+
+_MANIFEST = """\
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
+ manifestVersion="1.0">
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false">
+ </requestedExecutionLevel>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.VC90.CRT"
+ version="9.0.21022.8" processorArchitecture="x86"
+ publicKeyToken="XXXX">
+ </assemblyIdentity>
+ </dependentAssembly>
+ </dependency>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.VC90.MFC"
+ version="9.0.21022.8" processorArchitecture="x86"
+ publicKeyToken="XXXX"></assemblyIdentity>
+ </dependentAssembly>
+ </dependency>
+</assembly>
+"""
+
+_CLEANED_MANIFEST = """\
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
+ manifestVersion="1.0">
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false">
+ </requestedExecutionLevel>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <dependency>
+
+ </dependency>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity type="win32" name="Microsoft.VC90.MFC"
+ version="9.0.21022.8" processorArchitecture="x86"
+ publicKeyToken="XXXX"></assemblyIdentity>
+ </dependentAssembly>
+ </dependency>
+</assembly>"""
+
+
+class msvc9compilerTestCase(support.TempdirManager,
+ unittest.TestCase):
+
+ @unittest.skipUnless(sys.platform == "win32", "runs only on win32")
+ def test_no_compiler(self):
+ # make sure query_vcvarsall raises a PackagingPlatformError if
+ # the compiler is not found
+ from packaging.compiler.msvccompiler import get_build_version
+ if get_build_version() < 8.0:
+ raise unittest.SkipTest('only for MSVC8.0 or above')
+
+ from packaging.compiler import msvc9compiler
+ from packaging.compiler.msvc9compiler import query_vcvarsall
+
+ def _find_vcvarsall(version):
+ return None
+
+ old_find_vcvarsall = msvc9compiler.find_vcvarsall
+ msvc9compiler.find_vcvarsall = _find_vcvarsall
+ try:
+ self.assertRaises(PackagingPlatformError, query_vcvarsall,
+ 'wont find this version')
+ finally:
+ msvc9compiler.find_vcvarsall = old_find_vcvarsall
+
+ @unittest.skipUnless(sys.platform == "win32", "runs only on win32")
+ def test_reg_class(self):
+ from packaging.compiler.msvccompiler import get_build_version
+ if get_build_version() < 8.0:
+ raise unittest.SkipTest("requires MSVC 8.0 or later")
+
+ from packaging.compiler.msvc9compiler import Reg
+ self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx')
+
+ # looking for values that should exist on all
+ # windows registeries versions.
+ path = r'Control Panel\Desktop'
+ v = Reg.get_value(path, 'dragfullwindows')
+ self.assertIn(v, ('0', '1', '2'))
+
+ import winreg
+ HKCU = winreg.HKEY_CURRENT_USER
+ keys = Reg.read_keys(HKCU, 'xxxx')
+ self.assertEqual(keys, None)
+
+ keys = Reg.read_keys(HKCU, r'Control Panel')
+ self.assertIn('Desktop', keys)
+
+ @unittest.skipUnless(sys.platform == "win32", "runs only on win32")
+ def test_remove_visual_c_ref(self):
+ from packaging.compiler.msvccompiler import get_build_version
+ if get_build_version() < 8.0:
+ raise unittest.SkipTest("requires MSVC 8.0 or later")
+
+ from packaging.compiler.msvc9compiler import MSVCCompiler
+ tempdir = self.mkdtemp()
+ manifest = os.path.join(tempdir, 'manifest')
+ with open(manifest, 'w') as f:
+ f.write(_MANIFEST)
+
+ compiler = MSVCCompiler()
+ compiler._remove_visual_c_ref(manifest)
+
+ # see what we got
+ with open(manifest) as f:
+ # removing trailing spaces
+ content = '\n'.join(line.rstrip() for line in f.readlines())
+
+ # makes sure the manifest was properly cleaned
+ self.assertEqual(content, _CLEANED_MANIFEST)
+
+
+def test_suite():
+ return unittest.makeSuite(msvc9compilerTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_pypi_dist.py b/Lib/packaging/tests/test_pypi_dist.py
new file mode 100644
index 0000000..b438cb8
--- /dev/null
+++ b/Lib/packaging/tests/test_pypi_dist.py
@@ -0,0 +1,277 @@
+"""Tests for the packaging.pypi.dist module."""
+
+import os
+from packaging.version import VersionPredicate
+from packaging.pypi.dist import (ReleaseInfo, ReleasesList, DistInfo,
+ split_archive_name, get_infos_from_url)
+from packaging.pypi.errors import HashDoesNotMatch, UnsupportedHashName
+
+from packaging.tests import unittest
+from packaging.tests.support import TempdirManager
+from packaging.tests.pypi_server import use_pypi_server
+
+
+def Dist(*args, **kwargs):
+ # DistInfo takes a release as a first parameter, avoid this in tests.
+ return DistInfo(None, *args, **kwargs)
+
+
+class TestReleaseInfo(unittest.TestCase):
+
+ def test_instantiation(self):
+ # Test the DistInfo class provides us the good attributes when
+ # given on construction
+ release = ReleaseInfo("FooBar", "1.1")
+ self.assertEqual("FooBar", release.name)
+ self.assertEqual("1.1", "%s" % release.version)
+
+ def test_add_dist(self):
+ # empty distribution type should assume "sdist"
+ release = ReleaseInfo("FooBar", "1.1")
+ release.add_distribution(url="http://example.org/")
+ # should not fail
+ release['sdist']
+
+ def test_get_unknown_distribution(self):
+ # should raise a KeyError
+ pass
+
+ def test_get_infos_from_url(self):
+ # Test that the the URLs are parsed the right way
+ url_list = {
+ 'FooBar-1.1.0.tar.gz': {
+ 'name': 'foobar', # lowercase the name
+ 'version': '1.1.0',
+ },
+ 'Foo-Bar-1.1.0.zip': {
+ 'name': 'foo-bar', # keep the dash
+ 'version': '1.1.0',
+ },
+ 'foobar-1.1b2.tar.gz#md5=123123123123123': {
+ 'name': 'foobar',
+ 'version': '1.1b2',
+ 'url': 'http://example.org/foobar-1.1b2.tar.gz', # no hash
+ 'hashval': '123123123123123',
+ 'hashname': 'md5',
+ },
+ 'foobar-1.1-rc2.tar.gz': { # use suggested name
+ 'name': 'foobar',
+ 'version': '1.1c2',
+ 'url': 'http://example.org/foobar-1.1-rc2.tar.gz',
+ }
+ }
+
+ for url, attributes in url_list.items():
+ # for each url
+ infos = get_infos_from_url("http://example.org/" + url)
+ for attribute, expected in attributes.items():
+ got = infos.get(attribute)
+ if attribute == "version":
+ self.assertEqual("%s" % got, expected)
+ else:
+ self.assertEqual(got, expected)
+
+ def test_split_archive_name(self):
+ # Test we can split the archive names
+ names = {
+ 'foo-bar-baz-1.0-rc2': ('foo-bar-baz', '1.0c2'),
+ 'foo-bar-baz-1.0': ('foo-bar-baz', '1.0'),
+ 'foobarbaz-1.0': ('foobarbaz', '1.0'),
+ }
+ for name, results in names.items():
+ self.assertEqual(results, split_archive_name(name))
+
+
+class TestDistInfo(TempdirManager, unittest.TestCase):
+ srcpath = "/packages/source/f/foobar/foobar-0.1.tar.gz"
+
+ def test_get_url(self):
+ # Test that the url property works well
+
+ d = Dist(url="test_url")
+ self.assertDictEqual(d.url, {
+ "url": "test_url",
+ "is_external": True,
+ "hashname": None,
+ "hashval": None,
+ })
+
+ # add a new url
+ d.add_url(url="internal_url", is_external=False)
+ self.assertEqual(d._url, None)
+ self.assertDictEqual(d.url, {
+ "url": "internal_url",
+ "is_external": False,
+ "hashname": None,
+ "hashval": None,
+ })
+ self.assertEqual(2, len(d.urls))
+
+ def test_comparison(self):
+ # Test that we can compare DistInfoributionInfoList
+ foo1 = ReleaseInfo("foo", "1.0")
+ foo2 = ReleaseInfo("foo", "2.0")
+ bar = ReleaseInfo("bar", "2.0")
+ # assert we use the version to compare
+ self.assertTrue(foo1 < foo2)
+ self.assertFalse(foo1 > foo2)
+ self.assertFalse(foo1 == foo2)
+
+ # assert we can't compare dists with different names
+ self.assertRaises(TypeError, foo1.__eq__, bar)
+
+ @use_pypi_server("downloads_with_md5")
+ def test_download(self, server):
+ # Download is possible, and the md5 is checked if given
+
+ url = server.full_address + self.srcpath
+
+ # check that a md5 if given
+ dist = Dist(url=url, hashname="md5",
+ hashval="fe18804c5b722ff024cabdf514924fc4")
+ dist.download(self.mkdtemp())
+
+ # a wrong md5 fails
+ dist2 = Dist(url=url, hashname="md5", hashval="wrongmd5")
+
+ self.assertRaises(HashDoesNotMatch, dist2.download, self.mkdtemp())
+
+ # we can omit the md5 hash
+ dist3 = Dist(url=url)
+ dist3.download(self.mkdtemp())
+
+ # and specify a temporary location
+ # for an already downloaded dist
+ path1 = self.mkdtemp()
+ dist3.download(path=path1)
+ # and for a new one
+ path2_base = self.mkdtemp()
+ dist4 = Dist(url=url)
+ path2 = dist4.download(path=path2_base)
+ self.assertIn(path2_base, path2)
+
+ def test_hashname(self):
+ # Invalid hashnames raises an exception on assignation
+ Dist(hashname="md5", hashval="value")
+
+ self.assertRaises(UnsupportedHashName, Dist,
+ hashname="invalid_hashname",
+ hashval="value")
+
+ @use_pypi_server('downloads_with_md5')
+ def test_unpack(self, server):
+ url = server.full_address + self.srcpath
+ dist1 = Dist(url=url)
+
+ # unpack the distribution in a specfied folder
+ dist1_here = self.mkdtemp()
+ dist1_there = dist1.unpack(path=dist1_here)
+
+ # assert we unpack to the path provided
+ self.assertEqual(dist1_here, dist1_there)
+ dist1_result = os.listdir(dist1_there)
+ self.assertIn('paf', dist1_result)
+ os.remove(os.path.join(dist1_there, 'paf'))
+
+ # Test unpack works without a path argument
+ dist2 = Dist(url=url)
+ # doing an unpack
+ dist2_there = dist2.unpack()
+ dist2_result = os.listdir(dist2_there)
+ self.assertIn('paf', dist2_result)
+ os.remove(os.path.join(dist2_there, 'paf'))
+
+
+class TestReleasesList(unittest.TestCase):
+
+ def test_filter(self):
+ # Test we filter the distributions the right way, using version
+ # predicate match method
+ releases = ReleasesList('FooBar', (
+ ReleaseInfo("FooBar", "1.1"),
+ ReleaseInfo("FooBar", "1.1.1"),
+ ReleaseInfo("FooBar", "1.2"),
+ ReleaseInfo("FooBar", "1.2.1"),
+ ))
+ filtered = releases.filter(VersionPredicate("FooBar (<1.2)"))
+ self.assertNotIn(releases[2], filtered)
+ self.assertNotIn(releases[3], filtered)
+ self.assertIn(releases[0], filtered)
+ self.assertIn(releases[1], filtered)
+
+ def test_append(self):
+ # When adding a new item to the list, the behavior is to test if
+ # a release with the same name and version number already exists,
+ # and if so, to add a new distribution for it. If the distribution type
+ # is already defined too, add url informations to the existing DistInfo
+ # object.
+
+ releases = ReleasesList("FooBar", [
+ ReleaseInfo("FooBar", "1.1", url="external_url",
+ dist_type="sdist"),
+ ])
+ self.assertEqual(1, len(releases))
+ releases.add_release(release=ReleaseInfo("FooBar", "1.1",
+ url="internal_url",
+ is_external=False,
+ dist_type="sdist"))
+ self.assertEqual(1, len(releases))
+ self.assertEqual(2, len(releases[0]['sdist'].urls))
+
+ releases.add_release(release=ReleaseInfo("FooBar", "1.1.1",
+ dist_type="sdist"))
+ self.assertEqual(2, len(releases))
+
+ # when adding a distribution whith a different type, a new distribution
+ # has to be added.
+ releases.add_release(release=ReleaseInfo("FooBar", "1.1.1",
+ dist_type="bdist"))
+ self.assertEqual(2, len(releases))
+ self.assertEqual(2, len(releases[1].dists))
+
+ def test_prefer_final(self):
+ # Can order the distributions using prefer_final
+
+ fb10 = ReleaseInfo("FooBar", "1.0") # final distribution
+ fb11a = ReleaseInfo("FooBar", "1.1a1") # alpha
+ fb12a = ReleaseInfo("FooBar", "1.2a1") # alpha
+ fb12b = ReleaseInfo("FooBar", "1.2b1") # beta
+ dists = ReleasesList("FooBar", [fb10, fb11a, fb12a, fb12b])
+
+ dists.sort_releases(prefer_final=True)
+ self.assertEqual(fb10, dists[0])
+
+ dists.sort_releases(prefer_final=False)
+ self.assertEqual(fb12b, dists[0])
+
+# def test_prefer_source(self):
+# # Ordering support prefer_source
+# fb_source = Dist("FooBar", "1.0", type="source")
+# fb_binary = Dist("FooBar", "1.0", type="binary")
+# fb2_binary = Dist("FooBar", "2.0", type="binary")
+# dists = ReleasesList([fb_binary, fb_source])
+#
+# dists.sort_distributions(prefer_source=True)
+# self.assertEqual(fb_source, dists[0])
+#
+# dists.sort_distributions(prefer_source=False)
+# self.assertEqual(fb_binary, dists[0])
+#
+# dists.append(fb2_binary)
+# dists.sort_distributions(prefer_source=True)
+# self.assertEqual(fb2_binary, dists[0])
+
+ def test_get_last(self):
+ dists = ReleasesList('Foo')
+ self.assertEqual(dists.get_last('Foo 1.0'), None)
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TestDistInfo))
+ suite.addTest(unittest.makeSuite(TestReleaseInfo))
+ suite.addTest(unittest.makeSuite(TestReleasesList))
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_pypi_server.py b/Lib/packaging/tests/test_pypi_server.py
new file mode 100644
index 0000000..15c2e6c
--- /dev/null
+++ b/Lib/packaging/tests/test_pypi_server.py
@@ -0,0 +1,81 @@
+"""Tests for packaging.command.bdist."""
+import sys
+
+import urllib.request
+import urllib.parse
+import urllib.error
+
+from packaging.tests.pypi_server import PyPIServer, PYPI_DEFAULT_STATIC_PATH
+from packaging.tests import unittest
+
+
+class PyPIServerTest(unittest.TestCase):
+
+ def test_records_requests(self):
+ # We expect that PyPIServer can log our requests
+ server = PyPIServer()
+ server.default_response_status = 200
+
+ try:
+ server.start()
+ self.assertEqual(len(server.requests), 0)
+
+ data = b'Rock Around The Bunker'
+
+ headers = {"X-test-header": "Mister Iceberg"}
+
+ request = urllib.request.Request(server.full_address, data, headers)
+ urllib.request.urlopen(request)
+ self.assertEqual(len(server.requests), 1)
+ handler, request_data = server.requests[-1]
+ self.assertIn(data, request_data)
+ self.assertIn("x-test-header", handler.headers)
+ self.assertEqual(handler.headers["x-test-header"], "Mister Iceberg")
+
+ finally:
+ server.stop()
+
+
+ def test_serve_static_content(self):
+ # PYPI Mocked server can serve static content from disk.
+
+ def uses_local_files_for(server, url_path):
+ """Test that files are served statically (eg. the output from the
+ server is the same than the one made by a simple file read.
+ """
+ url = server.full_address + url_path
+ request = urllib.request.Request(url)
+ response = urllib.request.urlopen(request)
+ file = open(PYPI_DEFAULT_STATIC_PATH + "/test_pypi_server" +
+ url_path)
+ answer = response.read().decode() == file.read()
+ file.close()
+ return answer
+
+ server = PyPIServer(static_uri_paths=["simple", "external"],
+ static_filesystem_paths=["test_pypi_server"])
+ server.start()
+ try:
+ # the file does not exists on the disc, so it might not be served
+ url = server.full_address + "/simple/unexisting_page"
+ request = urllib.request.Request(url)
+ try:
+ urllib.request.urlopen(request)
+ except urllib.error.HTTPError as e:
+ self.assertEqual(e.code, 404)
+
+ # now try serving a content that do exists
+ self.assertTrue(uses_local_files_for(server, "/simple/index.html"))
+
+ # and another one in another root path
+ self.assertTrue(uses_local_files_for(server, "/external/index.html"))
+
+ finally:
+ server.stop()
+
+
+def test_suite():
+ return unittest.makeSuite(PyPIServerTest)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_pypi_simple.py b/Lib/packaging/tests/test_pypi_simple.py
new file mode 100644
index 0000000..c43a839
--- /dev/null
+++ b/Lib/packaging/tests/test_pypi_simple.py
@@ -0,0 +1,326 @@
+"""Tests for the packaging.pypi.simple module."""
+
+import os
+import sys
+import http.client
+import urllib.error
+import urllib.parse
+import urllib.request
+
+from packaging.pypi.simple import Crawler
+
+from packaging.tests import unittest
+from packaging.tests.support import TempdirManager, LoggingCatcher
+from packaging.tests.pypi_server import (use_pypi_server, PyPIServer,
+ PYPI_DEFAULT_STATIC_PATH)
+
+
+class SimpleCrawlerTestCase(TempdirManager,
+ LoggingCatcher,
+ unittest.TestCase):
+
+ def _get_simple_crawler(self, server, base_url="/simple/", hosts=None,
+ *args, **kwargs):
+ """Build and return a SimpleIndex with the test server urls"""
+ if hosts is None:
+ hosts = (server.full_address.replace("http://", ""),)
+ kwargs['hosts'] = hosts
+ return Crawler(server.full_address + base_url, *args,
+ **kwargs)
+
+ @use_pypi_server()
+ def test_bad_urls(self, server):
+ crawler = Crawler()
+ url = 'http://127.0.0.1:0/nonesuch/test_simple'
+ try:
+ v = crawler._open_url(url)
+ except Exception as v:
+ self.assertIn(url, str(v))
+ else:
+ v.close()
+ self.assertIsInstance(v, urllib.error.HTTPError)
+
+ # issue 16
+ # easy_install inquant.contentmirror.plone breaks because of a typo
+ # in its home URL
+ crawler = Crawler(hosts=('example.org',))
+ url = ('url:%20https://svn.plone.org/svn/collective/'
+ 'inquant.contentmirror.plone/trunk')
+ try:
+ v = crawler._open_url(url)
+ except Exception as v:
+ self.assertIn(url, str(v))
+ else:
+ v.close()
+ self.assertIsInstance(v, urllib.error.HTTPError)
+
+ def _urlopen(*args):
+ raise http.client.BadStatusLine('line')
+
+ old_urlopen = urllib.request.urlopen
+ urllib.request.urlopen = _urlopen
+ url = 'http://example.org'
+ try:
+ v = crawler._open_url(url)
+ except Exception as v:
+ self.assertIn('line', str(v))
+ else:
+ v.close()
+ # TODO use self.assertRaises
+ raise AssertionError('Should have raise here!')
+ finally:
+ urllib.request.urlopen = old_urlopen
+
+ # issue 20
+ url = 'http://http://svn.pythonpaste.org/Paste/wphp/trunk'
+ try:
+ crawler._open_url(url)
+ except Exception as v:
+ self.assertIn('nonnumeric port', str(v))
+
+ # issue #160
+ url = server.full_address
+ page = ('<a href="http://www.famfamfam.com]('
+ 'http://www.famfamfam.com/">')
+ crawler._process_url(url, page)
+
+ @use_pypi_server("test_found_links")
+ def test_found_links(self, server):
+ # Browse the index, asking for a specified release version
+ # The PyPI index contains links for version 1.0, 1.1, 2.0 and 2.0.1
+ crawler = self._get_simple_crawler(server)
+ last_release = crawler.get_release("foobar")
+
+ # we have scanned the index page
+ self.assertIn(server.full_address + "/simple/foobar/",
+ crawler._processed_urls)
+
+ # we have found 4 releases in this page
+ self.assertEqual(len(crawler._projects["foobar"]), 4)
+
+ # and returned the most recent one
+ self.assertEqual("%s" % last_release.version, '2.0.1')
+
+ def test_is_browsable(self):
+ crawler = Crawler(follow_externals=False)
+ self.assertTrue(crawler._is_browsable(crawler.index_url + "test"))
+
+ # Now, when following externals, we can have a list of hosts to trust.
+ # and don't follow other external links than the one described here.
+ crawler = Crawler(hosts=["pypi.python.org", "example.org"],
+ follow_externals=True)
+ good_urls = (
+ "http://pypi.python.org/foo/bar",
+ "http://pypi.python.org/simple/foobar",
+ "http://example.org",
+ "http://example.org/",
+ "http://example.org/simple/",
+ )
+ bad_urls = (
+ "http://python.org",
+ "http://example.tld",
+ )
+
+ for url in good_urls:
+ self.assertTrue(crawler._is_browsable(url))
+
+ for url in bad_urls:
+ self.assertFalse(crawler._is_browsable(url))
+
+ # allow all hosts
+ crawler = Crawler(follow_externals=True, hosts=("*",))
+ self.assertTrue(crawler._is_browsable("http://an-external.link/path"))
+ self.assertTrue(crawler._is_browsable("pypi.example.org/a/path"))
+
+ # specify a list of hosts we want to allow
+ crawler = Crawler(follow_externals=True,
+ hosts=("*.example.org",))
+ self.assertFalse(crawler._is_browsable("http://an-external.link/path"))
+ self.assertTrue(
+ crawler._is_browsable("http://pypi.example.org/a/path"))
+
+ @use_pypi_server("with_externals")
+ def test_follow_externals(self, server):
+ # Include external pages
+ # Try to request the package index, wich contains links to "externals"
+ # resources. They have to be scanned too.
+ crawler = self._get_simple_crawler(server, follow_externals=True)
+ crawler.get_release("foobar")
+ self.assertIn(server.full_address + "/external/external.html",
+ crawler._processed_urls)
+
+ @use_pypi_server("with_real_externals")
+ def test_restrict_hosts(self, server):
+ # Only use a list of allowed hosts is possible
+ # Test that telling the simple pyPI client to not retrieve external
+ # works
+ crawler = self._get_simple_crawler(server, follow_externals=False)
+ crawler.get_release("foobar")
+ self.assertNotIn(server.full_address + "/external/external.html",
+ crawler._processed_urls)
+
+ @use_pypi_server(static_filesystem_paths=["with_externals"],
+ static_uri_paths=["simple", "external"])
+ def test_links_priority(self, server):
+ # Download links from the pypi simple index should be used before
+ # external download links.
+ # http://bitbucket.org/tarek/distribute/issue/163/md5-validation-error
+ #
+ # Usecase :
+ # - someone uploads a package on pypi, a md5 is generated
+ # - someone manually coindexes this link (with the md5 in the url) onto
+ # an external page accessible from the package page.
+ # - someone reuploads the package (with a different md5)
+ # - while easy_installing, an MD5 error occurs because the external
+ # link is used
+ # -> The index should use the link from pypi, not the external one.
+
+ # start an index server
+ index_url = server.full_address + '/simple/'
+
+ # scan a test index
+ crawler = Crawler(index_url, follow_externals=True)
+ releases = crawler.get_releases("foobar")
+ server.stop()
+
+ # we have only one link, because links are compared without md5
+ self.assertEqual(1, len(releases))
+ self.assertEqual(1, len(releases[0].dists))
+ # the link should be from the index
+ self.assertEqual(2, len(releases[0].dists['sdist'].urls))
+ self.assertEqual('12345678901234567',
+ releases[0].dists['sdist'].url['hashval'])
+ self.assertEqual('md5', releases[0].dists['sdist'].url['hashname'])
+
+ @use_pypi_server(static_filesystem_paths=["with_norel_links"],
+ static_uri_paths=["simple", "external"])
+ def test_not_scan_all_links(self, server):
+ # Do not follow all index page links.
+ # The links not tagged with rel="download" and rel="homepage" have
+ # to not be processed by the package index, while processing "pages".
+
+ # process the pages
+ crawler = self._get_simple_crawler(server, follow_externals=True)
+ crawler.get_releases("foobar")
+ # now it should have processed only pages with links rel="download"
+ # and rel="homepage"
+ self.assertIn("%s/simple/foobar/" % server.full_address,
+ crawler._processed_urls) # it's the simple index page
+ self.assertIn("%s/external/homepage.html" % server.full_address,
+ crawler._processed_urls) # the external homepage is rel="homepage"
+ self.assertNotIn("%s/external/nonrel.html" % server.full_address,
+ crawler._processed_urls) # this link contains no rel=*
+ self.assertNotIn("%s/unrelated-0.2.tar.gz" % server.full_address,
+ crawler._processed_urls) # linked from simple index (no rel)
+ self.assertIn("%s/foobar-0.1.tar.gz" % server.full_address,
+ crawler._processed_urls) # linked from simple index (rel)
+ self.assertIn("%s/foobar-2.0.tar.gz" % server.full_address,
+ crawler._processed_urls) # linked from external homepage (rel)
+
+ def test_uses_mirrors(self):
+ # When the main repository seems down, try using the given mirrors"""
+ server = PyPIServer("foo_bar_baz")
+ mirror = PyPIServer("foo_bar_baz")
+ mirror.start() # we dont start the server here
+
+ try:
+ # create the index using both servers
+ crawler = Crawler(server.full_address + "/simple/", hosts=('*',),
+ # set the timeout to 1s for the tests
+ timeout=1, mirrors=[mirror.full_address])
+
+ # this should not raise a timeout
+ self.assertEqual(4, len(crawler.get_releases("foo")))
+ finally:
+ mirror.stop()
+
+ def test_simple_link_matcher(self):
+ # Test that the simple link matcher finds the right links"""
+ crawler = Crawler(follow_externals=False)
+
+ # Here, we define:
+ # 1. one link that must be followed, cause it's a download one
+ # 2. one link that must *not* be followed, cause the is_browsable
+ # returns false for it.
+ # 3. one link that must be followed cause it's a homepage that is
+ # browsable
+ # 4. one link that must be followed, because it contain a md5 hash
+ self.assertTrue(crawler._is_browsable("%stest" % crawler.index_url))
+ self.assertFalse(crawler._is_browsable("http://dl-link2"))
+ content = """
+ <a href="http://dl-link1" rel="download">download_link1</a>
+ <a href="http://dl-link2" rel="homepage">homepage_link1</a>
+ <a href="%(index_url)stest" rel="homepage">homepage_link2</a>
+ <a href="%(index_url)stest/foobar-1.tar.gz#md5=abcdef>download_link2</a>
+ """ % {'index_url': crawler.index_url}
+
+ # Test that the simple link matcher yield the good links.
+ generator = crawler._simple_link_matcher(content, crawler.index_url)
+ self.assertEqual(('%stest/foobar-1.tar.gz#md5=abcdef' %
+ crawler.index_url, True), next(generator))
+ self.assertEqual(('http://dl-link1', True), next(generator))
+ self.assertEqual(('%stest' % crawler.index_url, False),
+ next(generator))
+ self.assertRaises(StopIteration, generator.__next__)
+
+ # Follow the external links is possible (eg. homepages)
+ crawler.follow_externals = True
+ generator = crawler._simple_link_matcher(content, crawler.index_url)
+ self.assertEqual(('%stest/foobar-1.tar.gz#md5=abcdef' %
+ crawler.index_url, True), next(generator))
+ self.assertEqual(('http://dl-link1', True), next(generator))
+ self.assertEqual(('http://dl-link2', False), next(generator))
+ self.assertEqual(('%stest' % crawler.index_url, False),
+ next(generator))
+ self.assertRaises(StopIteration, generator.__next__)
+
+ def test_browse_local_files(self):
+ # Test that we can browse local files"""
+ index_path = os.sep.join(["file://" + PYPI_DEFAULT_STATIC_PATH,
+ "test_found_links", "simple"])
+ crawler = Crawler(index_path)
+ dists = crawler.get_releases("foobar")
+ self.assertEqual(4, len(dists))
+
+ def test_get_link_matcher(self):
+ crawler = Crawler("http://example.org")
+ self.assertEqual('_simple_link_matcher', crawler._get_link_matcher(
+ "http://example.org/some/file").__name__)
+ self.assertEqual('_default_link_matcher', crawler._get_link_matcher(
+ "http://other-url").__name__)
+
+ def test_default_link_matcher(self):
+ crawler = Crawler("http://example.org", mirrors=[])
+ crawler.follow_externals = True
+ crawler._is_browsable = lambda *args: True
+ base_url = "http://example.org/some/file/"
+ content = """
+<a href="../homepage" rel="homepage">link</a>
+<a href="../download" rel="download">link2</a>
+<a href="../simpleurl">link2</a>
+ """
+ found_links = set(uri for uri, _ in
+ crawler._default_link_matcher(content, base_url))
+ self.assertIn('http://example.org/some/homepage', found_links)
+ self.assertIn('http://example.org/some/simpleurl', found_links)
+ self.assertIn('http://example.org/some/download', found_links)
+
+ @use_pypi_server("project_list")
+ def test_search_projects(self, server):
+ # we can search the index for some projects, on their names
+ # the case used no matters here
+ crawler = self._get_simple_crawler(server)
+ tests = (('Foobar', ['FooBar-bar', 'Foobar-baz', 'Baz-FooBar']),
+ ('foobar*', ['FooBar-bar', 'Foobar-baz']),
+ ('*foobar', ['Baz-FooBar']))
+
+ for search, expected in tests:
+ projects = [p.name for p in crawler.search_projects(search)]
+ self.assertListEqual(expected, projects)
+
+
+def test_suite():
+ return unittest.makeSuite(SimpleCrawlerTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_pypi_xmlrpc.py b/Lib/packaging/tests/test_pypi_xmlrpc.py
new file mode 100644
index 0000000..e27c7b3
--- /dev/null
+++ b/Lib/packaging/tests/test_pypi_xmlrpc.py
@@ -0,0 +1,93 @@
+"""Tests for the packaging.pypi.xmlrpc module."""
+
+from packaging.pypi.xmlrpc import Client, InvalidSearchField, ProjectNotFound
+
+from packaging.tests import unittest
+from packaging.tests.pypi_server import use_xmlrpc_server
+
+
+class TestXMLRPCClient(unittest.TestCase):
+ def _get_client(self, server, *args, **kwargs):
+ return Client(server.full_address, *args, **kwargs)
+
+ @use_xmlrpc_server()
+ def test_search_projects(self, server):
+ client = self._get_client(server)
+ server.xmlrpc.set_search_result(['FooBar', 'Foo', 'FooFoo'])
+ results = [r.name for r in client.search_projects(name='Foo')]
+ self.assertEqual(3, len(results))
+ self.assertIn('FooBar', results)
+ self.assertIn('Foo', results)
+ self.assertIn('FooFoo', results)
+
+ def test_search_projects_bad_fields(self):
+ client = Client()
+ self.assertRaises(InvalidSearchField, client.search_projects,
+ invalid="test")
+
+ @use_xmlrpc_server()
+ def test_get_releases(self, server):
+ client = self._get_client(server)
+ server.xmlrpc.set_distributions([
+ {'name': 'FooBar', 'version': '1.1'},
+ {'name': 'FooBar', 'version': '1.2', 'url': 'http://some/url/'},
+ {'name': 'FooBar', 'version': '1.3', 'url': 'http://other/url/'},
+ ])
+
+ # use a lambda here to avoid an useless mock call
+ server.xmlrpc.list_releases = lambda *a, **k: ['1.1', '1.2', '1.3']
+
+ releases = client.get_releases('FooBar (<=1.2)')
+ # dont call release_data and release_url; just return name and version.
+ self.assertEqual(2, len(releases))
+ versions = releases.get_versions()
+ self.assertIn('1.1', versions)
+ self.assertIn('1.2', versions)
+ self.assertNotIn('1.3', versions)
+
+ self.assertRaises(ProjectNotFound, client.get_releases, 'Foo')
+
+ @use_xmlrpc_server()
+ def test_get_distributions(self, server):
+ client = self._get_client(server)
+ server.xmlrpc.set_distributions([
+ {'name': 'FooBar', 'version': '1.1',
+ 'url': 'http://example.org/foobar-1.1-sdist.tar.gz',
+ 'digest': '1234567',
+ 'type': 'sdist', 'python_version': 'source'},
+ {'name':'FooBar', 'version': '1.1',
+ 'url': 'http://example.org/foobar-1.1-bdist.tar.gz',
+ 'digest': '8912345', 'type': 'bdist'},
+ ])
+
+ releases = client.get_releases('FooBar', '1.1')
+ client.get_distributions('FooBar', '1.1')
+ release = releases.get_release('1.1')
+ self.assertTrue('http://example.org/foobar-1.1-sdist.tar.gz',
+ release['sdist'].url['url'])
+ self.assertTrue('http://example.org/foobar-1.1-bdist.tar.gz',
+ release['bdist'].url['url'])
+ self.assertEqual(release['sdist'].python_version, 'source')
+
+ @use_xmlrpc_server()
+ def test_get_metadata(self, server):
+ client = self._get_client(server)
+ server.xmlrpc.set_distributions([
+ {'name': 'FooBar',
+ 'version': '1.1',
+ 'keywords': '',
+ 'obsoletes_dist': ['FooFoo'],
+ 'requires_external': ['Foo'],
+ }])
+ release = client.get_metadata('FooBar', '1.1')
+ self.assertEqual(['Foo'], release.metadata['requires_external'])
+ self.assertEqual(['FooFoo'], release.metadata['obsoletes_dist'])
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TestXMLRPCClient))
+ return suite
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_resources.py b/Lib/packaging/tests/test_resources.py
new file mode 100644
index 0000000..158af36
--- /dev/null
+++ b/Lib/packaging/tests/test_resources.py
@@ -0,0 +1,168 @@
+"""Tests for packaging.resources."""
+
+import os
+import sys
+import shutil
+import tempfile
+from textwrap import dedent
+from packaging.config import get_resources_dests
+from packaging.database import disable_cache, enable_cache
+from packaging.resources import get_file, get_file_path
+
+from packaging.tests import unittest
+from packaging.tests.test_util import GlobTestCaseBase
+
+
+class DataFilesTestCase(GlobTestCaseBase):
+
+ def assertRulesMatch(self, rules, spec):
+ tempdir = self.build_files_tree(spec)
+ expected = self.clean_tree(spec)
+ result = get_resources_dests(tempdir, rules)
+ self.assertEqual(expected, result)
+
+ def clean_tree(self, spec):
+ files = {}
+ for path, value in spec.items():
+ if value is not None:
+ path = self.os_dependent_path(path)
+ files[path] = value
+ return files
+
+ def test_simple_glob(self):
+ rules = [('', '*.tpl', '{data}')]
+ spec = {'coucou.tpl': '{data}/coucou.tpl',
+ 'Donotwant': None}
+ self.assertRulesMatch(rules, spec)
+
+ def test_multiple_match(self):
+ rules = [('scripts', '*.bin', '{appdata}'),
+ ('scripts', '*', '{appscript}')]
+ spec = {'scripts/script.bin': '{appscript}/script.bin',
+ 'Babarlikestrawberry': None}
+ self.assertRulesMatch(rules, spec)
+
+ def test_set_match(self):
+ rules = [('scripts', '*.{bin,sh}', '{appscript}')]
+ spec = {'scripts/script.bin': '{appscript}/script.bin',
+ 'scripts/babar.sh': '{appscript}/babar.sh',
+ 'Babarlikestrawberry': None}
+ self.assertRulesMatch(rules, spec)
+
+ def test_set_match_multiple(self):
+ rules = [('scripts', 'script{s,}.{bin,sh}', '{appscript}')]
+ spec = {'scripts/scripts.bin': '{appscript}/scripts.bin',
+ 'scripts/script.sh': '{appscript}/script.sh',
+ 'Babarlikestrawberry': None}
+ self.assertRulesMatch(rules, spec)
+
+ def test_set_match_exclude(self):
+ rules = [('scripts', '*', '{appscript}'),
+ ('', '**/*.sh', None)]
+ spec = {'scripts/scripts.bin': '{appscript}/scripts.bin',
+ 'scripts/script.sh': None,
+ 'Babarlikestrawberry': None}
+ self.assertRulesMatch(rules, spec)
+
+ def test_glob_in_base(self):
+ rules = [('scrip*', '*.bin', '{appscript}')]
+ spec = {'scripts/scripts.bin': '{appscript}/scripts.bin',
+ 'scripouille/babar.bin': '{appscript}/babar.bin',
+ 'scriptortu/lotus.bin': '{appscript}/lotus.bin',
+ 'Babarlikestrawberry': None}
+ self.assertRulesMatch(rules, spec)
+
+ def test_recursive_glob(self):
+ rules = [('', '**/*.bin', '{binary}')]
+ spec = {'binary0.bin': '{binary}/binary0.bin',
+ 'scripts/binary1.bin': '{binary}/scripts/binary1.bin',
+ 'scripts/bin/binary2.bin': '{binary}/scripts/bin/binary2.bin',
+ 'you/kill/pandabear.guy': None}
+ self.assertRulesMatch(rules, spec)
+
+ def test_final_exemple_glob(self):
+ rules = [
+ ('mailman/database/schemas/', '*', '{appdata}/schemas'),
+ ('', '**/*.tpl', '{appdata}/templates'),
+ ('', 'developer-docs/**/*.txt', '{doc}'),
+ ('', 'README', '{doc}'),
+ ('mailman/etc/', '*', '{config}'),
+ ('mailman/foo/', '**/bar/*.cfg', '{config}/baz'),
+ ('mailman/foo/', '**/*.cfg', '{config}/hmm'),
+ ('', 'some-new-semantic.sns', '{funky-crazy-category}'),
+ ]
+ spec = {
+ 'README': '{doc}/README',
+ 'some.tpl': '{appdata}/templates/some.tpl',
+ 'some-new-semantic.sns':
+ '{funky-crazy-category}/some-new-semantic.sns',
+ 'mailman/database/mailman.db': None,
+ 'mailman/database/schemas/blah.schema':
+ '{appdata}/schemas/blah.schema',
+ 'mailman/etc/my.cnf': '{config}/my.cnf',
+ 'mailman/foo/some/path/bar/my.cfg':
+ '{config}/hmm/some/path/bar/my.cfg',
+ 'mailman/foo/some/path/other.cfg':
+ '{config}/hmm/some/path/other.cfg',
+ 'developer-docs/index.txt': '{doc}/developer-docs/index.txt',
+ 'developer-docs/api/toc.txt': '{doc}/developer-docs/api/toc.txt',
+ }
+ self.maxDiff = None
+ self.assertRulesMatch(rules, spec)
+
+ def test_get_file(self):
+ # Create a fake dist
+ temp_site_packages = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, temp_site_packages)
+
+ dist_name = 'test'
+ dist_info = os.path.join(temp_site_packages, 'test-0.1.dist-info')
+ os.mkdir(dist_info)
+
+ metadata_path = os.path.join(dist_info, 'METADATA')
+ resources_path = os.path.join(dist_info, 'RESOURCES')
+
+ with open(metadata_path, 'w') as fp:
+ fp.write(dedent("""\
+ Metadata-Version: 1.2
+ Name: test
+ Version: 0.1
+ Summary: test
+ Author: me
+ """))
+
+ test_path = 'test.cfg'
+
+ fd, test_resource_path = tempfile.mkstemp()
+ os.close(fd)
+ self.addCleanup(os.remove, test_resource_path)
+
+ with open(test_resource_path, 'w') as fp:
+ fp.write('Config')
+
+ with open(resources_path, 'w') as fp:
+ fp.write('%s,%s' % (test_path, test_resource_path))
+
+ # Add fake site-packages to sys.path to retrieve fake dist
+ self.addCleanup(sys.path.remove, temp_site_packages)
+ sys.path.insert(0, temp_site_packages)
+
+ # Force packaging.database to rescan the sys.path
+ self.addCleanup(enable_cache)
+ disable_cache()
+
+ # Try to retrieve resources paths and files
+ self.assertEqual(get_file_path(dist_name, test_path),
+ test_resource_path)
+ self.assertRaises(KeyError, get_file_path, dist_name, 'i-dont-exist')
+
+ with get_file(dist_name, test_path) as fp:
+ self.assertEqual(fp.read(), 'Config')
+ self.assertRaises(KeyError, get_file, dist_name, 'i-dont-exist')
+
+
+def test_suite():
+ return unittest.makeSuite(DataFilesTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_run.py b/Lib/packaging/tests/test_run.py
new file mode 100644
index 0000000..01fa5aa
--- /dev/null
+++ b/Lib/packaging/tests/test_run.py
@@ -0,0 +1,62 @@
+"""Tests for packaging.run."""
+
+import os
+import sys
+import shutil
+
+from packaging.tests import unittest, support, TESTFN
+
+# setup script that uses __file__
+setup_using___file__ = """\
+
+__file__
+
+from packaging.run import setup
+setup()
+"""
+
+setup_prints_cwd = """\
+
+import os
+print os.getcwd()
+
+from packaging.run import setup
+setup()
+"""
+
+
+class CoreTestCase(unittest.TestCase):
+
+ def setUp(self):
+ super(CoreTestCase, self).setUp()
+ self.old_stdout = sys.stdout
+ self.cleanup_testfn()
+ self.old_argv = sys.argv, sys.argv[:]
+
+ def tearDown(self):
+ sys.stdout = self.old_stdout
+ self.cleanup_testfn()
+ sys.argv = self.old_argv[0]
+ sys.argv[:] = self.old_argv[1]
+ super(CoreTestCase, self).tearDown()
+
+ def cleanup_testfn(self):
+ path = TESTFN
+ if os.path.isfile(path):
+ os.remove(path)
+ elif os.path.isdir(path):
+ shutil.rmtree(path)
+
+ def write_setup(self, text, path=TESTFN):
+ with open(path, "w") as fp:
+ fp.write(text)
+ return path
+
+ # TODO restore the tests removed six months ago and port them to pysetup
+
+
+def test_suite():
+ return unittest.makeSuite(CoreTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_uninstall.py b/Lib/packaging/tests/test_uninstall.py
new file mode 100644
index 0000000..ce345b6
--- /dev/null
+++ b/Lib/packaging/tests/test_uninstall.py
@@ -0,0 +1,99 @@
+"""Tests for the uninstall command."""
+import os
+import sys
+
+from packaging.database import disable_cache, enable_cache
+from packaging.run import main
+from packaging.errors import PackagingError
+from packaging.install import remove
+from packaging.command.install_dist import install_dist
+
+from packaging.tests import unittest, support
+
+SETUP_CFG = """
+[metadata]
+name = %(name)s
+version = %(version)s
+
+[files]
+packages =
+ %(pkg)s
+ %(pkg)s.sub
+"""
+
+
+class UninstallTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ support.EnvironRestorer,
+ unittest.TestCase):
+
+ restore_environ = ['PLAT']
+
+ def setUp(self):
+ super(UninstallTestCase, self).setUp()
+ self.addCleanup(setattr, sys, 'stdout', sys.stdout)
+ self.addCleanup(setattr, sys, 'stderr', sys.stderr)
+ self.addCleanup(os.chdir, os.getcwd())
+ self.addCleanup(enable_cache)
+ self.root_dir = self.mkdtemp()
+ disable_cache()
+
+ def run_setup(self, *args):
+ # run setup with args
+ args = ['run'] + list(args)
+ dist = main(args)
+ return dist
+
+ def get_path(self, dist, name):
+ cmd = install_dist(dist)
+ cmd.prefix = self.root_dir
+ cmd.finalize_options()
+ return getattr(cmd, 'install_' + name)
+
+ def make_dist(self, name='Foo', **kw):
+ kw['name'] = name
+ pkg = name.lower()
+ if 'version' not in kw:
+ kw['version'] = '0.1'
+ project_dir, dist = self.create_dist(**kw)
+ kw['pkg'] = pkg
+
+ pkg_dir = os.path.join(project_dir, pkg)
+ os.mkdir(pkg_dir)
+ os.mkdir(os.path.join(pkg_dir, 'sub'))
+
+ self.write_file((project_dir, 'setup.cfg'), SETUP_CFG % kw)
+ self.write_file((pkg_dir, '__init__.py'), '#')
+ self.write_file((pkg_dir, pkg + '_utils.py'), '#')
+ self.write_file((pkg_dir, 'sub', '__init__.py'), '#')
+ self.write_file((pkg_dir, 'sub', pkg + '_utils.py'), '#')
+
+ return project_dir
+
+ def install_dist(self, name='Foo', dirname=None, **kw):
+ if not dirname:
+ dirname = self.make_dist(name, **kw)
+ os.chdir(dirname)
+ dist = self.run_setup('install_dist', '--prefix=' + self.root_dir)
+ install_lib = self.get_path(dist, 'purelib')
+ return dist, install_lib
+
+ def test_uninstall_unknow_distribution(self):
+ self.assertRaises(PackagingError, remove, 'Foo',
+ paths=[self.root_dir])
+
+ def test_uninstall(self):
+ dist, install_lib = self.install_dist()
+ self.assertIsFile(install_lib, 'foo', '__init__.py')
+ self.assertIsFile(install_lib, 'foo', 'sub', '__init__.py')
+ self.assertIsFile(install_lib, 'Foo-0.1.dist-info', 'RECORD')
+ remove('Foo', paths=[install_lib])
+ self.assertIsNotFile(install_lib, 'foo', 'sub', '__init__.py')
+ self.assertIsNotFile(install_lib, 'Foo-0.1.dist-info', 'RECORD')
+
+
+def test_suite():
+ return unittest.makeSuite(UninstallTestCase)
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
diff --git a/Lib/packaging/tests/test_unixccompiler.py b/Lib/packaging/tests/test_unixccompiler.py
new file mode 100644
index 0000000..16a1af3
--- /dev/null
+++ b/Lib/packaging/tests/test_unixccompiler.py
@@ -0,0 +1,132 @@
+"""Tests for packaging.unixccompiler."""
+import sys
+
+import sysconfig
+from packaging.compiler.unixccompiler import UnixCCompiler
+from packaging.tests import unittest
+
+
+class UnixCCompilerTestCase(unittest.TestCase):
+
+ def setUp(self):
+ self._backup_platform = sys.platform
+ self._backup_get_config_var = sysconfig.get_config_var
+
+ class CompilerWrapper(UnixCCompiler):
+ def rpath_foo(self):
+ return self.runtime_library_dir_option('/foo')
+ self.cc = CompilerWrapper()
+
+ def tearDown(self):
+ sys.platform = self._backup_platform
+ sysconfig.get_config_var = self._backup_get_config_var
+
+ @unittest.skipIf(sys.platform == 'win32', 'irrelevant on win32')
+ def test_runtime_libdir_option(self):
+
+ # Issue #5900: Ensure RUNPATH is added to extension
+ # modules with RPATH if GNU ld is used
+
+ # darwin
+ sys.platform = 'darwin'
+ self.assertEqual(self.cc.rpath_foo(), '-L/foo')
+
+ # hp-ux
+ sys.platform = 'hp-ux'
+ old_gcv = sysconfig.get_config_var
+
+ def gcv(v):
+ return 'xxx'
+ sysconfig.get_config_var = gcv
+ self.assertEqual(self.cc.rpath_foo(), ['+s', '-L/foo'])
+
+ def gcv(v):
+ return 'gcc'
+ sysconfig.get_config_var = gcv
+ self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo'])
+
+ def gcv(v):
+ return 'g++'
+ sysconfig.get_config_var = gcv
+ self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo'])
+
+ sysconfig.get_config_var = old_gcv
+
+ # irix646
+ sys.platform = 'irix646'
+ self.assertEqual(self.cc.rpath_foo(), ['-rpath', '/foo'])
+
+ # osf1V5
+ sys.platform = 'osf1V5'
+ self.assertEqual(self.cc.rpath_foo(), ['-rpath', '/foo'])
+
+ # GCC GNULD
+ sys.platform = 'bar'
+
+ def gcv(v):
+ if v == 'CC':
+ return 'gcc'
+ elif v == 'GNULD':
+ return 'yes'
+ sysconfig.get_config_var = gcv
+ self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo')
+
+ # GCC non-GNULD
+ sys.platform = 'bar'
+
+ def gcv(v):
+ if v == 'CC':
+ return 'gcc'
+ elif v == 'GNULD':
+ return 'no'
+ sysconfig.get_config_var = gcv
+ self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo')
+
+ # GCC GNULD with fully qualified configuration prefix
+ # see #7617
+ sys.platform = 'bar'
+
+ def gcv(v):
+ if v == 'CC':
+ return 'x86_64-pc-linux-gnu-gcc-4.4.2'
+ elif v == 'GNULD':
+ return 'yes'
+ sysconfig.get_config_var = gcv
+ self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo')
+
+ # non-GCC GNULD
+ sys.platform = 'bar'
+
+ def gcv(v):
+ if v == 'CC':
+ return 'cc'
+ elif v == 'GNULD':
+ return 'yes'
+ sysconfig.get_config_var = gcv
+ self.assertEqual(self.cc.rpath_foo(), '-R/foo')
+
+ # non-GCC non-GNULD
+ sys.platform = 'bar'
+
+ def gcv(v):
+ if v == 'CC':
+ return 'cc'
+ elif v == 'GNULD':
+ return 'no'
+ sysconfig.get_config_var = gcv
+ self.assertEqual(self.cc.rpath_foo(), '-R/foo')
+
+ # AIX C/C++ linker
+ sys.platform = 'aix'
+
+ def gcv(v):
+ return 'xxx'
+ sysconfig.get_config_var = gcv
+ self.assertEqual(self.cc.rpath_foo(), '-blibpath:/foo')
+
+
+def test_suite():
+ return unittest.makeSuite(UnixCCompilerTestCase)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_util.py b/Lib/packaging/tests/test_util.py
new file mode 100644
index 0000000..336086d
--- /dev/null
+++ b/Lib/packaging/tests/test_util.py
@@ -0,0 +1,928 @@
+"""Tests for packaging.util."""
+import os
+import sys
+import time
+import logging
+import tempfile
+import subprocess
+from io import StringIO
+
+from packaging.tests import support, unittest
+from packaging.errors import (
+ PackagingPlatformError, PackagingByteCompileError, PackagingFileError,
+ PackagingExecError, InstallationException)
+from packaging import util
+from packaging.util import (
+ convert_path, change_root, split_quoted, strtobool, rfc822_escape,
+ get_compiler_versions, _MAC_OS_X_LD_VERSION, byte_compile, find_packages,
+ spawn, get_pypirc_path, generate_pypirc, read_pypirc, resolve_name, iglob,
+ RICH_GLOB, egginfo_to_distinfo, is_setuptools, is_distutils, is_packaging,
+ get_install_method)
+
+
+PYPIRC = """\
+[distutils]
+index-servers =
+ pypi
+ server1
+
+[pypi]
+username:me
+password:xxxx
+
+[server1]
+repository:http://example.com
+username:tarek
+password:secret
+"""
+
+PYPIRC_OLD = """\
+[server-login]
+username:tarek
+password:secret
+"""
+
+WANTED = """\
+[distutils]
+index-servers =
+ pypi
+
+[pypi]
+username:tarek
+password:xxx
+"""
+
+
+class FakePopen:
+ test_class = None
+
+ def __init__(self, cmd, shell, stdout, stderr):
+ self.cmd = cmd.split()[0]
+ exes = self.test_class._exes
+ if self.cmd not in exes:
+ # we don't want to call the system, returning an empty
+ # output so it doesn't match
+ self.stdout = StringIO()
+ self.stderr = StringIO()
+ else:
+ self.stdout = StringIO(exes[self.cmd])
+ self.stderr = StringIO()
+
+
+class UtilTestCase(support.EnvironRestorer,
+ support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ restore_environ = ['HOME']
+
+ def setUp(self):
+ super(UtilTestCase, self).setUp()
+ self.tmp_dir = self.mkdtemp()
+ self.rc = os.path.join(self.tmp_dir, '.pypirc')
+ os.environ['HOME'] = self.tmp_dir
+ # saving the environment
+ self.name = os.name
+ self.platform = sys.platform
+ self.version = sys.version
+ self.sep = os.sep
+ self.join = os.path.join
+ self.isabs = os.path.isabs
+ self.splitdrive = os.path.splitdrive
+ #self._config_vars = copy(sysconfig._config_vars)
+
+ # patching os.uname
+ if hasattr(os, 'uname'):
+ self.uname = os.uname
+ self._uname = os.uname()
+ else:
+ self.uname = None
+ self._uname = None
+ os.uname = self._get_uname
+
+ # patching POpen
+ self.old_find_executable = util.find_executable
+ util.find_executable = self._find_executable
+ self._exes = {}
+ self.old_popen = subprocess.Popen
+ self.old_stdout = sys.stdout
+ self.old_stderr = sys.stderr
+ FakePopen.test_class = self
+ subprocess.Popen = FakePopen
+
+ def tearDown(self):
+ # getting back the environment
+ os.name = self.name
+ sys.platform = self.platform
+ sys.version = self.version
+ os.sep = self.sep
+ os.path.join = self.join
+ os.path.isabs = self.isabs
+ os.path.splitdrive = self.splitdrive
+ if self.uname is not None:
+ os.uname = self.uname
+ else:
+ del os.uname
+ #sysconfig._config_vars = copy(self._config_vars)
+ util.find_executable = self.old_find_executable
+ subprocess.Popen = self.old_popen
+ sys.old_stdout = self.old_stdout
+ sys.old_stderr = self.old_stderr
+ super(UtilTestCase, self).tearDown()
+
+ def _set_uname(self, uname):
+ self._uname = uname
+
+ def _get_uname(self):
+ return self._uname
+
+ def test_convert_path(self):
+ # linux/mac
+ os.sep = '/'
+
+ def _join(path):
+ return '/'.join(path)
+ os.path.join = _join
+
+ self.assertEqual(convert_path('/home/to/my/stuff'),
+ '/home/to/my/stuff')
+
+ # win
+ os.sep = '\\'
+
+ def _join(*path):
+ return '\\'.join(path)
+ os.path.join = _join
+
+ self.assertRaises(ValueError, convert_path, '/home/to/my/stuff')
+ self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/')
+
+ self.assertEqual(convert_path('home/to/my/stuff'),
+ 'home\\to\\my\\stuff')
+ self.assertEqual(convert_path('.'),
+ os.curdir)
+
+ def test_change_root(self):
+ # linux/mac
+ os.name = 'posix'
+
+ def _isabs(path):
+ return path[0] == '/'
+ os.path.isabs = _isabs
+
+ def _join(*path):
+ return '/'.join(path)
+ os.path.join = _join
+
+ self.assertEqual(change_root('/root', '/old/its/here'),
+ '/root/old/its/here')
+ self.assertEqual(change_root('/root', 'its/here'),
+ '/root/its/here')
+
+ # windows
+ os.name = 'nt'
+
+ def _isabs(path):
+ return path.startswith('c:\\')
+ os.path.isabs = _isabs
+
+ def _splitdrive(path):
+ if path.startswith('c:'):
+ return '', path.replace('c:', '')
+ return '', path
+ os.path.splitdrive = _splitdrive
+
+ def _join(*path):
+ return '\\'.join(path)
+ os.path.join = _join
+
+ self.assertEqual(change_root('c:\\root', 'c:\\old\\its\\here'),
+ 'c:\\root\\old\\its\\here')
+ self.assertEqual(change_root('c:\\root', 'its\\here'),
+ 'c:\\root\\its\\here')
+
+ # BugsBunny os (it's a great os)
+ os.name = 'BugsBunny'
+ self.assertRaises(PackagingPlatformError,
+ change_root, 'c:\\root', 'its\\here')
+
+ # XXX platforms to be covered: os2, mac
+
+ def test_split_quoted(self):
+ self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'),
+ ['one', 'two', 'three', 'four'])
+
+ def test_strtobool(self):
+ yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1')
+ no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N')
+
+ for y in yes:
+ self.assertTrue(strtobool(y))
+
+ for n in no:
+ self.assertFalse(strtobool(n))
+
+ def test_rfc822_escape(self):
+ header = 'I am a\npoor\nlonesome\nheader\n'
+ res = rfc822_escape(header)
+ wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s'
+ 'header%(8s)s') % {'8s': '\n' + 8 * ' '}
+ self.assertEqual(res, wanted)
+
+ def test_find_exe_version(self):
+ # the ld version scheme under MAC OS is:
+ # ^@(#)PROGRAM:ld PROJECT:ld64-VERSION
+ #
+ # where VERSION is a 2-digit number for major
+ # revisions. For instance under Leopard, it's
+ # currently 77
+ #
+ # Dots are used when branching is done.
+ #
+ # The SnowLeopard ld64 is currently 95.2.12
+
+ for output, version in (('@(#)PROGRAM:ld PROJECT:ld64-77', '77'),
+ ('@(#)PROGRAM:ld PROJECT:ld64-95.2.12',
+ '95.2.12')):
+ result = _MAC_OS_X_LD_VERSION.search(output)
+ self.assertEqual(result.group(1), version)
+
+ def _find_executable(self, name):
+ if name in self._exes:
+ return name
+ return None
+
+ def test_get_compiler_versions(self):
+ # get_versions calls distutils.spawn.find_executable on
+ # 'gcc', 'ld' and 'dllwrap'
+ self.assertEqual(get_compiler_versions(), (None, None, None))
+
+ # Let's fake we have 'gcc' and it returns '3.4.5'
+ self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF'
+ res = get_compiler_versions()
+ self.assertEqual(str(res[0]), '3.4.5')
+
+ # and let's see what happens when the version
+ # doesn't match the regular expression
+ # (\d+\.\d+(\.\d+)*)
+ self._exes['gcc'] = 'very strange output'
+ res = get_compiler_versions()
+ self.assertEqual(res[0], None)
+
+ # same thing for ld
+ if sys.platform != 'darwin':
+ self._exes['ld'] = 'GNU ld version 2.17.50 20060824'
+ res = get_compiler_versions()
+ self.assertEqual(str(res[1]), '2.17.50')
+ self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77'
+ res = get_compiler_versions()
+ self.assertEqual(res[1], None)
+ else:
+ self._exes['ld'] = 'GNU ld version 2.17.50 20060824'
+ res = get_compiler_versions()
+ self.assertEqual(res[1], None)
+ self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77'
+ res = get_compiler_versions()
+ self.assertEqual(str(res[1]), '77')
+
+ # and dllwrap
+ self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF'
+ res = get_compiler_versions()
+ self.assertEqual(str(res[2]), '2.17.50')
+ self._exes['dllwrap'] = 'Cheese Wrap'
+ res = get_compiler_versions()
+ self.assertEqual(res[2], None)
+
+ @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'),
+ 'sys.dont_write_bytecode not supported')
+ def test_dont_write_bytecode(self):
+ # makes sure byte_compile raise a PackagingError
+ # if sys.dont_write_bytecode is True
+ old_dont_write_bytecode = sys.dont_write_bytecode
+ sys.dont_write_bytecode = True
+ try:
+ self.assertRaises(PackagingByteCompileError, byte_compile, [])
+ finally:
+ sys.dont_write_bytecode = old_dont_write_bytecode
+
+ def test_newer(self):
+ self.assertRaises(PackagingFileError, util.newer, 'xxx', 'xxx')
+ self.newer_f1 = self.mktempfile()
+ time.sleep(1)
+ self.newer_f2 = self.mktempfile()
+ self.assertTrue(util.newer(self.newer_f2.name, self.newer_f1.name))
+
+ def test_find_packages(self):
+ # let's create a structure we want to scan:
+ #
+ # pkg1
+ # __init__
+ # pkg2
+ # __init__
+ # pkg3
+ # __init__
+ # pkg6
+ # __init__
+ # pkg4 <--- not a pkg
+ # pkg8
+ # __init__
+ # pkg5
+ # __init__
+ #
+ root = self.mkdtemp()
+ pkg1 = os.path.join(root, 'pkg1')
+ os.mkdir(pkg1)
+ self.write_file(os.path.join(pkg1, '__init__.py'))
+ os.mkdir(os.path.join(pkg1, 'pkg2'))
+ self.write_file(os.path.join(pkg1, 'pkg2', '__init__.py'))
+ os.mkdir(os.path.join(pkg1, 'pkg3'))
+ self.write_file(os.path.join(pkg1, 'pkg3', '__init__.py'))
+ os.mkdir(os.path.join(pkg1, 'pkg3', 'pkg6'))
+ self.write_file(os.path.join(pkg1, 'pkg3', 'pkg6', '__init__.py'))
+ os.mkdir(os.path.join(pkg1, 'pkg4'))
+ os.mkdir(os.path.join(pkg1, 'pkg4', 'pkg8'))
+ self.write_file(os.path.join(pkg1, 'pkg4', 'pkg8', '__init__.py'))
+ pkg5 = os.path.join(root, 'pkg5')
+ os.mkdir(pkg5)
+ self.write_file(os.path.join(pkg5, '__init__.py'))
+
+ res = find_packages([root], ['pkg1.pkg2'])
+ self.assertEqual(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3',
+ 'pkg1.pkg3.pkg6']))
+
+ def test_resolve_name(self):
+ self.assertIs(str, resolve_name('builtins.str'))
+ self.assertEqual(
+ UtilTestCase.__name__,
+ resolve_name("packaging.tests.test_util.UtilTestCase").__name__)
+ self.assertEqual(
+ UtilTestCase.test_resolve_name.__name__,
+ resolve_name("packaging.tests.test_util.UtilTestCase."
+ "test_resolve_name").__name__)
+
+ self.assertRaises(ImportError, resolve_name,
+ "packaging.tests.test_util.UtilTestCaseNot")
+ self.assertRaises(ImportError, resolve_name,
+ "packaging.tests.test_util.UtilTestCase."
+ "nonexistent_attribute")
+
+ def test_import_nested_first_time(self):
+ tmp_dir = self.mkdtemp()
+ os.makedirs(os.path.join(tmp_dir, 'a', 'b'))
+ self.write_file(os.path.join(tmp_dir, 'a', '__init__.py'), '')
+ self.write_file(os.path.join(tmp_dir, 'a', 'b', '__init__.py'), '')
+ self.write_file(os.path.join(tmp_dir, 'a', 'b', 'c.py'),
+ 'class Foo: pass')
+
+ try:
+ sys.path.append(tmp_dir)
+ resolve_name("a.b.c.Foo")
+ # assert nothing raised
+ finally:
+ sys.path.remove(tmp_dir)
+
+ @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+ def test_run_2to3_on_code(self):
+ content = "print 'test'"
+ converted_content = "print('test')"
+ file_handle = self.mktempfile()
+ file_name = file_handle.name
+ file_handle.write(content)
+ file_handle.flush()
+ file_handle.seek(0)
+ from packaging.util import run_2to3
+ run_2to3([file_name])
+ new_content = "".join(file_handle.read())
+ file_handle.close()
+ self.assertEqual(new_content, converted_content)
+
+ @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher')
+ def test_run_2to3_on_doctests(self):
+ # to check if text files containing doctests only get converted.
+ content = ">>> print 'test'\ntest\n"
+ converted_content = ">>> print('test')\ntest\n\n"
+ file_handle = self.mktempfile()
+ file_name = file_handle.name
+ file_handle.write(content)
+ file_handle.flush()
+ file_handle.seek(0)
+ from packaging.util import run_2to3
+ run_2to3([file_name], doctests_only=True)
+ new_content = "".join(file_handle.readlines())
+ file_handle.close()
+ self.assertEqual(new_content, converted_content)
+
+ @unittest.skipUnless(os.name in ('nt', 'posix'),
+ 'runs only under posix or nt')
+ def test_spawn(self):
+ # Do not patch subprocess on unix because
+ # packaging.util._spawn_posix uses it
+ if os.name in 'posix':
+ subprocess.Popen = self.old_popen
+ tmpdir = self.mkdtemp()
+
+ # creating something executable
+ # through the shell that returns 1
+ if os.name == 'posix':
+ exe = os.path.join(tmpdir, 'foo.sh')
+ self.write_file(exe, '#!/bin/sh\nexit 1')
+ os.chmod(exe, 0o777)
+ else:
+ exe = os.path.join(tmpdir, 'foo.bat')
+ self.write_file(exe, 'exit 1')
+
+ os.chmod(exe, 0o777)
+ self.assertRaises(PackagingExecError, spawn, [exe])
+
+ # now something that works
+ if os.name == 'posix':
+ exe = os.path.join(tmpdir, 'foo.sh')
+ self.write_file(exe, '#!/bin/sh\nexit 0')
+ os.chmod(exe, 0o777)
+ else:
+ exe = os.path.join(tmpdir, 'foo.bat')
+ self.write_file(exe, 'exit 0')
+
+ os.chmod(exe, 0o777)
+ spawn([exe]) # should work without any error
+
+ def test_server_registration(self):
+ # This test makes sure we know how to:
+ # 1. handle several sections in .pypirc
+ # 2. handle the old format
+
+ # new format
+ self.write_file(self.rc, PYPIRC)
+ config = read_pypirc()
+
+ config = sorted(config.items())
+ expected = [('password', 'xxxx'), ('realm', 'pypi'),
+ ('repository', 'http://pypi.python.org/pypi'),
+ ('server', 'pypi'), ('username', 'me')]
+ self.assertEqual(config, expected)
+
+ # old format
+ self.write_file(self.rc, PYPIRC_OLD)
+ config = read_pypirc()
+ config = sorted(config.items())
+ expected = [('password', 'secret'), ('realm', 'pypi'),
+ ('repository', 'http://pypi.python.org/pypi'),
+ ('server', 'server-login'), ('username', 'tarek')]
+ self.assertEqual(config, expected)
+
+ def test_server_empty_registration(self):
+ rc = get_pypirc_path()
+ self.assertFalse(os.path.exists(rc))
+ generate_pypirc('tarek', 'xxx')
+ self.assertTrue(os.path.exists(rc))
+ with open(rc) as f:
+ content = f.read()
+ self.assertEqual(content, WANTED)
+
+
+class GlobTestCaseBase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def build_files_tree(self, files):
+ tempdir = self.mkdtemp()
+ for filepath in files:
+ is_dir = filepath.endswith('/')
+ filepath = os.path.join(tempdir, *filepath.split('/'))
+ if is_dir:
+ dirname = filepath
+ else:
+ dirname = os.path.dirname(filepath)
+ if dirname and not os.path.exists(dirname):
+ os.makedirs(dirname)
+ if not is_dir:
+ self.write_file(filepath, 'babar')
+ return tempdir
+
+ @staticmethod
+ def os_dependent_path(path):
+ path = path.rstrip('/').split('/')
+ return os.path.join(*path)
+
+ def clean_tree(self, spec):
+ files = []
+ for path, includes in spec.items():
+ if includes:
+ files.append(self.os_dependent_path(path))
+ return files
+
+
+class GlobTestCase(GlobTestCaseBase):
+
+ def assertGlobMatch(self, glob, spec):
+ """"""
+ tempdir = self.build_files_tree(spec)
+ expected = self.clean_tree(spec)
+ self.addCleanup(os.chdir, os.getcwd())
+ os.chdir(tempdir)
+ result = list(iglob(glob))
+ self.assertCountEqual(expected, result)
+
+ def test_regex_rich_glob(self):
+ matches = RICH_GLOB.findall(
+ r"babar aime les {fraises} est les {huitres}")
+ self.assertEqual(["fraises", "huitres"], matches)
+
+ def test_simple_glob(self):
+ glob = '*.tp?'
+ spec = {'coucou.tpl': True,
+ 'coucou.tpj': True,
+ 'Donotwant': False}
+ self.assertGlobMatch(glob, spec)
+
+ def test_simple_glob_in_dir(self):
+ glob = 'babar/*.tp?'
+ spec = {'babar/coucou.tpl': True,
+ 'babar/coucou.tpj': True,
+ 'babar/toto.bin': False,
+ 'Donotwant': False}
+ self.assertGlobMatch(glob, spec)
+
+ def test_recursive_glob_head(self):
+ glob = '**/tip/*.t?l'
+ spec = {'babar/zaza/zuzu/tip/coucou.tpl': True,
+ 'babar/z/tip/coucou.tpl': True,
+ 'babar/tip/coucou.tpl': True,
+ 'babar/zeop/tip/babar/babar.tpl': False,
+ 'babar/z/tip/coucou.bin': False,
+ 'babar/toto.bin': False,
+ 'zozo/zuzu/tip/babar.tpl': True,
+ 'zozo/tip/babar.tpl': True,
+ 'Donotwant': False}
+ self.assertGlobMatch(glob, spec)
+
+ def test_recursive_glob_tail(self):
+ glob = 'babar/**'
+ spec = {'babar/zaza/': True,
+ 'babar/zaza/zuzu/': True,
+ 'babar/zaza/zuzu/babar.xml': True,
+ 'babar/zaza/zuzu/toto.xml': True,
+ 'babar/zaza/zuzu/toto.csv': True,
+ 'babar/zaza/coucou.tpl': True,
+ 'babar/bubu.tpl': True,
+ 'zozo/zuzu/tip/babar.tpl': False,
+ 'zozo/tip/babar.tpl': False,
+ 'Donotwant': False}
+ self.assertGlobMatch(glob, spec)
+
+ def test_recursive_glob_middle(self):
+ glob = 'babar/**/tip/*.t?l'
+ spec = {'babar/zaza/zuzu/tip/coucou.tpl': True,
+ 'babar/z/tip/coucou.tpl': True,
+ 'babar/tip/coucou.tpl': True,
+ 'babar/zeop/tip/babar/babar.tpl': False,
+ 'babar/z/tip/coucou.bin': False,
+ 'babar/toto.bin': False,
+ 'zozo/zuzu/tip/babar.tpl': False,
+ 'zozo/tip/babar.tpl': False,
+ 'Donotwant': False}
+ self.assertGlobMatch(glob, spec)
+
+ def test_glob_set_tail(self):
+ glob = 'bin/*.{bin,sh,exe}'
+ spec = {'bin/babar.bin': True,
+ 'bin/zephir.sh': True,
+ 'bin/celestine.exe': True,
+ 'bin/cornelius.bat': False,
+ 'bin/cornelius.xml': False,
+ 'toto/yurg': False,
+ 'Donotwant': False}
+ self.assertGlobMatch(glob, spec)
+
+ def test_glob_set_middle(self):
+ glob = 'xml/{babar,toto}.xml'
+ spec = {'xml/babar.xml': True,
+ 'xml/toto.xml': True,
+ 'xml/babar.xslt': False,
+ 'xml/cornelius.sgml': False,
+ 'xml/zephir.xml': False,
+ 'toto/yurg.xml': False,
+ 'Donotwant': False}
+ self.assertGlobMatch(glob, spec)
+
+ def test_glob_set_head(self):
+ glob = '{xml,xslt}/babar.*'
+ spec = {'xml/babar.xml': True,
+ 'xml/toto.xml': False,
+ 'xslt/babar.xslt': True,
+ 'xslt/toto.xslt': False,
+ 'toto/yurg.xml': False,
+ 'Donotwant': False}
+ self.assertGlobMatch(glob, spec)
+
+ def test_glob_all(self):
+ glob = '{xml/*,xslt/**}/babar.xml'
+ spec = {'xml/a/babar.xml': True,
+ 'xml/b/babar.xml': True,
+ 'xml/a/c/babar.xml': False,
+ 'xslt/a/babar.xml': True,
+ 'xslt/b/babar.xml': True,
+ 'xslt/a/c/babar.xml': True,
+ 'toto/yurg.xml': False,
+ 'Donotwant': False}
+ self.assertGlobMatch(glob, spec)
+
+ def test_invalid_glob_pattern(self):
+ invalids = [
+ 'ppooa**',
+ 'azzaeaz4**/',
+ '/**ddsfs',
+ '**##1e"&e',
+ 'DSFb**c009',
+ '{',
+ '{aaQSDFa',
+ '}',
+ 'aQSDFSaa}',
+ '{**a,',
+ ',**a}',
+ '{a**,',
+ ',b**}',
+ '{a**a,babar}',
+ '{bob,b**z}',
+ ]
+ msg = "%r is not supposed to be a valid pattern"
+ for pattern in invalids:
+ try:
+ iglob(pattern)
+ except ValueError:
+ continue
+ else:
+ self.fail(msg % pattern)
+
+
+class EggInfoToDistInfoTestCase(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def get_metadata_file_paths(self, distinfo_path):
+ req_metadata_files = ['METADATA', 'RECORD', 'INSTALLER']
+ metadata_file_paths = []
+ for metadata_file in req_metadata_files:
+ path = os.path.join(distinfo_path, metadata_file)
+ metadata_file_paths.append(path)
+ return metadata_file_paths
+
+ def test_egginfo_to_distinfo_setuptools(self):
+ distinfo = 'hello-0.1.1-py3.3.dist-info'
+ egginfo = 'hello-0.1.1-py3.3.egg-info'
+ dirs = [egginfo]
+ files = ['hello.py', 'hello.pyc']
+ extra_metadata = ['dependency_links.txt', 'entry_points.txt',
+ 'not-zip-safe', 'PKG-INFO', 'top_level.txt',
+ 'SOURCES.txt']
+ for f in extra_metadata:
+ files.append(os.path.join(egginfo, f))
+
+ tempdir, record_file = self.build_dist_tree(files, dirs)
+ distinfo_path = os.path.join(tempdir, distinfo)
+ egginfo_path = os.path.join(tempdir, egginfo)
+ metadata_file_paths = self.get_metadata_file_paths(distinfo_path)
+
+ egginfo_to_distinfo(record_file)
+ # test that directories and files get created
+ self.assertTrue(os.path.isdir(distinfo_path))
+ self.assertTrue(os.path.isdir(egginfo_path))
+
+ for mfile in metadata_file_paths:
+ self.assertTrue(os.path.isfile(mfile))
+
+ def test_egginfo_to_distinfo_distutils(self):
+ distinfo = 'hello-0.1.1-py3.3.dist-info'
+ egginfo = 'hello-0.1.1-py3.3.egg-info'
+ # egginfo is a file in distutils which contains the metadata
+ files = ['hello.py', 'hello.pyc', egginfo]
+
+ tempdir, record_file = self.build_dist_tree(files, dirs=[])
+ distinfo_path = os.path.join(tempdir, distinfo)
+ egginfo_path = os.path.join(tempdir, egginfo)
+ metadata_file_paths = self.get_metadata_file_paths(distinfo_path)
+
+ egginfo_to_distinfo(record_file)
+ # test that directories and files get created
+ self.assertTrue(os.path.isdir(distinfo_path))
+ self.assertTrue(os.path.isfile(egginfo_path))
+
+ for mfile in metadata_file_paths:
+ self.assertTrue(os.path.isfile(mfile))
+
+ def build_dist_tree(self, files, dirs):
+ tempdir = self.mkdtemp()
+ record_file_path = os.path.join(tempdir, 'RECORD')
+ file_paths, dir_paths = ([], [])
+ for d in dirs:
+ path = os.path.join(tempdir, d)
+ os.makedirs(path)
+ dir_paths.append(path)
+ for f in files:
+ path = os.path.join(tempdir, f)
+ _f = open(path, 'w')
+ _f.write(f)
+ _f.close()
+ file_paths.append(path)
+
+ record_file = open(record_file_path, 'w')
+ for fpath in file_paths:
+ record_file.write(fpath + '\n')
+ for dpath in dir_paths:
+ record_file.write(dpath + '\n')
+ record_file.close()
+
+ return (tempdir, record_file_path)
+
+
+class PackagingLibChecks(support.TempdirManager,
+ support.LoggingCatcher,
+ unittest.TestCase):
+
+ def setUp(self):
+ super(PackagingLibChecks, self).setUp()
+ self._empty_dir = self.mkdtemp()
+
+ def test_empty_package_is_not_based_on_anything(self):
+ self.assertFalse(is_setuptools(self._empty_dir))
+ self.assertFalse(is_distutils(self._empty_dir))
+ self.assertFalse(is_packaging(self._empty_dir))
+
+ def test_setup_py_importing_setuptools_is_setuptools_based(self):
+ self.assertTrue(is_setuptools(self._setuptools_setup_py_pkg()))
+
+ def test_egg_info_dir_and_setup_py_is_setuptools_based(self):
+ self.assertTrue(is_setuptools(self._setuptools_egg_info_pkg()))
+
+ def test_egg_info_and_non_setuptools_setup_py_is_setuptools_based(self):
+ self.assertTrue(is_setuptools(self._egg_info_with_no_setuptools()))
+
+ def test_setup_py_not_importing_setuptools_is_not_setuptools_based(self):
+ self.assertFalse(is_setuptools(self._random_setup_py_pkg()))
+
+ def test_setup_py_importing_distutils_is_distutils_based(self):
+ self.assertTrue(is_distutils(self._distutils_setup_py_pkg()))
+
+ def test_pkg_info_file_and_setup_py_is_distutils_based(self):
+ self.assertTrue(is_distutils(self._distutils_pkg_info()))
+
+ def test_pkg_info_and_non_distutils_setup_py_is_distutils_based(self):
+ self.assertTrue(is_distutils(self._pkg_info_with_no_distutils()))
+
+ def test_setup_py_not_importing_distutils_is_not_distutils_based(self):
+ self.assertFalse(is_distutils(self._random_setup_py_pkg()))
+
+ def test_setup_cfg_with_no_metadata_section_is_not_packaging_based(self):
+ self.assertFalse(is_packaging(self._setup_cfg_with_no_metadata_pkg()))
+
+ def test_setup_cfg_with_valid_metadata_section_is_packaging_based(self):
+ self.assertTrue(is_packaging(self._valid_setup_cfg_pkg()))
+
+ def test_setup_cfg_and_invalid_setup_cfg_is_not_packaging_based(self):
+ self.assertFalse(is_packaging(self._invalid_setup_cfg_pkg()))
+
+ def test_get_install_method_with_setuptools_pkg(self):
+ path = self._setuptools_setup_py_pkg()
+ self.assertEqual("setuptools", get_install_method(path))
+
+ def test_get_install_method_with_distutils_pkg(self):
+ path = self._distutils_pkg_info()
+ self.assertEqual("distutils", get_install_method(path))
+
+ def test_get_install_method_with_packaging_pkg(self):
+ path = self._valid_setup_cfg_pkg()
+ self.assertEqual("packaging", get_install_method(path))
+
+ def test_get_install_method_with_unknown_pkg(self):
+ path = self._invalid_setup_cfg_pkg()
+ self.assertRaises(InstallationException, get_install_method, path)
+
+ def test_is_setuptools_logs_setup_py_text_found(self):
+ is_setuptools(self._setuptools_setup_py_pkg())
+ expected = ['setup.py file found', 'found setuptools text in setup.py']
+ self.assertEqual(expected, self.get_logs(logging.INFO))
+
+ def test_is_setuptools_logs_setup_py_text_not_found(self):
+ directory = self._random_setup_py_pkg()
+ is_setuptools(directory)
+ info_expected = ['setup.py file found']
+ warn_expected = ['no egg-info directory found',
+ 'no setuptools text found in setup.py']
+ self.assertEqual(info_expected, self.get_logs(logging.INFO))
+ self.assertEqual(warn_expected, self.get_logs(logging.WARN))
+
+ def test_is_setuptools_logs_egg_info_dir_found(self):
+ is_setuptools(self._setuptools_egg_info_pkg())
+ expected = ['setup.py file found', 'found egg-info directory']
+ self.assertEqual(expected, self.get_logs(logging.INFO))
+
+ def test_is_distutils_logs_setup_py_text_found(self):
+ is_distutils(self._distutils_setup_py_pkg())
+ expected = ['setup.py file found', 'found distutils text in setup.py']
+ self.assertEqual(expected, self.get_logs(logging.INFO))
+
+ def test_is_distutils_logs_setup_py_text_not_found(self):
+ directory = self._random_setup_py_pkg()
+ is_distutils(directory)
+ info_expected = ['setup.py file found']
+ warn_expected = ['no PKG-INFO file found',
+ 'no distutils text found in setup.py']
+ self.assertEqual(info_expected, self.get_logs(logging.INFO))
+ self.assertEqual(warn_expected, self.get_logs(logging.WARN))
+
+ def test_is_distutils_logs_pkg_info_file_found(self):
+ is_distutils(self._distutils_pkg_info())
+ expected = ['setup.py file found', 'PKG-INFO file found']
+ self.assertEqual(expected, self.get_logs(logging.INFO))
+
+ def test_is_packaging_logs_setup_cfg_found(self):
+ is_packaging(self._valid_setup_cfg_pkg())
+ expected = ['setup.cfg file found']
+ self.assertEqual(expected, self.get_logs(logging.INFO))
+
+ def test_is_packaging_logs_setup_cfg_not_found(self):
+ is_packaging(self._empty_dir)
+ expected = ['no setup.cfg file found']
+ self.assertEqual(expected, self.get_logs(logging.WARN))
+
+ def _write_setuptools_setup_py(self, directory):
+ self.write_file((directory, 'setup.py'),
+ "from setuptools import setup")
+
+ def _write_distutils_setup_py(self, directory):
+ self.write_file([directory, 'setup.py'],
+ "from distutils.core import setup")
+
+ def _write_packaging_setup_cfg(self, directory):
+ self.write_file([directory, 'setup.cfg'],
+ ("[metadata]\n"
+ "name = mypackage\n"
+ "version = 0.1.0\n"))
+
+ def _setuptools_setup_py_pkg(self):
+ tmp = self.mkdtemp()
+ self._write_setuptools_setup_py(tmp)
+ return tmp
+
+ def _distutils_setup_py_pkg(self):
+ tmp = self.mkdtemp()
+ self._write_distutils_setup_py(tmp)
+ return tmp
+
+ def _valid_setup_cfg_pkg(self):
+ tmp = self.mkdtemp()
+ self._write_packaging_setup_cfg(tmp)
+ return tmp
+
+ def _setuptools_egg_info_pkg(self):
+ tmp = self.mkdtemp()
+ self._write_setuptools_setup_py(tmp)
+ tempfile.mkdtemp(suffix='.egg-info', dir=tmp)
+ return tmp
+
+ def _distutils_pkg_info(self):
+ tmp = self._distutils_setup_py_pkg()
+ self.write_file([tmp, 'PKG-INFO'], '')
+ return tmp
+
+ def _setup_cfg_with_no_metadata_pkg(self):
+ tmp = self.mkdtemp()
+ self.write_file([tmp, 'setup.cfg'],
+ ("[othersection]\n"
+ "foo = bar\n"))
+ return tmp
+
+ def _invalid_setup_cfg_pkg(self):
+ tmp = self.mkdtemp()
+ self.write_file([tmp, 'setup.cfg'],
+ ("[metadata]\n"
+ "name = john\n"
+ "last_name = doe\n"))
+ return tmp
+
+ def _egg_info_with_no_setuptools(self):
+ tmp = self._random_setup_py_pkg()
+ tempfile.mkdtemp(suffix='.egg-info', dir=tmp)
+ return tmp
+
+ def _pkg_info_with_no_distutils(self):
+ tmp = self._random_setup_py_pkg()
+ self.write_file([tmp, 'PKG-INFO'], '')
+ return tmp
+
+ def _random_setup_py_pkg(self):
+ tmp = self.mkdtemp()
+ self.write_file((tmp, 'setup.py'), "from mypackage import setup")
+ return tmp
+
+
+def test_suite():
+ suite = unittest.makeSuite(UtilTestCase)
+ suite.addTest(unittest.makeSuite(GlobTestCase))
+ suite.addTest(unittest.makeSuite(EggInfoToDistInfoTestCase))
+ suite.addTest(unittest.makeSuite(PackagingLibChecks))
+ return suite
+
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")
diff --git a/Lib/packaging/tests/test_version.py b/Lib/packaging/tests/test_version.py
new file mode 100644
index 0000000..f94c800
--- /dev/null
+++ b/Lib/packaging/tests/test_version.py
@@ -0,0 +1,252 @@
+"""Tests for packaging.version."""
+import doctest
+import os
+
+from packaging.version import NormalizedVersion as V
+from packaging.version import HugeMajorVersionNumError, IrrationalVersionError
+from packaging.version import suggest_normalized_version as suggest
+from packaging.version import VersionPredicate
+from packaging.tests import unittest
+
+
+class VersionTestCase(unittest.TestCase):
+
+ versions = ((V('1.0'), '1.0'),
+ (V('1.1'), '1.1'),
+ (V('1.2.3'), '1.2.3'),
+ (V('1.2'), '1.2'),
+ (V('1.2.3a4'), '1.2.3a4'),
+ (V('1.2c4'), '1.2c4'),
+ (V('1.2.3.4'), '1.2.3.4'),
+ (V('1.2.3.4.0b3'), '1.2.3.4b3'),
+ (V('1.2.0.0.0'), '1.2'),
+ (V('1.0.dev345'), '1.0.dev345'),
+ (V('1.0.post456.dev623'), '1.0.post456.dev623'))
+
+ def test_repr(self):
+
+ self.assertEqual(repr(V('1.0')), "NormalizedVersion('1.0')")
+
+ def test_basic_versions(self):
+
+ for v, s in self.versions:
+ self.assertEqual(str(v), s)
+
+ def test_hash(self):
+
+ for v, s in self.versions:
+ self.assertEqual(hash(v), hash(V(s)))
+
+ versions = set([v for v, s in self.versions])
+ for v, s in self.versions:
+ self.assertIn(v, versions)
+
+ self.assertEqual(set([V('1.0')]), set([V('1.0'), V('1.0')]))
+
+ def test_from_parts(self):
+
+ for v, s in self.versions:
+ parts = v.parts
+ v2 = V.from_parts(*v.parts)
+ self.assertEqual(v, v2)
+ self.assertEqual(str(v), str(v2))
+
+ def test_irrational_versions(self):
+
+ irrational = ('1', '1.2a', '1.2.3b', '1.02', '1.2a03',
+ '1.2a3.04', '1.2.dev.2', '1.2dev', '1.2.dev',
+ '1.2.dev2.post2', '1.2.post2.dev3.post4')
+
+ for s in irrational:
+ self.assertRaises(IrrationalVersionError, V, s)
+
+ def test_huge_version(self):
+
+ self.assertEqual(str(V('1980.0')), '1980.0')
+ self.assertRaises(HugeMajorVersionNumError, V, '1981.0')
+ self.assertEqual(str(V('1981.0', error_on_huge_major_num=False)),
+ '1981.0')
+
+ def test_comparison(self):
+ comparison_doctest_string = r"""
+ >>> V('1.2.0') == '1.2'
+ Traceback (most recent call last):
+ ...
+ TypeError: cannot compare NormalizedVersion and str
+
+ >>> V('1.2') < '1.3'
+ Traceback (most recent call last):
+ ...
+ TypeError: cannot compare NormalizedVersion and str
+
+ >>> V('1.2.0') == V('1.2')
+ True
+ >>> V('1.2.0') == V('1.2.3')
+ False
+ >>> V('1.2.0') != V('1.2.3')
+ True
+ >>> V('1.2.0') < V('1.2.3')
+ True
+ >>> V('1.2.0') < V('1.2.0')
+ False
+ >>> V('1.2.0') <= V('1.2.0')
+ True
+ >>> V('1.2.0') <= V('1.2.3')
+ True
+ >>> V('1.2.3') <= V('1.2.0')
+ False
+ >>> V('1.2.0') >= V('1.2.0')
+ True
+ >>> V('1.2.3') >= V('1.2.0')
+ True
+ >>> V('1.2.0') >= V('1.2.3')
+ False
+ >>> (V('1.0') > V('1.0b2'))
+ True
+ >>> (V('1.0') > V('1.0c2') > V('1.0c1') > V('1.0b2') > V('1.0b1')
+ ... > V('1.0a2') > V('1.0a1'))
+ True
+ >>> (V('1.0.0') > V('1.0.0c2') > V('1.0.0c1') > V('1.0.0b2') > V('1.0.0b1')
+ ... > V('1.0.0a2') > V('1.0.0a1'))
+ True
+
+ >>> V('1.0') < V('1.0.post456.dev623')
+ True
+
+ >>> V('1.0.post456.dev623') < V('1.0.post456') < V('1.0.post1234')
+ True
+
+ >>> (V('1.0a1')
+ ... < V('1.0a2.dev456')
+ ... < V('1.0a2')
+ ... < V('1.0a2.1.dev456') # e.g. need to do a quick post release on 1.0a2
+ ... < V('1.0a2.1')
+ ... < V('1.0b1.dev456')
+ ... < V('1.0b2')
+ ... < V('1.0c1.dev456')
+ ... < V('1.0c1')
+ ... < V('1.0.dev7')
+ ... < V('1.0.dev18')
+ ... < V('1.0.dev456')
+ ... < V('1.0.dev1234')
+ ... < V('1.0')
+ ... < V('1.0.post456.dev623') # development version of a post release
+ ... < V('1.0.post456'))
+ True
+ """
+ doctest.script_from_examples(comparison_doctest_string)
+
+ def test_suggest_normalized_version(self):
+
+ self.assertEqual(suggest('1.0'), '1.0')
+ self.assertEqual(suggest('1.0-alpha1'), '1.0a1')
+ self.assertEqual(suggest('1.0c2'), '1.0c2')
+ self.assertEqual(suggest('walla walla washington'), None)
+ self.assertEqual(suggest('2.4c1'), '2.4c1')
+ self.assertEqual(suggest('v1.0'), '1.0')
+
+ # from setuptools
+ self.assertEqual(suggest('0.4a1.r10'), '0.4a1.post10')
+ self.assertEqual(suggest('0.7a1dev-r66608'), '0.7a1.dev66608')
+ self.assertEqual(suggest('0.6a9.dev-r41475'), '0.6a9.dev41475')
+ self.assertEqual(suggest('2.4preview1'), '2.4c1')
+ self.assertEqual(suggest('2.4pre1'), '2.4c1')
+ self.assertEqual(suggest('2.1-rc2'), '2.1c2')
+
+ # from pypi
+ self.assertEqual(suggest('0.1dev'), '0.1.dev0')
+ self.assertEqual(suggest('0.1.dev'), '0.1.dev0')
+
+ # we want to be able to parse Twisted
+ # development versions are like post releases in Twisted
+ self.assertEqual(suggest('9.0.0+r2363'), '9.0.0.post2363')
+
+ # pre-releases are using markers like "pre1"
+ self.assertEqual(suggest('9.0.0pre1'), '9.0.0c1')
+
+ # we want to be able to parse Tcl-TK
+ # they us "p1" "p2" for post releases
+ self.assertEqual(suggest('1.4p1'), '1.4.post1')
+
+ def test_predicate(self):
+ # VersionPredicate knows how to parse stuff like:
+ #
+ # Project (>=version, ver2)
+
+ predicates = ('zope.interface (>3.5.0)',
+ 'AnotherProject (3.4)',
+ 'OtherProject (<3.0)',
+ 'NoVersion',
+ 'Hey (>=2.5,<2.7)')
+
+ for predicate in predicates:
+ v = VersionPredicate(predicate)
+
+ self.assertTrue(VersionPredicate('Hey (>=2.5,<2.7)').match('2.6'))
+ self.assertTrue(VersionPredicate('Ho').match('2.6'))
+ self.assertFalse(VersionPredicate('Hey (>=2.5,!=2.6,<2.7)').match('2.6'))
+ self.assertTrue(VersionPredicate('Ho (<3.0)').match('2.6'))
+ self.assertTrue(VersionPredicate('Ho (<3.0,!=2.5)').match('2.6.0'))
+ self.assertFalse(VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.0'))
+ self.assertTrue(VersionPredicate('Ho (2.5)').match('2.5.4'))
+ self.assertFalse(VersionPredicate('Ho (!=2.5)').match('2.5.2'))
+ self.assertTrue(VersionPredicate('Hey (<=2.5)').match('2.5.9'))
+ self.assertFalse(VersionPredicate('Hey (<=2.5)').match('2.6.0'))
+ self.assertTrue(VersionPredicate('Hey (>=2.5)').match('2.5.1'))
+
+ self.assertRaises(ValueError, VersionPredicate, '')
+
+ self.assertTrue(VersionPredicate('Hey 2.5').match('2.5.1'))
+
+ # XXX need to silent the micro version in this case
+ self.assertFalse(VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.3'))
+
+ # Make sure a predicate that ends with a number works
+ self.assertTrue(VersionPredicate('virtualenv5 (1.0)').match('1.0'))
+ self.assertTrue(VersionPredicate('virtualenv5').match('1.0'))
+ self.assertTrue(VersionPredicate('vi5two').match('1.0'))
+ self.assertTrue(VersionPredicate('5two').match('1.0'))
+ self.assertTrue(VersionPredicate('vi5two 1.0').match('1.0'))
+ self.assertTrue(VersionPredicate('5two 1.0').match('1.0'))
+
+ # test repr
+ for predicate in predicates:
+ self.assertEqual(str(VersionPredicate(predicate)), predicate)
+
+ def test_predicate_name(self):
+ # Test that names are parsed the right way
+
+ self.assertEqual('Hey', VersionPredicate('Hey (<1.1)').name)
+ self.assertEqual('Foo-Bar', VersionPredicate('Foo-Bar (1.1)').name)
+ self.assertEqual('Foo Bar', VersionPredicate('Foo Bar (1.1)').name)
+
+ def test_is_final(self):
+ # VersionPredicate knows is a distribution is a final one or not.
+ final_versions = ('1.0', '1.0.post456')
+ other_versions = ('1.0.dev1', '1.0a2', '1.0c3')
+
+ for version in final_versions:
+ self.assertTrue(V(version).is_final)
+ for version in other_versions:
+ self.assertFalse(V(version).is_final)
+
+
+class VersionWhiteBoxTestCase(unittest.TestCase):
+
+ def test_parse_numdots(self):
+ # For code coverage completeness, as pad_zeros_length can't be set or
+ # influenced from the public interface
+ self.assertEqual(V('1.0')._parse_numdots('1.0', '1.0',
+ pad_zeros_length=3),
+ [1, 0, 0])
+
+
+def test_suite():
+ #README = os.path.join(os.path.dirname(__file__), 'README.txt')
+ #suite = [doctest.DocFileSuite(README), unittest.makeSuite(VersionTestCase)]
+ suite = [unittest.makeSuite(VersionTestCase),
+ unittest.makeSuite(VersionWhiteBoxTestCase)]
+ return unittest.TestSuite(suite)
+
+if __name__ == "__main__":
+ unittest.main(defaultTest="test_suite")