mirror of
https://github.com/kennethreitz/heroku-buildpack-python.git
synced 2026-06-05 23:10:16 +00:00
test
This commit is contained in:
+1
-1
@@ -166,7 +166,7 @@ if [ ! "$SKIP_INSTALL" ]; then
|
||||
ls /app/.heroku/python/bin
|
||||
hash -r
|
||||
which python
|
||||
python $ROOT_DIR/vendor/distribute-0.6.31/setup.py install
|
||||
python $ROOT_DIR/vendor/distribute-0.6.32/distribute_setup.py
|
||||
echo
|
||||
echo
|
||||
echo
|
||||
|
||||
Vendored
-459
@@ -1,459 +0,0 @@
|
||||
=======
|
||||
CHANGES
|
||||
=======
|
||||
|
||||
------
|
||||
0.6.31
|
||||
------
|
||||
|
||||
* Issue #303: Make sure the manifest only ever contains UTF-8 in Python 3.
|
||||
* Issue #329: Properly close files created by tests for compatibility with
|
||||
Jython.
|
||||
* Work around Jython bugs `#1980 <http://bugs.jython.org/issue1980>`_ and
|
||||
`#1981 <http://bugs.jython.org/issue1981>`_.
|
||||
* Issue #334: Provide workaround for packages that reference `sys.__stdout__`
|
||||
such as numpy does. This change should address
|
||||
`virtualenv #359 <https://github.com/pypa/virtualenv/issues/359>`_ as long
|
||||
as the system encoding is UTF-8 or the IO encoding is specified in the
|
||||
environment, i.e.::
|
||||
|
||||
PYTHONIOENCODING=utf8 pip install numpy
|
||||
|
||||
* Fix for encoding issue when installing from Windows executable on Python 3.
|
||||
* Issue #323: Allow `setup_requires` requirements to supercede installed
|
||||
requirements. Added some new keyword arguments to existing pkg_resources
|
||||
methods. Also had to updated how __path__ is handled for namespace packages
|
||||
to ensure that when a new egg distribution containing a namespace package is
|
||||
placed on sys.path, the entries in __path__ are found in the same order they
|
||||
would have been in had that egg been on the path when pkg_resources was
|
||||
first imported.
|
||||
|
||||
------
|
||||
0.6.30
|
||||
------
|
||||
|
||||
* Issue #328: Clean up temporary directories in distribute_setup.py.
|
||||
* Fix fatal bug in distribute_setup.py.
|
||||
|
||||
------
|
||||
0.6.29
|
||||
------
|
||||
|
||||
* Pull Request #14: Honor file permissions in zip files.
|
||||
* Issue #327: Merged pull request #24 to fix a dependency problem with pip.
|
||||
* Merged pull request #23 to fix https://github.com/pypa/virtualenv/issues/301.
|
||||
* If Sphinx is installed, the `upload_docs` command now runs `build_sphinx`
|
||||
to produce uploadable documentation.
|
||||
* Issue #326: `upload_docs` provided mangled auth credentials under Python 3.
|
||||
* Issue #320: Fix check for "createable" in distribute_setup.py.
|
||||
* Issue #305: Remove a warning that was triggered during normal operations.
|
||||
* Issue #311: Print metadata in UTF-8 independent of platform.
|
||||
* Issue #303: Read manifest file with UTF-8 encoding under Python 3.
|
||||
* Issue #301: Allow to run tests of namespace packages when using 2to3.
|
||||
* Issue #304: Prevent import loop in site.py under Python 3.3.
|
||||
* Issue #283: Reenable scanning of `*.pyc` / `*.pyo` files on Python 3.3.
|
||||
* Issue #299: The develop command didn't work on Python 3, when using 2to3,
|
||||
as the egg link would go to the Python 2 source. Linking to the 2to3'd code
|
||||
in build/lib makes it work, although you will have to rebuild the module
|
||||
before testing it.
|
||||
* Issue #306: Even if 2to3 is used, we build in-place under Python 2.
|
||||
* Issue #307: Prints the full path when .svn/entries is broken.
|
||||
* Issue #313: Support for sdist subcommands (Python 2.7)
|
||||
* Issue #314: test_local_index() would fail an OS X.
|
||||
* Issue #310: Non-ascii characters in a namespace __init__.py causes errors.
|
||||
* Issue #218: Improved documentation on behavior of `package_data` and
|
||||
`include_package_data`. Files indicated by `package_data` are now included
|
||||
in the manifest.
|
||||
* `distribute_setup.py` now allows a `--download-base` argument for retrieving
|
||||
distribute from a specified location.
|
||||
|
||||
------
|
||||
0.6.28
|
||||
------
|
||||
|
||||
* Issue #294: setup.py can now be invoked from any directory.
|
||||
* Scripts are now installed honoring the umask.
|
||||
* Added support for .dist-info directories.
|
||||
* Issue #283: Fix and disable scanning of `*.pyc` / `*.pyo` files on
|
||||
Python 3.3.
|
||||
|
||||
------
|
||||
0.6.27
|
||||
------
|
||||
|
||||
* Support current snapshots of CPython 3.3.
|
||||
* Distribute now recognizes README.rst as a standard, default readme file.
|
||||
* Exclude 'encodings' modules when removing modules from sys.modules.
|
||||
Workaround for #285.
|
||||
* Issue #231: Don't fiddle with system python when used with buildout
|
||||
(bootstrap.py)
|
||||
|
||||
------
|
||||
0.6.26
|
||||
------
|
||||
|
||||
* Issue #183: Symlinked files are now extracted from source distributions.
|
||||
* Issue #227: Easy_install fetch parameters are now passed during the
|
||||
installation of a source distribution; now fulfillment of setup_requires
|
||||
dependencies will honor the parameters passed to easy_install.
|
||||
|
||||
------
|
||||
0.6.25
|
||||
------
|
||||
|
||||
* Issue #258: Workaround a cache issue
|
||||
* Issue #260: distribute_setup.py now accepts the --user parameter for
|
||||
Python 2.6 and later.
|
||||
* Issue #262: package_index.open_with_auth no longer throws LookupError
|
||||
on Python 3.
|
||||
* Issue #269: AttributeError when an exception occurs reading Manifest.in
|
||||
on late releases of Python.
|
||||
* Issue #272: Prevent TypeError when namespace package names are unicode
|
||||
and single-install-externally-managed is used. Also fixes PIP issue
|
||||
449.
|
||||
* Issue #273: Legacy script launchers now install with Python2/3 support.
|
||||
|
||||
------
|
||||
0.6.24
|
||||
------
|
||||
|
||||
* Issue #249: Added options to exclude 2to3 fixers
|
||||
|
||||
------
|
||||
0.6.23
|
||||
------
|
||||
|
||||
* Issue #244: Fixed a test
|
||||
* Issue #243: Fixed a test
|
||||
* Issue #239: Fixed a test
|
||||
* Issue #240: Fixed a test
|
||||
* Issue #241: Fixed a test
|
||||
* Issue #237: Fixed a test
|
||||
* Issue #238: easy_install now uses 64bit executable wrappers on 64bit Python
|
||||
* Issue #208: Fixed parsed_versions, it now honors post-releases as noted in the documentation
|
||||
* Issue #207: Windows cli and gui wrappers pass CTRL-C to child python process
|
||||
* Issue #227: easy_install now passes its arguments to setup.py bdist_egg
|
||||
* Issue #225: Fixed a NameError on Python 2.5, 2.4
|
||||
|
||||
------
|
||||
0.6.21
|
||||
------
|
||||
|
||||
* Issue #225: FIxed a regression on py2.4
|
||||
|
||||
------
|
||||
0.6.20
|
||||
------
|
||||
|
||||
* Issue #135: Include url in warning when processing URLs in package_index.
|
||||
* Issue #212: Fix issue where easy_instal fails on Python 3 on windows installer.
|
||||
* Issue #213: Fix typo in documentation.
|
||||
|
||||
------
|
||||
0.6.19
|
||||
------
|
||||
|
||||
* Issue 206: AttributeError: 'HTTPMessage' object has no attribute 'getheaders'
|
||||
|
||||
------
|
||||
0.6.18
|
||||
------
|
||||
|
||||
* Issue 210: Fixed a regression introduced by Issue 204 fix.
|
||||
|
||||
------
|
||||
0.6.17
|
||||
------
|
||||
|
||||
* Support 'DISTRIBUTE_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT' environment
|
||||
variable to allow to disable installation of easy_install-${version} script.
|
||||
* Support Python >=3.1.4 and >=3.2.1.
|
||||
* Issue 204: Don't try to import the parent of a namespace package in
|
||||
declare_namespace
|
||||
* Issue 196: Tolerate responses with multiple Content-Length headers
|
||||
* Issue 205: Sandboxing doesn't preserve working_set. Leads to setup_requires
|
||||
problems.
|
||||
|
||||
------
|
||||
0.6.16
|
||||
------
|
||||
|
||||
* Builds sdist gztar even on Windows (avoiding Issue 193).
|
||||
* Issue 192: Fixed metadata omitted on Windows when package_dir
|
||||
specified with forward-slash.
|
||||
* Issue 195: Cython build support.
|
||||
* Issue 200: Issues with recognizing 64-bit packages on Windows.
|
||||
|
||||
------
|
||||
0.6.15
|
||||
------
|
||||
|
||||
* Fixed typo in bdist_egg
|
||||
* Several issues under Python 3 has been solved.
|
||||
* Issue 146: Fixed missing DLL files after easy_install of windows exe package.
|
||||
|
||||
------
|
||||
0.6.14
|
||||
------
|
||||
|
||||
* Issue 170: Fixed unittest failure. Thanks to Toshio.
|
||||
* Issue 171: Fixed race condition in unittests cause deadlocks in test suite.
|
||||
* Issue 143: Fixed a lookup issue with easy_install.
|
||||
Thanks to David and Zooko.
|
||||
* Issue 174: Fixed the edit mode when its used with setuptools itself
|
||||
|
||||
------
|
||||
0.6.13
|
||||
------
|
||||
|
||||
* Issue 160: 2.7 gives ValueError("Invalid IPv6 URL")
|
||||
* Issue 150: Fixed using ~/.local even in a --no-site-packages virtualenv
|
||||
* Issue 163: scan index links before external links, and don't use the md5 when
|
||||
comparing two distributions
|
||||
|
||||
------
|
||||
0.6.12
|
||||
------
|
||||
|
||||
* Issue 149: Fixed various failures on 2.3/2.4
|
||||
|
||||
------
|
||||
0.6.11
|
||||
------
|
||||
|
||||
* Found another case of SandboxViolation - fixed
|
||||
* Issue 15 and 48: Introduced a socket timeout of 15 seconds on url openings
|
||||
* Added indexsidebar.html into MANIFEST.in
|
||||
* Issue 108: Fixed TypeError with Python3.1
|
||||
* Issue 121: Fixed --help install command trying to actually install.
|
||||
* Issue 112: Added an os.makedirs so that Tarek's solution will work.
|
||||
* Issue 133: Added --no-find-links to easy_install
|
||||
* Added easy_install --user
|
||||
* Issue 100: Fixed develop --user not taking '.' in PYTHONPATH into account
|
||||
* Issue 134: removed spurious UserWarnings. Patch by VanLindberg
|
||||
* Issue 138: cant_write_to_target error when setup_requires is used.
|
||||
* Issue 147: respect the sys.dont_write_bytecode flag
|
||||
|
||||
------
|
||||
0.6.10
|
||||
------
|
||||
|
||||
* Reverted change made for the DistributionNotFound exception because
|
||||
zc.buildout uses the exception message to get the name of the
|
||||
distribution.
|
||||
|
||||
-----
|
||||
0.6.9
|
||||
-----
|
||||
|
||||
* Issue 90: unknown setuptools version can be added in the working set
|
||||
* Issue 87: setupt.py doesn't try to convert distribute_setup.py anymore
|
||||
Initial Patch by arfrever.
|
||||
* Issue 89: added a side bar with a download link to the doc.
|
||||
* Issue 86: fixed missing sentence in pkg_resources doc.
|
||||
* Added a nicer error message when a DistributionNotFound is raised.
|
||||
* Issue 80: test_develop now works with Python 3.1
|
||||
* Issue 93: upload_docs now works if there is an empty sub-directory.
|
||||
* Issue 70: exec bit on non-exec files
|
||||
* Issue 99: now the standalone easy_install command doesn't uses a
|
||||
"setup.cfg" if any exists in the working directory. It will use it
|
||||
only if triggered by ``install_requires`` from a setup.py call
|
||||
(install, develop, etc).
|
||||
* Issue 101: Allowing ``os.devnull`` in Sandbox
|
||||
* Issue 92: Fixed the "no eggs" found error with MacPort
|
||||
(platform.mac_ver() fails)
|
||||
* Issue 103: test_get_script_header_jython_workaround not run
|
||||
anymore under py3 with C or POSIX local. Contributed by Arfrever.
|
||||
* Issue 104: remvoved the assertion when the installation fails,
|
||||
with a nicer message for the end user.
|
||||
* Issue 100: making sure there's no SandboxViolation when
|
||||
the setup script patches setuptools.
|
||||
|
||||
-----
|
||||
0.6.8
|
||||
-----
|
||||
|
||||
* Added "check_packages" in dist. (added in Setuptools 0.6c11)
|
||||
* Fixed the DONT_PATCH_SETUPTOOLS state.
|
||||
|
||||
-----
|
||||
0.6.7
|
||||
-----
|
||||
|
||||
* Issue 58: Added --user support to the develop command
|
||||
* Issue 11: Generated scripts now wrap their call to the script entry point
|
||||
in the standard "if name == 'main'"
|
||||
* Added the 'DONT_PATCH_SETUPTOOLS' environment variable, so virtualenv
|
||||
can drive an installation that doesn't patch a global setuptools.
|
||||
* Reviewed unladen-swallow specific change from
|
||||
http://code.google.com/p/unladen-swallow/source/detail?spec=svn875&r=719
|
||||
and determined that it no longer applies. Distribute should work fine with
|
||||
Unladen Swallow 2009Q3.
|
||||
* Issue 21: Allow PackageIndex.open_url to gracefully handle all cases of a
|
||||
httplib.HTTPException instead of just InvalidURL and BadStatusLine.
|
||||
* Removed virtual-python.py from this distribution and updated documentation
|
||||
to point to the actively maintained virtualenv instead.
|
||||
* Issue 64: use_setuptools no longer rebuilds the distribute egg every
|
||||
time it is run
|
||||
* use_setuptools now properly respects the requested version
|
||||
* use_setuptools will no longer try to import a distribute egg for the
|
||||
wrong Python version
|
||||
* Issue 74: no_fake should be True by default.
|
||||
* Issue 72: avoid a bootstrapping issue with easy_install -U
|
||||
|
||||
-----
|
||||
0.6.6
|
||||
-----
|
||||
|
||||
* Unified the bootstrap file so it works on both py2.x and py3k without 2to3
|
||||
(patch by Holger Krekel)
|
||||
|
||||
-----
|
||||
0.6.5
|
||||
-----
|
||||
|
||||
* Issue 65: cli.exe and gui.exe are now generated at build time,
|
||||
depending on the platform in use.
|
||||
|
||||
* Issue 67: Fixed doc typo (PEP 381/382)
|
||||
|
||||
* Distribute no longer shadows setuptools if we require a 0.7-series
|
||||
setuptools. And an error is raised when installing a 0.7 setuptools with
|
||||
distribute.
|
||||
|
||||
* When run from within buildout, no attempt is made to modify an existing
|
||||
setuptools egg, whether in a shared egg directory or a system setuptools.
|
||||
|
||||
* Fixed a hole in sandboxing allowing builtin file to write outside of
|
||||
the sandbox.
|
||||
|
||||
-----
|
||||
0.6.4
|
||||
-----
|
||||
|
||||
* Added the generation of `distribute_setup_3k.py` during the release.
|
||||
This closes issue #52.
|
||||
|
||||
* Added an upload_docs command to easily upload project documentation to
|
||||
PyPI's http://packages.python.org. This close issue #56.
|
||||
|
||||
* Fixed a bootstrap bug on the use_setuptools() API.
|
||||
|
||||
-----
|
||||
0.6.3
|
||||
-----
|
||||
|
||||
setuptools
|
||||
==========
|
||||
|
||||
* Fixed a bunch of calls to file() that caused crashes on Python 3.
|
||||
|
||||
bootstrapping
|
||||
=============
|
||||
|
||||
* Fixed a bug in sorting that caused bootstrap to fail on Python 3.
|
||||
|
||||
-----
|
||||
0.6.2
|
||||
-----
|
||||
|
||||
setuptools
|
||||
==========
|
||||
|
||||
* Added Python 3 support; see docs/python3.txt.
|
||||
This closes http://bugs.python.org/setuptools/issue39.
|
||||
|
||||
* Added option to run 2to3 automatically when installing on Python 3.
|
||||
This closes issue #31.
|
||||
|
||||
* Fixed invalid usage of requirement.parse, that broke develop -d.
|
||||
This closes http://bugs.python.org/setuptools/issue44.
|
||||
|
||||
* Fixed script launcher for 64-bit Windows.
|
||||
This closes http://bugs.python.org/setuptools/issue2.
|
||||
|
||||
* KeyError when compiling extensions.
|
||||
This closes http://bugs.python.org/setuptools/issue41.
|
||||
|
||||
bootstrapping
|
||||
=============
|
||||
|
||||
* Fixed bootstrap not working on Windows. This closes issue #49.
|
||||
|
||||
* Fixed 2.6 dependencies. This closes issue #50.
|
||||
|
||||
* Make sure setuptools is patched when running through easy_install
|
||||
This closes http://bugs.python.org/setuptools/issue40.
|
||||
|
||||
-----
|
||||
0.6.1
|
||||
-----
|
||||
|
||||
setuptools
|
||||
==========
|
||||
|
||||
* package_index.urlopen now catches BadStatusLine and malformed url errors.
|
||||
This closes issue #16 and issue #18.
|
||||
|
||||
* zip_ok is now False by default. This closes
|
||||
http://bugs.python.org/setuptools/issue33.
|
||||
|
||||
* Fixed invalid URL error catching. http://bugs.python.org/setuptools/issue20.
|
||||
|
||||
* Fixed invalid bootstraping with easy_install installation (issue #40).
|
||||
Thanks to Florian Schulze for the help.
|
||||
|
||||
* Removed buildout/bootstrap.py. A new repository will create a specific
|
||||
bootstrap.py script.
|
||||
|
||||
|
||||
bootstrapping
|
||||
=============
|
||||
|
||||
* The boostrap process leave setuptools alone if detected in the system
|
||||
and --root or --prefix is provided, but is not in the same location.
|
||||
This closes issue #10.
|
||||
|
||||
---
|
||||
0.6
|
||||
---
|
||||
|
||||
setuptools
|
||||
==========
|
||||
|
||||
* Packages required at build time where not fully present at install time.
|
||||
This closes issue #12.
|
||||
|
||||
* Protected against failures in tarfile extraction. This closes issue #10.
|
||||
|
||||
* Made Jython api_tests.txt doctest compatible. This closes issue #7.
|
||||
|
||||
* sandbox.py replaced builtin type file with builtin function open. This
|
||||
closes issue #6.
|
||||
|
||||
* Immediately close all file handles. This closes issue #3.
|
||||
|
||||
* Added compatibility with Subversion 1.6. This references issue #1.
|
||||
|
||||
pkg_resources
|
||||
=============
|
||||
|
||||
* Avoid a call to /usr/bin/sw_vers on OSX and use the official platform API
|
||||
instead. Based on a patch from ronaldoussoren. This closes issue #5.
|
||||
|
||||
* Fixed a SandboxViolation for mkdir that could occur in certain cases.
|
||||
This closes issue #13.
|
||||
|
||||
* Allow to find_on_path on systems with tight permissions to fail gracefully.
|
||||
This closes issue #9.
|
||||
|
||||
* Corrected inconsistency between documentation and code of add_entry.
|
||||
This closes issue #8.
|
||||
|
||||
* Immediately close all file handles. This closes issue #3.
|
||||
|
||||
easy_install
|
||||
============
|
||||
|
||||
* Immediately close all file handles. This closes issue #3.
|
||||
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
============
|
||||
Contributors
|
||||
============
|
||||
|
||||
* Alex Grönholm
|
||||
* Alice Bevan-McGregor
|
||||
* Arfrever Frehtes Taifersar Arahesis
|
||||
* Christophe Combelles
|
||||
* Daniel Stutzbach
|
||||
* Daniel Holth
|
||||
* Hanno Schlichting
|
||||
* Jannis Leidel
|
||||
* Jason R. Coombs
|
||||
* Jim Fulton
|
||||
* Jonathan Lange
|
||||
* Justin Azoff
|
||||
* Lennart Regebro
|
||||
* Marc Abramowitz
|
||||
* Martin von Löwis
|
||||
* Noufal Ibrahim
|
||||
* Pete Hollobon
|
||||
* Philip Jenvey
|
||||
* Reinout van Rees
|
||||
* Robert Myers
|
||||
* Stefan H. Holek
|
||||
* Tarek Ziadé
|
||||
* Toshio Kuratomi
|
||||
|
||||
If you think you name is missing, please add it (alpha order by first name)
|
||||
|
||||
-22
@@ -1,22 +0,0 @@
|
||||
============================
|
||||
Quick notes for contributors
|
||||
============================
|
||||
|
||||
Distribute is using Mercurial.
|
||||
|
||||
Grab the code at bitbucket::
|
||||
|
||||
$ hg clone https://bitbucket.org/tarek/distribute
|
||||
|
||||
If you want to contribute changes, we recommend you fork the repository on
|
||||
bitbucket, commit the changes to your repository, and then make a pull request
|
||||
on bitbucket. If you make some changes, don't forget to:
|
||||
|
||||
- add a note in CHANGES.txt
|
||||
|
||||
And remember that 0.6 (the only development line) is only bug fixes, and the
|
||||
APIs should be fully backward compatible with Setuptools.
|
||||
|
||||
You can run the tests via::
|
||||
|
||||
$ python setup.py test
|
||||
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
recursive-include setuptools *.py *.txt *.exe
|
||||
recursive-include tests *.py *.c *.pyx *.txt
|
||||
recursive-include setuptools/tests *.html
|
||||
recursive-include docs *.py *.txt *.conf *.css *.css_t Makefile indexsidebar.html
|
||||
recursive-include _markerlib *.py
|
||||
include *.py
|
||||
include *.txt
|
||||
include MANIFEST.in
|
||||
include launcher.c
|
||||
Vendored
-837
@@ -1,837 +0,0 @@
|
||||
Metadata-Version: 1.1
|
||||
Name: distribute
|
||||
Version: 0.6.31
|
||||
Summary: Easily download, build, install, upgrade, and uninstall Python packages
|
||||
Home-page: http://packages.python.org/distribute
|
||||
Author: The fellowship of the packaging
|
||||
Author-email: distutils-sig@python.org
|
||||
License: PSF or ZPL
|
||||
Description: ===============================
|
||||
Installing and Using Distribute
|
||||
===============================
|
||||
|
||||
.. contents:: **Table of Contents**
|
||||
|
||||
-----------
|
||||
Disclaimers
|
||||
-----------
|
||||
|
||||
About the fork
|
||||
==============
|
||||
|
||||
`Distribute` is a fork of the `Setuptools` project.
|
||||
|
||||
Distribute is intended to replace Setuptools as the standard method
|
||||
for working with Python module distributions.
|
||||
|
||||
The fork has two goals:
|
||||
|
||||
- Providing a backward compatible version to replace Setuptools
|
||||
and make all distributions that depend on Setuptools work as
|
||||
before, but with less bugs and behaviorial issues.
|
||||
|
||||
This work is done in the 0.6.x series.
|
||||
|
||||
Starting with version 0.6.2, Distribute supports Python 3.
|
||||
Installing and using distribute for Python 3 code works exactly
|
||||
the same as for Python 2 code, but Distribute also helps you to support
|
||||
Python 2 and Python 3 from the same source code by letting you run 2to3
|
||||
on the code as a part of the build process, by setting the keyword parameter
|
||||
``use_2to3`` to True. See http://packages.python.org/distribute for more
|
||||
information.
|
||||
|
||||
- Refactoring the code, and releasing it in several distributions.
|
||||
This work is being done in the 0.7.x series but not yet released.
|
||||
|
||||
The roadmap is still evolving, and the page that is up-to-date is
|
||||
located at : `http://packages.python.org/distribute/roadmap`.
|
||||
|
||||
If you install `Distribute` and want to switch back for any reason to
|
||||
`Setuptools`, get to the `Uninstallation instructions`_ section.
|
||||
|
||||
More documentation
|
||||
==================
|
||||
|
||||
You can get more information in the Sphinx-based documentation, located
|
||||
at http://packages.python.org/distribute. This documentation includes the old
|
||||
Setuptools documentation that is slowly replaced, and brand new content.
|
||||
|
||||
About the installation process
|
||||
==============================
|
||||
|
||||
The `Distribute` installer modifies your installation by de-activating an
|
||||
existing installation of `Setuptools` in a bootstrap process. This process
|
||||
has been tested in various installation schemes and contexts but in case of a
|
||||
bug during this process your Python installation might be left in a broken
|
||||
state. Since all modified files and directories are copied before the
|
||||
installation starts, you will be able to get back to a normal state by reading
|
||||
the instructions in the `Uninstallation instructions`_ section.
|
||||
|
||||
In any case, it is recommended to save you `site-packages` directory before
|
||||
you start the installation of `Distribute`.
|
||||
|
||||
-------------------------
|
||||
Installation Instructions
|
||||
-------------------------
|
||||
|
||||
Distribute is only released as a source distribution.
|
||||
|
||||
It can be installed using pip, and can be done so with the source tarball,
|
||||
or by using the ``distribute_setup.py`` script provided online.
|
||||
|
||||
``distribute_setup.py`` is the simplest and preferred way on all systems.
|
||||
|
||||
distribute_setup.py
|
||||
===================
|
||||
|
||||
Download
|
||||
`distribute_setup.py <http://python-distribute.org/distribute_setup.py>`_
|
||||
and execute it, using the Python interpreter of your choice.
|
||||
|
||||
If your shell has the ``curl`` program you can do::
|
||||
|
||||
$ curl -O http://python-distribute.org/distribute_setup.py
|
||||
$ python distribute_setup.py
|
||||
|
||||
Notice this file is also provided in the source release.
|
||||
|
||||
pip
|
||||
===
|
||||
|
||||
Run easy_install or pip::
|
||||
|
||||
$ pip install distribute
|
||||
|
||||
Source installation
|
||||
===================
|
||||
|
||||
Download the source tarball, uncompress it, then run the install command::
|
||||
|
||||
$ curl -O http://pypi.python.org/packages/source/d/distribute/distribute-0.6.31.tar.gz
|
||||
$ tar -xzvf distribute-0.6.31.tar.gz
|
||||
$ cd distribute-0.6.31
|
||||
$ python setup.py install
|
||||
|
||||
---------------------------
|
||||
Uninstallation Instructions
|
||||
---------------------------
|
||||
|
||||
Like other distutils-based distributions, Distribute doesn't provide an
|
||||
uninstaller yet. It's all done manually! We are all waiting for PEP 376
|
||||
support in Python.
|
||||
|
||||
Distribute is installed in three steps:
|
||||
|
||||
1. it gets out of the way an existing installation of Setuptools
|
||||
2. it installs a `fake` setuptools installation
|
||||
3. it installs distribute
|
||||
|
||||
Distribute can be removed like this:
|
||||
|
||||
- remove the ``distribute*.egg`` file located in your site-packages directory
|
||||
- remove the ``setuptools.pth`` file located in you site-packages directory
|
||||
- remove the easy_install script located in you ``sys.prefix/bin`` directory
|
||||
- remove the ``setuptools*.egg`` directory located in your site-packages directory,
|
||||
if any.
|
||||
|
||||
If you want to get back to setuptools:
|
||||
|
||||
- reinstall setuptools using its instruction.
|
||||
|
||||
Lastly:
|
||||
|
||||
- remove the *.OLD.* directory located in your site-packages directory if any,
|
||||
**once you have checked everything was working correctly again**.
|
||||
|
||||
-------------------------
|
||||
Quick help for developers
|
||||
-------------------------
|
||||
|
||||
To create an egg which is compatible with Distribute, use the same
|
||||
practice as with Setuptools, e.g.::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(...
|
||||
)
|
||||
|
||||
To use `pkg_resources` to access data files in the egg, you should
|
||||
require the Setuptools distribution explicitly::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(...
|
||||
install_requires=['setuptools']
|
||||
)
|
||||
|
||||
Only if you need Distribute-specific functionality should you depend
|
||||
on it explicitly. In this case, replace the Setuptools dependency::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(...
|
||||
install_requires=['distribute']
|
||||
)
|
||||
|
||||
-----------
|
||||
Install FAQ
|
||||
-----------
|
||||
|
||||
- **Why is Distribute wrapping my Setuptools installation?**
|
||||
|
||||
Since Distribute is a fork, and since it provides the same package
|
||||
and modules, it renames the existing Setuptools egg and inserts a
|
||||
new one which merely wraps the Distribute code. This way, full
|
||||
backwards compatibility is kept for packages which rely on the
|
||||
Setuptools modules.
|
||||
|
||||
At the same time, packages can meet their dependency on Setuptools
|
||||
without actually installing it (which would disable Distribute).
|
||||
|
||||
- **How does Distribute interact with virtualenv?**
|
||||
|
||||
Everytime you create a virtualenv it will install setuptools by default.
|
||||
You either need to re-install Distribute in it right after or pass the
|
||||
``--distribute`` option when creating it.
|
||||
|
||||
Once installed, your virtualenv will use Distribute transparently.
|
||||
|
||||
Although, if you have Setuptools installed in your system-wide Python,
|
||||
and if the virtualenv you are in was generated without the `--no-site-packages`
|
||||
option, the Distribute installation will stop.
|
||||
|
||||
You need in this case to build a virtualenv with the `--no-site-packages`
|
||||
option or to install `Distribute` globally.
|
||||
|
||||
- **How does Distribute interacts with zc.buildout?**
|
||||
|
||||
You can use Distribute in your zc.buildout, with the --distribute option,
|
||||
starting at zc.buildout 1.4.2::
|
||||
|
||||
$ python bootstrap.py --distribute
|
||||
|
||||
For previous zc.buildout versions, *the only thing* you need to do
|
||||
is use the bootstrap at `http://python-distribute.org/bootstrap.py`. Run
|
||||
that bootstrap and ``bin/buildout`` (and all other buildout-generated
|
||||
scripts) will transparently use distribute instead of setuptools. You do
|
||||
not need a specific buildout release.
|
||||
|
||||
A shared eggs directory is no problem (since 0.6.6): the setuptools egg is
|
||||
left in place unmodified. So other buildouts that do not yet use the new
|
||||
bootstrap continue to work just fine. And there is no need to list
|
||||
``distribute`` somewhere in your eggs: using the bootstrap is enough.
|
||||
|
||||
The source code for the bootstrap script is located at
|
||||
`http://bitbucket.org/tarek/buildout-distribute`.
|
||||
|
||||
|
||||
|
||||
-----------------------------
|
||||
Feedback and getting involved
|
||||
-----------------------------
|
||||
|
||||
- Mailing list: http://mail.python.org/mailman/listinfo/distutils-sig
|
||||
- Issue tracker: http://bitbucket.org/tarek/distribute/issues/
|
||||
- Code Repository: http://bitbucket.org/tarek/distribute
|
||||
|
||||
=======
|
||||
CHANGES
|
||||
=======
|
||||
|
||||
------
|
||||
0.6.31
|
||||
------
|
||||
|
||||
* `Issue #303`_: Make sure the manifest only ever contains UTF-8 in Python 3.
|
||||
* `Issue #329`_: Properly close files created by tests for compatibility with
|
||||
Jython.
|
||||
* Work around Jython bugs `#1980 <http://bugs.jython.org/`issue1980`_>`_ and
|
||||
`#1981 <http://bugs.jython.org/`issue1981`_>`_.
|
||||
* `Issue #334`_: Provide workaround for packages that reference `sys.__stdout__`
|
||||
such as numpy does. This change should address
|
||||
`virtualenv #359 <https://github.com/pypa/virtualenv/issues/359>`_ as long
|
||||
as the system encoding is UTF-8 or the IO encoding is specified in the
|
||||
environment, i.e.::
|
||||
|
||||
PYTHONIOENCODING=utf8 pip install numpy
|
||||
|
||||
* Fix for encoding issue when installing from Windows executable on Python 3.
|
||||
* `Issue #323`_: Allow `setup_requires` requirements to supercede installed
|
||||
requirements. Added some new keyword arguments to existing pkg_resources
|
||||
methods. Also had to updated how __path__ is handled for namespace packages
|
||||
to ensure that when a new egg distribution containing a namespace package is
|
||||
placed on sys.path, the entries in __path__ are found in the same order they
|
||||
would have been in had that egg been on the path when pkg_resources was
|
||||
first imported.
|
||||
|
||||
------
|
||||
0.6.30
|
||||
------
|
||||
|
||||
* `Issue #328`_: Clean up temporary directories in distribute_setup.py.
|
||||
* Fix fatal bug in distribute_setup.py.
|
||||
|
||||
------
|
||||
0.6.29
|
||||
------
|
||||
|
||||
* Pull Request #14: Honor file permissions in zip files.
|
||||
* `Issue #327`_: Merged pull request #24 to fix a dependency problem with pip.
|
||||
* Merged pull request #23 to fix https://github.com/pypa/virtualenv/issues/301.
|
||||
* If Sphinx is installed, the `upload_docs` command now runs `build_sphinx`
|
||||
to produce uploadable documentation.
|
||||
* `Issue #326`_: `upload_docs` provided mangled auth credentials under Python 3.
|
||||
* `Issue #320`_: Fix check for "createable" in distribute_setup.py.
|
||||
* `Issue #305`_: Remove a warning that was triggered during normal operations.
|
||||
* `Issue #311`_: Print metadata in UTF-8 independent of platform.
|
||||
* `Issue #303`_: Read manifest file with UTF-8 encoding under Python 3.
|
||||
* `Issue #301`_: Allow to run tests of namespace packages when using 2to3.
|
||||
* `Issue #304`_: Prevent import loop in site.py under Python 3.3.
|
||||
* `Issue #283`_: Reenable scanning of `*.pyc` / `*.pyo` files on Python 3.3.
|
||||
* `Issue #299`_: The develop command didn't work on Python 3, when using 2to3,
|
||||
as the egg link would go to the Python 2 source. Linking to the 2to3'd code
|
||||
in build/lib makes it work, although you will have to rebuild the module
|
||||
before testing it.
|
||||
* `Issue #306`_: Even if 2to3 is used, we build in-place under Python 2.
|
||||
* `Issue #307`_: Prints the full path when .svn/entries is broken.
|
||||
* `Issue #313`_: Support for sdist subcommands (Python 2.7)
|
||||
* `Issue #314`_: test_local_index() would fail an OS X.
|
||||
* `Issue #310`_: Non-ascii characters in a namespace __init__.py causes errors.
|
||||
* `Issue #218`_: Improved documentation on behavior of `package_data` and
|
||||
`include_package_data`. Files indicated by `package_data` are now included
|
||||
in the manifest.
|
||||
* `distribute_setup.py` now allows a `--download-base` argument for retrieving
|
||||
distribute from a specified location.
|
||||
|
||||
------
|
||||
0.6.28
|
||||
------
|
||||
|
||||
* `Issue #294`_: setup.py can now be invoked from any directory.
|
||||
* Scripts are now installed honoring the umask.
|
||||
* Added support for .dist-info directories.
|
||||
* `Issue #283`_: Fix and disable scanning of `*.pyc` / `*.pyo` files on
|
||||
Python 3.3.
|
||||
|
||||
------
|
||||
0.6.27
|
||||
------
|
||||
|
||||
* Support current snapshots of CPython 3.3.
|
||||
* Distribute now recognizes README.rst as a standard, default readme file.
|
||||
* Exclude 'encodings' modules when removing modules from sys.modules.
|
||||
Workaround for #285.
|
||||
* `Issue #231`_: Don't fiddle with system python when used with buildout
|
||||
(bootstrap.py)
|
||||
|
||||
------
|
||||
0.6.26
|
||||
------
|
||||
|
||||
* `Issue #183`_: Symlinked files are now extracted from source distributions.
|
||||
* `Issue #227`_: Easy_install fetch parameters are now passed during the
|
||||
installation of a source distribution; now fulfillment of setup_requires
|
||||
dependencies will honor the parameters passed to easy_install.
|
||||
|
||||
------
|
||||
0.6.25
|
||||
------
|
||||
|
||||
* `Issue #258`_: Workaround a cache issue
|
||||
* `Issue #260`_: distribute_setup.py now accepts the --user parameter for
|
||||
Python 2.6 and later.
|
||||
* `Issue #262`_: package_index.open_with_auth no longer throws LookupError
|
||||
on Python 3.
|
||||
* `Issue #269`_: AttributeError when an exception occurs reading Manifest.in
|
||||
on late releases of Python.
|
||||
* `Issue #272`_: Prevent TypeError when namespace package names are unicode
|
||||
and single-install-externally-managed is used. Also fixes PIP `issue
|
||||
449`_.
|
||||
* `Issue #273`_: Legacy script launchers now install with Python2/3 support.
|
||||
|
||||
------
|
||||
0.6.24
|
||||
------
|
||||
|
||||
* `Issue #249`_: Added options to exclude 2to3 fixers
|
||||
|
||||
------
|
||||
0.6.23
|
||||
------
|
||||
|
||||
* `Issue #244`_: Fixed a test
|
||||
* `Issue #243`_: Fixed a test
|
||||
* `Issue #239`_: Fixed a test
|
||||
* `Issue #240`_: Fixed a test
|
||||
* `Issue #241`_: Fixed a test
|
||||
* `Issue #237`_: Fixed a test
|
||||
* `Issue #238`_: easy_install now uses 64bit executable wrappers on 64bit Python
|
||||
* `Issue #208`_: Fixed parsed_versions, it now honors post-releases as noted in the documentation
|
||||
* `Issue #207`_: Windows cli and gui wrappers pass CTRL-C to child python process
|
||||
* `Issue #227`_: easy_install now passes its arguments to setup.py bdist_egg
|
||||
* `Issue #225`_: Fixed a NameError on Python 2.5, 2.4
|
||||
|
||||
------
|
||||
0.6.21
|
||||
------
|
||||
|
||||
* `Issue #225`_: FIxed a regression on py2.4
|
||||
|
||||
------
|
||||
0.6.20
|
||||
------
|
||||
|
||||
* `Issue #135`_: Include url in warning when processing URLs in package_index.
|
||||
* `Issue #212`_: Fix issue where easy_instal fails on Python 3 on windows installer.
|
||||
* `Issue #213`_: Fix typo in documentation.
|
||||
|
||||
------
|
||||
0.6.19
|
||||
------
|
||||
|
||||
* `Issue 206`_: AttributeError: 'HTTPMessage' object has no attribute 'getheaders'
|
||||
|
||||
------
|
||||
0.6.18
|
||||
------
|
||||
|
||||
* `Issue 210`_: Fixed a regression introduced by `Issue 204`_ fix.
|
||||
|
||||
------
|
||||
0.6.17
|
||||
------
|
||||
|
||||
* Support 'DISTRIBUTE_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT' environment
|
||||
variable to allow to disable installation of easy_install-${version} script.
|
||||
* Support Python >=3.1.4 and >=3.2.1.
|
||||
* `Issue 204`_: Don't try to import the parent of a namespace package in
|
||||
declare_namespace
|
||||
* `Issue 196`_: Tolerate responses with multiple Content-Length headers
|
||||
* `Issue 205`_: Sandboxing doesn't preserve working_set. Leads to setup_requires
|
||||
problems.
|
||||
|
||||
------
|
||||
0.6.16
|
||||
------
|
||||
|
||||
* Builds sdist gztar even on Windows (avoiding `Issue 193`_).
|
||||
* `Issue 192`_: Fixed metadata omitted on Windows when package_dir
|
||||
specified with forward-slash.
|
||||
* `Issue 195`_: Cython build support.
|
||||
* `Issue 200`_: Issues with recognizing 64-bit packages on Windows.
|
||||
|
||||
------
|
||||
0.6.15
|
||||
------
|
||||
|
||||
* Fixed typo in bdist_egg
|
||||
* Several issues under Python 3 has been solved.
|
||||
* `Issue 146`_: Fixed missing DLL files after easy_install of windows exe package.
|
||||
|
||||
------
|
||||
0.6.14
|
||||
------
|
||||
|
||||
* `Issue 170`_: Fixed unittest failure. Thanks to Toshio.
|
||||
* `Issue 171`_: Fixed race condition in unittests cause deadlocks in test suite.
|
||||
* `Issue 143`_: Fixed a lookup issue with easy_install.
|
||||
Thanks to David and Zooko.
|
||||
* `Issue 174`_: Fixed the edit mode when its used with setuptools itself
|
||||
|
||||
------
|
||||
0.6.13
|
||||
------
|
||||
|
||||
* `Issue 160`_: 2.7 gives ValueError("Invalid IPv6 URL")
|
||||
* `Issue 150`_: Fixed using ~/.local even in a --no-site-packages virtualenv
|
||||
* `Issue 163`_: scan index links before external links, and don't use the md5 when
|
||||
comparing two distributions
|
||||
|
||||
------
|
||||
0.6.12
|
||||
------
|
||||
|
||||
* `Issue 149`_: Fixed various failures on 2.3/2.4
|
||||
|
||||
------
|
||||
0.6.11
|
||||
------
|
||||
|
||||
* Found another case of SandboxViolation - fixed
|
||||
* `Issue 15`_ and 48: Introduced a socket timeout of 15 seconds on url openings
|
||||
* Added indexsidebar.html into MANIFEST.in
|
||||
* `Issue 108`_: Fixed TypeError with Python3.1
|
||||
* `Issue 121`_: Fixed --help install command trying to actually install.
|
||||
* `Issue 112`_: Added an os.makedirs so that Tarek's solution will work.
|
||||
* `Issue 133`_: Added --no-find-links to easy_install
|
||||
* Added easy_install --user
|
||||
* `Issue 100`_: Fixed develop --user not taking '.' in PYTHONPATH into account
|
||||
* `Issue 134`_: removed spurious UserWarnings. Patch by VanLindberg
|
||||
* `Issue 138`_: cant_write_to_target error when setup_requires is used.
|
||||
* `Issue 147`_: respect the sys.dont_write_bytecode flag
|
||||
|
||||
------
|
||||
0.6.10
|
||||
------
|
||||
|
||||
* Reverted change made for the DistributionNotFound exception because
|
||||
zc.buildout uses the exception message to get the name of the
|
||||
distribution.
|
||||
|
||||
-----
|
||||
0.6.9
|
||||
-----
|
||||
|
||||
* `Issue 90`_: unknown setuptools version can be added in the working set
|
||||
* `Issue 87`_: setupt.py doesn't try to convert distribute_setup.py anymore
|
||||
Initial Patch by arfrever.
|
||||
* `Issue 89`_: added a side bar with a download link to the doc.
|
||||
* `Issue 86`_: fixed missing sentence in pkg_resources doc.
|
||||
* Added a nicer error message when a DistributionNotFound is raised.
|
||||
* `Issue 80`_: test_develop now works with Python 3.1
|
||||
* `Issue 93`_: upload_docs now works if there is an empty sub-directory.
|
||||
* `Issue 70`_: exec bit on non-exec files
|
||||
* `Issue 99`_: now the standalone easy_install command doesn't uses a
|
||||
"setup.cfg" if any exists in the working directory. It will use it
|
||||
only if triggered by ``install_requires`` from a setup.py call
|
||||
(install, develop, etc).
|
||||
* `Issue 101`_: Allowing ``os.devnull`` in Sandbox
|
||||
* `Issue 92`_: Fixed the "no eggs" found error with MacPort
|
||||
(platform.mac_ver() fails)
|
||||
* `Issue 103`_: test_get_script_header_jython_workaround not run
|
||||
anymore under py3 with C or POSIX local. Contributed by Arfrever.
|
||||
* `Issue 104`_: remvoved the assertion when the installation fails,
|
||||
with a nicer message for the end user.
|
||||
* `Issue 100`_: making sure there's no SandboxViolation when
|
||||
the setup script patches setuptools.
|
||||
|
||||
-----
|
||||
0.6.8
|
||||
-----
|
||||
|
||||
* Added "check_packages" in dist. (added in Setuptools 0.6c11)
|
||||
* Fixed the DONT_PATCH_SETUPTOOLS state.
|
||||
|
||||
-----
|
||||
0.6.7
|
||||
-----
|
||||
|
||||
* `Issue 58`_: Added --user support to the develop command
|
||||
* `Issue 11`_: Generated scripts now wrap their call to the script entry point
|
||||
in the standard "if name == 'main'"
|
||||
* Added the 'DONT_PATCH_SETUPTOOLS' environment variable, so virtualenv
|
||||
can drive an installation that doesn't patch a global setuptools.
|
||||
* Reviewed unladen-swallow specific change from
|
||||
http://code.google.com/p/unladen-swallow/source/detail?spec=svn875&r=719
|
||||
and determined that it no longer applies. Distribute should work fine with
|
||||
Unladen Swallow 2009Q3.
|
||||
* `Issue 21`_: Allow PackageIndex.open_url to gracefully handle all cases of a
|
||||
httplib.HTTPException instead of just InvalidURL and BadStatusLine.
|
||||
* Removed virtual-python.py from this distribution and updated documentation
|
||||
to point to the actively maintained virtualenv instead.
|
||||
* `Issue 64`_: use_setuptools no longer rebuilds the distribute egg every
|
||||
time it is run
|
||||
* use_setuptools now properly respects the requested version
|
||||
* use_setuptools will no longer try to import a distribute egg for the
|
||||
wrong Python version
|
||||
* `Issue 74`_: no_fake should be True by default.
|
||||
* `Issue 72`_: avoid a bootstrapping issue with easy_install -U
|
||||
|
||||
-----
|
||||
0.6.6
|
||||
-----
|
||||
|
||||
* Unified the bootstrap file so it works on both py2.x and py3k without 2to3
|
||||
(patch by Holger Krekel)
|
||||
|
||||
-----
|
||||
0.6.5
|
||||
-----
|
||||
|
||||
* `Issue 65`_: cli.exe and gui.exe are now generated at build time,
|
||||
depending on the platform in use.
|
||||
|
||||
* `Issue 67`_: Fixed doc typo (PEP 381/382)
|
||||
|
||||
* Distribute no longer shadows setuptools if we require a 0.7-series
|
||||
setuptools. And an error is raised when installing a 0.7 setuptools with
|
||||
distribute.
|
||||
|
||||
* When run from within buildout, no attempt is made to modify an existing
|
||||
setuptools egg, whether in a shared egg directory or a system setuptools.
|
||||
|
||||
* Fixed a hole in sandboxing allowing builtin file to write outside of
|
||||
the sandbox.
|
||||
|
||||
-----
|
||||
0.6.4
|
||||
-----
|
||||
|
||||
* Added the generation of `distribute_setup_3k.py` during the release.
|
||||
This closes `issue #52`_.
|
||||
|
||||
* Added an upload_docs command to easily upload project documentation to
|
||||
PyPI's http://packages.python.org. This close `issue #56`_.
|
||||
|
||||
* Fixed a bootstrap bug on the use_setuptools() API.
|
||||
|
||||
-----
|
||||
0.6.3
|
||||
-----
|
||||
|
||||
setuptools
|
||||
==========
|
||||
|
||||
* Fixed a bunch of calls to file() that caused crashes on Python 3.
|
||||
|
||||
bootstrapping
|
||||
=============
|
||||
|
||||
* Fixed a bug in sorting that caused bootstrap to fail on Python 3.
|
||||
|
||||
-----
|
||||
0.6.2
|
||||
-----
|
||||
|
||||
setuptools
|
||||
==========
|
||||
|
||||
* Added Python 3 support; see docs/python3.txt.
|
||||
This closes http://bugs.python.org/setuptools/`issue39`_.
|
||||
|
||||
* Added option to run 2to3 automatically when installing on Python 3.
|
||||
This closes `issue #31`_.
|
||||
|
||||
* Fixed invalid usage of requirement.parse, that broke develop -d.
|
||||
This closes http://bugs.python.org/setuptools/`issue44`_.
|
||||
|
||||
* Fixed script launcher for 64-bit Windows.
|
||||
This closes http://bugs.python.org/setuptools/`issue2`_.
|
||||
|
||||
* KeyError when compiling extensions.
|
||||
This closes http://bugs.python.org/setuptools/`issue41`_.
|
||||
|
||||
bootstrapping
|
||||
=============
|
||||
|
||||
* Fixed bootstrap not working on Windows. This closes `issue #49`_.
|
||||
|
||||
* Fixed 2.6 dependencies. This closes `issue #50`_.
|
||||
|
||||
* Make sure setuptools is patched when running through easy_install
|
||||
This closes http://bugs.python.org/setuptools/`issue40`_.
|
||||
|
||||
-----
|
||||
0.6.1
|
||||
-----
|
||||
|
||||
setuptools
|
||||
==========
|
||||
|
||||
* package_index.urlopen now catches BadStatusLine and malformed url errors.
|
||||
This closes `issue #16`_ and `issue #18`_.
|
||||
|
||||
* zip_ok is now False by default. This closes
|
||||
http://bugs.python.org/setuptools/`issue33`_.
|
||||
|
||||
* Fixed invalid URL error catching. http://bugs.python.org/setuptools/`issue20`_.
|
||||
|
||||
* Fixed invalid bootstraping with easy_install installation (`issue #40`_).
|
||||
Thanks to Florian Schulze for the help.
|
||||
|
||||
* Removed buildout/bootstrap.py. A new repository will create a specific
|
||||
bootstrap.py script.
|
||||
|
||||
|
||||
bootstrapping
|
||||
=============
|
||||
|
||||
* The boostrap process leave setuptools alone if detected in the system
|
||||
and --root or --prefix is provided, but is not in the same location.
|
||||
This closes `issue #10`_.
|
||||
|
||||
---
|
||||
0.6
|
||||
---
|
||||
|
||||
setuptools
|
||||
==========
|
||||
|
||||
* Packages required at build time where not fully present at install time.
|
||||
This closes `issue #12`_.
|
||||
|
||||
* Protected against failures in tarfile extraction. This closes `issue #10`_.
|
||||
|
||||
* Made Jython api_tests.txt doctest compatible. This closes `issue #7`_.
|
||||
|
||||
* sandbox.py replaced builtin type file with builtin function open. This
|
||||
closes `issue #6`_.
|
||||
|
||||
* Immediately close all file handles. This closes `issue #3`_.
|
||||
|
||||
* Added compatibility with Subversion 1.6. This references `issue #1`_.
|
||||
|
||||
pkg_resources
|
||||
=============
|
||||
|
||||
* Avoid a call to /usr/bin/sw_vers on OSX and use the official platform API
|
||||
instead. Based on a patch from ronaldoussoren. This closes `issue #5`_.
|
||||
|
||||
* Fixed a SandboxViolation for mkdir that could occur in certain cases.
|
||||
This closes `issue #13`_.
|
||||
|
||||
* Allow to find_on_path on systems with tight permissions to fail gracefully.
|
||||
This closes `issue #9`_.
|
||||
|
||||
* Corrected inconsistency between documentation and code of add_entry.
|
||||
This closes `issue #8`_.
|
||||
|
||||
* Immediately close all file handles. This closes `issue #3`_.
|
||||
|
||||
easy_install
|
||||
============
|
||||
|
||||
* Immediately close all file handles. This closes `issue #3`_.
|
||||
|
||||
|
||||
.. _`Issue #135`: http://bitbucket.org/tarek/distribute/issue/135
|
||||
.. _`Issue #183`: http://bitbucket.org/tarek/distribute/issue/183
|
||||
.. _`Issue #207`: http://bitbucket.org/tarek/distribute/issue/207
|
||||
.. _`Issue #208`: http://bitbucket.org/tarek/distribute/issue/208
|
||||
.. _`Issue #212`: http://bitbucket.org/tarek/distribute/issue/212
|
||||
.. _`Issue #213`: http://bitbucket.org/tarek/distribute/issue/213
|
||||
.. _`Issue #218`: http://bitbucket.org/tarek/distribute/issue/218
|
||||
.. _`Issue #225`: http://bitbucket.org/tarek/distribute/issue/225
|
||||
.. _`Issue #227`: http://bitbucket.org/tarek/distribute/issue/227
|
||||
.. _`Issue #231`: http://bitbucket.org/tarek/distribute/issue/231
|
||||
.. _`Issue #237`: http://bitbucket.org/tarek/distribute/issue/237
|
||||
.. _`Issue #238`: http://bitbucket.org/tarek/distribute/issue/238
|
||||
.. _`Issue #239`: http://bitbucket.org/tarek/distribute/issue/239
|
||||
.. _`Issue #240`: http://bitbucket.org/tarek/distribute/issue/240
|
||||
.. _`Issue #241`: http://bitbucket.org/tarek/distribute/issue/241
|
||||
.. _`Issue #243`: http://bitbucket.org/tarek/distribute/issue/243
|
||||
.. _`Issue #244`: http://bitbucket.org/tarek/distribute/issue/244
|
||||
.. _`Issue #249`: http://bitbucket.org/tarek/distribute/issue/249
|
||||
.. _`Issue #258`: http://bitbucket.org/tarek/distribute/issue/258
|
||||
.. _`Issue #260`: http://bitbucket.org/tarek/distribute/issue/260
|
||||
.. _`Issue #262`: http://bitbucket.org/tarek/distribute/issue/262
|
||||
.. _`Issue #269`: http://bitbucket.org/tarek/distribute/issue/269
|
||||
.. _`Issue #272`: http://bitbucket.org/tarek/distribute/issue/272
|
||||
.. _`Issue #273`: http://bitbucket.org/tarek/distribute/issue/273
|
||||
.. _`Issue #283`: http://bitbucket.org/tarek/distribute/issue/283
|
||||
.. _`Issue #294`: http://bitbucket.org/tarek/distribute/issue/294
|
||||
.. _`Issue #299`: http://bitbucket.org/tarek/distribute/issue/299
|
||||
.. _`Issue #301`: http://bitbucket.org/tarek/distribute/issue/301
|
||||
.. _`Issue #303`: http://bitbucket.org/tarek/distribute/issue/303
|
||||
.. _`Issue #304`: http://bitbucket.org/tarek/distribute/issue/304
|
||||
.. _`Issue #305`: http://bitbucket.org/tarek/distribute/issue/305
|
||||
.. _`Issue #306`: http://bitbucket.org/tarek/distribute/issue/306
|
||||
.. _`Issue #307`: http://bitbucket.org/tarek/distribute/issue/307
|
||||
.. _`Issue #310`: http://bitbucket.org/tarek/distribute/issue/310
|
||||
.. _`Issue #311`: http://bitbucket.org/tarek/distribute/issue/311
|
||||
.. _`Issue #313`: http://bitbucket.org/tarek/distribute/issue/313
|
||||
.. _`Issue #314`: http://bitbucket.org/tarek/distribute/issue/314
|
||||
.. _`Issue #320`: http://bitbucket.org/tarek/distribute/issue/320
|
||||
.. _`Issue #323`: http://bitbucket.org/tarek/distribute/issue/323
|
||||
.. _`Issue #326`: http://bitbucket.org/tarek/distribute/issue/326
|
||||
.. _`Issue #327`: http://bitbucket.org/tarek/distribute/issue/327
|
||||
.. _`Issue #328`: http://bitbucket.org/tarek/distribute/issue/328
|
||||
.. _`Issue #329`: http://bitbucket.org/tarek/distribute/issue/329
|
||||
.. _`Issue #334`: http://bitbucket.org/tarek/distribute/issue/334
|
||||
.. _`Issue 100`: http://bitbucket.org/tarek/distribute/issue/100
|
||||
.. _`Issue 101`: http://bitbucket.org/tarek/distribute/issue/101
|
||||
.. _`Issue 103`: http://bitbucket.org/tarek/distribute/issue/103
|
||||
.. _`Issue 104`: http://bitbucket.org/tarek/distribute/issue/104
|
||||
.. _`Issue 108`: http://bitbucket.org/tarek/distribute/issue/108
|
||||
.. _`Issue 11`: http://bitbucket.org/tarek/distribute/issue/11
|
||||
.. _`Issue 112`: http://bitbucket.org/tarek/distribute/issue/112
|
||||
.. _`Issue 121`: http://bitbucket.org/tarek/distribute/issue/121
|
||||
.. _`Issue 133`: http://bitbucket.org/tarek/distribute/issue/133
|
||||
.. _`Issue 134`: http://bitbucket.org/tarek/distribute/issue/134
|
||||
.. _`Issue 138`: http://bitbucket.org/tarek/distribute/issue/138
|
||||
.. _`Issue 143`: http://bitbucket.org/tarek/distribute/issue/143
|
||||
.. _`Issue 146`: http://bitbucket.org/tarek/distribute/issue/146
|
||||
.. _`Issue 147`: http://bitbucket.org/tarek/distribute/issue/147
|
||||
.. _`Issue 149`: http://bitbucket.org/tarek/distribute/issue/149
|
||||
.. _`Issue 15`: http://bitbucket.org/tarek/distribute/issue/15
|
||||
.. _`Issue 150`: http://bitbucket.org/tarek/distribute/issue/150
|
||||
.. _`Issue 160`: http://bitbucket.org/tarek/distribute/issue/160
|
||||
.. _`Issue 163`: http://bitbucket.org/tarek/distribute/issue/163
|
||||
.. _`Issue 170`: http://bitbucket.org/tarek/distribute/issue/170
|
||||
.. _`Issue 171`: http://bitbucket.org/tarek/distribute/issue/171
|
||||
.. _`Issue 174`: http://bitbucket.org/tarek/distribute/issue/174
|
||||
.. _`Issue 192`: http://bitbucket.org/tarek/distribute/issue/192
|
||||
.. _`Issue 193`: http://bitbucket.org/tarek/distribute/issue/193
|
||||
.. _`Issue 195`: http://bitbucket.org/tarek/distribute/issue/195
|
||||
.. _`Issue 196`: http://bitbucket.org/tarek/distribute/issue/196
|
||||
.. _`Issue 200`: http://bitbucket.org/tarek/distribute/issue/200
|
||||
.. _`Issue 204`: http://bitbucket.org/tarek/distribute/issue/204
|
||||
.. _`Issue 205`: http://bitbucket.org/tarek/distribute/issue/205
|
||||
.. _`Issue 206`: http://bitbucket.org/tarek/distribute/issue/206
|
||||
.. _`Issue 21`: http://bitbucket.org/tarek/distribute/issue/21
|
||||
.. _`Issue 210`: http://bitbucket.org/tarek/distribute/issue/210
|
||||
.. _`Issue 58`: http://bitbucket.org/tarek/distribute/issue/58
|
||||
.. _`Issue 64`: http://bitbucket.org/tarek/distribute/issue/64
|
||||
.. _`Issue 65`: http://bitbucket.org/tarek/distribute/issue/65
|
||||
.. _`Issue 67`: http://bitbucket.org/tarek/distribute/issue/67
|
||||
.. _`Issue 70`: http://bitbucket.org/tarek/distribute/issue/70
|
||||
.. _`Issue 72`: http://bitbucket.org/tarek/distribute/issue/72
|
||||
.. _`Issue 74`: http://bitbucket.org/tarek/distribute/issue/74
|
||||
.. _`Issue 80`: http://bitbucket.org/tarek/distribute/issue/80
|
||||
.. _`Issue 86`: http://bitbucket.org/tarek/distribute/issue/86
|
||||
.. _`Issue 87`: http://bitbucket.org/tarek/distribute/issue/87
|
||||
.. _`Issue 89`: http://bitbucket.org/tarek/distribute/issue/89
|
||||
.. _`Issue 90`: http://bitbucket.org/tarek/distribute/issue/90
|
||||
.. _`Issue 92`: http://bitbucket.org/tarek/distribute/issue/92
|
||||
.. _`Issue 93`: http://bitbucket.org/tarek/distribute/issue/93
|
||||
.. _`Issue 99`: http://bitbucket.org/tarek/distribute/issue/99
|
||||
.. _`issue
|
||||
449`: http://bitbucket.org/tarek/distribute/issue/449
|
||||
.. _`issue #1`: http://bitbucket.org/tarek/distribute/issue/1
|
||||
.. _`issue #10`: http://bitbucket.org/tarek/distribute/issue/10
|
||||
.. _`issue #12`: http://bitbucket.org/tarek/distribute/issue/12
|
||||
.. _`issue #13`: http://bitbucket.org/tarek/distribute/issue/13
|
||||
.. _`issue #16`: http://bitbucket.org/tarek/distribute/issue/16
|
||||
.. _`issue #18`: http://bitbucket.org/tarek/distribute/issue/18
|
||||
.. _`issue #3`: http://bitbucket.org/tarek/distribute/issue/3
|
||||
.. _`issue #31`: http://bitbucket.org/tarek/distribute/issue/31
|
||||
.. _`issue #40`: http://bitbucket.org/tarek/distribute/issue/40
|
||||
.. _`issue #49`: http://bitbucket.org/tarek/distribute/issue/49
|
||||
.. _`issue #5`: http://bitbucket.org/tarek/distribute/issue/5
|
||||
.. _`issue #50`: http://bitbucket.org/tarek/distribute/issue/50
|
||||
.. _`issue #52`: http://bitbucket.org/tarek/distribute/issue/52
|
||||
.. _`issue #56`: http://bitbucket.org/tarek/distribute/issue/56
|
||||
.. _`issue #6`: http://bitbucket.org/tarek/distribute/issue/6
|
||||
.. _`issue #7`: http://bitbucket.org/tarek/distribute/issue/7
|
||||
.. _`issue #8`: http://bitbucket.org/tarek/distribute/issue/8
|
||||
.. _`issue #9`: http://bitbucket.org/tarek/distribute/issue/9
|
||||
.. _`issue1980`: http://bitbucket.org/tarek/distribute/issue/1980
|
||||
.. _`issue1981`: http://bitbucket.org/tarek/distribute/issue/1981
|
||||
.. _`issue2`: http://bitbucket.org/tarek/distribute/issue/2
|
||||
.. _`issue20`: http://bitbucket.org/tarek/distribute/issue/20
|
||||
.. _`issue33`: http://bitbucket.org/tarek/distribute/issue/33
|
||||
.. _`issue39`: http://bitbucket.org/tarek/distribute/issue/39
|
||||
.. _`issue40`: http://bitbucket.org/tarek/distribute/issue/40
|
||||
.. _`issue41`: http://bitbucket.org/tarek/distribute/issue/41
|
||||
.. _`issue44`: http://bitbucket.org/tarek/distribute/issue/44
|
||||
|
||||
|
||||
Keywords: CPAN PyPI distutils eggs package management
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
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 :: 2.4
|
||||
Classifier: Programming Language :: Python :: 2.5
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.1
|
||||
Classifier: Programming Language :: Python :: 3.2
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: System :: Archiving :: Packaging
|
||||
Classifier: Topic :: System :: Systems Administration
|
||||
Classifier: Topic :: Utilities
|
||||
Vendored
-228
@@ -1,228 +0,0 @@
|
||||
===============================
|
||||
Installing and Using Distribute
|
||||
===============================
|
||||
|
||||
.. contents:: **Table of Contents**
|
||||
|
||||
-----------
|
||||
Disclaimers
|
||||
-----------
|
||||
|
||||
About the fork
|
||||
==============
|
||||
|
||||
`Distribute` is a fork of the `Setuptools` project.
|
||||
|
||||
Distribute is intended to replace Setuptools as the standard method
|
||||
for working with Python module distributions.
|
||||
|
||||
The fork has two goals:
|
||||
|
||||
- Providing a backward compatible version to replace Setuptools
|
||||
and make all distributions that depend on Setuptools work as
|
||||
before, but with less bugs and behaviorial issues.
|
||||
|
||||
This work is done in the 0.6.x series.
|
||||
|
||||
Starting with version 0.6.2, Distribute supports Python 3.
|
||||
Installing and using distribute for Python 3 code works exactly
|
||||
the same as for Python 2 code, but Distribute also helps you to support
|
||||
Python 2 and Python 3 from the same source code by letting you run 2to3
|
||||
on the code as a part of the build process, by setting the keyword parameter
|
||||
``use_2to3`` to True. See http://packages.python.org/distribute for more
|
||||
information.
|
||||
|
||||
- Refactoring the code, and releasing it in several distributions.
|
||||
This work is being done in the 0.7.x series but not yet released.
|
||||
|
||||
The roadmap is still evolving, and the page that is up-to-date is
|
||||
located at : `http://packages.python.org/distribute/roadmap`.
|
||||
|
||||
If you install `Distribute` and want to switch back for any reason to
|
||||
`Setuptools`, get to the `Uninstallation instructions`_ section.
|
||||
|
||||
More documentation
|
||||
==================
|
||||
|
||||
You can get more information in the Sphinx-based documentation, located
|
||||
at http://packages.python.org/distribute. This documentation includes the old
|
||||
Setuptools documentation that is slowly replaced, and brand new content.
|
||||
|
||||
About the installation process
|
||||
==============================
|
||||
|
||||
The `Distribute` installer modifies your installation by de-activating an
|
||||
existing installation of `Setuptools` in a bootstrap process. This process
|
||||
has been tested in various installation schemes and contexts but in case of a
|
||||
bug during this process your Python installation might be left in a broken
|
||||
state. Since all modified files and directories are copied before the
|
||||
installation starts, you will be able to get back to a normal state by reading
|
||||
the instructions in the `Uninstallation instructions`_ section.
|
||||
|
||||
In any case, it is recommended to save you `site-packages` directory before
|
||||
you start the installation of `Distribute`.
|
||||
|
||||
-------------------------
|
||||
Installation Instructions
|
||||
-------------------------
|
||||
|
||||
Distribute is only released as a source distribution.
|
||||
|
||||
It can be installed using pip, and can be done so with the source tarball,
|
||||
or by using the ``distribute_setup.py`` script provided online.
|
||||
|
||||
``distribute_setup.py`` is the simplest and preferred way on all systems.
|
||||
|
||||
distribute_setup.py
|
||||
===================
|
||||
|
||||
Download
|
||||
`distribute_setup.py <http://python-distribute.org/distribute_setup.py>`_
|
||||
and execute it, using the Python interpreter of your choice.
|
||||
|
||||
If your shell has the ``curl`` program you can do::
|
||||
|
||||
$ curl -O http://python-distribute.org/distribute_setup.py
|
||||
$ python distribute_setup.py
|
||||
|
||||
Notice this file is also provided in the source release.
|
||||
|
||||
pip
|
||||
===
|
||||
|
||||
Run easy_install or pip::
|
||||
|
||||
$ pip install distribute
|
||||
|
||||
Source installation
|
||||
===================
|
||||
|
||||
Download the source tarball, uncompress it, then run the install command::
|
||||
|
||||
$ curl -O http://pypi.python.org/packages/source/d/distribute/distribute-0.6.31.tar.gz
|
||||
$ tar -xzvf distribute-0.6.31.tar.gz
|
||||
$ cd distribute-0.6.31
|
||||
$ python setup.py install
|
||||
|
||||
---------------------------
|
||||
Uninstallation Instructions
|
||||
---------------------------
|
||||
|
||||
Like other distutils-based distributions, Distribute doesn't provide an
|
||||
uninstaller yet. It's all done manually! We are all waiting for PEP 376
|
||||
support in Python.
|
||||
|
||||
Distribute is installed in three steps:
|
||||
|
||||
1. it gets out of the way an existing installation of Setuptools
|
||||
2. it installs a `fake` setuptools installation
|
||||
3. it installs distribute
|
||||
|
||||
Distribute can be removed like this:
|
||||
|
||||
- remove the ``distribute*.egg`` file located in your site-packages directory
|
||||
- remove the ``setuptools.pth`` file located in you site-packages directory
|
||||
- remove the easy_install script located in you ``sys.prefix/bin`` directory
|
||||
- remove the ``setuptools*.egg`` directory located in your site-packages directory,
|
||||
if any.
|
||||
|
||||
If you want to get back to setuptools:
|
||||
|
||||
- reinstall setuptools using its instruction.
|
||||
|
||||
Lastly:
|
||||
|
||||
- remove the *.OLD.* directory located in your site-packages directory if any,
|
||||
**once you have checked everything was working correctly again**.
|
||||
|
||||
-------------------------
|
||||
Quick help for developers
|
||||
-------------------------
|
||||
|
||||
To create an egg which is compatible with Distribute, use the same
|
||||
practice as with Setuptools, e.g.::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(...
|
||||
)
|
||||
|
||||
To use `pkg_resources` to access data files in the egg, you should
|
||||
require the Setuptools distribution explicitly::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(...
|
||||
install_requires=['setuptools']
|
||||
)
|
||||
|
||||
Only if you need Distribute-specific functionality should you depend
|
||||
on it explicitly. In this case, replace the Setuptools dependency::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(...
|
||||
install_requires=['distribute']
|
||||
)
|
||||
|
||||
-----------
|
||||
Install FAQ
|
||||
-----------
|
||||
|
||||
- **Why is Distribute wrapping my Setuptools installation?**
|
||||
|
||||
Since Distribute is a fork, and since it provides the same package
|
||||
and modules, it renames the existing Setuptools egg and inserts a
|
||||
new one which merely wraps the Distribute code. This way, full
|
||||
backwards compatibility is kept for packages which rely on the
|
||||
Setuptools modules.
|
||||
|
||||
At the same time, packages can meet their dependency on Setuptools
|
||||
without actually installing it (which would disable Distribute).
|
||||
|
||||
- **How does Distribute interact with virtualenv?**
|
||||
|
||||
Everytime you create a virtualenv it will install setuptools by default.
|
||||
You either need to re-install Distribute in it right after or pass the
|
||||
``--distribute`` option when creating it.
|
||||
|
||||
Once installed, your virtualenv will use Distribute transparently.
|
||||
|
||||
Although, if you have Setuptools installed in your system-wide Python,
|
||||
and if the virtualenv you are in was generated without the `--no-site-packages`
|
||||
option, the Distribute installation will stop.
|
||||
|
||||
You need in this case to build a virtualenv with the `--no-site-packages`
|
||||
option or to install `Distribute` globally.
|
||||
|
||||
- **How does Distribute interacts with zc.buildout?**
|
||||
|
||||
You can use Distribute in your zc.buildout, with the --distribute option,
|
||||
starting at zc.buildout 1.4.2::
|
||||
|
||||
$ python bootstrap.py --distribute
|
||||
|
||||
For previous zc.buildout versions, *the only thing* you need to do
|
||||
is use the bootstrap at `http://python-distribute.org/bootstrap.py`. Run
|
||||
that bootstrap and ``bin/buildout`` (and all other buildout-generated
|
||||
scripts) will transparently use distribute instead of setuptools. You do
|
||||
not need a specific buildout release.
|
||||
|
||||
A shared eggs directory is no problem (since 0.6.6): the setuptools egg is
|
||||
left in place unmodified. So other buildouts that do not yet use the new
|
||||
bootstrap continue to work just fine. And there is no need to list
|
||||
``distribute`` somewhere in your eggs: using the bootstrap is enough.
|
||||
|
||||
The source code for the bootstrap script is located at
|
||||
`http://bitbucket.org/tarek/buildout-distribute`.
|
||||
|
||||
|
||||
|
||||
-----------------------------
|
||||
Feedback and getting involved
|
||||
-----------------------------
|
||||
|
||||
- Mailing list: http://mail.python.org/mailman/listinfo/distutils-sig
|
||||
- Issue tracker: http://bitbucket.org/tarek/distribute/issues/
|
||||
- Code Repository: http://bitbucket.org/tarek/distribute
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
try:
|
||||
import ast
|
||||
from _markerlib.markers import default_environment, compile, interpret
|
||||
except ImportError:
|
||||
if 'ast' in globals():
|
||||
raise
|
||||
def default_environment():
|
||||
return {}
|
||||
def compile(marker):
|
||||
def marker_fn(environment=None, override=None):
|
||||
# 'empty markers are True' heuristic won't install extra deps.
|
||||
return not marker.strip()
|
||||
marker_fn.__doc__ = marker
|
||||
return marker_fn
|
||||
def interpret(marker, environment=None, override=None):
|
||||
return compile(marker)()
|
||||
-106
@@ -1,106 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Interpret PEP 345 environment markers.
|
||||
|
||||
EXPR [in|==|!=|not in] EXPR [or|and] ...
|
||||
|
||||
where EXPR belongs to any of those:
|
||||
|
||||
python_version = '%s.%s' % (sys.version_info[0], sys.version_info[1])
|
||||
python_full_version = sys.version.split()[0]
|
||||
os.name = os.name
|
||||
sys.platform = sys.platform
|
||||
platform.version = platform.version()
|
||||
platform.machine = platform.machine()
|
||||
platform.python_implementation = platform.python_implementation()
|
||||
a free string, like '2.6', or 'win32'
|
||||
"""
|
||||
|
||||
__all__ = ['default_environment', 'compile', 'interpret']
|
||||
|
||||
import ast
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
import weakref
|
||||
|
||||
_builtin_compile = compile
|
||||
|
||||
from platform import python_implementation
|
||||
|
||||
# restricted set of variables
|
||||
_VARS = {'sys.platform': sys.platform,
|
||||
'python_version': '%s.%s' % sys.version_info[:2],
|
||||
# FIXME parsing sys.platform is not reliable, but there is no other
|
||||
# way to get e.g. 2.7.2+, and the PEP is defined with sys.version
|
||||
'python_full_version': sys.version.split(' ', 1)[0],
|
||||
'os.name': os.name,
|
||||
'platform.version': platform.version(),
|
||||
'platform.machine': platform.machine(),
|
||||
'platform.python_implementation': python_implementation(),
|
||||
'extra': None # wheel extension
|
||||
}
|
||||
|
||||
def default_environment():
|
||||
"""Return copy of default PEP 385 globals dictionary."""
|
||||
return dict(_VARS)
|
||||
|
||||
class ASTWhitelist(ast.NodeTransformer):
|
||||
def __init__(self, statement):
|
||||
self.statement = statement # for error messages
|
||||
|
||||
ALLOWED = (ast.Compare, ast.BoolOp, ast.Attribute, ast.Name, ast.Load, ast.Str)
|
||||
# Bool operations
|
||||
ALLOWED += (ast.And, ast.Or)
|
||||
# Comparison operations
|
||||
ALLOWED += (ast.Eq, ast.Gt, ast.GtE, ast.In, ast.Is, ast.IsNot, ast.Lt, ast.LtE, ast.NotEq, ast.NotIn)
|
||||
|
||||
def visit(self, node):
|
||||
"""Ensure statement only contains allowed nodes."""
|
||||
if not isinstance(node, self.ALLOWED):
|
||||
raise SyntaxError('Not allowed in environment markers.\n%s\n%s' %
|
||||
(self.statement,
|
||||
(' ' * node.col_offset) + '^'))
|
||||
return ast.NodeTransformer.visit(self, node)
|
||||
|
||||
def visit_Attribute(self, node):
|
||||
"""Flatten one level of attribute access."""
|
||||
new_node = ast.Name("%s.%s" % (node.value.id, node.attr), node.ctx)
|
||||
return ast.copy_location(new_node, node)
|
||||
|
||||
def parse_marker(marker):
|
||||
tree = ast.parse(marker, mode='eval')
|
||||
new_tree = ASTWhitelist(marker).generic_visit(tree)
|
||||
return new_tree
|
||||
|
||||
def compile_marker(parsed_marker):
|
||||
return _builtin_compile(parsed_marker, '<environment marker>', 'eval',
|
||||
dont_inherit=True)
|
||||
|
||||
_cache = weakref.WeakValueDictionary()
|
||||
|
||||
def compile(marker):
|
||||
"""Return compiled marker as a function accepting an environment dict."""
|
||||
try:
|
||||
return _cache[marker]
|
||||
except KeyError:
|
||||
pass
|
||||
if not marker.strip():
|
||||
def marker_fn(environment=None, override=None):
|
||||
""""""
|
||||
return True
|
||||
else:
|
||||
compiled_marker = compile_marker(parse_marker(marker))
|
||||
def marker_fn(environment=None, override=None):
|
||||
"""override updates environment"""
|
||||
if override is None:
|
||||
override = {}
|
||||
if environment is None:
|
||||
environment = default_environment()
|
||||
environment.update(override)
|
||||
return eval(compiled_marker, environment)
|
||||
marker_fn.__doc__ = marker
|
||||
_cache[marker] = marker_fn
|
||||
return _cache[marker]
|
||||
|
||||
def interpret(marker, environment=None):
|
||||
return compile(marker)(environment)
|
||||
-541
@@ -1,541 +0,0 @@
|
||||
#!python
|
||||
"""Bootstrap distribute installation
|
||||
|
||||
If you want to use setuptools in your package's setup.py, just include this
|
||||
file in the same directory with it, and add this to the top of your setup.py::
|
||||
|
||||
from distribute_setup import use_setuptools
|
||||
use_setuptools()
|
||||
|
||||
If you want to require a specific version of setuptools, set a download
|
||||
mirror, or use an alternate download directory, you can do so by supplying
|
||||
the appropriate options to ``use_setuptools()``.
|
||||
|
||||
This file can also be run as a script to install or upgrade setuptools.
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import time
|
||||
import fnmatch
|
||||
import tempfile
|
||||
import tarfile
|
||||
import optparse
|
||||
|
||||
from distutils import log
|
||||
|
||||
try:
|
||||
from site import USER_SITE
|
||||
except ImportError:
|
||||
USER_SITE = None
|
||||
|
||||
try:
|
||||
import subprocess
|
||||
|
||||
def _python_cmd(*args):
|
||||
args = (sys.executable,) + args
|
||||
return subprocess.call(args) == 0
|
||||
|
||||
except ImportError:
|
||||
# will be used for python 2.3
|
||||
def _python_cmd(*args):
|
||||
args = (sys.executable,) + args
|
||||
# quoting arguments if windows
|
||||
if sys.platform == 'win32':
|
||||
def quote(arg):
|
||||
if ' ' in arg:
|
||||
return '"%s"' % arg
|
||||
return arg
|
||||
args = [quote(arg) for arg in args]
|
||||
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
|
||||
|
||||
DEFAULT_VERSION = "0.6.31"
|
||||
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
|
||||
SETUPTOOLS_FAKED_VERSION = "0.6c11"
|
||||
|
||||
SETUPTOOLS_PKG_INFO = """\
|
||||
Metadata-Version: 1.0
|
||||
Name: setuptools
|
||||
Version: %s
|
||||
Summary: xxxx
|
||||
Home-page: xxx
|
||||
Author: xxx
|
||||
Author-email: xxx
|
||||
License: xxx
|
||||
Description: xxx
|
||||
""" % SETUPTOOLS_FAKED_VERSION
|
||||
|
||||
|
||||
def _install(tarball, install_args=()):
|
||||
# extracting the tarball
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
log.warn('Extracting in %s', tmpdir)
|
||||
old_wd = os.getcwd()
|
||||
try:
|
||||
os.chdir(tmpdir)
|
||||
tar = tarfile.open(tarball)
|
||||
_extractall(tar)
|
||||
tar.close()
|
||||
|
||||
# going in the directory
|
||||
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
|
||||
os.chdir(subdir)
|
||||
log.warn('Now working in %s', subdir)
|
||||
|
||||
# installing
|
||||
log.warn('Installing Distribute')
|
||||
if not _python_cmd('setup.py', 'install', *install_args):
|
||||
log.warn('Something went wrong during the installation.')
|
||||
log.warn('See the error message above.')
|
||||
# exitcode will be 2
|
||||
return 2
|
||||
finally:
|
||||
os.chdir(old_wd)
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
|
||||
def _build_egg(egg, tarball, to_dir):
|
||||
# extracting the tarball
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
log.warn('Extracting in %s', tmpdir)
|
||||
old_wd = os.getcwd()
|
||||
try:
|
||||
os.chdir(tmpdir)
|
||||
tar = tarfile.open(tarball)
|
||||
_extractall(tar)
|
||||
tar.close()
|
||||
|
||||
# going in the directory
|
||||
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
|
||||
os.chdir(subdir)
|
||||
log.warn('Now working in %s', subdir)
|
||||
|
||||
# building an egg
|
||||
log.warn('Building a Distribute egg in %s', to_dir)
|
||||
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
|
||||
|
||||
finally:
|
||||
os.chdir(old_wd)
|
||||
shutil.rmtree(tmpdir)
|
||||
# returning the result
|
||||
log.warn(egg)
|
||||
if not os.path.exists(egg):
|
||||
raise IOError('Could not build the egg.')
|
||||
|
||||
|
||||
def _do_download(version, download_base, to_dir, download_delay):
|
||||
egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
|
||||
% (version, sys.version_info[0], sys.version_info[1]))
|
||||
if not os.path.exists(egg):
|
||||
tarball = download_setuptools(version, download_base,
|
||||
to_dir, download_delay)
|
||||
_build_egg(egg, tarball, to_dir)
|
||||
sys.path.insert(0, egg)
|
||||
import setuptools
|
||||
setuptools.bootstrap_install_from = egg
|
||||
|
||||
|
||||
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||
to_dir=os.curdir, download_delay=15, no_fake=True):
|
||||
# making sure we use the absolute path
|
||||
to_dir = os.path.abspath(to_dir)
|
||||
was_imported = 'pkg_resources' in sys.modules or \
|
||||
'setuptools' in sys.modules
|
||||
try:
|
||||
try:
|
||||
import pkg_resources
|
||||
if not hasattr(pkg_resources, '_distribute'):
|
||||
if not no_fake:
|
||||
_fake_setuptools()
|
||||
raise ImportError
|
||||
except ImportError:
|
||||
return _do_download(version, download_base, to_dir, download_delay)
|
||||
try:
|
||||
pkg_resources.require("distribute>=" + version)
|
||||
return
|
||||
except pkg_resources.VersionConflict:
|
||||
e = sys.exc_info()[1]
|
||||
if was_imported:
|
||||
sys.stderr.write(
|
||||
"The required version of distribute (>=%s) is not available,\n"
|
||||
"and can't be installed while this script is running. Please\n"
|
||||
"install a more recent version first, using\n"
|
||||
"'easy_install -U distribute'."
|
||||
"\n\n(Currently using %r)\n" % (version, e.args[0]))
|
||||
sys.exit(2)
|
||||
else:
|
||||
del pkg_resources, sys.modules['pkg_resources'] # reload ok
|
||||
return _do_download(version, download_base, to_dir,
|
||||
download_delay)
|
||||
except pkg_resources.DistributionNotFound:
|
||||
return _do_download(version, download_base, to_dir,
|
||||
download_delay)
|
||||
finally:
|
||||
if not no_fake:
|
||||
_create_fake_setuptools_pkg_info(to_dir)
|
||||
|
||||
|
||||
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||
to_dir=os.curdir, delay=15):
|
||||
"""Download distribute from a specified location and return its filename
|
||||
|
||||
`version` should be a valid distribute version number that is available
|
||||
as an egg for download under the `download_base` URL (which should end
|
||||
with a '/'). `to_dir` is the directory where the egg will be downloaded.
|
||||
`delay` is the number of seconds to pause before an actual download
|
||||
attempt.
|
||||
"""
|
||||
# making sure we use the absolute path
|
||||
to_dir = os.path.abspath(to_dir)
|
||||
try:
|
||||
from urllib.request import urlopen
|
||||
except ImportError:
|
||||
from urllib2 import urlopen
|
||||
tgz_name = "distribute-%s.tar.gz" % version
|
||||
url = download_base + tgz_name
|
||||
saveto = os.path.join(to_dir, tgz_name)
|
||||
src = dst = None
|
||||
if not os.path.exists(saveto): # Avoid repeated downloads
|
||||
try:
|
||||
log.warn("Downloading %s", url)
|
||||
src = urlopen(url)
|
||||
# Read/write all in one block, so we don't create a corrupt file
|
||||
# if the download is interrupted.
|
||||
data = src.read()
|
||||
dst = open(saveto, "wb")
|
||||
dst.write(data)
|
||||
finally:
|
||||
if src:
|
||||
src.close()
|
||||
if dst:
|
||||
dst.close()
|
||||
return os.path.realpath(saveto)
|
||||
|
||||
|
||||
def _no_sandbox(function):
|
||||
def __no_sandbox(*args, **kw):
|
||||
try:
|
||||
from setuptools.sandbox import DirectorySandbox
|
||||
if not hasattr(DirectorySandbox, '_old'):
|
||||
def violation(*args):
|
||||
pass
|
||||
DirectorySandbox._old = DirectorySandbox._violation
|
||||
DirectorySandbox._violation = violation
|
||||
patched = True
|
||||
else:
|
||||
patched = False
|
||||
except ImportError:
|
||||
patched = False
|
||||
|
||||
try:
|
||||
return function(*args, **kw)
|
||||
finally:
|
||||
if patched:
|
||||
DirectorySandbox._violation = DirectorySandbox._old
|
||||
del DirectorySandbox._old
|
||||
|
||||
return __no_sandbox
|
||||
|
||||
|
||||
def _patch_file(path, content):
|
||||
"""Will backup the file then patch it"""
|
||||
existing_content = open(path).read()
|
||||
if existing_content == content:
|
||||
# already patched
|
||||
log.warn('Already patched.')
|
||||
return False
|
||||
log.warn('Patching...')
|
||||
_rename_path(path)
|
||||
f = open(path, 'w')
|
||||
try:
|
||||
f.write(content)
|
||||
finally:
|
||||
f.close()
|
||||
return True
|
||||
|
||||
_patch_file = _no_sandbox(_patch_file)
|
||||
|
||||
|
||||
def _same_content(path, content):
|
||||
return open(path).read() == content
|
||||
|
||||
|
||||
def _rename_path(path):
|
||||
new_name = path + '.OLD.%s' % time.time()
|
||||
log.warn('Renaming %s to %s', path, new_name)
|
||||
os.rename(path, new_name)
|
||||
return new_name
|
||||
|
||||
|
||||
def _remove_flat_installation(placeholder):
|
||||
if not os.path.isdir(placeholder):
|
||||
log.warn('Unkown installation at %s', placeholder)
|
||||
return False
|
||||
found = False
|
||||
for file in os.listdir(placeholder):
|
||||
if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
log.warn('Could not locate setuptools*.egg-info')
|
||||
return
|
||||
|
||||
log.warn('Moving elements out of the way...')
|
||||
pkg_info = os.path.join(placeholder, file)
|
||||
if os.path.isdir(pkg_info):
|
||||
patched = _patch_egg_dir(pkg_info)
|
||||
else:
|
||||
patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
|
||||
|
||||
if not patched:
|
||||
log.warn('%s already patched.', pkg_info)
|
||||
return False
|
||||
# now let's move the files out of the way
|
||||
for element in ('setuptools', 'pkg_resources.py', 'site.py'):
|
||||
element = os.path.join(placeholder, element)
|
||||
if os.path.exists(element):
|
||||
_rename_path(element)
|
||||
else:
|
||||
log.warn('Could not find the %s element of the '
|
||||
'Setuptools distribution', element)
|
||||
return True
|
||||
|
||||
_remove_flat_installation = _no_sandbox(_remove_flat_installation)
|
||||
|
||||
|
||||
def _after_install(dist):
|
||||
log.warn('After install bootstrap.')
|
||||
placeholder = dist.get_command_obj('install').install_purelib
|
||||
_create_fake_setuptools_pkg_info(placeholder)
|
||||
|
||||
|
||||
def _create_fake_setuptools_pkg_info(placeholder):
|
||||
if not placeholder or not os.path.exists(placeholder):
|
||||
log.warn('Could not find the install location')
|
||||
return
|
||||
pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
|
||||
setuptools_file = 'setuptools-%s-py%s.egg-info' % \
|
||||
(SETUPTOOLS_FAKED_VERSION, pyver)
|
||||
pkg_info = os.path.join(placeholder, setuptools_file)
|
||||
if os.path.exists(pkg_info):
|
||||
log.warn('%s already exists', pkg_info)
|
||||
return
|
||||
|
||||
log.warn('Creating %s', pkg_info)
|
||||
try:
|
||||
f = open(pkg_info, 'w')
|
||||
except EnvironmentError:
|
||||
log.warn("Don't have permissions to write %s, skipping", pkg_info)
|
||||
return
|
||||
try:
|
||||
f.write(SETUPTOOLS_PKG_INFO)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
pth_file = os.path.join(placeholder, 'setuptools.pth')
|
||||
log.warn('Creating %s', pth_file)
|
||||
f = open(pth_file, 'w')
|
||||
try:
|
||||
f.write(os.path.join(os.curdir, setuptools_file))
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
_create_fake_setuptools_pkg_info = _no_sandbox(
|
||||
_create_fake_setuptools_pkg_info
|
||||
)
|
||||
|
||||
|
||||
def _patch_egg_dir(path):
|
||||
# let's check if it's already patched
|
||||
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
|
||||
if os.path.exists(pkg_info):
|
||||
if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
|
||||
log.warn('%s already patched.', pkg_info)
|
||||
return False
|
||||
_rename_path(path)
|
||||
os.mkdir(path)
|
||||
os.mkdir(os.path.join(path, 'EGG-INFO'))
|
||||
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
|
||||
f = open(pkg_info, 'w')
|
||||
try:
|
||||
f.write(SETUPTOOLS_PKG_INFO)
|
||||
finally:
|
||||
f.close()
|
||||
return True
|
||||
|
||||
_patch_egg_dir = _no_sandbox(_patch_egg_dir)
|
||||
|
||||
|
||||
def _before_install():
|
||||
log.warn('Before install bootstrap.')
|
||||
_fake_setuptools()
|
||||
|
||||
|
||||
def _under_prefix(location):
|
||||
if 'install' not in sys.argv:
|
||||
return True
|
||||
args = sys.argv[sys.argv.index('install') + 1:]
|
||||
for index, arg in enumerate(args):
|
||||
for option in ('--root', '--prefix'):
|
||||
if arg.startswith('%s=' % option):
|
||||
top_dir = arg.split('root=')[-1]
|
||||
return location.startswith(top_dir)
|
||||
elif arg == option:
|
||||
if len(args) > index:
|
||||
top_dir = args[index + 1]
|
||||
return location.startswith(top_dir)
|
||||
if arg == '--user' and USER_SITE is not None:
|
||||
return location.startswith(USER_SITE)
|
||||
return True
|
||||
|
||||
|
||||
def _fake_setuptools():
|
||||
log.warn('Scanning installed packages')
|
||||
try:
|
||||
import pkg_resources
|
||||
except ImportError:
|
||||
# we're cool
|
||||
log.warn('Setuptools or Distribute does not seem to be installed.')
|
||||
return
|
||||
ws = pkg_resources.working_set
|
||||
try:
|
||||
setuptools_dist = ws.find(
|
||||
pkg_resources.Requirement.parse('setuptools', replacement=False)
|
||||
)
|
||||
except TypeError:
|
||||
# old distribute API
|
||||
setuptools_dist = ws.find(
|
||||
pkg_resources.Requirement.parse('setuptools')
|
||||
)
|
||||
|
||||
if setuptools_dist is None:
|
||||
log.warn('No setuptools distribution found')
|
||||
return
|
||||
# detecting if it was already faked
|
||||
setuptools_location = setuptools_dist.location
|
||||
log.warn('Setuptools installation detected at %s', setuptools_location)
|
||||
|
||||
# if --root or --preix was provided, and if
|
||||
# setuptools is not located in them, we don't patch it
|
||||
if not _under_prefix(setuptools_location):
|
||||
log.warn('Not patching, --root or --prefix is installing Distribute'
|
||||
' in another location')
|
||||
return
|
||||
|
||||
# let's see if its an egg
|
||||
if not setuptools_location.endswith('.egg'):
|
||||
log.warn('Non-egg installation')
|
||||
res = _remove_flat_installation(setuptools_location)
|
||||
if not res:
|
||||
return
|
||||
else:
|
||||
log.warn('Egg installation')
|
||||
pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
|
||||
if (os.path.exists(pkg_info) and
|
||||
_same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
|
||||
log.warn('Already patched.')
|
||||
return
|
||||
log.warn('Patching...')
|
||||
# let's create a fake egg replacing setuptools one
|
||||
res = _patch_egg_dir(setuptools_location)
|
||||
if not res:
|
||||
return
|
||||
log.warn('Patching complete.')
|
||||
_relaunch()
|
||||
|
||||
|
||||
def _relaunch():
|
||||
log.warn('Relaunching...')
|
||||
# we have to relaunch the process
|
||||
# pip marker to avoid a relaunch bug
|
||||
_cmd1 = ['-c', 'install', '--single-version-externally-managed']
|
||||
_cmd2 = ['-c', 'install', '--record']
|
||||
if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2:
|
||||
sys.argv[0] = 'setup.py'
|
||||
args = [sys.executable] + sys.argv
|
||||
sys.exit(subprocess.call(args))
|
||||
|
||||
|
||||
def _extractall(self, path=".", members=None):
|
||||
"""Extract all members from the archive to the current working
|
||||
directory and set owner, modification time and permissions on
|
||||
directories afterwards. `path' specifies a different directory
|
||||
to extract to. `members' is optional and must be a subset of the
|
||||
list returned by getmembers().
|
||||
"""
|
||||
import copy
|
||||
import operator
|
||||
from tarfile import ExtractError
|
||||
directories = []
|
||||
|
||||
if members is None:
|
||||
members = self
|
||||
|
||||
for tarinfo in members:
|
||||
if tarinfo.isdir():
|
||||
# Extract directories with a safe mode.
|
||||
directories.append(tarinfo)
|
||||
tarinfo = copy.copy(tarinfo)
|
||||
tarinfo.mode = 448 # decimal for oct 0700
|
||||
self.extract(tarinfo, path)
|
||||
|
||||
# Reverse sort directories.
|
||||
if sys.version_info < (2, 4):
|
||||
def sorter(dir1, dir2):
|
||||
return cmp(dir1.name, dir2.name)
|
||||
directories.sort(sorter)
|
||||
directories.reverse()
|
||||
else:
|
||||
directories.sort(key=operator.attrgetter('name'), reverse=True)
|
||||
|
||||
# Set correct owner, mtime and filemode on directories.
|
||||
for tarinfo in directories:
|
||||
dirpath = os.path.join(path, tarinfo.name)
|
||||
try:
|
||||
self.chown(tarinfo, dirpath)
|
||||
self.utime(tarinfo, dirpath)
|
||||
self.chmod(tarinfo, dirpath)
|
||||
except ExtractError:
|
||||
e = sys.exc_info()[1]
|
||||
if self.errorlevel > 1:
|
||||
raise
|
||||
else:
|
||||
self._dbg(1, "tarfile: %s" % e)
|
||||
|
||||
|
||||
def _build_install_args(options):
|
||||
"""
|
||||
Build the arguments to 'python setup.py install' on the distribute package
|
||||
"""
|
||||
install_args = []
|
||||
if options.user_install:
|
||||
if sys.version_info < (2, 6):
|
||||
log.warn("--user requires Python 2.6 or later")
|
||||
raise SystemExit(1)
|
||||
install_args.append('--user')
|
||||
return install_args
|
||||
|
||||
def _parse_args():
|
||||
"""
|
||||
Parse the command line for options
|
||||
"""
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option(
|
||||
'--user', dest='user_install', action='store_true', default=False,
|
||||
help='install in user site package (requires Python 2.6 or later)')
|
||||
parser.add_option(
|
||||
'--download-base', dest='download_base', metavar="URL",
|
||||
default=DEFAULT_URL,
|
||||
help='alternative URL from where to download the distribute package')
|
||||
options, args = parser.parse_args()
|
||||
# positional arguments are ignored
|
||||
return options
|
||||
|
||||
def main(version=DEFAULT_VERSION):
|
||||
"""Install or upgrade setuptools and EasyInstall"""
|
||||
options = _parse_args()
|
||||
tarball = download_setuptools(download_base=options.download_base)
|
||||
return _install(tarball, _build_install_args(options))
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
# Makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
|
||||
|
||||
.PHONY: help clean html web pickle htmlhelp latex changes linkcheck
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " changes to make an overview over all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
|
||||
clean:
|
||||
-rm -rf build/*
|
||||
|
||||
html:
|
||||
mkdir -p build/html build/doctrees
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in build/html."
|
||||
|
||||
pickle:
|
||||
mkdir -p build/pickle build/doctrees
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
web: pickle
|
||||
|
||||
json:
|
||||
mkdir -p build/json build/doctrees
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) build/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp:
|
||||
mkdir -p build/htmlhelp build/doctrees
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in build/htmlhelp."
|
||||
|
||||
latex:
|
||||
mkdir -p build/latex build/doctrees
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in build/latex."
|
||||
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
|
||||
"run these through (pdf)latex."
|
||||
|
||||
changes:
|
||||
mkdir -p build/changes build/doctrees
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes
|
||||
@echo
|
||||
@echo "The overview file is in build/changes."
|
||||
|
||||
linkcheck:
|
||||
mkdir -p build/linkcheck build/doctrees
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in build/linkcheck/output.txt."
|
||||
@@ -1,8 +0,0 @@
|
||||
<h3>Download</h3>
|
||||
|
||||
<p>Current version: <b>{{ version }}</b></p>
|
||||
<p>Get Distribute from the <a href="http://pypi.python.org/pypi/distribute"> Python Package Index</a>
|
||||
|
||||
<h3>Questions? Suggestions? Contributions?</h3>
|
||||
|
||||
<p>Visit the <a href="http://bitbucket.org/tarek/distribute">Distribute project page</a> </p>
|
||||
@@ -1,237 +0,0 @@
|
||||
/**
|
||||
* Sphinx stylesheet -- default theme
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 100%;
|
||||
background-color: #111111;
|
||||
color: #555555;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 0 0 300px;
|
||||
}
|
||||
|
||||
hr{
|
||||
border: 1px solid #B1B4B6;
|
||||
}
|
||||
|
||||
div.document {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: #ffffff;
|
||||
color: #3E4349;
|
||||
padding: 1em 30px 30px 30px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
color: #555;
|
||||
width: 100%;
|
||||
padding: 13px 0;
|
||||
text-align: center;
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #444444;
|
||||
}
|
||||
|
||||
div.related {
|
||||
background-color: #6BA81E;
|
||||
line-height: 36px;
|
||||
color: #ffffff;
|
||||
text-shadow: 0px 1px 0 #444444;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
div.related a {
|
||||
color: #E2F3CC;
|
||||
}
|
||||
|
||||
div.related .right {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
font-size: 0.9em;
|
||||
line-height: 1.5em;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper{
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3,
|
||||
div.sphinxsidebar h4 {
|
||||
font-family: Arial, sans-serif;
|
||||
color: #222222;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 5px 10px;
|
||||
text-shadow: 1px 1px 0 white
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 a {
|
||||
color: #444444;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p {
|
||||
color: #888888;
|
||||
padding: 5px 20px;
|
||||
margin: 0.5em 0px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.topless {
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
margin: 10px 10px 10px 20px;
|
||||
padding: 0;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #444444;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a:hover {
|
||||
color: #E32E00;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #cccccc;
|
||||
font-family: sans-serif;
|
||||
font-size: 1.1em;
|
||||
padding: 0.15em 0.3em;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input[type=text]{
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
a {
|
||||
color: #005B81;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #E32E00;
|
||||
}
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: Arial, sans-serif;
|
||||
font-weight: normal;
|
||||
color: #212224;
|
||||
margin: 30px 0px 10px 0px;
|
||||
padding: 5px 0 5px 0px;
|
||||
text-shadow: 0px 1px 0 white;
|
||||
border-bottom: 1px solid #C8D5E3;
|
||||
}
|
||||
|
||||
div.body h1 { margin-top: 0; font-size: 200%; }
|
||||
div.body h2 { font-size: 150%; }
|
||||
div.body h3 { font-size: 120%; }
|
||||
div.body h4 { font-size: 110%; }
|
||||
div.body h5 { font-size: 100%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: #c60f0f;
|
||||
font-size: 0.8em;
|
||||
padding: 0 4px 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
background-color: #c60f0f;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
line-height: 1.8em;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title + p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.highlight{
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eeeeee;
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #ffffcc;
|
||||
border: 1px solid #ffff66;
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: #fafafa;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
div.warning {
|
||||
background-color: #ffe4e4;
|
||||
border: 1px solid #ff6666;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 10px;
|
||||
background-color: #fafafa;
|
||||
color: #222222;
|
||||
line-height: 1.5em;
|
||||
font-size: 1.1em;
|
||||
margin: 1.5em 0 1.5em 0;
|
||||
-webkit-box-shadow: 0px 0px 4px #d8d8d8;
|
||||
-moz-box-shadow: 0px 0px 4px #d8d8d8;
|
||||
box-shadow: 0px 0px 4px #d8d8d8;
|
||||
}
|
||||
|
||||
tt {
|
||||
color: #222222;
|
||||
padding: 1px 2px;
|
||||
font-size: 1.2em;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
#table-of-contents ul {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
.c { color: #999988; font-style: italic } /* Comment */
|
||||
.k { font-weight: bold } /* Keyword */
|
||||
.o { font-weight: bold } /* Operator */
|
||||
.cm { color: #999988; font-style: italic } /* Comment.Multiline */
|
||||
.cp { color: #999999; font-weight: bold } /* Comment.preproc */
|
||||
.c1 { color: #999988; font-style: italic } /* Comment.Single */
|
||||
.gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
|
||||
.ge { font-style: italic } /* Generic.Emph */
|
||||
.gr { color: #aa0000 } /* Generic.Error */
|
||||
.gh { color: #999999 } /* Generic.Heading */
|
||||
.gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
|
||||
.go { color: #111 } /* Generic.Output */
|
||||
.gp { color: #555555 } /* Generic.Prompt */
|
||||
.gs { font-weight: bold } /* Generic.Strong */
|
||||
.gu { color: #aaaaaa } /* Generic.Subheading */
|
||||
.gt { color: #aa0000 } /* Generic.Traceback */
|
||||
.kc { font-weight: bold } /* Keyword.Constant */
|
||||
.kd { font-weight: bold } /* Keyword.Declaration */
|
||||
.kp { font-weight: bold } /* Keyword.Pseudo */
|
||||
.kr { font-weight: bold } /* Keyword.Reserved */
|
||||
.kt { color: #445588; font-weight: bold } /* Keyword.Type */
|
||||
.m { color: #009999 } /* Literal.Number */
|
||||
.s { color: #bb8844 } /* Literal.String */
|
||||
.na { color: #008080 } /* Name.Attribute */
|
||||
.nb { color: #999999 } /* Name.Builtin */
|
||||
.nc { color: #445588; font-weight: bold } /* Name.Class */
|
||||
.no { color: #ff99ff } /* Name.Constant */
|
||||
.ni { color: #800080 } /* Name.Entity */
|
||||
.ne { color: #990000; font-weight: bold } /* Name.Exception */
|
||||
.nf { color: #990000; font-weight: bold } /* Name.Function */
|
||||
.nn { color: #555555 } /* Name.Namespace */
|
||||
.nt { color: #000080 } /* Name.Tag */
|
||||
.nv { color: purple } /* Name.Variable */
|
||||
.ow { font-weight: bold } /* Operator.Word */
|
||||
.mf { color: #009999 } /* Literal.Number.Float */
|
||||
.mh { color: #009999 } /* Literal.Number.Hex */
|
||||
.mi { color: #009999 } /* Literal.Number.Integer */
|
||||
.mo { color: #009999 } /* Literal.Number.Oct */
|
||||
.sb { color: #bb8844 } /* Literal.String.Backtick */
|
||||
.sc { color: #bb8844 } /* Literal.String.Char */
|
||||
.sd { color: #bb8844 } /* Literal.String.Doc */
|
||||
.s2 { color: #bb8844 } /* Literal.String.Double */
|
||||
.se { color: #bb8844 } /* Literal.String.Escape */
|
||||
.sh { color: #bb8844 } /* Literal.String.Heredoc */
|
||||
.si { color: #bb8844 } /* Literal.String.Interpol */
|
||||
.sx { color: #bb8844 } /* Literal.String.Other */
|
||||
.sr { color: #808000 } /* Literal.String.Regex */
|
||||
.s1 { color: #bb8844 } /* Literal.String.Single */
|
||||
.ss { color: #bb8844 } /* Literal.String.Symbol */
|
||||
.bp { color: #999999 } /* Name.Builtin.Pseudo */
|
||||
.vc { color: #ff99ff } /* Name.Variable.Class */
|
||||
.vg { color: #ff99ff } /* Name.Variable.Global */
|
||||
.vi { color: #ff99ff } /* Name.Variable.Instance */
|
||||
.il { color: #009999 } /* Literal.Number.Integer.Long */
|
||||
@@ -1,4 +0,0 @@
|
||||
[theme]
|
||||
inherit = basic
|
||||
stylesheet = nature.css
|
||||
pygments_style = tango
|
||||
-197
@@ -1,197 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Distribute documentation build configuration file, created by
|
||||
# sphinx-quickstart on Fri Jul 17 14:22:37 2009.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# The contents of this file are pickled, so don't put values in the namespace
|
||||
# that aren't pickleable (module imports are okay, they're removed automatically).
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.append(os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = []
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.txt'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'Distribute'
|
||||
copyright = u'2009-2011, The fellowship of the packaging'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.6.31'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.6.31'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of documents that shouldn't be included in the build.
|
||||
#unused_docs = []
|
||||
|
||||
# List of directories, relative to source directory, that shouldn't be searched
|
||||
# for source files.
|
||||
exclude_trees = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
html_theme = 'nature'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = ['_theme']
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
html_title = "Distribute documentation"
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
html_short_title = "Distribute"
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
#html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
#html_last_updated_fmt = '%b %d, %Y'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
html_sidebars = {'index': 'indexsidebar.html'}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
html_use_modindex = False
|
||||
|
||||
# If false, no index is generated.
|
||||
html_use_index = False
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = ''
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'Distributedoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'Distribute.tex', ur'Distribute Documentation',
|
||||
ur'The fellowship of the packaging', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
#latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_use_modindex = True
|
||||
-1597
File diff suppressed because it is too large
Load Diff
-36
@@ -1,36 +0,0 @@
|
||||
Welcome to Distribute's documentation!
|
||||
======================================
|
||||
|
||||
`Distribute` is a fork of the `Setuptools` project.
|
||||
|
||||
Distribute is intended to replace Setuptools as the standard method for
|
||||
working with Python module distributions.
|
||||
|
||||
For those who may wonder why they should switch to Distribute over Setuptools, it’s quite simple:
|
||||
|
||||
- Distribute is a drop-in replacement for Setuptools
|
||||
- The code is actively maintained, and has over 10 commiters
|
||||
- Distribute offers Python 3 support !
|
||||
|
||||
Documentation content:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
roadmap
|
||||
python3
|
||||
using
|
||||
setuptools
|
||||
easy_install
|
||||
pkg_resources
|
||||
|
||||
|
||||
.. image:: http://python-distribute.org/pip_distribute.png
|
||||
|
||||
Design done by Idan Gazit (http://pixane.com) - License: cc-by-3.0
|
||||
|
||||
Copy & paste::
|
||||
|
||||
curl -O http://python-distribute.org/distribute_setup.py
|
||||
python distribute_setup.py
|
||||
easy_install pip
|
||||
-1955
File diff suppressed because it is too large
Load Diff
-121
@@ -1,121 +0,0 @@
|
||||
=====================================================
|
||||
Supporting both Python 2 and Python 3 with Distribute
|
||||
=====================================================
|
||||
|
||||
Starting with version 0.6.2, Distribute supports Python 3. Installing and
|
||||
using distribute for Python 3 code works exactly the same as for Python 2
|
||||
code, but Distribute also helps you to support Python 2 and Python 3 from
|
||||
the same source code by letting you run 2to3 on the code as a part of the
|
||||
build process, by setting the keyword parameter ``use_2to3`` to True.
|
||||
|
||||
|
||||
Distribute as help during porting
|
||||
=================================
|
||||
|
||||
Distribute can make the porting process much easier by automatically running
|
||||
2to3 as a part of the test running. To do this you need to configure the
|
||||
setup.py so that you can run the unit tests with ``python setup.py test``.
|
||||
|
||||
See :ref:`test` for more information on this.
|
||||
|
||||
Once you have the tests running under Python 2, you can add the use_2to3
|
||||
keyword parameters to setup(), and start running the tests under Python 3.
|
||||
The test command will now first run the build command during which the code
|
||||
will be converted with 2to3, and the tests will then be run from the build
|
||||
directory, as opposed from the source directory as is normally done.
|
||||
|
||||
Distribute will convert all Python files, and also all doctests in Python
|
||||
files. However, if you have doctests located in separate text files, these
|
||||
will not automatically be converted. By adding them to the
|
||||
``convert_2to3_doctests`` keyword parameter Distrubute will convert them as
|
||||
well.
|
||||
|
||||
By default, the conversion uses all fixers in the ``lib2to3.fixers`` package.
|
||||
To use additional fixers, the parameter ``use_2to3_fixers`` can be set
|
||||
to a list of names of packages containing fixers. To exclude fixers, the
|
||||
parameter ``use_2to3_exclude_fixers`` can be set to fixer names to be
|
||||
skipped.
|
||||
|
||||
A typical setup.py can look something like this::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='your.module',
|
||||
version = '1.0',
|
||||
description='This is your awesome module',
|
||||
author='You',
|
||||
author_email='your@email',
|
||||
package_dir = {'': 'src'},
|
||||
packages = ['your', 'you.module'],
|
||||
test_suite = 'your.module.tests',
|
||||
use_2to3 = True,
|
||||
convert_2to3_doctests = ['src/your/module/README.txt'],
|
||||
use_2to3_fixers = ['your.fixers'],
|
||||
use_2to3_exclude_fixers = ['lib2to3.fixes.fix_import'],
|
||||
)
|
||||
|
||||
Differential conversion
|
||||
-----------------------
|
||||
|
||||
Note that a file will only be copied and converted during the build process
|
||||
if the source file has been changed. If you add a file to the doctests
|
||||
that should be converted, it will not be converted the next time you run
|
||||
the tests, since it hasn't been modified. You need to remove it from the
|
||||
build directory. Also if you run the build, install or test commands before
|
||||
adding the use_2to3 parameter, you will have to remove the build directory
|
||||
before you run the test command, as the files otherwise will seem updated,
|
||||
and no conversion will happen.
|
||||
|
||||
In general, if code doesn't seem to be converted, deleting the build directory
|
||||
and trying again is a good saferguard against the build directory getting
|
||||
"out of sync" with the source directory.
|
||||
|
||||
Distributing Python 3 modules
|
||||
=============================
|
||||
|
||||
You can distribute your modules with Python 3 support in different ways. A
|
||||
normal source distribution will work, but can be slow in installing, as the
|
||||
2to3 process will be run during the install. But you can also distribute
|
||||
the module in binary format, such as a binary egg. That egg will contain the
|
||||
already converted code, and hence no 2to3 conversion is needed during install.
|
||||
|
||||
Advanced features
|
||||
=================
|
||||
|
||||
If you don't want to run the 2to3 conversion on the doctests in Python files,
|
||||
you can turn that off by setting ``setuptools.use_2to3_on_doctests = False``.
|
||||
|
||||
Note on compatibility with setuptools
|
||||
=====================================
|
||||
|
||||
Setuptools do not know about the new keyword parameters to support Python 3.
|
||||
As a result it will warn about the unknown keyword parameters if you use
|
||||
setuptools instead of Distribute under Python 2. This is not an error, and
|
||||
install process will continue as normal, but if you want to get rid of that
|
||||
error this is easy. Simply conditionally add the new parameters into an extra
|
||||
dict and pass that dict into setup()::
|
||||
|
||||
from setuptools import setup
|
||||
import sys
|
||||
|
||||
extra = {}
|
||||
if sys.version_info >= (3,):
|
||||
extra['use_2to3'] = True
|
||||
extra['convert_2to3_doctests'] = ['src/your/module/README.txt']
|
||||
extra['use_2to3_fixers'] = ['your.fixers']
|
||||
|
||||
setup(
|
||||
name='your.module',
|
||||
version = '1.0',
|
||||
description='This is your awesome module',
|
||||
author='You',
|
||||
author_email='your@email',
|
||||
package_dir = {'': 'src'},
|
||||
packages = ['your', 'you.module'],
|
||||
test_suite = 'your.module.tests',
|
||||
**extra
|
||||
)
|
||||
|
||||
This way the parameters will only be used under Python 3, where you have to
|
||||
use Distribute.
|
||||
-86
@@ -1,86 +0,0 @@
|
||||
=======
|
||||
Roadmap
|
||||
=======
|
||||
|
||||
Distribute has two branches:
|
||||
|
||||
- 0.6.x : provides a Setuptools-0.6cX compatible version
|
||||
- 0.7.x : will provide a refactoring
|
||||
|
||||
0.6.x
|
||||
=====
|
||||
|
||||
Not "much" is going to happen here, we want this branch to be helpful
|
||||
to the community *today* by addressing the 40-or-so bugs
|
||||
that were found in Setuptools and never fixed. This is eventually
|
||||
happen soon because its development is
|
||||
fast : there are up to 5 commiters that are working on it very often
|
||||
(and the number grows weekly.)
|
||||
|
||||
The biggest issue with this branch is that it is providing the same
|
||||
packages and modules setuptools does, and this
|
||||
requires some bootstrapping work where we make sure once Distribute is
|
||||
installed, all Distribution that requires Setuptools
|
||||
will continue to work. This is done by faking the metadata of
|
||||
Setuptools 0.6c9. That's the only way we found to do this.
|
||||
|
||||
There's one major thing though: thanks to the work of Lennart, Alex,
|
||||
Martin, this branch supports Python 3,
|
||||
which is great to have to speed up Py3 adoption.
|
||||
|
||||
The goal of the 0.6.x is to remove as much bugs as we can, and try if
|
||||
possible to remove the patches done
|
||||
on Distutils. We will support 0.6.x maintenance for years and we will
|
||||
promote its usage everywhere instead of
|
||||
Setuptools.
|
||||
|
||||
Some new commands are added there, when they are helpful and don't
|
||||
interact with the rest. I am thinking
|
||||
about "upload_docs" that let you upload documentation to PyPI. The
|
||||
goal is to move it to Distutils
|
||||
at some point, if the documentation feature of PyPI stays and starts to be used.
|
||||
|
||||
0.7.x
|
||||
=====
|
||||
|
||||
We've started to refactor Distribute with this roadmap in mind (and
|
||||
no, as someone said, it's not vaporware,
|
||||
we've done a lot already)
|
||||
|
||||
- 0.7.x can be installed and used with 0.6.x
|
||||
|
||||
- easy_install is going to be deprecated ! use Pip !
|
||||
|
||||
- the version system will be deprecated, in favor of the one in Distutils
|
||||
|
||||
- no more Distutils monkey-patch that happens once you use the code
|
||||
(things like 'from distutils import cmd; cmd.Command = CustomCommand')
|
||||
|
||||
- no more custom site.py (that is: if something misses in Python's
|
||||
site.py we'll add it there instead of patching it)
|
||||
|
||||
- no more namespaced packages system, if PEP 382 (namespaces package
|
||||
support) makes it to 2.7
|
||||
|
||||
- The code is splitted in many packages and might be distributed under
|
||||
several distributions.
|
||||
|
||||
- distribute.resources: that's the old pkg_resources, but
|
||||
reorganized in clean, pep-8 modules. This package will
|
||||
only contain the query APIs and will focus on being PEP 376
|
||||
compatible. We will promote its usage and see if Pip wants
|
||||
to use it as a basis.
|
||||
It will probably shrink a lot though, once the stdlib provides PEP 376 support.
|
||||
|
||||
- distribute.entrypoints: that's the old pkg_resources entry points
|
||||
system, but on its own. it uses distribute.resources
|
||||
|
||||
- distribute.index: that's package_index and a few other things.
|
||||
everything required to interact with PyPI. We will promote
|
||||
its usage and see if Pip wants to use it as a basis.
|
||||
|
||||
- distribute.core (might be renamed to main): that's everything
|
||||
else, and uses the other packages.
|
||||
|
||||
Goal: A first release before (or when) Python 2.7 / 3.2 is out.
|
||||
|
||||
-3236
File diff suppressed because it is too large
Load Diff
-21
@@ -1,21 +0,0 @@
|
||||
================================
|
||||
Using Distribute in your project
|
||||
================================
|
||||
|
||||
To use Distribute in your project, the recommended way is to ship
|
||||
`distribute_setup.py` alongside your `setup.py` script and call
|
||||
it at the very begining of `setup.py` like this::
|
||||
|
||||
from distribute_setup import use_setuptools
|
||||
use_setuptools()
|
||||
|
||||
Another way is to add ``Distribute`` in the ``install_requires`` option::
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
setup(...
|
||||
install_requires=['distribute']
|
||||
)
|
||||
|
||||
|
||||
XXX to be finished
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
"""Run the EasyInstall command"""
|
||||
|
||||
if __name__ == '__main__':
|
||||
from setuptools.command.easy_install import main
|
||||
main()
|
||||
Vendored
-327
@@ -1,327 +0,0 @@
|
||||
/* Setuptools Script Launcher for Windows
|
||||
|
||||
This is a stub executable for Windows that functions somewhat like
|
||||
Effbot's "exemaker", in that it runs a script with the same name but
|
||||
a .py extension, using information from a #! line. It differs in that
|
||||
it spawns the actual Python executable, rather than attempting to
|
||||
hook into the Python DLL. This means that the script will run with
|
||||
sys.executable set to the Python executable, where exemaker ends up with
|
||||
sys.executable pointing to itself. (Which means it won't work if you try
|
||||
to run another Python process using sys.executable.)
|
||||
|
||||
To build/rebuild with mingw32, do this in the setuptools project directory:
|
||||
|
||||
gcc -DGUI=0 -mno-cygwin -O -s -o setuptools/cli.exe launcher.c
|
||||
gcc -DGUI=1 -mwindows -mno-cygwin -O -s -o setuptools/gui.exe launcher.c
|
||||
|
||||
It links to msvcrt.dll, but this shouldn't be a problem since it doesn't
|
||||
actually run Python in the same process. Note that using 'exec' instead
|
||||
of 'spawn' doesn't work, because on Windows this leads to the Python
|
||||
executable running in the *background*, attached to the same console
|
||||
window, meaning you get a command prompt back *before* Python even finishes
|
||||
starting. So, we have to use spawnv() and wait for Python to exit before
|
||||
continuing. :(
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
#include <tchar.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int child_pid=0;
|
||||
|
||||
int fail(char *format, char *data) {
|
||||
/* Print error message to stderr and return 2 */
|
||||
fprintf(stderr, format, data);
|
||||
return 2;
|
||||
}
|
||||
|
||||
char *quoted(char *data) {
|
||||
int i, ln = strlen(data), nb;
|
||||
|
||||
/* We allocate twice as much space as needed to deal with worse-case
|
||||
of having to escape everything. */
|
||||
char *result = calloc(ln*2+3, sizeof(char));
|
||||
char *presult = result;
|
||||
|
||||
*presult++ = '"';
|
||||
for (nb=0, i=0; i < ln; i++)
|
||||
{
|
||||
if (data[i] == '\\')
|
||||
nb += 1;
|
||||
else if (data[i] == '"')
|
||||
{
|
||||
for (; nb > 0; nb--)
|
||||
*presult++ = '\\';
|
||||
*presult++ = '\\';
|
||||
}
|
||||
else
|
||||
nb = 0;
|
||||
*presult++ = data[i];
|
||||
}
|
||||
|
||||
for (; nb > 0; nb--) /* Deal w trailing slashes */
|
||||
*presult++ = '\\';
|
||||
|
||||
*presult++ = '"';
|
||||
*presult++ = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
char *loadable_exe(char *exename) {
|
||||
/* HINSTANCE hPython; DLL handle for python executable */
|
||||
char *result;
|
||||
|
||||
/* hPython = LoadLibraryEx(exename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||
if (!hPython) return NULL; */
|
||||
|
||||
/* Return the absolute filename for spawnv */
|
||||
result = calloc(MAX_PATH, sizeof(char));
|
||||
strncpy(result, exename, MAX_PATH);
|
||||
/*if (result) GetModuleFileNameA(hPython, result, MAX_PATH);
|
||||
|
||||
FreeLibrary(hPython); */
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
char *find_exe(char *exename, char *script) {
|
||||
char drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
|
||||
char path[_MAX_PATH], c, *result;
|
||||
|
||||
/* convert slashes to backslashes for uniform search below */
|
||||
result = exename;
|
||||
while (c = *result++) if (c=='/') result[-1] = '\\';
|
||||
|
||||
_splitpath(exename, drive, dir, fname, ext);
|
||||
if (drive[0] || dir[0]=='\\') {
|
||||
return loadable_exe(exename); /* absolute path, use directly */
|
||||
}
|
||||
/* Use the script's parent directory, which should be the Python home
|
||||
(This should only be used for bdist_wininst-installed scripts, because
|
||||
easy_install-ed scripts use the absolute path to python[w].exe
|
||||
*/
|
||||
_splitpath(script, drive, dir, fname, ext);
|
||||
result = dir + strlen(dir) -1;
|
||||
if (*result == '\\') result--;
|
||||
while (*result != '\\' && result>=dir) *result-- = 0;
|
||||
_makepath(path, drive, dir, exename, NULL);
|
||||
return loadable_exe(path);
|
||||
}
|
||||
|
||||
|
||||
char **parse_argv(char *cmdline, int *argc)
|
||||
{
|
||||
/* Parse a command line in-place using MS C rules */
|
||||
|
||||
char **result = calloc(strlen(cmdline), sizeof(char *));
|
||||
char *output = cmdline;
|
||||
char c;
|
||||
int nb = 0;
|
||||
int iq = 0;
|
||||
*argc = 0;
|
||||
|
||||
result[0] = output;
|
||||
while (isspace(*cmdline)) cmdline++; /* skip leading spaces */
|
||||
|
||||
do {
|
||||
c = *cmdline++;
|
||||
if (!c || (isspace(c) && !iq)) {
|
||||
while (nb) {*output++ = '\\'; nb--; }
|
||||
*output++ = 0;
|
||||
result[++*argc] = output;
|
||||
if (!c) return result;
|
||||
while (isspace(*cmdline)) cmdline++; /* skip leading spaces */
|
||||
if (!*cmdline) return result; /* avoid empty arg if trailing ws */
|
||||
continue;
|
||||
}
|
||||
if (c == '\\')
|
||||
++nb; /* count \'s */
|
||||
else {
|
||||
if (c == '"') {
|
||||
if (!(nb & 1)) { iq = !iq; c = 0; } /* skip " unless odd # of \ */
|
||||
nb = nb >> 1; /* cut \'s in half */
|
||||
}
|
||||
while (nb) {*output++ = '\\'; nb--; }
|
||||
if (c) *output++ = c;
|
||||
}
|
||||
} while (1);
|
||||
}
|
||||
|
||||
void pass_control_to_child(DWORD control_type) {
|
||||
/*
|
||||
* distribute-issue207
|
||||
* passes the control event to child process (Python)
|
||||
*/
|
||||
if (!child_pid) {
|
||||
return;
|
||||
}
|
||||
GenerateConsoleCtrlEvent(child_pid,0);
|
||||
}
|
||||
|
||||
BOOL control_handler(DWORD control_type) {
|
||||
/*
|
||||
* distribute-issue207
|
||||
* control event handler callback function
|
||||
*/
|
||||
switch (control_type) {
|
||||
case CTRL_C_EVENT:
|
||||
pass_control_to_child(0);
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int create_and_wait_for_subprocess(char* command) {
|
||||
/*
|
||||
* distribute-issue207
|
||||
* launches child process (Python)
|
||||
*/
|
||||
DWORD return_value = 0;
|
||||
LPSTR commandline = command;
|
||||
STARTUPINFOA s_info;
|
||||
PROCESS_INFORMATION p_info;
|
||||
ZeroMemory(&p_info, sizeof(p_info));
|
||||
ZeroMemory(&s_info, sizeof(s_info));
|
||||
s_info.cb = sizeof(STARTUPINFO);
|
||||
// set-up control handler callback funciotn
|
||||
SetConsoleCtrlHandler((PHANDLER_ROUTINE) control_handler, TRUE);
|
||||
if (!CreateProcessA(NULL, commandline, NULL, NULL, TRUE, 0, NULL, NULL, &s_info, &p_info)) {
|
||||
fprintf(stderr, "failed to create process.\n");
|
||||
return 0;
|
||||
}
|
||||
child_pid = p_info.dwProcessId;
|
||||
// wait for Python to exit
|
||||
WaitForSingleObject(p_info.hProcess, INFINITE);
|
||||
if (!GetExitCodeProcess(p_info.hProcess, &return_value)) {
|
||||
fprintf(stderr, "failed to get exit code from process.\n");
|
||||
return 0;
|
||||
}
|
||||
return return_value;
|
||||
}
|
||||
|
||||
char* join_executable_and_args(char *executable, char **args, int argc)
|
||||
{
|
||||
/*
|
||||
* distribute-issue207
|
||||
* CreateProcess needs a long string of the executable and command-line arguments,
|
||||
* so we need to convert it from the args that was built
|
||||
*/
|
||||
int len,counter;
|
||||
char* cmdline;
|
||||
|
||||
len=strlen(executable)+2;
|
||||
for (counter=1; counter<argc; counter++) {
|
||||
len+=strlen(args[counter])+1;
|
||||
}
|
||||
|
||||
cmdline = (char*)calloc(len, sizeof(char));
|
||||
sprintf(cmdline, "%s", executable);
|
||||
len=strlen(executable);
|
||||
for (counter=1; counter<argc; counter++) {
|
||||
sprintf(cmdline+len, " %s", args[counter]);
|
||||
len+=strlen(args[counter])+1;
|
||||
}
|
||||
return cmdline;
|
||||
}
|
||||
|
||||
int run(int argc, char **argv, int is_gui) {
|
||||
|
||||
char python[256]; /* python executable's filename*/
|
||||
char *pyopt; /* Python option */
|
||||
char script[256]; /* the script's filename */
|
||||
|
||||
int scriptf; /* file descriptor for script file */
|
||||
|
||||
char **newargs, **newargsp, **parsedargs; /* argument array for exec */
|
||||
char *ptr, *end; /* working pointers for string manipulation */
|
||||
char *cmdline;
|
||||
int i, parsedargc; /* loop counter */
|
||||
|
||||
/* compute script name from our .exe name*/
|
||||
GetModuleFileNameA(NULL, script, sizeof(script));
|
||||
end = script + strlen(script);
|
||||
while( end>script && *end != '.')
|
||||
*end-- = '\0';
|
||||
*end-- = '\0';
|
||||
strcat(script, (GUI ? "-script.pyw" : "-script.py"));
|
||||
|
||||
/* figure out the target python executable */
|
||||
|
||||
scriptf = open(script, O_RDONLY);
|
||||
if (scriptf == -1) {
|
||||
return fail("Cannot open %s\n", script);
|
||||
}
|
||||
end = python + read(scriptf, python, sizeof(python));
|
||||
close(scriptf);
|
||||
|
||||
ptr = python-1;
|
||||
while(++ptr < end && *ptr && *ptr!='\n' && *ptr!='\r') {;}
|
||||
|
||||
*ptr-- = '\0';
|
||||
|
||||
if (strncmp(python, "#!", 2)) {
|
||||
/* default to python.exe if no #! header */
|
||||
strcpy(python, "#!python.exe");
|
||||
}
|
||||
|
||||
parsedargs = parse_argv(python+2, &parsedargc);
|
||||
|
||||
/* Using spawnv() can fail strangely if you e.g. find the Cygwin
|
||||
Python, so we'll make sure Windows can find and load it */
|
||||
|
||||
ptr = find_exe(parsedargs[0], script);
|
||||
if (!ptr) {
|
||||
return fail("Cannot find Python executable %s\n", parsedargs[0]);
|
||||
}
|
||||
|
||||
/* printf("Python executable: %s\n", ptr); */
|
||||
|
||||
/* Argument array needs to be
|
||||
parsedargc + argc, plus 1 for null sentinel */
|
||||
|
||||
newargs = (char **)calloc(parsedargc + argc + 1, sizeof(char *));
|
||||
newargsp = newargs;
|
||||
|
||||
*newargsp++ = quoted(ptr);
|
||||
for (i = 1; i<parsedargc; i++) *newargsp++ = quoted(parsedargs[i]);
|
||||
|
||||
*newargsp++ = quoted(script);
|
||||
for (i = 1; i < argc; i++) *newargsp++ = quoted(argv[i]);
|
||||
|
||||
*newargsp++ = NULL;
|
||||
|
||||
/* printf("args 0: %s\nargs 1: %s\n", newargs[0], newargs[1]); */
|
||||
|
||||
if (is_gui) {
|
||||
/* Use exec, we don't need to wait for the GUI to finish */
|
||||
execv(ptr, (const char * const *)(newargs));
|
||||
return fail("Could not exec %s", ptr); /* shouldn't get here! */
|
||||
}
|
||||
|
||||
/*
|
||||
* distribute-issue207: using CreateProcessA instead of spawnv
|
||||
*/
|
||||
cmdline = join_executable_and_args(ptr, newargs, parsedargc + argc);
|
||||
return create_and_wait_for_subprocess(cmdline);
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE hI, HINSTANCE hP, LPSTR lpCmd, int nShow) {
|
||||
return run(__argc, __argv, GUI);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return run(argc, argv, GUI);
|
||||
}
|
||||
|
||||
-2851
File diff suppressed because it is too large
Load Diff
Vendored
-170
@@ -1,170 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Script to fully automate the release process. Requires Python 2.6+
|
||||
with sphinx installed and the 'hg' command on the path.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import subprocess
|
||||
import shutil
|
||||
import os
|
||||
import sys
|
||||
import urllib2
|
||||
import getpass
|
||||
import collections
|
||||
|
||||
try:
|
||||
import keyring
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
VERSION = '0.6.31'
|
||||
|
||||
def get_next_version():
|
||||
digits = map(int, VERSION.split('.'))
|
||||
digits[-1] += 1
|
||||
return '.'.join(map(str, digits))
|
||||
|
||||
NEXT_VERSION = get_next_version()
|
||||
|
||||
files_with_versions = ('docs/conf.py', 'setup.py', 'release.py',
|
||||
'README.txt', 'distribute_setup.py')
|
||||
|
||||
def get_repo_name():
|
||||
"""
|
||||
Get the repo name from the hgrc default path.
|
||||
"""
|
||||
default = subprocess.check_output('hg paths default').strip()
|
||||
parts = default.split('/')
|
||||
if parts[-1] == '':
|
||||
parts.pop()
|
||||
return '/'.join(parts[-2:])
|
||||
|
||||
def get_mercurial_creds(system='https://bitbucket.org', username=None):
|
||||
"""
|
||||
Return named tuple of username,password in much the same way that
|
||||
Mercurial would (from the keyring).
|
||||
"""
|
||||
# todo: consider getting this from .hgrc
|
||||
username = username or getpass.getuser()
|
||||
keyring_username = '@@'.join((username, system))
|
||||
system = '@'.join((keyring_username, 'Mercurial'))
|
||||
password = (
|
||||
keyring.get_password(system, keyring_username)
|
||||
if 'keyring' in globals()
|
||||
else None
|
||||
)
|
||||
if not password:
|
||||
password = getpass.getpass()
|
||||
Credential = collections.namedtuple('Credential', 'username password')
|
||||
return Credential(username, password)
|
||||
|
||||
def add_milestone_and_version(version=NEXT_VERSION):
|
||||
auth = 'Basic ' + ':'.join(get_mercurial_creds()).encode('base64').strip()
|
||||
headers = {
|
||||
'Authorization': auth,
|
||||
}
|
||||
base = 'https://api.bitbucket.org'
|
||||
for type in 'milestones', 'versions':
|
||||
url = (base + '/1.0/repositories/{repo}/issues/{type}'
|
||||
.format(repo = get_repo_name(), type=type))
|
||||
req = urllib2.Request(url = url, headers = headers,
|
||||
data='name='+version)
|
||||
try:
|
||||
urllib2.urlopen(req)
|
||||
except urllib2.HTTPError as e:
|
||||
print(e.fp.read())
|
||||
|
||||
def bump_versions():
|
||||
list(map(bump_version, files_with_versions))
|
||||
|
||||
def bump_version(filename):
|
||||
with open(filename, 'rb') as f:
|
||||
lines = [line.replace(VERSION, NEXT_VERSION) for line in f]
|
||||
with open(filename, 'wb') as f:
|
||||
f.writelines(lines)
|
||||
|
||||
def do_release():
|
||||
assert all(map(os.path.exists, files_with_versions)), (
|
||||
"Expected file(s) missing")
|
||||
|
||||
assert has_sphinx(), "You must have Sphinx installed to release"
|
||||
|
||||
res = raw_input('Have you read through the SCM changelog and '
|
||||
'confirmed the changelog is current for releasing {VERSION}? '
|
||||
.format(**globals()))
|
||||
if not res.lower().startswith('y'):
|
||||
print("Please do that")
|
||||
raise SystemExit(1)
|
||||
|
||||
print("Travis-CI tests: http://travis-ci.org/#!/jaraco/distribute")
|
||||
res = raw_input('Have you or has someone verified that the tests '
|
||||
'pass on this revision? ')
|
||||
if not res.lower().startswith('y'):
|
||||
print("Please do that")
|
||||
raise SystemExit(2)
|
||||
|
||||
subprocess.check_call(['hg', 'tag', VERSION])
|
||||
|
||||
subprocess.check_call(['hg', 'update', VERSION])
|
||||
|
||||
has_docs = build_docs()
|
||||
if os.path.isdir('./dist'):
|
||||
shutil.rmtree('./dist')
|
||||
cmd = [sys.executable, 'setup.py', '-q', 'egg_info', '-RD', '-b', '',
|
||||
'sdist', 'register', 'upload']
|
||||
if has_docs:
|
||||
cmd.append('upload_docs')
|
||||
subprocess.check_call(cmd)
|
||||
upload_bootstrap_script()
|
||||
|
||||
# update to the tip for the next operation
|
||||
subprocess.check_call(['hg', 'update'])
|
||||
|
||||
# we just tagged the current version, bump for the next release.
|
||||
bump_versions()
|
||||
subprocess.check_call(['hg', 'ci', '-m',
|
||||
'Bumped to {NEXT_VERSION} in preparation for next '
|
||||
'release.'.format(**globals())])
|
||||
|
||||
# push the changes
|
||||
subprocess.check_call(['hg', 'push'])
|
||||
|
||||
add_milestone_and_version()
|
||||
|
||||
def has_sphinx():
|
||||
try:
|
||||
devnull = open(os.path.devnull, 'wb')
|
||||
subprocess.Popen(['sphinx-build', '--version'], stdout=devnull,
|
||||
stderr=subprocess.STDOUT).wait()
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
def build_docs():
|
||||
if not os.path.isdir('docs'):
|
||||
return
|
||||
if os.path.isdir('docs/build'):
|
||||
shutil.rmtree('docs/build')
|
||||
subprocess.check_call([
|
||||
'sphinx-build',
|
||||
'-b', 'html',
|
||||
'-d', 'build/doctrees',
|
||||
'.',
|
||||
'build/html',
|
||||
],
|
||||
cwd='docs')
|
||||
return True
|
||||
|
||||
def upload_bootstrap_script():
|
||||
scp_command = 'pscp' if sys.platform.startswith('win') else 'scp'
|
||||
try:
|
||||
subprocess.check_call([scp_command, 'distribute_setup.py',
|
||||
'pypi@ziade.org:python-distribute.org/'])
|
||||
except:
|
||||
print("Unable to upload bootstrap script. Ask Tarek to do it.")
|
||||
|
||||
if __name__ == '__main__':
|
||||
do_release()
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
[egg_info]
|
||||
tag_build =
|
||||
tag_svn_revision = 0
|
||||
tag_date = 0
|
||||
|
||||
[aliases]
|
||||
release = egg_info -RDb ''
|
||||
source = register sdist binary
|
||||
binary = bdist_egg upload --show-response
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = docs/
|
||||
build-dir = docs/build
|
||||
all_files = 1
|
||||
|
||||
[upload_docs]
|
||||
upload-dir = docs/build/html
|
||||
|
||||
[sdist]
|
||||
formats = gztar
|
||||
|
||||
Vendored
-245
@@ -1,245 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Distutils setup file, used to install or test 'setuptools'"""
|
||||
import sys
|
||||
import os
|
||||
import textwrap
|
||||
import re
|
||||
|
||||
# Allow to run setup.py from another directory.
|
||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
src_root = None
|
||||
if sys.version_info >= (3,):
|
||||
tmp_src = os.path.join("build", "src")
|
||||
from distutils.filelist import FileList
|
||||
from distutils import dir_util, file_util, util, log
|
||||
log.set_verbosity(1)
|
||||
fl = FileList()
|
||||
for line in open("MANIFEST.in"):
|
||||
fl.process_template_line(line)
|
||||
dir_util.create_tree(tmp_src, fl.files)
|
||||
outfiles_2to3 = []
|
||||
dist_script = os.path.join("build", "src", "distribute_setup.py")
|
||||
for f in fl.files:
|
||||
outf, copied = file_util.copy_file(f, os.path.join(tmp_src, f), update=1)
|
||||
if copied and outf.endswith(".py") and outf != dist_script:
|
||||
outfiles_2to3.append(outf)
|
||||
if copied and outf.endswith('api_tests.txt'):
|
||||
# XXX support this in distutils as well
|
||||
from lib2to3.main import main
|
||||
main('lib2to3.fixes', ['-wd', os.path.join(tmp_src, 'tests', 'api_tests.txt')])
|
||||
|
||||
util.run_2to3(outfiles_2to3)
|
||||
|
||||
# arrange setup to use the copy
|
||||
sys.path.insert(0, os.path.abspath(tmp_src))
|
||||
src_root = tmp_src
|
||||
|
||||
from distutils.util import convert_path
|
||||
|
||||
d = {}
|
||||
init_path = convert_path('setuptools/command/__init__.py')
|
||||
exec(open(init_path).read(), d)
|
||||
|
||||
SETUP_COMMANDS = d['__all__']
|
||||
VERSION = "0.6.31"
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools.command.build_py import build_py as _build_py
|
||||
from setuptools.command.test import test as _test
|
||||
|
||||
scripts = []
|
||||
|
||||
console_scripts = ["easy_install = setuptools.command.easy_install:main"]
|
||||
if os.environ.get("DISTRIBUTE_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT") is None:
|
||||
console_scripts.append("easy_install-%s = setuptools.command.easy_install:main" % sys.version[:3])
|
||||
|
||||
# specific command that is used to generate windows .exe files
|
||||
class build_py(_build_py):
|
||||
def build_package_data(self):
|
||||
"""Copy data files into build directory"""
|
||||
lastdir = None
|
||||
for package, src_dir, build_dir, filenames in self.data_files:
|
||||
for filename in filenames:
|
||||
target = os.path.join(build_dir, filename)
|
||||
self.mkpath(os.path.dirname(target))
|
||||
srcfile = os.path.join(src_dir, filename)
|
||||
outf, copied = self.copy_file(srcfile, target)
|
||||
srcfile = os.path.abspath(srcfile)
|
||||
|
||||
# avoid a bootstrapping issue with easy_install -U (when the
|
||||
# previous version doesn't have convert_2to3_doctests)
|
||||
if not hasattr(self.distribution, 'convert_2to3_doctests'):
|
||||
continue
|
||||
|
||||
if copied and srcfile in self.distribution.convert_2to3_doctests:
|
||||
self.__doctests_2to3.append(outf)
|
||||
|
||||
class test(_test):
|
||||
"""Specific test class to avoid rewriting the entry_points.txt"""
|
||||
def run(self):
|
||||
entry_points = os.path.join('distribute.egg-info', 'entry_points.txt')
|
||||
|
||||
if not os.path.exists(entry_points):
|
||||
try:
|
||||
_test.run(self)
|
||||
finally:
|
||||
return
|
||||
|
||||
f = open(entry_points)
|
||||
|
||||
# running the test
|
||||
try:
|
||||
ep_content = f.read()
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
try:
|
||||
_test.run(self)
|
||||
finally:
|
||||
# restoring the file
|
||||
f = open(entry_points, 'w')
|
||||
try:
|
||||
f.write(ep_content)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
|
||||
# if we are installing Distribute using "python setup.py install"
|
||||
# we need to get setuptools out of the way
|
||||
def _easy_install_marker():
|
||||
return (len(sys.argv) == 5 and sys.argv[2] == 'bdist_egg' and
|
||||
sys.argv[3] == '--dist-dir' and 'egg-dist-tmp-' in sys.argv[-1])
|
||||
|
||||
def _buildout_marker():
|
||||
command = os.environ.get('_')
|
||||
if command:
|
||||
return 'buildout' in os.path.basename(command)
|
||||
|
||||
def _being_installed():
|
||||
if os.environ.get('DONT_PATCH_SETUPTOOLS') is not None:
|
||||
return False
|
||||
if _buildout_marker():
|
||||
# Installed by buildout, don't mess with a global setuptools.
|
||||
return False
|
||||
# easy_install marker
|
||||
if "--help" in sys.argv[1:] or "-h" in sys.argv[1:]: # Don't bother doing anything if they're just asking for help
|
||||
return False
|
||||
return 'install' in sys.argv[1:] or _easy_install_marker()
|
||||
|
||||
if _being_installed():
|
||||
from distribute_setup import _before_install
|
||||
_before_install()
|
||||
|
||||
# return contents of reStructureText file with linked issue references
|
||||
def _linkified(rstfile):
|
||||
bitroot = 'http://bitbucket.org/tarek/distribute'
|
||||
revision = re.compile(r'\b(issue\s*#?\d+)\b', re.M | re.I)
|
||||
|
||||
rstext = open(rstfile).read()
|
||||
|
||||
anchors = revision.findall(rstext) # ['Issue #43', ...]
|
||||
anchors = sorted(set(anchors))
|
||||
rstext = revision.sub(r'`\1`_', rstext)
|
||||
rstext += "\n"
|
||||
for x in anchors:
|
||||
issue = re.findall(r'\d+', x)[0]
|
||||
rstext += '.. _`%s`: %s/issue/%s\n' % (x, bitroot, issue)
|
||||
rstext += "\n"
|
||||
return rstext
|
||||
|
||||
dist = setup(
|
||||
name="distribute",
|
||||
version=VERSION,
|
||||
description="Easily download, build, install, upgrade, and uninstall "
|
||||
"Python packages",
|
||||
author="The fellowship of the packaging",
|
||||
author_email="distutils-sig@python.org",
|
||||
license="PSF or ZPL",
|
||||
long_description = open('README.txt').read() + _linkified('CHANGES.txt'),
|
||||
keywords = "CPAN PyPI distutils eggs package management",
|
||||
url = "http://packages.python.org/distribute",
|
||||
test_suite = 'setuptools.tests',
|
||||
src_root = src_root,
|
||||
packages = find_packages(),
|
||||
package_data = {'setuptools':['*.exe']},
|
||||
|
||||
py_modules = ['pkg_resources', 'easy_install', 'site'],
|
||||
|
||||
zip_safe = (sys.version>="2.5"), # <2.5 needs unzipped for -m to work
|
||||
|
||||
cmdclass = {'test': test},
|
||||
entry_points = {
|
||||
|
||||
"distutils.commands" : [
|
||||
"%(cmd)s = setuptools.command.%(cmd)s:%(cmd)s" % locals()
|
||||
for cmd in SETUP_COMMANDS
|
||||
],
|
||||
|
||||
"distutils.setup_keywords": [
|
||||
"eager_resources = setuptools.dist:assert_string_list",
|
||||
"namespace_packages = setuptools.dist:check_nsp",
|
||||
"extras_require = setuptools.dist:check_extras",
|
||||
"install_requires = setuptools.dist:check_requirements",
|
||||
"tests_require = setuptools.dist:check_requirements",
|
||||
"entry_points = setuptools.dist:check_entry_points",
|
||||
"test_suite = setuptools.dist:check_test_suite",
|
||||
"zip_safe = setuptools.dist:assert_bool",
|
||||
"package_data = setuptools.dist:check_package_data",
|
||||
"exclude_package_data = setuptools.dist:check_package_data",
|
||||
"include_package_data = setuptools.dist:assert_bool",
|
||||
"packages = setuptools.dist:check_packages",
|
||||
"dependency_links = setuptools.dist:assert_string_list",
|
||||
"test_loader = setuptools.dist:check_importable",
|
||||
"use_2to3 = setuptools.dist:assert_bool",
|
||||
"convert_2to3_doctests = setuptools.dist:assert_string_list",
|
||||
"use_2to3_fixers = setuptools.dist:assert_string_list",
|
||||
"use_2to3_exclude_fixers = setuptools.dist:assert_string_list",
|
||||
],
|
||||
|
||||
"egg_info.writers": [
|
||||
"PKG-INFO = setuptools.command.egg_info:write_pkg_info",
|
||||
"requires.txt = setuptools.command.egg_info:write_requirements",
|
||||
"entry_points.txt = setuptools.command.egg_info:write_entries",
|
||||
"eager_resources.txt = setuptools.command.egg_info:overwrite_arg",
|
||||
"namespace_packages.txt = setuptools.command.egg_info:overwrite_arg",
|
||||
"top_level.txt = setuptools.command.egg_info:write_toplevel_names",
|
||||
"depends.txt = setuptools.command.egg_info:warn_depends_obsolete",
|
||||
"dependency_links.txt = setuptools.command.egg_info:overwrite_arg",
|
||||
],
|
||||
|
||||
"console_scripts": console_scripts,
|
||||
|
||||
"setuptools.file_finders":
|
||||
["svn_cvs = setuptools.command.sdist:_default_revctrl"],
|
||||
|
||||
"setuptools.installation":
|
||||
['eggsecutable = setuptools.command.easy_install:bootstrap'],
|
||||
},
|
||||
|
||||
|
||||
classifiers = textwrap.dedent("""
|
||||
Development Status :: 5 - Production/Stable
|
||||
Intended Audience :: Developers
|
||||
License :: OSI Approved :: Python Software Foundation License
|
||||
License :: OSI Approved :: Zope Public License
|
||||
Operating System :: OS Independent
|
||||
Programming Language :: Python :: 2.4
|
||||
Programming Language :: Python :: 2.5
|
||||
Programming Language :: Python :: 2.6
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.1
|
||||
Programming Language :: Python :: 3.2
|
||||
Programming Language :: Python :: 3.3
|
||||
Topic :: Software Development :: Libraries :: Python Modules
|
||||
Topic :: System :: Archiving :: Packaging
|
||||
Topic :: System :: Systems Administration
|
||||
Topic :: Utilities
|
||||
""").strip().splitlines(),
|
||||
scripts = scripts,
|
||||
)
|
||||
|
||||
if _being_installed():
|
||||
from distribute_setup import _after_install
|
||||
_after_install(dist)
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
"""Extensions to the 'distutils' for large or complex distributions"""
|
||||
from setuptools.extension import Extension, Library
|
||||
from setuptools.dist import Distribution, Feature, _get_unpatched
|
||||
import distutils.core, setuptools.command
|
||||
from setuptools.depends import Require
|
||||
from distutils.core import Command as _Command
|
||||
from distutils.util import convert_path
|
||||
import os
|
||||
import sys
|
||||
|
||||
__version__ = '0.6'
|
||||
__all__ = [
|
||||
'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require',
|
||||
'find_packages'
|
||||
]
|
||||
|
||||
# This marker is used to simplify the process that checks is the
|
||||
# setuptools package was installed by the Setuptools project
|
||||
# or by the Distribute project, in case Setuptools creates
|
||||
# a distribution with the same version.
|
||||
#
|
||||
# The distribute_setup script for instance, will check if this
|
||||
# attribute is present to decide whether to reinstall the package
|
||||
# or not.
|
||||
_distribute = True
|
||||
|
||||
bootstrap_install_from = None
|
||||
|
||||
# If we run 2to3 on .py files, should we also convert docstrings?
|
||||
# Default: yes; assume that we can detect doctests reliably
|
||||
run_2to3_on_doctests = True
|
||||
# Standard package names for fixer packages
|
||||
lib2to3_fixer_packages = ['lib2to3.fixes']
|
||||
|
||||
def find_packages(where='.', exclude=()):
|
||||
"""Return a list all Python packages found within directory 'where'
|
||||
|
||||
'where' should be supplied as a "cross-platform" (i.e. URL-style) path; it
|
||||
will be converted to the appropriate local path syntax. 'exclude' is a
|
||||
sequence of package names to exclude; '*' can be used as a wildcard in the
|
||||
names, such that 'foo.*' will exclude all subpackages of 'foo' (but not
|
||||
'foo' itself).
|
||||
"""
|
||||
out = []
|
||||
stack=[(convert_path(where), '')]
|
||||
while stack:
|
||||
where,prefix = stack.pop(0)
|
||||
for name in os.listdir(where):
|
||||
fn = os.path.join(where,name)
|
||||
if ('.' not in name and os.path.isdir(fn) and
|
||||
os.path.isfile(os.path.join(fn,'__init__.py'))
|
||||
):
|
||||
out.append(prefix+name); stack.append((fn,prefix+name+'.'))
|
||||
for pat in list(exclude)+['ez_setup', 'distribute_setup']:
|
||||
from fnmatch import fnmatchcase
|
||||
out = [item for item in out if not fnmatchcase(item,pat)]
|
||||
return out
|
||||
|
||||
setup = distutils.core.setup
|
||||
|
||||
_Command = _get_unpatched(_Command)
|
||||
|
||||
class Command(_Command):
|
||||
__doc__ = _Command.__doc__
|
||||
|
||||
command_consumes_arguments = False
|
||||
|
||||
def __init__(self, dist, **kw):
|
||||
# Add support for keyword arguments
|
||||
_Command.__init__(self,dist)
|
||||
for k,v in kw.items():
|
||||
setattr(self,k,v)
|
||||
|
||||
def reinitialize_command(self, command, reinit_subcommands=0, **kw):
|
||||
cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
|
||||
for k,v in kw.items():
|
||||
setattr(cmd,k,v) # update command with keywords
|
||||
return cmd
|
||||
|
||||
import distutils.core
|
||||
distutils.core.Command = Command # we can't patch distutils.cmd, alas
|
||||
|
||||
def findall(dir = os.curdir):
|
||||
"""Find all files under 'dir' and return the list of full filenames
|
||||
(relative to 'dir').
|
||||
"""
|
||||
all_files = []
|
||||
for base, dirs, files in os.walk(dir):
|
||||
if base==os.curdir or base.startswith(os.curdir+os.sep):
|
||||
base = base[2:]
|
||||
if base:
|
||||
files = [os.path.join(base, f) for f in files]
|
||||
all_files.extend(filter(os.path.isfile, files))
|
||||
return all_files
|
||||
|
||||
import distutils.filelist
|
||||
distutils.filelist.findall = findall # fix findall bug in distutils.
|
||||
|
||||
# sys.dont_write_bytecode was introduced in Python 2.6.
|
||||
if ((hasattr(sys, "dont_write_bytecode") and sys.dont_write_bytecode) or
|
||||
(not hasattr(sys, "dont_write_bytecode") and os.environ.get("PYTHONDONTWRITEBYTECODE"))):
|
||||
_dont_write_bytecode = True
|
||||
else:
|
||||
_dont_write_bytecode = False
|
||||
-214
@@ -1,214 +0,0 @@
|
||||
"""Utilities for extracting common archive formats"""
|
||||
|
||||
|
||||
__all__ = [
|
||||
"unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter",
|
||||
"UnrecognizedFormat", "extraction_drivers", "unpack_directory",
|
||||
]
|
||||
|
||||
import zipfile, tarfile, os, shutil
|
||||
from pkg_resources import ensure_directory
|
||||
from distutils.errors import DistutilsError
|
||||
|
||||
class UnrecognizedFormat(DistutilsError):
|
||||
"""Couldn't recognize the archive type"""
|
||||
|
||||
def default_filter(src,dst):
|
||||
"""The default progress/filter callback; returns True for all files"""
|
||||
return dst
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def unpack_archive(filename, extract_dir, progress_filter=default_filter,
|
||||
drivers=None
|
||||
):
|
||||
"""Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat``
|
||||
|
||||
`progress_filter` is a function taking two arguments: a source path
|
||||
internal to the archive ('/'-separated), and a filesystem path where it
|
||||
will be extracted. The callback must return the desired extract path
|
||||
(which may be the same as the one passed in), or else ``None`` to skip
|
||||
that file or directory. The callback can thus be used to report on the
|
||||
progress of the extraction, as well as to filter the items extracted or
|
||||
alter their extraction paths.
|
||||
|
||||
`drivers`, if supplied, must be a non-empty sequence of functions with the
|
||||
same signature as this function (minus the `drivers` argument), that raise
|
||||
``UnrecognizedFormat`` if they do not support extracting the designated
|
||||
archive type. The `drivers` are tried in sequence until one is found that
|
||||
does not raise an error, or until all are exhausted (in which case
|
||||
``UnrecognizedFormat`` is raised). If you do not supply a sequence of
|
||||
drivers, the module's ``extraction_drivers`` constant will be used, which
|
||||
means that ``unpack_zipfile`` and ``unpack_tarfile`` will be tried, in that
|
||||
order.
|
||||
"""
|
||||
for driver in drivers or extraction_drivers:
|
||||
try:
|
||||
driver(filename, extract_dir, progress_filter)
|
||||
except UnrecognizedFormat:
|
||||
continue
|
||||
else:
|
||||
return
|
||||
else:
|
||||
raise UnrecognizedFormat(
|
||||
"Not a recognized archive type: %s" % filename
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def unpack_directory(filename, extract_dir, progress_filter=default_filter):
|
||||
""""Unpack" a directory, using the same interface as for archives
|
||||
|
||||
Raises ``UnrecognizedFormat`` if `filename` is not a directory
|
||||
"""
|
||||
if not os.path.isdir(filename):
|
||||
raise UnrecognizedFormat("%s is not a directory" % (filename,))
|
||||
|
||||
paths = {filename:('',extract_dir)}
|
||||
for base, dirs, files in os.walk(filename):
|
||||
src,dst = paths[base]
|
||||
for d in dirs:
|
||||
paths[os.path.join(base,d)] = src+d+'/', os.path.join(dst,d)
|
||||
for f in files:
|
||||
name = src+f
|
||||
target = os.path.join(dst,f)
|
||||
target = progress_filter(src+f, target)
|
||||
if not target:
|
||||
continue # skip non-files
|
||||
ensure_directory(target)
|
||||
f = os.path.join(base,f)
|
||||
shutil.copyfile(f, target)
|
||||
shutil.copystat(f, target)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def unpack_zipfile(filename, extract_dir, progress_filter=default_filter):
|
||||
"""Unpack zip `filename` to `extract_dir`
|
||||
|
||||
Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined
|
||||
by ``zipfile.is_zipfile()``). See ``unpack_archive()`` for an explanation
|
||||
of the `progress_filter` argument.
|
||||
"""
|
||||
|
||||
if not zipfile.is_zipfile(filename):
|
||||
raise UnrecognizedFormat("%s is not a zip file" % (filename,))
|
||||
|
||||
z = zipfile.ZipFile(filename)
|
||||
try:
|
||||
for info in z.infolist():
|
||||
name = info.filename
|
||||
|
||||
# don't extract absolute paths or ones with .. in them
|
||||
if name.startswith('/') or '..' in name:
|
||||
continue
|
||||
|
||||
target = os.path.join(extract_dir, *name.split('/'))
|
||||
target = progress_filter(name, target)
|
||||
if not target:
|
||||
continue
|
||||
if name.endswith('/'):
|
||||
# directory
|
||||
ensure_directory(target)
|
||||
else:
|
||||
# file
|
||||
ensure_directory(target)
|
||||
data = z.read(info.filename)
|
||||
f = open(target,'wb')
|
||||
try:
|
||||
f.write(data)
|
||||
finally:
|
||||
f.close()
|
||||
del data
|
||||
unix_attributes = info.external_attr >> 16
|
||||
if unix_attributes:
|
||||
os.chmod(target, unix_attributes)
|
||||
finally:
|
||||
z.close()
|
||||
|
||||
|
||||
def unpack_tarfile(filename, extract_dir, progress_filter=default_filter):
|
||||
"""Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir`
|
||||
|
||||
Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined
|
||||
by ``tarfile.open()``). See ``unpack_archive()`` for an explanation
|
||||
of the `progress_filter` argument.
|
||||
"""
|
||||
|
||||
try:
|
||||
tarobj = tarfile.open(filename)
|
||||
except tarfile.TarError:
|
||||
raise UnrecognizedFormat(
|
||||
"%s is not a compressed or uncompressed tar file" % (filename,)
|
||||
)
|
||||
|
||||
try:
|
||||
tarobj.chown = lambda *args: None # don't do any chowning!
|
||||
for member in tarobj:
|
||||
name = member.name
|
||||
# don't extract absolute paths or ones with .. in them
|
||||
if not name.startswith('/') and '..' not in name:
|
||||
prelim_dst = os.path.join(extract_dir, *name.split('/'))
|
||||
final_dst = progress_filter(name, prelim_dst)
|
||||
# If progress_filter returns None, then we do not extract
|
||||
# this file
|
||||
# TODO: Do we really need to limit to just these file types?
|
||||
# tarobj.extract() will handle all files on all platforms,
|
||||
# turning file types that aren't allowed on that platform into
|
||||
# regular files.
|
||||
if final_dst and (member.isfile() or member.isdir() or
|
||||
member.islnk() or member.issym()):
|
||||
tarobj.extract(member, extract_dir)
|
||||
if final_dst != prelim_dst:
|
||||
shutil.move(prelim_dst, final_dst)
|
||||
return True
|
||||
finally:
|
||||
tarobj.close()
|
||||
|
||||
|
||||
|
||||
|
||||
extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,21 +0,0 @@
|
||||
__all__ = [
|
||||
'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop',
|
||||
'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts',
|
||||
'sdist', 'setopt', 'test', 'upload', 'install_egg_info', 'install_scripts',
|
||||
'register', 'bdist_wininst', 'upload_docs',
|
||||
]
|
||||
|
||||
from setuptools.command import install_scripts
|
||||
import sys
|
||||
|
||||
if sys.version>='2.5':
|
||||
# In Python 2.5 and above, distutils includes its own upload command
|
||||
__all__.remove('upload')
|
||||
|
||||
from distutils.command.bdist import bdist
|
||||
|
||||
if 'egg' not in bdist.format_commands:
|
||||
bdist.format_command['egg'] = ('bdist_egg', "Python .egg file")
|
||||
bdist.format_commands.append('egg')
|
||||
|
||||
del bdist, sys
|
||||
@@ -1,82 +0,0 @@
|
||||
import distutils, os
|
||||
from setuptools import Command
|
||||
from distutils.util import convert_path
|
||||
from distutils import log
|
||||
from distutils.errors import *
|
||||
from setuptools.command.setopt import edit_config, option_base, config_file
|
||||
|
||||
def shquote(arg):
|
||||
"""Quote an argument for later parsing by shlex.split()"""
|
||||
for c in '"', "'", "\\", "#":
|
||||
if c in arg: return repr(arg)
|
||||
if arg.split()<>[arg]:
|
||||
return repr(arg)
|
||||
return arg
|
||||
|
||||
|
||||
class alias(option_base):
|
||||
"""Define a shortcut that invokes one or more commands"""
|
||||
|
||||
description = "define a shortcut to invoke one or more commands"
|
||||
command_consumes_arguments = True
|
||||
|
||||
user_options = [
|
||||
('remove', 'r', 'remove (unset) the alias'),
|
||||
] + option_base.user_options
|
||||
|
||||
boolean_options = option_base.boolean_options + ['remove']
|
||||
|
||||
def initialize_options(self):
|
||||
option_base.initialize_options(self)
|
||||
self.args = None
|
||||
self.remove = None
|
||||
|
||||
def finalize_options(self):
|
||||
option_base.finalize_options(self)
|
||||
if self.remove and len(self.args)<>1:
|
||||
raise DistutilsOptionError(
|
||||
"Must specify exactly one argument (the alias name) when "
|
||||
"using --remove"
|
||||
)
|
||||
|
||||
def run(self):
|
||||
aliases = self.distribution.get_option_dict('aliases')
|
||||
|
||||
if not self.args:
|
||||
print "Command Aliases"
|
||||
print "---------------"
|
||||
for alias in aliases:
|
||||
print "setup.py alias", format_alias(alias, aliases)
|
||||
return
|
||||
|
||||
elif len(self.args)==1:
|
||||
alias, = self.args
|
||||
if self.remove:
|
||||
command = None
|
||||
elif alias in aliases:
|
||||
print "setup.py alias", format_alias(alias, aliases)
|
||||
return
|
||||
else:
|
||||
print "No alias definition found for %r" % alias
|
||||
return
|
||||
else:
|
||||
alias = self.args[0]
|
||||
command = ' '.join(map(shquote,self.args[1:]))
|
||||
|
||||
edit_config(self.filename, {'aliases': {alias:command}}, self.dry_run)
|
||||
|
||||
|
||||
def format_alias(name, aliases):
|
||||
source, command = aliases[name]
|
||||
if source == config_file('global'):
|
||||
source = '--global-config '
|
||||
elif source == config_file('user'):
|
||||
source = '--user-config '
|
||||
elif source == config_file('local'):
|
||||
source = ''
|
||||
else:
|
||||
source = '--filename=%r' % source
|
||||
return source+name+' '+command
|
||||
|
||||
|
||||
|
||||
@@ -1,548 +0,0 @@
|
||||
"""setuptools.command.bdist_egg
|
||||
|
||||
Build .egg distributions"""
|
||||
|
||||
# This module should be kept compatible with Python 2.3
|
||||
import sys, os, marshal
|
||||
from setuptools import Command
|
||||
from distutils.dir_util import remove_tree, mkpath
|
||||
try:
|
||||
from distutils.sysconfig import get_python_version, get_python_lib
|
||||
except ImportError:
|
||||
from sysconfig import get_python_version
|
||||
from distutils.sysconfig import get_python_lib
|
||||
|
||||
from distutils import log
|
||||
from distutils.errors import DistutilsSetupError
|
||||
from pkg_resources import get_build_platform, Distribution, ensure_directory
|
||||
from pkg_resources import EntryPoint
|
||||
from types import CodeType
|
||||
from setuptools.extension import Library
|
||||
|
||||
def strip_module(filename):
|
||||
if '.' in filename:
|
||||
filename = os.path.splitext(filename)[0]
|
||||
if filename.endswith('module'):
|
||||
filename = filename[:-6]
|
||||
return filename
|
||||
|
||||
def write_stub(resource, pyfile):
|
||||
f = open(pyfile,'w')
|
||||
f.write('\n'.join([
|
||||
"def __bootstrap__():",
|
||||
" global __bootstrap__, __loader__, __file__",
|
||||
" import sys, pkg_resources, imp",
|
||||
" __file__ = pkg_resources.resource_filename(__name__,%r)"
|
||||
% resource,
|
||||
" __loader__ = None; del __bootstrap__, __loader__",
|
||||
" imp.load_dynamic(__name__,__file__)",
|
||||
"__bootstrap__()",
|
||||
"" # terminal \n
|
||||
]))
|
||||
f.close()
|
||||
|
||||
# stub __init__.py for packages distributed without one
|
||||
NS_PKG_STUB = '__import__("pkg_resources").declare_namespace(__name__)'
|
||||
|
||||
class bdist_egg(Command):
|
||||
|
||||
description = "create an \"egg\" distribution"
|
||||
|
||||
user_options = [
|
||||
('bdist-dir=', 'b',
|
||||
"temporary directory for creating the distribution"),
|
||||
('plat-name=', 'p',
|
||||
"platform name to embed in generated filenames "
|
||||
"(default: %s)" % get_build_platform()),
|
||||
('exclude-source-files', None,
|
||||
"remove all .py files from the generated egg"),
|
||||
('keep-temp', 'k',
|
||||
"keep the pseudo-installation tree around after " +
|
||||
"creating the distribution archive"),
|
||||
('dist-dir=', 'd',
|
||||
"directory to put final built distributions in"),
|
||||
('skip-build', None,
|
||||
"skip rebuilding everything (for testing/debugging)"),
|
||||
]
|
||||
|
||||
boolean_options = [
|
||||
'keep-temp', 'skip-build', 'exclude-source-files'
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def initialize_options (self):
|
||||
self.bdist_dir = None
|
||||
self.plat_name = None
|
||||
self.keep_temp = 0
|
||||
self.dist_dir = None
|
||||
self.skip_build = 0
|
||||
self.egg_output = None
|
||||
self.exclude_source_files = None
|
||||
|
||||
|
||||
def finalize_options(self):
|
||||
ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info")
|
||||
self.egg_info = ei_cmd.egg_info
|
||||
|
||||
if self.bdist_dir is None:
|
||||
bdist_base = self.get_finalized_command('bdist').bdist_base
|
||||
self.bdist_dir = os.path.join(bdist_base, 'egg')
|
||||
|
||||
if self.plat_name is None:
|
||||
self.plat_name = get_build_platform()
|
||||
|
||||
self.set_undefined_options('bdist',('dist_dir', 'dist_dir'))
|
||||
|
||||
if self.egg_output is None:
|
||||
|
||||
# Compute filename of the output egg
|
||||
basename = Distribution(
|
||||
None, None, ei_cmd.egg_name, ei_cmd.egg_version,
|
||||
get_python_version(),
|
||||
self.distribution.has_ext_modules() and self.plat_name
|
||||
).egg_name()
|
||||
|
||||
self.egg_output = os.path.join(self.dist_dir, basename+'.egg')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def do_install_data(self):
|
||||
# Hack for packages that install data to install's --install-lib
|
||||
self.get_finalized_command('install').install_lib = self.bdist_dir
|
||||
|
||||
site_packages = os.path.normcase(os.path.realpath(get_python_lib()))
|
||||
old, self.distribution.data_files = self.distribution.data_files,[]
|
||||
|
||||
for item in old:
|
||||
if isinstance(item,tuple) and len(item)==2:
|
||||
if os.path.isabs(item[0]):
|
||||
realpath = os.path.realpath(item[0])
|
||||
normalized = os.path.normcase(realpath)
|
||||
if normalized==site_packages or normalized.startswith(
|
||||
site_packages+os.sep
|
||||
):
|
||||
item = realpath[len(site_packages)+1:], item[1]
|
||||
# XXX else: raise ???
|
||||
self.distribution.data_files.append(item)
|
||||
|
||||
try:
|
||||
log.info("installing package data to %s" % self.bdist_dir)
|
||||
self.call_command('install_data', force=0, root=None)
|
||||
finally:
|
||||
self.distribution.data_files = old
|
||||
|
||||
|
||||
def get_outputs(self):
|
||||
return [self.egg_output]
|
||||
|
||||
|
||||
def call_command(self,cmdname,**kw):
|
||||
"""Invoke reinitialized command `cmdname` with keyword args"""
|
||||
for dirname in INSTALL_DIRECTORY_ATTRS:
|
||||
kw.setdefault(dirname,self.bdist_dir)
|
||||
kw.setdefault('skip_build',self.skip_build)
|
||||
kw.setdefault('dry_run', self.dry_run)
|
||||
cmd = self.reinitialize_command(cmdname, **kw)
|
||||
self.run_command(cmdname)
|
||||
return cmd
|
||||
|
||||
|
||||
def run(self):
|
||||
# Generate metadata first
|
||||
self.run_command("egg_info")
|
||||
|
||||
# We run install_lib before install_data, because some data hacks
|
||||
# pull their data path from the install_lib command.
|
||||
log.info("installing library code to %s" % self.bdist_dir)
|
||||
instcmd = self.get_finalized_command('install')
|
||||
old_root = instcmd.root; instcmd.root = None
|
||||
cmd = self.call_command('install_lib', warn_dir=0)
|
||||
instcmd.root = old_root
|
||||
|
||||
all_outputs, ext_outputs = self.get_ext_outputs()
|
||||
self.stubs = []
|
||||
to_compile = []
|
||||
for (p,ext_name) in enumerate(ext_outputs):
|
||||
filename,ext = os.path.splitext(ext_name)
|
||||
pyfile = os.path.join(self.bdist_dir, strip_module(filename)+'.py')
|
||||
self.stubs.append(pyfile)
|
||||
log.info("creating stub loader for %s" % ext_name)
|
||||
if not self.dry_run:
|
||||
write_stub(os.path.basename(ext_name), pyfile)
|
||||
to_compile.append(pyfile)
|
||||
ext_outputs[p] = ext_name.replace(os.sep,'/')
|
||||
|
||||
to_compile.extend(self.make_init_files())
|
||||
if to_compile:
|
||||
cmd.byte_compile(to_compile)
|
||||
|
||||
if self.distribution.data_files:
|
||||
self.do_install_data()
|
||||
|
||||
# Make the EGG-INFO directory
|
||||
archive_root = self.bdist_dir
|
||||
egg_info = os.path.join(archive_root,'EGG-INFO')
|
||||
self.mkpath(egg_info)
|
||||
if self.distribution.scripts:
|
||||
script_dir = os.path.join(egg_info, 'scripts')
|
||||
log.info("installing scripts to %s" % script_dir)
|
||||
self.call_command('install_scripts',install_dir=script_dir,no_ep=1)
|
||||
|
||||
self.copy_metadata_to(egg_info)
|
||||
native_libs = os.path.join(egg_info, "native_libs.txt")
|
||||
if all_outputs:
|
||||
log.info("writing %s" % native_libs)
|
||||
if not self.dry_run:
|
||||
ensure_directory(native_libs)
|
||||
libs_file = open(native_libs, 'wt')
|
||||
libs_file.write('\n'.join(all_outputs))
|
||||
libs_file.write('\n')
|
||||
libs_file.close()
|
||||
elif os.path.isfile(native_libs):
|
||||
log.info("removing %s" % native_libs)
|
||||
if not self.dry_run:
|
||||
os.unlink(native_libs)
|
||||
|
||||
write_safety_flag(
|
||||
os.path.join(archive_root,'EGG-INFO'), self.zip_safe()
|
||||
)
|
||||
|
||||
if os.path.exists(os.path.join(self.egg_info,'depends.txt')):
|
||||
log.warn(
|
||||
"WARNING: 'depends.txt' will not be used by setuptools 0.6!\n"
|
||||
"Use the install_requires/extras_require setup() args instead."
|
||||
)
|
||||
|
||||
if self.exclude_source_files:
|
||||
self.zap_pyfiles()
|
||||
|
||||
# Make the archive
|
||||
make_zipfile(self.egg_output, archive_root, verbose=self.verbose,
|
||||
dry_run=self.dry_run, mode=self.gen_header())
|
||||
if not self.keep_temp:
|
||||
remove_tree(self.bdist_dir, dry_run=self.dry_run)
|
||||
|
||||
# Add to 'Distribution.dist_files' so that the "upload" command works
|
||||
getattr(self.distribution,'dist_files',[]).append(
|
||||
('bdist_egg',get_python_version(),self.egg_output))
|
||||
|
||||
|
||||
|
||||
|
||||
def zap_pyfiles(self):
|
||||
log.info("Removing .py files from temporary directory")
|
||||
for base,dirs,files in walk_egg(self.bdist_dir):
|
||||
for name in files:
|
||||
if name.endswith('.py'):
|
||||
path = os.path.join(base,name)
|
||||
log.debug("Deleting %s", path)
|
||||
os.unlink(path)
|
||||
|
||||
def zip_safe(self):
|
||||
safe = getattr(self.distribution,'zip_safe',None)
|
||||
if safe is not None:
|
||||
return safe
|
||||
log.warn("zip_safe flag not set; analyzing archive contents...")
|
||||
return analyze_egg(self.bdist_dir, self.stubs)
|
||||
|
||||
def make_init_files(self):
|
||||
"""Create missing package __init__ files"""
|
||||
init_files = []
|
||||
for base,dirs,files in walk_egg(self.bdist_dir):
|
||||
if base==self.bdist_dir:
|
||||
# don't put an __init__ in the root
|
||||
continue
|
||||
for name in files:
|
||||
if name.endswith('.py'):
|
||||
if '__init__.py' not in files:
|
||||
pkg = base[len(self.bdist_dir)+1:].replace(os.sep,'.')
|
||||
if self.distribution.has_contents_for(pkg):
|
||||
log.warn("Creating missing __init__.py for %s",pkg)
|
||||
filename = os.path.join(base,'__init__.py')
|
||||
if not self.dry_run:
|
||||
f = open(filename,'w'); f.write(NS_PKG_STUB)
|
||||
f.close()
|
||||
init_files.append(filename)
|
||||
break
|
||||
else:
|
||||
# not a package, don't traverse to subdirectories
|
||||
dirs[:] = []
|
||||
|
||||
return init_files
|
||||
|
||||
def gen_header(self):
|
||||
epm = EntryPoint.parse_map(self.distribution.entry_points or '')
|
||||
ep = epm.get('setuptools.installation',{}).get('eggsecutable')
|
||||
if ep is None:
|
||||
return 'w' # not an eggsecutable, do it the usual way.
|
||||
|
||||
if not ep.attrs or ep.extras:
|
||||
raise DistutilsSetupError(
|
||||
"eggsecutable entry point (%r) cannot have 'extras' "
|
||||
"or refer to a module" % (ep,)
|
||||
)
|
||||
|
||||
pyver = sys.version[:3]
|
||||
pkg = ep.module_name
|
||||
full = '.'.join(ep.attrs)
|
||||
base = ep.attrs[0]
|
||||
basename = os.path.basename(self.egg_output)
|
||||
|
||||
header = (
|
||||
"#!/bin/sh\n"
|
||||
'if [ `basename $0` = "%(basename)s" ]\n'
|
||||
'then exec python%(pyver)s -c "'
|
||||
"import sys, os; sys.path.insert(0, os.path.abspath('$0')); "
|
||||
"from %(pkg)s import %(base)s; sys.exit(%(full)s())"
|
||||
'" "$@"\n'
|
||||
'else\n'
|
||||
' echo $0 is not the correct name for this egg file.\n'
|
||||
' echo Please rename it back to %(basename)s and try again.\n'
|
||||
' exec false\n'
|
||||
'fi\n'
|
||||
|
||||
) % locals()
|
||||
|
||||
if not self.dry_run:
|
||||
mkpath(os.path.dirname(self.egg_output), dry_run=self.dry_run)
|
||||
f = open(self.egg_output, 'w')
|
||||
f.write(header)
|
||||
f.close()
|
||||
return 'a'
|
||||
|
||||
|
||||
def copy_metadata_to(self, target_dir):
|
||||
"Copy metadata (egg info) to the target_dir"
|
||||
# normalize the path (so that a forward-slash in egg_info will
|
||||
# match using startswith below)
|
||||
norm_egg_info = os.path.normpath(self.egg_info)
|
||||
prefix = os.path.join(norm_egg_info,'')
|
||||
for path in self.ei_cmd.filelist.files:
|
||||
if path.startswith(prefix):
|
||||
target = os.path.join(target_dir, path[len(prefix):])
|
||||
ensure_directory(target)
|
||||
self.copy_file(path, target)
|
||||
|
||||
def get_ext_outputs(self):
|
||||
"""Get a list of relative paths to C extensions in the output distro"""
|
||||
|
||||
all_outputs = []
|
||||
ext_outputs = []
|
||||
|
||||
paths = {self.bdist_dir:''}
|
||||
for base, dirs, files in os.walk(self.bdist_dir):
|
||||
for filename in files:
|
||||
if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS:
|
||||
all_outputs.append(paths[base]+filename)
|
||||
for filename in dirs:
|
||||
paths[os.path.join(base,filename)] = paths[base]+filename+'/'
|
||||
|
||||
if self.distribution.has_ext_modules():
|
||||
build_cmd = self.get_finalized_command('build_ext')
|
||||
for ext in build_cmd.extensions:
|
||||
if isinstance(ext,Library):
|
||||
continue
|
||||
fullname = build_cmd.get_ext_fullname(ext.name)
|
||||
filename = build_cmd.get_ext_filename(fullname)
|
||||
if not os.path.basename(filename).startswith('dl-'):
|
||||
if os.path.exists(os.path.join(self.bdist_dir,filename)):
|
||||
ext_outputs.append(filename)
|
||||
|
||||
return all_outputs, ext_outputs
|
||||
|
||||
|
||||
NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split())
|
||||
|
||||
|
||||
|
||||
|
||||
def walk_egg(egg_dir):
|
||||
"""Walk an unpacked egg's contents, skipping the metadata directory"""
|
||||
walker = os.walk(egg_dir)
|
||||
base,dirs,files = walker.next()
|
||||
if 'EGG-INFO' in dirs:
|
||||
dirs.remove('EGG-INFO')
|
||||
yield base,dirs,files
|
||||
for bdf in walker:
|
||||
yield bdf
|
||||
|
||||
def analyze_egg(egg_dir, stubs):
|
||||
# check for existing flag in EGG-INFO
|
||||
for flag,fn in safety_flags.items():
|
||||
if os.path.exists(os.path.join(egg_dir,'EGG-INFO',fn)):
|
||||
return flag
|
||||
if not can_scan(): return False
|
||||
safe = True
|
||||
for base, dirs, files in walk_egg(egg_dir):
|
||||
for name in files:
|
||||
if name.endswith('.py') or name.endswith('.pyw'):
|
||||
continue
|
||||
elif name.endswith('.pyc') or name.endswith('.pyo'):
|
||||
# always scan, even if we already know we're not safe
|
||||
safe = scan_module(egg_dir, base, name, stubs) and safe
|
||||
return safe
|
||||
|
||||
def write_safety_flag(egg_dir, safe):
|
||||
# Write or remove zip safety flag file(s)
|
||||
for flag,fn in safety_flags.items():
|
||||
fn = os.path.join(egg_dir, fn)
|
||||
if os.path.exists(fn):
|
||||
if safe is None or bool(safe)<>flag:
|
||||
os.unlink(fn)
|
||||
elif safe is not None and bool(safe)==flag:
|
||||
f=open(fn,'wt'); f.write('\n'); f.close()
|
||||
|
||||
safety_flags = {
|
||||
True: 'zip-safe',
|
||||
False: 'not-zip-safe',
|
||||
}
|
||||
|
||||
def scan_module(egg_dir, base, name, stubs):
|
||||
"""Check whether module possibly uses unsafe-for-zipfile stuff"""
|
||||
|
||||
filename = os.path.join(base,name)
|
||||
if filename[:-1] in stubs:
|
||||
return True # Extension module
|
||||
pkg = base[len(egg_dir)+1:].replace(os.sep,'.')
|
||||
module = pkg+(pkg and '.' or '')+os.path.splitext(name)[0]
|
||||
if sys.version_info < (3, 3):
|
||||
skip = 8 # skip magic & date
|
||||
else:
|
||||
skip = 12 # skip magic & date & file size
|
||||
f = open(filename,'rb'); f.read(skip)
|
||||
code = marshal.load(f); f.close()
|
||||
safe = True
|
||||
symbols = dict.fromkeys(iter_symbols(code))
|
||||
for bad in ['__file__', '__path__']:
|
||||
if bad in symbols:
|
||||
log.warn("%s: module references %s", module, bad)
|
||||
safe = False
|
||||
if 'inspect' in symbols:
|
||||
for bad in [
|
||||
'getsource', 'getabsfile', 'getsourcefile', 'getfile'
|
||||
'getsourcelines', 'findsource', 'getcomments', 'getframeinfo',
|
||||
'getinnerframes', 'getouterframes', 'stack', 'trace'
|
||||
]:
|
||||
if bad in symbols:
|
||||
log.warn("%s: module MAY be using inspect.%s", module, bad)
|
||||
safe = False
|
||||
if '__name__' in symbols and '__main__' in symbols and '.' not in module:
|
||||
if sys.version[:3]=="2.4": # -m works w/zipfiles in 2.5
|
||||
log.warn("%s: top-level module may be 'python -m' script", module)
|
||||
safe = False
|
||||
return safe
|
||||
|
||||
def iter_symbols(code):
|
||||
"""Yield names and strings used by `code` and its nested code objects"""
|
||||
for name in code.co_names: yield name
|
||||
for const in code.co_consts:
|
||||
if isinstance(const,basestring):
|
||||
yield const
|
||||
elif isinstance(const,CodeType):
|
||||
for name in iter_symbols(const):
|
||||
yield name
|
||||
|
||||
def can_scan():
|
||||
if not sys.platform.startswith('java') and sys.platform != 'cli':
|
||||
# CPython, PyPy, etc.
|
||||
return True
|
||||
log.warn("Unable to analyze compiled code on this platform.")
|
||||
log.warn("Please ask the author to include a 'zip_safe'"
|
||||
" setting (either True or False) in the package's setup.py")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Attribute names of options for commands that might need to be convinced to
|
||||
# install to the egg build directory
|
||||
|
||||
INSTALL_DIRECTORY_ATTRS = [
|
||||
'install_lib', 'install_dir', 'install_data', 'install_base'
|
||||
]
|
||||
|
||||
def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=None,
|
||||
mode='w'
|
||||
):
|
||||
"""Create a zip file from all the files under 'base_dir'. The output
|
||||
zip file will be named 'base_dir' + ".zip". Uses either the "zipfile"
|
||||
Python module (if available) or the InfoZIP "zip" utility (if installed
|
||||
and found on the default search path). If neither tool is available,
|
||||
raises DistutilsExecError. Returns the name of the output zip file.
|
||||
"""
|
||||
import zipfile
|
||||
mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
|
||||
log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
|
||||
|
||||
def visit(z, dirname, names):
|
||||
for name in names:
|
||||
path = os.path.normpath(os.path.join(dirname, name))
|
||||
if os.path.isfile(path):
|
||||
p = path[len(base_dir)+1:]
|
||||
if not dry_run:
|
||||
z.write(path, p)
|
||||
log.debug("adding '%s'" % p)
|
||||
|
||||
if compress is None:
|
||||
compress = (sys.version>="2.4") # avoid 2.3 zipimport bug when 64 bits
|
||||
|
||||
compression = [zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED][bool(compress)]
|
||||
if not dry_run:
|
||||
z = zipfile.ZipFile(zip_filename, mode, compression=compression)
|
||||
for dirname, dirs, files in os.walk(base_dir):
|
||||
visit(z, dirname, files)
|
||||
z.close()
|
||||
else:
|
||||
for dirname, dirs, files in os.walk(base_dir):
|
||||
visit(None, dirname, files)
|
||||
return zip_filename
|
||||
#
|
||||
@@ -1,82 +0,0 @@
|
||||
# This is just a kludge so that bdist_rpm doesn't guess wrong about the
|
||||
# distribution name and version, if the egg_info command is going to alter
|
||||
# them, another kludge to allow you to build old-style non-egg RPMs, and
|
||||
# finally, a kludge to track .rpm files for uploading when run on Python <2.5.
|
||||
|
||||
from distutils.command.bdist_rpm import bdist_rpm as _bdist_rpm
|
||||
import sys, os
|
||||
|
||||
class bdist_rpm(_bdist_rpm):
|
||||
|
||||
def initialize_options(self):
|
||||
_bdist_rpm.initialize_options(self)
|
||||
self.no_egg = None
|
||||
|
||||
if sys.version<"2.5":
|
||||
# Track for uploading any .rpm file(s) moved to self.dist_dir
|
||||
def move_file(self, src, dst, level=1):
|
||||
_bdist_rpm.move_file(self, src, dst, level)
|
||||
if dst==self.dist_dir and src.endswith('.rpm'):
|
||||
getattr(self.distribution,'dist_files',[]).append(
|
||||
('bdist_rpm',
|
||||
src.endswith('.src.rpm') and 'any' or sys.version[:3],
|
||||
os.path.join(dst, os.path.basename(src)))
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self.run_command('egg_info') # ensure distro name is up-to-date
|
||||
_bdist_rpm.run(self)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _make_spec_file(self):
|
||||
version = self.distribution.get_version()
|
||||
rpmversion = version.replace('-','_')
|
||||
spec = _bdist_rpm._make_spec_file(self)
|
||||
line23 = '%define version '+version
|
||||
line24 = '%define version '+rpmversion
|
||||
spec = [
|
||||
line.replace(
|
||||
"Source0: %{name}-%{version}.tar",
|
||||
"Source0: %{name}-%{unmangled_version}.tar"
|
||||
).replace(
|
||||
"setup.py install ",
|
||||
"setup.py install --single-version-externally-managed "
|
||||
).replace(
|
||||
"%setup",
|
||||
"%setup -n %{name}-%{unmangled_version}"
|
||||
).replace(line23,line24)
|
||||
for line in spec
|
||||
]
|
||||
spec.insert(spec.index(line24)+1, "%define unmangled_version "+version)
|
||||
return spec
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
from distutils.command.bdist_wininst import bdist_wininst as _bdist_wininst
|
||||
import os, sys
|
||||
|
||||
class bdist_wininst(_bdist_wininst):
|
||||
|
||||
def create_exe(self, arcname, fullname, bitmap=None):
|
||||
_bdist_wininst.create_exe(self, arcname, fullname, bitmap)
|
||||
dist_files = getattr(self.distribution, 'dist_files', [])
|
||||
|
||||
if self.target_version:
|
||||
installer_name = os.path.join(self.dist_dir,
|
||||
"%s.win32-py%s.exe" %
|
||||
(fullname, self.target_version))
|
||||
pyversion = self.target_version
|
||||
|
||||
# fix 2.5 bdist_wininst ignoring --target-version spec
|
||||
bad = ('bdist_wininst','any',installer_name)
|
||||
if bad in dist_files:
|
||||
dist_files.remove(bad)
|
||||
else:
|
||||
installer_name = os.path.join(self.dist_dir,
|
||||
"%s.win32.exe" % fullname)
|
||||
pyversion = 'any'
|
||||
good = ('bdist_wininst', pyversion, installer_name)
|
||||
if good not in dist_files:
|
||||
dist_files.append(good)
|
||||
|
||||
def reinitialize_command (self, command, reinit_subcommands=0):
|
||||
cmd = self.distribution.reinitialize_command(
|
||||
command, reinit_subcommands)
|
||||
if command in ('install', 'install_lib'):
|
||||
cmd.install_lib = None # work around distutils bug
|
||||
return cmd
|
||||
|
||||
def run(self):
|
||||
self._is_running = True
|
||||
try:
|
||||
_bdist_wininst.run(self)
|
||||
finally:
|
||||
self._is_running = False
|
||||
|
||||
@@ -1,294 +0,0 @@
|
||||
from distutils.command.build_ext import build_ext as _du_build_ext
|
||||
try:
|
||||
# Attempt to use Pyrex for building extensions, if available
|
||||
from Pyrex.Distutils.build_ext import build_ext as _build_ext
|
||||
except ImportError:
|
||||
_build_ext = _du_build_ext
|
||||
|
||||
import os, sys
|
||||
from distutils.file_util import copy_file
|
||||
from setuptools.extension import Library
|
||||
from distutils.ccompiler import new_compiler
|
||||
from distutils.sysconfig import customize_compiler, get_config_var
|
||||
get_config_var("LDSHARED") # make sure _config_vars is initialized
|
||||
from distutils.sysconfig import _config_vars
|
||||
from distutils import log
|
||||
from distutils.errors import *
|
||||
|
||||
have_rtld = False
|
||||
use_stubs = False
|
||||
libtype = 'shared'
|
||||
|
||||
if sys.platform == "darwin":
|
||||
use_stubs = True
|
||||
elif os.name != 'nt':
|
||||
try:
|
||||
from dl import RTLD_NOW
|
||||
have_rtld = True
|
||||
use_stubs = True
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
def if_dl(s):
|
||||
if have_rtld:
|
||||
return s
|
||||
return ''
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class build_ext(_build_ext):
|
||||
def run(self):
|
||||
"""Build extensions in build directory, then copy if --inplace"""
|
||||
old_inplace, self.inplace = self.inplace, 0
|
||||
_build_ext.run(self)
|
||||
self.inplace = old_inplace
|
||||
if old_inplace:
|
||||
self.copy_extensions_to_source()
|
||||
|
||||
def copy_extensions_to_source(self):
|
||||
build_py = self.get_finalized_command('build_py')
|
||||
for ext in self.extensions:
|
||||
fullname = self.get_ext_fullname(ext.name)
|
||||
filename = self.get_ext_filename(fullname)
|
||||
modpath = fullname.split('.')
|
||||
package = '.'.join(modpath[:-1])
|
||||
package_dir = build_py.get_package_dir(package)
|
||||
dest_filename = os.path.join(package_dir,os.path.basename(filename))
|
||||
src_filename = os.path.join(self.build_lib,filename)
|
||||
|
||||
# Always copy, even if source is older than destination, to ensure
|
||||
# that the right extensions for the current Python/platform are
|
||||
# used.
|
||||
copy_file(
|
||||
src_filename, dest_filename, verbose=self.verbose,
|
||||
dry_run=self.dry_run
|
||||
)
|
||||
if ext._needs_stub:
|
||||
self.write_stub(package_dir or os.curdir, ext, True)
|
||||
|
||||
|
||||
if _build_ext is not _du_build_ext and not hasattr(_build_ext,'pyrex_sources'):
|
||||
# Workaround for problems using some Pyrex versions w/SWIG and/or 2.4
|
||||
def swig_sources(self, sources, *otherargs):
|
||||
# first do any Pyrex processing
|
||||
sources = _build_ext.swig_sources(self, sources) or sources
|
||||
# Then do any actual SWIG stuff on the remainder
|
||||
return _du_build_ext.swig_sources(self, sources, *otherargs)
|
||||
|
||||
|
||||
|
||||
def get_ext_filename(self, fullname):
|
||||
filename = _build_ext.get_ext_filename(self,fullname)
|
||||
if fullname not in self.ext_map:
|
||||
return filename
|
||||
ext = self.ext_map[fullname]
|
||||
if isinstance(ext,Library):
|
||||
fn, ext = os.path.splitext(filename)
|
||||
return self.shlib_compiler.library_filename(fn,libtype)
|
||||
elif use_stubs and ext._links_to_dynamic:
|
||||
d,fn = os.path.split(filename)
|
||||
return os.path.join(d,'dl-'+fn)
|
||||
else:
|
||||
return filename
|
||||
|
||||
def initialize_options(self):
|
||||
_build_ext.initialize_options(self)
|
||||
self.shlib_compiler = None
|
||||
self.shlibs = []
|
||||
self.ext_map = {}
|
||||
|
||||
def finalize_options(self):
|
||||
_build_ext.finalize_options(self)
|
||||
self.extensions = self.extensions or []
|
||||
self.check_extensions_list(self.extensions)
|
||||
self.shlibs = [ext for ext in self.extensions
|
||||
if isinstance(ext,Library)]
|
||||
if self.shlibs:
|
||||
self.setup_shlib_compiler()
|
||||
for ext in self.extensions:
|
||||
ext._full_name = self.get_ext_fullname(ext.name)
|
||||
for ext in self.extensions:
|
||||
fullname = ext._full_name
|
||||
self.ext_map[fullname] = ext
|
||||
|
||||
# distutils 3.1 will also ask for module names
|
||||
# XXX what to do with conflicts?
|
||||
self.ext_map[fullname.split('.')[-1]] = ext
|
||||
|
||||
ltd = ext._links_to_dynamic = \
|
||||
self.shlibs and self.links_to_dynamic(ext) or False
|
||||
ext._needs_stub = ltd and use_stubs and not isinstance(ext,Library)
|
||||
filename = ext._file_name = self.get_ext_filename(fullname)
|
||||
libdir = os.path.dirname(os.path.join(self.build_lib,filename))
|
||||
if ltd and libdir not in ext.library_dirs:
|
||||
ext.library_dirs.append(libdir)
|
||||
if ltd and use_stubs and os.curdir not in ext.runtime_library_dirs:
|
||||
ext.runtime_library_dirs.append(os.curdir)
|
||||
|
||||
def setup_shlib_compiler(self):
|
||||
compiler = self.shlib_compiler = new_compiler(
|
||||
compiler=self.compiler, dry_run=self.dry_run, force=self.force
|
||||
)
|
||||
if sys.platform == "darwin":
|
||||
tmp = _config_vars.copy()
|
||||
try:
|
||||
# XXX Help! I don't have any idea whether these are right...
|
||||
_config_vars['LDSHARED'] = "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup"
|
||||
_config_vars['CCSHARED'] = " -dynamiclib"
|
||||
_config_vars['SO'] = ".dylib"
|
||||
customize_compiler(compiler)
|
||||
finally:
|
||||
_config_vars.clear()
|
||||
_config_vars.update(tmp)
|
||||
else:
|
||||
customize_compiler(compiler)
|
||||
|
||||
if self.include_dirs is not None:
|
||||
compiler.set_include_dirs(self.include_dirs)
|
||||
if self.define is not None:
|
||||
# 'define' option is a list of (name,value) tuples
|
||||
for (name,value) in self.define:
|
||||
compiler.define_macro(name, value)
|
||||
if self.undef is not None:
|
||||
for macro in self.undef:
|
||||
compiler.undefine_macro(macro)
|
||||
if self.libraries is not None:
|
||||
compiler.set_libraries(self.libraries)
|
||||
if self.library_dirs is not None:
|
||||
compiler.set_library_dirs(self.library_dirs)
|
||||
if self.rpath is not None:
|
||||
compiler.set_runtime_library_dirs(self.rpath)
|
||||
if self.link_objects is not None:
|
||||
compiler.set_link_objects(self.link_objects)
|
||||
|
||||
# hack so distutils' build_extension() builds a library instead
|
||||
compiler.link_shared_object = link_shared_object.__get__(compiler)
|
||||
|
||||
|
||||
|
||||
def get_export_symbols(self, ext):
|
||||
if isinstance(ext,Library):
|
||||
return ext.export_symbols
|
||||
return _build_ext.get_export_symbols(self,ext)
|
||||
|
||||
def build_extension(self, ext):
|
||||
_compiler = self.compiler
|
||||
try:
|
||||
if isinstance(ext,Library):
|
||||
self.compiler = self.shlib_compiler
|
||||
_build_ext.build_extension(self,ext)
|
||||
if ext._needs_stub:
|
||||
self.write_stub(
|
||||
self.get_finalized_command('build_py').build_lib, ext
|
||||
)
|
||||
finally:
|
||||
self.compiler = _compiler
|
||||
|
||||
def links_to_dynamic(self, ext):
|
||||
"""Return true if 'ext' links to a dynamic lib in the same package"""
|
||||
# XXX this should check to ensure the lib is actually being built
|
||||
# XXX as dynamic, and not just using a locally-found version or a
|
||||
# XXX static-compiled version
|
||||
libnames = dict.fromkeys([lib._full_name for lib in self.shlibs])
|
||||
pkg = '.'.join(ext._full_name.split('.')[:-1]+[''])
|
||||
for libname in ext.libraries:
|
||||
if pkg+libname in libnames: return True
|
||||
return False
|
||||
|
||||
def get_outputs(self):
|
||||
outputs = _build_ext.get_outputs(self)
|
||||
optimize = self.get_finalized_command('build_py').optimize
|
||||
for ext in self.extensions:
|
||||
if ext._needs_stub:
|
||||
base = os.path.join(self.build_lib, *ext._full_name.split('.'))
|
||||
outputs.append(base+'.py')
|
||||
outputs.append(base+'.pyc')
|
||||
if optimize:
|
||||
outputs.append(base+'.pyo')
|
||||
return outputs
|
||||
|
||||
def write_stub(self, output_dir, ext, compile=False):
|
||||
log.info("writing stub loader for %s to %s",ext._full_name, output_dir)
|
||||
stub_file = os.path.join(output_dir, *ext._full_name.split('.'))+'.py'
|
||||
if compile and os.path.exists(stub_file):
|
||||
raise DistutilsError(stub_file+" already exists! Please delete.")
|
||||
if not self.dry_run:
|
||||
f = open(stub_file,'w')
|
||||
f.write('\n'.join([
|
||||
"def __bootstrap__():",
|
||||
" global __bootstrap__, __file__, __loader__",
|
||||
" import sys, os, pkg_resources, imp"+if_dl(", dl"),
|
||||
" __file__ = pkg_resources.resource_filename(__name__,%r)"
|
||||
% os.path.basename(ext._file_name),
|
||||
" del __bootstrap__",
|
||||
" if '__loader__' in globals():",
|
||||
" del __loader__",
|
||||
if_dl(" old_flags = sys.getdlopenflags()"),
|
||||
" old_dir = os.getcwd()",
|
||||
" try:",
|
||||
" os.chdir(os.path.dirname(__file__))",
|
||||
if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"),
|
||||
" imp.load_dynamic(__name__,__file__)",
|
||||
" finally:",
|
||||
if_dl(" sys.setdlopenflags(old_flags)"),
|
||||
" os.chdir(old_dir)",
|
||||
"__bootstrap__()",
|
||||
"" # terminal \n
|
||||
]))
|
||||
f.close()
|
||||
if compile:
|
||||
from distutils.util import byte_compile
|
||||
byte_compile([stub_file], optimize=0,
|
||||
force=True, dry_run=self.dry_run)
|
||||
optimize = self.get_finalized_command('install_lib').optimize
|
||||
if optimize > 0:
|
||||
byte_compile([stub_file], optimize=optimize,
|
||||
force=True, dry_run=self.dry_run)
|
||||
if os.path.exists(stub_file) and not self.dry_run:
|
||||
os.unlink(stub_file)
|
||||
|
||||
|
||||
if use_stubs or os.name=='nt':
|
||||
# Build shared libraries
|
||||
#
|
||||
def link_shared_object(self, objects, output_libname, output_dir=None,
|
||||
libraries=None, library_dirs=None, runtime_library_dirs=None,
|
||||
export_symbols=None, debug=0, extra_preargs=None,
|
||||
extra_postargs=None, build_temp=None, target_lang=None
|
||||
): self.link(
|
||||
self.SHARED_LIBRARY, objects, output_libname,
|
||||
output_dir, libraries, library_dirs, runtime_library_dirs,
|
||||
export_symbols, debug, extra_preargs, extra_postargs,
|
||||
build_temp, target_lang
|
||||
)
|
||||
else:
|
||||
# Build static libraries everywhere else
|
||||
libtype = 'static'
|
||||
|
||||
def link_shared_object(self, objects, output_libname, output_dir=None,
|
||||
libraries=None, library_dirs=None, runtime_library_dirs=None,
|
||||
export_symbols=None, debug=0, extra_preargs=None,
|
||||
extra_postargs=None, build_temp=None, target_lang=None
|
||||
):
|
||||
# XXX we need to either disallow these attrs on Library instances,
|
||||
# or warn/abort here if set, or something...
|
||||
#libraries=None, library_dirs=None, runtime_library_dirs=None,
|
||||
#export_symbols=None, extra_preargs=None, extra_postargs=None,
|
||||
#build_temp=None
|
||||
|
||||
assert output_dir is None # distutils build_ext doesn't pass this
|
||||
output_dir,filename = os.path.split(output_libname)
|
||||
basename, ext = os.path.splitext(filename)
|
||||
if self.library_filename("x").startswith('lib'):
|
||||
# strip 'lib' prefix; this is kludgy if some platform uses
|
||||
# a different prefix
|
||||
basename = basename[3:]
|
||||
|
||||
self.create_static_lib(
|
||||
objects, basename, output_dir, debug, target_lang
|
||||
)
|
||||
|
||||
|
||||
@@ -1,280 +0,0 @@
|
||||
import os.path, sys, fnmatch
|
||||
from distutils.command.build_py import build_py as _build_py
|
||||
from distutils.util import convert_path
|
||||
from glob import glob
|
||||
|
||||
try:
|
||||
from distutils.util import Mixin2to3 as _Mixin2to3
|
||||
# add support for converting doctests that is missing in 3.1 distutils
|
||||
from distutils import log
|
||||
from lib2to3.refactor import RefactoringTool, get_fixers_from_package
|
||||
import setuptools
|
||||
class DistutilsRefactoringTool(RefactoringTool):
|
||||
def log_error(self, msg, *args, **kw):
|
||||
log.error(msg, *args)
|
||||
|
||||
def log_message(self, msg, *args):
|
||||
log.info(msg, *args)
|
||||
|
||||
def log_debug(self, msg, *args):
|
||||
log.debug(msg, *args)
|
||||
|
||||
class Mixin2to3(_Mixin2to3):
|
||||
def run_2to3(self, files, doctests = False):
|
||||
# See of the distribution option has been set, otherwise check the
|
||||
# setuptools default.
|
||||
if self.distribution.use_2to3 is not True:
|
||||
return
|
||||
if not files:
|
||||
return
|
||||
log.info("Fixing "+" ".join(files))
|
||||
self.__build_fixer_names()
|
||||
self.__exclude_fixers()
|
||||
if doctests:
|
||||
if setuptools.run_2to3_on_doctests:
|
||||
r = DistutilsRefactoringTool(self.fixer_names)
|
||||
r.refactor(files, write=True, doctests_only=True)
|
||||
else:
|
||||
_Mixin2to3.run_2to3(self, files)
|
||||
|
||||
def __build_fixer_names(self):
|
||||
if self.fixer_names: return
|
||||
self.fixer_names = []
|
||||
for p in setuptools.lib2to3_fixer_packages:
|
||||
self.fixer_names.extend(get_fixers_from_package(p))
|
||||
if self.distribution.use_2to3_fixers is not None:
|
||||
for p in self.distribution.use_2to3_fixers:
|
||||
self.fixer_names.extend(get_fixers_from_package(p))
|
||||
|
||||
def __exclude_fixers(self):
|
||||
excluded_fixers = getattr(self, 'exclude_fixers', [])
|
||||
if self.distribution.use_2to3_exclude_fixers is not None:
|
||||
excluded_fixers.extend(self.distribution.use_2to3_exclude_fixers)
|
||||
for fixer_name in excluded_fixers:
|
||||
if fixer_name in self.fixer_names:
|
||||
self.fixer_names.remove(fixer_name)
|
||||
|
||||
except ImportError:
|
||||
class Mixin2to3:
|
||||
def run_2to3(self, files, doctests=True):
|
||||
# Nothing done in 2.x
|
||||
pass
|
||||
|
||||
class build_py(_build_py, Mixin2to3):
|
||||
"""Enhanced 'build_py' command that includes data files with packages
|
||||
|
||||
The data files are specified via a 'package_data' argument to 'setup()'.
|
||||
See 'setuptools.dist.Distribution' for more details.
|
||||
|
||||
Also, this version of the 'build_py' command allows you to specify both
|
||||
'py_modules' and 'packages' in the same setup operation.
|
||||
"""
|
||||
def finalize_options(self):
|
||||
_build_py.finalize_options(self)
|
||||
self.package_data = self.distribution.package_data
|
||||
self.exclude_package_data = self.distribution.exclude_package_data or {}
|
||||
if 'data_files' in self.__dict__: del self.__dict__['data_files']
|
||||
self.__updated_files = []
|
||||
self.__doctests_2to3 = []
|
||||
|
||||
def run(self):
|
||||
"""Build modules, packages, and copy data files to build directory"""
|
||||
if not self.py_modules and not self.packages:
|
||||
return
|
||||
|
||||
if self.py_modules:
|
||||
self.build_modules()
|
||||
|
||||
if self.packages:
|
||||
self.build_packages()
|
||||
self.build_package_data()
|
||||
|
||||
self.run_2to3(self.__updated_files, False)
|
||||
self.run_2to3(self.__updated_files, True)
|
||||
self.run_2to3(self.__doctests_2to3, True)
|
||||
|
||||
# Only compile actual .py files, using our base class' idea of what our
|
||||
# output files are.
|
||||
self.byte_compile(_build_py.get_outputs(self, include_bytecode=0))
|
||||
|
||||
def __getattr__(self,attr):
|
||||
if attr=='data_files': # lazily compute data files
|
||||
self.data_files = files = self._get_data_files(); return files
|
||||
return _build_py.__getattr__(self,attr)
|
||||
|
||||
def build_module(self, module, module_file, package):
|
||||
outfile, copied = _build_py.build_module(self, module, module_file, package)
|
||||
if copied:
|
||||
self.__updated_files.append(outfile)
|
||||
return outfile, copied
|
||||
|
||||
def _get_data_files(self):
|
||||
"""Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
|
||||
self.analyze_manifest()
|
||||
data = []
|
||||
for package in self.packages or ():
|
||||
# Locate package source directory
|
||||
src_dir = self.get_package_dir(package)
|
||||
|
||||
# Compute package build directory
|
||||
build_dir = os.path.join(*([self.build_lib] + package.split('.')))
|
||||
|
||||
# Length of path to strip from found files
|
||||
plen = len(src_dir)+1
|
||||
|
||||
# Strip directory from globbed filenames
|
||||
filenames = [
|
||||
file[plen:] for file in self.find_data_files(package, src_dir)
|
||||
]
|
||||
data.append( (package, src_dir, build_dir, filenames) )
|
||||
return data
|
||||
|
||||
def find_data_files(self, package, src_dir):
|
||||
"""Return filenames for package's data files in 'src_dir'"""
|
||||
globs = (self.package_data.get('', [])
|
||||
+ self.package_data.get(package, []))
|
||||
files = self.manifest_files.get(package, [])[:]
|
||||
for pattern in globs:
|
||||
# Each pattern has to be converted to a platform-specific path
|
||||
files.extend(glob(os.path.join(src_dir, convert_path(pattern))))
|
||||
return self.exclude_data_files(package, src_dir, files)
|
||||
|
||||
def build_package_data(self):
|
||||
"""Copy data files into build directory"""
|
||||
lastdir = None
|
||||
for package, src_dir, build_dir, filenames in self.data_files:
|
||||
for filename in filenames:
|
||||
target = os.path.join(build_dir, filename)
|
||||
self.mkpath(os.path.dirname(target))
|
||||
srcfile = os.path.join(src_dir, filename)
|
||||
outf, copied = self.copy_file(srcfile, target)
|
||||
srcfile = os.path.abspath(srcfile)
|
||||
if copied and srcfile in self.distribution.convert_2to3_doctests:
|
||||
self.__doctests_2to3.append(outf)
|
||||
|
||||
|
||||
def analyze_manifest(self):
|
||||
self.manifest_files = mf = {}
|
||||
if not self.distribution.include_package_data:
|
||||
return
|
||||
src_dirs = {}
|
||||
for package in self.packages or ():
|
||||
# Locate package source directory
|
||||
src_dirs[assert_relative(self.get_package_dir(package))] = package
|
||||
|
||||
self.run_command('egg_info')
|
||||
ei_cmd = self.get_finalized_command('egg_info')
|
||||
for path in ei_cmd.filelist.files:
|
||||
d,f = os.path.split(assert_relative(path))
|
||||
prev = None
|
||||
oldf = f
|
||||
while d and d!=prev and d not in src_dirs:
|
||||
prev = d
|
||||
d, df = os.path.split(d)
|
||||
f = os.path.join(df, f)
|
||||
if d in src_dirs:
|
||||
if path.endswith('.py') and f==oldf:
|
||||
continue # it's a module, not data
|
||||
mf.setdefault(src_dirs[d],[]).append(path)
|
||||
|
||||
def get_data_files(self): pass # kludge 2.4 for lazy computation
|
||||
|
||||
if sys.version<"2.4": # Python 2.4 already has this code
|
||||
def get_outputs(self, include_bytecode=1):
|
||||
"""Return complete list of files copied to the build directory
|
||||
|
||||
This includes both '.py' files and data files, as well as '.pyc'
|
||||
and '.pyo' files if 'include_bytecode' is true. (This method is
|
||||
needed for the 'install_lib' command to do its job properly, and to
|
||||
generate a correct installation manifest.)
|
||||
"""
|
||||
return _build_py.get_outputs(self, include_bytecode) + [
|
||||
os.path.join(build_dir, filename)
|
||||
for package, src_dir, build_dir,filenames in self.data_files
|
||||
for filename in filenames
|
||||
]
|
||||
|
||||
def check_package(self, package, package_dir):
|
||||
"""Check namespace packages' __init__ for declare_namespace"""
|
||||
try:
|
||||
return self.packages_checked[package]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
init_py = _build_py.check_package(self, package, package_dir)
|
||||
self.packages_checked[package] = init_py
|
||||
|
||||
if not init_py or not self.distribution.namespace_packages:
|
||||
return init_py
|
||||
|
||||
for pkg in self.distribution.namespace_packages:
|
||||
if pkg==package or pkg.startswith(package+'.'):
|
||||
break
|
||||
else:
|
||||
return init_py
|
||||
|
||||
f = open(init_py,'rbU')
|
||||
if 'declare_namespace'.encode() not in f.read():
|
||||
from distutils import log
|
||||
log.warn(
|
||||
"WARNING: %s is a namespace package, but its __init__.py does\n"
|
||||
"not declare_namespace(); setuptools 0.7 will REQUIRE this!\n"
|
||||
'(See the setuptools manual under "Namespace Packages" for '
|
||||
"details.)\n", package
|
||||
)
|
||||
f.close()
|
||||
return init_py
|
||||
|
||||
def initialize_options(self):
|
||||
self.packages_checked={}
|
||||
_build_py.initialize_options(self)
|
||||
|
||||
|
||||
def get_package_dir(self, package):
|
||||
res = _build_py.get_package_dir(self, package)
|
||||
if self.distribution.src_root is not None:
|
||||
return os.path.join(self.distribution.src_root, res)
|
||||
return res
|
||||
|
||||
|
||||
def exclude_data_files(self, package, src_dir, files):
|
||||
"""Filter filenames for package's data files in 'src_dir'"""
|
||||
globs = (self.exclude_package_data.get('', [])
|
||||
+ self.exclude_package_data.get(package, []))
|
||||
bad = []
|
||||
for pattern in globs:
|
||||
bad.extend(
|
||||
fnmatch.filter(
|
||||
files, os.path.join(src_dir, convert_path(pattern))
|
||||
)
|
||||
)
|
||||
bad = dict.fromkeys(bad)
|
||||
seen = {}
|
||||
return [
|
||||
f for f in files if f not in bad
|
||||
and f not in seen and seen.setdefault(f,1) # ditch dupes
|
||||
]
|
||||
|
||||
|
||||
def assert_relative(path):
|
||||
if not os.path.isabs(path):
|
||||
return path
|
||||
from distutils.errors import DistutilsSetupError
|
||||
raise DistutilsSetupError(
|
||||
"""Error: setup script specifies an absolute path:
|
||||
|
||||
%s
|
||||
|
||||
setup() arguments must *always* be /-separated paths relative to the
|
||||
setup.py directory, *never* absolute paths.
|
||||
""" % path
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
from setuptools.command.easy_install import easy_install
|
||||
from distutils.util import convert_path, subst_vars
|
||||
from pkg_resources import Distribution, PathMetadata, normalize_path
|
||||
from distutils import log
|
||||
from distutils.errors import DistutilsError, DistutilsOptionError
|
||||
import os, sys, setuptools, glob
|
||||
|
||||
class develop(easy_install):
|
||||
"""Set up package for development"""
|
||||
|
||||
description = "install package in 'development mode'"
|
||||
|
||||
user_options = easy_install.user_options + [
|
||||
("uninstall", "u", "Uninstall this source package"),
|
||||
("egg-path=", None, "Set the path to be used in the .egg-link file"),
|
||||
]
|
||||
|
||||
boolean_options = easy_install.boolean_options + ['uninstall']
|
||||
|
||||
command_consumes_arguments = False # override base
|
||||
|
||||
def run(self):
|
||||
if self.uninstall:
|
||||
self.multi_version = True
|
||||
self.uninstall_link()
|
||||
else:
|
||||
self.install_for_development()
|
||||
self.warn_deprecated_options()
|
||||
|
||||
def initialize_options(self):
|
||||
self.uninstall = None
|
||||
self.egg_path = None
|
||||
easy_install.initialize_options(self)
|
||||
self.setup_path = None
|
||||
self.always_copy_from = '.' # always copy eggs installed in curdir
|
||||
|
||||
|
||||
|
||||
def finalize_options(self):
|
||||
ei = self.get_finalized_command("egg_info")
|
||||
if ei.broken_egg_info:
|
||||
raise DistutilsError(
|
||||
"Please rename %r to %r before using 'develop'"
|
||||
% (ei.egg_info, ei.broken_egg_info)
|
||||
)
|
||||
self.args = [ei.egg_name]
|
||||
|
||||
|
||||
|
||||
|
||||
easy_install.finalize_options(self)
|
||||
self.expand_basedirs()
|
||||
self.expand_dirs()
|
||||
# pick up setup-dir .egg files only: no .egg-info
|
||||
self.package_index.scan(glob.glob('*.egg'))
|
||||
|
||||
self.egg_link = os.path.join(self.install_dir, ei.egg_name+'.egg-link')
|
||||
self.egg_base = ei.egg_base
|
||||
if self.egg_path is None:
|
||||
self.egg_path = os.path.abspath(ei.egg_base)
|
||||
|
||||
target = normalize_path(self.egg_base)
|
||||
if normalize_path(os.path.join(self.install_dir, self.egg_path)) != target:
|
||||
raise DistutilsOptionError(
|
||||
"--egg-path must be a relative path from the install"
|
||||
" directory to "+target
|
||||
)
|
||||
|
||||
# Make a distribution for the package's source
|
||||
self.dist = Distribution(
|
||||
target,
|
||||
PathMetadata(target, os.path.abspath(ei.egg_info)),
|
||||
project_name = ei.egg_name
|
||||
)
|
||||
|
||||
p = self.egg_base.replace(os.sep,'/')
|
||||
if p!= os.curdir:
|
||||
p = '../' * (p.count('/')+1)
|
||||
self.setup_path = p
|
||||
p = normalize_path(os.path.join(self.install_dir, self.egg_path, p))
|
||||
if p != normalize_path(os.curdir):
|
||||
raise DistutilsOptionError(
|
||||
"Can't get a consistent path to setup script from"
|
||||
" installation directory", p, normalize_path(os.curdir))
|
||||
|
||||
def install_for_development(self):
|
||||
if sys.version_info >= (3,) and getattr(self.distribution, 'use_2to3', False):
|
||||
# If we run 2to3 we can not do this inplace:
|
||||
|
||||
# Ensure metadata is up-to-date
|
||||
self.reinitialize_command('build_py', inplace=0)
|
||||
self.run_command('build_py')
|
||||
bpy_cmd = self.get_finalized_command("build_py")
|
||||
build_path = normalize_path(bpy_cmd.build_lib)
|
||||
|
||||
# Build extensions
|
||||
self.reinitialize_command('egg_info', egg_base=build_path)
|
||||
self.run_command('egg_info')
|
||||
|
||||
self.reinitialize_command('build_ext', inplace=0)
|
||||
self.run_command('build_ext')
|
||||
|
||||
# Fixup egg-link and easy-install.pth
|
||||
ei_cmd = self.get_finalized_command("egg_info")
|
||||
self.egg_path = build_path
|
||||
self.dist.location = build_path
|
||||
self.dist._provider = PathMetadata(build_path, ei_cmd.egg_info) # XXX
|
||||
else:
|
||||
# Without 2to3 inplace works fine:
|
||||
self.run_command('egg_info')
|
||||
|
||||
# Build extensions in-place
|
||||
self.reinitialize_command('build_ext', inplace=1)
|
||||
self.run_command('build_ext')
|
||||
|
||||
self.install_site_py() # ensure that target dir is site-safe
|
||||
if setuptools.bootstrap_install_from:
|
||||
self.easy_install(setuptools.bootstrap_install_from)
|
||||
setuptools.bootstrap_install_from = None
|
||||
|
||||
# create an .egg-link in the installation dir, pointing to our egg
|
||||
log.info("Creating %s (link to %s)", self.egg_link, self.egg_base)
|
||||
if not self.dry_run:
|
||||
f = open(self.egg_link,"w")
|
||||
f.write(self.egg_path + "\n" + self.setup_path)
|
||||
f.close()
|
||||
# postprocess the installed distro, fixing up .pth, installing scripts,
|
||||
# and handling requirements
|
||||
self.process_distribution(None, self.dist, not self.no_deps)
|
||||
|
||||
|
||||
def uninstall_link(self):
|
||||
if os.path.exists(self.egg_link):
|
||||
log.info("Removing %s (link to %s)", self.egg_link, self.egg_base)
|
||||
contents = [line.rstrip() for line in open(self.egg_link)]
|
||||
if contents not in ([self.egg_path], [self.egg_path, self.setup_path]):
|
||||
log.warn("Link points to %s: uninstall aborted", contents)
|
||||
return
|
||||
if not self.dry_run:
|
||||
os.unlink(self.egg_link)
|
||||
if not self.dry_run:
|
||||
self.update_pth(self.dist) # remove any .pth link to us
|
||||
if self.distribution.scripts:
|
||||
# XXX should also check for entry point scripts!
|
||||
log.warn("Note: you must uninstall or replace scripts manually!")
|
||||
|
||||
def install_egg_scripts(self, dist):
|
||||
if dist is not self.dist:
|
||||
# Installing a dependency, so fall back to normal behavior
|
||||
return easy_install.install_egg_scripts(self,dist)
|
||||
|
||||
# create wrapper scripts in the script dir, pointing to dist.scripts
|
||||
|
||||
# new-style...
|
||||
self.install_wrapper_scripts(dist)
|
||||
|
||||
# ...and old-style
|
||||
for script_name in self.distribution.scripts or []:
|
||||
script_path = os.path.abspath(convert_path(script_name))
|
||||
script_name = os.path.basename(script_path)
|
||||
f = open(script_path,'rU')
|
||||
script_text = f.read()
|
||||
f.close()
|
||||
self.install_script(dist, script_name, script_text, script_path)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,486 +0,0 @@
|
||||
"""setuptools.command.egg_info
|
||||
|
||||
Create a distribution's .egg-info directory and contents"""
|
||||
|
||||
# This module should be kept compatible with Python 2.3
|
||||
import os, re, sys
|
||||
from setuptools import Command
|
||||
from distutils.errors import *
|
||||
from distutils import log
|
||||
from setuptools.command.sdist import sdist
|
||||
from distutils.util import convert_path
|
||||
from distutils.filelist import FileList as _FileList
|
||||
from pkg_resources import parse_requirements, safe_name, parse_version, \
|
||||
safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename
|
||||
from sdist import walk_revctrl
|
||||
|
||||
class egg_info(Command):
|
||||
description = "create a distribution's .egg-info directory"
|
||||
|
||||
user_options = [
|
||||
('egg-base=', 'e', "directory containing .egg-info directories"
|
||||
" (default: top of the source tree)"),
|
||||
('tag-svn-revision', 'r',
|
||||
"Add subversion revision ID to version number"),
|
||||
('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"),
|
||||
('tag-build=', 'b', "Specify explicit tag to add to version number"),
|
||||
('no-svn-revision', 'R',
|
||||
"Don't add subversion revision ID [default]"),
|
||||
('no-date', 'D', "Don't include date stamp [default]"),
|
||||
]
|
||||
|
||||
boolean_options = ['tag-date', 'tag-svn-revision']
|
||||
negative_opt = {'no-svn-revision': 'tag-svn-revision',
|
||||
'no-date': 'tag-date'}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def initialize_options(self):
|
||||
self.egg_name = None
|
||||
self.egg_version = None
|
||||
self.egg_base = None
|
||||
self.egg_info = None
|
||||
self.tag_build = None
|
||||
self.tag_svn_revision = 0
|
||||
self.tag_date = 0
|
||||
self.broken_egg_info = False
|
||||
self.vtags = None
|
||||
|
||||
def save_version_info(self, filename):
|
||||
from setopt import edit_config
|
||||
edit_config(
|
||||
filename,
|
||||
{'egg_info':
|
||||
{'tag_svn_revision':0, 'tag_date': 0, 'tag_build': self.tags()}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def finalize_options (self):
|
||||
self.egg_name = safe_name(self.distribution.get_name())
|
||||
self.vtags = self.tags()
|
||||
self.egg_version = self.tagged_version()
|
||||
|
||||
try:
|
||||
list(
|
||||
parse_requirements('%s==%s' % (self.egg_name,self.egg_version))
|
||||
)
|
||||
except ValueError:
|
||||
raise DistutilsOptionError(
|
||||
"Invalid distribution name or version syntax: %s-%s" %
|
||||
(self.egg_name,self.egg_version)
|
||||
)
|
||||
|
||||
if self.egg_base is None:
|
||||
dirs = self.distribution.package_dir
|
||||
self.egg_base = (dirs or {}).get('',os.curdir)
|
||||
|
||||
self.ensure_dirname('egg_base')
|
||||
self.egg_info = to_filename(self.egg_name)+'.egg-info'
|
||||
if self.egg_base != os.curdir:
|
||||
self.egg_info = os.path.join(self.egg_base, self.egg_info)
|
||||
if '-' in self.egg_name: self.check_broken_egg_info()
|
||||
|
||||
# Set package version for the benefit of dumber commands
|
||||
# (e.g. sdist, bdist_wininst, etc.)
|
||||
#
|
||||
self.distribution.metadata.version = self.egg_version
|
||||
|
||||
# If we bootstrapped around the lack of a PKG-INFO, as might be the
|
||||
# case in a fresh checkout, make sure that any special tags get added
|
||||
# to the version info
|
||||
#
|
||||
pd = self.distribution._patched_dist
|
||||
if pd is not None and pd.key==self.egg_name.lower():
|
||||
pd._version = self.egg_version
|
||||
pd._parsed_version = parse_version(self.egg_version)
|
||||
self.distribution._patched_dist = None
|
||||
|
||||
|
||||
def write_or_delete_file(self, what, filename, data, force=False):
|
||||
"""Write `data` to `filename` or delete if empty
|
||||
|
||||
If `data` is non-empty, this routine is the same as ``write_file()``.
|
||||
If `data` is empty but not ``None``, this is the same as calling
|
||||
``delete_file(filename)`. If `data` is ``None``, then this is a no-op
|
||||
unless `filename` exists, in which case a warning is issued about the
|
||||
orphaned file (if `force` is false), or deleted (if `force` is true).
|
||||
"""
|
||||
if data:
|
||||
self.write_file(what, filename, data)
|
||||
elif os.path.exists(filename):
|
||||
if data is None and not force:
|
||||
log.warn(
|
||||
"%s not set in setup(), but %s exists", what, filename
|
||||
)
|
||||
return
|
||||
else:
|
||||
self.delete_file(filename)
|
||||
|
||||
def write_file(self, what, filename, data):
|
||||
"""Write `data` to `filename` (if not a dry run) after announcing it
|
||||
|
||||
`what` is used in a log message to identify what is being written
|
||||
to the file.
|
||||
"""
|
||||
log.info("writing %s to %s", what, filename)
|
||||
if sys.version_info >= (3,):
|
||||
data = data.encode("utf-8")
|
||||
if not self.dry_run:
|
||||
f = open(filename, 'wb')
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
def delete_file(self, filename):
|
||||
"""Delete `filename` (if not a dry run) after announcing it"""
|
||||
log.info("deleting %s", filename)
|
||||
if not self.dry_run:
|
||||
os.unlink(filename)
|
||||
|
||||
def tagged_version(self):
|
||||
version = self.distribution.get_version()
|
||||
# egg_info may be called more than once for a distribution,
|
||||
# in which case the version string already contains all tags.
|
||||
if self.vtags and version.endswith(self.vtags):
|
||||
return safe_version(version)
|
||||
return safe_version(version + self.vtags)
|
||||
|
||||
def run(self):
|
||||
self.mkpath(self.egg_info)
|
||||
installer = self.distribution.fetch_build_egg
|
||||
for ep in iter_entry_points('egg_info.writers'):
|
||||
writer = ep.load(installer=installer)
|
||||
writer(self, ep.name, os.path.join(self.egg_info,ep.name))
|
||||
|
||||
# Get rid of native_libs.txt if it was put there by older bdist_egg
|
||||
nl = os.path.join(self.egg_info, "native_libs.txt")
|
||||
if os.path.exists(nl):
|
||||
self.delete_file(nl)
|
||||
|
||||
self.find_sources()
|
||||
|
||||
def tags(self):
|
||||
version = ''
|
||||
if self.tag_build:
|
||||
version+=self.tag_build
|
||||
if self.tag_svn_revision and (
|
||||
os.path.exists('.svn') or os.path.exists('PKG-INFO')
|
||||
): version += '-r%s' % self.get_svn_revision()
|
||||
if self.tag_date:
|
||||
import time; version += time.strftime("-%Y%m%d")
|
||||
return version
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_svn_revision(self):
|
||||
revision = 0
|
||||
urlre = re.compile('url="([^"]+)"')
|
||||
revre = re.compile('committed-rev="(\d+)"')
|
||||
|
||||
for base,dirs,files in os.walk(os.curdir):
|
||||
if '.svn' not in dirs:
|
||||
dirs[:] = []
|
||||
continue # no sense walking uncontrolled subdirs
|
||||
dirs.remove('.svn')
|
||||
f = open(os.path.join(base,'.svn','entries'))
|
||||
data = f.read()
|
||||
f.close()
|
||||
|
||||
if data.startswith('10') or data.startswith('9') or data.startswith('8'):
|
||||
data = map(str.splitlines,data.split('\n\x0c\n'))
|
||||
del data[0][0] # get rid of the '8' or '9' or '10'
|
||||
dirurl = data[0][3]
|
||||
localrev = max([int(d[9]) for d in data if len(d)>9 and d[9]]+[0])
|
||||
elif data.startswith('<?xml'):
|
||||
dirurl = urlre.search(data).group(1) # get repository URL
|
||||
localrev = max([int(m.group(1)) for m in revre.finditer(data)]+[0])
|
||||
else:
|
||||
log.warn("unrecognized .svn/entries format; skipping %s", base)
|
||||
dirs[:] = []
|
||||
continue
|
||||
if base==os.curdir:
|
||||
base_url = dirurl+'/' # save the root url
|
||||
elif not dirurl.startswith(base_url):
|
||||
dirs[:] = []
|
||||
continue # not part of the same svn tree, skip it
|
||||
revision = max(revision, localrev)
|
||||
|
||||
return str(revision or get_pkg_info_revision())
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def find_sources(self):
|
||||
"""Generate SOURCES.txt manifest file"""
|
||||
manifest_filename = os.path.join(self.egg_info,"SOURCES.txt")
|
||||
mm = manifest_maker(self.distribution)
|
||||
mm.manifest = manifest_filename
|
||||
mm.run()
|
||||
self.filelist = mm.filelist
|
||||
|
||||
def check_broken_egg_info(self):
|
||||
bei = self.egg_name+'.egg-info'
|
||||
if self.egg_base != os.curdir:
|
||||
bei = os.path.join(self.egg_base, bei)
|
||||
if os.path.exists(bei):
|
||||
log.warn(
|
||||
"-"*78+'\n'
|
||||
"Note: Your current .egg-info directory has a '-' in its name;"
|
||||
'\nthis will not work correctly with "setup.py develop".\n\n'
|
||||
'Please rename %s to %s to correct this problem.\n'+'-'*78,
|
||||
bei, self.egg_info
|
||||
)
|
||||
self.broken_egg_info = self.egg_info
|
||||
self.egg_info = bei # make it work for now
|
||||
|
||||
class FileList(_FileList):
|
||||
"""File list that accepts only existing, platform-independent paths"""
|
||||
|
||||
def append(self, item):
|
||||
if item.endswith('\r'): # Fix older sdists built on Windows
|
||||
item = item[:-1]
|
||||
path = convert_path(item)
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
try:
|
||||
if os.path.exists(path) or os.path.exists(path.encode('utf-8')):
|
||||
self.files.append(path)
|
||||
except UnicodeEncodeError:
|
||||
# Accept UTF-8 filenames even if LANG=C
|
||||
if os.path.exists(path.encode('utf-8')):
|
||||
self.files.append(path)
|
||||
else:
|
||||
log.warn("'%s' not %s encodable -- skipping", path,
|
||||
sys.getfilesystemencoding())
|
||||
else:
|
||||
if os.path.exists(path):
|
||||
self.files.append(path)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class manifest_maker(sdist):
|
||||
|
||||
template = "MANIFEST.in"
|
||||
|
||||
def initialize_options (self):
|
||||
self.use_defaults = 1
|
||||
self.prune = 1
|
||||
self.manifest_only = 1
|
||||
self.force_manifest = 1
|
||||
|
||||
def finalize_options(self):
|
||||
pass
|
||||
|
||||
def run(self):
|
||||
self.filelist = FileList()
|
||||
if not os.path.exists(self.manifest):
|
||||
self.write_manifest() # it must exist so it'll get in the list
|
||||
self.filelist.findall()
|
||||
self.add_defaults()
|
||||
if os.path.exists(self.template):
|
||||
self.read_template()
|
||||
self.prune_file_list()
|
||||
self.filelist.sort()
|
||||
self.filelist.remove_duplicates()
|
||||
self.write_manifest()
|
||||
|
||||
def write_manifest (self):
|
||||
"""Write the file list in 'self.filelist' (presumably as filled in
|
||||
by 'add_defaults()' and 'read_template()') to the manifest file
|
||||
named by 'self.manifest'.
|
||||
"""
|
||||
# The manifest must be UTF-8 encodable. See #303.
|
||||
if sys.version_info >= (3,):
|
||||
files = []
|
||||
for file in self.filelist.files:
|
||||
try:
|
||||
file.encode("utf-8")
|
||||
except UnicodeEncodeError:
|
||||
log.warn("'%s' not UTF-8 encodable -- skipping" % file)
|
||||
else:
|
||||
files.append(file)
|
||||
self.filelist.files = files
|
||||
|
||||
files = self.filelist.files
|
||||
if os.sep!='/':
|
||||
files = [f.replace(os.sep,'/') for f in files]
|
||||
self.execute(write_file, (self.manifest, files),
|
||||
"writing manifest file '%s'" % self.manifest)
|
||||
|
||||
def warn(self, msg): # suppress missing-file warnings from sdist
|
||||
if not msg.startswith("standard file not found:"):
|
||||
sdist.warn(self, msg)
|
||||
|
||||
def add_defaults(self):
|
||||
sdist.add_defaults(self)
|
||||
self.filelist.append(self.template)
|
||||
self.filelist.append(self.manifest)
|
||||
rcfiles = list(walk_revctrl())
|
||||
if rcfiles:
|
||||
self.filelist.extend(rcfiles)
|
||||
elif os.path.exists(self.manifest):
|
||||
self.read_manifest()
|
||||
ei_cmd = self.get_finalized_command('egg_info')
|
||||
self.filelist.include_pattern("*", prefix=ei_cmd.egg_info)
|
||||
|
||||
def prune_file_list (self):
|
||||
build = self.get_finalized_command('build')
|
||||
base_dir = self.distribution.get_fullname()
|
||||
self.filelist.exclude_pattern(None, prefix=build.build_base)
|
||||
self.filelist.exclude_pattern(None, prefix=base_dir)
|
||||
sep = re.escape(os.sep)
|
||||
self.filelist.exclude_pattern(sep+r'(RCS|CVS|\.svn)'+sep, is_regex=1)
|
||||
|
||||
|
||||
def write_file (filename, contents):
|
||||
"""Create a file with the specified name and write 'contents' (a
|
||||
sequence of strings without line terminators) to it.
|
||||
"""
|
||||
contents = "\n".join(contents)
|
||||
if sys.version_info >= (3,):
|
||||
contents = contents.encode("utf-8")
|
||||
f = open(filename, "wb") # always write POSIX-style manifest
|
||||
f.write(contents)
|
||||
f.close()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def write_pkg_info(cmd, basename, filename):
|
||||
log.info("writing %s", filename)
|
||||
if not cmd.dry_run:
|
||||
metadata = cmd.distribution.metadata
|
||||
metadata.version, oldver = cmd.egg_version, metadata.version
|
||||
metadata.name, oldname = cmd.egg_name, metadata.name
|
||||
try:
|
||||
# write unescaped data to PKG-INFO, so older pkg_resources
|
||||
# can still parse it
|
||||
metadata.write_pkg_info(cmd.egg_info)
|
||||
finally:
|
||||
metadata.name, metadata.version = oldname, oldver
|
||||
|
||||
safe = getattr(cmd.distribution,'zip_safe',None)
|
||||
import bdist_egg; bdist_egg.write_safety_flag(cmd.egg_info, safe)
|
||||
|
||||
def warn_depends_obsolete(cmd, basename, filename):
|
||||
if os.path.exists(filename):
|
||||
log.warn(
|
||||
"WARNING: 'depends.txt' is not used by setuptools 0.6!\n"
|
||||
"Use the install_requires/extras_require setup() args instead."
|
||||
)
|
||||
|
||||
|
||||
def write_requirements(cmd, basename, filename):
|
||||
dist = cmd.distribution
|
||||
data = ['\n'.join(yield_lines(dist.install_requires or ()))]
|
||||
for extra,reqs in (dist.extras_require or {}).items():
|
||||
data.append('\n\n[%s]\n%s' % (extra, '\n'.join(yield_lines(reqs))))
|
||||
cmd.write_or_delete_file("requirements", filename, ''.join(data))
|
||||
|
||||
def write_toplevel_names(cmd, basename, filename):
|
||||
pkgs = dict.fromkeys(
|
||||
[k.split('.',1)[0]
|
||||
for k in cmd.distribution.iter_distribution_names()
|
||||
]
|
||||
)
|
||||
cmd.write_file("top-level names", filename, '\n'.join(pkgs)+'\n')
|
||||
|
||||
|
||||
|
||||
def overwrite_arg(cmd, basename, filename):
|
||||
write_arg(cmd, basename, filename, True)
|
||||
|
||||
def write_arg(cmd, basename, filename, force=False):
|
||||
argname = os.path.splitext(basename)[0]
|
||||
value = getattr(cmd.distribution, argname, None)
|
||||
if value is not None:
|
||||
value = '\n'.join(value)+'\n'
|
||||
cmd.write_or_delete_file(argname, filename, value, force)
|
||||
|
||||
def write_entries(cmd, basename, filename):
|
||||
ep = cmd.distribution.entry_points
|
||||
|
||||
if isinstance(ep,basestring) or ep is None:
|
||||
data = ep
|
||||
elif ep is not None:
|
||||
data = []
|
||||
for section, contents in ep.items():
|
||||
if not isinstance(contents,basestring):
|
||||
contents = EntryPoint.parse_group(section, contents)
|
||||
contents = '\n'.join(map(str,contents.values()))
|
||||
data.append('[%s]\n%s\n\n' % (section,contents))
|
||||
data = ''.join(data)
|
||||
|
||||
cmd.write_or_delete_file('entry points', filename, data, True)
|
||||
|
||||
def get_pkg_info_revision():
|
||||
# See if we can get a -r### off of PKG-INFO, in case this is an sdist of
|
||||
# a subversion revision
|
||||
#
|
||||
if os.path.exists('PKG-INFO'):
|
||||
f = open('PKG-INFO','rU')
|
||||
for line in f:
|
||||
match = re.match(r"Version:.*-r(\d+)\s*$", line)
|
||||
if match:
|
||||
return int(match.group(1))
|
||||
f.close()
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
#
|
||||
@@ -1,124 +0,0 @@
|
||||
import setuptools, sys, glob
|
||||
from distutils.command.install import install as _install
|
||||
from distutils.errors import DistutilsArgError
|
||||
|
||||
class install(_install):
|
||||
"""Use easy_install to install the package, w/dependencies"""
|
||||
|
||||
user_options = _install.user_options + [
|
||||
('old-and-unmanageable', None, "Try not to use this!"),
|
||||
('single-version-externally-managed', None,
|
||||
"used by system package builders to create 'flat' eggs"),
|
||||
]
|
||||
boolean_options = _install.boolean_options + [
|
||||
'old-and-unmanageable', 'single-version-externally-managed',
|
||||
]
|
||||
new_commands = [
|
||||
('install_egg_info', lambda self: True),
|
||||
('install_scripts', lambda self: True),
|
||||
]
|
||||
_nc = dict(new_commands)
|
||||
|
||||
def initialize_options(self):
|
||||
_install.initialize_options(self)
|
||||
self.old_and_unmanageable = None
|
||||
self.single_version_externally_managed = None
|
||||
self.no_compile = None # make DISTUTILS_DEBUG work right!
|
||||
|
||||
def finalize_options(self):
|
||||
_install.finalize_options(self)
|
||||
if self.root:
|
||||
self.single_version_externally_managed = True
|
||||
elif self.single_version_externally_managed:
|
||||
if not self.root and not self.record:
|
||||
raise DistutilsArgError(
|
||||
"You must specify --record or --root when building system"
|
||||
" packages"
|
||||
)
|
||||
|
||||
def handle_extra_path(self):
|
||||
if self.root or self.single_version_externally_managed:
|
||||
# explicit backward-compatibility mode, allow extra_path to work
|
||||
return _install.handle_extra_path(self)
|
||||
|
||||
# Ignore extra_path when installing an egg (or being run by another
|
||||
# command without --root or --single-version-externally-managed
|
||||
self.path_file = None
|
||||
self.extra_dirs = ''
|
||||
|
||||
|
||||
def run(self):
|
||||
# Explicit request for old-style install? Just do it
|
||||
if self.old_and_unmanageable or self.single_version_externally_managed:
|
||||
return _install.run(self)
|
||||
|
||||
# Attempt to detect whether we were called from setup() or by another
|
||||
# command. If we were called by setup(), our caller will be the
|
||||
# 'run_command' method in 'distutils.dist', and *its* caller will be
|
||||
# the 'run_commands' method. If we were called any other way, our
|
||||
# immediate caller *might* be 'run_command', but it won't have been
|
||||
# called by 'run_commands'. This is slightly kludgy, but seems to
|
||||
# work.
|
||||
#
|
||||
caller = sys._getframe(2)
|
||||
caller_module = caller.f_globals.get('__name__','')
|
||||
caller_name = caller.f_code.co_name
|
||||
|
||||
if caller_module != 'distutils.dist' or caller_name!='run_commands':
|
||||
# We weren't called from the command line or setup(), so we
|
||||
# should run in backward-compatibility mode to support bdist_*
|
||||
# commands.
|
||||
_install.run(self)
|
||||
else:
|
||||
self.do_egg_install()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def do_egg_install(self):
|
||||
|
||||
easy_install = self.distribution.get_command_class('easy_install')
|
||||
|
||||
cmd = easy_install(
|
||||
self.distribution, args="x", root=self.root, record=self.record,
|
||||
)
|
||||
cmd.ensure_finalized() # finalize before bdist_egg munges install cmd
|
||||
cmd.always_copy_from = '.' # make sure local-dir eggs get installed
|
||||
|
||||
# pick up setup-dir .egg files only: no .egg-info
|
||||
cmd.package_index.scan(glob.glob('*.egg'))
|
||||
|
||||
self.run_command('bdist_egg')
|
||||
args = [self.distribution.get_command_obj('bdist_egg').egg_output]
|
||||
|
||||
if setuptools.bootstrap_install_from:
|
||||
# Bootstrap self-installation of setuptools
|
||||
args.insert(0, setuptools.bootstrap_install_from)
|
||||
|
||||
cmd.args = args
|
||||
cmd.run()
|
||||
setuptools.bootstrap_install_from = None
|
||||
|
||||
# XXX Python 3.1 doesn't see _nc if this is inside the class
|
||||
install.sub_commands = [
|
||||
cmd for cmd in _install.sub_commands if cmd[0] not in install._nc
|
||||
] + install.new_commands
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#
|
||||
@@ -1,125 +0,0 @@
|
||||
from setuptools import Command
|
||||
from setuptools.archive_util import unpack_archive
|
||||
from distutils import log, dir_util
|
||||
import os, shutil, pkg_resources
|
||||
|
||||
class install_egg_info(Command):
|
||||
"""Install an .egg-info directory for the package"""
|
||||
|
||||
description = "Install an .egg-info directory for the package"
|
||||
|
||||
user_options = [
|
||||
('install-dir=', 'd', "directory to install to"),
|
||||
]
|
||||
|
||||
def initialize_options(self):
|
||||
self.install_dir = None
|
||||
|
||||
def finalize_options(self):
|
||||
self.set_undefined_options('install_lib',('install_dir','install_dir'))
|
||||
ei_cmd = self.get_finalized_command("egg_info")
|
||||
basename = pkg_resources.Distribution(
|
||||
None, None, ei_cmd.egg_name, ei_cmd.egg_version
|
||||
).egg_name()+'.egg-info'
|
||||
self.source = ei_cmd.egg_info
|
||||
self.target = os.path.join(self.install_dir, basename)
|
||||
self.outputs = [self.target]
|
||||
|
||||
def run(self):
|
||||
self.run_command('egg_info')
|
||||
target = self.target
|
||||
if os.path.isdir(self.target) and not os.path.islink(self.target):
|
||||
dir_util.remove_tree(self.target, dry_run=self.dry_run)
|
||||
elif os.path.exists(self.target):
|
||||
self.execute(os.unlink,(self.target,),"Removing "+self.target)
|
||||
if not self.dry_run:
|
||||
pkg_resources.ensure_directory(self.target)
|
||||
self.execute(self.copytree, (),
|
||||
"Copying %s to %s" % (self.source, self.target)
|
||||
)
|
||||
self.install_namespaces()
|
||||
|
||||
def get_outputs(self):
|
||||
return self.outputs
|
||||
|
||||
def copytree(self):
|
||||
# Copy the .egg-info tree to site-packages
|
||||
def skimmer(src,dst):
|
||||
# filter out source-control directories; note that 'src' is always
|
||||
# a '/'-separated path, regardless of platform. 'dst' is a
|
||||
# platform-specific path.
|
||||
for skip in '.svn/','CVS/':
|
||||
if src.startswith(skip) or '/'+skip in src:
|
||||
return None
|
||||
self.outputs.append(dst)
|
||||
log.debug("Copying %s to %s", src, dst)
|
||||
return dst
|
||||
unpack_archive(self.source, self.target, skimmer)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def install_namespaces(self):
|
||||
nsp = self._get_all_ns_packages()
|
||||
if not nsp: return
|
||||
filename,ext = os.path.splitext(self.target)
|
||||
filename += '-nspkg.pth'; self.outputs.append(filename)
|
||||
log.info("Installing %s",filename)
|
||||
if not self.dry_run:
|
||||
f = open(filename,'wt')
|
||||
for pkg in nsp:
|
||||
# ensure pkg is not a unicode string under Python 2.7
|
||||
pkg = str(pkg)
|
||||
pth = tuple(pkg.split('.'))
|
||||
trailer = '\n'
|
||||
if '.' in pkg:
|
||||
trailer = (
|
||||
"; m and setattr(sys.modules[%r], %r, m)\n"
|
||||
% ('.'.join(pth[:-1]), pth[-1])
|
||||
)
|
||||
f.write(
|
||||
"import sys,types,os; "
|
||||
"p = os.path.join(sys._getframe(1).f_locals['sitedir'], "
|
||||
"*%(pth)r); "
|
||||
"ie = os.path.exists(os.path.join(p,'__init__.py')); "
|
||||
"m = not ie and "
|
||||
"sys.modules.setdefault(%(pkg)r,types.ModuleType(%(pkg)r)); "
|
||||
"mp = (m or []) and m.__dict__.setdefault('__path__',[]); "
|
||||
"(p not in mp) and mp.append(p)%(trailer)s"
|
||||
% locals()
|
||||
)
|
||||
f.close()
|
||||
|
||||
def _get_all_ns_packages(self):
|
||||
nsp = {}
|
||||
for pkg in self.distribution.namespace_packages or []:
|
||||
pkg = pkg.split('.')
|
||||
while pkg:
|
||||
nsp['.'.join(pkg)] = 1
|
||||
pkg.pop()
|
||||
nsp=list(nsp)
|
||||
nsp.sort() # set up shorter names first
|
||||
return nsp
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
from distutils.command.install_lib import install_lib as _install_lib
|
||||
import os
|
||||
|
||||
class install_lib(_install_lib):
|
||||
"""Don't add compiled flags to filenames of non-Python files"""
|
||||
|
||||
def _bytecode_filenames (self, py_filenames):
|
||||
bytecode_files = []
|
||||
for py_file in py_filenames:
|
||||
if not py_file.endswith('.py'):
|
||||
continue
|
||||
if self.compile:
|
||||
bytecode_files.append(py_file + "c")
|
||||
if self.optimize > 0:
|
||||
bytecode_files.append(py_file + "o")
|
||||
|
||||
return bytecode_files
|
||||
|
||||
def run(self):
|
||||
self.build()
|
||||
outfiles = self.install()
|
||||
if outfiles is not None:
|
||||
# always compile, in case we have any extension stubs to deal with
|
||||
self.byte_compile(outfiles)
|
||||
|
||||
def get_exclusions(self):
|
||||
exclude = {}
|
||||
nsp = self.distribution.namespace_packages
|
||||
|
||||
if (nsp and self.get_finalized_command('install')
|
||||
.single_version_externally_managed
|
||||
):
|
||||
for pkg in nsp:
|
||||
parts = pkg.split('.')
|
||||
while parts:
|
||||
pkgdir = os.path.join(self.install_dir, *parts)
|
||||
for f in '__init__.py', '__init__.pyc', '__init__.pyo':
|
||||
exclude[os.path.join(pkgdir,f)] = 1
|
||||
parts.pop()
|
||||
return exclude
|
||||
|
||||
def copy_tree(
|
||||
self, infile, outfile,
|
||||
preserve_mode=1, preserve_times=1, preserve_symlinks=0, level=1
|
||||
):
|
||||
assert preserve_mode and preserve_times and not preserve_symlinks
|
||||
exclude = self.get_exclusions()
|
||||
|
||||
if not exclude:
|
||||
return _install_lib.copy_tree(self, infile, outfile)
|
||||
|
||||
# Exclude namespace package __init__.py* files from the output
|
||||
|
||||
from setuptools.archive_util import unpack_directory
|
||||
from distutils import log
|
||||
|
||||
outfiles = []
|
||||
|
||||
def pf(src, dst):
|
||||
if dst in exclude:
|
||||
log.warn("Skipping installation of %s (namespace package)",dst)
|
||||
return False
|
||||
|
||||
log.info("copying %s -> %s", src, os.path.dirname(dst))
|
||||
outfiles.append(dst)
|
||||
return dst
|
||||
|
||||
unpack_directory(infile, outfile, pf)
|
||||
return outfiles
|
||||
|
||||
def get_outputs(self):
|
||||
outputs = _install_lib.get_outputs(self)
|
||||
exclude = self.get_exclusions()
|
||||
if exclude:
|
||||
return [f for f in outputs if f not in exclude]
|
||||
return outputs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
from distutils.command.install_scripts import install_scripts \
|
||||
as _install_scripts
|
||||
from pkg_resources import Distribution, PathMetadata, ensure_directory
|
||||
import os
|
||||
from distutils import log
|
||||
|
||||
class install_scripts(_install_scripts):
|
||||
"""Do normal script install, plus any egg_info wrapper scripts"""
|
||||
|
||||
def initialize_options(self):
|
||||
_install_scripts.initialize_options(self)
|
||||
self.no_ep = False
|
||||
|
||||
def run(self):
|
||||
from setuptools.command.easy_install import get_script_args
|
||||
from setuptools.command.easy_install import sys_executable
|
||||
|
||||
self.run_command("egg_info")
|
||||
if self.distribution.scripts:
|
||||
_install_scripts.run(self) # run first to set up self.outfiles
|
||||
else:
|
||||
self.outfiles = []
|
||||
if self.no_ep:
|
||||
# don't install entry point scripts into .egg file!
|
||||
return
|
||||
|
||||
ei_cmd = self.get_finalized_command("egg_info")
|
||||
dist = Distribution(
|
||||
ei_cmd.egg_base, PathMetadata(ei_cmd.egg_base, ei_cmd.egg_info),
|
||||
ei_cmd.egg_name, ei_cmd.egg_version,
|
||||
)
|
||||
bs_cmd = self.get_finalized_command('build_scripts')
|
||||
executable = getattr(bs_cmd,'executable',sys_executable)
|
||||
is_wininst = getattr(
|
||||
self.get_finalized_command("bdist_wininst"), '_is_running', False
|
||||
)
|
||||
for args in get_script_args(dist, executable, is_wininst):
|
||||
self.write_script(*args)
|
||||
|
||||
def write_script(self, script_name, contents, mode="t", *ignored):
|
||||
"""Write an executable file to the scripts directory"""
|
||||
from setuptools.command.easy_install import chmod, current_umask
|
||||
log.info("Installing %s script to %s", script_name, self.install_dir)
|
||||
target = os.path.join(self.install_dir, script_name)
|
||||
self.outfiles.append(target)
|
||||
|
||||
mask = current_umask()
|
||||
if not self.dry_run:
|
||||
ensure_directory(target)
|
||||
f = open(target,"w"+mode)
|
||||
f.write(contents)
|
||||
f.close()
|
||||
chmod(target, 0777-mask)
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
from distutils.command.register import register as _register
|
||||
|
||||
class register(_register):
|
||||
__doc__ = _register.__doc__
|
||||
|
||||
def run(self):
|
||||
# Make sure that we are using valid current name/version info
|
||||
self.run_command('egg_info')
|
||||
_register.run(self)
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
import distutils, os
|
||||
from setuptools import Command
|
||||
from distutils.util import convert_path
|
||||
from distutils import log
|
||||
from distutils.errors import *
|
||||
|
||||
class rotate(Command):
|
||||
"""Delete older distributions"""
|
||||
|
||||
description = "delete older distributions, keeping N newest files"
|
||||
user_options = [
|
||||
('match=', 'm', "patterns to match (required)"),
|
||||
('dist-dir=', 'd', "directory where the distributions are"),
|
||||
('keep=', 'k', "number of matching distributions to keep"),
|
||||
]
|
||||
|
||||
boolean_options = []
|
||||
|
||||
def initialize_options(self):
|
||||
self.match = None
|
||||
self.dist_dir = None
|
||||
self.keep = None
|
||||
|
||||
def finalize_options(self):
|
||||
if self.match is None:
|
||||
raise DistutilsOptionError(
|
||||
"Must specify one or more (comma-separated) match patterns "
|
||||
"(e.g. '.zip' or '.egg')"
|
||||
)
|
||||
if self.keep is None:
|
||||
raise DistutilsOptionError("Must specify number of files to keep")
|
||||
try:
|
||||
self.keep = int(self.keep)
|
||||
except ValueError:
|
||||
raise DistutilsOptionError("--keep must be an integer")
|
||||
if isinstance(self.match, basestring):
|
||||
self.match = [
|
||||
convert_path(p.strip()) for p in self.match.split(',')
|
||||
]
|
||||
self.set_undefined_options('bdist',('dist_dir', 'dist_dir'))
|
||||
|
||||
def run(self):
|
||||
self.run_command("egg_info")
|
||||
from glob import glob
|
||||
for pattern in self.match:
|
||||
pattern = self.distribution.get_name()+'*'+pattern
|
||||
files = glob(os.path.join(self.dist_dir,pattern))
|
||||
files = [(os.path.getmtime(f),f) for f in files]
|
||||
files.sort()
|
||||
files.reverse()
|
||||
|
||||
log.info("%d file(s) matching %s", len(files), pattern)
|
||||
files = files[self.keep:]
|
||||
for (t,f) in files:
|
||||
log.info("Deleting %s", f)
|
||||
if not self.dry_run:
|
||||
os.unlink(f)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import distutils, os
|
||||
from setuptools import Command
|
||||
from setuptools.command.setopt import edit_config, option_base
|
||||
|
||||
class saveopts(option_base):
|
||||
"""Save command-line options to a file"""
|
||||
|
||||
description = "save supplied options to setup.cfg or other config file"
|
||||
|
||||
def run(self):
|
||||
dist = self.distribution
|
||||
commands = dist.command_options.keys()
|
||||
settings = {}
|
||||
|
||||
for cmd in commands:
|
||||
|
||||
if cmd=='saveopts':
|
||||
continue # don't save our own options!
|
||||
|
||||
for opt,(src,val) in dist.get_option_dict(cmd).items():
|
||||
if src=="command line":
|
||||
settings.setdefault(cmd,{})[opt] = val
|
||||
|
||||
edit_config(self.filename, settings, self.dry_run)
|
||||
|
||||
@@ -1,313 +0,0 @@
|
||||
from distutils.command.sdist import sdist as _sdist
|
||||
from distutils.util import convert_path
|
||||
from distutils import log
|
||||
import os, re, sys, pkg_resources
|
||||
from glob import glob
|
||||
|
||||
READMES = ('README', 'README.rst', 'README.txt')
|
||||
|
||||
entities = [
|
||||
("<","<"), (">", ">"), (""", '"'), ("'", "'"),
|
||||
("&", "&")
|
||||
]
|
||||
|
||||
def unescape(data):
|
||||
for old,new in entities:
|
||||
data = data.replace(old,new)
|
||||
return data
|
||||
|
||||
def re_finder(pattern, postproc=None):
|
||||
def find(dirname, filename):
|
||||
f = open(filename,'rU')
|
||||
data = f.read()
|
||||
f.close()
|
||||
for match in pattern.finditer(data):
|
||||
path = match.group(1)
|
||||
if postproc:
|
||||
path = postproc(path)
|
||||
yield joinpath(dirname,path)
|
||||
return find
|
||||
|
||||
def joinpath(prefix,suffix):
|
||||
if not prefix:
|
||||
return suffix
|
||||
return os.path.join(prefix,suffix)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def walk_revctrl(dirname=''):
|
||||
"""Find all files under revision control"""
|
||||
for ep in pkg_resources.iter_entry_points('setuptools.file_finders'):
|
||||
for item in ep.load()(dirname):
|
||||
yield item
|
||||
|
||||
def _default_revctrl(dirname=''):
|
||||
for path, finder in finders:
|
||||
path = joinpath(dirname,path)
|
||||
if os.path.isfile(path):
|
||||
for path in finder(dirname,path):
|
||||
if os.path.isfile(path):
|
||||
yield path
|
||||
elif os.path.isdir(path):
|
||||
for item in _default_revctrl(path):
|
||||
yield item
|
||||
|
||||
def externals_finder(dirname, filename):
|
||||
"""Find any 'svn:externals' directories"""
|
||||
found = False
|
||||
f = open(filename,'rt')
|
||||
for line in iter(f.readline, ''): # can't use direct iter!
|
||||
parts = line.split()
|
||||
if len(parts)==2:
|
||||
kind,length = parts
|
||||
data = f.read(int(length))
|
||||
if kind=='K' and data=='svn:externals':
|
||||
found = True
|
||||
elif kind=='V' and found:
|
||||
f.close()
|
||||
break
|
||||
else:
|
||||
f.close()
|
||||
return
|
||||
|
||||
for line in data.splitlines():
|
||||
parts = line.split()
|
||||
if parts:
|
||||
yield joinpath(dirname, parts[0])
|
||||
|
||||
|
||||
entries_pattern = re.compile(r'name="([^"]+)"(?![^>]+deleted="true")', re.I)
|
||||
|
||||
def entries_finder(dirname, filename):
|
||||
f = open(filename,'rU')
|
||||
data = f.read()
|
||||
f.close()
|
||||
if data.startswith('10') or data.startswith('9') or data.startswith('8'):
|
||||
for record in map(str.splitlines, data.split('\n\x0c\n')[1:]):
|
||||
# subversion 1.6/1.5/1.4
|
||||
if not record or len(record)>=6 and record[5]=="delete":
|
||||
continue # skip deleted
|
||||
yield joinpath(dirname, record[0])
|
||||
elif data.startswith('<?xml'):
|
||||
for match in entries_pattern.finditer(data):
|
||||
yield joinpath(dirname,unescape(match.group(1)))
|
||||
else:
|
||||
log.warn("unrecognized .svn/entries format in %s", os.path.abspath(dirname))
|
||||
|
||||
|
||||
finders = [
|
||||
(convert_path('CVS/Entries'),
|
||||
re_finder(re.compile(r"^\w?/([^/]+)/", re.M))),
|
||||
(convert_path('.svn/entries'), entries_finder),
|
||||
(convert_path('.svn/dir-props'), externals_finder),
|
||||
(convert_path('.svn/dir-prop-base'), externals_finder), # svn 1.4
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class sdist(_sdist):
|
||||
"""Smart sdist that finds anything supported by revision control"""
|
||||
|
||||
user_options = [
|
||||
('formats=', None,
|
||||
"formats for source distribution (comma-separated list)"),
|
||||
('keep-temp', 'k',
|
||||
"keep the distribution tree around after creating " +
|
||||
"archive file(s)"),
|
||||
('dist-dir=', 'd',
|
||||
"directory to put the source distribution archive(s) in "
|
||||
"[default: dist]"),
|
||||
]
|
||||
|
||||
negative_opt = {}
|
||||
|
||||
def run(self):
|
||||
self.run_command('egg_info')
|
||||
ei_cmd = self.get_finalized_command('egg_info')
|
||||
self.filelist = ei_cmd.filelist
|
||||
self.filelist.append(os.path.join(ei_cmd.egg_info,'SOURCES.txt'))
|
||||
self.check_readme()
|
||||
|
||||
# Run sub commands
|
||||
for cmd_name in self.get_sub_commands():
|
||||
self.run_command(cmd_name)
|
||||
|
||||
# Call check_metadata only if no 'check' command
|
||||
# (distutils <= 2.6)
|
||||
import distutils.command
|
||||
if 'check' not in distutils.command.__all__:
|
||||
self.check_metadata()
|
||||
|
||||
self.make_distribution()
|
||||
|
||||
dist_files = getattr(self.distribution,'dist_files',[])
|
||||
for file in self.archive_files:
|
||||
data = ('sdist', '', file)
|
||||
if data not in dist_files:
|
||||
dist_files.append(data)
|
||||
|
||||
def add_defaults(self):
|
||||
standards = [READMES,
|
||||
self.distribution.script_name]
|
||||
for fn in standards:
|
||||
if isinstance(fn, tuple):
|
||||
alts = fn
|
||||
got_it = 0
|
||||
for fn in alts:
|
||||
if os.path.exists(fn):
|
||||
got_it = 1
|
||||
self.filelist.append(fn)
|
||||
break
|
||||
|
||||
if not got_it:
|
||||
self.warn("standard file not found: should have one of " +
|
||||
', '.join(alts))
|
||||
else:
|
||||
if os.path.exists(fn):
|
||||
self.filelist.append(fn)
|
||||
else:
|
||||
self.warn("standard file '%s' not found" % fn)
|
||||
|
||||
optional = ['test/test*.py', 'setup.cfg']
|
||||
for pattern in optional:
|
||||
files = filter(os.path.isfile, glob(pattern))
|
||||
if files:
|
||||
self.filelist.extend(files)
|
||||
|
||||
# getting python files
|
||||
if self.distribution.has_pure_modules():
|
||||
build_py = self.get_finalized_command('build_py')
|
||||
self.filelist.extend(build_py.get_source_files())
|
||||
# This functionality is incompatible with include_package_data, and
|
||||
# will in fact create an infinite recursion if include_package_data
|
||||
# is True. Use of include_package_data will imply that
|
||||
# distutils-style automatic handling of package_data is disabled
|
||||
if not self.distribution.include_package_data:
|
||||
for _, src_dir, _, filenames in build_py.data_files:
|
||||
self.filelist.extend([os.path.join(src_dir, filename)
|
||||
for filename in filenames])
|
||||
|
||||
if self.distribution.has_ext_modules():
|
||||
build_ext = self.get_finalized_command('build_ext')
|
||||
self.filelist.extend(build_ext.get_source_files())
|
||||
|
||||
if self.distribution.has_c_libraries():
|
||||
build_clib = self.get_finalized_command('build_clib')
|
||||
self.filelist.extend(build_clib.get_source_files())
|
||||
|
||||
if self.distribution.has_scripts():
|
||||
build_scripts = self.get_finalized_command('build_scripts')
|
||||
self.filelist.extend(build_scripts.get_source_files())
|
||||
|
||||
def __read_template_hack(self):
|
||||
# This grody hack closes the template file (MANIFEST.in) if an
|
||||
# exception occurs during read_template.
|
||||
# Doing so prevents an error when easy_install attempts to delete the
|
||||
# file.
|
||||
try:
|
||||
_sdist.read_template(self)
|
||||
except:
|
||||
sys.exc_info()[2].tb_next.tb_frame.f_locals['template'].close()
|
||||
raise
|
||||
# Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle
|
||||
# has been fixed, so only override the method if we're using an earlier
|
||||
# Python.
|
||||
if (
|
||||
sys.version_info < (2,7,2)
|
||||
or (3,0) <= sys.version_info < (3,1,4)
|
||||
or (3,2) <= sys.version_info < (3,2,1)
|
||||
):
|
||||
read_template = __read_template_hack
|
||||
|
||||
def check_readme(self):
|
||||
for f in READMES:
|
||||
if os.path.exists(f):
|
||||
return
|
||||
else:
|
||||
self.warn(
|
||||
"standard file not found: should have one of " +', '.join(READMES)
|
||||
)
|
||||
|
||||
|
||||
def make_release_tree(self, base_dir, files):
|
||||
_sdist.make_release_tree(self, base_dir, files)
|
||||
|
||||
# Save any egg_info command line options used to create this sdist
|
||||
dest = os.path.join(base_dir, 'setup.cfg')
|
||||
if hasattr(os,'link') and os.path.exists(dest):
|
||||
# unlink and re-copy, since it might be hard-linked, and
|
||||
# we don't want to change the source version
|
||||
os.unlink(dest)
|
||||
self.copy_file('setup.cfg', dest)
|
||||
|
||||
self.get_finalized_command('egg_info').save_version_info(dest)
|
||||
|
||||
def _manifest_is_not_generated(self):
|
||||
# check for special comment used in 2.7.1 and higher
|
||||
if not os.path.isfile(self.manifest):
|
||||
return False
|
||||
|
||||
fp = open(self.manifest, 'rbU')
|
||||
try:
|
||||
first_line = fp.readline()
|
||||
finally:
|
||||
fp.close()
|
||||
return first_line != '# file GENERATED by distutils, do NOT edit\n'.encode()
|
||||
|
||||
def read_manifest(self):
|
||||
"""Read the manifest file (named by 'self.manifest') and use it to
|
||||
fill in 'self.filelist', the list of files to include in the source
|
||||
distribution.
|
||||
"""
|
||||
log.info("reading manifest file '%s'", self.manifest)
|
||||
manifest = open(self.manifest, 'rbU')
|
||||
for line in manifest:
|
||||
# The manifest must contain UTF-8. See #303.
|
||||
if sys.version_info >= (3,):
|
||||
try:
|
||||
line = line.decode('UTF-8')
|
||||
except UnicodeDecodeError:
|
||||
log.warn("%r not UTF-8 decodable -- skipping" % line)
|
||||
continue
|
||||
# ignore comments and blank lines
|
||||
line = line.strip()
|
||||
if line.startswith('#') or not line:
|
||||
continue
|
||||
self.filelist.append(line)
|
||||
manifest.close()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#
|
||||
@@ -1,164 +0,0 @@
|
||||
import distutils, os
|
||||
from setuptools import Command
|
||||
from distutils.util import convert_path
|
||||
from distutils import log
|
||||
from distutils.errors import *
|
||||
|
||||
__all__ = ['config_file', 'edit_config', 'option_base', 'setopt']
|
||||
|
||||
|
||||
def config_file(kind="local"):
|
||||
"""Get the filename of the distutils, local, global, or per-user config
|
||||
|
||||
`kind` must be one of "local", "global", or "user"
|
||||
"""
|
||||
if kind=='local':
|
||||
return 'setup.cfg'
|
||||
if kind=='global':
|
||||
return os.path.join(
|
||||
os.path.dirname(distutils.__file__),'distutils.cfg'
|
||||
)
|
||||
if kind=='user':
|
||||
dot = os.name=='posix' and '.' or ''
|
||||
return os.path.expanduser(convert_path("~/%spydistutils.cfg" % dot))
|
||||
raise ValueError(
|
||||
"config_file() type must be 'local', 'global', or 'user'", kind
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def edit_config(filename, settings, dry_run=False):
|
||||
"""Edit a configuration file to include `settings`
|
||||
|
||||
`settings` is a dictionary of dictionaries or ``None`` values, keyed by
|
||||
command/section name. A ``None`` value means to delete the entire section,
|
||||
while a dictionary lists settings to be changed or deleted in that section.
|
||||
A setting of ``None`` means to delete that setting.
|
||||
"""
|
||||
from ConfigParser import RawConfigParser
|
||||
log.debug("Reading configuration from %s", filename)
|
||||
opts = RawConfigParser()
|
||||
opts.read([filename])
|
||||
for section, options in settings.items():
|
||||
if options is None:
|
||||
log.info("Deleting section [%s] from %s", section, filename)
|
||||
opts.remove_section(section)
|
||||
else:
|
||||
if not opts.has_section(section):
|
||||
log.debug("Adding new section [%s] to %s", section, filename)
|
||||
opts.add_section(section)
|
||||
for option,value in options.items():
|
||||
if value is None:
|
||||
log.debug("Deleting %s.%s from %s",
|
||||
section, option, filename
|
||||
)
|
||||
opts.remove_option(section,option)
|
||||
if not opts.options(section):
|
||||
log.info("Deleting empty [%s] section from %s",
|
||||
section, filename)
|
||||
opts.remove_section(section)
|
||||
else:
|
||||
log.debug(
|
||||
"Setting %s.%s to %r in %s",
|
||||
section, option, value, filename
|
||||
)
|
||||
opts.set(section,option,value)
|
||||
|
||||
log.info("Writing %s", filename)
|
||||
if not dry_run:
|
||||
f = open(filename,'w'); opts.write(f); f.close()
|
||||
|
||||
class option_base(Command):
|
||||
"""Abstract base class for commands that mess with config files"""
|
||||
|
||||
user_options = [
|
||||
('global-config', 'g',
|
||||
"save options to the site-wide distutils.cfg file"),
|
||||
('user-config', 'u',
|
||||
"save options to the current user's pydistutils.cfg file"),
|
||||
('filename=', 'f',
|
||||
"configuration file to use (default=setup.cfg)"),
|
||||
]
|
||||
|
||||
boolean_options = [
|
||||
'global-config', 'user-config',
|
||||
]
|
||||
|
||||
def initialize_options(self):
|
||||
self.global_config = None
|
||||
self.user_config = None
|
||||
self.filename = None
|
||||
|
||||
def finalize_options(self):
|
||||
filenames = []
|
||||
if self.global_config:
|
||||
filenames.append(config_file('global'))
|
||||
if self.user_config:
|
||||
filenames.append(config_file('user'))
|
||||
if self.filename is not None:
|
||||
filenames.append(self.filename)
|
||||
if not filenames:
|
||||
filenames.append(config_file('local'))
|
||||
if len(filenames)>1:
|
||||
raise DistutilsOptionError(
|
||||
"Must specify only one configuration file option",
|
||||
filenames
|
||||
)
|
||||
self.filename, = filenames
|
||||
|
||||
|
||||
|
||||
|
||||
class setopt(option_base):
|
||||
"""Save command-line options to a file"""
|
||||
|
||||
description = "set an option in setup.cfg or another config file"
|
||||
|
||||
user_options = [
|
||||
('command=', 'c', 'command to set an option for'),
|
||||
('option=', 'o', 'option to set'),
|
||||
('set-value=', 's', 'value of the option'),
|
||||
('remove', 'r', 'remove (unset) the value'),
|
||||
] + option_base.user_options
|
||||
|
||||
boolean_options = option_base.boolean_options + ['remove']
|
||||
|
||||
def initialize_options(self):
|
||||
option_base.initialize_options(self)
|
||||
self.command = None
|
||||
self.option = None
|
||||
self.set_value = None
|
||||
self.remove = None
|
||||
|
||||
def finalize_options(self):
|
||||
option_base.finalize_options(self)
|
||||
if self.command is None or self.option is None:
|
||||
raise DistutilsOptionError("Must specify --command *and* --option")
|
||||
if self.set_value is None and not self.remove:
|
||||
raise DistutilsOptionError("Must specify --set-value or --remove")
|
||||
|
||||
def run(self):
|
||||
edit_config(
|
||||
self.filename, {
|
||||
self.command: {self.option.replace('-','_'):self.set_value}
|
||||
},
|
||||
self.dry_run
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-198
@@ -1,198 +0,0 @@
|
||||
from setuptools import Command
|
||||
from distutils.errors import DistutilsOptionError
|
||||
import sys
|
||||
from pkg_resources import *
|
||||
from pkg_resources import _namespace_packages
|
||||
from unittest import TestLoader, main
|
||||
|
||||
class ScanningLoader(TestLoader):
|
||||
|
||||
def loadTestsFromModule(self, module):
|
||||
"""Return a suite of all tests cases contained in the given module
|
||||
|
||||
If the module is a package, load tests from all the modules in it.
|
||||
If the module has an ``additional_tests`` function, call it and add
|
||||
the return value to the tests.
|
||||
"""
|
||||
tests = []
|
||||
if module.__name__!='setuptools.tests.doctest': # ugh
|
||||
tests.append(TestLoader.loadTestsFromModule(self,module))
|
||||
|
||||
if hasattr(module, "additional_tests"):
|
||||
tests.append(module.additional_tests())
|
||||
|
||||
if hasattr(module, '__path__'):
|
||||
for file in resource_listdir(module.__name__, ''):
|
||||
if file.endswith('.py') and file!='__init__.py':
|
||||
submodule = module.__name__+'.'+file[:-3]
|
||||
else:
|
||||
if resource_exists(
|
||||
module.__name__, file+'/__init__.py'
|
||||
):
|
||||
submodule = module.__name__+'.'+file
|
||||
else:
|
||||
continue
|
||||
tests.append(self.loadTestsFromName(submodule))
|
||||
|
||||
if len(tests)!=1:
|
||||
return self.suiteClass(tests)
|
||||
else:
|
||||
return tests[0] # don't create a nested suite for only one return
|
||||
|
||||
|
||||
class test(Command):
|
||||
|
||||
"""Command to run unit tests after in-place build"""
|
||||
|
||||
description = "run unit tests after in-place build"
|
||||
|
||||
user_options = [
|
||||
('test-module=','m', "Run 'test_suite' in specified module"),
|
||||
('test-suite=','s',
|
||||
"Test suite to run (e.g. 'some_module.test_suite')"),
|
||||
]
|
||||
|
||||
def initialize_options(self):
|
||||
self.test_suite = None
|
||||
self.test_module = None
|
||||
self.test_loader = None
|
||||
|
||||
|
||||
def finalize_options(self):
|
||||
|
||||
if self.test_suite is None:
|
||||
if self.test_module is None:
|
||||
self.test_suite = self.distribution.test_suite
|
||||
else:
|
||||
self.test_suite = self.test_module+".test_suite"
|
||||
elif self.test_module:
|
||||
raise DistutilsOptionError(
|
||||
"You may specify a module or a suite, but not both"
|
||||
)
|
||||
|
||||
self.test_args = [self.test_suite]
|
||||
|
||||
if self.verbose:
|
||||
self.test_args.insert(0,'--verbose')
|
||||
if self.test_loader is None:
|
||||
self.test_loader = getattr(self.distribution,'test_loader',None)
|
||||
if self.test_loader is None:
|
||||
self.test_loader = "setuptools.command.test:ScanningLoader"
|
||||
|
||||
|
||||
|
||||
def with_project_on_sys_path(self, func):
|
||||
if sys.version_info >= (3,) and getattr(self.distribution, 'use_2to3', False):
|
||||
# If we run 2to3 we can not do this inplace:
|
||||
|
||||
# Ensure metadata is up-to-date
|
||||
self.reinitialize_command('build_py', inplace=0)
|
||||
self.run_command('build_py')
|
||||
bpy_cmd = self.get_finalized_command("build_py")
|
||||
build_path = normalize_path(bpy_cmd.build_lib)
|
||||
|
||||
# Build extensions
|
||||
self.reinitialize_command('egg_info', egg_base=build_path)
|
||||
self.run_command('egg_info')
|
||||
|
||||
self.reinitialize_command('build_ext', inplace=0)
|
||||
self.run_command('build_ext')
|
||||
else:
|
||||
# Without 2to3 inplace works fine:
|
||||
self.run_command('egg_info')
|
||||
|
||||
# Build extensions in-place
|
||||
self.reinitialize_command('build_ext', inplace=1)
|
||||
self.run_command('build_ext')
|
||||
|
||||
ei_cmd = self.get_finalized_command("egg_info")
|
||||
|
||||
old_path = sys.path[:]
|
||||
old_modules = sys.modules.copy()
|
||||
|
||||
try:
|
||||
sys.path.insert(0, normalize_path(ei_cmd.egg_base))
|
||||
working_set.__init__()
|
||||
add_activation_listener(lambda dist: dist.activate())
|
||||
require('%s==%s' % (ei_cmd.egg_name, ei_cmd.egg_version))
|
||||
func()
|
||||
finally:
|
||||
sys.path[:] = old_path
|
||||
sys.modules.clear()
|
||||
sys.modules.update(old_modules)
|
||||
working_set.__init__()
|
||||
|
||||
|
||||
def run(self):
|
||||
if self.distribution.install_requires:
|
||||
self.distribution.fetch_build_eggs(self.distribution.install_requires)
|
||||
if self.distribution.tests_require:
|
||||
self.distribution.fetch_build_eggs(self.distribution.tests_require)
|
||||
|
||||
if self.test_suite:
|
||||
cmd = ' '.join(self.test_args)
|
||||
if self.dry_run:
|
||||
self.announce('skipping "unittest %s" (dry run)' % cmd)
|
||||
else:
|
||||
self.announce('running "unittest %s"' % cmd)
|
||||
self.with_project_on_sys_path(self.run_tests)
|
||||
|
||||
|
||||
def run_tests(self):
|
||||
import unittest
|
||||
|
||||
# Purge modules under test from sys.modules. The test loader will
|
||||
# re-import them from the build location. Required when 2to3 is used
|
||||
# with namespace packages.
|
||||
if sys.version_info >= (3,) and getattr(self.distribution, 'use_2to3', False):
|
||||
module = self.test_args[-1].split('.')[0]
|
||||
if module in _namespace_packages:
|
||||
del_modules = []
|
||||
if module in sys.modules:
|
||||
del_modules.append(module)
|
||||
module += '.'
|
||||
for name in sys.modules:
|
||||
if name.startswith(module):
|
||||
del_modules.append(name)
|
||||
map(sys.modules.__delitem__, del_modules)
|
||||
|
||||
loader_ep = EntryPoint.parse("x="+self.test_loader)
|
||||
loader_class = loader_ep.load(require=False)
|
||||
cks = loader_class()
|
||||
unittest.main(
|
||||
None, None, [unittest.__file__]+self.test_args,
|
||||
testLoader = cks
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
"""distutils.command.upload
|
||||
|
||||
Implements the Distutils 'upload' subcommand (upload package to PyPI)."""
|
||||
|
||||
from distutils.errors import *
|
||||
from distutils.core import Command
|
||||
from distutils.spawn import spawn
|
||||
from distutils import log
|
||||
try:
|
||||
from hashlib import md5
|
||||
except ImportError:
|
||||
from md5 import md5
|
||||
import os
|
||||
import socket
|
||||
import platform
|
||||
import ConfigParser
|
||||
import httplib
|
||||
import base64
|
||||
import urlparse
|
||||
import cStringIO as StringIO
|
||||
|
||||
class upload(Command):
|
||||
|
||||
description = "upload binary package to PyPI"
|
||||
|
||||
DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi'
|
||||
|
||||
user_options = [
|
||||
('repository=', 'r',
|
||||
"url of repository [default: %s]" % DEFAULT_REPOSITORY),
|
||||
('show-response', None,
|
||||
'display full response text from server'),
|
||||
('sign', 's',
|
||||
'sign files to upload using gpg'),
|
||||
('identity=', 'i', 'GPG identity used to sign files'),
|
||||
]
|
||||
boolean_options = ['show-response', 'sign']
|
||||
|
||||
def initialize_options(self):
|
||||
self.username = ''
|
||||
self.password = ''
|
||||
self.repository = ''
|
||||
self.show_response = 0
|
||||
self.sign = False
|
||||
self.identity = None
|
||||
|
||||
def finalize_options(self):
|
||||
if self.identity and not self.sign:
|
||||
raise DistutilsOptionError(
|
||||
"Must use --sign for --identity to have meaning"
|
||||
)
|
||||
if os.environ.has_key('HOME'):
|
||||
rc = os.path.join(os.environ['HOME'], '.pypirc')
|
||||
if os.path.exists(rc):
|
||||
self.announce('Using PyPI login from %s' % rc)
|
||||
config = ConfigParser.ConfigParser({
|
||||
'username':'',
|
||||
'password':'',
|
||||
'repository':''})
|
||||
config.read(rc)
|
||||
if not self.repository:
|
||||
self.repository = config.get('server-login', 'repository')
|
||||
if not self.username:
|
||||
self.username = config.get('server-login', 'username')
|
||||
if not self.password:
|
||||
self.password = config.get('server-login', 'password')
|
||||
if not self.repository:
|
||||
self.repository = self.DEFAULT_REPOSITORY
|
||||
|
||||
def run(self):
|
||||
if not self.distribution.dist_files:
|
||||
raise DistutilsOptionError("No dist file created in earlier command")
|
||||
for command, pyversion, filename in self.distribution.dist_files:
|
||||
self.upload_file(command, pyversion, filename)
|
||||
|
||||
def upload_file(self, command, pyversion, filename):
|
||||
# Sign if requested
|
||||
if self.sign:
|
||||
gpg_args = ["gpg", "--detach-sign", "-a", filename]
|
||||
if self.identity:
|
||||
gpg_args[2:2] = ["--local-user", self.identity]
|
||||
spawn(gpg_args,
|
||||
dry_run=self.dry_run)
|
||||
|
||||
# Fill in the data
|
||||
f = open(filename,'rb')
|
||||
content = f.read()
|
||||
f.close()
|
||||
basename = os.path.basename(filename)
|
||||
comment = ''
|
||||
if command=='bdist_egg' and self.distribution.has_ext_modules():
|
||||
comment = "built on %s" % platform.platform(terse=1)
|
||||
data = {
|
||||
':action':'file_upload',
|
||||
'protocol_version':'1',
|
||||
'name':self.distribution.get_name(),
|
||||
'version':self.distribution.get_version(),
|
||||
'content':(basename,content),
|
||||
'filetype':command,
|
||||
'pyversion':pyversion,
|
||||
'md5_digest':md5(content).hexdigest(),
|
||||
}
|
||||
if command == 'bdist_rpm':
|
||||
dist, version, id = platform.dist()
|
||||
if dist:
|
||||
comment = 'built for %s %s' % (dist, version)
|
||||
elif command == 'bdist_dumb':
|
||||
comment = 'built for %s' % platform.platform(terse=1)
|
||||
data['comment'] = comment
|
||||
|
||||
if self.sign:
|
||||
data['gpg_signature'] = (os.path.basename(filename) + ".asc",
|
||||
open(filename+".asc").read())
|
||||
|
||||
# set up the authentication
|
||||
auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip()
|
||||
|
||||
# Build up the MIME payload for the POST data
|
||||
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
|
||||
sep_boundary = '\n--' + boundary
|
||||
end_boundary = sep_boundary + '--'
|
||||
body = StringIO.StringIO()
|
||||
for key, value in data.items():
|
||||
# handle multiple entries for the same name
|
||||
if type(value) != type([]):
|
||||
value = [value]
|
||||
for value in value:
|
||||
if type(value) is tuple:
|
||||
fn = ';filename="%s"' % value[0]
|
||||
value = value[1]
|
||||
else:
|
||||
fn = ""
|
||||
value = str(value)
|
||||
body.write(sep_boundary)
|
||||
body.write('\nContent-Disposition: form-data; name="%s"'%key)
|
||||
body.write(fn)
|
||||
body.write("\n\n")
|
||||
body.write(value)
|
||||
if value and value[-1] == '\r':
|
||||
body.write('\n') # write an extra newline (lurve Macs)
|
||||
body.write(end_boundary)
|
||||
body.write("\n")
|
||||
body = body.getvalue()
|
||||
|
||||
self.announce("Submitting %s to %s" % (filename, self.repository), log.INFO)
|
||||
|
||||
# build the Request
|
||||
# We can't use urllib2 since we need to send the Basic
|
||||
# auth right with the first request
|
||||
schema, netloc, url, params, query, fragments = \
|
||||
urlparse.urlparse(self.repository)
|
||||
assert not params and not query and not fragments
|
||||
if schema == 'http':
|
||||
http = httplib.HTTPConnection(netloc)
|
||||
elif schema == 'https':
|
||||
http = httplib.HTTPSConnection(netloc)
|
||||
else:
|
||||
raise AssertionError, "unsupported schema "+schema
|
||||
|
||||
data = ''
|
||||
loglevel = log.INFO
|
||||
try:
|
||||
http.connect()
|
||||
http.putrequest("POST", url)
|
||||
http.putheader('Content-type',
|
||||
'multipart/form-data; boundary=%s'%boundary)
|
||||
http.putheader('Content-length', str(len(body)))
|
||||
http.putheader('Authorization', auth)
|
||||
http.endheaders()
|
||||
http.send(body)
|
||||
except socket.error, e:
|
||||
self.announce(str(e), log.ERROR)
|
||||
return
|
||||
|
||||
r = http.getresponse()
|
||||
if r.status == 200:
|
||||
self.announce('Server response (%s): %s' % (r.status, r.reason),
|
||||
log.INFO)
|
||||
else:
|
||||
self.announce('Upload failed (%s): %s' % (r.status, r.reason),
|
||||
log.ERROR)
|
||||
if self.show_response:
|
||||
print '-'*75, r.read(), '-'*75
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""upload_docs
|
||||
|
||||
Implements a Distutils 'upload_docs' subcommand (upload documentation to
|
||||
PyPI's packages.python.org).
|
||||
"""
|
||||
|
||||
import os
|
||||
import socket
|
||||
import zipfile
|
||||
import httplib
|
||||
import urlparse
|
||||
import tempfile
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
from base64 import standard_b64encode
|
||||
from pkg_resources import iter_entry_points
|
||||
|
||||
from distutils import log
|
||||
from distutils.errors import DistutilsOptionError
|
||||
|
||||
try:
|
||||
from distutils.command.upload import upload
|
||||
except ImportError:
|
||||
from setuptools.command.upload import upload
|
||||
|
||||
|
||||
# This is not just a replacement for byte literals
|
||||
# but works as a general purpose encoder
|
||||
def b(s, encoding='utf-8'):
|
||||
if isinstance(s, unicode):
|
||||
return s.encode(encoding)
|
||||
return s
|
||||
|
||||
|
||||
class upload_docs(upload):
|
||||
|
||||
description = 'Upload documentation to PyPI'
|
||||
|
||||
user_options = [
|
||||
('repository=', 'r',
|
||||
"url of repository [default: %s]" % upload.DEFAULT_REPOSITORY),
|
||||
('show-response', None,
|
||||
'display full response text from server'),
|
||||
('upload-dir=', None, 'directory to upload'),
|
||||
]
|
||||
boolean_options = upload.boolean_options
|
||||
|
||||
def has_sphinx(self):
|
||||
if self.upload_dir is None:
|
||||
for ep in iter_entry_points('distutils.commands', 'build_sphinx'):
|
||||
return True
|
||||
|
||||
sub_commands = [('build_sphinx', has_sphinx)]
|
||||
|
||||
def initialize_options(self):
|
||||
upload.initialize_options(self)
|
||||
self.upload_dir = None
|
||||
self.target_dir = None
|
||||
|
||||
def finalize_options(self):
|
||||
upload.finalize_options(self)
|
||||
if self.upload_dir is None:
|
||||
if self.has_sphinx():
|
||||
build_sphinx = self.get_finalized_command('build_sphinx')
|
||||
self.target_dir = build_sphinx.builder_target_dir
|
||||
else:
|
||||
build = self.get_finalized_command('build')
|
||||
self.target_dir = os.path.join(build.build_base, 'docs')
|
||||
else:
|
||||
self.ensure_dirname('upload_dir')
|
||||
self.target_dir = self.upload_dir
|
||||
self.announce('Using upload directory %s' % self.target_dir)
|
||||
|
||||
def create_zipfile(self, filename):
|
||||
zip_file = zipfile.ZipFile(filename, "w")
|
||||
try:
|
||||
self.mkpath(self.target_dir) # just in case
|
||||
for root, dirs, files in os.walk(self.target_dir):
|
||||
if root == self.target_dir and not files:
|
||||
raise DistutilsOptionError(
|
||||
"no files found in upload directory '%s'"
|
||||
% self.target_dir)
|
||||
for name in files:
|
||||
full = os.path.join(root, name)
|
||||
relative = root[len(self.target_dir):].lstrip(os.path.sep)
|
||||
dest = os.path.join(relative, name)
|
||||
zip_file.write(full, dest)
|
||||
finally:
|
||||
zip_file.close()
|
||||
|
||||
def run(self):
|
||||
# Run sub commands
|
||||
for cmd_name in self.get_sub_commands():
|
||||
self.run_command(cmd_name)
|
||||
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
name = self.distribution.metadata.get_name()
|
||||
zip_file = os.path.join(tmp_dir, "%s.zip" % name)
|
||||
try:
|
||||
self.create_zipfile(zip_file)
|
||||
self.upload_file(zip_file)
|
||||
finally:
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
||||
def upload_file(self, filename):
|
||||
content = open(filename, 'rb').read()
|
||||
meta = self.distribution.metadata
|
||||
data = {
|
||||
':action': 'doc_upload',
|
||||
'name': meta.get_name(),
|
||||
'content': (os.path.basename(filename), content),
|
||||
}
|
||||
# set up the authentication
|
||||
credentials = b(self.username + ':' + self.password)
|
||||
credentials = standard_b64encode(credentials)
|
||||
if sys.version_info >= (3,):
|
||||
credentials = credentials.decode('ascii')
|
||||
auth = "Basic " + credentials
|
||||
|
||||
# Build up the MIME payload for the POST data
|
||||
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
|
||||
sep_boundary = b('\n--') + b(boundary)
|
||||
end_boundary = sep_boundary + b('--')
|
||||
body = []
|
||||
for key, values in data.iteritems():
|
||||
title = '\nContent-Disposition: form-data; name="%s"' % key
|
||||
# handle multiple entries for the same name
|
||||
if type(values) != type([]):
|
||||
values = [values]
|
||||
for value in values:
|
||||
if type(value) is tuple:
|
||||
title += '; filename="%s"' % value[0]
|
||||
value = value[1]
|
||||
else:
|
||||
value = b(value)
|
||||
body.append(sep_boundary)
|
||||
body.append(b(title))
|
||||
body.append(b("\n\n"))
|
||||
body.append(value)
|
||||
if value and value[-1:] == b('\r'):
|
||||
body.append(b('\n')) # write an extra newline (lurve Macs)
|
||||
body.append(end_boundary)
|
||||
body.append(b("\n"))
|
||||
body = b('').join(body)
|
||||
|
||||
self.announce("Submitting documentation to %s" % (self.repository),
|
||||
log.INFO)
|
||||
|
||||
# build the Request
|
||||
# We can't use urllib2 since we need to send the Basic
|
||||
# auth right with the first request
|
||||
schema, netloc, url, params, query, fragments = \
|
||||
urlparse.urlparse(self.repository)
|
||||
assert not params and not query and not fragments
|
||||
if schema == 'http':
|
||||
conn = httplib.HTTPConnection(netloc)
|
||||
elif schema == 'https':
|
||||
conn = httplib.HTTPSConnection(netloc)
|
||||
else:
|
||||
raise AssertionError("unsupported schema "+schema)
|
||||
|
||||
data = ''
|
||||
loglevel = log.INFO
|
||||
try:
|
||||
conn.connect()
|
||||
conn.putrequest("POST", url)
|
||||
conn.putheader('Content-type',
|
||||
'multipart/form-data; boundary=%s'%boundary)
|
||||
conn.putheader('Content-length', str(len(body)))
|
||||
conn.putheader('Authorization', auth)
|
||||
conn.endheaders()
|
||||
conn.send(body)
|
||||
except socket.error, e:
|
||||
self.announce(str(e), log.ERROR)
|
||||
return
|
||||
|
||||
r = conn.getresponse()
|
||||
if r.status == 200:
|
||||
self.announce('Server response (%s): %s' % (r.status, r.reason),
|
||||
log.INFO)
|
||||
elif r.status == 301:
|
||||
location = r.getheader('Location')
|
||||
if location is None:
|
||||
location = 'http://packages.python.org/%s/' % meta.get_name()
|
||||
self.announce('Upload successful. Visit %s' % location,
|
||||
log.INFO)
|
||||
else:
|
||||
self.announce('Upload failed (%s): %s' % (r.status, r.reason),
|
||||
log.ERROR)
|
||||
if self.show_response:
|
||||
print '-'*75, r.read(), '-'*75
|
||||
-246
@@ -1,246 +0,0 @@
|
||||
from __future__ import generators
|
||||
import sys, imp, marshal
|
||||
from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN
|
||||
from distutils.version import StrictVersion, LooseVersion
|
||||
|
||||
__all__ = [
|
||||
'Require', 'find_module', 'get_module_constant', 'extract_constant'
|
||||
]
|
||||
|
||||
class Require:
|
||||
"""A prerequisite to building or installing a distribution"""
|
||||
|
||||
def __init__(self,name,requested_version,module,homepage='',
|
||||
attribute=None,format=None
|
||||
):
|
||||
|
||||
if format is None and requested_version is not None:
|
||||
format = StrictVersion
|
||||
|
||||
if format is not None:
|
||||
requested_version = format(requested_version)
|
||||
if attribute is None:
|
||||
attribute = '__version__'
|
||||
|
||||
self.__dict__.update(locals())
|
||||
del self.self
|
||||
|
||||
|
||||
def full_name(self):
|
||||
"""Return full package/distribution name, w/version"""
|
||||
if self.requested_version is not None:
|
||||
return '%s-%s' % (self.name,self.requested_version)
|
||||
return self.name
|
||||
|
||||
|
||||
def version_ok(self,version):
|
||||
"""Is 'version' sufficiently up-to-date?"""
|
||||
return self.attribute is None or self.format is None or \
|
||||
str(version)<>"unknown" and version >= self.requested_version
|
||||
|
||||
|
||||
def get_version(self, paths=None, default="unknown"):
|
||||
|
||||
"""Get version number of installed module, 'None', or 'default'
|
||||
|
||||
Search 'paths' for module. If not found, return 'None'. If found,
|
||||
return the extracted version attribute, or 'default' if no version
|
||||
attribute was specified, or the value cannot be determined without
|
||||
importing the module. The version is formatted according to the
|
||||
requirement's version format (if any), unless it is 'None' or the
|
||||
supplied 'default'.
|
||||
"""
|
||||
|
||||
if self.attribute is None:
|
||||
try:
|
||||
f,p,i = find_module(self.module,paths)
|
||||
if f: f.close()
|
||||
return default
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
v = get_module_constant(self.module,self.attribute,default,paths)
|
||||
|
||||
if v is not None and v is not default and self.format is not None:
|
||||
return self.format(v)
|
||||
|
||||
return v
|
||||
|
||||
|
||||
def is_present(self,paths=None):
|
||||
"""Return true if dependency is present on 'paths'"""
|
||||
return self.get_version(paths) is not None
|
||||
|
||||
|
||||
def is_current(self,paths=None):
|
||||
"""Return true if dependency is present and up-to-date on 'paths'"""
|
||||
version = self.get_version(paths)
|
||||
if version is None:
|
||||
return False
|
||||
return self.version_ok(version)
|
||||
|
||||
|
||||
def _iter_code(code):
|
||||
|
||||
"""Yield '(op,arg)' pair for each operation in code object 'code'"""
|
||||
|
||||
from array import array
|
||||
from dis import HAVE_ARGUMENT, EXTENDED_ARG
|
||||
|
||||
bytes = array('b',code.co_code)
|
||||
eof = len(code.co_code)
|
||||
|
||||
ptr = 0
|
||||
extended_arg = 0
|
||||
|
||||
while ptr<eof:
|
||||
|
||||
op = bytes[ptr]
|
||||
|
||||
if op>=HAVE_ARGUMENT:
|
||||
|
||||
arg = bytes[ptr+1] + bytes[ptr+2]*256 + extended_arg
|
||||
ptr += 3
|
||||
|
||||
if op==EXTENDED_ARG:
|
||||
extended_arg = arg * 65536L
|
||||
continue
|
||||
|
||||
else:
|
||||
arg = None
|
||||
ptr += 1
|
||||
|
||||
yield op,arg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def find_module(module, paths=None):
|
||||
"""Just like 'imp.find_module()', but with package support"""
|
||||
|
||||
parts = module.split('.')
|
||||
|
||||
while parts:
|
||||
part = parts.pop(0)
|
||||
f, path, (suffix,mode,kind) = info = imp.find_module(part, paths)
|
||||
|
||||
if kind==PKG_DIRECTORY:
|
||||
parts = parts or ['__init__']
|
||||
paths = [path]
|
||||
|
||||
elif parts:
|
||||
raise ImportError("Can't find %r in %s" % (parts,module))
|
||||
|
||||
return info
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_module_constant(module, symbol, default=-1, paths=None):
|
||||
|
||||
"""Find 'module' by searching 'paths', and extract 'symbol'
|
||||
|
||||
Return 'None' if 'module' does not exist on 'paths', or it does not define
|
||||
'symbol'. If the module defines 'symbol' as a constant, return the
|
||||
constant. Otherwise, return 'default'."""
|
||||
|
||||
try:
|
||||
f, path, (suffix,mode,kind) = find_module(module,paths)
|
||||
except ImportError:
|
||||
# Module doesn't exist
|
||||
return None
|
||||
|
||||
try:
|
||||
if kind==PY_COMPILED:
|
||||
f.read(8) # skip magic & date
|
||||
code = marshal.load(f)
|
||||
elif kind==PY_FROZEN:
|
||||
code = imp.get_frozen_object(module)
|
||||
elif kind==PY_SOURCE:
|
||||
code = compile(f.read(), path, 'exec')
|
||||
else:
|
||||
# Not something we can parse; we'll have to import it. :(
|
||||
if module not in sys.modules:
|
||||
imp.load_module(module,f,path,(suffix,mode,kind))
|
||||
return getattr(sys.modules[module],symbol,None)
|
||||
|
||||
finally:
|
||||
if f:
|
||||
f.close()
|
||||
|
||||
return extract_constant(code,symbol,default)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def extract_constant(code,symbol,default=-1):
|
||||
"""Extract the constant value of 'symbol' from 'code'
|
||||
|
||||
If the name 'symbol' is bound to a constant value by the Python code
|
||||
object 'code', return that value. If 'symbol' is bound to an expression,
|
||||
return 'default'. Otherwise, return 'None'.
|
||||
|
||||
Return value is based on the first assignment to 'symbol'. 'symbol' must
|
||||
be a global, or at least a non-"fast" local in the code block. That is,
|
||||
only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol'
|
||||
must be present in 'code.co_names'.
|
||||
"""
|
||||
|
||||
if symbol not in code.co_names:
|
||||
# name's not there, can't possibly be an assigment
|
||||
return None
|
||||
|
||||
name_idx = list(code.co_names).index(symbol)
|
||||
|
||||
STORE_NAME = 90
|
||||
STORE_GLOBAL = 97
|
||||
LOAD_CONST = 100
|
||||
|
||||
const = default
|
||||
|
||||
for op, arg in _iter_code(code):
|
||||
|
||||
if op==LOAD_CONST:
|
||||
const = code.co_consts[arg]
|
||||
elif arg==name_idx and (op==STORE_NAME or op==STORE_GLOBAL):
|
||||
return const
|
||||
else:
|
||||
const = default
|
||||
|
||||
if sys.platform.startswith('java') or sys.platform == 'cli':
|
||||
# XXX it'd be better to test assertions about bytecode instead...
|
||||
del extract_constant, get_module_constant
|
||||
__all__.remove('extract_constant')
|
||||
__all__.remove('get_module_constant')
|
||||
|
||||
|
||||
-856
@@ -1,856 +0,0 @@
|
||||
__all__ = ['Distribution']
|
||||
|
||||
import re
|
||||
from distutils.core import Distribution as _Distribution
|
||||
from setuptools.depends import Require
|
||||
from setuptools.command.install import install
|
||||
from setuptools.command.sdist import sdist
|
||||
from setuptools.command.install_lib import install_lib
|
||||
from distutils.errors import DistutilsOptionError, DistutilsPlatformError
|
||||
from distutils.errors import DistutilsSetupError
|
||||
import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd
|
||||
import os, distutils.log
|
||||
|
||||
def _get_unpatched(cls):
|
||||
"""Protect against re-patching the distutils if reloaded
|
||||
|
||||
Also ensures that no other distutils extension monkeypatched the distutils
|
||||
first.
|
||||
"""
|
||||
while cls.__module__.startswith('setuptools'):
|
||||
cls, = cls.__bases__
|
||||
if not cls.__module__.startswith('distutils'):
|
||||
raise AssertionError(
|
||||
"distutils has already been patched by %r" % cls
|
||||
)
|
||||
return cls
|
||||
|
||||
_Distribution = _get_unpatched(_Distribution)
|
||||
|
||||
sequence = tuple, list
|
||||
|
||||
def check_importable(dist, attr, value):
|
||||
try:
|
||||
ep = pkg_resources.EntryPoint.parse('x='+value)
|
||||
assert not ep.extras
|
||||
except (TypeError,ValueError,AttributeError,AssertionError):
|
||||
raise DistutilsSetupError(
|
||||
"%r must be importable 'module:attrs' string (got %r)"
|
||||
% (attr,value)
|
||||
)
|
||||
|
||||
|
||||
def assert_string_list(dist, attr, value):
|
||||
"""Verify that value is a string list or None"""
|
||||
try:
|
||||
assert ''.join(value)!=value
|
||||
except (TypeError,ValueError,AttributeError,AssertionError):
|
||||
raise DistutilsSetupError(
|
||||
"%r must be a list of strings (got %r)" % (attr,value)
|
||||
)
|
||||
|
||||
def check_nsp(dist, attr, value):
|
||||
"""Verify that namespace packages are valid"""
|
||||
assert_string_list(dist,attr,value)
|
||||
for nsp in value:
|
||||
if not dist.has_contents_for(nsp):
|
||||
raise DistutilsSetupError(
|
||||
"Distribution contains no modules or packages for " +
|
||||
"namespace package %r" % nsp
|
||||
)
|
||||
if '.' in nsp:
|
||||
parent = '.'.join(nsp.split('.')[:-1])
|
||||
if parent not in value:
|
||||
distutils.log.warn(
|
||||
"%r is declared as a package namespace, but %r is not:"
|
||||
" please correct this in setup.py", nsp, parent
|
||||
)
|
||||
|
||||
def check_extras(dist, attr, value):
|
||||
"""Verify that extras_require mapping is valid"""
|
||||
try:
|
||||
for k,v in value.items():
|
||||
list(pkg_resources.parse_requirements(v))
|
||||
except (TypeError,ValueError,AttributeError):
|
||||
raise DistutilsSetupError(
|
||||
"'extras_require' must be a dictionary whose values are "
|
||||
"strings or lists of strings containing valid project/version "
|
||||
"requirement specifiers."
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
def assert_bool(dist, attr, value):
|
||||
"""Verify that value is True, False, 0, or 1"""
|
||||
if bool(value) != value:
|
||||
raise DistutilsSetupError(
|
||||
"%r must be a boolean value (got %r)" % (attr,value)
|
||||
)
|
||||
def check_requirements(dist, attr, value):
|
||||
"""Verify that install_requires is a valid requirements list"""
|
||||
try:
|
||||
list(pkg_resources.parse_requirements(value))
|
||||
except (TypeError,ValueError):
|
||||
raise DistutilsSetupError(
|
||||
"%r must be a string or list of strings "
|
||||
"containing valid project/version requirement specifiers" % (attr,)
|
||||
)
|
||||
def check_entry_points(dist, attr, value):
|
||||
"""Verify that entry_points map is parseable"""
|
||||
try:
|
||||
pkg_resources.EntryPoint.parse_map(value)
|
||||
except ValueError, e:
|
||||
raise DistutilsSetupError(e)
|
||||
|
||||
def check_test_suite(dist, attr, value):
|
||||
if not isinstance(value,basestring):
|
||||
raise DistutilsSetupError("test_suite must be a string")
|
||||
|
||||
def check_package_data(dist, attr, value):
|
||||
"""Verify that value is a dictionary of package names to glob lists"""
|
||||
if isinstance(value,dict):
|
||||
for k,v in value.items():
|
||||
if not isinstance(k,str): break
|
||||
try: iter(v)
|
||||
except TypeError:
|
||||
break
|
||||
else:
|
||||
return
|
||||
raise DistutilsSetupError(
|
||||
attr+" must be a dictionary mapping package names to lists of "
|
||||
"wildcard patterns"
|
||||
)
|
||||
|
||||
class Distribution(_Distribution):
|
||||
"""Distribution with support for features, tests, and package data
|
||||
|
||||
This is an enhanced version of 'distutils.dist.Distribution' that
|
||||
effectively adds the following new optional keyword arguments to 'setup()':
|
||||
|
||||
'install_requires' -- a string or sequence of strings specifying project
|
||||
versions that the distribution requires when installed, in the format
|
||||
used by 'pkg_resources.require()'. They will be installed
|
||||
automatically when the package is installed. If you wish to use
|
||||
packages that are not available in PyPI, or want to give your users an
|
||||
alternate download location, you can add a 'find_links' option to the
|
||||
'[easy_install]' section of your project's 'setup.cfg' file, and then
|
||||
setuptools will scan the listed web pages for links that satisfy the
|
||||
requirements.
|
||||
|
||||
'extras_require' -- a dictionary mapping names of optional "extras" to the
|
||||
additional requirement(s) that using those extras incurs. For example,
|
||||
this::
|
||||
|
||||
extras_require = dict(reST = ["docutils>=0.3", "reSTedit"])
|
||||
|
||||
indicates that the distribution can optionally provide an extra
|
||||
capability called "reST", but it can only be used if docutils and
|
||||
reSTedit are installed. If the user installs your package using
|
||||
EasyInstall and requests one of your extras, the corresponding
|
||||
additional requirements will be installed if needed.
|
||||
|
||||
'features' -- a dictionary mapping option names to 'setuptools.Feature'
|
||||
objects. Features are a portion of the distribution that can be
|
||||
included or excluded based on user options, inter-feature dependencies,
|
||||
and availability on the current system. Excluded features are omitted
|
||||
from all setup commands, including source and binary distributions, so
|
||||
you can create multiple distributions from the same source tree.
|
||||
Feature names should be valid Python identifiers, except that they may
|
||||
contain the '-' (minus) sign. Features can be included or excluded
|
||||
via the command line options '--with-X' and '--without-X', where 'X' is
|
||||
the name of the feature. Whether a feature is included by default, and
|
||||
whether you are allowed to control this from the command line, is
|
||||
determined by the Feature object. See the 'Feature' class for more
|
||||
information.
|
||||
|
||||
'test_suite' -- the name of a test suite to run for the 'test' command.
|
||||
If the user runs 'python setup.py test', the package will be installed,
|
||||
and the named test suite will be run. The format is the same as
|
||||
would be used on a 'unittest.py' command line. That is, it is the
|
||||
dotted name of an object to import and call to generate a test suite.
|
||||
|
||||
'package_data' -- a dictionary mapping package names to lists of filenames
|
||||
or globs to use to find data files contained in the named packages.
|
||||
If the dictionary has filenames or globs listed under '""' (the empty
|
||||
string), those names will be searched for in every package, in addition
|
||||
to any names for the specific package. Data files found using these
|
||||
names/globs will be installed along with the package, in the same
|
||||
location as the package. Note that globs are allowed to reference
|
||||
the contents of non-package subdirectories, as long as you use '/' as
|
||||
a path separator. (Globs are automatically converted to
|
||||
platform-specific paths at runtime.)
|
||||
|
||||
In addition to these new keywords, this class also has several new methods
|
||||
for manipulating the distribution's contents. For example, the 'include()'
|
||||
and 'exclude()' methods can be thought of as in-place add and subtract
|
||||
commands that add or remove packages, modules, extensions, and so on from
|
||||
the distribution. They are used by the feature subsystem to configure the
|
||||
distribution for the included and excluded features.
|
||||
"""
|
||||
|
||||
_patched_dist = None
|
||||
|
||||
def patch_missing_pkg_info(self, attrs):
|
||||
# Fake up a replacement for the data that would normally come from
|
||||
# PKG-INFO, but which might not yet be built if this is a fresh
|
||||
# checkout.
|
||||
#
|
||||
if not attrs or 'name' not in attrs or 'version' not in attrs:
|
||||
return
|
||||
key = pkg_resources.safe_name(str(attrs['name'])).lower()
|
||||
dist = pkg_resources.working_set.by_key.get(key)
|
||||
if dist is not None and not dist.has_metadata('PKG-INFO'):
|
||||
dist._version = pkg_resources.safe_version(str(attrs['version']))
|
||||
self._patched_dist = dist
|
||||
|
||||
def __init__ (self, attrs=None):
|
||||
have_package_data = hasattr(self, "package_data")
|
||||
if not have_package_data:
|
||||
self.package_data = {}
|
||||
self.require_features = []
|
||||
self.features = {}
|
||||
self.dist_files = []
|
||||
self.src_root = attrs and attrs.pop("src_root", None)
|
||||
self.patch_missing_pkg_info(attrs)
|
||||
# Make sure we have any eggs needed to interpret 'attrs'
|
||||
if attrs is not None:
|
||||
self.dependency_links = attrs.pop('dependency_links', [])
|
||||
assert_string_list(self,'dependency_links',self.dependency_links)
|
||||
if attrs and 'setup_requires' in attrs:
|
||||
self.fetch_build_eggs(attrs.pop('setup_requires'))
|
||||
for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
|
||||
if not hasattr(self,ep.name):
|
||||
setattr(self,ep.name,None)
|
||||
_Distribution.__init__(self,attrs)
|
||||
if isinstance(self.metadata.version, (int,long,float)):
|
||||
# Some people apparently take "version number" too literally :)
|
||||
self.metadata.version = str(self.metadata.version)
|
||||
|
||||
def parse_command_line(self):
|
||||
"""Process features after parsing command line options"""
|
||||
result = _Distribution.parse_command_line(self)
|
||||
if self.features:
|
||||
self._finalize_features()
|
||||
return result
|
||||
|
||||
def _feature_attrname(self,name):
|
||||
"""Convert feature name to corresponding option attribute name"""
|
||||
return 'with_'+name.replace('-','_')
|
||||
|
||||
def fetch_build_eggs(self, requires):
|
||||
"""Resolve pre-setup requirements"""
|
||||
from pkg_resources import working_set, parse_requirements
|
||||
for dist in working_set.resolve(
|
||||
parse_requirements(requires), installer=self.fetch_build_egg,
|
||||
replace_conflicting=True
|
||||
):
|
||||
working_set.add(dist, replace=True)
|
||||
|
||||
def finalize_options(self):
|
||||
_Distribution.finalize_options(self)
|
||||
if self.features:
|
||||
self._set_global_opts_from_features()
|
||||
|
||||
for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
|
||||
value = getattr(self,ep.name,None)
|
||||
if value is not None:
|
||||
ep.require(installer=self.fetch_build_egg)
|
||||
ep.load()(self, ep.name, value)
|
||||
if getattr(self, 'convert_2to3_doctests', None):
|
||||
# XXX may convert to set here when we can rely on set being builtin
|
||||
self.convert_2to3_doctests = [os.path.abspath(p) for p in self.convert_2to3_doctests]
|
||||
else:
|
||||
self.convert_2to3_doctests = []
|
||||
|
||||
def fetch_build_egg(self, req):
|
||||
"""Fetch an egg needed for building"""
|
||||
|
||||
try:
|
||||
cmd = self._egg_fetcher
|
||||
cmd.package_index.to_scan = []
|
||||
except AttributeError:
|
||||
from setuptools.command.easy_install import easy_install
|
||||
dist = self.__class__({'script_args':['easy_install']})
|
||||
dist.parse_config_files()
|
||||
opts = dist.get_option_dict('easy_install')
|
||||
keep = (
|
||||
'find_links', 'site_dirs', 'index_url', 'optimize',
|
||||
'site_dirs', 'allow_hosts'
|
||||
)
|
||||
for key in opts.keys():
|
||||
if key not in keep:
|
||||
del opts[key] # don't use any other settings
|
||||
if self.dependency_links:
|
||||
links = self.dependency_links[:]
|
||||
if 'find_links' in opts:
|
||||
links = opts['find_links'][1].split() + links
|
||||
opts['find_links'] = ('setup', links)
|
||||
cmd = easy_install(
|
||||
dist, args=["x"], install_dir=os.curdir, exclude_scripts=True,
|
||||
always_copy=False, build_directory=None, editable=False,
|
||||
upgrade=False, multi_version=True, no_report=True, user=False
|
||||
)
|
||||
cmd.ensure_finalized()
|
||||
self._egg_fetcher = cmd
|
||||
return cmd.easy_install(req)
|
||||
|
||||
def _set_global_opts_from_features(self):
|
||||
"""Add --with-X/--without-X options based on optional features"""
|
||||
|
||||
go = []
|
||||
no = self.negative_opt.copy()
|
||||
|
||||
for name,feature in self.features.items():
|
||||
self._set_feature(name,None)
|
||||
feature.validate(self)
|
||||
|
||||
if feature.optional:
|
||||
descr = feature.description
|
||||
incdef = ' (default)'
|
||||
excdef=''
|
||||
if not feature.include_by_default():
|
||||
excdef, incdef = incdef, excdef
|
||||
|
||||
go.append(('with-'+name, None, 'include '+descr+incdef))
|
||||
go.append(('without-'+name, None, 'exclude '+descr+excdef))
|
||||
no['without-'+name] = 'with-'+name
|
||||
|
||||
self.global_options = self.feature_options = go + self.global_options
|
||||
self.negative_opt = self.feature_negopt = no
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _finalize_features(self):
|
||||
"""Add/remove features and resolve dependencies between them"""
|
||||
|
||||
# First, flag all the enabled items (and thus their dependencies)
|
||||
for name,feature in self.features.items():
|
||||
enabled = self.feature_is_included(name)
|
||||
if enabled or (enabled is None and feature.include_by_default()):
|
||||
feature.include_in(self)
|
||||
self._set_feature(name,1)
|
||||
|
||||
# Then disable the rest, so that off-by-default features don't
|
||||
# get flagged as errors when they're required by an enabled feature
|
||||
for name,feature in self.features.items():
|
||||
if not self.feature_is_included(name):
|
||||
feature.exclude_from(self)
|
||||
self._set_feature(name,0)
|
||||
|
||||
|
||||
def get_command_class(self, command):
|
||||
"""Pluggable version of get_command_class()"""
|
||||
if command in self.cmdclass:
|
||||
return self.cmdclass[command]
|
||||
|
||||
for ep in pkg_resources.iter_entry_points('distutils.commands',command):
|
||||
ep.require(installer=self.fetch_build_egg)
|
||||
self.cmdclass[command] = cmdclass = ep.load()
|
||||
return cmdclass
|
||||
else:
|
||||
return _Distribution.get_command_class(self, command)
|
||||
|
||||
def print_commands(self):
|
||||
for ep in pkg_resources.iter_entry_points('distutils.commands'):
|
||||
if ep.name not in self.cmdclass:
|
||||
cmdclass = ep.load(False) # don't require extras, we're not running
|
||||
self.cmdclass[ep.name] = cmdclass
|
||||
return _Distribution.print_commands(self)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _set_feature(self,name,status):
|
||||
"""Set feature's inclusion status"""
|
||||
setattr(self,self._feature_attrname(name),status)
|
||||
|
||||
def feature_is_included(self,name):
|
||||
"""Return 1 if feature is included, 0 if excluded, 'None' if unknown"""
|
||||
return getattr(self,self._feature_attrname(name))
|
||||
|
||||
def include_feature(self,name):
|
||||
"""Request inclusion of feature named 'name'"""
|
||||
|
||||
if self.feature_is_included(name)==0:
|
||||
descr = self.features[name].description
|
||||
raise DistutilsOptionError(
|
||||
descr + " is required, but was excluded or is not available"
|
||||
)
|
||||
self.features[name].include_in(self)
|
||||
self._set_feature(name,1)
|
||||
|
||||
def include(self,**attrs):
|
||||
"""Add items to distribution that are named in keyword arguments
|
||||
|
||||
For example, 'dist.exclude(py_modules=["x"])' would add 'x' to
|
||||
the distribution's 'py_modules' attribute, if it was not already
|
||||
there.
|
||||
|
||||
Currently, this method only supports inclusion for attributes that are
|
||||
lists or tuples. If you need to add support for adding to other
|
||||
attributes in this or a subclass, you can add an '_include_X' method,
|
||||
where 'X' is the name of the attribute. The method will be called with
|
||||
the value passed to 'include()'. So, 'dist.include(foo={"bar":"baz"})'
|
||||
will try to call 'dist._include_foo({"bar":"baz"})', which can then
|
||||
handle whatever special inclusion logic is needed.
|
||||
"""
|
||||
for k,v in attrs.items():
|
||||
include = getattr(self, '_include_'+k, None)
|
||||
if include:
|
||||
include(v)
|
||||
else:
|
||||
self._include_misc(k,v)
|
||||
|
||||
def exclude_package(self,package):
|
||||
"""Remove packages, modules, and extensions in named package"""
|
||||
|
||||
pfx = package+'.'
|
||||
if self.packages:
|
||||
self.packages = [
|
||||
p for p in self.packages
|
||||
if p != package and not p.startswith(pfx)
|
||||
]
|
||||
|
||||
if self.py_modules:
|
||||
self.py_modules = [
|
||||
p for p in self.py_modules
|
||||
if p != package and not p.startswith(pfx)
|
||||
]
|
||||
|
||||
if self.ext_modules:
|
||||
self.ext_modules = [
|
||||
p for p in self.ext_modules
|
||||
if p.name != package and not p.name.startswith(pfx)
|
||||
]
|
||||
|
||||
|
||||
def has_contents_for(self,package):
|
||||
"""Return true if 'exclude_package(package)' would do something"""
|
||||
|
||||
pfx = package+'.'
|
||||
|
||||
for p in self.iter_distribution_names():
|
||||
if p==package or p.startswith(pfx):
|
||||
return True
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _exclude_misc(self,name,value):
|
||||
"""Handle 'exclude()' for list/tuple attrs without a special handler"""
|
||||
if not isinstance(value,sequence):
|
||||
raise DistutilsSetupError(
|
||||
"%s: setting must be a list or tuple (%r)" % (name, value)
|
||||
)
|
||||
try:
|
||||
old = getattr(self,name)
|
||||
except AttributeError:
|
||||
raise DistutilsSetupError(
|
||||
"%s: No such distribution setting" % name
|
||||
)
|
||||
if old is not None and not isinstance(old,sequence):
|
||||
raise DistutilsSetupError(
|
||||
name+": this setting cannot be changed via include/exclude"
|
||||
)
|
||||
elif old:
|
||||
setattr(self,name,[item for item in old if item not in value])
|
||||
|
||||
def _include_misc(self,name,value):
|
||||
"""Handle 'include()' for list/tuple attrs without a special handler"""
|
||||
|
||||
if not isinstance(value,sequence):
|
||||
raise DistutilsSetupError(
|
||||
"%s: setting must be a list (%r)" % (name, value)
|
||||
)
|
||||
try:
|
||||
old = getattr(self,name)
|
||||
except AttributeError:
|
||||
raise DistutilsSetupError(
|
||||
"%s: No such distribution setting" % name
|
||||
)
|
||||
if old is None:
|
||||
setattr(self,name,value)
|
||||
elif not isinstance(old,sequence):
|
||||
raise DistutilsSetupError(
|
||||
name+": this setting cannot be changed via include/exclude"
|
||||
)
|
||||
else:
|
||||
setattr(self,name,old+[item for item in value if item not in old])
|
||||
|
||||
def exclude(self,**attrs):
|
||||
"""Remove items from distribution that are named in keyword arguments
|
||||
|
||||
For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from
|
||||
the distribution's 'py_modules' attribute. Excluding packages uses
|
||||
the 'exclude_package()' method, so all of the package's contained
|
||||
packages, modules, and extensions are also excluded.
|
||||
|
||||
Currently, this method only supports exclusion from attributes that are
|
||||
lists or tuples. If you need to add support for excluding from other
|
||||
attributes in this or a subclass, you can add an '_exclude_X' method,
|
||||
where 'X' is the name of the attribute. The method will be called with
|
||||
the value passed to 'exclude()'. So, 'dist.exclude(foo={"bar":"baz"})'
|
||||
will try to call 'dist._exclude_foo({"bar":"baz"})', which can then
|
||||
handle whatever special exclusion logic is needed.
|
||||
"""
|
||||
for k,v in attrs.items():
|
||||
exclude = getattr(self, '_exclude_'+k, None)
|
||||
if exclude:
|
||||
exclude(v)
|
||||
else:
|
||||
self._exclude_misc(k,v)
|
||||
|
||||
def _exclude_packages(self,packages):
|
||||
if not isinstance(packages,sequence):
|
||||
raise DistutilsSetupError(
|
||||
"packages: setting must be a list or tuple (%r)" % (packages,)
|
||||
)
|
||||
map(self.exclude_package, packages)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def _parse_command_opts(self, parser, args):
|
||||
# Remove --with-X/--without-X options when processing command args
|
||||
self.global_options = self.__class__.global_options
|
||||
self.negative_opt = self.__class__.negative_opt
|
||||
|
||||
# First, expand any aliases
|
||||
command = args[0]
|
||||
aliases = self.get_option_dict('aliases')
|
||||
while command in aliases:
|
||||
src,alias = aliases[command]
|
||||
del aliases[command] # ensure each alias can expand only once!
|
||||
import shlex
|
||||
args[:1] = shlex.split(alias,True)
|
||||
command = args[0]
|
||||
|
||||
nargs = _Distribution._parse_command_opts(self, parser, args)
|
||||
|
||||
# Handle commands that want to consume all remaining arguments
|
||||
cmd_class = self.get_command_class(command)
|
||||
if getattr(cmd_class,'command_consumes_arguments',None):
|
||||
self.get_option_dict(command)['args'] = ("command line", nargs)
|
||||
if nargs is not None:
|
||||
return []
|
||||
|
||||
return nargs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_cmdline_options(self):
|
||||
"""Return a '{cmd: {opt:val}}' map of all command-line options
|
||||
|
||||
Option names are all long, but do not include the leading '--', and
|
||||
contain dashes rather than underscores. If the option doesn't take
|
||||
an argument (e.g. '--quiet'), the 'val' is 'None'.
|
||||
|
||||
Note that options provided by config files are intentionally excluded.
|
||||
"""
|
||||
|
||||
d = {}
|
||||
|
||||
for cmd,opts in self.command_options.items():
|
||||
|
||||
for opt,(src,val) in opts.items():
|
||||
|
||||
if src != "command line":
|
||||
continue
|
||||
|
||||
opt = opt.replace('_','-')
|
||||
|
||||
if val==0:
|
||||
cmdobj = self.get_command_obj(cmd)
|
||||
neg_opt = self.negative_opt.copy()
|
||||
neg_opt.update(getattr(cmdobj,'negative_opt',{}))
|
||||
for neg,pos in neg_opt.items():
|
||||
if pos==opt:
|
||||
opt=neg
|
||||
val=None
|
||||
break
|
||||
else:
|
||||
raise AssertionError("Shouldn't be able to get here")
|
||||
|
||||
elif val==1:
|
||||
val = None
|
||||
|
||||
d.setdefault(cmd,{})[opt] = val
|
||||
|
||||
return d
|
||||
|
||||
|
||||
def iter_distribution_names(self):
|
||||
"""Yield all packages, modules, and extension names in distribution"""
|
||||
|
||||
for pkg in self.packages or ():
|
||||
yield pkg
|
||||
|
||||
for module in self.py_modules or ():
|
||||
yield module
|
||||
|
||||
for ext in self.ext_modules or ():
|
||||
if isinstance(ext,tuple):
|
||||
name, buildinfo = ext
|
||||
else:
|
||||
name = ext.name
|
||||
if name.endswith('module'):
|
||||
name = name[:-6]
|
||||
yield name
|
||||
|
||||
|
||||
def handle_display_options(self, option_order):
|
||||
"""If there were any non-global "display-only" options
|
||||
(--help-commands or the metadata display options) on the command
|
||||
line, display the requested info and return true; else return
|
||||
false.
|
||||
"""
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3,) or self.help_commands:
|
||||
return _Distribution.handle_display_options(self, option_order)
|
||||
|
||||
# Stdout may be StringIO (e.g. in tests)
|
||||
import io
|
||||
if not isinstance(sys.stdout, io.TextIOWrapper):
|
||||
return _Distribution.handle_display_options(self, option_order)
|
||||
|
||||
# Don't wrap stdout if utf-8 is already the encoding. Provides
|
||||
# workaround for #334.
|
||||
if sys.stdout.encoding.lower() in ('utf-8', 'utf8'):
|
||||
return _Distribution.handle_display_options(self, option_order)
|
||||
|
||||
# Print metadata in UTF-8 no matter the platform
|
||||
encoding = sys.stdout.encoding
|
||||
errors = sys.stdout.errors
|
||||
newline = sys.platform != 'win32' and '\n' or None
|
||||
line_buffering = sys.stdout.line_buffering
|
||||
|
||||
sys.stdout = io.TextIOWrapper(
|
||||
sys.stdout.detach(), 'utf-8', errors, newline, line_buffering)
|
||||
try:
|
||||
return _Distribution.handle_display_options(self, option_order)
|
||||
finally:
|
||||
sys.stdout = io.TextIOWrapper(
|
||||
sys.stdout.detach(), encoding, errors, newline, line_buffering)
|
||||
|
||||
|
||||
# Install it throughout the distutils
|
||||
for module in distutils.dist, distutils.core, distutils.cmd:
|
||||
module.Distribution = Distribution
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Feature:
|
||||
"""A subset of the distribution that can be excluded if unneeded/wanted
|
||||
|
||||
Features are created using these keyword arguments:
|
||||
|
||||
'description' -- a short, human readable description of the feature, to
|
||||
be used in error messages, and option help messages.
|
||||
|
||||
'standard' -- if true, the feature is included by default if it is
|
||||
available on the current system. Otherwise, the feature is only
|
||||
included if requested via a command line '--with-X' option, or if
|
||||
another included feature requires it. The default setting is 'False'.
|
||||
|
||||
'available' -- if true, the feature is available for installation on the
|
||||
current system. The default setting is 'True'.
|
||||
|
||||
'optional' -- if true, the feature's inclusion can be controlled from the
|
||||
command line, using the '--with-X' or '--without-X' options. If
|
||||
false, the feature's inclusion status is determined automatically,
|
||||
based on 'availabile', 'standard', and whether any other feature
|
||||
requires it. The default setting is 'True'.
|
||||
|
||||
'require_features' -- a string or sequence of strings naming features
|
||||
that should also be included if this feature is included. Defaults to
|
||||
empty list. May also contain 'Require' objects that should be
|
||||
added/removed from the distribution.
|
||||
|
||||
'remove' -- a string or list of strings naming packages to be removed
|
||||
from the distribution if this feature is *not* included. If the
|
||||
feature *is* included, this argument is ignored. This argument exists
|
||||
to support removing features that "crosscut" a distribution, such as
|
||||
defining a 'tests' feature that removes all the 'tests' subpackages
|
||||
provided by other features. The default for this argument is an empty
|
||||
list. (Note: the named package(s) or modules must exist in the base
|
||||
distribution when the 'setup()' function is initially called.)
|
||||
|
||||
other keywords -- any other keyword arguments are saved, and passed to
|
||||
the distribution's 'include()' and 'exclude()' methods when the
|
||||
feature is included or excluded, respectively. So, for example, you
|
||||
could pass 'packages=["a","b"]' to cause packages 'a' and 'b' to be
|
||||
added or removed from the distribution as appropriate.
|
||||
|
||||
A feature must include at least one 'requires', 'remove', or other
|
||||
keyword argument. Otherwise, it can't affect the distribution in any way.
|
||||
Note also that you can subclass 'Feature' to create your own specialized
|
||||
feature types that modify the distribution in other ways when included or
|
||||
excluded. See the docstrings for the various methods here for more detail.
|
||||
Aside from the methods, the only feature attributes that distributions look
|
||||
at are 'description' and 'optional'.
|
||||
"""
|
||||
def __init__(self, description, standard=False, available=True,
|
||||
optional=True, require_features=(), remove=(), **extras
|
||||
):
|
||||
|
||||
self.description = description
|
||||
self.standard = standard
|
||||
self.available = available
|
||||
self.optional = optional
|
||||
if isinstance(require_features,(str,Require)):
|
||||
require_features = require_features,
|
||||
|
||||
self.require_features = [
|
||||
r for r in require_features if isinstance(r,str)
|
||||
]
|
||||
er = [r for r in require_features if not isinstance(r,str)]
|
||||
if er: extras['require_features'] = er
|
||||
|
||||
if isinstance(remove,str):
|
||||
remove = remove,
|
||||
self.remove = remove
|
||||
self.extras = extras
|
||||
|
||||
if not remove and not require_features and not extras:
|
||||
raise DistutilsSetupError(
|
||||
"Feature %s: must define 'require_features', 'remove', or at least one"
|
||||
" of 'packages', 'py_modules', etc."
|
||||
)
|
||||
|
||||
def include_by_default(self):
|
||||
"""Should this feature be included by default?"""
|
||||
return self.available and self.standard
|
||||
|
||||
def include_in(self,dist):
|
||||
|
||||
"""Ensure feature and its requirements are included in distribution
|
||||
|
||||
You may override this in a subclass to perform additional operations on
|
||||
the distribution. Note that this method may be called more than once
|
||||
per feature, and so should be idempotent.
|
||||
|
||||
"""
|
||||
|
||||
if not self.available:
|
||||
raise DistutilsPlatformError(
|
||||
self.description+" is required,"
|
||||
"but is not available on this platform"
|
||||
)
|
||||
|
||||
dist.include(**self.extras)
|
||||
|
||||
for f in self.require_features:
|
||||
dist.include_feature(f)
|
||||
|
||||
|
||||
|
||||
def exclude_from(self,dist):
|
||||
|
||||
"""Ensure feature is excluded from distribution
|
||||
|
||||
You may override this in a subclass to perform additional operations on
|
||||
the distribution. This method will be called at most once per
|
||||
feature, and only after all included features have been asked to
|
||||
include themselves.
|
||||
"""
|
||||
|
||||
dist.exclude(**self.extras)
|
||||
|
||||
if self.remove:
|
||||
for item in self.remove:
|
||||
dist.exclude_package(item)
|
||||
|
||||
|
||||
|
||||
def validate(self,dist):
|
||||
|
||||
"""Verify that feature makes sense in context of distribution
|
||||
|
||||
This method is called by the distribution just before it parses its
|
||||
command line. It checks to ensure that the 'remove' attribute, if any,
|
||||
contains only valid package/module names that are present in the base
|
||||
distribution when 'setup()' is called. You may override it in a
|
||||
subclass to perform any other required validation of the feature
|
||||
against a target distribution.
|
||||
"""
|
||||
|
||||
for item in self.remove:
|
||||
if not dist.has_contents_for(item):
|
||||
raise DistutilsSetupError(
|
||||
"%s wants to be able to remove %s, but the distribution"
|
||||
" doesn't contain any packages or modules under %s"
|
||||
% (self.description, item, item)
|
||||
)
|
||||
|
||||
|
||||
|
||||
def check_packages(dist, attr, value):
|
||||
for pkgname in value:
|
||||
if not re.match(r'\w+(\.\w+)*', pkgname):
|
||||
distutils.log.warn(
|
||||
"WARNING: %r not a valid package name; please use only"
|
||||
".-separated package names in setup.py", pkgname
|
||||
)
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import sys
|
||||
import distutils.core
|
||||
import distutils.extension
|
||||
|
||||
from setuptools.dist import _get_unpatched
|
||||
|
||||
_Extension = _get_unpatched(distutils.core.Extension)
|
||||
|
||||
def have_pyrex():
|
||||
"""
|
||||
Return True if Cython or Pyrex can be imported.
|
||||
"""
|
||||
pyrex_impls = 'Cython.Distutils.build_ext', 'Pyrex.Distutils.build_ext'
|
||||
for pyrex_impl in pyrex_impls:
|
||||
try:
|
||||
# from (pyrex_impl) import build_ext
|
||||
__import__(pyrex_impl, fromlist=['build_ext']).build_ext
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
class Extension(_Extension):
|
||||
"""Extension that uses '.c' files in place of '.pyx' files"""
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
_Extension.__init__(self, *args, **kw)
|
||||
if not have_pyrex():
|
||||
self._convert_pyx_sources_to_c()
|
||||
|
||||
def _convert_pyx_sources_to_c(self):
|
||||
"convert .pyx extensions to .c"
|
||||
def pyx_to_c(source):
|
||||
if source.endswith('.pyx'):
|
||||
source = source[:-4] + '.c'
|
||||
return source
|
||||
self.sources = map(pyx_to_c, self.sources)
|
||||
|
||||
class Library(Extension):
|
||||
"""Just like a regular Extension, but built as a library instead"""
|
||||
|
||||
distutils.core.Extension = Extension
|
||||
distutils.extension.Extension = Extension
|
||||
if 'distutils.command.build_ext' in sys.modules:
|
||||
sys.modules['distutils.command.build_ext'].Extension = Extension
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1,920 +0,0 @@
|
||||
"""PyPI and direct package downloading"""
|
||||
import sys, os.path, re, urlparse, urllib, urllib2, shutil, random, socket, cStringIO
|
||||
import base64
|
||||
import httplib
|
||||
from pkg_resources import *
|
||||
from distutils import log
|
||||
from distutils.errors import DistutilsError
|
||||
try:
|
||||
from hashlib import md5
|
||||
except ImportError:
|
||||
from md5 import md5
|
||||
from fnmatch import translate
|
||||
|
||||
EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$')
|
||||
HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I)
|
||||
# this is here to fix emacs' cruddy broken syntax highlighting
|
||||
PYPI_MD5 = re.compile(
|
||||
'<a href="([^"#]+)">([^<]+)</a>\n\s+\\(<a (?:title="MD5 hash"\n\s+)'
|
||||
'href="[^?]+\?:action=show_md5&digest=([0-9a-f]{32})">md5</a>\\)'
|
||||
)
|
||||
URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):',re.I).match
|
||||
EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split()
|
||||
|
||||
__all__ = [
|
||||
'PackageIndex', 'distros_for_url', 'parse_bdist_wininst',
|
||||
'interpret_distro_name',
|
||||
]
|
||||
|
||||
_SOCKET_TIMEOUT = 15
|
||||
|
||||
def parse_bdist_wininst(name):
|
||||
"""Return (base,pyversion) or (None,None) for possible .exe name"""
|
||||
|
||||
lower = name.lower()
|
||||
base, py_ver, plat = None, None, None
|
||||
|
||||
if lower.endswith('.exe'):
|
||||
if lower.endswith('.win32.exe'):
|
||||
base = name[:-10]
|
||||
plat = 'win32'
|
||||
elif lower.startswith('.win32-py',-16):
|
||||
py_ver = name[-7:-4]
|
||||
base = name[:-16]
|
||||
plat = 'win32'
|
||||
elif lower.endswith('.win-amd64.exe'):
|
||||
base = name[:-14]
|
||||
plat = 'win-amd64'
|
||||
elif lower.startswith('.win-amd64-py',-20):
|
||||
py_ver = name[-7:-4]
|
||||
base = name[:-20]
|
||||
plat = 'win-amd64'
|
||||
return base,py_ver,plat
|
||||
|
||||
|
||||
def egg_info_for_url(url):
|
||||
scheme, server, path, parameters, query, fragment = urlparse.urlparse(url)
|
||||
base = urllib2.unquote(path.split('/')[-1])
|
||||
if '#' in base: base, fragment = base.split('#',1)
|
||||
return base,fragment
|
||||
|
||||
def distros_for_url(url, metadata=None):
|
||||
"""Yield egg or source distribution objects that might be found at a URL"""
|
||||
base, fragment = egg_info_for_url(url)
|
||||
for dist in distros_for_location(url, base, metadata): yield dist
|
||||
if fragment:
|
||||
match = EGG_FRAGMENT.match(fragment)
|
||||
if match:
|
||||
for dist in interpret_distro_name(
|
||||
url, match.group(1), metadata, precedence = CHECKOUT_DIST
|
||||
):
|
||||
yield dist
|
||||
|
||||
def distros_for_location(location, basename, metadata=None):
|
||||
"""Yield egg or source distribution objects based on basename"""
|
||||
if basename.endswith('.egg.zip'):
|
||||
basename = basename[:-4] # strip the .zip
|
||||
if basename.endswith('.egg') and '-' in basename:
|
||||
# only one, unambiguous interpretation
|
||||
return [Distribution.from_location(location, basename, metadata)]
|
||||
|
||||
if basename.endswith('.exe'):
|
||||
win_base, py_ver, platform = parse_bdist_wininst(basename)
|
||||
if win_base is not None:
|
||||
return interpret_distro_name(
|
||||
location, win_base, metadata, py_ver, BINARY_DIST, platform
|
||||
)
|
||||
|
||||
# Try source distro extensions (.zip, .tgz, etc.)
|
||||
#
|
||||
for ext in EXTENSIONS:
|
||||
if basename.endswith(ext):
|
||||
basename = basename[:-len(ext)]
|
||||
return interpret_distro_name(location, basename, metadata)
|
||||
return [] # no extension matched
|
||||
|
||||
def distros_for_filename(filename, metadata=None):
|
||||
"""Yield possible egg or source distribution objects based on a filename"""
|
||||
return distros_for_location(
|
||||
normalize_path(filename), os.path.basename(filename), metadata
|
||||
)
|
||||
|
||||
|
||||
def interpret_distro_name(location, basename, metadata,
|
||||
py_version=None, precedence=SOURCE_DIST, platform=None
|
||||
):
|
||||
"""Generate alternative interpretations of a source distro name
|
||||
|
||||
Note: if `location` is a filesystem filename, you should call
|
||||
``pkg_resources.normalize_path()`` on it before passing it to this
|
||||
routine!
|
||||
"""
|
||||
# Generate alternative interpretations of a source distro name
|
||||
# Because some packages are ambiguous as to name/versions split
|
||||
# e.g. "adns-python-1.1.0", "egenix-mx-commercial", etc.
|
||||
# So, we generate each possible interepretation (e.g. "adns, python-1.1.0"
|
||||
# "adns-python, 1.1.0", and "adns-python-1.1.0, no version"). In practice,
|
||||
# the spurious interpretations should be ignored, because in the event
|
||||
# there's also an "adns" package, the spurious "python-1.1.0" version will
|
||||
# compare lower than any numeric version number, and is therefore unlikely
|
||||
# to match a request for it. It's still a potential problem, though, and
|
||||
# in the long run PyPI and the distutils should go for "safe" names and
|
||||
# versions in distribution archive names (sdist and bdist).
|
||||
|
||||
parts = basename.split('-')
|
||||
if not py_version:
|
||||
for i,p in enumerate(parts[2:]):
|
||||
if len(p)==5 and p.startswith('py2.'):
|
||||
return # It's a bdist_dumb, not an sdist -- bail out
|
||||
|
||||
for p in range(1,len(parts)+1):
|
||||
yield Distribution(
|
||||
location, metadata, '-'.join(parts[:p]), '-'.join(parts[p:]),
|
||||
py_version=py_version, precedence = precedence,
|
||||
platform = platform
|
||||
)
|
||||
|
||||
REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I)
|
||||
# this line is here to fix emacs' cruddy broken syntax highlighting
|
||||
|
||||
def find_external_links(url, page):
|
||||
"""Find rel="homepage" and rel="download" links in `page`, yielding URLs"""
|
||||
|
||||
for match in REL.finditer(page):
|
||||
tag, rel = match.groups()
|
||||
rels = map(str.strip, rel.lower().split(','))
|
||||
if 'homepage' in rels or 'download' in rels:
|
||||
for match in HREF.finditer(tag):
|
||||
yield urlparse.urljoin(url, htmldecode(match.group(1)))
|
||||
|
||||
for tag in ("<th>Home Page", "<th>Download URL"):
|
||||
pos = page.find(tag)
|
||||
if pos!=-1:
|
||||
match = HREF.search(page,pos)
|
||||
if match:
|
||||
yield urlparse.urljoin(url, htmldecode(match.group(1)))
|
||||
|
||||
user_agent = "Python-urllib/%s distribute/%s" % (
|
||||
sys.version[:3], require('distribute')[0].version
|
||||
)
|
||||
|
||||
|
||||
class PackageIndex(Environment):
|
||||
"""A distribution index that scans web pages for download URLs"""
|
||||
|
||||
def __init__(self, index_url="http://pypi.python.org/simple", hosts=('*',),
|
||||
*args, **kw
|
||||
):
|
||||
Environment.__init__(self,*args,**kw)
|
||||
self.index_url = index_url + "/"[:not index_url.endswith('/')]
|
||||
self.scanned_urls = {}
|
||||
self.fetched_urls = {}
|
||||
self.package_pages = {}
|
||||
self.allows = re.compile('|'.join(map(translate,hosts))).match
|
||||
self.to_scan = []
|
||||
|
||||
|
||||
|
||||
def process_url(self, url, retrieve=False):
|
||||
"""Evaluate a URL as a possible download, and maybe retrieve it"""
|
||||
if url in self.scanned_urls and not retrieve:
|
||||
return
|
||||
self.scanned_urls[url] = True
|
||||
if not URL_SCHEME(url):
|
||||
self.process_filename(url)
|
||||
return
|
||||
else:
|
||||
dists = list(distros_for_url(url))
|
||||
if dists:
|
||||
if not self.url_ok(url):
|
||||
return
|
||||
self.debug("Found link: %s", url)
|
||||
|
||||
if dists or not retrieve or url in self.fetched_urls:
|
||||
map(self.add, dists)
|
||||
return # don't need the actual page
|
||||
|
||||
if not self.url_ok(url):
|
||||
self.fetched_urls[url] = True
|
||||
return
|
||||
|
||||
self.info("Reading %s", url)
|
||||
f = self.open_url(url, "Download error on %s: %%s -- Some packages may not be found!" % url)
|
||||
if f is None: return
|
||||
self.fetched_urls[url] = self.fetched_urls[f.url] = True
|
||||
|
||||
if 'html' not in f.headers.get('content-type', '').lower():
|
||||
f.close() # not html, we can't process it
|
||||
return
|
||||
|
||||
base = f.url # handle redirects
|
||||
page = f.read()
|
||||
if not isinstance(page, str): # We are in Python 3 and got bytes. We want str.
|
||||
if isinstance(f, urllib2.HTTPError):
|
||||
# Errors have no charset, assume latin1:
|
||||
charset = 'latin-1'
|
||||
else:
|
||||
charset = f.headers.get_param('charset') or 'latin-1'
|
||||
page = page.decode(charset, "ignore")
|
||||
f.close()
|
||||
for match in HREF.finditer(page):
|
||||
link = urlparse.urljoin(base, htmldecode(match.group(1)))
|
||||
self.process_url(link)
|
||||
if url.startswith(self.index_url) and getattr(f,'code',None)!=404:
|
||||
page = self.process_index(url, page)
|
||||
|
||||
def process_filename(self, fn, nested=False):
|
||||
# process filenames or directories
|
||||
if not os.path.exists(fn):
|
||||
self.warn("Not found: %s", fn)
|
||||
return
|
||||
|
||||
if os.path.isdir(fn) and not nested:
|
||||
path = os.path.realpath(fn)
|
||||
for item in os.listdir(path):
|
||||
self.process_filename(os.path.join(path,item), True)
|
||||
|
||||
dists = distros_for_filename(fn)
|
||||
if dists:
|
||||
self.debug("Found: %s", fn)
|
||||
map(self.add, dists)
|
||||
|
||||
def url_ok(self, url, fatal=False):
|
||||
s = URL_SCHEME(url)
|
||||
if (s and s.group(1).lower()=='file') or self.allows(urlparse.urlparse(url)[1]):
|
||||
return True
|
||||
msg = "\nLink to % s ***BLOCKED*** by --allow-hosts\n"
|
||||
if fatal:
|
||||
raise DistutilsError(msg % url)
|
||||
else:
|
||||
self.warn(msg, url)
|
||||
|
||||
def scan_egg_links(self, search_path):
|
||||
for item in search_path:
|
||||
if os.path.isdir(item):
|
||||
for entry in os.listdir(item):
|
||||
if entry.endswith('.egg-link'):
|
||||
self.scan_egg_link(item, entry)
|
||||
|
||||
def scan_egg_link(self, path, entry):
|
||||
lines = filter(None, map(str.strip, open(os.path.join(path, entry))))
|
||||
if len(lines)==2:
|
||||
for dist in find_distributions(os.path.join(path, lines[0])):
|
||||
dist.location = os.path.join(path, *lines)
|
||||
dist.precedence = SOURCE_DIST
|
||||
self.add(dist)
|
||||
|
||||
def process_index(self,url,page):
|
||||
"""Process the contents of a PyPI page"""
|
||||
def scan(link):
|
||||
# Process a URL to see if it's for a package page
|
||||
if link.startswith(self.index_url):
|
||||
parts = map(
|
||||
urllib2.unquote, link[len(self.index_url):].split('/')
|
||||
)
|
||||
if len(parts)==2 and '#' not in parts[1]:
|
||||
# it's a package page, sanitize and index it
|
||||
pkg = safe_name(parts[0])
|
||||
ver = safe_version(parts[1])
|
||||
self.package_pages.setdefault(pkg.lower(),{})[link] = True
|
||||
return to_filename(pkg), to_filename(ver)
|
||||
return None, None
|
||||
|
||||
# process an index page into the package-page index
|
||||
for match in HREF.finditer(page):
|
||||
try:
|
||||
scan( urlparse.urljoin(url, htmldecode(match.group(1))) )
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
pkg, ver = scan(url) # ensure this page is in the page index
|
||||
if pkg:
|
||||
# process individual package page
|
||||
for new_url in find_external_links(url, page):
|
||||
# Process the found URL
|
||||
base, frag = egg_info_for_url(new_url)
|
||||
if base.endswith('.py') and not frag:
|
||||
if ver:
|
||||
new_url+='#egg=%s-%s' % (pkg,ver)
|
||||
else:
|
||||
self.need_version_info(url)
|
||||
self.scan_url(new_url)
|
||||
|
||||
return PYPI_MD5.sub(
|
||||
lambda m: '<a href="%s#md5=%s">%s</a>' % m.group(1,3,2), page
|
||||
)
|
||||
else:
|
||||
return "" # no sense double-scanning non-package pages
|
||||
|
||||
|
||||
|
||||
def need_version_info(self, url):
|
||||
self.scan_all(
|
||||
"Page at %s links to .py file(s) without version info; an index "
|
||||
"scan is required.", url
|
||||
)
|
||||
|
||||
def scan_all(self, msg=None, *args):
|
||||
if self.index_url not in self.fetched_urls:
|
||||
if msg: self.warn(msg,*args)
|
||||
self.info(
|
||||
"Scanning index of all packages (this may take a while)"
|
||||
)
|
||||
self.scan_url(self.index_url)
|
||||
|
||||
def find_packages(self, requirement):
|
||||
self.scan_url(self.index_url + requirement.unsafe_name+'/')
|
||||
|
||||
if not self.package_pages.get(requirement.key):
|
||||
# Fall back to safe version of the name
|
||||
self.scan_url(self.index_url + requirement.project_name+'/')
|
||||
|
||||
if not self.package_pages.get(requirement.key):
|
||||
# We couldn't find the target package, so search the index page too
|
||||
self.not_found_in_index(requirement)
|
||||
|
||||
for url in list(self.package_pages.get(requirement.key,())):
|
||||
# scan each page that might be related to the desired package
|
||||
self.scan_url(url)
|
||||
|
||||
def obtain(self, requirement, installer=None):
|
||||
self.prescan(); self.find_packages(requirement)
|
||||
for dist in self[requirement.key]:
|
||||
if dist in requirement:
|
||||
return dist
|
||||
self.debug("%s does not match %s", requirement, dist)
|
||||
return super(PackageIndex, self).obtain(requirement,installer)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def check_md5(self, cs, info, filename, tfp):
|
||||
if re.match('md5=[0-9a-f]{32}$', info):
|
||||
self.debug("Validating md5 checksum for %s", filename)
|
||||
if cs.hexdigest()<>info[4:]:
|
||||
tfp.close()
|
||||
os.unlink(filename)
|
||||
raise DistutilsError(
|
||||
"MD5 validation failed for "+os.path.basename(filename)+
|
||||
"; possible download problem?"
|
||||
)
|
||||
|
||||
def add_find_links(self, urls):
|
||||
"""Add `urls` to the list that will be prescanned for searches"""
|
||||
for url in urls:
|
||||
if (
|
||||
self.to_scan is None # if we have already "gone online"
|
||||
or not URL_SCHEME(url) # or it's a local file/directory
|
||||
or url.startswith('file:')
|
||||
or list(distros_for_url(url)) # or a direct package link
|
||||
):
|
||||
# then go ahead and process it now
|
||||
self.scan_url(url)
|
||||
else:
|
||||
# otherwise, defer retrieval till later
|
||||
self.to_scan.append(url)
|
||||
|
||||
def prescan(self):
|
||||
"""Scan urls scheduled for prescanning (e.g. --find-links)"""
|
||||
if self.to_scan:
|
||||
map(self.scan_url, self.to_scan)
|
||||
self.to_scan = None # from now on, go ahead and process immediately
|
||||
|
||||
def not_found_in_index(self, requirement):
|
||||
if self[requirement.key]: # we've seen at least one distro
|
||||
meth, msg = self.info, "Couldn't retrieve index page for %r"
|
||||
else: # no distros seen for this name, might be misspelled
|
||||
meth, msg = (self.warn,
|
||||
"Couldn't find index page for %r (maybe misspelled?)")
|
||||
meth(msg, requirement.unsafe_name)
|
||||
self.scan_all()
|
||||
|
||||
def download(self, spec, tmpdir):
|
||||
"""Locate and/or download `spec` to `tmpdir`, returning a local path
|
||||
|
||||
`spec` may be a ``Requirement`` object, or a string containing a URL,
|
||||
an existing local filename, or a project/version requirement spec
|
||||
(i.e. the string form of a ``Requirement`` object). If it is the URL
|
||||
of a .py file with an unambiguous ``#egg=name-version`` tag (i.e., one
|
||||
that escapes ``-`` as ``_`` throughout), a trivial ``setup.py`` is
|
||||
automatically created alongside the downloaded file.
|
||||
|
||||
If `spec` is a ``Requirement`` object or a string containing a
|
||||
project/version requirement spec, this method returns the location of
|
||||
a matching distribution (possibly after downloading it to `tmpdir`).
|
||||
If `spec` is a locally existing file or directory name, it is simply
|
||||
returned unchanged. If `spec` is a URL, it is downloaded to a subpath
|
||||
of `tmpdir`, and the local filename is returned. Various errors may be
|
||||
raised if a problem occurs during downloading.
|
||||
"""
|
||||
if not isinstance(spec,Requirement):
|
||||
scheme = URL_SCHEME(spec)
|
||||
if scheme:
|
||||
# It's a url, download it to tmpdir
|
||||
found = self._download_url(scheme.group(1), spec, tmpdir)
|
||||
base, fragment = egg_info_for_url(spec)
|
||||
if base.endswith('.py'):
|
||||
found = self.gen_setup(found,fragment,tmpdir)
|
||||
return found
|
||||
elif os.path.exists(spec):
|
||||
# Existing file or directory, just return it
|
||||
return spec
|
||||
else:
|
||||
try:
|
||||
spec = Requirement.parse(spec)
|
||||
except ValueError:
|
||||
raise DistutilsError(
|
||||
"Not a URL, existing file, or requirement spec: %r" %
|
||||
(spec,)
|
||||
)
|
||||
return getattr(self.fetch_distribution(spec, tmpdir),'location',None)
|
||||
|
||||
|
||||
def fetch_distribution(self,
|
||||
requirement, tmpdir, force_scan=False, source=False, develop_ok=False,
|
||||
local_index=None
|
||||
):
|
||||
"""Obtain a distribution suitable for fulfilling `requirement`
|
||||
|
||||
`requirement` must be a ``pkg_resources.Requirement`` instance.
|
||||
If necessary, or if the `force_scan` flag is set, the requirement is
|
||||
searched for in the (online) package index as well as the locally
|
||||
installed packages. If a distribution matching `requirement` is found,
|
||||
the returned distribution's ``location`` is the value you would have
|
||||
gotten from calling the ``download()`` method with the matching
|
||||
distribution's URL or filename. If no matching distribution is found,
|
||||
``None`` is returned.
|
||||
|
||||
If the `source` flag is set, only source distributions and source
|
||||
checkout links will be considered. Unless the `develop_ok` flag is
|
||||
set, development and system eggs (i.e., those using the ``.egg-info``
|
||||
format) will be ignored.
|
||||
"""
|
||||
|
||||
# process a Requirement
|
||||
self.info("Searching for %s", requirement)
|
||||
skipped = {}
|
||||
dist = None
|
||||
|
||||
def find(req, env=None):
|
||||
if env is None:
|
||||
env = self
|
||||
# Find a matching distribution; may be called more than once
|
||||
|
||||
for dist in env[req.key]:
|
||||
|
||||
if dist.precedence==DEVELOP_DIST and not develop_ok:
|
||||
if dist not in skipped:
|
||||
self.warn("Skipping development or system egg: %s",dist)
|
||||
skipped[dist] = 1
|
||||
continue
|
||||
|
||||
if dist in req and (dist.precedence<=SOURCE_DIST or not source):
|
||||
self.info("Best match: %s", dist)
|
||||
return dist.clone(
|
||||
location=self.download(dist.location, tmpdir)
|
||||
)
|
||||
|
||||
if force_scan:
|
||||
self.prescan()
|
||||
self.find_packages(requirement)
|
||||
dist = find(requirement)
|
||||
|
||||
if local_index is not None:
|
||||
dist = dist or find(requirement, local_index)
|
||||
|
||||
if dist is None and self.to_scan is not None:
|
||||
self.prescan()
|
||||
dist = find(requirement)
|
||||
|
||||
if dist is None and not force_scan:
|
||||
self.find_packages(requirement)
|
||||
dist = find(requirement)
|
||||
|
||||
if dist is None:
|
||||
self.warn(
|
||||
"No local packages or download links found for %s%s",
|
||||
(source and "a source distribution of " or ""),
|
||||
requirement,
|
||||
)
|
||||
return dist
|
||||
|
||||
def fetch(self, requirement, tmpdir, force_scan=False, source=False):
|
||||
"""Obtain a file suitable for fulfilling `requirement`
|
||||
|
||||
DEPRECATED; use the ``fetch_distribution()`` method now instead. For
|
||||
backward compatibility, this routine is identical but returns the
|
||||
``location`` of the downloaded distribution instead of a distribution
|
||||
object.
|
||||
"""
|
||||
dist = self.fetch_distribution(requirement,tmpdir,force_scan,source)
|
||||
if dist is not None:
|
||||
return dist.location
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def gen_setup(self, filename, fragment, tmpdir):
|
||||
match = EGG_FRAGMENT.match(fragment)
|
||||
dists = match and [d for d in
|
||||
interpret_distro_name(filename, match.group(1), None) if d.version
|
||||
] or []
|
||||
|
||||
if len(dists)==1: # unambiguous ``#egg`` fragment
|
||||
basename = os.path.basename(filename)
|
||||
|
||||
# Make sure the file has been downloaded to the temp dir.
|
||||
if os.path.dirname(filename) != tmpdir:
|
||||
dst = os.path.join(tmpdir, basename)
|
||||
from setuptools.command.easy_install import samefile
|
||||
if not samefile(filename, dst):
|
||||
shutil.copy2(filename, dst)
|
||||
filename=dst
|
||||
|
||||
file = open(os.path.join(tmpdir, 'setup.py'), 'w')
|
||||
file.write(
|
||||
"from setuptools import setup\n"
|
||||
"setup(name=%r, version=%r, py_modules=[%r])\n"
|
||||
% (
|
||||
dists[0].project_name, dists[0].version,
|
||||
os.path.splitext(basename)[0]
|
||||
)
|
||||
)
|
||||
file.close()
|
||||
return filename
|
||||
|
||||
elif match:
|
||||
raise DistutilsError(
|
||||
"Can't unambiguously interpret project/version identifier %r; "
|
||||
"any dashes in the name or version should be escaped using "
|
||||
"underscores. %r" % (fragment,dists)
|
||||
)
|
||||
else:
|
||||
raise DistutilsError(
|
||||
"Can't process plain .py files without an '#egg=name-version'"
|
||||
" suffix to enable automatic setup script generation."
|
||||
)
|
||||
|
||||
dl_blocksize = 8192
|
||||
def _download_to(self, url, filename):
|
||||
self.info("Downloading %s", url)
|
||||
# Download the file
|
||||
fp, tfp, info = None, None, None
|
||||
try:
|
||||
if '#' in url:
|
||||
url, info = url.split('#', 1)
|
||||
fp = self.open_url(url)
|
||||
if isinstance(fp, urllib2.HTTPError):
|
||||
raise DistutilsError(
|
||||
"Can't download %s: %s %s" % (url, fp.code,fp.msg)
|
||||
)
|
||||
cs = md5()
|
||||
headers = fp.info()
|
||||
blocknum = 0
|
||||
bs = self.dl_blocksize
|
||||
size = -1
|
||||
if "content-length" in headers:
|
||||
# Some servers return multiple Content-Length headers :(
|
||||
content_length = headers.get("Content-Length")
|
||||
size = int(content_length)
|
||||
self.reporthook(url, filename, blocknum, bs, size)
|
||||
tfp = open(filename,'wb')
|
||||
while True:
|
||||
block = fp.read(bs)
|
||||
if block:
|
||||
cs.update(block)
|
||||
tfp.write(block)
|
||||
blocknum += 1
|
||||
self.reporthook(url, filename, blocknum, bs, size)
|
||||
else:
|
||||
break
|
||||
if info: self.check_md5(cs, info, filename, tfp)
|
||||
return headers
|
||||
finally:
|
||||
if fp: fp.close()
|
||||
if tfp: tfp.close()
|
||||
|
||||
def reporthook(self, url, filename, blocknum, blksize, size):
|
||||
pass # no-op
|
||||
|
||||
|
||||
def open_url(self, url, warning=None):
|
||||
if url.startswith('file:'):
|
||||
return local_open(url)
|
||||
try:
|
||||
return open_with_auth(url)
|
||||
except (ValueError, httplib.InvalidURL), v:
|
||||
msg = ' '.join([str(arg) for arg in v.args])
|
||||
if warning:
|
||||
self.warn(warning, msg)
|
||||
else:
|
||||
raise DistutilsError('%s %s' % (url, msg))
|
||||
except urllib2.HTTPError, v:
|
||||
return v
|
||||
except urllib2.URLError, v:
|
||||
if warning:
|
||||
self.warn(warning, v.reason)
|
||||
else:
|
||||
raise DistutilsError("Download error for %s: %s"
|
||||
% (url, v.reason))
|
||||
except httplib.BadStatusLine, v:
|
||||
if warning:
|
||||
self.warn(warning, v.line)
|
||||
else:
|
||||
raise DistutilsError('%s returned a bad status line. '
|
||||
'The server might be down, %s' % \
|
||||
(url, v.line))
|
||||
except httplib.HTTPException, v:
|
||||
if warning:
|
||||
self.warn(warning, v)
|
||||
else:
|
||||
raise DistutilsError("Download error for %s: %s"
|
||||
% (url, v))
|
||||
|
||||
def _download_url(self, scheme, url, tmpdir):
|
||||
# Determine download filename
|
||||
#
|
||||
name = filter(None,urlparse.urlparse(url)[2].split('/'))
|
||||
if name:
|
||||
name = name[-1]
|
||||
while '..' in name:
|
||||
name = name.replace('..','.').replace('\\','_')
|
||||
else:
|
||||
name = "__downloaded__" # default if URL has no path contents
|
||||
|
||||
if name.endswith('.egg.zip'):
|
||||
name = name[:-4] # strip the extra .zip before download
|
||||
|
||||
filename = os.path.join(tmpdir,name)
|
||||
|
||||
# Download the file
|
||||
#
|
||||
if scheme=='svn' or scheme.startswith('svn+'):
|
||||
return self._download_svn(url, filename)
|
||||
elif scheme=='git' or scheme.startswith('git+'):
|
||||
return self._download_git(url, filename)
|
||||
elif scheme.startswith('hg+'):
|
||||
return self._download_hg(url, filename)
|
||||
elif scheme=='file':
|
||||
return urllib.url2pathname(urlparse.urlparse(url)[2])
|
||||
else:
|
||||
self.url_ok(url, True) # raises error if not allowed
|
||||
return self._attempt_download(url, filename)
|
||||
|
||||
|
||||
|
||||
def scan_url(self, url):
|
||||
self.process_url(url, True)
|
||||
|
||||
|
||||
def _attempt_download(self, url, filename):
|
||||
headers = self._download_to(url, filename)
|
||||
if 'html' in headers.get('content-type','').lower():
|
||||
return self._download_html(url, headers, filename)
|
||||
else:
|
||||
return filename
|
||||
|
||||
def _download_html(self, url, headers, filename):
|
||||
file = open(filename)
|
||||
for line in file:
|
||||
if line.strip():
|
||||
# Check for a subversion index page
|
||||
if re.search(r'<title>([^- ]+ - )?Revision \d+:', line):
|
||||
# it's a subversion index page:
|
||||
file.close()
|
||||
os.unlink(filename)
|
||||
return self._download_svn(url, filename)
|
||||
break # not an index page
|
||||
file.close()
|
||||
os.unlink(filename)
|
||||
raise DistutilsError("Unexpected HTML page found at "+url)
|
||||
|
||||
def _download_svn(self, url, filename):
|
||||
url = url.split('#',1)[0] # remove any fragment for svn's sake
|
||||
self.info("Doing subversion checkout from %s to %s", url, filename)
|
||||
os.system("svn checkout -q %s %s" % (url, filename))
|
||||
return filename
|
||||
|
||||
def _vcs_split_rev_from_url(self, url, pop_prefix=False):
|
||||
scheme, netloc, path, query, frag = urlparse.urlsplit(url)
|
||||
|
||||
scheme = scheme.split('+', 1)[-1]
|
||||
|
||||
# Some fragment identification fails
|
||||
path = path.split('#',1)[0]
|
||||
|
||||
rev = None
|
||||
if '@' in path:
|
||||
path, rev = path.rsplit('@', 1)
|
||||
|
||||
# Also, discard fragment
|
||||
url = urlparse.urlunsplit((scheme, netloc, path, query, ''))
|
||||
|
||||
return url, rev
|
||||
|
||||
def _download_git(self, url, filename):
|
||||
filename = filename.split('#',1)[0]
|
||||
url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True)
|
||||
|
||||
self.info("Doing git clone from %s to %s", url, filename)
|
||||
os.system("git clone --quiet %s %s" % (url, filename))
|
||||
|
||||
if rev is not None:
|
||||
self.info("Checking out %s", rev)
|
||||
os.system("(cd %s && git checkout --quiet %s)" % (
|
||||
filename,
|
||||
rev,
|
||||
))
|
||||
|
||||
return filename
|
||||
|
||||
def _download_hg(self, url, filename):
|
||||
filename = filename.split('#',1)[0]
|
||||
url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True)
|
||||
|
||||
self.info("Doing hg clone from %s to %s", url, filename)
|
||||
os.system("hg clone --quiet %s %s" % (url, filename))
|
||||
|
||||
if rev is not None:
|
||||
self.info("Updating to %s", rev)
|
||||
os.system("(cd %s && hg up -C -r %s >&-)" % (
|
||||
filename,
|
||||
rev,
|
||||
))
|
||||
|
||||
return filename
|
||||
|
||||
def debug(self, msg, *args):
|
||||
log.debug(msg, *args)
|
||||
|
||||
def info(self, msg, *args):
|
||||
log.info(msg, *args)
|
||||
|
||||
def warn(self, msg, *args):
|
||||
log.warn(msg, *args)
|
||||
|
||||
# This pattern matches a character entity reference (a decimal numeric
|
||||
# references, a hexadecimal numeric reference, or a named reference).
|
||||
entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub
|
||||
|
||||
def uchr(c):
|
||||
if not isinstance(c, int):
|
||||
return c
|
||||
if c>255: return unichr(c)
|
||||
return chr(c)
|
||||
|
||||
def decode_entity(match):
|
||||
what = match.group(1)
|
||||
if what.startswith('#x'):
|
||||
what = int(what[2:], 16)
|
||||
elif what.startswith('#'):
|
||||
what = int(what[1:])
|
||||
else:
|
||||
from htmlentitydefs import name2codepoint
|
||||
what = name2codepoint.get(what, match.group(0))
|
||||
return uchr(what)
|
||||
|
||||
def htmldecode(text):
|
||||
"""Decode HTML entities in the given text."""
|
||||
return entity_sub(decode_entity, text)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def socket_timeout(timeout=15):
|
||||
def _socket_timeout(func):
|
||||
def _socket_timeout(*args, **kwargs):
|
||||
old_timeout = socket.getdefaulttimeout()
|
||||
socket.setdefaulttimeout(timeout)
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
finally:
|
||||
socket.setdefaulttimeout(old_timeout)
|
||||
return _socket_timeout
|
||||
return _socket_timeout
|
||||
|
||||
def _encode_auth(auth):
|
||||
"""
|
||||
A function compatible with Python 2.3-3.3 that will encode
|
||||
auth from a URL suitable for an HTTP header.
|
||||
>>> _encode_auth('username%3Apassword')
|
||||
u'dXNlcm5hbWU6cGFzc3dvcmQ='
|
||||
"""
|
||||
auth_s = urllib2.unquote(auth)
|
||||
# convert to bytes
|
||||
auth_bytes = auth_s.encode()
|
||||
# use the legacy interface for Python 2.3 support
|
||||
encoded_bytes = base64.encodestring(auth_bytes)
|
||||
# convert back to a string
|
||||
encoded = encoded_bytes.decode()
|
||||
# strip the trailing carriage return
|
||||
return encoded.rstrip()
|
||||
|
||||
def open_with_auth(url):
|
||||
"""Open a urllib2 request, handling HTTP authentication"""
|
||||
|
||||
scheme, netloc, path, params, query, frag = urlparse.urlparse(url)
|
||||
|
||||
# Double scheme does not raise on Mac OS X as revealed by a
|
||||
# failing test. We would expect "nonnumeric port". Refs #20.
|
||||
if netloc.endswith(':'):
|
||||
raise httplib.InvalidURL("nonnumeric port: ''")
|
||||
|
||||
if scheme in ('http', 'https'):
|
||||
auth, host = urllib2.splituser(netloc)
|
||||
else:
|
||||
auth = None
|
||||
|
||||
if auth:
|
||||
auth = "Basic " + _encode_auth(auth)
|
||||
new_url = urlparse.urlunparse((scheme,host,path,params,query,frag))
|
||||
request = urllib2.Request(new_url)
|
||||
request.add_header("Authorization", auth)
|
||||
else:
|
||||
request = urllib2.Request(url)
|
||||
|
||||
request.add_header('User-Agent', user_agent)
|
||||
fp = urllib2.urlopen(request)
|
||||
|
||||
if auth:
|
||||
# Put authentication info back into request URL if same host,
|
||||
# so that links found on the page will work
|
||||
s2, h2, path2, param2, query2, frag2 = urlparse.urlparse(fp.url)
|
||||
if s2==scheme and h2==host:
|
||||
fp.url = urlparse.urlunparse((s2,netloc,path2,param2,query2,frag2))
|
||||
|
||||
return fp
|
||||
|
||||
# adding a timeout to avoid freezing package_index
|
||||
open_with_auth = socket_timeout(_SOCKET_TIMEOUT)(open_with_auth)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def fix_sf_url(url):
|
||||
return url # backward compatibility
|
||||
|
||||
def local_open(url):
|
||||
"""Read a local path, with special support for directories"""
|
||||
scheme, server, path, param, query, frag = urlparse.urlparse(url)
|
||||
filename = urllib.url2pathname(path)
|
||||
if os.path.isfile(filename):
|
||||
return urllib2.urlopen(url)
|
||||
elif path.endswith('/') and os.path.isdir(filename):
|
||||
files = []
|
||||
for f in os.listdir(filename):
|
||||
if f=='index.html':
|
||||
fp = open(os.path.join(filename,f),'rb')
|
||||
body = fp.read()
|
||||
fp.close()
|
||||
break
|
||||
elif os.path.isdir(os.path.join(filename,f)):
|
||||
f+='/'
|
||||
files.append("<a href=%r>%s</a>" % (f,f))
|
||||
else:
|
||||
body = ("<html><head><title>%s</title>" % url) + \
|
||||
"</head><body>%s</body></html>" % '\n'.join(files)
|
||||
status, message = 200, "OK"
|
||||
else:
|
||||
status, message, body = 404, "Path not found", "Not found"
|
||||
|
||||
return urllib2.HTTPError(url, status, message,
|
||||
{'content-type':'text/html'}, cStringIO.StringIO(body))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# this line is a kludge to keep the trailing blank lines for pje's editor
|
||||
-290
@@ -1,290 +0,0 @@
|
||||
import os, sys, __builtin__, tempfile, operator, pkg_resources
|
||||
_os = sys.modules[os.name]
|
||||
try:
|
||||
_file = file
|
||||
except NameError:
|
||||
_file = None
|
||||
_open = open
|
||||
from distutils.errors import DistutilsError
|
||||
__all__ = [
|
||||
"AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup",
|
||||
]
|
||||
def run_setup(setup_script, args):
|
||||
"""Run a distutils setup script, sandboxed in its directory"""
|
||||
old_dir = os.getcwd()
|
||||
save_argv = sys.argv[:]
|
||||
save_path = sys.path[:]
|
||||
setup_dir = os.path.abspath(os.path.dirname(setup_script))
|
||||
temp_dir = os.path.join(setup_dir,'temp')
|
||||
if not os.path.isdir(temp_dir): os.makedirs(temp_dir)
|
||||
save_tmp = tempfile.tempdir
|
||||
save_modules = sys.modules.copy()
|
||||
pr_state = pkg_resources.__getstate__()
|
||||
try:
|
||||
tempfile.tempdir = temp_dir
|
||||
os.chdir(setup_dir)
|
||||
try:
|
||||
sys.argv[:] = [setup_script]+list(args)
|
||||
sys.path.insert(0, setup_dir)
|
||||
DirectorySandbox(setup_dir).run(
|
||||
lambda: execfile(
|
||||
"setup.py",
|
||||
{'__file__':setup_script, '__name__':'__main__'}
|
||||
)
|
||||
)
|
||||
except SystemExit, v:
|
||||
if v.args and v.args[0]:
|
||||
raise
|
||||
# Normal exit, just return
|
||||
finally:
|
||||
pkg_resources.__setstate__(pr_state)
|
||||
sys.modules.update(save_modules)
|
||||
# remove any modules imported within the sandbox
|
||||
del_modules = [
|
||||
mod_name for mod_name in sys.modules
|
||||
if mod_name not in save_modules
|
||||
# exclude any encodings modules. See #285
|
||||
and not mod_name.startswith('encodings.')
|
||||
]
|
||||
map(sys.modules.__delitem__, del_modules)
|
||||
os.chdir(old_dir)
|
||||
sys.path[:] = save_path
|
||||
sys.argv[:] = save_argv
|
||||
tempfile.tempdir = save_tmp
|
||||
|
||||
class AbstractSandbox:
|
||||
"""Wrap 'os' module and 'open()' builtin for virtualizing setup scripts"""
|
||||
|
||||
_active = False
|
||||
|
||||
def __init__(self):
|
||||
self._attrs = [
|
||||
name for name in dir(_os)
|
||||
if not name.startswith('_') and hasattr(self,name)
|
||||
]
|
||||
|
||||
def _copy(self, source):
|
||||
for name in self._attrs:
|
||||
setattr(os, name, getattr(source,name))
|
||||
|
||||
def run(self, func):
|
||||
"""Run 'func' under os sandboxing"""
|
||||
try:
|
||||
self._copy(self)
|
||||
if _file:
|
||||
__builtin__.file = self._file
|
||||
__builtin__.open = self._open
|
||||
self._active = True
|
||||
return func()
|
||||
finally:
|
||||
self._active = False
|
||||
if _file:
|
||||
__builtin__.file = _file
|
||||
__builtin__.open = _open
|
||||
self._copy(_os)
|
||||
|
||||
|
||||
def _mk_dual_path_wrapper(name):
|
||||
original = getattr(_os,name)
|
||||
def wrap(self,src,dst,*args,**kw):
|
||||
if self._active:
|
||||
src,dst = self._remap_pair(name,src,dst,*args,**kw)
|
||||
return original(src,dst,*args,**kw)
|
||||
return wrap
|
||||
|
||||
|
||||
for name in ["rename", "link", "symlink"]:
|
||||
if hasattr(_os,name): locals()[name] = _mk_dual_path_wrapper(name)
|
||||
|
||||
|
||||
def _mk_single_path_wrapper(name, original=None):
|
||||
original = original or getattr(_os,name)
|
||||
def wrap(self,path,*args,**kw):
|
||||
if self._active:
|
||||
path = self._remap_input(name,path,*args,**kw)
|
||||
return original(path,*args,**kw)
|
||||
return wrap
|
||||
|
||||
if _file:
|
||||
_file = _mk_single_path_wrapper('file', _file)
|
||||
_open = _mk_single_path_wrapper('open', _open)
|
||||
for name in [
|
||||
"stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir",
|
||||
"remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat",
|
||||
"startfile", "mkfifo", "mknod", "pathconf", "access"
|
||||
]:
|
||||
if hasattr(_os,name): locals()[name] = _mk_single_path_wrapper(name)
|
||||
|
||||
|
||||
def _mk_single_with_return(name):
|
||||
original = getattr(_os,name)
|
||||
def wrap(self,path,*args,**kw):
|
||||
if self._active:
|
||||
path = self._remap_input(name,path,*args,**kw)
|
||||
return self._remap_output(name, original(path,*args,**kw))
|
||||
return original(path,*args,**kw)
|
||||
return wrap
|
||||
|
||||
for name in ['readlink', 'tempnam']:
|
||||
if hasattr(_os,name): locals()[name] = _mk_single_with_return(name)
|
||||
|
||||
def _mk_query(name):
|
||||
original = getattr(_os,name)
|
||||
def wrap(self,*args,**kw):
|
||||
retval = original(*args,**kw)
|
||||
if self._active:
|
||||
return self._remap_output(name, retval)
|
||||
return retval
|
||||
return wrap
|
||||
|
||||
for name in ['getcwd', 'tmpnam']:
|
||||
if hasattr(_os,name): locals()[name] = _mk_query(name)
|
||||
|
||||
def _validate_path(self,path):
|
||||
"""Called to remap or validate any path, whether input or output"""
|
||||
return path
|
||||
|
||||
def _remap_input(self,operation,path,*args,**kw):
|
||||
"""Called for path inputs"""
|
||||
return self._validate_path(path)
|
||||
|
||||
def _remap_output(self,operation,path):
|
||||
"""Called for path outputs"""
|
||||
return self._validate_path(path)
|
||||
|
||||
def _remap_pair(self,operation,src,dst,*args,**kw):
|
||||
"""Called for path pairs like rename, link, and symlink operations"""
|
||||
return (
|
||||
self._remap_input(operation+'-from',src,*args,**kw),
|
||||
self._remap_input(operation+'-to',dst,*args,**kw)
|
||||
)
|
||||
|
||||
|
||||
if hasattr(os, 'devnull'):
|
||||
_EXCEPTIONS = [os.devnull,]
|
||||
else:
|
||||
_EXCEPTIONS = []
|
||||
|
||||
try:
|
||||
from win32com.client.gencache import GetGeneratePath
|
||||
_EXCEPTIONS.append(GetGeneratePath())
|
||||
del GetGeneratePath
|
||||
except ImportError:
|
||||
# it appears pywin32 is not installed, so no need to exclude.
|
||||
pass
|
||||
|
||||
class DirectorySandbox(AbstractSandbox):
|
||||
"""Restrict operations to a single subdirectory - pseudo-chroot"""
|
||||
|
||||
write_ops = dict.fromkeys([
|
||||
"open", "chmod", "chown", "mkdir", "remove", "unlink", "rmdir",
|
||||
"utime", "lchown", "chroot", "mkfifo", "mknod", "tempnam",
|
||||
])
|
||||
|
||||
def __init__(self, sandbox, exceptions=_EXCEPTIONS):
|
||||
self._sandbox = os.path.normcase(os.path.realpath(sandbox))
|
||||
self._prefix = os.path.join(self._sandbox,'')
|
||||
self._exceptions = [os.path.normcase(os.path.realpath(path)) for path in exceptions]
|
||||
AbstractSandbox.__init__(self)
|
||||
|
||||
def _violation(self, operation, *args, **kw):
|
||||
raise SandboxViolation(operation, args, kw)
|
||||
|
||||
if _file:
|
||||
def _file(self, path, mode='r', *args, **kw):
|
||||
if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path):
|
||||
self._violation("file", path, mode, *args, **kw)
|
||||
return _file(path,mode,*args,**kw)
|
||||
|
||||
def _open(self, path, mode='r', *args, **kw):
|
||||
if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path):
|
||||
self._violation("open", path, mode, *args, **kw)
|
||||
return _open(path,mode,*args,**kw)
|
||||
|
||||
def tmpnam(self):
|
||||
self._violation("tmpnam")
|
||||
|
||||
def _ok(self,path):
|
||||
active = self._active
|
||||
try:
|
||||
self._active = False
|
||||
realpath = os.path.normcase(os.path.realpath(path))
|
||||
if (self._exempted(realpath) or realpath == self._sandbox
|
||||
or realpath.startswith(self._prefix)):
|
||||
return True
|
||||
finally:
|
||||
self._active = active
|
||||
|
||||
def _exempted(self, filepath):
|
||||
exception_matches = map(filepath.startswith, self._exceptions)
|
||||
return True in exception_matches
|
||||
|
||||
def _remap_input(self,operation,path,*args,**kw):
|
||||
"""Called for path inputs"""
|
||||
if operation in self.write_ops and not self._ok(path):
|
||||
self._violation(operation, os.path.realpath(path), *args, **kw)
|
||||
return path
|
||||
|
||||
def _remap_pair(self,operation,src,dst,*args,**kw):
|
||||
"""Called for path pairs like rename, link, and symlink operations"""
|
||||
if not self._ok(src) or not self._ok(dst):
|
||||
self._violation(operation, src, dst, *args, **kw)
|
||||
return (src,dst)
|
||||
|
||||
def open(self, file, flags, mode=0777):
|
||||
"""Called for low-level os.open()"""
|
||||
if flags & WRITE_FLAGS and not self._ok(file):
|
||||
self._violation("os.open", file, flags, mode)
|
||||
return _os.open(file,flags,mode)
|
||||
|
||||
|
||||
WRITE_FLAGS = reduce(
|
||||
operator.or_,
|
||||
[getattr(_os, a, 0) for a in
|
||||
"O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()]
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
class SandboxViolation(DistutilsError):
|
||||
"""A setup script attempted to modify the filesystem outside the sandbox"""
|
||||
|
||||
def __str__(self):
|
||||
return """SandboxViolation: %s%r %s
|
||||
|
||||
The package setup script has attempted to modify files on your system
|
||||
that are not within the EasyInstall build area, and has been aborted.
|
||||
|
||||
This package cannot be safely installed by EasyInstall, and may not
|
||||
support alternate installation locations even if you run its setup
|
||||
script by hand. Please inform the package's author and the EasyInstall
|
||||
maintainers to find out if a fix or workaround is available.""" % self.args
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#
|
||||
@@ -1,6 +0,0 @@
|
||||
# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r
|
||||
__requires__ = """%(spec)r"""
|
||||
from pkg_resources import require; require("""%(spec)r""")
|
||||
del require
|
||||
__file__ = """%(dev_path)r"""
|
||||
execfile(__file__)
|
||||
@@ -1,4 +0,0 @@
|
||||
# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r
|
||||
__requires__ = """%(spec)r"""
|
||||
import pkg_resources
|
||||
pkg_resources.run_script("""%(spec)r""", """%(script_name)r""")
|
||||
@@ -1,349 +0,0 @@
|
||||
"""Tests for the 'setuptools' package"""
|
||||
import sys
|
||||
import os
|
||||
import unittest
|
||||
import doctest
|
||||
import distutils.core
|
||||
import distutils.cmd
|
||||
from distutils.errors import DistutilsOptionError, DistutilsPlatformError
|
||||
from distutils.errors import DistutilsSetupError
|
||||
from distutils.core import Extension
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
import setuptools.dist
|
||||
import setuptools.depends as dep
|
||||
from setuptools import Feature
|
||||
from setuptools.depends import Require
|
||||
|
||||
def additional_tests():
|
||||
import doctest, unittest
|
||||
suite = unittest.TestSuite((
|
||||
doctest.DocFileSuite(
|
||||
os.path.join('tests', 'api_tests.txt'),
|
||||
optionflags=doctest.ELLIPSIS, package='pkg_resources',
|
||||
),
|
||||
))
|
||||
if sys.platform == 'win32':
|
||||
suite.addTest(doctest.DocFileSuite('win_script_wrapper.txt'))
|
||||
return suite
|
||||
|
||||
def makeSetup(**args):
|
||||
"""Return distribution from 'setup(**args)', without executing commands"""
|
||||
|
||||
distutils.core._setup_stop_after = "commandline"
|
||||
|
||||
# Don't let system command line leak into tests!
|
||||
args.setdefault('script_args',['install'])
|
||||
|
||||
try:
|
||||
return setuptools.setup(**args)
|
||||
finally:
|
||||
distutils.core._setup_stop_after = None
|
||||
|
||||
|
||||
class DependsTests(unittest.TestCase):
|
||||
|
||||
def testExtractConst(self):
|
||||
if not hasattr(dep, 'extract_constant'):
|
||||
# skip on non-bytecode platforms
|
||||
return
|
||||
|
||||
def f1():
|
||||
global x, y, z
|
||||
x = "test"
|
||||
y = z
|
||||
|
||||
# unrecognized name
|
||||
self.assertEqual(dep.extract_constant(f1.func_code,'q', -1), None)
|
||||
|
||||
# constant assigned
|
||||
self.assertEqual(dep.extract_constant(f1.func_code,'x', -1), "test")
|
||||
|
||||
# expression assigned
|
||||
self.assertEqual(dep.extract_constant(f1.func_code,'y', -1), -1)
|
||||
|
||||
# recognized name, not assigned
|
||||
self.assertEqual(dep.extract_constant(f1.func_code,'z', -1), None)
|
||||
|
||||
def testFindModule(self):
|
||||
self.assertRaises(ImportError, dep.find_module, 'no-such.-thing')
|
||||
self.assertRaises(ImportError, dep.find_module, 'setuptools.non-existent')
|
||||
f,p,i = dep.find_module('setuptools.tests')
|
||||
f.close()
|
||||
|
||||
def testModuleExtract(self):
|
||||
if not hasattr(dep, 'get_module_constant'):
|
||||
# skip on non-bytecode platforms
|
||||
return
|
||||
|
||||
from email import __version__
|
||||
self.assertEqual(
|
||||
dep.get_module_constant('email','__version__'), __version__
|
||||
)
|
||||
self.assertEqual(
|
||||
dep.get_module_constant('sys','version'), sys.version
|
||||
)
|
||||
self.assertEqual(
|
||||
dep.get_module_constant('setuptools.tests','__doc__'),__doc__
|
||||
)
|
||||
|
||||
def testRequire(self):
|
||||
if not hasattr(dep, 'extract_constant'):
|
||||
# skip on non-bytecode platformsh
|
||||
return
|
||||
|
||||
req = Require('Email','1.0.3','email')
|
||||
|
||||
self.assertEqual(req.name, 'Email')
|
||||
self.assertEqual(req.module, 'email')
|
||||
self.assertEqual(req.requested_version, '1.0.3')
|
||||
self.assertEqual(req.attribute, '__version__')
|
||||
self.assertEqual(req.full_name(), 'Email-1.0.3')
|
||||
|
||||
from email import __version__
|
||||
self.assertEqual(req.get_version(), __version__)
|
||||
self.assertTrue(req.version_ok('1.0.9'))
|
||||
self.assertTrue(not req.version_ok('0.9.1'))
|
||||
self.assertTrue(not req.version_ok('unknown'))
|
||||
|
||||
self.assertTrue(req.is_present())
|
||||
self.assertTrue(req.is_current())
|
||||
|
||||
req = Require('Email 3000','03000','email',format=LooseVersion)
|
||||
self.assertTrue(req.is_present())
|
||||
self.assertTrue(not req.is_current())
|
||||
self.assertTrue(not req.version_ok('unknown'))
|
||||
|
||||
req = Require('Do-what-I-mean','1.0','d-w-i-m')
|
||||
self.assertTrue(not req.is_present())
|
||||
self.assertTrue(not req.is_current())
|
||||
|
||||
req = Require('Tests', None, 'tests', homepage="http://example.com")
|
||||
self.assertEqual(req.format, None)
|
||||
self.assertEqual(req.attribute, None)
|
||||
self.assertEqual(req.requested_version, None)
|
||||
self.assertEqual(req.full_name(), 'Tests')
|
||||
self.assertEqual(req.homepage, 'http://example.com')
|
||||
|
||||
paths = [os.path.dirname(p) for p in __path__]
|
||||
self.assertTrue(req.is_present(paths))
|
||||
self.assertTrue(req.is_current(paths))
|
||||
|
||||
|
||||
class DistroTests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.e1 = Extension('bar.ext',['bar.c'])
|
||||
self.e2 = Extension('c.y', ['y.c'])
|
||||
|
||||
self.dist = makeSetup(
|
||||
packages=['a', 'a.b', 'a.b.c', 'b', 'c'],
|
||||
py_modules=['b.d','x'],
|
||||
ext_modules = (self.e1, self.e2),
|
||||
package_dir = {},
|
||||
)
|
||||
|
||||
def testDistroType(self):
|
||||
self.assertTrue(isinstance(self.dist,setuptools.dist.Distribution))
|
||||
|
||||
def testExcludePackage(self):
|
||||
self.dist.exclude_package('a')
|
||||
self.assertEqual(self.dist.packages, ['b','c'])
|
||||
|
||||
self.dist.exclude_package('b')
|
||||
self.assertEqual(self.dist.packages, ['c'])
|
||||
self.assertEqual(self.dist.py_modules, ['x'])
|
||||
self.assertEqual(self.dist.ext_modules, [self.e1, self.e2])
|
||||
|
||||
self.dist.exclude_package('c')
|
||||
self.assertEqual(self.dist.packages, [])
|
||||
self.assertEqual(self.dist.py_modules, ['x'])
|
||||
self.assertEqual(self.dist.ext_modules, [self.e1])
|
||||
|
||||
# test removals from unspecified options
|
||||
makeSetup().exclude_package('x')
|
||||
|
||||
def testIncludeExclude(self):
|
||||
# remove an extension
|
||||
self.dist.exclude(ext_modules=[self.e1])
|
||||
self.assertEqual(self.dist.ext_modules, [self.e2])
|
||||
|
||||
# add it back in
|
||||
self.dist.include(ext_modules=[self.e1])
|
||||
self.assertEqual(self.dist.ext_modules, [self.e2, self.e1])
|
||||
|
||||
# should not add duplicate
|
||||
self.dist.include(ext_modules=[self.e1])
|
||||
self.assertEqual(self.dist.ext_modules, [self.e2, self.e1])
|
||||
|
||||
def testExcludePackages(self):
|
||||
self.dist.exclude(packages=['c','b','a'])
|
||||
self.assertEqual(self.dist.packages, [])
|
||||
self.assertEqual(self.dist.py_modules, ['x'])
|
||||
self.assertEqual(self.dist.ext_modules, [self.e1])
|
||||
|
||||
def testEmpty(self):
|
||||
dist = makeSetup()
|
||||
dist.include(packages=['a'], py_modules=['b'], ext_modules=[self.e2])
|
||||
dist = makeSetup()
|
||||
dist.exclude(packages=['a'], py_modules=['b'], ext_modules=[self.e2])
|
||||
|
||||
def testContents(self):
|
||||
self.assertTrue(self.dist.has_contents_for('a'))
|
||||
self.dist.exclude_package('a')
|
||||
self.assertTrue(not self.dist.has_contents_for('a'))
|
||||
|
||||
self.assertTrue(self.dist.has_contents_for('b'))
|
||||
self.dist.exclude_package('b')
|
||||
self.assertTrue(not self.dist.has_contents_for('b'))
|
||||
|
||||
self.assertTrue(self.dist.has_contents_for('c'))
|
||||
self.dist.exclude_package('c')
|
||||
self.assertTrue(not self.dist.has_contents_for('c'))
|
||||
|
||||
def testInvalidIncludeExclude(self):
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.include, nonexistent_option='x'
|
||||
)
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.exclude, nonexistent_option='x'
|
||||
)
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.include, packages={'x':'y'}
|
||||
)
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.exclude, packages={'x':'y'}
|
||||
)
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.include, ext_modules={'x':'y'}
|
||||
)
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.exclude, ext_modules={'x':'y'}
|
||||
)
|
||||
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.include, package_dir=['q']
|
||||
)
|
||||
self.assertRaises(DistutilsSetupError,
|
||||
self.dist.exclude, package_dir=['q']
|
||||
)
|
||||
|
||||
|
||||
class FeatureTests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.req = Require('Distutils','1.0.3','distutils')
|
||||
self.dist = makeSetup(
|
||||
features={
|
||||
'foo': Feature("foo",standard=True,require_features=['baz',self.req]),
|
||||
'bar': Feature("bar", standard=True, packages=['pkg.bar'],
|
||||
py_modules=['bar_et'], remove=['bar.ext'],
|
||||
),
|
||||
'baz': Feature(
|
||||
"baz", optional=False, packages=['pkg.baz'],
|
||||
scripts = ['scripts/baz_it'],
|
||||
libraries=[('libfoo','foo/foofoo.c')]
|
||||
),
|
||||
'dwim': Feature("DWIM", available=False, remove='bazish'),
|
||||
},
|
||||
script_args=['--without-bar', 'install'],
|
||||
packages = ['pkg.bar', 'pkg.foo'],
|
||||
py_modules = ['bar_et', 'bazish'],
|
||||
ext_modules = [Extension('bar.ext',['bar.c'])]
|
||||
)
|
||||
|
||||
def testDefaults(self):
|
||||
self.assertTrue(not
|
||||
Feature(
|
||||
"test",standard=True,remove='x',available=False
|
||||
).include_by_default()
|
||||
)
|
||||
self.assertTrue(
|
||||
Feature("test",standard=True,remove='x').include_by_default()
|
||||
)
|
||||
# Feature must have either kwargs, removes, or require_features
|
||||
self.assertRaises(DistutilsSetupError, Feature, "test")
|
||||
|
||||
def testAvailability(self):
|
||||
self.assertRaises(
|
||||
DistutilsPlatformError,
|
||||
self.dist.features['dwim'].include_in, self.dist
|
||||
)
|
||||
|
||||
def testFeatureOptions(self):
|
||||
dist = self.dist
|
||||
self.assertTrue(
|
||||
('with-dwim',None,'include DWIM') in dist.feature_options
|
||||
)
|
||||
self.assertTrue(
|
||||
('without-dwim',None,'exclude DWIM (default)') in dist.feature_options
|
||||
)
|
||||
self.assertTrue(
|
||||
('with-bar',None,'include bar (default)') in dist.feature_options
|
||||
)
|
||||
self.assertTrue(
|
||||
('without-bar',None,'exclude bar') in dist.feature_options
|
||||
)
|
||||
self.assertEqual(dist.feature_negopt['without-foo'],'with-foo')
|
||||
self.assertEqual(dist.feature_negopt['without-bar'],'with-bar')
|
||||
self.assertEqual(dist.feature_negopt['without-dwim'],'with-dwim')
|
||||
self.assertTrue(not 'without-baz' in dist.feature_negopt)
|
||||
|
||||
def testUseFeatures(self):
|
||||
dist = self.dist
|
||||
self.assertEqual(dist.with_foo,1)
|
||||
self.assertEqual(dist.with_bar,0)
|
||||
self.assertEqual(dist.with_baz,1)
|
||||
self.assertTrue(not 'bar_et' in dist.py_modules)
|
||||
self.assertTrue(not 'pkg.bar' in dist.packages)
|
||||
self.assertTrue('pkg.baz' in dist.packages)
|
||||
self.assertTrue('scripts/baz_it' in dist.scripts)
|
||||
self.assertTrue(('libfoo','foo/foofoo.c') in dist.libraries)
|
||||
self.assertEqual(dist.ext_modules,[])
|
||||
self.assertEqual(dist.require_features, [self.req])
|
||||
|
||||
# If we ask for bar, it should fail because we explicitly disabled
|
||||
# it on the command line
|
||||
self.assertRaises(DistutilsOptionError, dist.include_feature, 'bar')
|
||||
|
||||
def testFeatureWithInvalidRemove(self):
|
||||
self.assertRaises(
|
||||
SystemExit, makeSetup, features = {'x':Feature('x', remove='y')}
|
||||
)
|
||||
|
||||
class TestCommandTests(unittest.TestCase):
|
||||
|
||||
def testTestIsCommand(self):
|
||||
test_cmd = makeSetup().get_command_obj('test')
|
||||
self.assertTrue(isinstance(test_cmd, distutils.cmd.Command))
|
||||
|
||||
def testLongOptSuiteWNoDefault(self):
|
||||
ts1 = makeSetup(script_args=['test','--test-suite=foo.tests.suite'])
|
||||
ts1 = ts1.get_command_obj('test')
|
||||
ts1.ensure_finalized()
|
||||
self.assertEqual(ts1.test_suite, 'foo.tests.suite')
|
||||
|
||||
def testDefaultSuite(self):
|
||||
ts2 = makeSetup(test_suite='bar.tests.suite').get_command_obj('test')
|
||||
ts2.ensure_finalized()
|
||||
self.assertEqual(ts2.test_suite, 'bar.tests.suite')
|
||||
|
||||
def testDefaultWModuleOnCmdLine(self):
|
||||
ts3 = makeSetup(
|
||||
test_suite='bar.tests',
|
||||
script_args=['test','-m','foo.tests']
|
||||
).get_command_obj('test')
|
||||
ts3.ensure_finalized()
|
||||
self.assertEqual(ts3.test_module, 'foo.tests')
|
||||
self.assertEqual(ts3.test_suite, 'foo.tests.test_suite')
|
||||
|
||||
def testConflictingOptions(self):
|
||||
ts4 = makeSetup(
|
||||
script_args=['test','-m','bar.tests', '-s','foo.tests.suite']
|
||||
).get_command_obj('test')
|
||||
self.assertRaises(DistutilsOptionError, ts4.ensure_finalized)
|
||||
|
||||
def testNoSuite(self):
|
||||
ts5 = makeSetup().get_command_obj('test')
|
||||
ts5.ensure_finalized()
|
||||
self.assertEqual(ts5.test_suite, None)
|
||||
-2679
File diff suppressed because it is too large
Load Diff
-3
@@ -1,3 +0,0 @@
|
||||
<html><body>
|
||||
<a href="/foobar-0.1.tar.gz#md5=1__bad_md5___">bad old link</a>
|
||||
</body></html>
|
||||
Vendored
-4
@@ -1,4 +0,0 @@
|
||||
<html><body>
|
||||
<a href="/foobar-0.1.tar.gz#md5=0_correct_md5">foobar-0.1.tar.gz</a><br/>
|
||||
<a href="../../external.html" rel="homepage">external homepage</a><br/>
|
||||
</body></html>
|
||||
@@ -1,14 +0,0 @@
|
||||
import unittest
|
||||
|
||||
try:
|
||||
# provide skipIf for Python 2.4-2.6
|
||||
skipIf = unittest.skipIf
|
||||
except AttributeError:
|
||||
def skipIf(condition, reason):
|
||||
def skipper(func):
|
||||
def skip(*args, **kwargs):
|
||||
return
|
||||
if condition:
|
||||
return skip
|
||||
return func
|
||||
return skipper
|
||||
@@ -1,82 +0,0 @@
|
||||
"""Basic http server for tests to simulate PyPI or custom indexes
|
||||
"""
|
||||
import urllib2
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
import BaseHTTPServer
|
||||
from BaseHTTPServer import HTTPServer
|
||||
from SimpleHTTPServer import SimpleHTTPRequestHandler
|
||||
|
||||
class IndexServer(HTTPServer):
|
||||
"""Basic single-threaded http server simulating a package index
|
||||
|
||||
You can use this server in unittest like this::
|
||||
s = IndexServer()
|
||||
s.start()
|
||||
index_url = s.base_url() + 'mytestindex'
|
||||
# do some test requests to the index
|
||||
# The index files should be located in setuptools/tests/indexes
|
||||
s.stop()
|
||||
"""
|
||||
def __init__(self, server_address=('', 0),
|
||||
RequestHandlerClass=SimpleHTTPRequestHandler):
|
||||
HTTPServer.__init__(self, server_address, RequestHandlerClass)
|
||||
self._run = True
|
||||
|
||||
def serve(self):
|
||||
while self._run:
|
||||
self.handle_request()
|
||||
|
||||
def start(self):
|
||||
self.thread = threading.Thread(target=self.serve)
|
||||
self.thread.start()
|
||||
|
||||
def stop(self):
|
||||
"Stop the server"
|
||||
|
||||
# Let the server finish the last request and wait for a new one.
|
||||
time.sleep(0.1)
|
||||
|
||||
# self.shutdown is not supported on python < 2.6, so just
|
||||
# set _run to false, and make a request, causing it to
|
||||
# terminate.
|
||||
self._run = False
|
||||
url = 'http://127.0.0.1:%(server_port)s/' % vars(self)
|
||||
try:
|
||||
if sys.version_info >= (2, 6):
|
||||
urllib2.urlopen(url, timeout=5)
|
||||
else:
|
||||
urllib2.urlopen(url)
|
||||
except urllib2.URLError:
|
||||
# ignore any errors; all that's important is the request
|
||||
pass
|
||||
self.thread.join()
|
||||
|
||||
def base_url(self):
|
||||
port = self.server_port
|
||||
return 'http://127.0.0.1:%s/setuptools/tests/indexes/' % port
|
||||
|
||||
class RequestRecorder(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
requests = vars(self.server).setdefault('requests', [])
|
||||
requests.append(self)
|
||||
self.send_response(200, 'OK')
|
||||
|
||||
class MockServer(HTTPServer, threading.Thread):
|
||||
"""
|
||||
A simple HTTP Server that records the requests made to it.
|
||||
"""
|
||||
def __init__(self, server_address=('', 0),
|
||||
RequestHandlerClass=RequestRecorder):
|
||||
HTTPServer.__init__(self, server_address, RequestHandlerClass)
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
self.requests = []
|
||||
|
||||
def run(self):
|
||||
self.serve_forever()
|
||||
|
||||
def url(self):
|
||||
return 'http://localhost:%(server_port)s/' % vars(self)
|
||||
url = property(url)
|
||||
@@ -1,69 +0,0 @@
|
||||
"""develop tests
|
||||
"""
|
||||
import sys
|
||||
import os, re, shutil, tempfile, unittest
|
||||
import tempfile
|
||||
import site
|
||||
from StringIO import StringIO
|
||||
|
||||
from distutils.errors import DistutilsError
|
||||
from setuptools.command.bdist_egg import bdist_egg
|
||||
from setuptools.command import easy_install as easy_install_pkg
|
||||
from setuptools.dist import Distribution
|
||||
|
||||
SETUP_PY = """\
|
||||
from setuptools import setup
|
||||
|
||||
setup(name='foo', py_modules=['hi'])
|
||||
"""
|
||||
|
||||
class TestDevelopTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.dir = tempfile.mkdtemp()
|
||||
self.old_cwd = os.getcwd()
|
||||
os.chdir(self.dir)
|
||||
f = open('setup.py', 'w')
|
||||
f.write(SETUP_PY)
|
||||
f.close()
|
||||
f = open('hi.py', 'w')
|
||||
f.write('1\n')
|
||||
f.close()
|
||||
if sys.version >= "2.6":
|
||||
self.old_base = site.USER_BASE
|
||||
site.USER_BASE = tempfile.mkdtemp()
|
||||
self.old_site = site.USER_SITE
|
||||
site.USER_SITE = tempfile.mkdtemp()
|
||||
|
||||
def tearDown(self):
|
||||
os.chdir(self.old_cwd)
|
||||
shutil.rmtree(self.dir)
|
||||
if sys.version >= "2.6":
|
||||
shutil.rmtree(site.USER_BASE)
|
||||
shutil.rmtree(site.USER_SITE)
|
||||
site.USER_BASE = self.old_base
|
||||
site.USER_SITE = self.old_site
|
||||
|
||||
def test_bdist_egg(self):
|
||||
dist = Distribution(dict(
|
||||
script_name='setup.py',
|
||||
script_args=['bdist_egg'],
|
||||
name='foo',
|
||||
py_modules=['hi']
|
||||
))
|
||||
os.makedirs(os.path.join('build', 'src'))
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = o = StringIO()
|
||||
try:
|
||||
dist.parse_command_line()
|
||||
dist.run_commands()
|
||||
finally:
|
||||
sys.stdout = old_stdout
|
||||
|
||||
# let's see if we got our egg link at the right place
|
||||
[content] = os.listdir('dist')
|
||||
self.assertTrue(re.match('foo-0.0.0-py[23].\d.egg$', content))
|
||||
|
||||
def test_suite():
|
||||
return unittest.makeSuite(TestDevelopTest)
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
"""build_ext tests
|
||||
"""
|
||||
import os, shutil, tempfile, unittest
|
||||
from distutils.command.build_ext import build_ext as distutils_build_ext
|
||||
from setuptools.command.build_ext import build_ext
|
||||
from setuptools.dist import Distribution
|
||||
|
||||
class TestBuildExtTest(unittest.TestCase):
|
||||
|
||||
def test_get_ext_filename(self):
|
||||
# setuptools needs to give back the same
|
||||
# result than distutils, even if the fullname
|
||||
# is not in ext_map
|
||||
dist = Distribution()
|
||||
cmd = build_ext(dist)
|
||||
cmd.ext_map['foo/bar'] = ''
|
||||
res = cmd.get_ext_filename('foo')
|
||||
wanted = distutils_build_ext.get_ext_filename(cmd, 'foo')
|
||||
assert res == wanted
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
"""develop tests
|
||||
"""
|
||||
import sys
|
||||
import os, shutil, tempfile, unittest
|
||||
import tempfile
|
||||
import site
|
||||
from StringIO import StringIO
|
||||
|
||||
from distutils.errors import DistutilsError
|
||||
from setuptools.command.develop import develop
|
||||
from setuptools.command import easy_install as easy_install_pkg
|
||||
from setuptools.dist import Distribution
|
||||
|
||||
SETUP_PY = """\
|
||||
from setuptools import setup
|
||||
|
||||
setup(name='foo',
|
||||
packages=['foo'],
|
||||
use_2to3=True,
|
||||
)
|
||||
"""
|
||||
|
||||
INIT_PY = """print "foo"
|
||||
"""
|
||||
|
||||
class TestDevelopTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
if sys.version < "2.6" or hasattr(sys, 'real_prefix'):
|
||||
return
|
||||
|
||||
# Directory structure
|
||||
self.dir = tempfile.mkdtemp()
|
||||
os.mkdir(os.path.join(self.dir, 'foo'))
|
||||
# setup.py
|
||||
setup = os.path.join(self.dir, 'setup.py')
|
||||
f = open(setup, 'w')
|
||||
f.write(SETUP_PY)
|
||||
f.close()
|
||||
self.old_cwd = os.getcwd()
|
||||
# foo/__init__.py
|
||||
init = os.path.join(self.dir, 'foo', '__init__.py')
|
||||
f = open(init, 'w')
|
||||
f.write(INIT_PY)
|
||||
f.close()
|
||||
|
||||
os.chdir(self.dir)
|
||||
self.old_base = site.USER_BASE
|
||||
site.USER_BASE = tempfile.mkdtemp()
|
||||
self.old_site = site.USER_SITE
|
||||
site.USER_SITE = tempfile.mkdtemp()
|
||||
|
||||
def tearDown(self):
|
||||
if sys.version < "2.6" or hasattr(sys, 'real_prefix'):
|
||||
return
|
||||
|
||||
os.chdir(self.old_cwd)
|
||||
shutil.rmtree(self.dir)
|
||||
shutil.rmtree(site.USER_BASE)
|
||||
shutil.rmtree(site.USER_SITE)
|
||||
site.USER_BASE = self.old_base
|
||||
site.USER_SITE = self.old_site
|
||||
|
||||
def test_develop(self):
|
||||
if sys.version < "2.6" or hasattr(sys, 'real_prefix'):
|
||||
return
|
||||
dist = Distribution(
|
||||
dict(name='foo',
|
||||
packages=['foo'],
|
||||
use_2to3=True,
|
||||
version='0.0',
|
||||
))
|
||||
dist.script_name = 'setup.py'
|
||||
cmd = develop(dist)
|
||||
cmd.user = 1
|
||||
cmd.ensure_finalized()
|
||||
cmd.install_dir = site.USER_SITE
|
||||
cmd.user = 1
|
||||
old_stdout = sys.stdout
|
||||
#sys.stdout = StringIO()
|
||||
try:
|
||||
cmd.run()
|
||||
finally:
|
||||
sys.stdout = old_stdout
|
||||
|
||||
# let's see if we got our egg link at the right place
|
||||
content = os.listdir(site.USER_SITE)
|
||||
content.sort()
|
||||
self.assertEqual(content, ['easy-install.pth', 'foo.egg-link'])
|
||||
|
||||
# Check that we are using the right code.
|
||||
path = open(os.path.join(site.USER_SITE, 'foo.egg-link'), 'rt').read().split()[0].strip()
|
||||
init = open(os.path.join(path, 'foo', '__init__.py'), 'rt').read().strip()
|
||||
if sys.version < "3":
|
||||
self.assertEqual(init, 'print "foo"')
|
||||
else:
|
||||
self.assertEqual(init, 'print("foo")')
|
||||
|
||||
def notest_develop_with_setup_requires(self):
|
||||
|
||||
wanted = ("Could not find suitable distribution for "
|
||||
"Requirement.parse('I-DONT-EXIST')")
|
||||
old_dir = os.getcwd()
|
||||
os.chdir(self.dir)
|
||||
try:
|
||||
try:
|
||||
dist = Distribution({'setup_requires': ['I_DONT_EXIST']})
|
||||
except DistutilsError, e:
|
||||
error = str(e)
|
||||
if error == wanted:
|
||||
pass
|
||||
finally:
|
||||
os.chdir(old_dir)
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
"""Test .dist-info style distributions.
|
||||
"""
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
import textwrap
|
||||
|
||||
try:
|
||||
import ast
|
||||
except:
|
||||
pass
|
||||
|
||||
import pkg_resources
|
||||
|
||||
from setuptools.tests.py26compat import skipIf
|
||||
|
||||
def DALS(s):
|
||||
"dedent and left-strip"
|
||||
return textwrap.dedent(s).lstrip()
|
||||
|
||||
class TestDistInfo(unittest.TestCase):
|
||||
|
||||
def test_distinfo(self):
|
||||
dists = {}
|
||||
for d in pkg_resources.find_distributions(self.tmpdir):
|
||||
dists[d.project_name] = d
|
||||
|
||||
assert len(dists) == 2, dists
|
||||
|
||||
unversioned = dists['UnversionedDistribution']
|
||||
versioned = dists['VersionedDistribution']
|
||||
|
||||
assert versioned.version == '2.718' # from filename
|
||||
assert unversioned.version == '0.3' # from METADATA
|
||||
|
||||
@skipIf('ast' not in globals(),
|
||||
"ast is used to test conditional dependencies (Python >= 2.6)")
|
||||
def test_conditional_dependencies(self):
|
||||
requires = [pkg_resources.Requirement.parse('splort==4'),
|
||||
pkg_resources.Requirement.parse('quux>=1.1')]
|
||||
|
||||
for d in pkg_resources.find_distributions(self.tmpdir):
|
||||
self.assertEqual(d.requires(), requires[:1])
|
||||
self.assertEqual(d.requires(extras=('baz',)), requires)
|
||||
self.assertEqual(d.extras, ['baz'])
|
||||
|
||||
def setUp(self):
|
||||
self.tmpdir = tempfile.mkdtemp()
|
||||
versioned = os.path.join(self.tmpdir,
|
||||
'VersionedDistribution-2.718.dist-info')
|
||||
os.mkdir(versioned)
|
||||
metadata_file = open(os.path.join(versioned, 'METADATA'), 'w+')
|
||||
metadata_file.write(DALS(
|
||||
"""
|
||||
Metadata-Version: 1.2
|
||||
Name: VersionedDistribution
|
||||
Requires-Dist: splort (4)
|
||||
Provides-Extra: baz
|
||||
Requires-Dist: quux (>=1.1); extra == 'baz'
|
||||
"""))
|
||||
metadata_file.close()
|
||||
|
||||
unversioned = os.path.join(self.tmpdir,
|
||||
'UnversionedDistribution.dist-info')
|
||||
os.mkdir(unversioned)
|
||||
metadata_file = open(os.path.join(unversioned, 'METADATA'), 'w+')
|
||||
metadata_file.write(DALS(
|
||||
"""
|
||||
Metadata-Version: 1.2
|
||||
Name: UnversionedDistribution
|
||||
Version: 0.3
|
||||
Requires-Dist: splort (==4)
|
||||
Provides-Extra: baz
|
||||
Requires-Dist: quux (>=1.1); extra == 'baz'
|
||||
"""))
|
||||
metadata_file.close()
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tmpdir)
|
||||
@@ -1,526 +0,0 @@
|
||||
"""Easy install Tests
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
import site
|
||||
import textwrap
|
||||
import tarfile
|
||||
import urlparse
|
||||
import StringIO
|
||||
import distutils.core
|
||||
|
||||
from setuptools.sandbox import run_setup, SandboxViolation
|
||||
from setuptools.command.easy_install import easy_install, get_script_args, main
|
||||
from setuptools.command.easy_install import PthDistributions
|
||||
from setuptools.command import easy_install as easy_install_pkg
|
||||
from setuptools.dist import Distribution
|
||||
from pkg_resources import working_set, VersionConflict
|
||||
from pkg_resources import Distribution as PRDistribution
|
||||
import setuptools.tests.server
|
||||
import pkg_resources
|
||||
|
||||
try:
|
||||
# import multiprocessing solely for the purpose of testing its existence
|
||||
__import__('multiprocessing')
|
||||
import logging
|
||||
_LOG = logging.getLogger('test_easy_install')
|
||||
logging.basicConfig(level=logging.INFO, stream=sys.stderr)
|
||||
_MULTIPROC = True
|
||||
except ImportError:
|
||||
_MULTIPROC = False
|
||||
_LOG = None
|
||||
|
||||
class FakeDist(object):
|
||||
def get_entry_map(self, group):
|
||||
if group != 'console_scripts':
|
||||
return {}
|
||||
return {'name': 'ep'}
|
||||
|
||||
def as_requirement(self):
|
||||
return 'spec'
|
||||
|
||||
WANTED = """\
|
||||
#!%s
|
||||
# EASY-INSTALL-ENTRY-SCRIPT: 'spec','console_scripts','name'
|
||||
__requires__ = 'spec'
|
||||
import sys
|
||||
from pkg_resources import load_entry_point
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(
|
||||
load_entry_point('spec', 'console_scripts', 'name')()
|
||||
)
|
||||
""" % sys.executable
|
||||
|
||||
SETUP_PY = """\
|
||||
from setuptools import setup
|
||||
|
||||
setup(name='foo')
|
||||
"""
|
||||
|
||||
class TestEasyInstallTest(unittest.TestCase):
|
||||
|
||||
def test_install_site_py(self):
|
||||
dist = Distribution()
|
||||
cmd = easy_install(dist)
|
||||
cmd.sitepy_installed = False
|
||||
cmd.install_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
cmd.install_site_py()
|
||||
sitepy = os.path.join(cmd.install_dir, 'site.py')
|
||||
self.assertTrue(os.path.exists(sitepy))
|
||||
finally:
|
||||
shutil.rmtree(cmd.install_dir)
|
||||
|
||||
def test_get_script_args(self):
|
||||
dist = FakeDist()
|
||||
|
||||
old_platform = sys.platform
|
||||
try:
|
||||
name, script = [i for i in get_script_args(dist).next()][0:2]
|
||||
finally:
|
||||
sys.platform = old_platform
|
||||
|
||||
self.assertEqual(script, WANTED)
|
||||
|
||||
def test_no_setup_cfg(self):
|
||||
# makes sure easy_install as a command (main)
|
||||
# doesn't use a setup.cfg file that is located
|
||||
# in the current working directory
|
||||
dir = tempfile.mkdtemp()
|
||||
setup_cfg = open(os.path.join(dir, 'setup.cfg'), 'w')
|
||||
setup_cfg.write('[easy_install]\nfind_links = http://example.com')
|
||||
setup_cfg.close()
|
||||
setup_py = open(os.path.join(dir, 'setup.py'), 'w')
|
||||
setup_py.write(SETUP_PY)
|
||||
setup_py.close()
|
||||
|
||||
from setuptools.dist import Distribution
|
||||
|
||||
def _parse_command_line(self):
|
||||
msg = 'Error: a local setup.cfg was used'
|
||||
opts = self.command_options
|
||||
if 'easy_install' in opts:
|
||||
assert 'find_links' not in opts['easy_install'], msg
|
||||
return self._old_parse_command_line()
|
||||
|
||||
Distribution._old_parse_command_line = Distribution.parse_command_line
|
||||
Distribution.parse_command_line = _parse_command_line
|
||||
|
||||
old_wd = os.getcwd()
|
||||
try:
|
||||
os.chdir(dir)
|
||||
reset_setup_stop_context(
|
||||
lambda: self.assertRaises(SystemExit, main, [])
|
||||
)
|
||||
finally:
|
||||
os.chdir(old_wd)
|
||||
shutil.rmtree(dir)
|
||||
Distribution.parse_command_line = Distribution._old_parse_command_line
|
||||
|
||||
def test_no_find_links(self):
|
||||
# new option '--no-find-links', that blocks find-links added at
|
||||
# the project level
|
||||
dist = Distribution()
|
||||
cmd = easy_install(dist)
|
||||
cmd.check_pth_processing = lambda: True
|
||||
cmd.no_find_links = True
|
||||
cmd.find_links = ['link1', 'link2']
|
||||
cmd.install_dir = os.path.join(tempfile.mkdtemp(), 'ok')
|
||||
cmd.args = ['ok']
|
||||
cmd.ensure_finalized()
|
||||
self.assertEqual(cmd.package_index.scanned_urls, {})
|
||||
|
||||
# let's try without it (default behavior)
|
||||
cmd = easy_install(dist)
|
||||
cmd.check_pth_processing = lambda: True
|
||||
cmd.find_links = ['link1', 'link2']
|
||||
cmd.install_dir = os.path.join(tempfile.mkdtemp(), 'ok')
|
||||
cmd.args = ['ok']
|
||||
cmd.ensure_finalized()
|
||||
keys = cmd.package_index.scanned_urls.keys()
|
||||
keys.sort()
|
||||
self.assertEqual(keys, ['link1', 'link2'])
|
||||
|
||||
|
||||
class TestPTHFileWriter(unittest.TestCase):
|
||||
def test_add_from_cwd_site_sets_dirty(self):
|
||||
'''a pth file manager should set dirty
|
||||
if a distribution is in site but also the cwd
|
||||
'''
|
||||
pth = PthDistributions('does-not_exist', [os.getcwd()])
|
||||
self.assertTrue(not pth.dirty)
|
||||
pth.add(PRDistribution(os.getcwd()))
|
||||
self.assertTrue(pth.dirty)
|
||||
|
||||
def test_add_from_site_is_ignored(self):
|
||||
if os.name != 'nt':
|
||||
location = '/test/location/does-not-have-to-exist'
|
||||
else:
|
||||
location = 'c:\\does_not_exist'
|
||||
pth = PthDistributions('does-not_exist', [location, ])
|
||||
self.assertTrue(not pth.dirty)
|
||||
pth.add(PRDistribution(location))
|
||||
self.assertTrue(not pth.dirty)
|
||||
|
||||
|
||||
class TestUserInstallTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.dir = tempfile.mkdtemp()
|
||||
setup = os.path.join(self.dir, 'setup.py')
|
||||
f = open(setup, 'w')
|
||||
f.write(SETUP_PY)
|
||||
f.close()
|
||||
self.old_cwd = os.getcwd()
|
||||
os.chdir(self.dir)
|
||||
if sys.version >= "2.6":
|
||||
self.old_has_site = easy_install_pkg.HAS_USER_SITE
|
||||
self.old_file = easy_install_pkg.__file__
|
||||
self.old_base = site.USER_BASE
|
||||
site.USER_BASE = tempfile.mkdtemp()
|
||||
self.old_site = site.USER_SITE
|
||||
site.USER_SITE = tempfile.mkdtemp()
|
||||
easy_install_pkg.__file__ = site.USER_SITE
|
||||
|
||||
def tearDown(self):
|
||||
os.chdir(self.old_cwd)
|
||||
shutil.rmtree(self.dir)
|
||||
if sys.version >= "2.6":
|
||||
shutil.rmtree(site.USER_BASE)
|
||||
shutil.rmtree(site.USER_SITE)
|
||||
site.USER_BASE = self.old_base
|
||||
site.USER_SITE = self.old_site
|
||||
easy_install_pkg.HAS_USER_SITE = self.old_has_site
|
||||
easy_install_pkg.__file__ = self.old_file
|
||||
|
||||
def test_user_install_implied(self):
|
||||
easy_install_pkg.HAS_USER_SITE = True # disabled sometimes
|
||||
#XXX: replace with something meaningfull
|
||||
if sys.version < "2.6":
|
||||
return #SKIP
|
||||
dist = Distribution()
|
||||
dist.script_name = 'setup.py'
|
||||
cmd = easy_install(dist)
|
||||
cmd.args = ['py']
|
||||
cmd.ensure_finalized()
|
||||
self.assertTrue(cmd.user, 'user should be implied')
|
||||
|
||||
def test_multiproc_atexit(self):
|
||||
if not _MULTIPROC:
|
||||
return
|
||||
_LOG.info('this should not break')
|
||||
|
||||
def test_user_install_not_implied_without_usersite_enabled(self):
|
||||
easy_install_pkg.HAS_USER_SITE = False # usually enabled
|
||||
#XXX: replace with something meaningfull
|
||||
if sys.version < "2.6":
|
||||
return #SKIP
|
||||
dist = Distribution()
|
||||
dist.script_name = 'setup.py'
|
||||
cmd = easy_install(dist)
|
||||
cmd.args = ['py']
|
||||
cmd.initialize_options()
|
||||
self.assertFalse(cmd.user, 'NOT user should be implied')
|
||||
|
||||
def test_local_index(self):
|
||||
# make sure the local index is used
|
||||
# when easy_install looks for installed
|
||||
# packages
|
||||
new_location = tempfile.mkdtemp()
|
||||
target = tempfile.mkdtemp()
|
||||
egg_file = os.path.join(new_location, 'foo-1.0.egg-info')
|
||||
f = open(egg_file, 'w')
|
||||
try:
|
||||
f.write('Name: foo\n')
|
||||
except:
|
||||
f.close()
|
||||
|
||||
sys.path.append(target)
|
||||
old_ppath = os.environ.get('PYTHONPATH')
|
||||
os.environ['PYTHONPATH'] = os.path.pathsep.join(sys.path)
|
||||
try:
|
||||
dist = Distribution()
|
||||
dist.script_name = 'setup.py'
|
||||
cmd = easy_install(dist)
|
||||
cmd.install_dir = target
|
||||
cmd.args = ['foo']
|
||||
cmd.ensure_finalized()
|
||||
cmd.local_index.scan([new_location])
|
||||
res = cmd.easy_install('foo')
|
||||
self.assertEqual(os.path.realpath(res.location),
|
||||
os.path.realpath(new_location))
|
||||
finally:
|
||||
sys.path.remove(target)
|
||||
for basedir in [new_location, target, ]:
|
||||
if not os.path.exists(basedir) or not os.path.isdir(basedir):
|
||||
continue
|
||||
try:
|
||||
shutil.rmtree(basedir)
|
||||
except:
|
||||
pass
|
||||
if old_ppath is not None:
|
||||
os.environ['PYTHONPATH'] = old_ppath
|
||||
else:
|
||||
del os.environ['PYTHONPATH']
|
||||
|
||||
def test_setup_requires(self):
|
||||
"""Regression test for issue #318
|
||||
|
||||
Ensures that a package with setup_requires can be installed when
|
||||
distribute is installed in the user site-packages without causing a
|
||||
SandboxViolation.
|
||||
"""
|
||||
|
||||
test_pkg = create_setup_requires_package(self.dir)
|
||||
test_setup_py = os.path.join(test_pkg, 'setup.py')
|
||||
|
||||
try:
|
||||
quiet_context(
|
||||
lambda: reset_setup_stop_context(
|
||||
lambda: run_setup(test_setup_py, ['install'])
|
||||
))
|
||||
except SandboxViolation:
|
||||
self.fail('Installation caused SandboxViolation')
|
||||
|
||||
|
||||
class TestSetupRequires(unittest.TestCase):
|
||||
|
||||
def test_setup_requires_honors_fetch_params(self):
|
||||
"""
|
||||
When easy_install installs a source distribution which specifies
|
||||
setup_requires, it should honor the fetch parameters (such as
|
||||
allow-hosts, index-url, and find-links).
|
||||
"""
|
||||
# set up a server which will simulate an alternate package index.
|
||||
p_index = setuptools.tests.server.MockServer()
|
||||
p_index.start()
|
||||
netloc = 1
|
||||
p_index_loc = urlparse.urlparse(p_index.url)[netloc]
|
||||
if p_index_loc.endswith(':0'):
|
||||
# Some platforms (Jython) don't find a port to which to bind,
|
||||
# so skip this test for them.
|
||||
return
|
||||
|
||||
# I realize this is all-but-impossible to read, because it was
|
||||
# ported from some well-factored, safe code using 'with'. If you
|
||||
# need to maintain this code, consider making the changes in
|
||||
# the parent revision (of this comment) and then port the changes
|
||||
# back for Python 2.4 (or deprecate Python 2.4).
|
||||
|
||||
def install(dist_file):
|
||||
def install_at(temp_install_dir):
|
||||
def install_env():
|
||||
ei_params = ['--index-url', p_index.url,
|
||||
'--allow-hosts', p_index_loc,
|
||||
'--exclude-scripts', '--install-dir', temp_install_dir,
|
||||
dist_file]
|
||||
def install_clean_reset():
|
||||
def install_clean_argv():
|
||||
# attempt to install the dist. It should fail because
|
||||
# it doesn't exist.
|
||||
self.assertRaises(SystemExit,
|
||||
easy_install_pkg.main, ei_params)
|
||||
argv_context(install_clean_argv, ['easy_install'])
|
||||
reset_setup_stop_context(install_clean_reset)
|
||||
environment_context(install_env, PYTHONPATH=temp_install_dir)
|
||||
tempdir_context(install_at)
|
||||
|
||||
# create an sdist that has a build-time dependency.
|
||||
quiet_context(lambda: self.create_sdist(install))
|
||||
|
||||
# there should have been two or three requests to the server
|
||||
# (three happens on Python 3.3a)
|
||||
self.assertTrue(2 <= len(p_index.requests) <= 3)
|
||||
self.assertEqual(p_index.requests[0].path, '/does-not-exist/')
|
||||
|
||||
def create_sdist(self, installer):
|
||||
"""
|
||||
Create an sdist with a setup_requires dependency (of something that
|
||||
doesn't exist) and invoke installer on it.
|
||||
"""
|
||||
def build_sdist(dir):
|
||||
dist_path = os.path.join(dir, 'distribute-test-fetcher-1.0.tar.gz')
|
||||
make_trivial_sdist(
|
||||
dist_path,
|
||||
textwrap.dedent("""
|
||||
import setuptools
|
||||
setuptools.setup(
|
||||
name="distribute-test-fetcher",
|
||||
version="1.0",
|
||||
setup_requires = ['does-not-exist'],
|
||||
)
|
||||
""").lstrip())
|
||||
installer(dist_path)
|
||||
tempdir_context(build_sdist)
|
||||
|
||||
def test_setup_requires_overrides_version_conflict(self):
|
||||
"""
|
||||
Regression test for issue #323.
|
||||
|
||||
Ensures that a distribution's setup_requires requirements can still be
|
||||
installed and used locally even if a conflicting version of that
|
||||
requirement is already on the path.
|
||||
"""
|
||||
|
||||
pr_state = pkg_resources.__getstate__()
|
||||
fake_dist = PRDistribution('does-not-matter', project_name='foobar',
|
||||
version='0.0')
|
||||
working_set.add(fake_dist)
|
||||
|
||||
def setup_and_run(temp_dir):
|
||||
test_pkg = create_setup_requires_package(temp_dir)
|
||||
test_setup_py = os.path.join(test_pkg, 'setup.py')
|
||||
try:
|
||||
stdout, stderr = quiet_context(
|
||||
lambda: reset_setup_stop_context(
|
||||
# Don't even need to install the package, just running
|
||||
# the setup.py at all is sufficient
|
||||
lambda: run_setup(test_setup_py, ['--name'])
|
||||
))
|
||||
except VersionConflict:
|
||||
self.fail('Installing setup.py requirements caused '
|
||||
'VersionConflict')
|
||||
|
||||
lines = stdout.splitlines()
|
||||
self.assertGreater(len(lines), 0)
|
||||
self.assert_(lines[-1].strip(), 'test_pkg')
|
||||
|
||||
try:
|
||||
tempdir_context(setup_and_run)
|
||||
finally:
|
||||
pkg_resources.__setstate__(pr_state)
|
||||
|
||||
|
||||
def create_setup_requires_package(path):
|
||||
"""Creates a source tree under path for a trivial test package that has a
|
||||
single requirement in setup_requires--a tarball for that requirement is
|
||||
also created and added to the dependency_links argument.
|
||||
"""
|
||||
|
||||
test_setup_attrs = {
|
||||
'name': 'test_pkg', 'version': '0.0',
|
||||
'setup_requires': ['foobar==0.1'],
|
||||
'dependency_links': [os.path.abspath(path)]
|
||||
}
|
||||
|
||||
test_pkg = os.path.join(path, 'test_pkg')
|
||||
test_setup_py = os.path.join(test_pkg, 'setup.py')
|
||||
test_setup_cfg = os.path.join(test_pkg, 'setup.cfg')
|
||||
os.mkdir(test_pkg)
|
||||
|
||||
f = open(test_setup_py, 'w')
|
||||
f.write(textwrap.dedent("""\
|
||||
import setuptools
|
||||
setuptools.setup(**%r)
|
||||
""" % test_setup_attrs))
|
||||
f.close()
|
||||
|
||||
foobar_path = os.path.join(path, 'foobar-0.1.tar.gz')
|
||||
make_trivial_sdist(
|
||||
foobar_path,
|
||||
textwrap.dedent("""\
|
||||
import setuptools
|
||||
setuptools.setup(
|
||||
name='foobar',
|
||||
version='0.1'
|
||||
)
|
||||
"""))
|
||||
|
||||
return test_pkg
|
||||
|
||||
|
||||
def make_trivial_sdist(dist_path, setup_py):
|
||||
"""Create a simple sdist tarball at dist_path, containing just a
|
||||
setup.py, the contents of which are provided by the setup_py string.
|
||||
"""
|
||||
|
||||
setup_py_file = tarfile.TarInfo(name='setup.py')
|
||||
try:
|
||||
# Python 3 (StringIO gets converted to io module)
|
||||
MemFile = StringIO.BytesIO
|
||||
except AttributeError:
|
||||
MemFile = StringIO.StringIO
|
||||
setup_py_bytes = MemFile(setup_py.encode('utf-8'))
|
||||
setup_py_file.size = len(setup_py_bytes.getvalue())
|
||||
dist = tarfile.open(dist_path, 'w:gz')
|
||||
try:
|
||||
dist.addfile(setup_py_file, fileobj=setup_py_bytes)
|
||||
finally:
|
||||
dist.close()
|
||||
|
||||
|
||||
def tempdir_context(f, cd=lambda dir:None):
|
||||
"""
|
||||
Invoke f in the context
|
||||
"""
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
orig_dir = os.getcwd()
|
||||
try:
|
||||
cd(temp_dir)
|
||||
f(temp_dir)
|
||||
finally:
|
||||
cd(orig_dir)
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
|
||||
def environment_context(f, **updates):
|
||||
"""
|
||||
Invoke f in the context
|
||||
"""
|
||||
old_env = os.environ.copy()
|
||||
os.environ.update(updates)
|
||||
try:
|
||||
f()
|
||||
finally:
|
||||
for key in updates:
|
||||
del os.environ[key]
|
||||
os.environ.update(old_env)
|
||||
|
||||
|
||||
def argv_context(f, repl):
|
||||
"""
|
||||
Invoke f in the context
|
||||
"""
|
||||
old_argv = sys.argv[:]
|
||||
sys.argv[:] = repl
|
||||
try:
|
||||
f()
|
||||
finally:
|
||||
sys.argv[:] = old_argv
|
||||
|
||||
|
||||
def reset_setup_stop_context(f):
|
||||
"""
|
||||
When the distribute tests are run using setup.py test, and then
|
||||
one wants to invoke another setup() command (such as easy_install)
|
||||
within those tests, it's necessary to reset the global variable
|
||||
in distutils.core so that the setup() command will run naturally.
|
||||
"""
|
||||
setup_stop_after = distutils.core._setup_stop_after
|
||||
distutils.core._setup_stop_after = None
|
||||
try:
|
||||
f()
|
||||
finally:
|
||||
distutils.core._setup_stop_after = setup_stop_after
|
||||
|
||||
|
||||
def quiet_context(f):
|
||||
"""
|
||||
Redirect stdout/stderr to StringIO objects to prevent console output from
|
||||
distutils commands.
|
||||
"""
|
||||
|
||||
old_stdout = sys.stdout
|
||||
old_stderr = sys.stderr
|
||||
new_stdout = sys.stdout = StringIO.StringIO()
|
||||
new_stderr = sys.stderr = StringIO.StringIO()
|
||||
try:
|
||||
f()
|
||||
finally:
|
||||
sys.stdout = old_stdout
|
||||
sys.stderr = old_stderr
|
||||
return new_stdout.getvalue(), new_stderr.getvalue()
|
||||
@@ -1,64 +0,0 @@
|
||||
import os
|
||||
import unittest
|
||||
from setuptools.tests.py26compat import skipIf
|
||||
|
||||
try:
|
||||
import ast
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
class TestMarkerlib(unittest.TestCase):
|
||||
|
||||
@skipIf('ast' not in globals(),
|
||||
"ast not available (Python < 2.6?)")
|
||||
def test_markers(self):
|
||||
from _markerlib import interpret, default_environment, compile
|
||||
|
||||
os_name = os.name
|
||||
|
||||
self.assert_(interpret(""))
|
||||
|
||||
self.assert_(interpret("os.name != 'buuuu'"))
|
||||
self.assert_(interpret("python_version > '1.0'"))
|
||||
self.assert_(interpret("python_version < '5.0'"))
|
||||
self.assert_(interpret("python_version <= '5.0'"))
|
||||
self.assert_(interpret("python_version >= '1.0'"))
|
||||
self.assert_(interpret("'%s' in os.name" % os_name))
|
||||
self.assert_(interpret("'buuuu' not in os.name"))
|
||||
|
||||
self.assertFalse(interpret("os.name == 'buuuu'"))
|
||||
self.assertFalse(interpret("python_version < '1.0'"))
|
||||
self.assertFalse(interpret("python_version > '5.0'"))
|
||||
self.assertFalse(interpret("python_version >= '5.0'"))
|
||||
self.assertFalse(interpret("python_version <= '1.0'"))
|
||||
self.assertFalse(interpret("'%s' not in os.name" % os_name))
|
||||
self.assertFalse(interpret("'buuuu' in os.name and python_version >= '5.0'"))
|
||||
|
||||
environment = default_environment()
|
||||
environment['extra'] = 'test'
|
||||
self.assert_(interpret("extra == 'test'", environment))
|
||||
self.assertFalse(interpret("extra == 'doc'", environment))
|
||||
|
||||
def raises_nameError():
|
||||
try:
|
||||
interpret("python.version == '42'")
|
||||
except NameError:
|
||||
pass
|
||||
else:
|
||||
raise Exception("Expected NameError")
|
||||
|
||||
raises_nameError()
|
||||
|
||||
def raises_syntaxError():
|
||||
try:
|
||||
interpret("(x for x in (4,))")
|
||||
except SyntaxError:
|
||||
pass
|
||||
else:
|
||||
raise Exception("Expected SyntaxError")
|
||||
|
||||
raises_syntaxError()
|
||||
|
||||
statement = "python_version == '5'"
|
||||
self.assertEqual(compile(statement).__doc__, statement)
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
"""Package Index Tests
|
||||
"""
|
||||
import sys
|
||||
import unittest
|
||||
import urllib2
|
||||
import pkg_resources
|
||||
import httplib
|
||||
import distutils.errors
|
||||
import setuptools.package_index
|
||||
from server import IndexServer
|
||||
|
||||
class TestPackageIndex(unittest.TestCase):
|
||||
|
||||
def test_bad_url_bad_port(self):
|
||||
index = setuptools.package_index.PackageIndex()
|
||||
url = 'http://127.0.0.1:0/nonesuch/test_package_index'
|
||||
try:
|
||||
v = index.open_url(url)
|
||||
except Exception, v:
|
||||
self.assertTrue(url in str(v))
|
||||
else:
|
||||
self.assertTrue(isinstance(v,urllib2.HTTPError))
|
||||
|
||||
def test_bad_url_typo(self):
|
||||
# issue 16
|
||||
# easy_install inquant.contentmirror.plone breaks because of a typo
|
||||
# in its home URL
|
||||
index = setuptools.package_index.PackageIndex(
|
||||
hosts=('www.example.com',)
|
||||
)
|
||||
|
||||
url = 'url:%20https://svn.plone.org/svn/collective/inquant.contentmirror.plone/trunk'
|
||||
try:
|
||||
v = index.open_url(url)
|
||||
except Exception, v:
|
||||
self.assertTrue(url in str(v))
|
||||
else:
|
||||
self.assertTrue(isinstance(v, urllib2.HTTPError))
|
||||
|
||||
def test_bad_url_bad_status_line(self):
|
||||
index = setuptools.package_index.PackageIndex(
|
||||
hosts=('www.example.com',)
|
||||
)
|
||||
|
||||
def _urlopen(*args):
|
||||
import httplib
|
||||
raise httplib.BadStatusLine('line')
|
||||
|
||||
old_urlopen = urllib2.urlopen
|
||||
urllib2.urlopen = _urlopen
|
||||
url = 'http://example.com'
|
||||
try:
|
||||
try:
|
||||
v = index.open_url(url)
|
||||
except Exception, v:
|
||||
self.assertTrue('line' in str(v))
|
||||
else:
|
||||
raise AssertionError('Should have raise here!')
|
||||
finally:
|
||||
urllib2.urlopen = old_urlopen
|
||||
|
||||
def test_bad_url_double_scheme(self):
|
||||
"""
|
||||
A bad URL with a double scheme should raise a DistutilsError.
|
||||
"""
|
||||
index = setuptools.package_index.PackageIndex(
|
||||
hosts=('www.example.com',)
|
||||
)
|
||||
|
||||
# issue 20
|
||||
url = 'http://http://svn.pythonpaste.org/Paste/wphp/trunk'
|
||||
try:
|
||||
index.open_url(url)
|
||||
except distutils.errors.DistutilsError, error:
|
||||
msg = unicode(error)
|
||||
assert 'nonnumeric port' in msg or 'getaddrinfo failed' in msg or 'Name or service not known' in msg
|
||||
return
|
||||
raise RuntimeError("Did not raise")
|
||||
|
||||
def test_bad_url_screwy_href(self):
|
||||
index = setuptools.package_index.PackageIndex(
|
||||
hosts=('www.example.com',)
|
||||
)
|
||||
|
||||
# issue #160
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] == 7:
|
||||
# this should not fail
|
||||
url = 'http://example.com'
|
||||
page = ('<a href="http://www.famfamfam.com]('
|
||||
'http://www.famfamfam.com/">')
|
||||
index.process_index(url, page)
|
||||
|
||||
def test_url_ok(self):
|
||||
index = setuptools.package_index.PackageIndex(
|
||||
hosts=('www.example.com',)
|
||||
)
|
||||
url = 'file:///tmp/test_package_index'
|
||||
self.assertTrue(index.url_ok(url, True))
|
||||
|
||||
def test_links_priority(self):
|
||||
"""
|
||||
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 copies 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
|
||||
-> Distribute should use the link from pypi, not the external one.
|
||||
"""
|
||||
if sys.platform.startswith('java'):
|
||||
# Skip this test on jython because binding to :0 fails
|
||||
return
|
||||
|
||||
# start an index server
|
||||
server = IndexServer()
|
||||
server.start()
|
||||
index_url = server.base_url() + 'test_links_priority/simple/'
|
||||
|
||||
# scan a test index
|
||||
pi = setuptools.package_index.PackageIndex(index_url)
|
||||
requirement = pkg_resources.Requirement.parse('foobar')
|
||||
pi.find_packages(requirement)
|
||||
server.stop()
|
||||
|
||||
# the distribution has been found
|
||||
self.assertTrue('foobar' in pi)
|
||||
# we have only one link, because links are compared without md5
|
||||
self.assertTrue(len(pi['foobar'])==1)
|
||||
# the link should be from the index
|
||||
self.assertTrue('correct_md5' in pi['foobar'][0].location)
|
||||
|
||||
def test_parse_bdist_wininst(self):
|
||||
self.assertEqual(setuptools.package_index.parse_bdist_wininst(
|
||||
'reportlab-2.5.win32-py2.4.exe'), ('reportlab-2.5', '2.4', 'win32'))
|
||||
self.assertEqual(setuptools.package_index.parse_bdist_wininst(
|
||||
'reportlab-2.5.win32.exe'), ('reportlab-2.5', None, 'win32'))
|
||||
self.assertEqual(setuptools.package_index.parse_bdist_wininst(
|
||||
'reportlab-2.5.win-amd64-py2.7.exe'), ('reportlab-2.5', '2.7', 'win-amd64'))
|
||||
self.assertEqual(setuptools.package_index.parse_bdist_wininst(
|
||||
'reportlab-2.5.win-amd64.exe'), ('reportlab-2.5', None, 'win-amd64'))
|
||||
@@ -1,649 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
# NOTE: the shebang and encoding lines are for ScriptHeaderTests; do not remove
|
||||
from unittest import TestCase, makeSuite; from pkg_resources import *
|
||||
from setuptools.command.easy_install import get_script_header, is_sh
|
||||
import os, pkg_resources, sys, StringIO, tempfile, shutil
|
||||
try: frozenset
|
||||
except NameError:
|
||||
from sets import ImmutableSet as frozenset
|
||||
|
||||
def safe_repr(obj, short=False):
|
||||
""" copied from Python2.7"""
|
||||
try:
|
||||
result = repr(obj)
|
||||
except Exception:
|
||||
result = object.__repr__(obj)
|
||||
if not short or len(result) < _MAX_LENGTH:
|
||||
return result
|
||||
return result[:_MAX_LENGTH] + ' [truncated]...'
|
||||
|
||||
class Metadata(EmptyProvider):
|
||||
"""Mock object to return metadata as if from an on-disk distribution"""
|
||||
|
||||
def __init__(self,*pairs):
|
||||
self.metadata = dict(pairs)
|
||||
|
||||
def has_metadata(self,name):
|
||||
return name in self.metadata
|
||||
|
||||
def get_metadata(self,name):
|
||||
return self.metadata[name]
|
||||
|
||||
def get_metadata_lines(self,name):
|
||||
return yield_lines(self.get_metadata(name))
|
||||
|
||||
class DistroTests(TestCase):
|
||||
|
||||
def testCollection(self):
|
||||
# empty path should produce no distributions
|
||||
ad = Environment([], platform=None, python=None)
|
||||
self.assertEqual(list(ad), [])
|
||||
self.assertEqual(ad['FooPkg'],[])
|
||||
ad.add(Distribution.from_filename("FooPkg-1.3_1.egg"))
|
||||
ad.add(Distribution.from_filename("FooPkg-1.4-py2.4-win32.egg"))
|
||||
ad.add(Distribution.from_filename("FooPkg-1.2-py2.4.egg"))
|
||||
|
||||
# Name is in there now
|
||||
self.assertTrue(ad['FooPkg'])
|
||||
# But only 1 package
|
||||
self.assertEqual(list(ad), ['foopkg'])
|
||||
|
||||
# Distributions sort by version
|
||||
self.assertEqual(
|
||||
[dist.version for dist in ad['FooPkg']], ['1.4','1.3-1','1.2']
|
||||
)
|
||||
# Removing a distribution leaves sequence alone
|
||||
ad.remove(ad['FooPkg'][1])
|
||||
self.assertEqual(
|
||||
[dist.version for dist in ad['FooPkg']], ['1.4','1.2']
|
||||
)
|
||||
# And inserting adds them in order
|
||||
ad.add(Distribution.from_filename("FooPkg-1.9.egg"))
|
||||
self.assertEqual(
|
||||
[dist.version for dist in ad['FooPkg']], ['1.9','1.4','1.2']
|
||||
)
|
||||
|
||||
ws = WorkingSet([])
|
||||
foo12 = Distribution.from_filename("FooPkg-1.2-py2.4.egg")
|
||||
foo14 = Distribution.from_filename("FooPkg-1.4-py2.4-win32.egg")
|
||||
req, = parse_requirements("FooPkg>=1.3")
|
||||
|
||||
# Nominal case: no distros on path, should yield all applicable
|
||||
self.assertEqual(ad.best_match(req,ws).version, '1.9')
|
||||
# If a matching distro is already installed, should return only that
|
||||
ws.add(foo14); self.assertEqual(ad.best_match(req,ws).version, '1.4')
|
||||
|
||||
# If the first matching distro is unsuitable, it's a version conflict
|
||||
ws = WorkingSet([]); ws.add(foo12); ws.add(foo14)
|
||||
self.assertRaises(VersionConflict, ad.best_match, req, ws)
|
||||
|
||||
# If more than one match on the path, the first one takes precedence
|
||||
ws = WorkingSet([]); ws.add(foo14); ws.add(foo12); ws.add(foo14);
|
||||
self.assertEqual(ad.best_match(req,ws).version, '1.4')
|
||||
|
||||
def checkFooPkg(self,d):
|
||||
self.assertEqual(d.project_name, "FooPkg")
|
||||
self.assertEqual(d.key, "foopkg")
|
||||
self.assertEqual(d.version, "1.3-1")
|
||||
self.assertEqual(d.py_version, "2.4")
|
||||
self.assertEqual(d.platform, "win32")
|
||||
self.assertEqual(d.parsed_version, parse_version("1.3-1"))
|
||||
|
||||
def testDistroBasics(self):
|
||||
d = Distribution(
|
||||
"/some/path",
|
||||
project_name="FooPkg",version="1.3-1",py_version="2.4",platform="win32"
|
||||
)
|
||||
self.checkFooPkg(d)
|
||||
|
||||
d = Distribution("/some/path")
|
||||
self.assertEqual(d.py_version, sys.version[:3])
|
||||
self.assertEqual(d.platform, None)
|
||||
|
||||
def testDistroParse(self):
|
||||
d = Distribution.from_filename("FooPkg-1.3_1-py2.4-win32.egg")
|
||||
self.checkFooPkg(d)
|
||||
d = Distribution.from_filename("FooPkg-1.3_1-py2.4-win32.egg-info")
|
||||
self.checkFooPkg(d)
|
||||
|
||||
def testDistroMetadata(self):
|
||||
d = Distribution(
|
||||
"/some/path", project_name="FooPkg", py_version="2.4", platform="win32",
|
||||
metadata = Metadata(
|
||||
('PKG-INFO',"Metadata-Version: 1.0\nVersion: 1.3-1\n")
|
||||
)
|
||||
)
|
||||
self.checkFooPkg(d)
|
||||
|
||||
|
||||
def distRequires(self, txt):
|
||||
return Distribution("/foo", metadata=Metadata(('depends.txt', txt)))
|
||||
|
||||
def checkRequires(self, dist, txt, extras=()):
|
||||
self.assertEqual(
|
||||
list(dist.requires(extras)),
|
||||
list(parse_requirements(txt))
|
||||
)
|
||||
|
||||
def testDistroDependsSimple(self):
|
||||
for v in "Twisted>=1.5", "Twisted>=1.5\nZConfig>=2.0":
|
||||
self.checkRequires(self.distRequires(v), v)
|
||||
|
||||
|
||||
def testResolve(self):
|
||||
ad = Environment([]); ws = WorkingSet([])
|
||||
# Resolving no requirements -> nothing to install
|
||||
self.assertEqual( list(ws.resolve([],ad)), [] )
|
||||
# Request something not in the collection -> DistributionNotFound
|
||||
self.assertRaises(
|
||||
DistributionNotFound, ws.resolve, parse_requirements("Foo"), ad
|
||||
)
|
||||
Foo = Distribution.from_filename(
|
||||
"/foo_dir/Foo-1.2.egg",
|
||||
metadata=Metadata(('depends.txt', "[bar]\nBaz>=2.0"))
|
||||
)
|
||||
ad.add(Foo); ad.add(Distribution.from_filename("Foo-0.9.egg"))
|
||||
|
||||
# Request thing(s) that are available -> list to activate
|
||||
for i in range(3):
|
||||
targets = list(ws.resolve(parse_requirements("Foo"), ad))
|
||||
self.assertEqual(targets, [Foo])
|
||||
map(ws.add,targets)
|
||||
self.assertRaises(VersionConflict, ws.resolve,
|
||||
parse_requirements("Foo==0.9"), ad)
|
||||
ws = WorkingSet([]) # reset
|
||||
|
||||
# Request an extra that causes an unresolved dependency for "Baz"
|
||||
self.assertRaises(
|
||||
DistributionNotFound, ws.resolve,parse_requirements("Foo[bar]"), ad
|
||||
)
|
||||
Baz = Distribution.from_filename(
|
||||
"/foo_dir/Baz-2.1.egg", metadata=Metadata(('depends.txt', "Foo"))
|
||||
)
|
||||
ad.add(Baz)
|
||||
|
||||
# Activation list now includes resolved dependency
|
||||
self.assertEqual(
|
||||
list(ws.resolve(parse_requirements("Foo[bar]"), ad)), [Foo,Baz]
|
||||
)
|
||||
# Requests for conflicting versions produce VersionConflict
|
||||
self.assertRaises( VersionConflict,
|
||||
ws.resolve, parse_requirements("Foo==1.2\nFoo!=1.2"), ad
|
||||
)
|
||||
|
||||
def testDistroDependsOptions(self):
|
||||
d = self.distRequires("""
|
||||
Twisted>=1.5
|
||||
[docgen]
|
||||
ZConfig>=2.0
|
||||
docutils>=0.3
|
||||
[fastcgi]
|
||||
fcgiapp>=0.1""")
|
||||
self.checkRequires(d,"Twisted>=1.5")
|
||||
self.checkRequires(
|
||||
d,"Twisted>=1.5 ZConfig>=2.0 docutils>=0.3".split(), ["docgen"]
|
||||
)
|
||||
self.checkRequires(
|
||||
d,"Twisted>=1.5 fcgiapp>=0.1".split(), ["fastcgi"]
|
||||
)
|
||||
self.checkRequires(
|
||||
d,"Twisted>=1.5 ZConfig>=2.0 docutils>=0.3 fcgiapp>=0.1".split(),
|
||||
["docgen","fastcgi"]
|
||||
)
|
||||
self.checkRequires(
|
||||
d,"Twisted>=1.5 fcgiapp>=0.1 ZConfig>=2.0 docutils>=0.3".split(),
|
||||
["fastcgi", "docgen"]
|
||||
)
|
||||
self.assertRaises(UnknownExtra, d.requires, ["foo"])
|
||||
|
||||
def testSetuptoolsDistributeCombination(self):
|
||||
# Ensure that installing a 0.7-series setuptools fails. PJE says that
|
||||
# it will not co-exist.
|
||||
ws = WorkingSet([])
|
||||
d = Distribution(
|
||||
"/some/path",
|
||||
project_name="setuptools",
|
||||
version="0.7a1")
|
||||
self.assertRaises(ValueError, ws.add, d)
|
||||
# A 0.6-series is no problem
|
||||
d2 = Distribution(
|
||||
"/some/path",
|
||||
project_name="setuptools",
|
||||
version="0.6c9")
|
||||
ws.add(d2)
|
||||
|
||||
# a unexisting version needs to work
|
||||
ws = WorkingSet([])
|
||||
d3 = Distribution(
|
||||
"/some/path",
|
||||
project_name="setuptools")
|
||||
ws.add(d3)
|
||||
|
||||
|
||||
class EntryPointTests(TestCase):
|
||||
|
||||
def assertfields(self, ep):
|
||||
self.assertEqual(ep.name,"foo")
|
||||
self.assertEqual(ep.module_name,"setuptools.tests.test_resources")
|
||||
self.assertEqual(ep.attrs, ("EntryPointTests",))
|
||||
self.assertEqual(ep.extras, ("x",))
|
||||
self.assertTrue(ep.load() is EntryPointTests)
|
||||
self.assertEqual(
|
||||
str(ep),
|
||||
"foo = setuptools.tests.test_resources:EntryPointTests [x]"
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
self.dist = Distribution.from_filename(
|
||||
"FooPkg-1.2-py2.4.egg", metadata=Metadata(('requires.txt','[x]')))
|
||||
|
||||
def testBasics(self):
|
||||
ep = EntryPoint(
|
||||
"foo", "setuptools.tests.test_resources", ["EntryPointTests"],
|
||||
["x"], self.dist
|
||||
)
|
||||
self.assertfields(ep)
|
||||
|
||||
def testParse(self):
|
||||
s = "foo = setuptools.tests.test_resources:EntryPointTests [x]"
|
||||
ep = EntryPoint.parse(s, self.dist)
|
||||
self.assertfields(ep)
|
||||
|
||||
ep = EntryPoint.parse("bar baz= spammity[PING]")
|
||||
self.assertEqual(ep.name,"bar baz")
|
||||
self.assertEqual(ep.module_name,"spammity")
|
||||
self.assertEqual(ep.attrs, ())
|
||||
self.assertEqual(ep.extras, ("ping",))
|
||||
|
||||
ep = EntryPoint.parse(" fizzly = wocka:foo")
|
||||
self.assertEqual(ep.name,"fizzly")
|
||||
self.assertEqual(ep.module_name,"wocka")
|
||||
self.assertEqual(ep.attrs, ("foo",))
|
||||
self.assertEqual(ep.extras, ())
|
||||
|
||||
def testRejects(self):
|
||||
for ep in [
|
||||
"foo", "x=1=2", "x=a:b:c", "q=x/na", "fez=pish:tush-z", "x=f[a]>2",
|
||||
]:
|
||||
try: EntryPoint.parse(ep)
|
||||
except ValueError: pass
|
||||
else: raise AssertionError("Should've been bad", ep)
|
||||
|
||||
def checkSubMap(self, m):
|
||||
self.assertEqual(len(m), len(self.submap_expect))
|
||||
for key, ep in self.submap_expect.iteritems():
|
||||
self.assertEqual(repr(m.get(key)), repr(ep))
|
||||
|
||||
submap_expect = dict(
|
||||
feature1=EntryPoint('feature1', 'somemodule', ['somefunction']),
|
||||
feature2=EntryPoint('feature2', 'another.module', ['SomeClass'], ['extra1','extra2']),
|
||||
feature3=EntryPoint('feature3', 'this.module', extras=['something'])
|
||||
)
|
||||
submap_str = """
|
||||
# define features for blah blah
|
||||
feature1 = somemodule:somefunction
|
||||
feature2 = another.module:SomeClass [extra1,extra2]
|
||||
feature3 = this.module [something]
|
||||
"""
|
||||
|
||||
def testParseList(self):
|
||||
self.checkSubMap(EntryPoint.parse_group("xyz", self.submap_str))
|
||||
self.assertRaises(ValueError, EntryPoint.parse_group, "x a", "foo=bar")
|
||||
self.assertRaises(ValueError, EntryPoint.parse_group, "x",
|
||||
["foo=baz", "foo=bar"])
|
||||
|
||||
def testParseMap(self):
|
||||
m = EntryPoint.parse_map({'xyz':self.submap_str})
|
||||
self.checkSubMap(m['xyz'])
|
||||
self.assertEqual(m.keys(),['xyz'])
|
||||
m = EntryPoint.parse_map("[xyz]\n"+self.submap_str)
|
||||
self.checkSubMap(m['xyz'])
|
||||
self.assertEqual(m.keys(),['xyz'])
|
||||
self.assertRaises(ValueError, EntryPoint.parse_map, ["[xyz]", "[xyz]"])
|
||||
self.assertRaises(ValueError, EntryPoint.parse_map, self.submap_str)
|
||||
|
||||
class RequirementsTests(TestCase):
|
||||
|
||||
def testBasics(self):
|
||||
r = Requirement.parse("Twisted>=1.2")
|
||||
self.assertEqual(str(r),"Twisted>=1.2")
|
||||
self.assertEqual(repr(r),"Requirement.parse('Twisted>=1.2')")
|
||||
self.assertEqual(r, Requirement("Twisted", [('>=','1.2')], ()))
|
||||
self.assertEqual(r, Requirement("twisTed", [('>=','1.2')], ()))
|
||||
self.assertNotEqual(r, Requirement("Twisted", [('>=','2.0')], ()))
|
||||
self.assertNotEqual(r, Requirement("Zope", [('>=','1.2')], ()))
|
||||
self.assertNotEqual(r, Requirement("Zope", [('>=','3.0')], ()))
|
||||
self.assertNotEqual(r, Requirement.parse("Twisted[extras]>=1.2"))
|
||||
|
||||
def testOrdering(self):
|
||||
r1 = Requirement("Twisted", [('==','1.2c1'),('>=','1.2')], ())
|
||||
r2 = Requirement("Twisted", [('>=','1.2'),('==','1.2c1')], ())
|
||||
self.assertEqual(r1,r2)
|
||||
self.assertEqual(str(r1),str(r2))
|
||||
self.assertEqual(str(r2),"Twisted==1.2c1,>=1.2")
|
||||
|
||||
def testBasicContains(self):
|
||||
r = Requirement("Twisted", [('>=','1.2')], ())
|
||||
foo_dist = Distribution.from_filename("FooPkg-1.3_1.egg")
|
||||
twist11 = Distribution.from_filename("Twisted-1.1.egg")
|
||||
twist12 = Distribution.from_filename("Twisted-1.2.egg")
|
||||
self.assertTrue(parse_version('1.2') in r)
|
||||
self.assertTrue(parse_version('1.1') not in r)
|
||||
self.assertTrue('1.2' in r)
|
||||
self.assertTrue('1.1' not in r)
|
||||
self.assertTrue(foo_dist not in r)
|
||||
self.assertTrue(twist11 not in r)
|
||||
self.assertTrue(twist12 in r)
|
||||
|
||||
def testAdvancedContains(self):
|
||||
r, = parse_requirements("Foo>=1.2,<=1.3,==1.9,>2.0,!=2.5,<3.0,==4.5")
|
||||
for v in ('1.2','1.2.2','1.3','1.9','2.0.1','2.3','2.6','3.0c1','4.5'):
|
||||
self.assertTrue(v in r, (v,r))
|
||||
for v in ('1.2c1','1.3.1','1.5','1.9.1','2.0','2.5','3.0','4.0'):
|
||||
self.assertTrue(v not in r, (v,r))
|
||||
|
||||
|
||||
def testOptionsAndHashing(self):
|
||||
r1 = Requirement.parse("Twisted[foo,bar]>=1.2")
|
||||
r2 = Requirement.parse("Twisted[bar,FOO]>=1.2")
|
||||
r3 = Requirement.parse("Twisted[BAR,FOO]>=1.2.0")
|
||||
self.assertEqual(r1,r2)
|
||||
self.assertEqual(r1,r3)
|
||||
self.assertEqual(r1.extras, ("foo","bar"))
|
||||
self.assertEqual(r2.extras, ("bar","foo")) # extras are normalized
|
||||
self.assertEqual(hash(r1), hash(r2))
|
||||
self.assertEqual(
|
||||
hash(r1), hash(("twisted", ((">=",parse_version("1.2")),),
|
||||
frozenset(["foo","bar"])))
|
||||
)
|
||||
|
||||
def testVersionEquality(self):
|
||||
r1 = Requirement.parse("foo==0.3a2")
|
||||
r2 = Requirement.parse("foo!=0.3a4")
|
||||
d = Distribution.from_filename
|
||||
|
||||
self.assertTrue(d("foo-0.3a4.egg") not in r1)
|
||||
self.assertTrue(d("foo-0.3a1.egg") not in r1)
|
||||
self.assertTrue(d("foo-0.3a4.egg") not in r2)
|
||||
|
||||
self.assertTrue(d("foo-0.3a2.egg") in r1)
|
||||
self.assertTrue(d("foo-0.3a2.egg") in r2)
|
||||
self.assertTrue(d("foo-0.3a3.egg") in r2)
|
||||
self.assertTrue(d("foo-0.3a5.egg") in r2)
|
||||
|
||||
def testDistributeSetuptoolsOverride(self):
|
||||
# Plain setuptools or distribute mean we return distribute.
|
||||
self.assertEqual(
|
||||
Requirement.parse('setuptools').project_name, 'distribute')
|
||||
self.assertEqual(
|
||||
Requirement.parse('distribute').project_name, 'distribute')
|
||||
# setuptools lower than 0.7 means distribute
|
||||
self.assertEqual(
|
||||
Requirement.parse('setuptools==0.6c9').project_name, 'distribute')
|
||||
self.assertEqual(
|
||||
Requirement.parse('setuptools==0.6c10').project_name, 'distribute')
|
||||
self.assertEqual(
|
||||
Requirement.parse('setuptools>=0.6').project_name, 'distribute')
|
||||
self.assertEqual(
|
||||
Requirement.parse('setuptools < 0.7').project_name, 'distribute')
|
||||
# setuptools 0.7 and higher means setuptools.
|
||||
self.assertEqual(
|
||||
Requirement.parse('setuptools == 0.7').project_name, 'setuptools')
|
||||
self.assertEqual(
|
||||
Requirement.parse('setuptools == 0.7a1').project_name, 'setuptools')
|
||||
self.assertEqual(
|
||||
Requirement.parse('setuptools >= 0.7').project_name, 'setuptools')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ParseTests(TestCase):
|
||||
|
||||
def testEmptyParse(self):
|
||||
self.assertEqual(list(parse_requirements('')), [])
|
||||
|
||||
def testYielding(self):
|
||||
for inp,out in [
|
||||
([], []), ('x',['x']), ([[]],[]), (' x\n y', ['x','y']),
|
||||
(['x\n\n','y'], ['x','y']),
|
||||
]:
|
||||
self.assertEqual(list(pkg_resources.yield_lines(inp)),out)
|
||||
|
||||
def testSplitting(self):
|
||||
self.assertEqual(
|
||||
list(
|
||||
pkg_resources.split_sections("""
|
||||
x
|
||||
[Y]
|
||||
z
|
||||
|
||||
a
|
||||
[b ]
|
||||
# foo
|
||||
c
|
||||
[ d]
|
||||
[q]
|
||||
v
|
||||
"""
|
||||
)
|
||||
),
|
||||
[(None,["x"]), ("Y",["z","a"]), ("b",["c"]), ("d",[]), ("q",["v"])]
|
||||
)
|
||||
self.assertRaises(ValueError,list,pkg_resources.split_sections("[foo"))
|
||||
|
||||
def testSafeName(self):
|
||||
self.assertEqual(safe_name("adns-python"), "adns-python")
|
||||
self.assertEqual(safe_name("WSGI Utils"), "WSGI-Utils")
|
||||
self.assertEqual(safe_name("WSGI Utils"), "WSGI-Utils")
|
||||
self.assertEqual(safe_name("Money$$$Maker"), "Money-Maker")
|
||||
self.assertNotEqual(safe_name("peak.web"), "peak-web")
|
||||
|
||||
def testSafeVersion(self):
|
||||
self.assertEqual(safe_version("1.2-1"), "1.2-1")
|
||||
self.assertEqual(safe_version("1.2 alpha"), "1.2.alpha")
|
||||
self.assertEqual(safe_version("2.3.4 20050521"), "2.3.4.20050521")
|
||||
self.assertEqual(safe_version("Money$$$Maker"), "Money-Maker")
|
||||
self.assertEqual(safe_version("peak.web"), "peak.web")
|
||||
|
||||
def testSimpleRequirements(self):
|
||||
self.assertEqual(
|
||||
list(parse_requirements('Twis-Ted>=1.2-1')),
|
||||
[Requirement('Twis-Ted',[('>=','1.2-1')], ())]
|
||||
)
|
||||
self.assertEqual(
|
||||
list(parse_requirements('Twisted >=1.2, \ # more\n<2.0')),
|
||||
[Requirement('Twisted',[('>=','1.2'),('<','2.0')], ())]
|
||||
)
|
||||
self.assertEqual(
|
||||
Requirement.parse("FooBar==1.99a3"),
|
||||
Requirement("FooBar", [('==','1.99a3')], ())
|
||||
)
|
||||
self.assertRaises(ValueError,Requirement.parse,">=2.3")
|
||||
self.assertRaises(ValueError,Requirement.parse,"x\\")
|
||||
self.assertRaises(ValueError,Requirement.parse,"x==2 q")
|
||||
self.assertRaises(ValueError,Requirement.parse,"X==1\nY==2")
|
||||
self.assertRaises(ValueError,Requirement.parse,"#")
|
||||
|
||||
def testVersionEquality(self):
|
||||
def c(s1,s2):
|
||||
p1, p2 = parse_version(s1),parse_version(s2)
|
||||
self.assertEqual(p1,p2, (s1,s2,p1,p2))
|
||||
|
||||
c('0.4', '0.4.0')
|
||||
c('0.4.0.0', '0.4.0')
|
||||
c('0.4.0-0', '0.4-0')
|
||||
c('0pl1', '0.0pl1')
|
||||
c('0pre1', '0.0c1')
|
||||
c('0.0.0preview1', '0c1')
|
||||
c('0.0c1', '0rc1')
|
||||
c('1.2a1', '1.2.a.1'); c('1.2...a', '1.2a')
|
||||
|
||||
def testVersionOrdering(self):
|
||||
def c(s1,s2):
|
||||
p1, p2 = parse_version(s1),parse_version(s2)
|
||||
self.assertTrue(p1<p2, (s1,s2,p1,p2))
|
||||
|
||||
c('2.1','2.1.1')
|
||||
c('2.1.0','2.10')
|
||||
c('2a1','2b0')
|
||||
c('2b1','2c0')
|
||||
c('2a1','2.1')
|
||||
c('2.3a1', '2.3')
|
||||
c('2.1-1', '2.1-2')
|
||||
c('2.1-1', '2.1.1')
|
||||
c('2.1', '2.1.1-1')
|
||||
c('2.1', '2.1pl4')
|
||||
c('2.1a0-20040501', '2.1')
|
||||
c('1.1', '02.1')
|
||||
c('A56','B27')
|
||||
c('3.2', '3.2.pl0')
|
||||
c('3.2-1', '3.2pl1')
|
||||
c('3.2pl1', '3.2pl1-1')
|
||||
c('0.4', '4.0')
|
||||
c('0.0.4', '0.4.0')
|
||||
c('0pl1', '0.4pl1')
|
||||
c('2.1dev','2.1a0')
|
||||
c('2.1.0rc1','2.1.0')
|
||||
c('2.1.0','2.1.0-rc0')
|
||||
c('2.1.0','2.1.0-a')
|
||||
c('2.1.0','2.1.0-alpha')
|
||||
c('2.1.0','2.1.0-foo')
|
||||
c('1.0','1.0-1')
|
||||
c('1.0-1','1.0.1')
|
||||
c('1.0a','1.0b')
|
||||
c('1.0dev','1.0rc1')
|
||||
c('1.0pre','1.0')
|
||||
c('1.0pre','1.0')
|
||||
c('1.0a','1.0-a')
|
||||
c('1.0rc1','1.0-rc1')
|
||||
|
||||
torture ="""
|
||||
0.80.1-3 0.80.1-2 0.80.1-1 0.79.9999+0.80.0pre4-1
|
||||
0.79.9999+0.80.0pre2-3 0.79.9999+0.80.0pre2-2
|
||||
0.77.2-1 0.77.1-1 0.77.0-1
|
||||
""".split()
|
||||
|
||||
for p,v1 in enumerate(torture):
|
||||
for v2 in torture[p+1:]:
|
||||
c(v2,v1)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class ScriptHeaderTests(TestCase):
|
||||
non_ascii_exe = '/Users/José/bin/python'
|
||||
|
||||
def test_get_script_header(self):
|
||||
if not sys.platform.startswith('java') or not is_sh(sys.executable):
|
||||
# This test is for non-Jython platforms
|
||||
self.assertEqual(get_script_header('#!/usr/local/bin/python'),
|
||||
'#!%s\n' % os.path.normpath(sys.executable))
|
||||
self.assertEqual(get_script_header('#!/usr/bin/python -x'),
|
||||
'#!%s -x\n' % os.path.normpath(sys.executable))
|
||||
self.assertEqual(get_script_header('#!/usr/bin/python',
|
||||
executable=self.non_ascii_exe),
|
||||
'#!%s -x\n' % self.non_ascii_exe)
|
||||
|
||||
def test_get_script_header_jython_workaround(self):
|
||||
# This test doesn't work with Python 3 in some locales
|
||||
if (sys.version_info >= (3,) and os.environ.get("LC_CTYPE")
|
||||
in (None, "C", "POSIX")):
|
||||
return
|
||||
platform = sys.platform
|
||||
sys.platform = 'java1.5.0_13'
|
||||
stdout = sys.stdout
|
||||
try:
|
||||
# A mock sys.executable that uses a shebang line (this file)
|
||||
exe = os.path.normpath(os.path.splitext(__file__)[0] + '.py')
|
||||
self.assertEqual(
|
||||
get_script_header('#!/usr/local/bin/python', executable=exe),
|
||||
'#!/usr/bin/env %s\n' % exe)
|
||||
|
||||
# Ensure we generate what is basically a broken shebang line
|
||||
# when there's options, with a warning emitted
|
||||
sys.stdout = sys.stderr = StringIO.StringIO()
|
||||
self.assertEqual(get_script_header('#!/usr/bin/python -x',
|
||||
executable=exe),
|
||||
'#!%s -x\n' % exe)
|
||||
self.assertTrue('Unable to adapt shebang line' in sys.stdout.getvalue())
|
||||
sys.stdout = sys.stderr = StringIO.StringIO()
|
||||
self.assertEqual(get_script_header('#!/usr/bin/python',
|
||||
executable=self.non_ascii_exe),
|
||||
'#!%s -x\n' % self.non_ascii_exe)
|
||||
self.assertTrue('Unable to adapt shebang line' in sys.stdout.getvalue())
|
||||
finally:
|
||||
sys.platform = platform
|
||||
sys.stdout = stdout
|
||||
|
||||
|
||||
|
||||
|
||||
class NamespaceTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._ns_pkgs = pkg_resources._namespace_packages.copy()
|
||||
self._tmpdir = tempfile.mkdtemp(prefix="tests-distribute-")
|
||||
os.makedirs(os.path.join(self._tmpdir, "site-pkgs"))
|
||||
self._prev_sys_path = sys.path[:]
|
||||
sys.path.append(os.path.join(self._tmpdir, "site-pkgs"))
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self._tmpdir)
|
||||
pkg_resources._namespace_packages = self._ns_pkgs.copy()
|
||||
sys.path = self._prev_sys_path[:]
|
||||
|
||||
def _assertIn(self, member, container):
|
||||
""" assertIn and assertTrue does not exist in Python2.3"""
|
||||
if member not in container:
|
||||
standardMsg = '%s not found in %s' % (safe_repr(member),
|
||||
safe_repr(container))
|
||||
self.fail(self._formatMessage(msg, standardMsg))
|
||||
|
||||
def test_two_levels_deep(self):
|
||||
"""
|
||||
Test nested namespace packages
|
||||
Create namespace packages in the following tree :
|
||||
site-packages-1/pkg1/pkg2
|
||||
site-packages-2/pkg1/pkg2
|
||||
Check both are in the _namespace_packages dict and that their __path__
|
||||
is correct
|
||||
"""
|
||||
sys.path.append(os.path.join(self._tmpdir, "site-pkgs2"))
|
||||
os.makedirs(os.path.join(self._tmpdir, "site-pkgs", "pkg1", "pkg2"))
|
||||
os.makedirs(os.path.join(self._tmpdir, "site-pkgs2", "pkg1", "pkg2"))
|
||||
ns_str = "__import__('pkg_resources').declare_namespace(__name__)\n"
|
||||
for site in ["site-pkgs", "site-pkgs2"]:
|
||||
pkg1_init = open(os.path.join(self._tmpdir, site,
|
||||
"pkg1", "__init__.py"), "w")
|
||||
pkg1_init.write(ns_str)
|
||||
pkg1_init.close()
|
||||
pkg2_init = open(os.path.join(self._tmpdir, site,
|
||||
"pkg1", "pkg2", "__init__.py"), "w")
|
||||
pkg2_init.write(ns_str)
|
||||
pkg2_init.close()
|
||||
import pkg1
|
||||
self._assertIn("pkg1", pkg_resources._namespace_packages.keys())
|
||||
try:
|
||||
import pkg1.pkg2
|
||||
except ImportError, e:
|
||||
self.fail("Distribute tried to import the parent namespace package")
|
||||
# check the _namespace_packages dict
|
||||
self._assertIn("pkg1.pkg2", pkg_resources._namespace_packages.keys())
|
||||
self.assertEqual(pkg_resources._namespace_packages["pkg1"], ["pkg1.pkg2"])
|
||||
# check the __path__ attribute contains both paths
|
||||
self.assertEqual(pkg1.pkg2.__path__, [
|
||||
os.path.join(self._tmpdir, "site-pkgs", "pkg1", "pkg2"),
|
||||
os.path.join(self._tmpdir, "site-pkgs2", "pkg1", "pkg2") ])
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
"""develop tests
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import shutil
|
||||
import unittest
|
||||
import tempfile
|
||||
|
||||
from setuptools.sandbox import DirectorySandbox, SandboxViolation
|
||||
|
||||
def has_win32com():
|
||||
"""
|
||||
Run this to determine if the local machine has win32com, and if it
|
||||
does, include additional tests.
|
||||
"""
|
||||
if not sys.platform.startswith('win32'):
|
||||
return False
|
||||
try:
|
||||
mod = __import__('win32com')
|
||||
except ImportError:
|
||||
return False
|
||||
return True
|
||||
|
||||
class TestSandbox(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.dir = tempfile.mkdtemp()
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.dir)
|
||||
|
||||
def test_devnull(self):
|
||||
if sys.version < '2.4':
|
||||
return
|
||||
sandbox = DirectorySandbox(self.dir)
|
||||
sandbox.run(self._file_writer(os.devnull))
|
||||
|
||||
def _file_writer(path):
|
||||
def do_write():
|
||||
f = open(path, 'w')
|
||||
f.write('xxx')
|
||||
f.close()
|
||||
return do_write
|
||||
|
||||
_file_writer = staticmethod(_file_writer)
|
||||
|
||||
if has_win32com():
|
||||
def test_win32com(self):
|
||||
"""
|
||||
win32com should not be prevented from caching COM interfaces
|
||||
in gen_py.
|
||||
"""
|
||||
import win32com
|
||||
gen_py = win32com.__gen_path__
|
||||
target = os.path.join(gen_py, 'test_write')
|
||||
sandbox = DirectorySandbox(self.dir)
|
||||
try:
|
||||
try:
|
||||
sandbox.run(self._file_writer(target))
|
||||
except SandboxViolation:
|
||||
self.fail("Could not create gen_py file due to SandboxViolation")
|
||||
finally:
|
||||
if os.path.exists(target): os.remove(target)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,383 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""sdist tests"""
|
||||
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import urllib
|
||||
import unicodedata
|
||||
from StringIO import StringIO
|
||||
|
||||
|
||||
from setuptools.command.sdist import sdist
|
||||
from setuptools.command.egg_info import manifest_maker
|
||||
from setuptools.dist import Distribution
|
||||
|
||||
|
||||
SETUP_ATTRS = {
|
||||
'name': 'sdist_test',
|
||||
'version': '0.0',
|
||||
'packages': ['sdist_test'],
|
||||
'package_data': {'sdist_test': ['*.txt']}
|
||||
}
|
||||
|
||||
|
||||
SETUP_PY = """\
|
||||
from setuptools import setup
|
||||
|
||||
setup(**%r)
|
||||
""" % SETUP_ATTRS
|
||||
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
LATIN1_FILENAME = 'smörbröd.py'.encode('latin-1')
|
||||
else:
|
||||
LATIN1_FILENAME = 'sm\xf6rbr\xf6d.py'
|
||||
|
||||
|
||||
# Cannot use context manager because of Python 2.4
|
||||
def quiet():
|
||||
global old_stdout, old_stderr
|
||||
old_stdout, old_stderr = sys.stdout, sys.stderr
|
||||
sys.stdout, sys.stderr = StringIO(), StringIO()
|
||||
|
||||
def unquiet():
|
||||
sys.stdout, sys.stderr = old_stdout, old_stderr
|
||||
|
||||
|
||||
# Fake byte literals for Python <= 2.5
|
||||
def b(s, encoding='utf-8'):
|
||||
if sys.version_info >= (3,):
|
||||
return s.encode(encoding)
|
||||
return s
|
||||
|
||||
|
||||
# Convert to POSIX path
|
||||
def posix(path):
|
||||
if sys.version_info >= (3,) and not isinstance(path, unicode):
|
||||
return path.replace(os.sep.encode('ascii'), b('/'))
|
||||
else:
|
||||
return path.replace(os.sep, '/')
|
||||
|
||||
|
||||
# HFS Plus uses decomposed UTF-8
|
||||
def decompose(path):
|
||||
if isinstance(path, unicode):
|
||||
return unicodedata.normalize('NFD', path)
|
||||
try:
|
||||
path = path.decode('utf-8')
|
||||
path = unicodedata.normalize('NFD', path)
|
||||
path = path.encode('utf-8')
|
||||
except UnicodeError:
|
||||
pass # Not UTF-8
|
||||
return path
|
||||
|
||||
|
||||
class TestSdistTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
f = open(os.path.join(self.temp_dir, 'setup.py'), 'w')
|
||||
f.write(SETUP_PY)
|
||||
f.close()
|
||||
# Set up the rest of the test package
|
||||
test_pkg = os.path.join(self.temp_dir, 'sdist_test')
|
||||
os.mkdir(test_pkg)
|
||||
# *.rst was not included in package_data, so c.rst should not be
|
||||
# automatically added to the manifest when not under version control
|
||||
for fname in ['__init__.py', 'a.txt', 'b.txt', 'c.rst']:
|
||||
# Just touch the files; their contents are irrelevant
|
||||
open(os.path.join(test_pkg, fname), 'w').close()
|
||||
|
||||
self.old_cwd = os.getcwd()
|
||||
os.chdir(self.temp_dir)
|
||||
|
||||
def tearDown(self):
|
||||
os.chdir(self.old_cwd)
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
||||
def test_package_data_in_sdist(self):
|
||||
"""Regression test for pull request #4: ensures that files listed in
|
||||
package_data are included in the manifest even if they're not added to
|
||||
version control.
|
||||
"""
|
||||
|
||||
dist = Distribution(SETUP_ATTRS)
|
||||
dist.script_name = 'setup.py'
|
||||
cmd = sdist(dist)
|
||||
cmd.ensure_finalized()
|
||||
|
||||
# squelch output
|
||||
quiet()
|
||||
try:
|
||||
cmd.run()
|
||||
finally:
|
||||
unquiet()
|
||||
|
||||
manifest = cmd.filelist.files
|
||||
self.assertTrue(os.path.join('sdist_test', 'a.txt') in manifest)
|
||||
self.assertTrue(os.path.join('sdist_test', 'b.txt') in manifest)
|
||||
self.assertTrue(os.path.join('sdist_test', 'c.rst') not in manifest)
|
||||
|
||||
def test_manifest_is_written_with_utf8_encoding(self):
|
||||
# Test for #303.
|
||||
dist = Distribution(SETUP_ATTRS)
|
||||
dist.script_name = 'setup.py'
|
||||
mm = manifest_maker(dist)
|
||||
mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
|
||||
os.mkdir('sdist_test.egg-info')
|
||||
|
||||
# UTF-8 filename
|
||||
filename = os.path.join('sdist_test', 'smörbröd.py')
|
||||
|
||||
# Add UTF-8 filename and write manifest
|
||||
quiet()
|
||||
try:
|
||||
mm.run()
|
||||
mm.filelist.files.append(filename)
|
||||
mm.write_manifest()
|
||||
finally:
|
||||
unquiet()
|
||||
|
||||
manifest = open(mm.manifest, 'rbU')
|
||||
contents = manifest.read()
|
||||
manifest.close()
|
||||
|
||||
# The manifest should be UTF-8 encoded
|
||||
try:
|
||||
u_contents = contents.decode('UTF-8')
|
||||
except UnicodeDecodeError, e:
|
||||
self.fail(e)
|
||||
|
||||
# The manifest should contain the UTF-8 filename
|
||||
if sys.version_info >= (3,):
|
||||
self.assertTrue(posix(filename) in u_contents)
|
||||
else:
|
||||
self.assertTrue(posix(filename) in contents)
|
||||
|
||||
# Python 3 only
|
||||
if sys.version_info >= (3,):
|
||||
|
||||
def test_write_manifest_allows_utf8_filenames(self):
|
||||
# Test for #303.
|
||||
dist = Distribution(SETUP_ATTRS)
|
||||
dist.script_name = 'setup.py'
|
||||
mm = manifest_maker(dist)
|
||||
mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
|
||||
os.mkdir('sdist_test.egg-info')
|
||||
|
||||
# UTF-8 filename
|
||||
filename = os.path.join(b('sdist_test'), b('smörbröd.py'))
|
||||
|
||||
# Add filename and write manifest
|
||||
quiet()
|
||||
try:
|
||||
mm.run()
|
||||
u_filename = filename.decode('utf-8')
|
||||
mm.filelist.files.append(u_filename)
|
||||
# Re-write manifest
|
||||
mm.write_manifest()
|
||||
finally:
|
||||
unquiet()
|
||||
|
||||
manifest = open(mm.manifest, 'rbU')
|
||||
contents = manifest.read()
|
||||
manifest.close()
|
||||
|
||||
# The manifest should be UTF-8 encoded
|
||||
try:
|
||||
contents.decode('UTF-8')
|
||||
except UnicodeDecodeError, e:
|
||||
self.fail(e)
|
||||
|
||||
# The manifest should contain the UTF-8 filename
|
||||
self.assertTrue(posix(filename) in contents)
|
||||
|
||||
# The filelist should have been updated as well
|
||||
self.assertTrue(u_filename in mm.filelist.files)
|
||||
|
||||
def test_write_manifest_skips_non_utf8_filenames(self):
|
||||
# Test for #303.
|
||||
dist = Distribution(SETUP_ATTRS)
|
||||
dist.script_name = 'setup.py'
|
||||
mm = manifest_maker(dist)
|
||||
mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
|
||||
os.mkdir('sdist_test.egg-info')
|
||||
|
||||
# Latin-1 filename
|
||||
filename = os.path.join(b('sdist_test'), LATIN1_FILENAME)
|
||||
|
||||
# Add filename with surrogates and write manifest
|
||||
quiet()
|
||||
try:
|
||||
mm.run()
|
||||
u_filename = filename.decode('utf-8', 'surrogateescape')
|
||||
mm.filelist.files.append(u_filename)
|
||||
# Re-write manifest
|
||||
mm.write_manifest()
|
||||
finally:
|
||||
unquiet()
|
||||
|
||||
manifest = open(mm.manifest, 'rbU')
|
||||
contents = manifest.read()
|
||||
manifest.close()
|
||||
|
||||
# The manifest should be UTF-8 encoded
|
||||
try:
|
||||
contents.decode('UTF-8')
|
||||
except UnicodeDecodeError, e:
|
||||
self.fail(e)
|
||||
|
||||
# The Latin-1 filename should have been skipped
|
||||
self.assertFalse(posix(filename) in contents)
|
||||
|
||||
# The filelist should have been updated as well
|
||||
self.assertFalse(u_filename in mm.filelist.files)
|
||||
|
||||
def test_manifest_is_read_with_utf8_encoding(self):
|
||||
# Test for #303.
|
||||
dist = Distribution(SETUP_ATTRS)
|
||||
dist.script_name = 'setup.py'
|
||||
cmd = sdist(dist)
|
||||
cmd.ensure_finalized()
|
||||
|
||||
# Create manifest
|
||||
quiet()
|
||||
try:
|
||||
cmd.run()
|
||||
finally:
|
||||
unquiet()
|
||||
|
||||
# Add UTF-8 filename to manifest
|
||||
filename = os.path.join(b('sdist_test'), b('smörbröd.py'))
|
||||
cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
|
||||
manifest = open(cmd.manifest, 'ab')
|
||||
manifest.write(b('\n')+filename)
|
||||
manifest.close()
|
||||
|
||||
# The file must exist to be included in the filelist
|
||||
open(filename, 'w').close()
|
||||
|
||||
# Re-read manifest
|
||||
cmd.filelist.files = []
|
||||
quiet()
|
||||
try:
|
||||
cmd.read_manifest()
|
||||
finally:
|
||||
unquiet()
|
||||
|
||||
# The filelist should contain the UTF-8 filename
|
||||
if sys.version_info >= (3,):
|
||||
filename = filename.decode('utf-8')
|
||||
self.assertTrue(filename in cmd.filelist.files)
|
||||
|
||||
# Python 3 only
|
||||
if sys.version_info >= (3,):
|
||||
|
||||
def test_read_manifest_skips_non_utf8_filenames(self):
|
||||
# Test for #303.
|
||||
dist = Distribution(SETUP_ATTRS)
|
||||
dist.script_name = 'setup.py'
|
||||
cmd = sdist(dist)
|
||||
cmd.ensure_finalized()
|
||||
|
||||
# Create manifest
|
||||
quiet()
|
||||
try:
|
||||
cmd.run()
|
||||
finally:
|
||||
unquiet()
|
||||
|
||||
# Add Latin-1 filename to manifest
|
||||
filename = os.path.join(b('sdist_test'), LATIN1_FILENAME)
|
||||
cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
|
||||
manifest = open(cmd.manifest, 'ab')
|
||||
manifest.write(b('\n')+filename)
|
||||
manifest.close()
|
||||
|
||||
# The file must exist to be included in the filelist
|
||||
open(filename, 'w').close()
|
||||
|
||||
# Re-read manifest
|
||||
cmd.filelist.files = []
|
||||
quiet()
|
||||
try:
|
||||
try:
|
||||
cmd.read_manifest()
|
||||
except UnicodeDecodeError, e:
|
||||
self.fail(e)
|
||||
finally:
|
||||
unquiet()
|
||||
|
||||
# The Latin-1 filename should have been skipped
|
||||
filename = filename.decode('latin-1')
|
||||
self.assertFalse(filename in cmd.filelist.files)
|
||||
|
||||
def test_sdist_with_utf8_encoded_filename(self):
|
||||
# Test for #303.
|
||||
dist = Distribution(SETUP_ATTRS)
|
||||
dist.script_name = 'setup.py'
|
||||
cmd = sdist(dist)
|
||||
cmd.ensure_finalized()
|
||||
|
||||
# UTF-8 filename
|
||||
filename = os.path.join(b('sdist_test'), b('smörbröd.py'))
|
||||
open(filename, 'w').close()
|
||||
|
||||
quiet()
|
||||
try:
|
||||
cmd.run()
|
||||
finally:
|
||||
unquiet()
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
filename = decompose(filename)
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
if sys.platform == 'win32':
|
||||
# Python 3 mangles the UTF-8 filename
|
||||
filename = filename.decode('cp1252')
|
||||
self.assertTrue(filename in cmd.filelist.files)
|
||||
else:
|
||||
filename = filename.decode('utf-8')
|
||||
self.assertTrue(filename in cmd.filelist.files)
|
||||
else:
|
||||
self.assertTrue(filename in cmd.filelist.files)
|
||||
|
||||
def test_sdist_with_latin1_encoded_filename(self):
|
||||
# Test for #303.
|
||||
dist = Distribution(SETUP_ATTRS)
|
||||
dist.script_name = 'setup.py'
|
||||
cmd = sdist(dist)
|
||||
cmd.ensure_finalized()
|
||||
|
||||
# Latin-1 filename
|
||||
filename = os.path.join(b('sdist_test'), LATIN1_FILENAME)
|
||||
open(filename, 'w').close()
|
||||
|
||||
quiet()
|
||||
try:
|
||||
cmd.run()
|
||||
finally:
|
||||
unquiet()
|
||||
|
||||
if sys.version_info >= (3,):
|
||||
filename = filename.decode('latin-1')
|
||||
if sys.platform == 'win32':
|
||||
# Latin-1 is similar to Windows-1252
|
||||
self.assertTrue(filename in cmd.filelist.files)
|
||||
else:
|
||||
# The Latin-1 filename should have been skipped
|
||||
self.assertFalse(filename in cmd.filelist.files)
|
||||
else:
|
||||
# No conversion takes place under Python 2 and the file
|
||||
# is included. We shall keep it that way for BBB.
|
||||
self.assertTrue(filename in cmd.filelist.files)
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.defaultTestLoader.loadTestsFromName(__name__)
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
"""develop tests
|
||||
"""
|
||||
import sys
|
||||
import os, shutil, tempfile, unittest
|
||||
import tempfile
|
||||
import site
|
||||
from StringIO import StringIO
|
||||
|
||||
from distutils.errors import DistutilsError
|
||||
from setuptools.command.test import test
|
||||
from setuptools.command import easy_install as easy_install_pkg
|
||||
from setuptools.dist import Distribution
|
||||
|
||||
SETUP_PY = """\
|
||||
from setuptools import setup
|
||||
|
||||
setup(name='foo',
|
||||
packages=['name', 'name.space', 'name.space.tests'],
|
||||
namespace_packages=['name'],
|
||||
test_suite='name.space.tests.test_suite',
|
||||
)
|
||||
"""
|
||||
|
||||
NS_INIT = """# -*- coding: Latin-1 -*-
|
||||
# Söme Arbiträry Ünicode to test Issüé 310
|
||||
try:
|
||||
__import__('pkg_resources').declare_namespace(__name__)
|
||||
except ImportError:
|
||||
from pkgutil import extend_path
|
||||
__path__ = extend_path(__path__, __name__)
|
||||
"""
|
||||
# Make sure this is Latin-1 binary, before writing:
|
||||
if sys.version_info < (3,):
|
||||
NS_INIT = NS_INIT.decode('UTF-8')
|
||||
NS_INIT = NS_INIT.encode('Latin-1')
|
||||
|
||||
TEST_PY = """import unittest
|
||||
|
||||
class TestTest(unittest.TestCase):
|
||||
def test_test(self):
|
||||
print "Foo" # Should fail under Python 3 unless 2to3 is used
|
||||
|
||||
test_suite = unittest.makeSuite(TestTest)
|
||||
"""
|
||||
|
||||
class TestTestTest(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
if sys.version < "2.6" or hasattr(sys, 'real_prefix'):
|
||||
return
|
||||
|
||||
# Directory structure
|
||||
self.dir = tempfile.mkdtemp()
|
||||
os.mkdir(os.path.join(self.dir, 'name'))
|
||||
os.mkdir(os.path.join(self.dir, 'name', 'space'))
|
||||
os.mkdir(os.path.join(self.dir, 'name', 'space', 'tests'))
|
||||
# setup.py
|
||||
setup = os.path.join(self.dir, 'setup.py')
|
||||
f = open(setup, 'wt')
|
||||
f.write(SETUP_PY)
|
||||
f.close()
|
||||
self.old_cwd = os.getcwd()
|
||||
# name/__init__.py
|
||||
init = os.path.join(self.dir, 'name', '__init__.py')
|
||||
f = open(init, 'wb')
|
||||
f.write(NS_INIT)
|
||||
f.close()
|
||||
# name/space/__init__.py
|
||||
init = os.path.join(self.dir, 'name', 'space', '__init__.py')
|
||||
f = open(init, 'wt')
|
||||
f.write('#empty\n')
|
||||
f.close()
|
||||
# name/space/tests/__init__.py
|
||||
init = os.path.join(self.dir, 'name', 'space', 'tests', '__init__.py')
|
||||
f = open(init, 'wt')
|
||||
f.write(TEST_PY)
|
||||
f.close()
|
||||
|
||||
os.chdir(self.dir)
|
||||
self.old_base = site.USER_BASE
|
||||
site.USER_BASE = tempfile.mkdtemp()
|
||||
self.old_site = site.USER_SITE
|
||||
site.USER_SITE = tempfile.mkdtemp()
|
||||
|
||||
def tearDown(self):
|
||||
if sys.version < "2.6" or hasattr(sys, 'real_prefix'):
|
||||
return
|
||||
|
||||
os.chdir(self.old_cwd)
|
||||
shutil.rmtree(self.dir)
|
||||
shutil.rmtree(site.USER_BASE)
|
||||
shutil.rmtree(site.USER_SITE)
|
||||
site.USER_BASE = self.old_base
|
||||
site.USER_SITE = self.old_site
|
||||
|
||||
def test_test(self):
|
||||
if sys.version < "2.6" or hasattr(sys, 'real_prefix'):
|
||||
return
|
||||
|
||||
dist = Distribution(dict(
|
||||
name='foo',
|
||||
packages=['name', 'name.space', 'name.space.tests'],
|
||||
namespace_packages=['name'],
|
||||
test_suite='name.space.tests.test_suite',
|
||||
use_2to3=True,
|
||||
))
|
||||
dist.script_name = 'setup.py'
|
||||
cmd = test(dist)
|
||||
cmd.user = 1
|
||||
cmd.ensure_finalized()
|
||||
cmd.install_dir = site.USER_SITE
|
||||
cmd.user = 1
|
||||
old_stdout = sys.stdout
|
||||
sys.stdout = StringIO()
|
||||
try:
|
||||
try: # try/except/finally doesn't work in Python 2.4, so we need nested try-statements.
|
||||
cmd.run()
|
||||
except SystemExit: # The test runner calls sys.exit, stop that making an error.
|
||||
pass
|
||||
finally:
|
||||
sys.stdout = old_stdout
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
"""build_ext tests
|
||||
"""
|
||||
import sys, os, shutil, tempfile, unittest, site, zipfile
|
||||
from setuptools.command.upload_docs import upload_docs
|
||||
from setuptools.dist import Distribution
|
||||
|
||||
SETUP_PY = """\
|
||||
from setuptools import setup
|
||||
|
||||
setup(name='foo')
|
||||
"""
|
||||
|
||||
class TestUploadDocsTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.dir = tempfile.mkdtemp()
|
||||
setup = os.path.join(self.dir, 'setup.py')
|
||||
f = open(setup, 'w')
|
||||
f.write(SETUP_PY)
|
||||
f.close()
|
||||
self.old_cwd = os.getcwd()
|
||||
os.chdir(self.dir)
|
||||
|
||||
self.upload_dir = os.path.join(self.dir, 'build')
|
||||
os.mkdir(self.upload_dir)
|
||||
|
||||
# A test document.
|
||||
f = open(os.path.join(self.upload_dir, 'index.html'), 'w')
|
||||
f.write("Hello world.")
|
||||
f.close()
|
||||
|
||||
# An empty folder.
|
||||
os.mkdir(os.path.join(self.upload_dir, 'empty'))
|
||||
|
||||
if sys.version >= "2.6":
|
||||
self.old_base = site.USER_BASE
|
||||
site.USER_BASE = upload_docs.USER_BASE = tempfile.mkdtemp()
|
||||
self.old_site = site.USER_SITE
|
||||
site.USER_SITE = upload_docs.USER_SITE = tempfile.mkdtemp()
|
||||
|
||||
def tearDown(self):
|
||||
os.chdir(self.old_cwd)
|
||||
shutil.rmtree(self.dir)
|
||||
if sys.version >= "2.6":
|
||||
shutil.rmtree(site.USER_BASE)
|
||||
shutil.rmtree(site.USER_SITE)
|
||||
site.USER_BASE = self.old_base
|
||||
site.USER_SITE = self.old_site
|
||||
|
||||
def test_create_zipfile(self):
|
||||
# Test to make sure zipfile creation handles common cases.
|
||||
# This explicitly includes a folder containing an empty folder.
|
||||
|
||||
dist = Distribution()
|
||||
|
||||
cmd = upload_docs(dist)
|
||||
cmd.upload_dir = self.upload_dir
|
||||
cmd.target_dir = self.upload_dir
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
tmp_file = os.path.join(tmp_dir, 'foo.zip')
|
||||
try:
|
||||
zip_file = cmd.create_zipfile(tmp_file)
|
||||
|
||||
assert zipfile.is_zipfile(tmp_file)
|
||||
|
||||
zip_file = zipfile.ZipFile(tmp_file) # woh...
|
||||
|
||||
assert zip_file.namelist() == ['index.html']
|
||||
|
||||
zip_file.close()
|
||||
finally:
|
||||
shutil.rmtree(tmp_dir)
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
Python Script Wrapper for Windows
|
||||
=================================
|
||||
|
||||
setuptools includes wrappers for Python scripts that allows them to be
|
||||
executed like regular windows programs. There are 2 wrappers, once
|
||||
for command-line programs, cli.exe, and one for graphica programs,
|
||||
gui.exe. These programs are almost identical, function pretty much
|
||||
the same way, and are generated from the same source file. The
|
||||
wrapper programs are used by copying them to the directory containing
|
||||
the script they are to wrap and with the same name as the script they
|
||||
are to wrap. In the rest of this document, we'll give an example that
|
||||
will illustrate this.
|
||||
|
||||
Let's create a simple script, foo-script.py:
|
||||
|
||||
>>> import os, sys, tempfile
|
||||
>>> from setuptools.command.easy_install import nt_quote_arg
|
||||
>>> sample_directory = tempfile.mkdtemp()
|
||||
>>> open(os.path.join(sample_directory, 'foo-script.py'), 'w').write(
|
||||
... """#!%(python_exe)s
|
||||
... import sys
|
||||
... input = repr(sys.stdin.read())
|
||||
... print sys.argv[0][-14:]
|
||||
... print sys.argv[1:]
|
||||
... print input
|
||||
... if __debug__:
|
||||
... print 'non-optimized'
|
||||
... """ % dict(python_exe=nt_quote_arg(sys.executable)))
|
||||
|
||||
Note that the script starts with a Unix-style '#!' line saying which
|
||||
Python executable to run. The wrapper will use this to find the
|
||||
correct Python executable.
|
||||
|
||||
We'll also copy cli.exe to the sample-directory with the name foo.exe:
|
||||
|
||||
>>> import pkg_resources
|
||||
>>> open(os.path.join(sample_directory, 'foo.exe'), 'wb').write(
|
||||
... pkg_resources.resource_string('setuptools', 'cli.exe')
|
||||
... )
|
||||
|
||||
When the copy of cli.exe, foo.exe in this example, runs, it examines
|
||||
the path name it was run with and computes a Python script path name
|
||||
by removing the '.exe' suffic and adding the '-script.py' suffix. (For
|
||||
GUI programs, the suffix '-script-pyw' is added.) This is why we
|
||||
named out script the way we did. Now we can run out script by running
|
||||
the wrapper:
|
||||
|
||||
>>> import os
|
||||
>>> input, output = os.popen4('"'+nt_quote_arg(os.path.join(sample_directory, 'foo.exe'))
|
||||
... + r' arg1 "arg 2" "arg \"2\\\"" "arg 4\\" "arg5 a\\b"')
|
||||
>>> input.write('hello\nworld\n')
|
||||
>>> input.close()
|
||||
>>> print output.read(),
|
||||
\foo-script.py
|
||||
['arg1', 'arg 2', 'arg "2\\"', 'arg 4\\', 'arg5 a\\\\b']
|
||||
'hello\nworld\n'
|
||||
non-optimized
|
||||
|
||||
This example was a little pathological in that it exercised windows
|
||||
(MS C runtime) quoting rules:
|
||||
|
||||
- Strings containing spaces are surrounded by double quotes.
|
||||
|
||||
- Double quotes in strings need to be escaped by preceding them with
|
||||
back slashes.
|
||||
|
||||
- One or more backslashes preceding double quotes quotes need to be
|
||||
escaped by preceding each of them them with back slashes.
|
||||
|
||||
|
||||
Specifying Python Command-line Options
|
||||
--------------------------------------
|
||||
|
||||
You can specify a single argument on the '#!' line. This can be used
|
||||
to specify Python options like -O, to run in optimized mode or -i
|
||||
to start the interactive interpreter. You can combine multiple
|
||||
options as usual. For example, to run in optimized mode and
|
||||
enter the interpreter after running the script, you could use -Oi:
|
||||
|
||||
>>> open(os.path.join(sample_directory, 'foo-script.py'), 'w').write(
|
||||
... """#!%(python_exe)s -Oi
|
||||
... import sys
|
||||
... input = repr(sys.stdin.read())
|
||||
... print sys.argv[0][-14:]
|
||||
... print sys.argv[1:]
|
||||
... print input
|
||||
... if __debug__:
|
||||
... print 'non-optimized'
|
||||
... sys.ps1 = '---'
|
||||
... """ % dict(python_exe=nt_quote_arg(sys.executable)))
|
||||
|
||||
>>> input, output = os.popen4(nt_quote_arg(os.path.join(sample_directory, 'foo.exe')))
|
||||
>>> input.close()
|
||||
>>> print output.read(),
|
||||
\foo-script.py
|
||||
[]
|
||||
''
|
||||
---
|
||||
|
||||
Testing the GUI Version
|
||||
-----------------------
|
||||
|
||||
Now let's test the GUI version with the simple scipt, bar-script.py:
|
||||
|
||||
>>> import os, sys, tempfile
|
||||
>>> from setuptools.command.easy_install import nt_quote_arg
|
||||
>>> sample_directory = tempfile.mkdtemp()
|
||||
>>> open(os.path.join(sample_directory, 'bar-script.pyw'), 'w').write(
|
||||
... """#!%(python_exe)s
|
||||
... import sys
|
||||
... open(sys.argv[1], 'wb').write(repr(sys.argv[2]))
|
||||
... """ % dict(python_exe=nt_quote_arg(sys.executable)))
|
||||
|
||||
We'll also copy gui.exe to the sample-directory with the name bar.exe:
|
||||
|
||||
>>> import pkg_resources
|
||||
>>> open(os.path.join(sample_directory, 'bar.exe'), 'wb').write(
|
||||
... pkg_resources.resource_string('setuptools', 'gui.exe')
|
||||
... )
|
||||
|
||||
Finally, we'll run the script and check the result:
|
||||
|
||||
>>> import os
|
||||
>>> input, output = os.popen4('"'+nt_quote_arg(os.path.join(sample_directory, 'bar.exe'))
|
||||
... + r' "%s" "Test Argument"' % os.path.join(sample_directory, 'test_output.txt'))
|
||||
>>> input.close()
|
||||
>>> print output.read()
|
||||
<BLANKLINE>
|
||||
>>> print open(os.path.join(sample_directory, 'test_output.txt'), 'rb').read()
|
||||
'Test Argument'
|
||||
|
||||
|
||||
We're done with the sample_directory:
|
||||
|
||||
>>> import shutil
|
||||
>>> shutil.rmtree(sample_directory)
|
||||
|
||||
Vendored
-83
@@ -1,83 +0,0 @@
|
||||
def __boot():
|
||||
import sys, os, os.path
|
||||
PYTHONPATH = os.environ.get('PYTHONPATH')
|
||||
if PYTHONPATH is None or (sys.platform=='win32' and not PYTHONPATH):
|
||||
PYTHONPATH = []
|
||||
else:
|
||||
PYTHONPATH = PYTHONPATH.split(os.pathsep)
|
||||
|
||||
pic = getattr(sys,'path_importer_cache',{})
|
||||
stdpath = sys.path[len(PYTHONPATH):]
|
||||
mydir = os.path.dirname(__file__)
|
||||
#print "searching",stdpath,sys.path
|
||||
|
||||
for item in stdpath:
|
||||
if item==mydir or not item:
|
||||
continue # skip if current dir. on Windows, or my own directory
|
||||
importer = pic.get(item)
|
||||
if importer is not None:
|
||||
loader = importer.find_module('site')
|
||||
if loader is not None:
|
||||
# This should actually reload the current module
|
||||
loader.load_module('site')
|
||||
break
|
||||
else:
|
||||
try:
|
||||
import imp # Avoid import loop in Python >= 3.3
|
||||
stream, path, descr = imp.find_module('site',[item])
|
||||
except ImportError:
|
||||
continue
|
||||
if stream is None:
|
||||
continue
|
||||
try:
|
||||
# This should actually reload the current module
|
||||
imp.load_module('site',stream,path,descr)
|
||||
finally:
|
||||
stream.close()
|
||||
break
|
||||
else:
|
||||
raise ImportError("Couldn't find the real 'site' module")
|
||||
|
||||
#print "loaded", __file__
|
||||
|
||||
known_paths = dict([(makepath(item)[1],1) for item in sys.path]) # 2.2 comp
|
||||
|
||||
oldpos = getattr(sys,'__egginsert',0) # save old insertion position
|
||||
sys.__egginsert = 0 # and reset the current one
|
||||
|
||||
for item in PYTHONPATH:
|
||||
addsitedir(item)
|
||||
|
||||
sys.__egginsert += oldpos # restore effective old position
|
||||
|
||||
d,nd = makepath(stdpath[0])
|
||||
insert_at = None
|
||||
new_path = []
|
||||
|
||||
for item in sys.path:
|
||||
p,np = makepath(item)
|
||||
|
||||
if np==nd and insert_at is None:
|
||||
# We've hit the first 'system' path entry, so added entries go here
|
||||
insert_at = len(new_path)
|
||||
|
||||
if np in known_paths or insert_at is None:
|
||||
new_path.append(item)
|
||||
else:
|
||||
# new path after the insert point, back-insert it
|
||||
new_path.insert(insert_at, item)
|
||||
insert_at += 1
|
||||
|
||||
sys.path[:] = new_path
|
||||
|
||||
if __name__=='site':
|
||||
__boot()
|
||||
del __boot
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-330
@@ -1,330 +0,0 @@
|
||||
Pluggable Distributions of Python Software
|
||||
==========================================
|
||||
|
||||
Distributions
|
||||
-------------
|
||||
|
||||
A "Distribution" is a collection of files that represent a "Release" of a
|
||||
"Project" as of a particular point in time, denoted by a
|
||||
"Version"::
|
||||
|
||||
>>> import sys, pkg_resources
|
||||
>>> from pkg_resources import Distribution
|
||||
>>> Distribution(project_name="Foo", version="1.2")
|
||||
Foo 1.2
|
||||
|
||||
Distributions have a location, which can be a filename, URL, or really anything
|
||||
else you care to use::
|
||||
|
||||
>>> dist = Distribution(
|
||||
... location="http://example.com/something",
|
||||
... project_name="Bar", version="0.9"
|
||||
... )
|
||||
|
||||
>>> dist
|
||||
Bar 0.9 (http://example.com/something)
|
||||
|
||||
|
||||
Distributions have various introspectable attributes::
|
||||
|
||||
>>> dist.location
|
||||
'http://example.com/something'
|
||||
|
||||
>>> dist.project_name
|
||||
'Bar'
|
||||
|
||||
>>> dist.version
|
||||
'0.9'
|
||||
|
||||
>>> dist.py_version == sys.version[:3]
|
||||
True
|
||||
|
||||
>>> print dist.platform
|
||||
None
|
||||
|
||||
Including various computed attributes::
|
||||
|
||||
>>> from pkg_resources import parse_version
|
||||
>>> dist.parsed_version == parse_version(dist.version)
|
||||
True
|
||||
|
||||
>>> dist.key # case-insensitive form of the project name
|
||||
'bar'
|
||||
|
||||
Distributions are compared (and hashed) by version first::
|
||||
|
||||
>>> Distribution(version='1.0') == Distribution(version='1.0')
|
||||
True
|
||||
>>> Distribution(version='1.0') == Distribution(version='1.1')
|
||||
False
|
||||
>>> Distribution(version='1.0') < Distribution(version='1.1')
|
||||
True
|
||||
|
||||
but also by project name (case-insensitive), platform, Python version,
|
||||
location, etc.::
|
||||
|
||||
>>> Distribution(project_name="Foo",version="1.0") == \
|
||||
... Distribution(project_name="Foo",version="1.0")
|
||||
True
|
||||
|
||||
>>> Distribution(project_name="Foo",version="1.0") == \
|
||||
... Distribution(project_name="foo",version="1.0")
|
||||
True
|
||||
|
||||
>>> Distribution(project_name="Foo",version="1.0") == \
|
||||
... Distribution(project_name="Foo",version="1.1")
|
||||
False
|
||||
|
||||
>>> Distribution(project_name="Foo",py_version="2.3",version="1.0") == \
|
||||
... Distribution(project_name="Foo",py_version="2.4",version="1.0")
|
||||
False
|
||||
|
||||
>>> Distribution(location="spam",version="1.0") == \
|
||||
... Distribution(location="spam",version="1.0")
|
||||
True
|
||||
|
||||
>>> Distribution(location="spam",version="1.0") == \
|
||||
... Distribution(location="baz",version="1.0")
|
||||
False
|
||||
|
||||
|
||||
|
||||
Hash and compare distribution by prio/plat
|
||||
|
||||
Get version from metadata
|
||||
provider capabilities
|
||||
egg_name()
|
||||
as_requirement()
|
||||
from_location, from_filename (w/path normalization)
|
||||
|
||||
Releases may have zero or more "Requirements", which indicate
|
||||
what releases of another project the release requires in order to
|
||||
function. A Requirement names the other project, expresses some criteria
|
||||
as to what releases of that project are acceptable, and lists any "Extras"
|
||||
that the requiring release may need from that project. (An Extra is an
|
||||
optional feature of a Release, that can only be used if its additional
|
||||
Requirements are satisfied.)
|
||||
|
||||
|
||||
|
||||
The Working Set
|
||||
---------------
|
||||
|
||||
A collection of active distributions is called a Working Set. Note that a
|
||||
Working Set can contain any importable distribution, not just pluggable ones.
|
||||
For example, the Python standard library is an importable distribution that
|
||||
will usually be part of the Working Set, even though it is not pluggable.
|
||||
Similarly, when you are doing development work on a project, the files you are
|
||||
editing are also a Distribution. (And, with a little attention to the
|
||||
directory names used, and including some additional metadata, such a
|
||||
"development distribution" can be made pluggable as well.)
|
||||
|
||||
>>> from pkg_resources import WorkingSet, VersionConflict
|
||||
|
||||
A working set's entries are the sys.path entries that correspond to the active
|
||||
distributions. By default, the working set's entries are the items on
|
||||
``sys.path``::
|
||||
|
||||
>>> ws = WorkingSet()
|
||||
>>> ws.entries == sys.path
|
||||
True
|
||||
|
||||
But you can also create an empty working set explicitly, and add distributions
|
||||
to it::
|
||||
|
||||
>>> ws = WorkingSet([])
|
||||
>>> ws.add(dist)
|
||||
>>> ws.entries
|
||||
['http://example.com/something']
|
||||
>>> dist in ws
|
||||
True
|
||||
>>> Distribution('foo',version="") in ws
|
||||
False
|
||||
|
||||
And you can iterate over its distributions::
|
||||
|
||||
>>> list(ws)
|
||||
[Bar 0.9 (http://example.com/something)]
|
||||
|
||||
Adding the same distribution more than once is a no-op::
|
||||
|
||||
>>> ws.add(dist)
|
||||
>>> list(ws)
|
||||
[Bar 0.9 (http://example.com/something)]
|
||||
|
||||
For that matter, adding multiple distributions for the same project also does
|
||||
nothing, because a working set can only hold one active distribution per
|
||||
project -- the first one added to it::
|
||||
|
||||
>>> ws.add(
|
||||
... Distribution(
|
||||
... 'http://example.com/something', project_name="Bar",
|
||||
... version="7.2"
|
||||
... )
|
||||
... )
|
||||
>>> list(ws)
|
||||
[Bar 0.9 (http://example.com/something)]
|
||||
|
||||
You can append a path entry to a working set using ``add_entry()``::
|
||||
|
||||
>>> ws.entries
|
||||
['http://example.com/something']
|
||||
>>> ws.add_entry(pkg_resources.__file__)
|
||||
>>> ws.entries == ['http://example.com/something', pkg_resources.__file__]
|
||||
True
|
||||
|
||||
Multiple additions result in multiple entries, even if the entry is already in
|
||||
the working set (because ``sys.path`` can contain the same entry more than
|
||||
once)::
|
||||
|
||||
>>> ws.add_entry(pkg_resources.__file__)
|
||||
>>> ws.entries
|
||||
['...example.com...', '...pkg_resources...', '...pkg_resources...']
|
||||
|
||||
And you can specify the path entry a distribution was found under, using the
|
||||
optional second parameter to ``add()``::
|
||||
|
||||
>>> ws = WorkingSet([])
|
||||
>>> ws.add(dist,"foo")
|
||||
>>> ws.entries
|
||||
['foo']
|
||||
|
||||
But even if a distribution is found under multiple path entries, it still only
|
||||
shows up once when iterating the working set:
|
||||
|
||||
>>> ws.add_entry(ws.entries[0])
|
||||
>>> list(ws)
|
||||
[Bar 0.9 (http://example.com/something)]
|
||||
|
||||
You can ask a WorkingSet to ``find()`` a distribution matching a requirement::
|
||||
|
||||
>>> from pkg_resources import Requirement
|
||||
>>> print ws.find(Requirement.parse("Foo==1.0")) # no match, return None
|
||||
None
|
||||
|
||||
>>> ws.find(Requirement.parse("Bar==0.9")) # match, return distribution
|
||||
Bar 0.9 (http://example.com/something)
|
||||
|
||||
Note that asking for a conflicting version of a distribution already in a
|
||||
working set triggers a ``pkg_resources.VersionConflict`` error:
|
||||
|
||||
>>> try:
|
||||
... ws.find(Requirement.parse("Bar==1.0"))
|
||||
... except VersionConflict:
|
||||
... print 'ok'
|
||||
ok
|
||||
|
||||
You can subscribe a callback function to receive notifications whenever a new
|
||||
distribution is added to a working set. The callback is immediately invoked
|
||||
once for each existing distribution in the working set, and then is called
|
||||
again for new distributions added thereafter::
|
||||
|
||||
>>> def added(dist): print "Added", dist
|
||||
>>> ws.subscribe(added)
|
||||
Added Bar 0.9
|
||||
>>> foo12 = Distribution(project_name="Foo", version="1.2", location="f12")
|
||||
>>> ws.add(foo12)
|
||||
Added Foo 1.2
|
||||
|
||||
Note, however, that only the first distribution added for a given project name
|
||||
will trigger a callback, even during the initial ``subscribe()`` callback::
|
||||
|
||||
>>> foo14 = Distribution(project_name="Foo", version="1.4", location="f14")
|
||||
>>> ws.add(foo14) # no callback, because Foo 1.2 is already active
|
||||
|
||||
>>> ws = WorkingSet([])
|
||||
>>> ws.add(foo12)
|
||||
>>> ws.add(foo14)
|
||||
>>> ws.subscribe(added)
|
||||
Added Foo 1.2
|
||||
|
||||
And adding a callback more than once has no effect, either::
|
||||
|
||||
>>> ws.subscribe(added) # no callbacks
|
||||
|
||||
# and no double-callbacks on subsequent additions, either
|
||||
>>> just_a_test = Distribution(project_name="JustATest", version="0.99")
|
||||
>>> ws.add(just_a_test)
|
||||
Added JustATest 0.99
|
||||
|
||||
|
||||
Finding Plugins
|
||||
---------------
|
||||
|
||||
``WorkingSet`` objects can be used to figure out what plugins in an
|
||||
``Environment`` can be loaded without any resolution errors::
|
||||
|
||||
>>> from pkg_resources import Environment
|
||||
|
||||
>>> plugins = Environment([]) # normally, a list of plugin directories
|
||||
>>> plugins.add(foo12)
|
||||
>>> plugins.add(foo14)
|
||||
>>> plugins.add(just_a_test)
|
||||
|
||||
In the simplest case, we just get the newest version of each distribution in
|
||||
the plugin environment::
|
||||
|
||||
>>> ws = WorkingSet([])
|
||||
>>> ws.find_plugins(plugins)
|
||||
([JustATest 0.99, Foo 1.4 (f14)], {})
|
||||
|
||||
But if there's a problem with a version conflict or missing requirements, the
|
||||
method falls back to older versions, and the error info dict will contain an
|
||||
exception instance for each unloadable plugin::
|
||||
|
||||
>>> ws.add(foo12) # this will conflict with Foo 1.4
|
||||
>>> ws.find_plugins(plugins)
|
||||
([JustATest 0.99, Foo 1.2 (f12)], {Foo 1.4 (f14): VersionConflict(...)})
|
||||
|
||||
But if you disallow fallbacks, the failed plugin will be skipped instead of
|
||||
trying older versions::
|
||||
|
||||
>>> ws.find_plugins(plugins, fallback=False)
|
||||
([JustATest 0.99], {Foo 1.4 (f14): VersionConflict(...)})
|
||||
|
||||
|
||||
|
||||
Platform Compatibility Rules
|
||||
----------------------------
|
||||
|
||||
On the Mac, there are potential compatibility issues for modules compiled
|
||||
on newer versions of Mac OS X than what the user is running. Additionally,
|
||||
Mac OS X will soon have two platforms to contend with: Intel and PowerPC.
|
||||
|
||||
Basic equality works as on other platforms::
|
||||
|
||||
>>> from pkg_resources import compatible_platforms as cp
|
||||
>>> reqd = 'macosx-10.4-ppc'
|
||||
>>> cp(reqd, reqd)
|
||||
True
|
||||
>>> cp("win32", reqd)
|
||||
False
|
||||
|
||||
Distributions made on other machine types are not compatible::
|
||||
|
||||
>>> cp("macosx-10.4-i386", reqd)
|
||||
False
|
||||
|
||||
Distributions made on earlier versions of the OS are compatible, as
|
||||
long as they are from the same top-level version. The patchlevel version
|
||||
number does not matter::
|
||||
|
||||
>>> cp("macosx-10.4-ppc", reqd)
|
||||
True
|
||||
>>> cp("macosx-10.3-ppc", reqd)
|
||||
True
|
||||
>>> cp("macosx-10.5-ppc", reqd)
|
||||
False
|
||||
>>> cp("macosx-9.5-ppc", reqd)
|
||||
False
|
||||
|
||||
Backwards compatibility for packages made via earlier versions of
|
||||
setuptools is provided as well::
|
||||
|
||||
>>> cp("darwin-8.2.0-Power_Macintosh", reqd)
|
||||
True
|
||||
>>> cp("darwin-7.2.0-Power_Macintosh", reqd)
|
||||
True
|
||||
>>> cp("darwin-8.2.0-Power_Macintosh", "macosx-10.3-ppc")
|
||||
False
|
||||
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
import urllib2
|
||||
import sys
|
||||
import os
|
||||
|
||||
if os.path.exists('distribute_setup.py'):
|
||||
print 'distribute_setup.py exists in the current dir, aborting'
|
||||
sys.exit(2)
|
||||
|
||||
print '**** Starting Test'
|
||||
print '\n\n'
|
||||
|
||||
is_jython = sys.platform.startswith('java')
|
||||
if is_jython:
|
||||
import subprocess
|
||||
|
||||
print 'Downloading bootstrap'
|
||||
file = urllib2.urlopen('http://nightly.ziade.org/distribute_setup.py')
|
||||
f = open('distribute_setup.py', 'w')
|
||||
f.write(file.read())
|
||||
f.close()
|
||||
|
||||
# running it
|
||||
args = [sys.executable] + ['distribute_setup.py']
|
||||
if is_jython:
|
||||
res = subprocess.call(args)
|
||||
else:
|
||||
res = os.spawnv(os.P_WAIT, sys.executable, args)
|
||||
|
||||
if res != 0:
|
||||
print '**** Test failed, please send me the output at tarek@ziade.org'
|
||||
os.remove('distribute_setup.py')
|
||||
sys.exit(2)
|
||||
|
||||
# now checking if Distribute is installed
|
||||
script = """\
|
||||
import sys
|
||||
try:
|
||||
import setuptools
|
||||
except ImportError:
|
||||
sys.exit(0)
|
||||
|
||||
sys.exit(hasattr(setuptools, "_distribute"))
|
||||
"""
|
||||
|
||||
root = 'script'
|
||||
seed = 0
|
||||
script_name = '%s%d.py' % (root, seed)
|
||||
|
||||
while os.path.exists(script_name):
|
||||
seed += 1
|
||||
script_name = '%s%d.py' % (root, seed)
|
||||
|
||||
f = open(script_name, 'w')
|
||||
try:
|
||||
f.write(script)
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
try:
|
||||
args = [sys.executable] + [script_name]
|
||||
if is_jython:
|
||||
res = subprocess.call(args)
|
||||
else:
|
||||
res = os.spawnv(os.P_WAIT, sys.executable, args)
|
||||
|
||||
print '\n\n'
|
||||
if res:
|
||||
print '**** Test is OK'
|
||||
else:
|
||||
print '**** Test failed, please send me the output at tarek@ziade.org'
|
||||
finally:
|
||||
if os.path.exists(script_name):
|
||||
os.remove(script_name)
|
||||
os.remove('distribute_setup.py')
|
||||
|
||||
-110
@@ -1,110 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] >= 3:
|
||||
raise NotImplementedError('Py3 not supported in this test yet')
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from distutils.command.install import INSTALL_SCHEMES
|
||||
from string import Template
|
||||
from urllib2 import urlopen
|
||||
|
||||
try:
|
||||
import subprocess
|
||||
def _system_call(*args):
|
||||
assert subprocess.call(args) == 0
|
||||
except ImportError:
|
||||
# Python 2.3
|
||||
def _system_call(*args):
|
||||
# quoting arguments if windows
|
||||
if sys.platform == 'win32':
|
||||
def quote(arg):
|
||||
if ' ' in arg:
|
||||
return '"%s"' % arg
|
||||
return arg
|
||||
args = [quote(arg) for arg in args]
|
||||
assert os.system(' '.join(args)) == 0
|
||||
|
||||
def tempdir(func):
|
||||
def _tempdir(*args, **kwargs):
|
||||
test_dir = tempfile.mkdtemp()
|
||||
old_dir = os.getcwd()
|
||||
os.chdir(test_dir)
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
finally:
|
||||
os.chdir(old_dir)
|
||||
shutil.rmtree(test_dir)
|
||||
return _tempdir
|
||||
|
||||
SIMPLE_BUILDOUT = """\
|
||||
[buildout]
|
||||
|
||||
parts = eggs
|
||||
|
||||
[eggs]
|
||||
recipe = zc.recipe.egg
|
||||
|
||||
eggs =
|
||||
extensions
|
||||
"""
|
||||
|
||||
BOOTSTRAP = 'http://python-distribute.org/bootstrap.py'
|
||||
PYVER = sys.version.split()[0][:3]
|
||||
DEV_URL = 'http://bitbucket.org/tarek/distribute/get/0.6-maintenance.zip#egg=distribute-dev'
|
||||
|
||||
_VARS = {'base': '.',
|
||||
'py_version_short': PYVER}
|
||||
|
||||
if sys.platform == 'win32':
|
||||
PURELIB = INSTALL_SCHEMES['nt']['purelib']
|
||||
else:
|
||||
PURELIB = INSTALL_SCHEMES['unix_prefix']['purelib']
|
||||
|
||||
|
||||
@tempdir
|
||||
def test_virtualenv():
|
||||
"""virtualenv with distribute"""
|
||||
purelib = os.path.abspath(Template(PURELIB).substitute(**_VARS))
|
||||
_system_call('virtualenv', '--no-site-packages', '.', '--distribute')
|
||||
_system_call('bin/easy_install', 'distribute==dev')
|
||||
# linux specific
|
||||
site_pkg = os.listdir(purelib)
|
||||
site_pkg.sort()
|
||||
assert 'distribute' in site_pkg[0]
|
||||
easy_install = os.path.join(purelib, 'easy-install.pth')
|
||||
with open(easy_install) as f:
|
||||
res = f.read()
|
||||
assert 'distribute' in res
|
||||
assert 'setuptools' not in res
|
||||
|
||||
@tempdir
|
||||
def test_full():
|
||||
"""virtualenv + pip + buildout"""
|
||||
_system_call('virtualenv', '--no-site-packages', '.')
|
||||
_system_call('bin/easy_install', '-q', 'distribute==dev')
|
||||
_system_call('bin/easy_install', '-qU', 'distribute==dev')
|
||||
_system_call('bin/easy_install', '-q', 'pip')
|
||||
_system_call('bin/pip', 'install', '-q', 'zc.buildout')
|
||||
|
||||
with open('buildout.cfg', 'w') as f:
|
||||
f.write(SIMPLE_BUILDOUT)
|
||||
|
||||
with open('bootstrap.py', 'w') as f:
|
||||
f.write(urlopen(BOOTSTRAP).read())
|
||||
|
||||
_system_call('bin/python', 'bootstrap.py', '--distribute')
|
||||
_system_call('bin/buildout', '-q')
|
||||
eggs = os.listdir('eggs')
|
||||
eggs.sort()
|
||||
assert len(eggs) == 3
|
||||
assert eggs[0].startswith('distribute')
|
||||
assert eggs[1:] == ['extensions-0.3-py2.6.egg',
|
||||
'zc.recipe.egg-1.2.2-py2.6.egg']
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_virtualenv()
|
||||
test_full()
|
||||
|
||||
-168
@@ -1,168 +0,0 @@
|
||||
/* Generated by Pyrex 0.9.3 on Thu Jan 05 17:47:12 2006 */
|
||||
|
||||
#include "Python.h"
|
||||
#include "structmember.h"
|
||||
#ifndef PY_LONG_LONG
|
||||
#define PY_LONG_LONG LONG_LONG
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct {PyObject **p; char *s;} __Pyx_InternTabEntry; /*proto*/
|
||||
typedef struct {PyObject **p; char *s; long n;} __Pyx_StringTabEntry; /*proto*/
|
||||
static PyObject *__Pyx_UnpackItem(PyObject *, int); /*proto*/
|
||||
static int __Pyx_EndUnpack(PyObject *, int); /*proto*/
|
||||
static int __Pyx_PrintItem(PyObject *); /*proto*/
|
||||
static int __Pyx_PrintNewline(void); /*proto*/
|
||||
static void __Pyx_Raise(PyObject *type, PyObject *value, PyObject *tb); /*proto*/
|
||||
static void __Pyx_ReRaise(void); /*proto*/
|
||||
static PyObject *__Pyx_Import(PyObject *name, PyObject *from_list); /*proto*/
|
||||
static PyObject *__Pyx_GetExcValue(void); /*proto*/
|
||||
static int __Pyx_ArgTypeTest(PyObject *obj, PyTypeObject *type, int none_allowed, char *name); /*proto*/
|
||||
static int __Pyx_TypeTest(PyObject *obj, PyTypeObject *type); /*proto*/
|
||||
static int __Pyx_GetStarArgs(PyObject **args, PyObject **kwds, char *kwd_list[], int nargs, PyObject **args2, PyObject **kwds2); /*proto*/
|
||||
static void __Pyx_WriteUnraisable(char *name); /*proto*/
|
||||
static void __Pyx_AddTraceback(char *funcname); /*proto*/
|
||||
static PyTypeObject *__Pyx_ImportType(char *module_name, char *class_name, long size); /*proto*/
|
||||
static int __Pyx_SetVtable(PyObject *dict, void *vtable); /*proto*/
|
||||
static int __Pyx_GetVtable(PyObject *dict, void *vtabptr); /*proto*/
|
||||
static PyObject *__Pyx_CreateClass(PyObject *bases, PyObject *dict, PyObject *name, char *modname); /*proto*/
|
||||
static int __Pyx_InternStrings(__Pyx_InternTabEntry *t); /*proto*/
|
||||
static int __Pyx_InitStrings(__Pyx_StringTabEntry *t); /*proto*/
|
||||
static PyObject *__Pyx_GetName(PyObject *dict, PyObject *name); /*proto*/
|
||||
|
||||
static PyObject *__pyx_m;
|
||||
static PyObject *__pyx_b;
|
||||
static int __pyx_lineno;
|
||||
static char *__pyx_filename;
|
||||
staticforward char **__pyx_f;
|
||||
|
||||
/* Declarations from hello */
|
||||
|
||||
char (*(get_hello_msg(void))); /*proto*/
|
||||
|
||||
/* Implementation of hello */
|
||||
|
||||
static PyObject *__pyx_n_hello;
|
||||
|
||||
static PyObject *__pyx_f_5hello_hello(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
|
||||
static PyObject *__pyx_f_5hello_hello(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
|
||||
PyObject *__pyx_r;
|
||||
PyObject *__pyx_1 = 0;
|
||||
static char *__pyx_argnames[] = {0};
|
||||
if (!PyArg_ParseTupleAndKeywords(__pyx_args, __pyx_kwds, "", __pyx_argnames)) return 0;
|
||||
|
||||
/* "C:\cygwin\home\pje\setuptools\tests\shlib_test\hello.pyx":4 */
|
||||
__pyx_1 = PyString_FromString(get_hello_msg()); if (!__pyx_1) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 4; goto __pyx_L1;}
|
||||
__pyx_r = __pyx_1;
|
||||
__pyx_1 = 0;
|
||||
goto __pyx_L0;
|
||||
|
||||
__pyx_r = Py_None; Py_INCREF(__pyx_r);
|
||||
goto __pyx_L0;
|
||||
__pyx_L1:;
|
||||
Py_XDECREF(__pyx_1);
|
||||
__Pyx_AddTraceback("hello.hello");
|
||||
__pyx_r = 0;
|
||||
__pyx_L0:;
|
||||
return __pyx_r;
|
||||
}
|
||||
|
||||
static __Pyx_InternTabEntry __pyx_intern_tab[] = {
|
||||
{&__pyx_n_hello, "hello"},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static struct PyMethodDef __pyx_methods[] = {
|
||||
{"hello", (PyCFunction)__pyx_f_5hello_hello, METH_VARARGS|METH_KEYWORDS, 0},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
DL_EXPORT(void) inithello(void); /*proto*/
|
||||
DL_EXPORT(void) inithello(void) {
|
||||
__pyx_m = Py_InitModule4("hello", __pyx_methods, 0, 0, PYTHON_API_VERSION);
|
||||
if (!__pyx_m) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; goto __pyx_L1;};
|
||||
__pyx_b = PyImport_AddModule("__builtin__");
|
||||
if (!__pyx_b) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; goto __pyx_L1;};
|
||||
if (PyObject_SetAttrString(__pyx_m, "__builtins__", __pyx_b) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; goto __pyx_L1;};
|
||||
if (__Pyx_InternStrings(__pyx_intern_tab) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 1; goto __pyx_L1;};
|
||||
|
||||
/* "C:\cygwin\home\pje\setuptools\tests\shlib_test\hello.pyx":3 */
|
||||
return;
|
||||
__pyx_L1:;
|
||||
__Pyx_AddTraceback("hello");
|
||||
}
|
||||
|
||||
static char *__pyx_filenames[] = {
|
||||
"hello.pyx",
|
||||
};
|
||||
statichere char **__pyx_f = __pyx_filenames;
|
||||
|
||||
/* Runtime support code */
|
||||
|
||||
static int __Pyx_InternStrings(__Pyx_InternTabEntry *t) {
|
||||
while (t->p) {
|
||||
*t->p = PyString_InternFromString(t->s);
|
||||
if (!*t->p)
|
||||
return -1;
|
||||
++t;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "compile.h"
|
||||
#include "frameobject.h"
|
||||
#include "traceback.h"
|
||||
|
||||
static void __Pyx_AddTraceback(char *funcname) {
|
||||
PyObject *py_srcfile = 0;
|
||||
PyObject *py_funcname = 0;
|
||||
PyObject *py_globals = 0;
|
||||
PyObject *empty_tuple = 0;
|
||||
PyObject *empty_string = 0;
|
||||
PyCodeObject *py_code = 0;
|
||||
PyFrameObject *py_frame = 0;
|
||||
|
||||
py_srcfile = PyString_FromString(__pyx_filename);
|
||||
if (!py_srcfile) goto bad;
|
||||
py_funcname = PyString_FromString(funcname);
|
||||
if (!py_funcname) goto bad;
|
||||
py_globals = PyModule_GetDict(__pyx_m);
|
||||
if (!py_globals) goto bad;
|
||||
empty_tuple = PyTuple_New(0);
|
||||
if (!empty_tuple) goto bad;
|
||||
empty_string = PyString_FromString("");
|
||||
if (!empty_string) goto bad;
|
||||
py_code = PyCode_New(
|
||||
0, /*int argcount,*/
|
||||
0, /*int nlocals,*/
|
||||
0, /*int stacksize,*/
|
||||
0, /*int flags,*/
|
||||
empty_string, /*PyObject *code,*/
|
||||
empty_tuple, /*PyObject *consts,*/
|
||||
empty_tuple, /*PyObject *names,*/
|
||||
empty_tuple, /*PyObject *varnames,*/
|
||||
empty_tuple, /*PyObject *freevars,*/
|
||||
empty_tuple, /*PyObject *cellvars,*/
|
||||
py_srcfile, /*PyObject *filename,*/
|
||||
py_funcname, /*PyObject *name,*/
|
||||
__pyx_lineno, /*int firstlineno,*/
|
||||
empty_string /*PyObject *lnotab*/
|
||||
);
|
||||
if (!py_code) goto bad;
|
||||
py_frame = PyFrame_New(
|
||||
PyThreadState_Get(), /*PyThreadState *tstate,*/
|
||||
py_code, /*PyCodeObject *code,*/
|
||||
py_globals, /*PyObject *globals,*/
|
||||
0 /*PyObject *locals*/
|
||||
);
|
||||
if (!py_frame) goto bad;
|
||||
py_frame->f_lineno = __pyx_lineno;
|
||||
PyTraceBack_Here(py_frame);
|
||||
bad:
|
||||
Py_XDECREF(py_srcfile);
|
||||
Py_XDECREF(py_funcname);
|
||||
Py_XDECREF(empty_tuple);
|
||||
Py_XDECREF(empty_string);
|
||||
Py_XDECREF(py_code);
|
||||
Py_XDECREF(py_frame);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
cdef extern char *get_hello_msg()
|
||||
|
||||
def hello():
|
||||
return get_hello_msg()
|
||||
@@ -1,3 +0,0 @@
|
||||
extern char* get_hello_msg() {
|
||||
return "Hello, world!";
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
from setuptools import setup, Extension, Library
|
||||
|
||||
setup(
|
||||
name="shlib_test",
|
||||
ext_modules = [
|
||||
Library("hellolib", ["hellolib.c"]),
|
||||
Extension("hello", ["hello.pyx"], libraries=["hellolib"])
|
||||
],
|
||||
test_suite="test_hello.HelloWorldTest",
|
||||
)
|
||||
@@ -1,7 +0,0 @@
|
||||
from unittest import TestCase
|
||||
|
||||
class HelloWorldTest(TestCase):
|
||||
def testHelloMsg(self):
|
||||
from hello import hello
|
||||
self.assertEqual(hello(), "Hello, world!")
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import sys
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
import shutil
|
||||
import copy
|
||||
|
||||
CURDIR = os.path.abspath(os.path.dirname(__file__))
|
||||
TOPDIR = os.path.split(CURDIR)[0]
|
||||
sys.path.insert(0, TOPDIR)
|
||||
|
||||
from distribute_setup import (use_setuptools, _build_egg, _python_cmd,
|
||||
_do_download, _install, DEFAULT_URL,
|
||||
DEFAULT_VERSION)
|
||||
import distribute_setup
|
||||
|
||||
class TestSetup(unittest.TestCase):
|
||||
|
||||
def urlopen(self, url):
|
||||
return open(self.tarball)
|
||||
|
||||
def setUp(self):
|
||||
self.old_sys_path = copy.copy(sys.path)
|
||||
self.cwd = os.getcwd()
|
||||
self.tmpdir = tempfile.mkdtemp()
|
||||
os.chdir(TOPDIR)
|
||||
_python_cmd("setup.py", "-q", "egg_info", "-RDb", "''", "sdist",
|
||||
"--dist-dir", "%s" % self.tmpdir)
|
||||
tarball = os.listdir(self.tmpdir)[0]
|
||||
self.tarball = os.path.join(self.tmpdir, tarball)
|
||||
import urllib2
|
||||
urllib2.urlopen = self.urlopen
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tmpdir)
|
||||
os.chdir(self.cwd)
|
||||
sys.path = copy.copy(self.old_sys_path)
|
||||
|
||||
def test_build_egg(self):
|
||||
# making it an egg
|
||||
egg = _build_egg(self.tarball, self.tmpdir)
|
||||
|
||||
# now trying to import it
|
||||
sys.path[0] = egg
|
||||
import setuptools
|
||||
self.assertTrue(setuptools.__file__.startswith(egg))
|
||||
|
||||
def test_do_download(self):
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
_do_download(DEFAULT_VERSION, DEFAULT_URL, tmpdir, 1)
|
||||
import setuptools
|
||||
self.assertTrue(setuptools.bootstrap_install_from.startswith(tmpdir))
|
||||
|
||||
def test_install(self):
|
||||
def _faked(*args):
|
||||
return True
|
||||
distribute_setup.python_cmd = _faked
|
||||
_install(self.tarball)
|
||||
|
||||
def test_use_setuptools(self):
|
||||
self.assertEqual(use_setuptools(), None)
|
||||
|
||||
# make sure fake_setuptools is not called by default
|
||||
import pkg_resources
|
||||
del pkg_resources._distribute
|
||||
def fake_setuptools(*args):
|
||||
raise AssertionError
|
||||
|
||||
pkg_resources._fake_setuptools = fake_setuptools
|
||||
use_setuptools()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user