Commit a85aa143 authored by Jason R. Coombs's avatar Jason R. Coombs

Merge Vinay Sajip's unified Python 2/3 support from distribute 3

--HG--
branch : distribute
parents 94fc39cb 641eac65
...@@ -11,3 +11,4 @@ bin ...@@ -11,3 +11,4 @@ bin
include include
\.Python \.Python
*.swp *.swp
CHANGES (links).txt
...@@ -37,3 +37,21 @@ de44acab3cfce1f5bc811d6c0fa1a88ca0e9533f 0.6.21 ...@@ -37,3 +37,21 @@ de44acab3cfce1f5bc811d6c0fa1a88ca0e9533f 0.6.21
b69f072c000237435e17b8bbb304ba6f957283eb 0.6.26 b69f072c000237435e17b8bbb304ba6f957283eb 0.6.26
469c3b948e41ef28752b3cdf3c7fb9618355ebf5 0.6.27 469c3b948e41ef28752b3cdf3c7fb9618355ebf5 0.6.27
fc379e63586ad3c6838e1bda216548ba8270b8f0 0.6.28 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
...@@ -4,5 +4,6 @@ python: ...@@ -4,5 +4,6 @@ python:
- 2.6 - 2.6
- 2.7 - 2.7
- 3.2 - 3.2
- 3.3
# command to run tests # command to run tests
script: python setup.py test script: python setup.py test
...@@ -2,17 +2,179 @@ ...@@ -2,17 +2,179 @@
CHANGES 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 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 #320: Fix check for "createable" in distribute_setup.py.
* Issue #305: Remove a warning that was triggered during normal operations. * Issue #305: Remove a warning that was triggered during normal operations.
* Issue #311: Print metadata in UTF-8 independent of platform. * Issue #311: Print metadata in UTF-8 independent of platform.
* Issue #303: Read manifest file with UTF-8 encoding under Python 3. * 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 #301: Allow to run tests of namespace packages when using 2to3.
* Issue #304: Prevent import loop in site.py under Python 3.3. * 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, * 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 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 in build/lib makes it work, although you will have to rebuild the module
...@@ -35,7 +197,8 @@ CHANGES ...@@ -35,7 +197,8 @@ CHANGES
* Issue #294: setup.py can now be invoked from any directory. * Issue #294: setup.py can now be invoked from any directory.
* Scripts are now installed honoring the umask. * Scripts are now installed honoring the umask.
* Added support for .dist-info directories. * 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 0.6.27
...@@ -292,11 +455,10 @@ CHANGES ...@@ -292,11 +455,10 @@ CHANGES
----- -----
* Added the generation of `distribute_setup_3k.py` during the release. * 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 * Added an upload_docs command to easily upload project documentation to
PyPI's http://packages.python.org. PyPI's http://packages.python.org. This close issue #56.
This close http://bitbucket.org/tarek/distribute/issue/56.
* Fixed a bootstrap bug on the use_setuptools() API. * Fixed a bootstrap bug on the use_setuptools() API.
...@@ -325,7 +487,7 @@ setuptools ...@@ -325,7 +487,7 @@ setuptools
This closes http://bugs.python.org/setuptools/issue39. This closes http://bugs.python.org/setuptools/issue39.
* Added option to run 2to3 automatically when installing on Python 3. * 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. * Fixed invalid usage of requirement.parse, that broke develop -d.
This closes http://bugs.python.org/setuptools/issue44. This closes http://bugs.python.org/setuptools/issue44.
...@@ -339,11 +501,9 @@ setuptools ...@@ -339,11 +501,9 @@ setuptools
bootstrapping bootstrapping
============= =============
* Fixed bootstrap not working on Windows. * Fixed bootstrap not working on Windows. This closes issue #49.
This closes http://bitbucket.org/tarek/distribute/issue/49.
* Fixed 2.6 dependencies. * Fixed 2.6 dependencies. This closes issue #50.
This closes http://bitbucket.org/tarek/distribute/issue/50.
* Make sure setuptools is patched when running through easy_install * Make sure setuptools is patched when running through easy_install
This closes http://bugs.python.org/setuptools/issue40. This closes http://bugs.python.org/setuptools/issue40.
...@@ -356,16 +516,14 @@ setuptools ...@@ -356,16 +516,14 @@ setuptools
========== ==========
* package_index.urlopen now catches BadStatusLine and malformed url errors. * package_index.urlopen now catches BadStatusLine and malformed url errors.
This closes http://bitbucket.org/tarek/distribute/issue/16 and This closes issue #16 and issue #18.
http://bitbucket.org/tarek/distribute/issue/18.
* zip_ok is now False by default. This closes * zip_ok is now False by default. This closes
http://bugs.python.org/setuptools/issue33. http://bugs.python.org/setuptools/issue33.
* Fixed invalid URL error catching. http://bugs.python.org/setuptools/issue20. * Fixed invalid URL error catching. http://bugs.python.org/setuptools/issue20.
* Fixed invalid bootstraping with easy_install installation * Fixed invalid bootstraping with easy_install installation (issue #40).
http://bitbucket.org/tarek/distribute/issue/40.
Thanks to Florian Schulze for the help. Thanks to Florian Schulze for the help.
* Removed buildout/bootstrap.py. A new repository will create a specific * Removed buildout/bootstrap.py. A new repository will create a specific
...@@ -377,7 +535,7 @@ bootstrapping ...@@ -377,7 +535,7 @@ bootstrapping
* The boostrap process leave setuptools alone if detected in the system * The boostrap process leave setuptools alone if detected in the system
and --root or --prefix is provided, but is not in the same location. 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 0.6
...@@ -387,45 +545,38 @@ setuptools ...@@ -387,45 +545,38 @@ setuptools
========== ==========
* Packages required at build time where not fully present at install time. * 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 * Protected against failures in tarfile extraction. This closes issue #10.
http://bitbucket.org/tarek/distribute/issue/10.
* Made Jython api_tests.txt doctest compatible. This closes * Made Jython api_tests.txt doctest compatible. This closes issue #7.
http://bitbucket.org/tarek/distribute/issue/7.
* sandbox.py replaced builtin type file with builtin function open. This * 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 * Immediately close all file handles. This closes issue #3.
http://bitbucket.org/tarek/distribute/issue/3.
* Added compatibility with Subversion 1.6. This references * Added compatibility with Subversion 1.6. This references issue #1.
http://bitbucket.org/tarek/distribute/issue/1.
pkg_resources pkg_resources
============= =============
* Avoid a call to /usr/bin/sw_vers on OSX and use the official platform API * 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 instead. Based on a patch from ronaldoussoren. This closes issue #5.
http://bitbucket.org/tarek/distribute/issue/5.
* Fixed a SandboxViolation for mkdir that could occur in certain cases. * 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. * 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. * 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 * Immediately close all file handles. This closes issue #3.
http://bitbucket.org/tarek/distribute/issue/3.
easy_install easy_install
============ ============
* Immediately close all file handles. This closes * Immediately close all file handles. This closes issue #3.
http://bitbucket.org/tarek/distribute/issue/3.
...@@ -8,16 +8,21 @@ Contributors ...@@ -8,16 +8,21 @@ Contributors
* Christophe Combelles * Christophe Combelles
* Daniel Stutzbach * Daniel Stutzbach
* Daniel Holth * Daniel Holth
* Dirley Rodrigues
* Grigory Petrov
* Hanno Schlichting * Hanno Schlichting
* Jannis Leidel * Jannis Leidel
* Jason R. Coombs * Jason R. Coombs
* Jim Fulton * Jim Fulton
* Jonathan Lange
* Justin Azoff * Justin Azoff
* Lennart Regebro * Lennart Regebro
* Marc Abramowitz * Marc Abramowitz
* Martin von Löwis * Martin von Löwis
* Noufal Ibrahim * Noufal Ibrahim
* Pete Hollobon
* Philip Jenvey * Philip Jenvey
* Philip Thiem
* Reinout van Rees * Reinout van Rees
* Robert Myers * Robert Myers
* Stefan H. Holek * Stefan H. Holek
......
recursive-include setuptools *.py *.txt *.exe recursive-include setuptools *.py *.txt *.exe *.xml
recursive-include tests *.py *.c *.pyx *.txt recursive-include tests *.py *.c *.pyx *.txt
recursive-include setuptools/tests *.html recursive-include setuptools/tests *.html
recursive-include docs *.py *.txt *.conf *.css *.css_t Makefile indexsidebar.html recursive-include docs *.py *.txt *.conf *.css *.css_t Makefile indexsidebar.html
......
...@@ -11,35 +11,14 @@ Disclaimers ...@@ -11,35 +11,14 @@ Disclaimers
About the fork 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 Distribute was intended to replace Setuptools as the standard method
for working with Python module distributions. 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: `Distribute` is now being maintained as a branch in the `Setuptools
repository <https://bitbucket.org/pypa/setuptools>`_.
- Providing a backward compatible version to replace Setuptools
and make all distributions that depend on Setuptools work as
before, but with less bugs and behaviorial issues.
This work is done in the 0.6.x series.
Starting with version 0.6.2, Distribute supports Python 3.
Installing and using distribute for Python 3 code works exactly
the same as for Python 2 code, but Distribute also helps you to support
Python 2 and Python 3 from the same source code by letting you run 2to3
on the code as a part of the build process, by setting the keyword parameter
``use_2to3`` to True. See http://packages.python.org/distribute for more
information.
- Refactoring the code, and releasing it in several distributions.
This work is being done in the 0.7.x series but not yet released.
The roadmap is still evolving, and the page that is up-to-date is
located at : `http://packages.python.org/distribute/roadmap`.
If you install `Distribute` and want to switch back for any reason to
`Setuptools`, get to the `Uninstallation instructions`_ section.
More documentation More documentation
================== ==================
...@@ -99,9 +78,9 @@ Source installation ...@@ -99,9 +78,9 @@ Source installation
Download the source tarball, uncompress it, then run the install command:: 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 $ curl -O http://pypi.python.org/packages/source/d/distribute/distribute-0.6.46.tar.gz
$ tar -xzvf distribute-0.6.29.tar.gz $ tar -xzvf distribute-0.6.46.tar.gz
$ cd distribute-0.6.29 $ cd distribute-0.6.46
$ python setup.py install $ python setup.py install
--------------------------- ---------------------------
...@@ -224,5 +203,4 @@ Feedback and getting involved ...@@ -224,5 +203,4 @@ Feedback and getting involved
- Mailing list: http://mail.python.org/mailman/listinfo/distutils-sig - Mailing list: http://mail.python.org/mailman/listinfo/distutils-sig
- Issue tracker: http://bitbucket.org/tarek/distribute/issues/ - Issue tracker: http://bitbucket.org/tarek/distribute/issues/
- Code Repository: http://bitbucket.org/tarek/distribute - Code Repository: http://bitbucket.org/pypa/setuptools?at=distribute
...@@ -17,9 +17,7 @@ where EXPR belongs to any of those: ...@@ -17,9 +17,7 @@ where EXPR belongs to any of those:
__all__ = ['default_environment', 'compile', 'interpret'] __all__ = ['default_environment', 'compile', 'interpret']
from ast import Compare, BoolOp, Attribute, Name, Load, Str, cmpop, boolop import ast
from ast import parse, copy_location, NodeTransformer
import os import os
import platform import platform
import sys import sys
...@@ -27,7 +25,16 @@ import weakref ...@@ -27,7 +25,16 @@ import weakref
_builtin_compile = compile _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 # restricted set of variables
_VARS = {'sys.platform': sys.platform, _VARS = {'sys.platform': sys.platform,
...@@ -46,11 +53,15 @@ def default_environment(): ...@@ -46,11 +53,15 @@ def default_environment():
"""Return copy of default PEP 385 globals dictionary.""" """Return copy of default PEP 385 globals dictionary."""
return dict(_VARS) return dict(_VARS)
class ASTWhitelist(NodeTransformer): class ASTWhitelist(ast.NodeTransformer):
def __init__(self, statement): def __init__(self, statement):
self.statement = statement # for error messages 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): def visit(self, node):
"""Ensure statement only contains allowed nodes.""" """Ensure statement only contains allowed nodes."""
...@@ -58,15 +69,15 @@ class ASTWhitelist(NodeTransformer): ...@@ -58,15 +69,15 @@ class ASTWhitelist(NodeTransformer):
raise SyntaxError('Not allowed in environment markers.\n%s\n%s' % raise SyntaxError('Not allowed in environment markers.\n%s\n%s' %
(self.statement, (self.statement,
(' ' * node.col_offset) + '^')) (' ' * node.col_offset) + '^'))
return NodeTransformer.visit(self, node) return ast.NodeTransformer.visit(self, node)
def visit_Attribute(self, node): def visit_Attribute(self, node):
"""Flatten one level of attribute access.""" """Flatten one level of attribute access."""
new_node = Name("%s.%s" % (node.value.id, node.attr), node.ctx) new_node = ast.Name("%s.%s" % (node.value.id, node.attr), node.ctx)
return copy_location(new_node, node) return ast.copy_location(new_node, node)
def parse_marker(marker): def parse_marker(marker):
tree = parse(marker, mode='eval') tree = ast.parse(marker, mode='eval')
new_tree = ASTWhitelist(marker).generic_visit(tree) new_tree = ASTWhitelist(marker).generic_visit(tree)
return new_tree return new_tree
......
...@@ -14,6 +14,7 @@ the appropriate options to ``use_setuptools()``. ...@@ -14,6 +14,7 @@ the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools. This file can also be run as a script to install or upgrade setuptools.
""" """
import os import os
import shutil
import sys import sys
import time import time
import fnmatch import fnmatch
...@@ -48,7 +49,7 @@ except ImportError: ...@@ -48,7 +49,7 @@ except ImportError:
args = [quote(arg) for arg in args] args = [quote(arg) for arg in args]
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 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/" DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
SETUPTOOLS_FAKED_VERSION = "0.6c11" SETUPTOOLS_FAKED_VERSION = "0.6c11"
...@@ -86,8 +87,11 @@ def _install(tarball, install_args=()): ...@@ -86,8 +87,11 @@ def _install(tarball, install_args=()):
if not _python_cmd('setup.py', 'install', *install_args): if not _python_cmd('setup.py', 'install', *install_args):
log.warn('Something went wrong during the installation.') log.warn('Something went wrong during the installation.')
log.warn('See the error message above.') log.warn('See the error message above.')
# exitcode will be 2
return 2
finally: finally:
os.chdir(old_wd) os.chdir(old_wd)
shutil.rmtree(tmpdir)
def _build_egg(egg, tarball, to_dir): def _build_egg(egg, tarball, to_dir):
...@@ -112,6 +116,7 @@ def _build_egg(egg, tarball, to_dir): ...@@ -112,6 +116,7 @@ def _build_egg(egg, tarball, to_dir):
finally: finally:
os.chdir(old_wd) os.chdir(old_wd)
shutil.rmtree(tmpdir)
# returning the result # returning the result
log.warn(egg) log.warn(egg)
if not os.path.exists(egg): if not os.path.exists(egg):
...@@ -139,6 +144,16 @@ def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, ...@@ -139,6 +144,16 @@ def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
try: try:
try: try:
import pkg_resources 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 hasattr(pkg_resources, '_distribute'):
if not no_fake: if not no_fake:
_fake_setuptools() _fake_setuptools()
...@@ -234,7 +249,9 @@ def _no_sandbox(function): ...@@ -234,7 +249,9 @@ def _no_sandbox(function):
def _patch_file(path, content): def _patch_file(path, content):
"""Will backup the file then patch it""" """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: if existing_content == content:
# already patched # already patched
log.warn('Already patched.') log.warn('Already patched.')
...@@ -252,7 +269,10 @@ _patch_file = _no_sandbox(_patch_file) ...@@ -252,7 +269,10 @@ _patch_file = _no_sandbox(_patch_file)
def _same_content(path, content): 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): def _rename_path(path):
...@@ -443,8 +463,9 @@ def _relaunch(): ...@@ -443,8 +463,9 @@ def _relaunch():
log.warn('Relaunching...') log.warn('Relaunching...')
# we have to relaunch the process # we have to relaunch the process
# pip marker to avoid a relaunch bug # pip marker to avoid a relaunch bug
_cmd = ['-c', 'install', '--single-version-externally-managed'] _cmd1 = ['-c', 'install', '--single-version-externally-managed']
if sys.argv[:3] == _cmd: _cmd2 = ['-c', 'install', '--record']
if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2:
sys.argv[0] = 'setup.py' sys.argv[0] = 'setup.py'
args = [sys.executable] + sys.argv args = [sys.executable] + sys.argv
sys.exit(subprocess.call(args)) sys.exit(subprocess.call(args))
...@@ -529,7 +550,7 @@ def main(version=DEFAULT_VERSION): ...@@ -529,7 +550,7 @@ def main(version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall""" """Install or upgrade setuptools and EasyInstall"""
options = _parse_args() options = _parse_args()
tarball = download_setuptools(download_base=options.download_base) tarball = download_setuptools(download_base=options.download_base)
_install(tarball, _build_install_args(options)) return _install(tarball, _build_install_args(options))
if __name__ == '__main__': if __name__ == '__main__':
main() sys.exit(main())
...@@ -48,9 +48,9 @@ copyright = u'2009-2011, The fellowship of the packaging' ...@@ -48,9 +48,9 @@ copyright = u'2009-2011, The fellowship of the packaging'
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.6.29' version = '0.6.46'
# The full version, including alpha/beta/rc tags. # 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 # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
......
...@@ -187,10 +187,11 @@ than ``2.4.1`` (which has a higher release number). ...@@ -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 A pre-release tag is a series of letters that are alphabetically before
"final". Some examples of prerelease tags would include ``alpha``, ``beta``, "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 ``a``, ``c``, ``dev``, and so on. You do not have to place a dot or dash
the prerelease tag if it's immediately after a number, but it's okay to do before the prerelease tag if it's immediately after a number, but it's okay to
so if you prefer. Thus, ``2.4c1`` and ``2.4.c1`` both represent release do so if you prefer. Thus, ``2.4c1`` and ``2.4.c1`` and ``2.4-c1`` all
candidate 1 of version ``2.4``, and are treated as identical by setuptools. 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 In addition, there are three special prerelease tags that are treated as if
they were the letter ``c``: ``pre``, ``preview``, and ``rc``. So, version 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``. ...@@ -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, 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: 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 * 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``, 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 *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: ...@@ -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') >>> parse_version('1.9.a.dev') == parse_version('1.9a0dev')
True True
>>> parse_version('2.1-rc2') < parse_version('2.1') >>> parse_version('2.1-rc2') < parse_version('2.1')
False True
>>> parse_version('0.6a9dev-r41475') < parse_version('0.6a9') >>> parse_version('0.6a9dev-r41475') < parse_version('0.6a9')
True True
...@@ -616,14 +610,20 @@ Dependencies that aren't in PyPI ...@@ -616,14 +610,20 @@ Dependencies that aren't in PyPI
If your project depends on packages that aren't registered in PyPI, you may 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 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`` as:
file. You just need to add some URLs to the ``dependency_links`` argument to
- 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()``. ``setup()``.
The URLs must be either: The URLs must be either:
1. direct download URLs, or 1. direct download URLs,
2. the URLs of web pages that contain direct download links 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 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. 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 ...@@ -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 and automatically create a trivial ``setup.py`` to wrap the single ``.py`` file
as an egg. 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 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 example, the below will cause EasyInstall to search the specified page for
eggs or source distributions, if the package's dependencies aren't already eggs or source distributions, if the package's dependencies aren't already
...@@ -2423,7 +2444,12 @@ command:: ...@@ -2423,7 +2444,12 @@ command::
python setup.py upload_docs --upload-dir=docs/build/html 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.: defaults in the ``setup.cfg`` of your Python project, e.g.:
.. code-block:: ini .. code-block:: ini
......
...@@ -14,6 +14,14 @@ ...@@ -14,6 +14,14 @@
gcc -DGUI=0 -mno-cygwin -O -s -o setuptools/cli.exe launcher.c 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 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 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 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 of 'spawn' doesn't work, because on Windows this leads to the Python
......
@echo off @echo off
REM VCVARSALL may be in Program Files or Program Files (x86) REM Use old Windows SDK 6.1 so created .exe will be compatible with
PATH=C:\Program Files\Microsoft Visual Studio 9.0\VC;%PATH% REM old Windows versions.
PATH=C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC;%PATH% 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 REM set up the environment to compile to x86
call VCVARSALL x86 call VCVARS32
cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /out:setuptools/cli-32.exe if "%ERRORLEVEL%"=="0" (
cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /out:setuptools/gui-32.exe 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 REM now for 64-bit
call VCVARSALL x86_amd64 REM Use the x86_amd64 profile, which is the 32-bit cross compiler for amd64
cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /out:setuptools/cli-64.exe call VCVARSx86_amd64
cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /out:setuptools/gui-64.exe if "%ERRORLEVEL%"=="0" (
\ No newline at end of file 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%
...@@ -13,7 +13,7 @@ The package resource API is designed to work with normal filesystem packages, ...@@ -13,7 +13,7 @@ The package resource API is designed to work with normal filesystem packages,
method. method.
""" """
import sys, os, zipimport, time, re, imp, types import sys, os, time, re, imp, types, zipfile, zipimport
try: try:
from urlparse import urlparse, urlunparse from urlparse import urlparse, urlunparse
except ImportError: except ImportError:
...@@ -60,6 +60,12 @@ except ImportError: ...@@ -60,6 +60,12 @@ except ImportError:
from os import open as os_open from os import open as os_open
from os.path import isdir, split 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 # This marker is used to simplify the process that checks is the
# setuptools package was installed by the Setuptools project # setuptools package was installed by the Setuptools project
# or by the Distribute project, in case Setuptools creates # or by the Distribute project, in case Setuptools creates
...@@ -1354,13 +1360,8 @@ class DefaultProvider(EggProvider): ...@@ -1354,13 +1360,8 @@ class DefaultProvider(EggProvider):
register_loader_type(type(None), DefaultProvider) register_loader_type(type(None), DefaultProvider)
try: if importlib_bootstrap is not None:
# CPython >=3.3 register_loader_type(importlib_bootstrap.SourceFileLoader, DefaultProvider)
import _frozen_importlib
except ImportError:
pass
else:
register_loader_type(_frozen_importlib.SourceFileLoader, DefaultProvider)
class EmptyProvider(NullProvider): class EmptyProvider(NullProvider):
...@@ -1377,6 +1378,37 @@ class EmptyProvider(NullProvider): ...@@ -1377,6 +1378,37 @@ class EmptyProvider(NullProvider):
empty_provider = EmptyProvider() 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): class ZipProvider(EggProvider):
...@@ -1386,7 +1418,7 @@ class ZipProvider(EggProvider): ...@@ -1386,7 +1418,7 @@ class ZipProvider(EggProvider):
def __init__(self, module): def __init__(self, module):
EggProvider.__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 self.zip_pre = self.loader.archive+os.sep
def _zipinfo_name(self, fspath): def _zipinfo_name(self, fspath):
...@@ -1420,6 +1452,14 @@ class ZipProvider(EggProvider): ...@@ -1420,6 +1452,14 @@ class ZipProvider(EggProvider):
self._extract_resource(manager, self._eager_to_zip(name)) self._extract_resource(manager, self._eager_to_zip(name))
return self._extract_resource(manager, zip_path) 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): def _extract_resource(self, manager, zip_path):
if zip_path in self._index(): if zip_path in self._index():
...@@ -1429,27 +1469,18 @@ class ZipProvider(EggProvider): ...@@ -1429,27 +1469,18 @@ class ZipProvider(EggProvider):
) )
return os.path.dirname(last) # return the extracted directory name return os.path.dirname(last) # return the extracted directory name
zip_stat = self.zipinfo[zip_path] timestamp, size = self._get_date_and_size(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)
try:
if not WRITE_SUPPORT: if not WRITE_SUPPORT:
raise IOError('"os.rename" and "os.unlink" are not supported ' raise IOError('"os.rename" and "os.unlink" are not supported '
'on this platform') 'on this platform')
try:
real_path = manager.get_cache_path( real_path = manager.get_cache_path(
self.egg_name, self._parts(zip_path) self.egg_name, self._parts(zip_path)
) )
if os.path.isfile(real_path): if self._is_current(real_path, zip_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 return real_path
outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path)) outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path))
...@@ -1463,11 +1494,9 @@ class ZipProvider(EggProvider): ...@@ -1463,11 +1494,9 @@ class ZipProvider(EggProvider):
except os.error: except os.error:
if os.path.isfile(real_path): if os.path.isfile(real_path):
stat = os.stat(real_path) if self._is_current(real_path, zip_path):
# the file became current since it was checked above,
if stat.st_size==size and stat.st_mtime==timestamp: # so proceed.
# size and stamp match, somebody did it just ahead of
# us, so we're done
return real_path return real_path
elif os.name=='nt': # Windows, del old file and retry elif os.name=='nt': # Windows, del old file and retry
unlink(real_path) unlink(real_path)
...@@ -1480,6 +1509,23 @@ class ZipProvider(EggProvider): ...@@ -1480,6 +1509,23 @@ class ZipProvider(EggProvider):
return real_path 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): def _get_eager_resources(self):
if self.eagers is None: if self.eagers is None:
eagers = [] eagers = []
...@@ -1622,7 +1668,7 @@ class EggMetadata(ZipProvider): ...@@ -1622,7 +1668,7 @@ class EggMetadata(ZipProvider):
def __init__(self, importer): def __init__(self, importer):
"""Create a metadata provider from a zipimporter""" """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.zip_pre = importer.archive+os.sep
self.loader = importer self.loader = importer
if importer.prefix: if importer.prefix:
...@@ -1789,20 +1835,20 @@ def find_on_path(importer, path_item, only=False): ...@@ -1789,20 +1835,20 @@ def find_on_path(importer, path_item, only=False):
for dist in find_distributions(os.path.join(path_item, entry)): for dist in find_distributions(os.path.join(path_item, entry)):
yield dist yield dist
elif not only and lower.endswith('.egg-link'): 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 if not line.strip(): continue
for item in find_distributions(os.path.join(path_item,line.rstrip())): for item in find_distributions(os.path.join(path_item,line.rstrip())):
yield item yield item
break break
register_finder(ImpWrapper,find_on_path) register_finder(ImpWrapper,find_on_path)
try: if importlib_bootstrap is not None:
# CPython >=3.3 register_finder(importlib_bootstrap.FileFinder, find_on_path)
import _frozen_importlib
except ImportError:
pass
else:
register_finder(_frozen_importlib.FileFinder, find_on_path)
_declare_state('dict', _namespace_handlers={}) _declare_state('dict', _namespace_handlers={})
_declare_state('dict', _namespace_packages={}) _declare_state('dict', _namespace_packages={})
...@@ -1903,13 +1949,8 @@ def file_ns_handler(importer, path_item, packageName, module): ...@@ -1903,13 +1949,8 @@ def file_ns_handler(importer, path_item, packageName, module):
register_namespace_handler(ImpWrapper,file_ns_handler) register_namespace_handler(ImpWrapper,file_ns_handler)
register_namespace_handler(zipimport.zipimporter,file_ns_handler) register_namespace_handler(zipimport.zipimporter,file_ns_handler)
try: if importlib_bootstrap is not None:
# CPython >=3.3 register_namespace_handler(importlib_bootstrap.FileFinder, file_ns_handler)
import _frozen_importlib
except ImportError:
pass
else:
register_namespace_handler(_frozen_importlib.FileFinder, file_ns_handler)
def null_ns_handler(importer, path_item, packageName, module): def null_ns_handler(importer, path_item, packageName, module):
...@@ -1969,7 +2010,7 @@ replace = {'pre':'c', 'preview':'c','-':'final-','rc':'c','dev':'@'}.get ...@@ -1969,7 +2010,7 @@ replace = {'pre':'c', 'preview':'c','-':'final-','rc':'c','dev':'@'}.get
def _parse_version_parts(s): def _parse_version_parts(s):
for part in component_re.split(s): for part in component_re.split(s):
part = replace(part,part) part = replace(part,part)
if part in ['', '.']: if not part or part=='.':
continue continue
if part[:1] in '0123456789': if part[:1] in '0123456789':
yield part.zfill(8) # pad for numeric comparison yield part.zfill(8) # pad for numeric comparison
...@@ -2012,6 +2053,8 @@ def parse_version(s): ...@@ -2012,6 +2053,8 @@ def parse_version(s):
parts = [] parts = []
for part in _parse_version_parts(s.lower()): for part in _parse_version_parts(s.lower()):
if part.startswith('*'): 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 # remove trailing zeros from each series of numeric parts
while parts and parts[-1]=='00000000': while parts and parts[-1]=='00000000':
parts.pop() parts.pop()
...@@ -2856,3 +2899,4 @@ run_main = run_script # backward compatibility ...@@ -2856,3 +2899,4 @@ run_main = run_script # backward compatibility
add_activation_listener(lambda dist: dist.activate()) add_activation_listener(lambda dist: dist.activate())
working_set.entries=[]; list(map(working_set.add_entry,sys.path)) # match order working_set.entries=[]; list(map(working_set.add_entry,sys.path)) # match order
...@@ -14,13 +14,17 @@ import sys ...@@ -14,13 +14,17 @@ import sys
import urllib2 import urllib2
import getpass import getpass
import collections import collections
import itertools
import re
try: try:
import keyring import keyring
except Exception: except Exception:
pass 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(): def get_next_version():
digits = map(int, VERSION.split('.')) digits = map(int, VERSION.split('.'))
...@@ -50,7 +54,7 @@ def get_mercurial_creds(system='https://bitbucket.org', username=None): ...@@ -50,7 +54,7 @@ def get_mercurial_creds(system='https://bitbucket.org', username=None):
# todo: consider getting this from .hgrc # todo: consider getting this from .hgrc
username = username or getpass.getuser() username = username or getpass.getuser()
keyring_username = '@@'.join((username, system)) keyring_username = '@@'.join((username, system))
system = '@'.join((keyring_username, 'Mercurial')) system = 'Mercurial'
password = ( password = (
keyring.get_password(system, keyring_username) keyring.get_password(system, keyring_username)
if 'keyring' in globals() if 'keyring' in globals()
...@@ -99,7 +103,7 @@ def do_release(): ...@@ -99,7 +103,7 @@ def do_release():
print("Please do that") print("Please do that")
raise SystemExit(1) 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 ' res = raw_input('Have you or has someone verified that the tests '
'pass on this revision? ') 'pass on this revision? ')
if not res.lower().startswith('y'): if not res.lower().startswith('y'):
...@@ -110,13 +114,20 @@ def do_release(): ...@@ -110,13 +114,20 @@ def do_release():
subprocess.check_call(['hg', 'update', VERSION]) subprocess.check_call(['hg', 'update', VERSION])
linkify('CHANGES.txt', 'CHANGES (links).txt')
has_docs = build_docs() has_docs = build_docs()
if os.path.isdir('./dist'): if os.path.isdir('./dist'):
shutil.rmtree('./dist') shutil.rmtree('./dist')
cmd = [sys.executable, 'setup.py', '-q', 'egg_info', '-RD', '-b', '', cmd = [
'sdist', 'register', 'upload'] sys.executable, 'setup.py', '-q',
'egg_info', '-RD', '-b', '',
'sdist',
'register', '-r', PACKAGE_INDEX,
'upload', '-r', PACKAGE_INDEX,
]
if has_docs: if has_docs:
cmd.append('upload_docs') cmd.extend(['upload_docs', '-r', PACKAGE_INDEX])
subprocess.check_call(cmd) subprocess.check_call(cmd)
upload_bootstrap_script() upload_bootstrap_script()
...@@ -148,14 +159,14 @@ def build_docs(): ...@@ -148,14 +159,14 @@ def build_docs():
return return
if os.path.isdir('docs/build'): if os.path.isdir('docs/build'):
shutil.rmtree('docs/build') shutil.rmtree('docs/build')
subprocess.check_call([ cmd = [
'sphinx-build', 'sphinx-build',
'-b', 'html', '-b', 'html',
'-d', 'build/doctrees', '-d', 'build/doctrees',
'.', '.',
'build/html', 'build/html',
], ]
cwd='docs') subprocess.check_call(cmd, cwd='docs')
return True return True
def upload_bootstrap_script(): def upload_bootstrap_script():
...@@ -166,5 +177,59 @@ def upload_bootstrap_script(): ...@@ -166,5 +177,59 @@ def upload_bootstrap_script():
except: except:
print("Unable to upload bootstrap script. Ask Tarek to do it.") 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__': if __name__ == '__main__':
do_release() do_release()
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
import sys import sys
import os import os
import textwrap import textwrap
import re
# Allow to run setup.py from another directory. # Allow to run setup.py from another directory.
os.chdir(os.path.dirname(os.path.abspath(__file__))) os.chdir(os.path.dirname(os.path.abspath(__file__)))
...@@ -15,8 +16,10 @@ if sys.version_info >= (3,) and do_2to3: ...@@ -15,8 +16,10 @@ if sys.version_info >= (3,) and do_2to3:
from distutils import dir_util, file_util, util, log from distutils import dir_util, file_util, util, log
log.set_verbosity(1) log.set_verbosity(1)
fl = FileList() fl = FileList()
for line in open("MANIFEST.in"): manifest_file = open("MANIFEST.in")
for line in manifest_file:
fl.process_template_line(line) fl.process_template_line(line)
manifest_file.close()
dir_util.create_tree(tmp_src, fl.files) dir_util.create_tree(tmp_src, fl.files)
outfiles_2to3 = [] outfiles_2to3 = []
dist_script = os.path.join("build", "src", "distribute_setup.py") dist_script = os.path.join("build", "src", "distribute_setup.py")
...@@ -39,10 +42,12 @@ from distutils.util import convert_path ...@@ -39,10 +42,12 @@ from distutils.util import convert_path
d = {} d = {}
init_path = convert_path('setuptools/command/__init__.py') 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__'] SETUP_COMMANDS = d['__all__']
VERSION = "0.6.29" VERSION = "0.6.46"
from setuptools import setup, find_packages from setuptools import setup, find_packages
from setuptools.command.build_py import build_py as _build_py from setuptools.command.build_py import build_py as _build_py
...@@ -82,10 +87,8 @@ class test(_test): ...@@ -82,10 +87,8 @@ class test(_test):
entry_points = os.path.join('distribute.egg-info', 'entry_points.txt') entry_points = os.path.join('distribute.egg-info', 'entry_points.txt')
if not os.path.exists(entry_points): if not os.path.exists(entry_points):
try:
_test.run(self) _test.run(self)
finally: return # even though _test.run will raise SystemExit
return
f = open(entry_points) f = open(entry_points)
...@@ -132,6 +135,16 @@ if _being_installed(): ...@@ -132,6 +135,16 @@ if _being_installed():
from distribute_setup import _before_install from distribute_setup import _before_install
_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( dist = setup(
name="distribute", name="distribute",
...@@ -141,26 +154,24 @@ dist = setup( ...@@ -141,26 +154,24 @@ dist = setup(
author="The fellowship of the packaging", author="The fellowship of the packaging",
author_email="distutils-sig@python.org", author_email="distutils-sig@python.org",
license="PSF or ZPL", 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", keywords = "CPAN PyPI distutils eggs package management",
url = "http://packages.python.org/distribute", url = "http://packages.python.org/distribute",
test_suite = 'setuptools.tests', test_suite = 'setuptools.tests',
src_root = src_root, src_root = src_root,
packages = find_packages(), 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 zip_safe = (sys.version>="2.5"), # <2.5 needs unzipped for -m to work
cmdclass = {'test': test}, cmdclass = {'test': test},
entry_points = { entry_points = {
"distutils.commands" : [ "distutils.commands" : [
"%(cmd)s = setuptools.command.%(cmd)s:%(cmd)s" % locals() "%(cmd)s = setuptools.command.%(cmd)s:%(cmd)s" % locals()
for cmd in SETUP_COMMANDS for cmd in SETUP_COMMANDS
], ],
"distutils.setup_keywords": [ "distutils.setup_keywords": [
"eager_resources = setuptools.dist:assert_string_list", "eager_resources = setuptools.dist:assert_string_list",
"namespace_packages = setuptools.dist:check_nsp", "namespace_packages = setuptools.dist:check_nsp",
...@@ -181,7 +192,6 @@ dist = setup( ...@@ -181,7 +192,6 @@ dist = setup(
"use_2to3_fixers = setuptools.dist:assert_string_list", "use_2to3_fixers = setuptools.dist:assert_string_list",
"use_2to3_exclude_fixers = setuptools.dist:assert_string_list", "use_2to3_exclude_fixers = setuptools.dist:assert_string_list",
], ],
"egg_info.writers": [ "egg_info.writers": [
"PKG-INFO = setuptools.command.egg_info:write_pkg_info", "PKG-INFO = setuptools.command.egg_info:write_pkg_info",
"requires.txt = setuptools.command.egg_info:write_requirements", "requires.txt = setuptools.command.egg_info:write_requirements",
...@@ -192,7 +202,6 @@ dist = setup( ...@@ -192,7 +202,6 @@ dist = setup(
"depends.txt = setuptools.command.egg_info:warn_depends_obsolete", "depends.txt = setuptools.command.egg_info:warn_depends_obsolete",
"dependency_links.txt = setuptools.command.egg_info:overwrite_arg", "dependency_links.txt = setuptools.command.egg_info:overwrite_arg",
], ],
"console_scripts": console_scripts, "console_scripts": console_scripts,
"setuptools.file_finders": "setuptools.file_finders":
......
...@@ -158,6 +158,9 @@ def unpack_zipfile(filename, extract_dir, progress_filter=default_filter): ...@@ -158,6 +158,9 @@ def unpack_zipfile(filename, extract_dir, progress_filter=default_filter):
finally: finally:
f.close() f.close()
del data del data
unix_attributes = info.external_attr >> 16
if unix_attributes:
os.chmod(target, unix_attributes)
finally: finally:
z.close() z.close()
......
No preview for this file type
No preview for this file type
No preview for this file type
...@@ -132,7 +132,9 @@ class develop(easy_install): ...@@ -132,7 +132,9 @@ class develop(easy_install):
def uninstall_link(self): def uninstall_link(self):
if os.path.exists(self.egg_link): if os.path.exists(self.egg_link):
log.info("Removing %s (link to %s)", self.egg_link, self.egg_base) 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]): if contents not in ([self.egg_path], [self.egg_path, self.setup_path]):
log.warn("Link points to %s: uninstall aborted", contents) log.warn("Link points to %s: uninstall aborted", contents)
return return
......
...@@ -19,7 +19,9 @@ import zipfile ...@@ -19,7 +19,9 @@ import zipfile
import re import re
import stat import stat
import random import random
import platform
from glob import glob from glob import glob
import pkg_resources
from setuptools import Command, _dont_write_bytecode from setuptools import Command, _dont_write_bytecode
from setuptools.sandbox import run_setup from setuptools.sandbox import run_setup
from distutils import log, dir_util from distutils import log, dir_util
...@@ -493,7 +495,7 @@ Please make the appropriate changes for your system and try again. ...@@ -493,7 +495,7 @@ Please make the appropriate changes for your system and try again.
self.cant_write_to_target() self.cant_write_to_target()
else: else:
try: 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 f.close(); f=None
executable = sys.executable executable = sys.executable
if os.name=='nt': if os.name=='nt':
...@@ -524,6 +526,10 @@ Please make the appropriate changes for your system and try again. ...@@ -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""" """Write all the scripts for `dist`, unless scripts are excluded"""
if not self.exclude_scripts and dist.metadata_isdir('scripts'): if not self.exclude_scripts and dist.metadata_isdir('scripts'):
for script_name in dist.metadata_listdir('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( self.install_script(
dist, script_name, dist, script_name,
dist.get_metadata('scripts/'+script_name) dist.get_metadata('scripts/'+script_name)
...@@ -1276,7 +1282,7 @@ Please make the appropriate changes for your system and try again.""" % ( ...@@ -1276,7 +1282,7 @@ Please make the appropriate changes for your system and try again.""" % (
return # already did it, or don't need to return # already did it, or don't need to
sitepy = os.path.join(self.install_dir, "site.py") 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 = "" current = ""
if os.path.exists(sitepy): if os.path.exists(sitepy):
...@@ -1529,7 +1535,10 @@ def get_exe_prefixes(exe_filename): ...@@ -1529,7 +1535,10 @@ def get_exe_prefixes(exe_filename):
if name.endswith('-nspkg.pth'): if name.endswith('-nspkg.pth'):
continue continue
if parts[0].upper() in ('PURELIB','PLATLIB'): 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('\\','/') pth = pth.strip().replace('\\','/')
if not pth.startswith('import'): if not pth.startswith('import'):
prefixes.append((('%s/%s/' % (parts[0],pth)), '')) prefixes.append((('%s/%s/' % (parts[0],pth)), ''))
...@@ -1794,6 +1803,11 @@ def chmod(path, mode): ...@@ -1794,6 +1803,11 @@ def chmod(path, mode):
def fix_jython_executable(executable, options): def fix_jython_executable(executable, options):
if sys.platform.startswith('java') and is_sh(executable): 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 # Workaround Jython's sys.executable being a .sh (an invalid
# shebang line interpreter) # shebang line interpreter)
if options: if options:
...@@ -1828,31 +1842,61 @@ def get_script_args(dist, executable=sys_executable, wininst=False): ...@@ -1828,31 +1842,61 @@ def get_script_args(dist, executable=sys_executable, wininst=False):
if sys.platform=='win32' or wininst: if sys.platform=='win32' or wininst:
# On Windows/wininst, add a .py extension and an .exe launcher # On Windows/wininst, add a .py extension and an .exe launcher
if group=='gui_scripts': if group=='gui_scripts':
ext, launcher = '-script.pyw', 'gui.exe' launcher_type = 'gui'
ext = '-script.pyw'
old = ['.pyw'] old = ['.pyw']
new_header = re.sub('(?i)python.exe','pythonw.exe',header) new_header = re.sub('(?i)python.exe','pythonw.exe',header)
else: else:
ext, launcher = '-script.py', 'cli.exe' launcher_type = 'cli'
ext = '-script.py'
old = ['.py','.pyc','.pyo'] old = ['.py','.pyc','.pyo']
new_header = re.sub('(?i)pythonw.exe','python.exe',header) 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': if os.path.exists(new_header[2:-1]) or sys.platform!='win32':
hdr = new_header hdr = new_header
else: else:
hdr = header hdr = header
yield (name+ext, hdr+script_text, 't', [name+x for x in old]) yield (name+ext, hdr+script_text, 't', [name+x for x in old])
yield ( yield (
name+'.exe', resource_string('setuptools', launcher), name+'.exe', get_win_launcher(launcher_type),
'b' # write in binary mode '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: else:
# On other platforms, we assume the right thing to do is to # On other platforms, we assume the right thing to do is to
# just write the stub with no extension. # just write the stub with no extension.
yield (name, header+script_text) 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): def rmtree(path, ignore_errors=False, onerror=auto_chmod):
"""Recursively delete a directory tree. """Recursively delete a directory tree.
......
...@@ -10,7 +10,7 @@ from distutils import log ...@@ -10,7 +10,7 @@ from distutils import log
from setuptools.command.sdist import sdist from setuptools.command.sdist import sdist
from setuptools.compat import basestring from setuptools.compat import basestring
from distutils.util import convert_path 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, \ from pkg_resources import parse_requirements, safe_name, parse_version, \
safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename
from setuptools.command.sdist import walk_revctrl from setuptools.command.sdist import walk_revctrl
...@@ -275,13 +275,26 @@ class egg_info(Command): ...@@ -275,13 +275,26 @@ class egg_info(Command):
self.broken_egg_info = self.egg_info self.broken_egg_info = self.egg_info
self.egg_info = bei # make it work for now self.egg_info = bei # make it work for now
class FileList(FileList): class FileList(_FileList):
"""File list that accepts only existing, platform-independent paths""" """File list that accepts only existing, platform-independent paths"""
def append(self, item): def append(self, item):
if item.endswith('\r'): # Fix older sdists built on Windows if item.endswith('\r'): # Fix older sdists built on Windows
item = item[:-1] item = item[:-1]
path = convert_path(item) path = convert_path(item)
if sys.version_info >= (3,):
try:
if os.path.exists(path) or os.path.exists(path.encode('utf-8')):
self.files.append(path)
except UnicodeEncodeError:
# Accept UTF-8 filenames even if LANG=C
if os.path.exists(path.encode('utf-8')):
self.files.append(path)
else:
log.warn("'%s' not %s encodable -- skipping", path,
sys.getfilesystemencoding())
else:
if os.path.exists(path): if os.path.exists(path):
self.files.append(path) self.files.append(path)
...@@ -292,20 +305,6 @@ class FileList(FileList): ...@@ -292,20 +305,6 @@ class FileList(FileList):
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): class manifest_maker(sdist):
template = "MANIFEST.in" template = "MANIFEST.in"
...@@ -330,7 +329,6 @@ class manifest_maker(sdist): ...@@ -330,7 +329,6 @@ class manifest_maker(sdist):
self.prune_file_list() self.prune_file_list()
self.filelist.sort() self.filelist.sort()
self.filelist.remove_duplicates() self.filelist.remove_duplicates()
self.filelist.files = [compose(path) for path in self.filelist.files]
self.write_manifest() self.write_manifest()
def write_manifest (self): def write_manifest (self):
...@@ -338,6 +336,18 @@ class manifest_maker(sdist): ...@@ -338,6 +336,18 @@ class manifest_maker(sdist):
by 'add_defaults()' and 'read_template()') to the manifest file by 'add_defaults()' and 'read_template()') to the manifest file
named by 'self.manifest'. 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 files = self.filelist.files
if os.sep!='/': if os.sep!='/':
files = [f.replace(os.sep,'/') for f in files] files = [f.replace(os.sep,'/') for f in files]
......
<?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>
...@@ -282,8 +282,13 @@ class sdist(_sdist): ...@@ -282,8 +282,13 @@ class sdist(_sdist):
log.info("reading manifest file '%s'", self.manifest) log.info("reading manifest file '%s'", self.manifest)
manifest = open(self.manifest, 'rbU') manifest = open(self.manifest, 'rbU')
for line in manifest: for line in manifest:
# The manifest must contain UTF-8. See #303.
if sys.version_info >= (3,): if sys.version_info >= (3,):
try:
line = line.decode('UTF-8') line = line.decode('UTF-8')
except UnicodeDecodeError:
log.warn("%r not UTF-8 decodable -- skipping" % line)
continue
# ignore comments and blank lines # ignore comments and blank lines
line = line.strip() line = line.strip()
if line.startswith('#') or not line: if line.startswith('#') or not line:
......
...@@ -108,8 +108,9 @@ class upload(Command): ...@@ -108,8 +108,9 @@ class upload(Command):
data['comment'] = comment data['comment'] = comment
if self.sign: if self.sign:
data['gpg_signature'] = (os.path.basename(filename) + ".asc", asc_file = open(filename + ".asc")
open(filename+".asc").read()) data['gpg_signature'] = (os.path.basename(filename) + ".asc", asc_file.read())
asc_file.close()
# set up the authentication # set up the authentication
auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip() auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip()
......
...@@ -8,9 +8,12 @@ PyPI's packages.python.org). ...@@ -8,9 +8,12 @@ PyPI's packages.python.org).
import os import os
import socket import socket
import zipfile import zipfile
import base64
import tempfile import tempfile
import sys import sys
import shutil
from base64 import standard_b64encode
from pkg_resources import iter_entry_points
from distutils import log from distutils import log
from distutils.errors import DistutilsOptionError from distutils.errors import DistutilsOptionError
...@@ -24,18 +27,12 @@ from setuptools.compat import httplib, urlparse ...@@ -24,18 +27,12 @@ from setuptools.compat import httplib, urlparse
_IS_PYTHON3 = sys.version > '3' _IS_PYTHON3 = sys.version > '3'
try: # This is not just a replacement for byte literals
bytes # but works as a general purpose encoder
except NameError: def b(s, encoding='utf-8'):
bytes = str if isinstance(s, unicode):
return s.encode(encoding)
def b(str_or_bytes): return s
"""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
class upload_docs(upload): class upload_docs(upload):
...@@ -51,43 +48,67 @@ class upload_docs(upload): ...@@ -51,43 +48,67 @@ class upload_docs(upload):
] ]
boolean_options = upload.boolean_options 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): def initialize_options(self):
upload.initialize_options(self) upload.initialize_options(self)
self.upload_dir = None self.upload_dir = None
self.target_dir = None
def finalize_options(self): def finalize_options(self):
upload.finalize_options(self) upload.finalize_options(self)
if self.upload_dir is None: if self.upload_dir is None:
if self.has_sphinx():
build_sphinx = self.get_finalized_command('build_sphinx')
self.target_dir = build_sphinx.builder_target_dir
else:
build = self.get_finalized_command('build') build = self.get_finalized_command('build')
self.upload_dir = os.path.join(build.build_base, 'docs') self.target_dir = os.path.join(build.build_base, 'docs')
self.mkpath(self.upload_dir) else:
self.ensure_dirname('upload_dir') self.ensure_dirname('upload_dir')
self.announce('Using upload directory %s' % self.upload_dir) self.target_dir = self.upload_dir
self.announce('Using upload directory %s' % self.target_dir)
def create_zipfile(self): def create_zipfile(self, filename):
name = self.distribution.metadata.get_name() zip_file = zipfile.ZipFile(filename, "w")
tmp_dir = tempfile.mkdtemp() try:
tmp_file = os.path.join(tmp_dir, "%s.zip" % name) self.mkpath(self.target_dir) # just in case
zip_file = zipfile.ZipFile(tmp_file, "w") for root, dirs, files in os.walk(self.target_dir):
for root, dirs, files in os.walk(self.upload_dir): if root == self.target_dir and not files:
if root == self.upload_dir and not files:
raise DistutilsOptionError( raise DistutilsOptionError(
"no files found in upload directory '%s'" "no files found in upload directory '%s'"
% self.upload_dir) % self.target_dir)
for name in files: for name in files:
full = os.path.join(root, name) full = os.path.join(root, name)
relative = root[len(self.upload_dir):].lstrip(os.path.sep) relative = root[len(self.target_dir):].lstrip(os.path.sep)
dest = os.path.join(relative, name) dest = os.path.join(relative, name)
zip_file.write(full, dest) zip_file.write(full, dest)
finally:
zip_file.close() zip_file.close()
return tmp_file
def run(self): def run(self):
zip_file = self.create_zipfile() # 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) self.upload_file(zip_file)
finally:
shutil.rmtree(tmp_dir)
def upload_file(self, filename): def upload_file(self, filename):
content = open(filename, 'rb').read() f = open(filename, 'rb')
content = f.read()
f.close()
meta = self.distribution.metadata meta = self.distribution.metadata
data = { data = {
':action': 'doc_upload', ':action': 'doc_upload',
...@@ -95,36 +116,33 @@ class upload_docs(upload): ...@@ -95,36 +116,33 @@ class upload_docs(upload):
'content': (os.path.basename(filename), content), 'content': (os.path.basename(filename), content),
} }
# set up the authentication # set up the authentication
credentials = self.username + ':' + self.password credentials = b(self.username + ':' + self.password)
if _IS_PYTHON3: # base64 only works with bytes in Python 3. credentials = standard_b64encode(credentials)
encoded_creds = base64.encodebytes(credentials.encode('utf8')) if sys.version_info >= (3,):
auth = bytes("Basic ") credentials = credentials.decode('ascii')
else: auth = "Basic " + credentials
encoded_creds = base64.encodestring(credentials)
auth = "Basic "
auth += encoded_creds.strip()
# Build up the MIME payload for the POST data # Build up the MIME payload for the POST data
boundary = b('--------------GHSKFJDLGDS7543FJKLFHRE75642756743254') boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
sep_boundary = b('\n--') + boundary sep_boundary = b('\n--') + b(boundary)
end_boundary = sep_boundary + b('--') end_boundary = sep_boundary + b('--')
body = [] 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 # handle multiple entries for the same name
if type(values) != type([]): if type(values) != type([]):
values = [values] values = [values]
for value in values: for value in values:
if type(value) is tuple: if type(value) is tuple:
fn = b(';filename="%s"' % value[0]) title += '; filename="%s"' % value[0]
value = value[1] value = value[1]
else: else:
fn = b("") value = b(value)
body.append(sep_boundary) body.append(sep_boundary)
body.append(b('\nContent-Disposition: form-data; name="%s"'%key)) body.append(b(title))
body.append(fn)
body.append(b("\n\n")) body.append(b("\n\n"))
body.append(b(value)) body.append(value)
if value and value[-1] == b('\r'): if value and value[-1:] == b('\r'):
body.append(b('\n')) # write an extra newline (lurve Macs) body.append(b('\n')) # write an extra newline (lurve Macs)
body.append(end_boundary) body.append(end_boundary)
body.append(b("\n")) body.append(b("\n"))
......
...@@ -660,6 +660,11 @@ class Distribution(_Distribution): ...@@ -660,6 +660,11 @@ class Distribution(_Distribution):
if not isinstance(sys.stdout, io.TextIOWrapper): if not isinstance(sys.stdout, io.TextIOWrapper):
return _Distribution.handle_display_options(self, option_order) 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 # Print metadata in UTF-8 no matter the platform
encoding = sys.stdout.encoding encoding = sys.stdout.encoding
errors = sys.stdout.errors errors = sys.stdout.errors
......
No preview for this file type
No preview for this file type
No preview for this file type
"""PyPI and direct package downloading""" """PyPI and direct package downloading"""
import sys, os.path, re, shutil, random, socket import sys, os.path, re, shutil, random, socket
import itertools
import base64 import base64
from pkg_resources import * from pkg_resources import *
from distutils import log from distutils import log
...@@ -14,6 +15,8 @@ except ImportError: ...@@ -14,6 +15,8 @@ except ImportError:
from md5 import md5 from md5 import md5
from fnmatch import translate from fnmatch import translate
from setuptools.py24compat import wraps
EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$') EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$')
HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I) HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I)
# this is here to fix emacs' cruddy broken syntax highlighting # this is here to fix emacs' cruddy broken syntax highlighting
...@@ -137,9 +140,38 @@ def interpret_distro_name(location, basename, metadata, ...@@ -137,9 +140,38 @@ def interpret_distro_name(location, basename, metadata,
platform = platform 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) REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I)
# this line is here to fix emacs' cruddy broken syntax highlighting # this line is here to fix emacs' cruddy broken syntax highlighting
@unique_values
def find_external_links(url, page): def find_external_links(url, page):
"""Find rel="homepage" and rel="download" links in `page`, yielding URLs""" """Find rel="homepage" and rel="download" links in `page`, yielding URLs"""
...@@ -157,6 +189,7 @@ def find_external_links(url, page): ...@@ -157,6 +189,7 @@ def find_external_links(url, page):
if match: if match:
yield urljoin(url, htmldecode(match.group(1))) yield urljoin(url, htmldecode(match.group(1)))
user_agent = "Python-urllib/%s distribute/%s" % ( user_agent = "Python-urllib/%s distribute/%s" % (
sys.version[:3], require('distribute')[0].version sys.version[:3], require('distribute')[0].version
) )
...@@ -666,6 +699,10 @@ class PackageIndex(Environment): ...@@ -666,6 +699,10 @@ class PackageIndex(Environment):
# #
if scheme=='svn' or scheme.startswith('svn+'): if scheme=='svn' or scheme.startswith('svn+'):
return self._download_svn(url, filename) 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': elif scheme=='file':
return url2pathname(urlparse.urlparse(url)[2]) return url2pathname(urlparse.urlparse(url)[2])
else: else:
...@@ -706,6 +743,55 @@ class PackageIndex(Environment): ...@@ -706,6 +743,55 @@ class PackageIndex(Environment):
os.system("svn checkout -q %s %s" % (url, filename)) os.system("svn checkout -q %s %s" % (url, filename))
return 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): def debug(self, msg, *args):
log.debug(msg, *args) log.debug(msg, *args)
...@@ -789,7 +875,6 @@ def open_with_auth(url): ...@@ -789,7 +875,6 @@ def open_with_auth(url):
# Double scheme does not raise on Mac OS X as revealed by a # Double scheme does not raise on Mac OS X as revealed by a
# failing test. We would expect "nonnumeric port". Refs #20. # failing test. We would expect "nonnumeric port". Refs #20.
if sys.platform == 'darwin':
if netloc.endswith(':'): if netloc.endswith(':'):
raise httplib.InvalidURL("nonnumeric port: ''") raise httplib.InvalidURL("nonnumeric port: ''")
......
"""
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
import os, sys, tempfile, operator, pkg_resources 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: try:
_file = file _file = file
except NameError: except NameError:
......
...@@ -1968,7 +1968,9 @@ def testfile(filename, module_relative=True, name=None, package=None, ...@@ -1968,7 +1968,9 @@ def testfile(filename, module_relative=True, name=None, package=None,
runner = DocTestRunner(verbose=verbose, optionflags=optionflags) runner = DocTestRunner(verbose=verbose, optionflags=optionflags)
# Read the file, convert it to a test, and run it. # 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) test = parser.get_doctest(s, globs, name, filename, 0)
runner.run(test) runner.run(test)
...@@ -2353,7 +2355,9 @@ def DocFileTest(path, module_relative=True, package=None, ...@@ -2353,7 +2355,9 @@ def DocFileTest(path, module_relative=True, package=None,
# Find the file and read it. # Find the file and read it.
name = os.path.basename(path) 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. # Convert it to a test, and wrap it in a DocFileCase.
test = parser.get_doctest(doc, globs, name, path, 0) test = parser.get_doctest(doc, globs, name, path, 0)
......
...@@ -89,16 +89,16 @@ class TestDevelopTest(unittest.TestCase): ...@@ -89,16 +89,16 @@ class TestDevelopTest(unittest.TestCase):
self.assertEqual(content, ['easy-install.pth', 'foo.egg-link']) self.assertEqual(content, ['easy-install.pth', 'foo.egg-link'])
# Check that we are using the right code. # 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: try:
path = f.read().split()[0].strip() path = egg_link_file.read().split()[0].strip()
finally: finally:
f.close() egg_link_file.close()
f = open(os.path.join(path, 'foo', '__init__.py'), 'rt') init_file = open(os.path.join(path, 'foo', '__init__.py'), 'rt')
try: try:
init = f.read().strip() init = init_file.read().strip()
finally: finally:
f.close() init_file.close()
if sys.version < "3": if sys.version < "3":
self.assertEqual(init, 'print "foo"') self.assertEqual(init, 'print "foo"')
else: else:
......
...@@ -50,9 +50,9 @@ class TestDistInfo(unittest.TestCase): ...@@ -50,9 +50,9 @@ class TestDistInfo(unittest.TestCase):
versioned = os.path.join(self.tmpdir, versioned = os.path.join(self.tmpdir,
'VersionedDistribution-2.718.dist-info') 'VersionedDistribution-2.718.dist-info')
os.mkdir(versioned) os.mkdir(versioned)
f = open(os.path.join(versioned, 'METADATA'), 'w+') metadata_file = open(os.path.join(versioned, 'METADATA'), 'w+')
try: try:
f.write(DALS( metadata_file.write(DALS(
""" """
Metadata-Version: 1.2 Metadata-Version: 1.2
Name: VersionedDistribution Name: VersionedDistribution
...@@ -61,13 +61,13 @@ class TestDistInfo(unittest.TestCase): ...@@ -61,13 +61,13 @@ class TestDistInfo(unittest.TestCase):
Requires-Dist: quux (>=1.1); extra == 'baz' Requires-Dist: quux (>=1.1); extra == 'baz'
""")) """))
finally: finally:
f.close() metadata_file.close()
unversioned = os.path.join(self.tmpdir, unversioned = os.path.join(self.tmpdir,
'UnversionedDistribution.dist-info') 'UnversionedDistribution.dist-info')
os.mkdir(unversioned) os.mkdir(unversioned)
f = open(os.path.join(unversioned, 'METADATA'), 'w+') metadata_file = open(os.path.join(unversioned, 'METADATA'), 'w+')
try: try:
f.write(DALS( metadata_file.write(DALS(
""" """
Metadata-Version: 1.2 Metadata-Version: 1.2
Name: UnversionedDistribution Name: UnversionedDistribution
...@@ -77,7 +77,7 @@ class TestDistInfo(unittest.TestCase): ...@@ -77,7 +77,7 @@ class TestDistInfo(unittest.TestCase):
Requires-Dist: quux (>=1.1); extra == 'baz' Requires-Dist: quux (>=1.1); extra == 'baz'
""")) """))
finally: finally:
f.close() metadata_file.close()
def tearDown(self): def tearDown(self):
shutil.rmtree(self.tmpdir) shutil.rmtree(self.tmpdir)
...@@ -13,7 +13,7 @@ import tarfile ...@@ -13,7 +13,7 @@ import tarfile
import distutils.core import distutils.core
from setuptools.sandbox import run_setup, SandboxViolation 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.easy_install import PthDistributions
from setuptools.command import easy_install as easy_install_pkg from setuptools.command import easy_install as easy_install_pkg
from setuptools.dist import Distribution from setuptools.dist import Distribution
...@@ -51,7 +51,7 @@ if __name__ == '__main__': ...@@ -51,7 +51,7 @@ if __name__ == '__main__':
sys.exit( sys.exit(
load_entry_point('spec', 'console_scripts', 'name')() load_entry_point('spec', 'console_scripts', 'name')()
) )
""" % sys.executable """ % fix_jython_executable(sys.executable, "")
SETUP_PY = """\ SETUP_PY = """\
from setuptools import setup from setuptools import setup
......
...@@ -16,15 +16,15 @@ class TestMarkerlib(unittest.TestCase): ...@@ -16,15 +16,15 @@ class TestMarkerlib(unittest.TestCase):
os_name = os.name os_name = os.name
self.assert_(interpret("")) self.assertTrue(interpret(""))
self.assert_(interpret("os.name != 'buuuu'")) self.assertTrue(interpret("os.name != 'buuuu'"))
self.assert_(interpret("python_version > '1.0'")) self.assertTrue(interpret("python_version > '1.0'"))
self.assert_(interpret("python_version < '5.0'")) self.assertTrue(interpret("python_version < '5.0'"))
self.assert_(interpret("python_version <= '5.0'")) self.assertTrue(interpret("python_version <= '5.0'"))
self.assert_(interpret("python_version >= '1.0'")) self.assertTrue(interpret("python_version >= '1.0'"))
self.assert_(interpret("'%s' in os.name" % os_name)) self.assertTrue(interpret("'%s' in os.name" % os_name))
self.assert_(interpret("'buuuu' not in os.name")) self.assertTrue(interpret("'buuuu' not in os.name"))
self.assertFalse(interpret("os.name == 'buuuu'")) self.assertFalse(interpret("os.name == 'buuuu'"))
self.assertFalse(interpret("python_version < '1.0'")) self.assertFalse(interpret("python_version < '1.0'"))
...@@ -36,7 +36,7 @@ class TestMarkerlib(unittest.TestCase): ...@@ -36,7 +36,7 @@ class TestMarkerlib(unittest.TestCase):
environment = default_environment() environment = default_environment()
environment['extra'] = 'test' environment['extra'] = 'test'
self.assert_(interpret("extra == 'test'", environment)) self.assertTrue(interpret("extra == 'test'", environment))
self.assertFalse(interpret("extra == 'doc'", environment)) self.assertFalse(interpret("extra == 'doc'", environment))
def raises_nameError(): def raises_nameError():
......
...@@ -478,13 +478,14 @@ class ParseTests(TestCase): ...@@ -478,13 +478,14 @@ class ParseTests(TestCase):
p1, p2 = parse_version(s1),parse_version(s2) p1, p2 = parse_version(s1),parse_version(s2)
self.assertEqual(p1,p2, (s1,s2,p1,p2)) self.assertEqual(p1,p2, (s1,s2,p1,p2))
c('1.2-rc1', '1.2rc1')
c('0.4', '0.4.0') c('0.4', '0.4.0')
c('0.4.0.0', '0.4.0') c('0.4.0.0', '0.4.0')
c('0.4.0-0', '0.4-0') c('0.4.0-0', '0.4-0')
c('0pl1', '0.0pl1') c('0pl1', '0.0pl1')
c('0pre1', '0.0c1') c('0pre1', '0.0c1')
c('0.0.0preview1', '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') c('1.2a1', '1.2.a.1'); c('1.2...a', '1.2a')
def testVersionOrdering(self): def testVersionOrdering(self):
...@@ -493,14 +494,11 @@ class ParseTests(TestCase): ...@@ -493,14 +494,11 @@ class ParseTests(TestCase):
self.assertTrue(p1<p2, (s1,s2,p1,p2)) self.assertTrue(p1<p2, (s1,s2,p1,p2))
c('2.1','2.1.1') c('2.1','2.1.1')
c('2.1.0','2.10')
c('2a1','2b0') c('2a1','2b0')
c('2b1','2c0')
c('2a1','2.1') c('2a1','2.1')
c('2.3a1', '2.3') c('2.3a1', '2.3')
c('2.1-1', '2.1-2') c('2.1-1', '2.1-2')
c('2.1-1', '2.1.1') c('2.1-1', '2.1.1')
c('2.1', '2.1.1-1')
c('2.1', '2.1pl4') c('2.1', '2.1pl4')
c('2.1a0-20040501', '2.1') c('2.1a0-20040501', '2.1')
c('1.1', '02.1') c('1.1', '02.1')
...@@ -511,20 +509,8 @@ class ParseTests(TestCase): ...@@ -511,20 +509,8 @@ class ParseTests(TestCase):
c('0.4', '4.0') c('0.4', '4.0')
c('0.0.4', '0.4.0') c('0.0.4', '0.4.0')
c('0pl1', '0.4pl1') c('0pl1', '0.4pl1')
c('2.1.0-rc1','2.1.0')
c('2.1dev','2.1a0') 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 =""" torture ="""
0.80.1-3 0.80.1-2 0.80.1-1 0.79.9999+0.80.0pre4-1 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): ...@@ -562,6 +548,15 @@ class ScriptHeaderTests(TestCase):
if (sys.version_info >= (3,) and os.environ.get("LC_CTYPE") if (sys.version_info >= (3,) and os.environ.get("LC_CTYPE")
in (None, "C", "POSIX")): in (None, "C", "POSIX")):
return return
class java:
class lang:
class System:
@staticmethod
def getProperty(property):
return ""
sys.modules["java"] = java
platform = sys.platform platform = sys.platform
sys.platform = 'java1.5.0_13' sys.platform = 'java1.5.0_13'
stdout = sys.stdout stdout = sys.stdout
...@@ -585,6 +580,7 @@ class ScriptHeaderTests(TestCase): ...@@ -585,6 +580,7 @@ class ScriptHeaderTests(TestCase):
'#!%s -x\n' % self.non_ascii_exe) '#!%s -x\n' % self.non_ascii_exe)
self.assertTrue('Unable to adapt shebang line' in sys.stdout.getvalue()) self.assertTrue('Unable to adapt shebang line' in sys.stdout.getvalue())
finally: finally:
del sys.modules["java"]
sys.platform = platform sys.platform = platform
sys.stdout = stdout sys.stdout = stdout
......
...@@ -7,9 +7,11 @@ import shutil ...@@ -7,9 +7,11 @@ import shutil
import sys import sys
import tempfile import tempfile
import unittest import unittest
import unicodedata
from setuptools.compat import StringIO from setuptools.compat import StringIO, urllib
from setuptools.command.sdist import sdist from setuptools.command.sdist import sdist
from setuptools.command.egg_info import manifest_maker
from setuptools.dist import Distribution from setuptools.dist import Distribution
...@@ -28,7 +30,52 @@ setup(**%r) ...@@ -28,7 +30,52 @@ setup(**%r)
""" % SETUP_ATTRS """ % 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): class TestSdistTest(unittest.TestCase):
def setUp(self): def setUp(self):
self.temp_dir = tempfile.mkdtemp() self.temp_dir = tempfile.mkdtemp()
f = open(os.path.join(self.temp_dir, 'setup.py'), 'w') f = open(os.path.join(self.temp_dir, 'setup.py'), 'w')
...@@ -62,106 +109,286 @@ class TestSdistTest(unittest.TestCase): ...@@ -62,106 +109,286 @@ class TestSdistTest(unittest.TestCase):
cmd.ensure_finalized() cmd.ensure_finalized()
# squelch output # squelch output
old_stdout = sys.stdout quiet()
old_stderr = sys.stderr
sys.stdout = StringIO()
sys.stderr = StringIO()
try: try:
cmd.run() cmd.run()
finally: finally:
sys.stdout = old_stdout unquiet()
sys.stderr = old_stderr
manifest = cmd.filelist.files manifest = cmd.filelist.files
self.assertTrue(os.path.join('sdist_test', 'a.txt') in manifest) 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', 'b.txt') in manifest)
self.assertTrue(os.path.join('sdist_test', 'c.rst') not in manifest) self.assertTrue(os.path.join('sdist_test', 'c.rst') not in manifest)
def test_filelist_is_fully_composed(self): def test_manifest_is_written_with_utf8_encoding(self):
# Test for #303. Requires HFS Plus to fail. # 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')
# Add file with non-ASCII filename # UTF-8 filename
filename = os.path.join('sdist_test', 'smörbröd.py') filename = os.path.join('sdist_test', 'smörbröd.py')
open(filename, 'w').close()
# Add UTF-8 filename and write manifest
quiet()
try:
mm.run()
mm.filelist.files.append(filename)
mm.write_manifest()
finally:
unquiet()
manifest = open(mm.manifest, 'rbU')
contents = manifest.read()
manifest.close()
# The manifest should be UTF-8 encoded
try:
u_contents = contents.decode('UTF-8')
except UnicodeDecodeError, e:
self.fail(e)
# The manifest should contain the UTF-8 filename
if sys.version_info >= (3,):
self.assertTrue(posix(filename) in u_contents)
else:
self.assertTrue(posix(filename) in contents)
# Python 3 only
if sys.version_info >= (3,):
def test_write_manifest_allows_utf8_filenames(self):
# Test for #303.
dist = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py'
mm = manifest_maker(dist)
mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
os.mkdir('sdist_test.egg-info')
# UTF-8 filename
filename = os.path.join(b('sdist_test'), b('smörbröd.py'))
# Add filename and write manifest
quiet()
try:
mm.run()
u_filename = filename.decode('utf-8')
mm.filelist.files.append(u_filename)
# Re-write manifest
mm.write_manifest()
finally:
unquiet()
manifest = open(mm.manifest, 'rbU')
contents = manifest.read()
manifest.close()
# The manifest should be UTF-8 encoded
try:
contents.decode('UTF-8')
except UnicodeDecodeError, e:
self.fail(e)
# The manifest should contain the UTF-8 filename
self.assertTrue(posix(filename) in contents)
# The filelist should have been updated as well
self.assertTrue(u_filename in mm.filelist.files)
def test_write_manifest_skips_non_utf8_filenames(self):
# Test for #303.
dist = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py'
mm = manifest_maker(dist)
mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
os.mkdir('sdist_test.egg-info')
# Latin-1 filename
filename = os.path.join(b('sdist_test'), LATIN1_FILENAME)
# Add filename with surrogates and write manifest
quiet()
try:
mm.run()
u_filename = filename.decode('utf-8', 'surrogateescape')
mm.filelist.files.append(u_filename)
# Re-write manifest
mm.write_manifest()
finally:
unquiet()
manifest = open(mm.manifest, 'rbU')
contents = manifest.read()
manifest.close()
# The manifest should be UTF-8 encoded
try:
contents.decode('UTF-8')
except UnicodeDecodeError, e:
self.fail(e)
# The Latin-1 filename should have been skipped
self.assertFalse(posix(filename) in contents)
# The filelist should have been updated as well
self.assertFalse(u_filename in mm.filelist.files)
def test_manifest_is_read_with_utf8_encoding(self):
# Test for #303.
dist = Distribution(SETUP_ATTRS) dist = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py' dist.script_name = 'setup.py'
cmd = sdist(dist) cmd = sdist(dist)
cmd.ensure_finalized() cmd.ensure_finalized()
# squelch output # Create manifest
old_stdout = sys.stdout quiet()
old_stderr = sys.stderr
sys.stdout = StringIO()
sys.stderr = StringIO()
try: try:
cmd.run() cmd.run()
finally: finally:
sys.stdout = old_stdout unquiet()
sys.stderr = old_stderr
self.assertTrue(filename in cmd.filelist.files)
def test_manifest_is_written_in_utf8(self): # Add UTF-8 filename to manifest
# Test for #303. 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()
# Add file with non-ASCII filename # The file must exist to be included in the filelist
filename = os.path.join('sdist_test', 'smörbröd.py')
open(filename, 'w').close() open(filename, 'w').close()
# Re-read manifest
cmd.filelist.files = []
quiet()
try:
cmd.read_manifest()
finally:
unquiet()
# The filelist should contain the UTF-8 filename
if sys.version_info >= (3,):
filename = filename.decode('utf-8')
self.assertTrue(filename in cmd.filelist.files)
# Python 3 only
if sys.version_info >= (3,):
def test_read_manifest_skips_non_utf8_filenames(self):
# Test for #303.
dist = Distribution(SETUP_ATTRS) dist = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py' dist.script_name = 'setup.py'
cmd = sdist(dist) cmd = sdist(dist)
cmd.ensure_finalized() cmd.ensure_finalized()
# squelch output # Create manifest
old_stdout = sys.stdout quiet()
old_stderr = sys.stderr
sys.stdout = StringIO()
sys.stderr = StringIO()
try: try:
cmd.run() cmd.run()
finally: finally:
sys.stdout = old_stdout unquiet()
sys.stderr = old_stderr
manifest = open(os.path.join('sdist_test.egg-info', 'SOURCES.txt'), 'rbU') # Add Latin-1 filename to manifest
contents = manifest.read() 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() manifest.close()
self.assertTrue(len(contents))
# This must not fail: # The file must exist to be included in the filelist
contents.decode('UTF-8') open(filename, 'w').close()
def test_manifest_is_read_in_utf8(self): # 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. # Test for #303.
dist = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py'
cmd = sdist(dist)
cmd.ensure_finalized()
# Add file with non-ASCII filename # UTF-8 filename
filename = os.path.join('sdist_test', 'smörbröd.py') filename = os.path.join(b('sdist_test'), b('smörbröd.py'))
open(filename, 'w').close() open(filename, 'w').close()
quiet()
try:
cmd.run()
finally:
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 = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py' dist.script_name = 'setup.py'
cmd = sdist(dist) cmd = sdist(dist)
cmd.ensure_finalized() cmd.ensure_finalized()
# squelch output # Latin-1 filename
old_stdout = sys.stdout filename = os.path.join(b('sdist_test'), LATIN1_FILENAME)
old_stderr = sys.stderr open(filename, 'w').close()
sys.stdout = StringIO() self.assertTrue(os.path.isfile(filename))
sys.stderr = StringIO()
quiet()
try: try:
cmd.run() cmd.run()
finally: finally:
sys.stdout = old_stdout unquiet()
sys.stderr = old_stderr
if sys.version_info >= (3,):
cmd.filelist.files = [] #not all windows systems have a default FS encoding of cp1252
cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') if sys.platform == 'win32':
cmd.read_manifest() # 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) 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(): def test_suite():
......
...@@ -54,12 +54,19 @@ class TestUploadDocsTest(unittest.TestCase): ...@@ -54,12 +54,19 @@ class TestUploadDocsTest(unittest.TestCase):
cmd = upload_docs(dist) cmd = upload_docs(dist)
cmd.upload_dir = self.upload_dir 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)
...@@ -17,7 +17,7 @@ Let's create a simple script, foo-script.py: ...@@ -17,7 +17,7 @@ Let's create a simple script, foo-script.py:
>>> from setuptools.command.easy_install import nt_quote_arg >>> from setuptools.command.easy_install import nt_quote_arg
>>> sample_directory = tempfile.mkdtemp() >>> sample_directory = tempfile.mkdtemp()
>>> f = open(os.path.join(sample_directory, 'foo-script.py'), 'w') >>> f = open(os.path.join(sample_directory, 'foo-script.py'), 'w')
>>> bytes = f.write( >>> bytes_written = f.write(
... """#!%(python_exe)s ... """#!%(python_exe)s
... import sys ... import sys
... input = repr(sys.stdin.read()) ... input = repr(sys.stdin.read())
...@@ -37,8 +37,8 @@ We'll also copy cli.exe to the sample-directory with the name foo.exe: ...@@ -37,8 +37,8 @@ We'll also copy cli.exe to the sample-directory with the name foo.exe:
>>> import pkg_resources >>> import pkg_resources
>>> f = open(os.path.join(sample_directory, 'foo.exe'), 'wb') >>> f = open(os.path.join(sample_directory, 'foo.exe'), 'wb')
>>> bytes = f.write( >>> bytes_written = f.write(
... pkg_resources.resource_string('setuptools', 'cli.exe') ... pkg_resources.resource_string('setuptools', 'cli-32.exe')
... ) ... )
>>> f.close() >>> f.close()
...@@ -82,7 +82,7 @@ options as usual. For example, to run in optimized mode and ...@@ -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: enter the interpreter after running the script, you could use -Oi:
>>> f = open(os.path.join(sample_directory, 'foo-script.py'), 'w') >>> f = open(os.path.join(sample_directory, 'foo-script.py'), 'w')
>>> bytes = f.write( >>> bytes_written = f.write(
... """#!%(python_exe)s -Oi ... """#!%(python_exe)s -Oi
... import sys ... import sys
... input = repr(sys.stdin.read()) ... input = repr(sys.stdin.read())
...@@ -112,10 +112,12 @@ Now let's test the GUI version with the simple scipt, bar-script.py: ...@@ -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 >>> from setuptools.command.easy_install import nt_quote_arg
>>> sample_directory = tempfile.mkdtemp() >>> sample_directory = tempfile.mkdtemp()
>>> f = open(os.path.join(sample_directory, 'bar-script.pyw'), 'w') >>> f = open(os.path.join(sample_directory, 'bar-script.pyw'), 'w')
>>> bytes = f.write( >>> bytes_written = f.write(
... """#!%(python_exe)s ... """#!%(python_exe)s
... import sys ... 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))) ... """ % dict(python_exe=nt_quote_arg(sys.executable)))
>>> f.close() >>> f.close()
...@@ -123,8 +125,8 @@ We'll also copy gui.exe to the sample-directory with the name bar.exe: ...@@ -123,8 +125,8 @@ We'll also copy gui.exe to the sample-directory with the name bar.exe:
>>> import pkg_resources >>> import pkg_resources
>>> f = open(os.path.join(sample_directory, 'bar.exe'), 'wb') >>> f = open(os.path.join(sample_directory, 'bar.exe'), 'wb')
>>> bytes = f.write( >>> bytes_written = f.write(
... pkg_resources.resource_string('setuptools', 'gui.exe') ... pkg_resources.resource_string('setuptools', 'gui-32.exe')
... ) ... )
>>> f.close() >>> f.close()
......
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()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment