aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2013-06-18 14:08:20 -0500
committerJason R. Coombs <jaraco@jaraco.com>2013-06-18 14:08:20 -0500
commita85aa143f971ebfbb31ccaf58cb82a311f9315c6 (patch)
treeeeae1529ae15884aca3301fefae768fc2e531a18
parent94fc39cb62df19e85b07658f2fa5d0b4a7bf9303 (diff)
parent641eac6550896506fa939205f249bcfb8f057d57 (diff)
downloadexternal_python_setuptools-a85aa143f971ebfbb31ccaf58cb82a311f9315c6.tar.gz
external_python_setuptools-a85aa143f971ebfbb31ccaf58cb82a311f9315c6.tar.bz2
external_python_setuptools-a85aa143f971ebfbb31ccaf58cb82a311f9315c6.zip
Merge Vinay Sajip's unified Python 2/3 support from distribute 3
--HG-- branch : distribute
-rw-r--r--.hgignore1
-rw-r--r--.hgtags18
-rw-r--r--.travis.yml1
-rw-r--r--CHANGES.txt219
-rw-r--r--CONTRIBUTORS.txt5
-rw-r--r--MANIFEST.in2
-rwxr-xr-xREADME.txt42
-rw-r--r--_markerlib/markers.py39
-rw-r--r--distribute_setup.py35
-rw-r--r--docs/conf.py4
-rw-r--r--docs/setuptools.txt60
-rwxr-xr-xlauncher.c8
-rw-r--r--msvc-build-launcher.cmd58
-rw-r--r--pkg_resources.py150
-rw-r--r--release.py85
-rwxr-xr-xsetup.py39
-rwxr-xr-xsetuptools/archive_util.py3
-rw-r--r--setuptools/cli-32.exebin69632 -> 65536 bytes
-rw-r--r--setuptools/cli-64.exebin75264 -> 74752 bytes
-rw-r--r--setuptools/cli-arm-32.exebin0 -> 69120 bytes
-rw-r--r--setuptools/cli.exebin69632 -> 65536 bytes
-rwxr-xr-xsetuptools/command/develop.py4
-rwxr-xr-xsetuptools/command/easy_install.py64
-rwxr-xr-xsetuptools/command/egg_info.py46
-rw-r--r--setuptools/command/launcher manifest.xml15
-rwxr-xr-xsetuptools/command/sdist.py7
-rwxr-xr-xsetuptools/command/upload.py5
-rw-r--r--setuptools/command/upload_docs.py128
-rw-r--r--setuptools/dist.py5
-rw-r--r--setuptools/gui-32.exebin65536 -> 65536 bytes
-rw-r--r--setuptools/gui-64.exebin75264 -> 75264 bytes
-rw-r--r--setuptools/gui-arm-32.exebin0 -> 69120 bytes
-rw-r--r--setuptools/gui.exebin65536 -> 65536 bytes
-rwxr-xr-xsetuptools/package_index.py91
-rw-r--r--setuptools/py24compat.py11
-rwxr-xr-xsetuptools/sandbox.py5
-rw-r--r--[-rwxr-xr-x]setuptools/site-patch.py (renamed from site.py)0
-rw-r--r--setuptools/tests/doctest.py8
-rw-r--r--setuptools/tests/test_develop.py12
-rw-r--r--setuptools/tests/test_dist_info.py12
-rw-r--r--setuptools/tests/test_easy_install.py4
-rw-r--r--setuptools/tests/test_markerlib.py18
-rw-r--r--setuptools/tests/test_resources.py30
-rw-r--r--setuptools/tests/test_sdist.py347
-rw-r--r--setuptools/tests/test_upload_docs.py15
-rw-r--r--setuptools/tests/win_script_wrapper.txt18
-rw-r--r--tests/test_pkg_resources.py61
47 files changed, 1276 insertions, 399 deletions
diff --git a/.hgignore b/.hgignore
index 21ec620a..4e5d0bcf 100644
--- a/.hgignore
+++ b/.hgignore
@@ -11,3 +11,4 @@ bin
include
\.Python
*.swp
+CHANGES (links).txt
diff --git a/.hgtags b/.hgtags
index 287e85d2..c4b02145 100644
--- a/.hgtags
+++ b/.hgtags
@@ -37,3 +37,21 @@ de44acab3cfce1f5bc811d6c0fa1a88ca0e9533f 0.6.21
b69f072c000237435e17b8bbb304ba6f957283eb 0.6.26
469c3b948e41ef28752b3cdf3c7fb9618355ebf5 0.6.27
fc379e63586ad3c6838e1bda216548ba8270b8f0 0.6.28
+4f82563d0f5d1af1fb215c0ac87f38b16bb5c42d 0.6.29
+7464fc916fa4d8308e34e45a1198512fe04c97b4 0.6.30
+17bc972d67edd96c7748061910172e1200a73efe 0.6.31
+b1a7f86b315a1f8c20036d718d6dc641bb84cac6 0.6.32
+6acac3919ae9a7dba2cbecbe3d4b31ece25d5f09 0.6.33
+23c310bf4ae8e4616e37027f08891702f5a33bc9 0.6.34
+2abe1117543be0edbafb10c7c159d1bcb1cb1b87 0.6.35
+c813a29e831f266d427d4a4bce3da97f475a8eee 0.6.36
+be6f65eea9c10ce78b6698d8c220b6e5de577292 0.6.37
+2b26ec8909bff210f47c5f8fc620bc505e1610b5 0.6.37
+f0d502a83f6c83ba38ad21c15a849c2daf389ec7 0.6.38
+d737b2039c5f92af8000f78bbc80b6a5183caa97 0.6.39
+0a783fa0dceb95b5fc743e47c2d89c1523d0afb7 0.6.40
+ad107e9b4beea24516ac4e1e854696e586fe279d 0.6.41
+f30167716b659f96c5e0b7ea3d5be2bcff8c0eac 0.6.42
+35086ee286732b0f63d2be18d9f26f2734586e2d 0.6.43
+73aa98aee6bbc4a9d19a334a8ac928dece7799c6 0.6.44
+ddca71ae5ceb9b14512dc60ea83802c10e224cf0 0.6.45
diff --git a/.travis.yml b/.travis.yml
index cbc671e7..83efa5a4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,5 +4,6 @@ python:
- 2.6
- 2.7
- 3.2
+ - 3.3
# command to run tests
script: python setup.py test
diff --git a/CHANGES.txt b/CHANGES.txt
index e2b7874b..ac7f59d1 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -3,16 +3,178 @@ CHANGES
=======
------
+0.6.45
+------
+
+* Issue #379: ``distribute_setup.py`` now traps VersionConflict as well,
+ restoring ability to upgrade from an older setuptools version.
+
+------
+0.6.44
+------
+
+* ``distribute_setup.py`` has been updated to allow Setuptools 0.7 to
+ satisfy use_setuptools.
+
+------
+0.6.43
+------
+
+* Issue #378: Restore support for Python 2.4 Syntax (regression in 0.6.42).
+
+------
+0.6.42
+------
+
+* External links finder no longer yields duplicate links.
+* Issue #337: Moved site.py to setuptools/site-patch.py (graft of very old
+ patch from setuptools trunk which inspired PR #31).
+
+------
+0.6.41
+------
+
+* Issue #27: Use public api for loading resources from zip files rather than
+ the private method `_zip_directory_cache`.
+* Added a new function ``easy_install.get_win_launcher`` which may be used by
+ third-party libraries such as buildout to get a suitable script launcher.
+
+------
+0.6.40
+------
+
+* Issue #376: brought back cli.exe and gui.exe that were deleted in the
+ previous release.
+
+------
+0.6.39
+------
+
+* Add support for console launchers on ARM platforms.
+* Fix possible issue in GUI launchers where the subsystem was not supplied to
+ the linker.
+* Launcher build script now refactored for robustness.
+* Issue #375: Resources extracted from a zip egg to the file system now also
+ check the contents of the file against the zip contents during each
+ invocation of get_resource_filename.
+
+------
+0.6.38
+------
+
+* Issue #371: The launcher manifest file is now installed properly.
+
+------
+0.6.37
+------
+
+* Issue #143: Launcher scripts, including easy_install itself, are now
+ accompanied by a manifest on 32-bit Windows environments to avoid the
+ Installer Detection Technology and thus undesirable UAC elevation described
+ in `this Microsoft article
+ <http://technet.microsoft.com/en-us/library/cc709628%28WS.10%29.aspx>`_.
+
+------
+0.6.36
+------
+
+* Pull Request #35: In `Buildout issue 64
+ <https://github.com/buildout/buildout/issues/64>`_, it was reported that
+ under Python 3, installation of distutils scripts could attempt to copy
+ the ``__pycache__`` directory as a file, causing an error, apparently only
+ under Windows. Easy_install now skips all directories when processing
+ metadata scripts.
+
+------
+0.6.35
+------
+
+
+Note this release is backward-incompatible with distribute 0.6.23-0.6.34 in
+how it parses version numbers.
+
+* Issue #278: Restored compatibility with distribute 0.6.22 and setuptools
+ 0.6. Updated the documentation to match more closely with the version
+ parsing as intended in setuptools 0.6.
+
+------
+0.6.34
+------
+
+* Issue #341: 0.6.33 fails to build under Python 2.4.
+
+------
+0.6.33
+------
+
+* Fix 2 errors with Jython 2.5.
+* Fix 1 failure with Jython 2.5 and 2.7.
+* Disable workaround for Jython scripts on Linux systems.
+* Issue #336: `setup.py` no longer masks failure exit code when tests fail.
+* Fix issue in pkg_resources where try/except around a platform-dependent
+ import would trigger hook load failures on Mercurial. See pull request 32
+ for details.
+* Issue #341: Fix a ResourceWarning.
+
+------
+0.6.32
+------
+
+* Fix test suite with Python 2.6.
+* Fix some DeprecationWarnings and ResourceWarnings.
+* Issue #335: Backed out `setup_requires` superceding installed requirements
+ until regression can be addressed.
+
+------
+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 #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
@@ -35,7 +197,8 @@ CHANGES
* 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.
+* Issue #283: Fix and disable scanning of `*.pyc` / `*.pyo` files on
+ Python 3.3.
------
0.6.27
@@ -292,11 +455,10 @@ CHANGES
-----
* Added the generation of `distribute_setup_3k.py` during the release.
- This close http://bitbucket.org/tarek/distribute/issue/52.
+ This closes issue #52.
* Added an upload_docs command to easily upload project documentation to
- PyPI's http://packages.python.org.
- This close http://bitbucket.org/tarek/distribute/issue/56.
+ PyPI's http://packages.python.org. This close issue #56.
* Fixed a bootstrap bug on the use_setuptools() API.
@@ -325,7 +487,7 @@ setuptools
This closes http://bugs.python.org/setuptools/issue39.
* Added option to run 2to3 automatically when installing on Python 3.
- This closes http://bitbucket.org/tarek/distribute/issue/31.
+ This closes issue #31.
* Fixed invalid usage of requirement.parse, that broke develop -d.
This closes http://bugs.python.org/setuptools/issue44.
@@ -339,11 +501,9 @@ setuptools
bootstrapping
=============
-* Fixed bootstrap not working on Windows.
- This closes http://bitbucket.org/tarek/distribute/issue/49.
+* Fixed bootstrap not working on Windows. This closes issue #49.
-* Fixed 2.6 dependencies.
- This closes http://bitbucket.org/tarek/distribute/issue/50.
+* 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.
@@ -356,16 +516,14 @@ setuptools
==========
* package_index.urlopen now catches BadStatusLine and malformed url errors.
- This closes http://bitbucket.org/tarek/distribute/issue/16 and
- http://bitbucket.org/tarek/distribute/issue/18.
+ 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
- http://bitbucket.org/tarek/distribute/issue/40.
+* 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
@@ -377,7 +535,7 @@ 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 http://bitbucket.org/tarek/distribute/issue/10.
+ This closes issue #10.
---
0.6
@@ -387,45 +545,38 @@ setuptools
==========
* Packages required at build time where not fully present at install time.
- This closes http://bitbucket.org/tarek/distribute/issue/12.
+ This closes issue #12.
-* Protected against failures in tarfile extraction. This closes
- http://bitbucket.org/tarek/distribute/issue/10.
+* Protected against failures in tarfile extraction. This closes issue #10.
-* Made Jython api_tests.txt doctest compatible. This closes
- http://bitbucket.org/tarek/distribute/issue/7.
+* Made Jython api_tests.txt doctest compatible. This closes issue #7.
* sandbox.py replaced builtin type file with builtin function open. This
- closes http://bitbucket.org/tarek/distribute/issue/6.
+ closes issue #6.
-* Immediately close all file handles. This closes
- http://bitbucket.org/tarek/distribute/issue/3.
+* Immediately close all file handles. This closes issue #3.
-* Added compatibility with Subversion 1.6. This references
- http://bitbucket.org/tarek/distribute/issue/1.
+* 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
- http://bitbucket.org/tarek/distribute/issue/5.
+ instead. Based on a patch from ronaldoussoren. This closes issue #5.
* Fixed a SandboxViolation for mkdir that could occur in certain cases.
- This closes http://bitbucket.org/tarek/distribute/issue/13.
+ This closes issue #13.
* Allow to find_on_path on systems with tight permissions to fail gracefully.
- This closes http://bitbucket.org/tarek/distribute/issue/9.
+ This closes issue #9.
* Corrected inconsistency between documentation and code of add_entry.
- This closes http://bitbucket.org/tarek/distribute/issue/8.
+ This closes issue #8.
-* Immediately close all file handles. This closes
- http://bitbucket.org/tarek/distribute/issue/3.
+* Immediately close all file handles. This closes issue #3.
easy_install
============
-* Immediately close all file handles. This closes
- http://bitbucket.org/tarek/distribute/issue/3.
+* Immediately close all file handles. This closes issue #3.
diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt
index 0335b224..d6aa151d 100644
--- a/CONTRIBUTORS.txt
+++ b/CONTRIBUTORS.txt
@@ -8,16 +8,21 @@ Contributors
* Christophe Combelles
* Daniel Stutzbach
* Daniel Holth
+* Dirley Rodrigues
+* Grigory Petrov
* 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
+* Philip Thiem
* Reinout van Rees
* Robert Myers
* Stefan H. Holek
diff --git a/MANIFEST.in b/MANIFEST.in
index 9837747a..68337800 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,4 @@
-recursive-include setuptools *.py *.txt *.exe
+recursive-include setuptools *.py *.txt *.exe *.xml
recursive-include tests *.py *.c *.pyx *.txt
recursive-include setuptools/tests *.html
recursive-include docs *.py *.txt *.conf *.css *.css_t Makefile indexsidebar.html
diff --git a/README.txt b/README.txt
index 679b2551..dd763a57 100755
--- a/README.txt
+++ b/README.txt
@@ -11,35 +11,14 @@ Disclaimers
About the fork
==============
-`Distribute` is a fork of the `Setuptools` project.
+`Distribute` is a now deprecated fork of the `Setuptools` project.
-Distribute is intended to replace Setuptools as the standard method
-for working with Python module distributions.
+Distribute was intended to replace Setuptools as the standard method
+for working with Python module distributions. The code has since been merged
+back into the parent project as is being maintained by the community at large.
-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.
+`Distribute` is now being maintained as a branch in the `Setuptools
+repository <https://bitbucket.org/pypa/setuptools>`_.
More documentation
==================
@@ -99,9 +78,9 @@ 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.29.tar.gz
- $ tar -xzvf distribute-0.6.29.tar.gz
- $ cd distribute-0.6.29
+ $ curl -O http://pypi.python.org/packages/source/d/distribute/distribute-0.6.46.tar.gz
+ $ tar -xzvf distribute-0.6.46.tar.gz
+ $ cd distribute-0.6.46
$ python setup.py install
---------------------------
@@ -224,5 +203,4 @@ 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
-
+- Code Repository: http://bitbucket.org/pypa/setuptools?at=distribute
diff --git a/_markerlib/markers.py b/_markerlib/markers.py
index 54c2828a..c93d7f3b 100644
--- a/_markerlib/markers.py
+++ b/_markerlib/markers.py
@@ -17,9 +17,7 @@ where EXPR belongs to any of those:
__all__ = ['default_environment', 'compile', 'interpret']
-from ast import Compare, BoolOp, Attribute, Name, Load, Str, cmpop, boolop
-from ast import parse, copy_location, NodeTransformer
-
+import ast
import os
import platform
import sys
@@ -27,7 +25,16 @@ import weakref
_builtin_compile = compile
-from platform import python_implementation
+try:
+ from platform import python_implementation
+except ImportError:
+ if os.name == "java":
+ # Jython 2.5 has ast module, but not platform.python_implementation() function.
+ def python_implementation():
+ return "Jython"
+ else:
+ raise
+
# restricted set of variables
_VARS = {'sys.platform': sys.platform,
@@ -46,27 +53,31 @@ def default_environment():
"""Return copy of default PEP 385 globals dictionary."""
return dict(_VARS)
-class ASTWhitelist(NodeTransformer):
+class ASTWhitelist(ast.NodeTransformer):
def __init__(self, statement):
self.statement = statement # for error messages
-
- ALLOWED = (Compare, BoolOp, Attribute, Name, Load, Str, cmpop, boolop)
-
+
+ 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,
+ (self.statement,
(' ' * node.col_offset) + '^'))
- return NodeTransformer.visit(self, node)
-
+ return ast.NodeTransformer.visit(self, node)
+
def visit_Attribute(self, node):
"""Flatten one level of attribute access."""
- new_node = Name("%s.%s" % (node.value.id, node.attr), node.ctx)
- return copy_location(new_node, node)
+ 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 = parse(marker, mode='eval')
+ tree = ast.parse(marker, mode='eval')
new_tree = ASTWhitelist(marker).generic_visit(tree)
return new_tree
diff --git a/distribute_setup.py b/distribute_setup.py
index a52d5c16..f7f465a8 100644
--- a/distribute_setup.py
+++ b/distribute_setup.py
@@ -14,6 +14,7 @@ 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
@@ -48,7 +49,7 @@ except ImportError:
args = [quote(arg) for arg in args]
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
-DEFAULT_VERSION = "0.6.29"
+DEFAULT_VERSION = "0.6.46"
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
SETUPTOOLS_FAKED_VERSION = "0.6c11"
@@ -86,8 +87,11 @@ def _install(tarball, install_args=()):
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):
@@ -112,6 +116,7 @@ def _build_egg(egg, tarball, to_dir):
finally:
os.chdir(old_wd)
+ shutil.rmtree(tmpdir)
# returning the result
log.warn(egg)
if not os.path.exists(egg):
@@ -139,6 +144,16 @@ def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
try:
try:
import pkg_resources
+
+ # Setuptools 0.7b and later is a suitable (and preferable)
+ # substitute for any Distribute version.
+ try:
+ pkg_resources.require("setuptools>=0.7b")
+ return
+ except (pkg_resources.DistributionNotFound,
+ pkg_resources.VersionConflict):
+ pass
+
if not hasattr(pkg_resources, '_distribute'):
if not no_fake:
_fake_setuptools()
@@ -234,7 +249,9 @@ def _no_sandbox(function):
def _patch_file(path, content):
"""Will backup the file then patch it"""
- existing_content = open(path).read()
+ f = open(path)
+ existing_content = f.read()
+ f.close()
if existing_content == content:
# already patched
log.warn('Already patched.')
@@ -252,7 +269,10 @@ _patch_file = _no_sandbox(_patch_file)
def _same_content(path, content):
- return open(path).read() == content
+ f = open(path)
+ existing_content = f.read()
+ f.close()
+ return existing_content == content
def _rename_path(path):
@@ -443,8 +463,9 @@ def _relaunch():
log.warn('Relaunching...')
# we have to relaunch the process
# pip marker to avoid a relaunch bug
- _cmd = ['-c', 'install', '--single-version-externally-managed']
- if sys.argv[:3] == _cmd:
+ _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))
@@ -529,7 +550,7 @@ def main(version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
options = _parse_args()
tarball = download_setuptools(download_base=options.download_base)
- _install(tarball, _build_install_args(options))
+ return _install(tarball, _build_install_args(options))
if __name__ == '__main__':
- main()
+ sys.exit(main())
diff --git a/docs/conf.py b/docs/conf.py
index 7b82a884..ba020915 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -48,9 +48,9 @@ copyright = u'2009-2011, The fellowship of the packaging'
# built documents.
#
# The short X.Y version.
-version = '0.6.29'
+version = '0.6.46'
# The full version, including alpha/beta/rc tags.
-release = '0.6.29'
+release = '0.6.46'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/docs/setuptools.txt b/docs/setuptools.txt
index 31eedcb1..fe8bb3f6 100644
--- a/docs/setuptools.txt
+++ b/docs/setuptools.txt
@@ -187,10 +187,11 @@ than ``2.4.1`` (which has a higher release number).
A pre-release tag is a series of letters that are alphabetically before
"final". Some examples of prerelease tags would include ``alpha``, ``beta``,
-``a``, ``c``, ``dev``, and so on. You do not have to place a dot before
-the prerelease tag if it's immediately after a number, but it's okay to do
-so if you prefer. Thus, ``2.4c1`` and ``2.4.c1`` both represent release
-candidate 1 of version ``2.4``, and are treated as identical by setuptools.
+``a``, ``c``, ``dev``, and so on. You do not have to place a dot or dash
+before the prerelease tag if it's immediately after a number, but it's okay to
+do so if you prefer. Thus, ``2.4c1`` and ``2.4.c1`` and ``2.4-c1`` all
+represent release candidate 1 of version ``2.4``, and are treated as identical
+by setuptools.
In addition, there are three special prerelease tags that are treated as if
they were the letter ``c``: ``pre``, ``preview``, and ``rc``. So, version
@@ -216,13 +217,6 @@ a post-release tag, so this version is *newer* than ``0.6a9.dev``.
For the most part, setuptools' interpretation of version numbers is intuitive,
but here are a few tips that will keep you out of trouble in the corner cases:
-* Don't use ``-`` or any other character than ``.`` as a separator, unless you
- really want a post-release. Remember that ``2.1-rc2`` means you've
- *already* released ``2.1``, whereas ``2.1rc2`` and ``2.1.c2`` are candidates
- you're putting out *before* ``2.1``. If you accidentally distribute copies
- of a post-release that you meant to be a pre-release, the only safe fix is to
- bump your main release number (e.g. to ``2.1.1``) and re-release the project.
-
* Don't stick adjoining pre-release tags together without a dot or number
between them. Version ``1.9adev`` is the ``adev`` prerelease of ``1.9``,
*not* a development pre-release of ``1.9a``. Use ``.dev`` instead, as in
@@ -239,7 +233,7 @@ but here are a few tips that will keep you out of trouble in the corner cases:
>>> parse_version('1.9.a.dev') == parse_version('1.9a0dev')
True
>>> parse_version('2.1-rc2') < parse_version('2.1')
- False
+ True
>>> parse_version('0.6a9dev-r41475') < parse_version('0.6a9')
True
@@ -616,14 +610,20 @@ Dependencies that aren't in PyPI
If your project depends on packages that aren't registered in PyPI, you may
still be able to depend on them, as long as they are available for download
-as an egg, in the standard distutils ``sdist`` format, or as a single ``.py``
-file. You just need to add some URLs to the ``dependency_links`` argument to
+as:
+
+- an egg, in the standard distutils ``sdist`` format,
+- a single ``.py`` file, or
+- a VCS repository (Subversion, Mercurial, or Git).
+
+You just need to add some URLs to the ``dependency_links`` argument to
``setup()``.
The URLs must be either:
-1. direct download URLs, or
-2. the URLs of web pages that contain direct download links
+1. direct download URLs,
+2. the URLs of web pages that contain direct download links, or
+3. the repository's URL
In general, it's better to link to web pages, because it is usually less
complex to update a web page than to release a new version of your project.
@@ -637,6 +637,27 @@ by replacing them with underscores.) EasyInstall will recognize this suffix
and automatically create a trivial ``setup.py`` to wrap the single ``.py`` file
as an egg.
+In the case of a VCS checkout, you should also append ``#egg=project-version``
+in order to identify for what package that checkout should be used. You can
+append ``@REV`` to the URL's path (before the fragment) to specify a revision.
+Additionally, you can also force the VCS being used by prepending the URL with
+a certain prefix. Currently available are:
+
+- ``svn+URL`` for Subversion,
+- ``git+URL`` for Git, and
+- ``hg+URL`` for Mercurial
+
+A more complete example would be:
+
+ ``vcs+proto://host/path@revision#egg=project-version``
+
+Be careful with the version. It should match the one inside the project files.
+If you want do disregard the version, you have to omit it both in the
+``requires`` and in the URL's fragment.
+
+This will do a checkout (or a clone, in Git and Mercurial parlance) to a
+temporary folder and run ``setup.py bdist_egg``.
+
The ``dependency_links`` option takes the form of a list of URL strings. For
example, the below will cause EasyInstall to search the specified page for
eggs or source distributions, if the package's dependencies aren't already
@@ -2423,7 +2444,12 @@ command::
python setup.py upload_docs --upload-dir=docs/build/html
-As with any other ``setuptools`` based command, you can define useful
+If no ``--upload-dir`` is given, ``upload_docs`` will attempt to run the
+``build_sphinx`` command to generate uploadable documentation.
+For the command to become available, `Sphinx <http://sphinx.pocoo.org/>`_
+must be installed in the same environment as distribute.
+
+As with other ``setuptools``-based commands, you can define useful
defaults in the ``setup.cfg`` of your Python project, e.g.:
.. code-block:: ini
diff --git a/launcher.c b/launcher.c
index ea4c80b5..be69f0c6 100755
--- a/launcher.c
+++ b/launcher.c
@@ -14,6 +14,14 @@
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
+ To build for Windows RT, install both Visual Studio Express for Windows 8
+ and for Windows Desktop (both freeware), create "win32" application using
+ "Windows Desktop" version, create new "ARM" target via
+ "Configuration Manager" menu and modify ".vcxproj" file by adding
+ "<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>" tag
+ as child of "PropertyGroup" tags that has "Debug|ARM" and "Release|ARM"
+ properties.
+
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
diff --git a/msvc-build-launcher.cmd b/msvc-build-launcher.cmd
index 3666d723..e54c4f6c 100644
--- a/msvc-build-launcher.cmd
+++ b/msvc-build-launcher.cmd
@@ -1,15 +1,55 @@
@echo off
-REM VCVARSALL may be in Program Files or Program Files (x86)
-PATH=C:\Program Files\Microsoft Visual Studio 9.0\VC;%PATH%
-PATH=C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC;%PATH%
+REM Use old Windows SDK 6.1 so created .exe will be compatible with
+REM old Windows versions.
+REM Windows SDK 6.1 may be downloaded at:
+REM http://www.microsoft.com/en-us/download/details.aspx?id=11310
+set PATH_OLD=%PATH%
+
+REM The SDK creates a false install of Visual Studio at one of these locations
+set PATH=C:\Program Files\Microsoft Visual Studio 9.0\VC\bin;%PATH%
+set PATH=C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin;%PATH%
REM set up the environment to compile to x86
-call VCVARSALL x86
-cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /out:setuptools/cli-32.exe
-cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /out:setuptools/gui-32.exe
+call VCVARS32
+if "%ERRORLEVEL%"=="0" (
+ cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /SUBSYSTEM:CONSOLE /out:setuptools/cli-32.exe
+ cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /SUBSYSTEM:WINDOWS /out:setuptools/gui-32.exe
+) else (
+ echo Windows SDK 6.1 not found to build Windows 32-bit version
+)
+
+REM buildout (and possibly other implementations) currently depend on
+REM the 32-bit launcher scripts without the -32 in the filename, so copy them
+REM there for now.
+copy setuptools/cli-32.exe setuptools/cli.exe
+copy setuptools/gui-32.exe setuptools/gui.exe
REM now for 64-bit
-call VCVARSALL x86_amd64
-cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /out:setuptools/cli-64.exe
-cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /out:setuptools/gui-64.exe \ No newline at end of file
+REM Use the x86_amd64 profile, which is the 32-bit cross compiler for amd64
+call VCVARSx86_amd64
+if "%ERRORLEVEL%"=="0" (
+ cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /SUBSYSTEM:CONSOLE /out:setuptools/cli-64.exe
+ cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /SUBSYSTEM:WINDOWS /out:setuptools/gui-64.exe
+) else (
+ echo Windows SDK 6.1 not found to build Windows 64-bit version
+)
+
+REM Windows RT ARM build requires both freeware
+REM "Visual Studio Express 2012 for Windows 8" and
+REM "Visual Studio Express 2012 for Windows Desktop" to be installed from
+REM http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products
+set PATH=%PATH_OLD%
+set PATH=C:\Program Files\Microsoft Visual Studio 11.0\VC;%PATH%
+set PATH=C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC;%PATH%
+call VCVARSALL x86_arm >nul 2>&1
+if "%ERRORLEVEL%"=="0" (
+ echo Building Windows RT Version ...
+ cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" /D _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE launcher.c /O2 /link /MACHINE:ARM /SUBSYSTEM:CONSOLE /out:setuptools/cli-arm-32.exe
+ cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" /D _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE launcher.c /O2 /link /MACHINE:ARM /SUBSYSTEM:WINDOWS /out:setuptools/gui-arm-32.exe
+) else (
+ echo Visual Studio ^(Express^) 2012 not found to build Windows RT Version
+)
+
+set PATH=%PATH_OLD%
+
diff --git a/pkg_resources.py b/pkg_resources.py
index b63e3f0f..4c05b09f 100644
--- a/pkg_resources.py
+++ b/pkg_resources.py
@@ -13,7 +13,7 @@ The package resource API is designed to work with normal filesystem packages,
method.
"""
-import sys, os, zipimport, time, re, imp, types
+import sys, os, time, re, imp, types, zipfile, zipimport
try:
from urlparse import urlparse, urlunparse
except ImportError:
@@ -60,6 +60,12 @@ except ImportError:
from os import open as os_open
from os.path import isdir, split
+# Avoid try/except due to potential problems with delayed import mechanisms.
+if sys.version_info >= (3, 3) and sys.implementation.name == "cpython":
+ import importlib._bootstrap as importlib_bootstrap
+else:
+ importlib_bootstrap = None
+
# 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
@@ -1354,13 +1360,8 @@ class DefaultProvider(EggProvider):
register_loader_type(type(None), DefaultProvider)
-try:
- # CPython >=3.3
- import _frozen_importlib
-except ImportError:
- pass
-else:
- register_loader_type(_frozen_importlib.SourceFileLoader, DefaultProvider)
+if importlib_bootstrap is not None:
+ register_loader_type(importlib_bootstrap.SourceFileLoader, DefaultProvider)
class EmptyProvider(NullProvider):
@@ -1377,6 +1378,37 @@ class EmptyProvider(NullProvider):
empty_provider = EmptyProvider()
+def build_zipmanifest(path):
+ """
+ This builds a similar dictionary to the zipimport directory
+ caches. However instead of tuples, ZipInfo objects are stored.
+
+ The translation of the tuple is as follows:
+ * [0] - zipinfo.filename on stock pythons this needs "/" --> os.sep
+ on pypy it is the same (one reason why distribute did work
+ in some cases on pypy and win32).
+ * [1] - zipinfo.compress_type
+ * [2] - zipinfo.compress_size
+ * [3] - zipinfo.file_size
+ * [4] - len(utf-8 encoding of filename) if zipinfo & 0x800
+ len(ascii encoding of filename) otherwise
+ * [5] - (zipinfo.date_time[0] - 1980) << 9 |
+ zipinfo.date_time[1] << 5 | zipinfo.date_time[2]
+ * [6] - (zipinfo.date_time[3] - 1980) << 11 |
+ zipinfo.date_time[4] << 5 | (zipinfo.date_time[5] // 2)
+ * [7] - zipinfo.CRC
+ """
+ zipinfo = dict()
+ zfile = zipfile.ZipFile(path)
+ #Got ZipFile has not __exit__ on python 3.1
+ try:
+ for zitem in zfile.namelist():
+ zpath = zitem.replace('/', os.sep)
+ zipinfo[zpath] = zfile.getinfo(zitem)
+ assert zipinfo[zpath] is not None
+ finally:
+ zfile.close()
+ return zipinfo
class ZipProvider(EggProvider):
@@ -1386,7 +1418,7 @@ class ZipProvider(EggProvider):
def __init__(self, module):
EggProvider.__init__(self,module)
- self.zipinfo = zipimport._zip_directory_cache[self.loader.archive]
+ self.zipinfo = build_zipmanifest(self.loader.archive)
self.zip_pre = self.loader.archive+os.sep
def _zipinfo_name(self, fspath):
@@ -1420,6 +1452,14 @@ class ZipProvider(EggProvider):
self._extract_resource(manager, self._eager_to_zip(name))
return self._extract_resource(manager, zip_path)
+ @staticmethod
+ def _get_date_and_size(zip_stat):
+ size = zip_stat.file_size
+ date_time = zip_stat.date_time + (0, 0, -1) # ymdhms+wday, yday, dst
+ #1980 offset already done
+ timestamp = time.mktime(date_time)
+ return timestamp, size
+
def _extract_resource(self, manager, zip_path):
if zip_path in self._index():
@@ -1429,28 +1469,19 @@ class ZipProvider(EggProvider):
)
return os.path.dirname(last) # return the extracted directory name
- zip_stat = self.zipinfo[zip_path]
- t,d,size = zip_stat[5], zip_stat[6], zip_stat[3]
- date_time = (
- (d>>9)+1980, (d>>5)&0xF, d&0x1F, # ymd
- (t&0xFFFF)>>11, (t>>5)&0x3F, (t&0x1F) * 2, 0, 0, -1 # hms, etc.
- )
- timestamp = time.mktime(date_time)
+ timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])
+ if not WRITE_SUPPORT:
+ raise IOError('"os.rename" and "os.unlink" are not supported '
+ 'on this platform')
try:
- if not WRITE_SUPPORT:
- raise IOError('"os.rename" and "os.unlink" are not supported '
- 'on this platform')
real_path = manager.get_cache_path(
self.egg_name, self._parts(zip_path)
)
- if os.path.isfile(real_path):
- stat = os.stat(real_path)
- if stat.st_size==size and stat.st_mtime==timestamp:
- # size and stamp match, don't bother extracting
- return real_path
+ if self._is_current(real_path, zip_path):
+ return real_path
outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path))
os.write(outf, self.loader.get_data(zip_path))
@@ -1463,11 +1494,9 @@ class ZipProvider(EggProvider):
except os.error:
if os.path.isfile(real_path):
- stat = os.stat(real_path)
-
- if stat.st_size==size and stat.st_mtime==timestamp:
- # size and stamp match, somebody did it just ahead of
- # us, so we're done
+ if self._is_current(real_path, zip_path):
+ # the file became current since it was checked above,
+ # so proceed.
return real_path
elif os.name=='nt': # Windows, del old file and retry
unlink(real_path)
@@ -1480,6 +1509,23 @@ class ZipProvider(EggProvider):
return real_path
+ def _is_current(self, file_path, zip_path):
+ """
+ Return True if the file_path is current for this zip_path
+ """
+ timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])
+ if not os.path.isfile(file_path):
+ return False
+ stat = os.stat(file_path)
+ if stat.st_size!=size or stat.st_mtime!=timestamp:
+ return False
+ # check that the contents match
+ zip_contents = self.loader.get_data(zip_path)
+ f = open(file_path, 'rb')
+ file_contents = f.read()
+ f.close()
+ return zip_contents == file_contents
+
def _get_eager_resources(self):
if self.eagers is None:
eagers = []
@@ -1622,7 +1668,7 @@ class EggMetadata(ZipProvider):
def __init__(self, importer):
"""Create a metadata provider from a zipimporter"""
- self.zipinfo = zipimport._zip_directory_cache[importer.archive]
+ self.zipinfo = build_zipmanifest(importer.archive)
self.zip_pre = importer.archive+os.sep
self.loader = importer
if importer.prefix:
@@ -1789,20 +1835,20 @@ def find_on_path(importer, path_item, only=False):
for dist in find_distributions(os.path.join(path_item, entry)):
yield dist
elif not only and lower.endswith('.egg-link'):
- for line in open(os.path.join(path_item, entry)):
+ entry_file = open(os.path.join(path_item, entry))
+ try:
+ entry_lines = entry_file.readlines()
+ finally:
+ entry_file.close()
+ for line in entry_lines:
if not line.strip(): continue
for item in find_distributions(os.path.join(path_item,line.rstrip())):
yield item
break
register_finder(ImpWrapper,find_on_path)
-try:
- # CPython >=3.3
- import _frozen_importlib
-except ImportError:
- pass
-else:
- register_finder(_frozen_importlib.FileFinder, find_on_path)
+if importlib_bootstrap is not None:
+ register_finder(importlib_bootstrap.FileFinder, find_on_path)
_declare_state('dict', _namespace_handlers={})
_declare_state('dict', _namespace_packages={})
@@ -1903,13 +1949,8 @@ def file_ns_handler(importer, path_item, packageName, module):
register_namespace_handler(ImpWrapper,file_ns_handler)
register_namespace_handler(zipimport.zipimporter,file_ns_handler)
-try:
- # CPython >=3.3
- import _frozen_importlib
-except ImportError:
- pass
-else:
- register_namespace_handler(_frozen_importlib.FileFinder, file_ns_handler)
+if importlib_bootstrap is not None:
+ register_namespace_handler(importlib_bootstrap.FileFinder, file_ns_handler)
def null_ns_handler(importer, path_item, packageName, module):
@@ -1969,7 +2010,7 @@ replace = {'pre':'c', 'preview':'c','-':'final-','rc':'c','dev':'@'}.get
def _parse_version_parts(s):
for part in component_re.split(s):
part = replace(part,part)
- if part in ['', '.']:
+ if not part or part=='.':
continue
if part[:1] in '0123456789':
yield part.zfill(8) # pad for numeric comparison
@@ -2012,6 +2053,8 @@ def parse_version(s):
parts = []
for part in _parse_version_parts(s.lower()):
if part.startswith('*'):
+ if part<'*final': # remove '-' before a prerelease tag
+ while parts and parts[-1]=='*final-': parts.pop()
# remove trailing zeros from each series of numeric parts
while parts and parts[-1]=='00000000':
parts.pop()
@@ -2149,7 +2192,7 @@ def _remove_md5_fragment(location):
class Distribution(object):
"""Wrap an actual or potential sys.path entry w/metadata"""
PKG_INFO = 'PKG-INFO'
-
+
def __init__(self,
location=None, metadata=None, project_name=None, version=None,
py_version=PY_MAJOR, platform=None, precedence = EGG_DIST
@@ -2488,7 +2531,7 @@ class DistInfoDistribution(Distribution):
from email.parser import Parser
self._pkg_info = Parser().parsestr(self.get_metadata(self.PKG_INFO))
return self._pkg_info
-
+
@property
def _dep_map(self):
try:
@@ -2499,7 +2542,7 @@ class DistInfoDistribution(Distribution):
def _preparse_requirement(self, requires_dist):
"""Convert 'Foobar (1); baz' to ('Foobar ==1', 'baz')
- Split environment marker, add == prefix to version specifiers as
+ Split environment marker, add == prefix to version specifiers as
necessary, and remove parenthesis.
"""
parts = requires_dist.split(';', 1) + ['']
@@ -2508,7 +2551,7 @@ class DistInfoDistribution(Distribution):
distvers = re.sub(self.EQEQ, r"\1==\2\3", distvers)
distvers = distvers.replace('(', '').replace(')', '')
return (distvers, mark)
-
+
def _compute_dependencies(self):
"""Recompute this distribution's dependencies."""
from _markerlib import compile as compile_marker
@@ -2521,7 +2564,7 @@ class DistInfoDistribution(Distribution):
parsed = next(parse_requirements(distvers))
parsed.marker_fn = compile_marker(mark)
reqs.append(parsed)
-
+
def reqs_for_extra(extra):
for req in reqs:
if req.marker_fn(override={'extra':extra}):
@@ -2529,13 +2572,13 @@ class DistInfoDistribution(Distribution):
common = frozenset(reqs_for_extra(None))
dm[None].extend(common)
-
+
for extra in self._parsed_pkg_info.get_all('Provides-Extra') or []:
extra = safe_extra(extra.strip())
dm[extra] = list(frozenset(reqs_for_extra(extra)) - common)
return dm
-
+
_distributionImpl = {'.egg': Distribution,
'.egg-info': Distribution,
@@ -2856,3 +2899,4 @@ run_main = run_script # backward compatibility
add_activation_listener(lambda dist: dist.activate())
working_set.entries=[]; list(map(working_set.add_entry,sys.path)) # match order
+
diff --git a/release.py b/release.py
index f4f88bac..9e876466 100644
--- a/release.py
+++ b/release.py
@@ -14,13 +14,17 @@ import sys
import urllib2
import getpass
import collections
+import itertools
+import re
try:
import keyring
except Exception:
pass
-VERSION = '0.6.29'
+VERSION = '0.6.46'
+PACKAGE_INDEX = 'https://pypi.python.org/pypi'
+PACKAGE_INDEX = 'https://pypi.python.org/pypi'
def get_next_version():
digits = map(int, VERSION.split('.'))
@@ -50,7 +54,7 @@ def get_mercurial_creds(system='https://bitbucket.org', username=None):
# todo: consider getting this from .hgrc
username = username or getpass.getuser()
keyring_username = '@@'.join((username, system))
- system = '@'.join((keyring_username, 'Mercurial'))
+ system = 'Mercurial'
password = (
keyring.get_password(system, keyring_username)
if 'keyring' in globals()
@@ -65,7 +69,7 @@ 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}'
@@ -99,7 +103,7 @@ def do_release():
print("Please do that")
raise SystemExit(1)
- print("Travis-CI tests: http://travis-ci.org/#!/jaraco/distribute")
+ print("Travis-CI tests: http://travis-ci.org/#!/jaraco/setuptools")
res = raw_input('Have you or has someone verified that the tests '
'pass on this revision? ')
if not res.lower().startswith('y'):
@@ -110,13 +114,20 @@ def do_release():
subprocess.check_call(['hg', 'update', VERSION])
+ linkify('CHANGES.txt', 'CHANGES (links).txt')
+
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']
+ cmd = [
+ sys.executable, 'setup.py', '-q',
+ 'egg_info', '-RD', '-b', '',
+ 'sdist',
+ 'register', '-r', PACKAGE_INDEX,
+ 'upload', '-r', PACKAGE_INDEX,
+ ]
if has_docs:
- cmd.append('upload_docs')
+ cmd.extend(['upload_docs', '-r', PACKAGE_INDEX])
subprocess.check_call(cmd)
upload_bootstrap_script()
@@ -148,14 +159,14 @@ def build_docs():
return
if os.path.isdir('docs/build'):
shutil.rmtree('docs/build')
- subprocess.check_call([
+ cmd = [
'sphinx-build',
'-b', 'html',
'-d', 'build/doctrees',
'.',
'build/html',
- ],
- cwd='docs')
+ ]
+ subprocess.check_call(cmd, cwd='docs')
return True
def upload_bootstrap_script():
@@ -166,5 +177,59 @@ def upload_bootstrap_script():
except:
print("Unable to upload bootstrap script. Ask Tarek to do it.")
+def linkify(source, dest):
+ with open(source) as source:
+ out = _linkified_text(source.read())
+ with open(dest, 'w') as dest:
+ dest.write(out)
+
+def _linkified(rst_path):
+ "return contents of reStructureText file with linked issue references"
+ rst_file = open(rst_path)
+ rst_content = rst_file.read()
+ rst_file.close()
+
+ return _linkified_text(rst_content)
+
+def _linkified_text(rst_content):
+ # first identify any existing HREFs so they're not changed
+ HREF_pattern = re.compile('`.*?`_', re.MULTILINE | re.DOTALL)
+
+ # split on the HREF pattern, returning the parts to be linkified
+ plain_text_parts = HREF_pattern.split(rst_content)
+ anchors = []
+ linkified_parts = [_linkified_part(part, anchors)
+ for part in plain_text_parts]
+ pairs = itertools.izip_longest(
+ linkified_parts,
+ HREF_pattern.findall(rst_content),
+ fillvalue='',
+ )
+ rst_content = ''.join(flatten(pairs))
+
+ anchors = sorted(anchors)
+
+ bitroot = 'http://bitbucket.org/tarek/distribute'
+ rst_content += "\n"
+ for x in anchors:
+ issue = re.findall(r'\d+', x)[0]
+ rst_content += '.. _`%s`: %s/issue/%s\n' % (x, bitroot, issue)
+ rst_content += "\n"
+ return rst_content
+
+def flatten(listOfLists):
+ "Flatten one level of nesting"
+ return itertools.chain.from_iterable(listOfLists)
+
+
+def _linkified_part(text, anchors):
+ """
+ Linkify a part and collect any anchors generated
+ """
+ revision = re.compile(r'\b(issue\s+#?\d+)\b', re.M | re.I)
+
+ anchors.extend(revision.findall(text)) # ['Issue #43', ...]
+ return revision.sub(r'`\1`_', text)
+
if __name__ == '__main__':
do_release()
diff --git a/setup.py b/setup.py
index 221d59d7..e7b3c027 100755
--- a/setup.py
+++ b/setup.py
@@ -3,6 +3,7 @@
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__)))
@@ -15,8 +16,10 @@ if sys.version_info >= (3,) and do_2to3:
from distutils import dir_util, file_util, util, log
log.set_verbosity(1)
fl = FileList()
- for line in open("MANIFEST.in"):
+ manifest_file = open("MANIFEST.in")
+ for line in manifest_file:
fl.process_template_line(line)
+ manifest_file.close()
dir_util.create_tree(tmp_src, fl.files)
outfiles_2to3 = []
dist_script = os.path.join("build", "src", "distribute_setup.py")
@@ -39,10 +42,12 @@ from distutils.util import convert_path
d = {}
init_path = convert_path('setuptools/command/__init__.py')
-exec(open(init_path).read(), d)
+init_file = open(init_path)
+exec(init_file.read(), d)
+init_file.close()
SETUP_COMMANDS = d['__all__']
-VERSION = "0.6.29"
+VERSION = "0.6.46"
from setuptools import setup, find_packages
from setuptools.command.build_py import build_py as _build_py
@@ -82,10 +87,8 @@ class test(_test):
entry_points = os.path.join('distribute.egg-info', 'entry_points.txt')
if not os.path.exists(entry_points):
- try:
- _test.run(self)
- finally:
- return
+ _test.run(self)
+ return # even though _test.run will raise SystemExit
f = open(entry_points)
@@ -132,6 +135,16 @@ if _being_installed():
from distribute_setup import _before_install
_before_install()
+readme_file = open('README.txt')
+# the release script adds hyperlinks to issues
+if os.path.exists('CHANGES (links).txt'):
+ changes_file = open('CHANGES (links).txt')
+else:
+ # but if the release script has not run, fall back to the source file
+ changes_file = open('CHANGES.txt')
+long_description = readme_file.read() + changes_file.read()
+readme_file.close()
+changes_file.close()
dist = setup(
name="distribute",
@@ -141,26 +154,24 @@ dist = setup(
author="The fellowship of the packaging",
author_email="distutils-sig@python.org",
license="PSF or ZPL",
- long_description = open('README.txt').read() + open('CHANGES.txt').read(),
+ long_description = long_description,
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']},
+ package_data = {'setuptools':['*.exe', 'site-patch.py'], 'setuptools.command':['*.xml']},
- py_modules = ['pkg_resources', 'easy_install', 'site'],
+ py_modules = ['pkg_resources', 'easy_install'],
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",
@@ -181,7 +192,6 @@ dist = setup(
"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",
@@ -192,7 +202,6 @@ dist = setup(
"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":
@@ -200,7 +209,7 @@ dist = setup(
"setuptools.installation":
['eggsecutable = setuptools.command.easy_install:bootstrap'],
- },
+ },
classifiers = textwrap.dedent("""
diff --git a/setuptools/archive_util.py b/setuptools/archive_util.py
index 5787753f..e22b25c0 100755
--- a/setuptools/archive_util.py
+++ b/setuptools/archive_util.py
@@ -158,6 +158,9 @@ def unpack_zipfile(filename, extract_dir, progress_filter=default_filter):
finally:
f.close()
del data
+ unix_attributes = info.external_attr >> 16
+ if unix_attributes:
+ os.chmod(target, unix_attributes)
finally:
z.close()
diff --git a/setuptools/cli-32.exe b/setuptools/cli-32.exe
index 9b7717b7..b1487b78 100644
--- a/setuptools/cli-32.exe
+++ b/setuptools/cli-32.exe
Binary files differ
diff --git a/setuptools/cli-64.exe b/setuptools/cli-64.exe
index 265585af..675e6bf3 100644
--- a/setuptools/cli-64.exe
+++ b/setuptools/cli-64.exe
Binary files differ
diff --git a/setuptools/cli-arm-32.exe b/setuptools/cli-arm-32.exe
new file mode 100644
index 00000000..2f40402d
--- /dev/null
+++ b/setuptools/cli-arm-32.exe
Binary files differ
diff --git a/setuptools/cli.exe b/setuptools/cli.exe
index 9b7717b7..b1487b78 100644
--- a/setuptools/cli.exe
+++ b/setuptools/cli.exe
Binary files differ
diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py
index 709e349c..1d500040 100755
--- a/setuptools/command/develop.py
+++ b/setuptools/command/develop.py
@@ -132,7 +132,9 @@ class develop(easy_install):
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)]
+ egg_link_file = open(self.egg_link)
+ contents = [line.rstrip() for line in egg_link_file]
+ egg_link_file.close()
if contents not in ([self.egg_path], [self.egg_path, self.setup_path]):
log.warn("Link points to %s: uninstall aborted", contents)
return
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
index 8ce71614..50339e8f 100755
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -19,7 +19,9 @@ import zipfile
import re
import stat
import random
+import platform
from glob import glob
+import pkg_resources
from setuptools import Command, _dont_write_bytecode
from setuptools.sandbox import run_setup
from distutils import log, dir_util
@@ -493,7 +495,7 @@ Please make the appropriate changes for your system and try again.
self.cant_write_to_target()
else:
try:
- f.write("import os;open(%r,'w').write('OK')\n" % (ok_file,))
+ f.write("import os; f = open(%r, 'w'); f.write('OK'); f.close()\n" % (ok_file,))
f.close(); f=None
executable = sys.executable
if os.name=='nt':
@@ -524,6 +526,10 @@ Please make the appropriate changes for your system and try again.
"""Write all the scripts for `dist`, unless scripts are excluded"""
if not self.exclude_scripts and dist.metadata_isdir('scripts'):
for script_name in dist.metadata_listdir('scripts'):
+ if dist.metadata_isdir('scripts/' + script_name):
+ # The "script" is a directory, likely a Python 3
+ # __pycache__ directory, so skip it.
+ continue
self.install_script(
dist, script_name,
dist.get_metadata('scripts/'+script_name)
@@ -1276,7 +1282,7 @@ Please make the appropriate changes for your system and try again.""" % (
return # already did it, or don't need to
sitepy = os.path.join(self.install_dir, "site.py")
- source = resource_string(Requirement.parse("distribute"), "site.py")
+ source = resource_string("setuptools", "site-patch.py")
current = ""
if os.path.exists(sitepy):
@@ -1529,7 +1535,10 @@ def get_exe_prefixes(exe_filename):
if name.endswith('-nspkg.pth'):
continue
if parts[0].upper() in ('PURELIB','PLATLIB'):
- for pth in yield_lines(z.read(name)):
+ contents = z.read(name)
+ if sys.version_info >= (3,):
+ contents = contents.decode()
+ for pth in yield_lines(contents):
pth = pth.strip().replace('\\','/')
if not pth.startswith('import'):
prefixes.append((('%s/%s/' % (parts[0],pth)), ''))
@@ -1794,6 +1803,11 @@ def chmod(path, mode):
def fix_jython_executable(executable, options):
if sys.platform.startswith('java') and is_sh(executable):
+ # Workaround for Jython is not needed on Linux systems.
+ import java
+ if java.lang.System.getProperty("os.name") == "Linux":
+ return executable
+
# Workaround Jython's sys.executable being a .sh (an invalid
# shebang line interpreter)
if options:
@@ -1828,31 +1842,61 @@ def get_script_args(dist, executable=sys_executable, wininst=False):
if sys.platform=='win32' or wininst:
# On Windows/wininst, add a .py extension and an .exe launcher
if group=='gui_scripts':
- ext, launcher = '-script.pyw', 'gui.exe'
+ launcher_type = 'gui'
+ ext = '-script.pyw'
old = ['.pyw']
new_header = re.sub('(?i)python.exe','pythonw.exe',header)
else:
- ext, launcher = '-script.py', 'cli.exe'
+ launcher_type = 'cli'
+ ext = '-script.py'
old = ['.py','.pyc','.pyo']
new_header = re.sub('(?i)pythonw.exe','python.exe',header)
- if is_64bit():
- launcher = launcher.replace(".", "-64.")
- else:
- launcher = launcher.replace(".", "-32.")
if os.path.exists(new_header[2:-1]) or sys.platform!='win32':
hdr = new_header
else:
hdr = header
yield (name+ext, hdr+script_text, 't', [name+x for x in old])
yield (
- name+'.exe', resource_string('setuptools', launcher),
+ name+'.exe', get_win_launcher(launcher_type),
'b' # write in binary mode
)
+ if not is_64bit():
+ # install a manifest for the launcher to prevent Windows
+ # from detecting it as an installer (which it will for
+ # launchers like easy_install.exe). Consider only
+ # adding a manifest for launchers detected as installers.
+ # See Distribute #143 for details.
+ m_name = name + '.exe.manifest'
+ yield (m_name, load_launcher_manifest(name), 't')
else:
# On other platforms, we assume the right thing to do is to
# just write the stub with no extension.
yield (name, header+script_text)
+def get_win_launcher(type):
+ """
+ Load the Windows launcher (executable) suitable for launching a script.
+
+ `type` should be either 'cli' or 'gui'
+
+ Returns the executable as a byte string.
+ """
+ launcher_fn = '%s.exe' % type
+ if platform.machine().lower()=='arm':
+ launcher_fn = launcher_fn.replace(".", "-arm.")
+ if is_64bit():
+ launcher_fn = launcher_fn.replace(".", "-64.")
+ else:
+ launcher_fn = launcher_fn.replace(".", "-32.")
+ return resource_string('setuptools', launcher_fn)
+
+def load_launcher_manifest(name):
+ manifest = pkg_resources.resource_string(__name__, 'launcher manifest.xml')
+ if sys.version_info[0] < 3:
+ return manifest % vars()
+ else:
+ return manifest.decode('utf-8') % vars()
+
def rmtree(path, ignore_errors=False, onerror=auto_chmod):
"""Recursively delete a directory tree.
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
index 124c410e..12acc422 100755
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -10,7 +10,7 @@ from distutils import log
from setuptools.command.sdist import sdist
from setuptools.compat import basestring
from distutils.util import convert_path
-from distutils.filelist import FileList
+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 setuptools.command.sdist import walk_revctrl
@@ -275,35 +275,34 @@ class egg_info(Command):
self.broken_egg_info = self.egg_info
self.egg_info = bei # make it work for now
-class FileList(FileList):
+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 os.path.exists(path):
- self.files.append(path)
-
-
+ 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)
-def compose(path):
- # Apple's HFS Plus returns decomposed UTF-8. Since just about
- # everyone else chokes on it, we must make sure to return fully
- # composed UTF-8 only.
- if sys.getfilesystemencoding().lower() == 'utf-8':
- from unicodedata import normalize
- if sys.version_info >= (3,):
- path = normalize('NFC', path)
- else:
- path = normalize('NFC', path.decode('utf-8')).encode('utf-8')
- return path
class manifest_maker(sdist):
@@ -330,7 +329,6 @@ class manifest_maker(sdist):
self.prune_file_list()
self.filelist.sort()
self.filelist.remove_duplicates()
- self.filelist.files = [compose(path) for path in self.filelist.files]
self.write_manifest()
def write_manifest (self):
@@ -338,6 +336,18 @@ class manifest_maker(sdist):
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]
diff --git a/setuptools/command/launcher manifest.xml b/setuptools/command/launcher manifest.xml
new file mode 100644
index 00000000..844d2264
--- /dev/null
+++ b/setuptools/command/launcher manifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity version="1.0.0.0"
+ processorArchitecture="X86"
+ name="%(name)s"
+ type="win32"/>
+ <!-- Identify the application security requirements. -->
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly>
diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py
index 56ef8a66..6f3f48c8 100755
--- a/setuptools/command/sdist.py
+++ b/setuptools/command/sdist.py
@@ -282,8 +282,13 @@ class sdist(_sdist):
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,):
- line = line.decode('UTF-8')
+ 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:
diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py
index bf9c0668..7edbf56a 100755
--- a/setuptools/command/upload.py
+++ b/setuptools/command/upload.py
@@ -108,8 +108,9 @@ class upload(Command):
data['comment'] = comment
if self.sign:
- data['gpg_signature'] = (os.path.basename(filename) + ".asc",
- open(filename+".asc").read())
+ asc_file = open(filename + ".asc")
+ data['gpg_signature'] = (os.path.basename(filename) + ".asc", asc_file.read())
+ asc_file.close()
# set up the authentication
auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip()
diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py
index 505ddadb..178a8793 100644
--- a/setuptools/command/upload_docs.py
+++ b/setuptools/command/upload_docs.py
@@ -8,9 +8,12 @@ PyPI's packages.python.org).
import os
import socket
import zipfile
-import base64
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
@@ -24,18 +27,12 @@ from setuptools.compat import httplib, urlparse
_IS_PYTHON3 = sys.version > '3'
-try:
- bytes
-except NameError:
- bytes = str
-
-def b(str_or_bytes):
- """Return bytes by either encoding the argument as ASCII or simply return
- the argument as-is."""
- if not isinstance(str_or_bytes, bytes):
- return str_or_bytes.encode('ascii')
- else:
- return str_or_bytes
+# 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):
@@ -51,43 +48,67 @@ class upload_docs(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:
- build = self.get_finalized_command('build')
- self.upload_dir = os.path.join(build.build_base, 'docs')
- self.mkpath(self.upload_dir)
- self.ensure_dirname('upload_dir')
- self.announce('Using upload directory %s' % self.upload_dir)
+ 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):
- name = self.distribution.metadata.get_name()
- tmp_dir = tempfile.mkdtemp()
- tmp_file = os.path.join(tmp_dir, "%s.zip" % name)
- zip_file = zipfile.ZipFile(tmp_file, "w")
- for root, dirs, files in os.walk(self.upload_dir):
- if root == self.upload_dir and not files:
- raise DistutilsOptionError(
- "no files found in upload directory '%s'"
- % self.upload_dir)
- for name in files:
- full = os.path.join(root, name)
- relative = root[len(self.upload_dir):].lstrip(os.path.sep)
- dest = os.path.join(relative, name)
- zip_file.write(full, dest)
- zip_file.close()
- return tmp_file
+ 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):
- zip_file = self.create_zipfile()
- self.upload_file(zip_file)
+ # 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()
+ f = open(filename, 'rb')
+ content = f.read()
+ f.close()
meta = self.distribution.metadata
data = {
':action': 'doc_upload',
@@ -95,36 +116,33 @@ class upload_docs(upload):
'content': (os.path.basename(filename), content),
}
# set up the authentication
- credentials = self.username + ':' + self.password
- if _IS_PYTHON3: # base64 only works with bytes in Python 3.
- encoded_creds = base64.encodebytes(credentials.encode('utf8'))
- auth = bytes("Basic ")
- else:
- encoded_creds = base64.encodestring(credentials)
- auth = "Basic "
- auth += encoded_creds.strip()
+ 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 = b('--------------GHSKFJDLGDS7543FJKLFHRE75642756743254')
- sep_boundary = b('\n--') + boundary
+ boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
+ sep_boundary = b('\n--') + b(boundary)
end_boundary = sep_boundary + b('--')
body = []
- for key, values in data.items():
+ 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:
- fn = b(';filename="%s"' % value[0])
+ title += '; filename="%s"' % value[0]
value = value[1]
else:
- fn = b("")
+ value = b(value)
body.append(sep_boundary)
- body.append(b('\nContent-Disposition: form-data; name="%s"'%key))
- body.append(fn)
+ body.append(b(title))
body.append(b("\n\n"))
- body.append(b(value))
- if value and value[-1] == b('\r'):
+ 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"))
diff --git a/setuptools/dist.py b/setuptools/dist.py
index d90acbec..62dd0eb2 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -660,6 +660,11 @@ class Distribution(_Distribution):
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
diff --git a/setuptools/gui-32.exe b/setuptools/gui-32.exe
index 3f64af7d..f8d35096 100644
--- a/setuptools/gui-32.exe
+++ b/setuptools/gui-32.exe
Binary files differ
diff --git a/setuptools/gui-64.exe b/setuptools/gui-64.exe
index 3ab4378e..330c51a5 100644
--- a/setuptools/gui-64.exe
+++ b/setuptools/gui-64.exe
Binary files differ
diff --git a/setuptools/gui-arm-32.exe b/setuptools/gui-arm-32.exe
new file mode 100644
index 00000000..537aff37
--- /dev/null
+++ b/setuptools/gui-arm-32.exe
Binary files differ
diff --git a/setuptools/gui.exe b/setuptools/gui.exe
index 3f64af7d..f8d35096 100644
--- a/setuptools/gui.exe
+++ b/setuptools/gui.exe
Binary files differ
diff --git a/setuptools/package_index.py b/setuptools/package_index.py
index 2cab63c1..85a1deeb 100755
--- a/setuptools/package_index.py
+++ b/setuptools/package_index.py
@@ -1,5 +1,6 @@
"""PyPI and direct package downloading"""
import sys, os.path, re, shutil, random, socket
+import itertools
import base64
from pkg_resources import *
from distutils import log
@@ -14,6 +15,8 @@ except ImportError:
from md5 import md5
from fnmatch import translate
+from setuptools.py24compat import wraps
+
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
@@ -137,9 +140,38 @@ def interpret_distro_name(location, basename, metadata,
platform = platform
)
+# From Python 2.7 docs
+def unique_everseen(iterable, key=None):
+ "List unique elements, preserving order. Remember all elements ever seen."
+ # unique_everseen('AAAABBBCCDAABBB') --> A B C D
+ # unique_everseen('ABBCcAD', str.lower) --> A B C D
+ seen = set()
+ seen_add = seen.add
+ if key is None:
+ for element in itertools.ifilterfalse(seen.__contains__, iterable):
+ seen_add(element)
+ yield element
+ else:
+ for element in iterable:
+ k = key(element)
+ if k not in seen:
+ seen_add(k)
+ yield element
+
+def unique_values(func):
+ """
+ Wrap a function returning an iterable such that the resulting iterable
+ only ever yields unique items.
+ """
+ @wraps(func)
+ def wrapper(*args, **kwargs):
+ return unique_everseen(func(*args, **kwargs))
+ return wrapper
+
REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I)
# this line is here to fix emacs' cruddy broken syntax highlighting
+@unique_values
def find_external_links(url, page):
"""Find rel="homepage" and rel="download" links in `page`, yielding URLs"""
@@ -157,6 +189,7 @@ def find_external_links(url, page):
if match:
yield urljoin(url, htmldecode(match.group(1)))
+
user_agent = "Python-urllib/%s distribute/%s" % (
sys.version[:3], require('distribute')[0].version
)
@@ -666,6 +699,10 @@ class PackageIndex(Environment):
#
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 url2pathname(urlparse.urlparse(url)[2])
else:
@@ -706,6 +743,55 @@ class PackageIndex(Environment):
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)
@@ -789,9 +875,8 @@ def open_with_auth(url):
# Double scheme does not raise on Mac OS X as revealed by a
# failing test. We would expect "nonnumeric port". Refs #20.
- if sys.platform == 'darwin':
- if netloc.endswith(':'):
- raise httplib.InvalidURL("nonnumeric port: ''")
+ if netloc.endswith(':'):
+ raise httplib.InvalidURL("nonnumeric port: ''")
if scheme in ('http', 'https'):
auth, host = splituser(netloc)
diff --git a/setuptools/py24compat.py b/setuptools/py24compat.py
new file mode 100644
index 00000000..c5d7d204
--- /dev/null
+++ b/setuptools/py24compat.py
@@ -0,0 +1,11 @@
+"""
+Forward-compatibility support for Python 2.4 and earlier
+"""
+
+# from jaraco.compat 1.2
+try:
+ from functools import wraps
+except ImportError:
+ def wraps(func):
+ "Just return the function unwrapped"
+ return lambda x: x
diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py
index c49d1cfe..05b73d84 100755
--- a/setuptools/sandbox.py
+++ b/setuptools/sandbox.py
@@ -1,5 +1,8 @@
import os, sys, tempfile, operator, pkg_resources
-_os = sys.modules[os.name]
+if os.name == "java":
+ import org.python.modules.posix.PosixModule as _os
+else:
+ _os = sys.modules[os.name]
try:
_file = file
except NameError:
diff --git a/site.py b/setuptools/site-patch.py
index a7166f14..a7166f14 100755..100644
--- a/site.py
+++ b/setuptools/site-patch.py
diff --git a/setuptools/tests/doctest.py b/setuptools/tests/doctest.py
index 1f23fd8e..35d588d0 100644
--- a/setuptools/tests/doctest.py
+++ b/setuptools/tests/doctest.py
@@ -1968,7 +1968,9 @@ def testfile(filename, module_relative=True, name=None, package=None,
runner = DocTestRunner(verbose=verbose, optionflags=optionflags)
# Read the file, convert it to a test, and run it.
- s = open(filename).read()
+ f = open(filename)
+ s = f.read()
+ f.close()
test = parser.get_doctest(s, globs, name, filename, 0)
runner.run(test)
@@ -2353,7 +2355,9 @@ def DocFileTest(path, module_relative=True, package=None,
# Find the file and read it.
name = os.path.basename(path)
- doc = open(path).read()
+ f = open(path)
+ doc = f.read()
+ f.close()
# Convert it to a test, and wrap it in a DocFileCase.
test = parser.get_doctest(doc, globs, name, path, 0)
diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py
index 0813959d..9d7ce711 100644
--- a/setuptools/tests/test_develop.py
+++ b/setuptools/tests/test_develop.py
@@ -89,16 +89,16 @@ class TestDevelopTest(unittest.TestCase):
self.assertEqual(content, ['easy-install.pth', 'foo.egg-link'])
# Check that we are using the right code.
- f = open(os.path.join(site.USER_SITE, 'foo.egg-link'), 'rt')
+ egg_link_file = open(os.path.join(site.USER_SITE, 'foo.egg-link'), 'rt')
try:
- path = f.read().split()[0].strip()
+ path = egg_link_file.read().split()[0].strip()
finally:
- f.close()
- f = open(os.path.join(path, 'foo', '__init__.py'), 'rt')
+ egg_link_file.close()
+ init_file = open(os.path.join(path, 'foo', '__init__.py'), 'rt')
try:
- init = f.read().strip()
+ init = init_file.read().strip()
finally:
- f.close()
+ init_file.close()
if sys.version < "3":
self.assertEqual(init, 'print "foo"')
else:
diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py
index 93ab8816..a8adb68c 100644
--- a/setuptools/tests/test_dist_info.py
+++ b/setuptools/tests/test_dist_info.py
@@ -50,9 +50,9 @@ class TestDistInfo(unittest.TestCase):
versioned = os.path.join(self.tmpdir,
'VersionedDistribution-2.718.dist-info')
os.mkdir(versioned)
- f = open(os.path.join(versioned, 'METADATA'), 'w+')
+ metadata_file = open(os.path.join(versioned, 'METADATA'), 'w+')
try:
- f.write(DALS(
+ metadata_file.write(DALS(
"""
Metadata-Version: 1.2
Name: VersionedDistribution
@@ -61,13 +61,13 @@ class TestDistInfo(unittest.TestCase):
Requires-Dist: quux (>=1.1); extra == 'baz'
"""))
finally:
- f.close()
+ metadata_file.close()
unversioned = os.path.join(self.tmpdir,
'UnversionedDistribution.dist-info')
os.mkdir(unversioned)
- f = open(os.path.join(unversioned, 'METADATA'), 'w+')
+ metadata_file = open(os.path.join(unversioned, 'METADATA'), 'w+')
try:
- f.write(DALS(
+ metadata_file.write(DALS(
"""
Metadata-Version: 1.2
Name: UnversionedDistribution
@@ -77,7 +77,7 @@ class TestDistInfo(unittest.TestCase):
Requires-Dist: quux (>=1.1); extra == 'baz'
"""))
finally:
- f.close()
+ metadata_file.close()
def tearDown(self):
shutil.rmtree(self.tmpdir)
diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py
index 34faeaad..d76025d2 100644
--- a/setuptools/tests/test_easy_install.py
+++ b/setuptools/tests/test_easy_install.py
@@ -13,7 +13,7 @@ import tarfile
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 easy_install, fix_jython_executable, 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
@@ -51,7 +51,7 @@ if __name__ == '__main__':
sys.exit(
load_entry_point('spec', 'console_scripts', 'name')()
)
-""" % sys.executable
+""" % fix_jython_executable(sys.executable, "")
SETUP_PY = """\
from setuptools import setup
diff --git a/setuptools/tests/test_markerlib.py b/setuptools/tests/test_markerlib.py
index 7ff2f584..aa461846 100644
--- a/setuptools/tests/test_markerlib.py
+++ b/setuptools/tests/test_markerlib.py
@@ -16,15 +16,15 @@ class TestMarkerlib(unittest.TestCase):
os_name = os.name
- self.assert_(interpret(""))
+ self.assertTrue(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.assertTrue(interpret("os.name != 'buuuu'"))
+ self.assertTrue(interpret("python_version > '1.0'"))
+ self.assertTrue(interpret("python_version < '5.0'"))
+ self.assertTrue(interpret("python_version <= '5.0'"))
+ self.assertTrue(interpret("python_version >= '1.0'"))
+ self.assertTrue(interpret("'%s' in os.name" % os_name))
+ self.assertTrue(interpret("'buuuu' not in os.name"))
self.assertFalse(interpret("os.name == 'buuuu'"))
self.assertFalse(interpret("python_version < '1.0'"))
@@ -36,7 +36,7 @@ class TestMarkerlib(unittest.TestCase):
environment = default_environment()
environment['extra'] = 'test'
- self.assert_(interpret("extra == 'test'", environment))
+ self.assertTrue(interpret("extra == 'test'", environment))
self.assertFalse(interpret("extra == 'doc'", environment))
def raises_nameError():
diff --git a/setuptools/tests/test_resources.py b/setuptools/tests/test_resources.py
index 4b3d50a8..a2ec03c0 100644
--- a/setuptools/tests/test_resources.py
+++ b/setuptools/tests/test_resources.py
@@ -478,13 +478,14 @@ class ParseTests(TestCase):
p1, p2 = parse_version(s1),parse_version(s2)
self.assertEqual(p1,p2, (s1,s2,p1,p2))
+ c('1.2-rc1', '1.2rc1')
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('0.0c1', '0-rc1')
c('1.2a1', '1.2.a.1'); c('1.2...a', '1.2a')
def testVersionOrdering(self):
@@ -493,14 +494,11 @@ class ParseTests(TestCase):
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')
@@ -511,20 +509,8 @@ class ParseTests(TestCase):
c('0.4', '4.0')
c('0.0.4', '0.4.0')
c('0pl1', '0.4pl1')
+ c('2.1.0-rc1','2.1.0')
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
@@ -562,6 +548,15 @@ class ScriptHeaderTests(TestCase):
if (sys.version_info >= (3,) and os.environ.get("LC_CTYPE")
in (None, "C", "POSIX")):
return
+
+ class java:
+ class lang:
+ class System:
+ @staticmethod
+ def getProperty(property):
+ return ""
+ sys.modules["java"] = java
+
platform = sys.platform
sys.platform = 'java1.5.0_13'
stdout = sys.stdout
@@ -585,6 +580,7 @@ class ScriptHeaderTests(TestCase):
'#!%s -x\n' % self.non_ascii_exe)
self.assertTrue('Unable to adapt shebang line' in sys.stdout.getvalue())
finally:
+ del sys.modules["java"]
sys.platform = platform
sys.stdout = stdout
diff --git a/setuptools/tests/test_sdist.py b/setuptools/tests/test_sdist.py
index 49007c3d..ececa765 100644
--- a/setuptools/tests/test_sdist.py
+++ b/setuptools/tests/test_sdist.py
@@ -7,9 +7,11 @@ import shutil
import sys
import tempfile
import unittest
+import unicodedata
-from setuptools.compat import StringIO
+from setuptools.compat import StringIO, urllib
from setuptools.command.sdist import sdist
+from setuptools.command.egg_info import manifest_maker
from setuptools.dist import Distribution
@@ -28,7 +30,52 @@ 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')
@@ -62,106 +109,286 @@ class TestSdistTest(unittest.TestCase):
cmd.ensure_finalized()
# squelch output
- old_stdout = sys.stdout
- old_stderr = sys.stderr
- sys.stdout = StringIO()
- sys.stderr = StringIO()
+ quiet()
try:
cmd.run()
finally:
- sys.stdout = old_stdout
- sys.stderr = old_stderr
+ 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_filelist_is_fully_composed(self):
- # Test for #303. Requires HFS Plus to fail.
-
- # Add file with non-ASCII filename
- filename = os.path.join('sdist_test', 'smörbröd.py')
- open(filename, 'w').close()
-
+ def test_manifest_is_written_with_utf8_encoding(self):
+ # Test for #303.
dist = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py'
- cmd = sdist(dist)
- cmd.ensure_finalized()
+ mm = manifest_maker(dist)
+ mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
+ os.mkdir('sdist_test.egg-info')
- # squelch output
- old_stdout = sys.stdout
- old_stderr = sys.stderr
- sys.stdout = StringIO()
- sys.stderr = StringIO()
+ # UTF-8 filename
+ filename = os.path.join('sdist_test', 'smörbröd.py')
+
+ # Add UTF-8 filename and write manifest
+ quiet()
try:
- cmd.run()
+ mm.run()
+ mm.filelist.files.append(filename)
+ mm.write_manifest()
finally:
- sys.stdout = old_stdout
- sys.stderr = old_stderr
+ unquiet()
- self.assertTrue(filename in cmd.filelist.files)
+ manifest = open(mm.manifest, 'rbU')
+ contents = manifest.read()
+ manifest.close()
- def test_manifest_is_written_in_utf8(self):
+ # 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.
-
- # Add file with non-ASCII filename
- filename = os.path.join('sdist_test', 'smörbröd.py')
- open(filename, 'w').close()
-
dist = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py'
cmd = sdist(dist)
cmd.ensure_finalized()
- # squelch output
- old_stdout = sys.stdout
- old_stderr = sys.stderr
- sys.stdout = StringIO()
- sys.stderr = StringIO()
+ # Create manifest
+ quiet()
try:
cmd.run()
finally:
- sys.stdout = old_stdout
- sys.stderr = old_stderr
+ unquiet()
- manifest = open(os.path.join('sdist_test.egg-info', 'SOURCES.txt'), 'rbU')
- contents = manifest.read()
+ # 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()
- self.assertTrue(len(contents))
- # This must not fail:
- contents.decode('UTF-8')
+ # The file must exist to be included in the filelist
+ open(filename, 'w').close()
- def test_manifest_is_read_in_utf8(self):
- # Test for #303.
+ # Re-read manifest
+ cmd.filelist.files = []
+ quiet()
+ try:
+ cmd.read_manifest()
+ finally:
+ unquiet()
- # Add file with non-ASCII filename
- filename = os.path.join('sdist_test', 'smörbröd.py')
- open(filename, 'w').close()
+ # 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()
- # squelch output
- old_stdout = sys.stdout
- old_stderr = sys.stderr
- sys.stdout = StringIO()
- sys.stderr = StringIO()
+ # UTF-8 filename
+ filename = os.path.join(b('sdist_test'), b('smörbröd.py'))
+ open(filename, 'w').close()
+
+ quiet()
try:
cmd.run()
finally:
- sys.stdout = old_stdout
- sys.stderr = old_stderr
+ unquiet()
+
+ if sys.platform == 'darwin':
+ filename = decompose(filename)
+
+ if sys.version_info >= (3,):
+ fs_enc = sys.getfilesystemencoding()
+
+ if sys.platform == 'win32':
+ if fs_enc == 'cp1252':
+ # Python 3 mangles the UTF-8 filename
+ filename = filename.decode('cp1252')
+ self.assertTrue(filename in cmd.filelist.files)
+ else:
+ filename = filename.decode('mbcs')
+ 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()
- cmd.filelist.files = []
- cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
- cmd.read_manifest()
+ # Latin-1 filename
+ filename = os.path.join(b('sdist_test'), LATIN1_FILENAME)
+ open(filename, 'w').close()
+ self.assertTrue(os.path.isfile(filename))
- self.assertTrue(filename in cmd.filelist.files)
+ quiet()
+ try:
+ cmd.run()
+ finally:
+ unquiet()
+
+ if sys.version_info >= (3,):
+ #not all windows systems have a default FS encoding of cp1252
+ if sys.platform == 'win32':
+ # Latin-1 is similar to Windows-1252 however
+ # on mbcs filesys it is not in latin-1 encoding
+ fs_enc = sys.getfilesystemencoding()
+ if fs_enc == 'mbcs':
+ filename = filename.decode('mbcs')
+ else:
+ filename = filename.decode('latin-1')
+
+ self.assertTrue(filename in cmd.filelist.files)
+ else:
+ # The Latin-1 filename should have been skipped
+ filename = filename.decode('latin-1')
+ 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():
diff --git a/setuptools/tests/test_upload_docs.py b/setuptools/tests/test_upload_docs.py
index 8b2dc892..769f16cc 100644
--- a/setuptools/tests/test_upload_docs.py
+++ b/setuptools/tests/test_upload_docs.py
@@ -54,12 +54,19 @@ class TestUploadDocsTest(unittest.TestCase):
cmd = upload_docs(dist)
cmd.upload_dir = self.upload_dir
- zip_file = cmd.create_zipfile()
+ 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(zip_file)
+ assert zipfile.is_zipfile(tmp_file)
- zip_f = zipfile.ZipFile(zip_file) # woh...
+ zip_file = zipfile.ZipFile(tmp_file) # woh...
- assert zip_f.namelist() == ['index.html']
+ assert zip_file.namelist() == ['index.html']
+ zip_file.close()
+ finally:
+ shutil.rmtree(tmp_dir)
diff --git a/setuptools/tests/win_script_wrapper.txt b/setuptools/tests/win_script_wrapper.txt
index 1fe47bc5..731243dd 100644
--- a/setuptools/tests/win_script_wrapper.txt
+++ b/setuptools/tests/win_script_wrapper.txt
@@ -17,7 +17,7 @@ Let's create a simple script, foo-script.py:
>>> from setuptools.command.easy_install import nt_quote_arg
>>> sample_directory = tempfile.mkdtemp()
>>> f = open(os.path.join(sample_directory, 'foo-script.py'), 'w')
- >>> bytes = f.write(
+ >>> bytes_written = f.write(
... """#!%(python_exe)s
... import sys
... input = repr(sys.stdin.read())
@@ -37,8 +37,8 @@ We'll also copy cli.exe to the sample-directory with the name foo.exe:
>>> import pkg_resources
>>> f = open(os.path.join(sample_directory, 'foo.exe'), 'wb')
- >>> bytes = f.write(
- ... pkg_resources.resource_string('setuptools', 'cli.exe')
+ >>> bytes_written = f.write(
+ ... pkg_resources.resource_string('setuptools', 'cli-32.exe')
... )
>>> f.close()
@@ -82,7 +82,7 @@ options as usual. For example, to run in optimized mode and
enter the interpreter after running the script, you could use -Oi:
>>> f = open(os.path.join(sample_directory, 'foo-script.py'), 'w')
- >>> bytes = f.write(
+ >>> bytes_written = f.write(
... """#!%(python_exe)s -Oi
... import sys
... input = repr(sys.stdin.read())
@@ -112,10 +112,12 @@ Now let's test the GUI version with the simple scipt, bar-script.py:
>>> from setuptools.command.easy_install import nt_quote_arg
>>> sample_directory = tempfile.mkdtemp()
>>> f = open(os.path.join(sample_directory, 'bar-script.pyw'), 'w')
- >>> bytes = f.write(
+ >>> bytes_written = f.write(
... """#!%(python_exe)s
... import sys
- ... open(sys.argv[1], 'wb').write(repr(sys.argv[2]).encode('ascii'))
+ ... f = open(sys.argv[1], 'wb')
+ ... bytes_written = f.write(repr(sys.argv[2]).encode('utf-8'))
+ ... f.close()
... """ % dict(python_exe=nt_quote_arg(sys.executable)))
>>> f.close()
@@ -123,8 +125,8 @@ We'll also copy gui.exe to the sample-directory with the name bar.exe:
>>> import pkg_resources
>>> f = open(os.path.join(sample_directory, 'bar.exe'), 'wb')
- >>> bytes = f.write(
- ... pkg_resources.resource_string('setuptools', 'gui.exe')
+ >>> bytes_written = f.write(
+ ... pkg_resources.resource_string('setuptools', 'gui-32.exe')
... )
>>> f.close()
diff --git a/tests/test_pkg_resources.py b/tests/test_pkg_resources.py
new file mode 100644
index 00000000..7009b4ab
--- /dev/null
+++ b/tests/test_pkg_resources.py
@@ -0,0 +1,61 @@
+import sys
+import tempfile
+import os
+import zipfile
+
+import pkg_resources
+
+class EggRemover(unicode):
+ def __call__(self):
+ if self in sys.path:
+ sys.path.remove(self)
+ if os.path.exists(self):
+ os.remove(self)
+
+class TestZipProvider(object):
+ finalizers = []
+
+ @classmethod
+ def setup_class(cls):
+ "create a zip egg and add it to sys.path"
+ egg = tempfile.NamedTemporaryFile(suffix='.egg', delete=False)
+ zip_egg = zipfile.ZipFile(egg, 'w')
+ zip_info = zipfile.ZipInfo()
+ zip_info.filename = 'mod.py'
+ zip_info.date_time = 2013, 5, 12, 13, 25, 0
+ zip_egg.writestr(zip_info, 'x = 3\n')
+ zip_info = zipfile.ZipInfo()
+ zip_info.filename = 'data.dat'
+ zip_info.date_time = 2013, 5, 12, 13, 25, 0
+ zip_egg.writestr(zip_info, 'hello, world!')
+ zip_egg.close()
+ egg.close()
+
+ sys.path.append(egg.name)
+ cls.finalizers.append(EggRemover(egg.name))
+
+ @classmethod
+ def teardown_class(cls):
+ for finalizer in cls.finalizers:
+ finalizer()
+
+ def test_resource_filename_rewrites_on_change(self):
+ """
+ If a previous call to get_resource_filename has saved the file, but
+ the file has been subsequently mutated with different file of the
+ same size and modification time, it should not be overwritten on a
+ subsequent call to get_resource_filename.
+ """
+ import mod
+ manager = pkg_resources.ResourceManager()
+ zp = pkg_resources.ZipProvider(mod)
+ filename = zp.get_resource_filename(manager, 'data.dat')
+ assert os.stat(filename).st_mtime == 1368379500
+ f = open(filename, 'wb')
+ f.write('hello, world?')
+ f.close()
+ os.utime(filename, (1368379500, 1368379500))
+ filename = zp.get_resource_filename(manager, 'data.dat')
+ f = open(filename)
+ assert f.read() == 'hello, world!'
+ manager.cleanup_resources()