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
include
\.Python
*.swp
CHANGES (links).txt
......@@ -37,3 +37,21 @@ de44acab3cfce1f5bc811d6c0fa1a88ca0e9533f 0.6.21
b69f072c000237435e17b8bbb304ba6f957283eb 0.6.26
469c3b948e41ef28752b3cdf3c7fb9618355ebf5 0.6.27
fc379e63586ad3c6838e1bda216548ba8270b8f0 0.6.28
4f82563d0f5d1af1fb215c0ac87f38b16bb5c42d 0.6.29
7464fc916fa4d8308e34e45a1198512fe04c97b4 0.6.30
17bc972d67edd96c7748061910172e1200a73efe 0.6.31
b1a7f86b315a1f8c20036d718d6dc641bb84cac6 0.6.32
6acac3919ae9a7dba2cbecbe3d4b31ece25d5f09 0.6.33
23c310bf4ae8e4616e37027f08891702f5a33bc9 0.6.34
2abe1117543be0edbafb10c7c159d1bcb1cb1b87 0.6.35
c813a29e831f266d427d4a4bce3da97f475a8eee 0.6.36
be6f65eea9c10ce78b6698d8c220b6e5de577292 0.6.37
2b26ec8909bff210f47c5f8fc620bc505e1610b5 0.6.37
f0d502a83f6c83ba38ad21c15a849c2daf389ec7 0.6.38
d737b2039c5f92af8000f78bbc80b6a5183caa97 0.6.39
0a783fa0dceb95b5fc743e47c2d89c1523d0afb7 0.6.40
ad107e9b4beea24516ac4e1e854696e586fe279d 0.6.41
f30167716b659f96c5e0b7ea3d5be2bcff8c0eac 0.6.42
35086ee286732b0f63d2be18d9f26f2734586e2d 0.6.43
73aa98aee6bbc4a9d19a334a8ac928dece7799c6 0.6.44
ddca71ae5ceb9b14512dc60ea83802c10e224cf0 0.6.45
......@@ -4,5 +4,6 @@ python:
- 2.6
- 2.7
- 3.2
- 3.3
# command to run tests
script: python setup.py test
......@@ -2,17 +2,179 @@
CHANGES
=======
------
0.6.45
------
* Issue #379: ``distribute_setup.py`` now traps VersionConflict as well,
restoring ability to upgrade from an older setuptools version.
------
0.6.44
------
* ``distribute_setup.py`` has been updated to allow Setuptools 0.7 to
satisfy use_setuptools.
------
0.6.43
------
* Issue #378: Restore support for Python 2.4 Syntax (regression in 0.6.42).
------
0.6.42
------
* External links finder no longer yields duplicate links.
* Issue #337: Moved site.py to setuptools/site-patch.py (graft of very old
patch from setuptools trunk which inspired PR #31).
------
0.6.41
------
* Issue #27: Use public api for loading resources from zip files rather than
the private method `_zip_directory_cache`.
* Added a new function ``easy_install.get_win_launcher`` which may be used by
third-party libraries such as buildout to get a suitable script launcher.
------
0.6.40
------
* Issue #376: brought back cli.exe and gui.exe that were deleted in the
previous release.
------
0.6.39
------
* Add support for console launchers on ARM platforms.
* Fix possible issue in GUI launchers where the subsystem was not supplied to
the linker.
* Launcher build script now refactored for robustness.
* Issue #375: Resources extracted from a zip egg to the file system now also
check the contents of the file against the zip contents during each
invocation of get_resource_filename.
------
0.6.38
------
* Issue #371: The launcher manifest file is now installed properly.
------
0.6.37
------
* Issue #143: Launcher scripts, including easy_install itself, are now
accompanied by a manifest on 32-bit Windows environments to avoid the
Installer Detection Technology and thus undesirable UAC elevation described
in `this Microsoft article
<http://technet.microsoft.com/en-us/library/cc709628%28WS.10%29.aspx>`_.
------
0.6.36
------
* Pull Request #35: In `Buildout issue 64
<https://github.com/buildout/buildout/issues/64>`_, it was reported that
under Python 3, installation of distutils scripts could attempt to copy
the ``__pycache__`` directory as a file, causing an error, apparently only
under Windows. Easy_install now skips all directories when processing
metadata scripts.
------
0.6.35
------
Note this release is backward-incompatible with distribute 0.6.23-0.6.34 in
how it parses version numbers.
* Issue #278: Restored compatibility with distribute 0.6.22 and setuptools
0.6. Updated the documentation to match more closely with the version
parsing as intended in setuptools 0.6.
------
0.6.34
------
* Issue #341: 0.6.33 fails to build under Python 2.4.
------
0.6.33
------
* Fix 2 errors with Jython 2.5.
* Fix 1 failure with Jython 2.5 and 2.7.
* Disable workaround for Jython scripts on Linux systems.
* Issue #336: `setup.py` no longer masks failure exit code when tests fail.
* Fix issue in pkg_resources where try/except around a platform-dependent
import would trigger hook load failures on Mercurial. See pull request 32
for details.
* Issue #341: Fix a ResourceWarning.
------
0.6.32
------
* Fix test suite with Python 2.6.
* Fix some DeprecationWarnings and ResourceWarnings.
* Issue #335: Backed out `setup_requires` superceding installed requirements
until regression can be addressed.
------
0.6.31
------
* Issue #303: Make sure the manifest only ever contains UTF-8 in Python 3.
* Issue #329: Properly close files created by tests for compatibility with
Jython.
* Work around Jython bugs `#1980 <http://bugs.jython.org/issue1980>`_ and
`#1981 <http://bugs.jython.org/issue1981>`_.
* Issue #334: Provide workaround for packages that reference `sys.__stdout__`
such as numpy does. This change should address
`virtualenv #359 <https://github.com/pypa/virtualenv/issues/359>`_ as long
as the system encoding is UTF-8 or the IO encoding is specified in the
environment, i.e.::
PYTHONIOENCODING=utf8 pip install numpy
* Fix for encoding issue when installing from Windows executable on Python 3.
* Issue #323: Allow `setup_requires` requirements to supercede installed
requirements. Added some new keyword arguments to existing pkg_resources
methods. Also had to updated how __path__ is handled for namespace packages
to ensure that when a new egg distribution containing a namespace package is
placed on sys.path, the entries in __path__ are found in the same order they
would have been in had that egg been on the path when pkg_resources was
first imported.
------
0.6.30
------
* Issue #328: Clean up temporary directories in distribute_setup.py.
* Fix fatal bug in distribute_setup.py.
------
0.6.29
------
* Pull Request #14: Honor file permissions in zip files.
* Issue #327: Merged pull request #24 to fix a dependency problem with pip.
* Merged pull request #23 to fix https://github.com/pypa/virtualenv/issues/301.
* If Sphinx is installed, the `upload_docs` command now runs `build_sphinx`
to produce uploadable documentation.
* Issue #326: `upload_docs` provided mangled auth credentials under Python 3.
* Issue #320: Fix check for "createable" in distribute_setup.py.
* Issue #305: Remove a warning that was triggered during normal operations.
* Issue #311: Print metadata in UTF-8 independent of platform.
* Issue #303: Read manifest file with UTF-8 encoding under Python 3.
* Issue #301: Allow to run tests of namespace packages when using 2to3.
* Issue #304: Prevent import loop in site.py under Python 3.3.
* Issue #283: Reenable scanning of *.pyc / *.pyo files on Python 3.3.
* Issue #283: Reenable scanning of `*.pyc` / `*.pyo` files on Python 3.3.
* Issue #299: The develop command didn't work on Python 3, when using 2to3,
as the egg link would go to the Python 2 source. Linking to the 2to3'd code
in build/lib makes it work, although you will have to rebuild the module
......@@ -35,7 +197,8 @@ CHANGES
* Issue #294: setup.py can now be invoked from any directory.
* Scripts are now installed honoring the umask.
* Added support for .dist-info directories.
* Issue #283: Fix and disable scanning of *.pyc / *.pyo files on Python 3.3.
* Issue #283: Fix and disable scanning of `*.pyc` / `*.pyo` files on
Python 3.3.
------
0.6.27
......@@ -292,11 +455,10 @@ CHANGES
-----
* Added the generation of `distribute_setup_3k.py` during the release.
This close http://bitbucket.org/tarek/distribute/issue/52.
This closes issue #52.
* Added an upload_docs command to easily upload project documentation to
PyPI's http://packages.python.org.
This close http://bitbucket.org/tarek/distribute/issue/56.
PyPI's http://packages.python.org. This close issue #56.
* Fixed a bootstrap bug on the use_setuptools() API.
......@@ -325,7 +487,7 @@ setuptools
This closes http://bugs.python.org/setuptools/issue39.
* Added option to run 2to3 automatically when installing on Python 3.
This closes http://bitbucket.org/tarek/distribute/issue/31.
This closes issue #31.
* Fixed invalid usage of requirement.parse, that broke develop -d.
This closes http://bugs.python.org/setuptools/issue44.
......@@ -339,11 +501,9 @@ setuptools
bootstrapping
=============
* Fixed bootstrap not working on Windows.
This closes http://bitbucket.org/tarek/distribute/issue/49.
* Fixed bootstrap not working on Windows. This closes issue #49.
* Fixed 2.6 dependencies.
This closes http://bitbucket.org/tarek/distribute/issue/50.
* Fixed 2.6 dependencies. This closes issue #50.
* Make sure setuptools is patched when running through easy_install
This closes http://bugs.python.org/setuptools/issue40.
......@@ -356,16 +516,14 @@ setuptools
==========
* package_index.urlopen now catches BadStatusLine and malformed url errors.
This closes http://bitbucket.org/tarek/distribute/issue/16 and
http://bitbucket.org/tarek/distribute/issue/18.
This closes issue #16 and issue #18.
* zip_ok is now False by default. This closes
http://bugs.python.org/setuptools/issue33.
* Fixed invalid URL error catching. http://bugs.python.org/setuptools/issue20.
* Fixed invalid bootstraping with easy_install installation
http://bitbucket.org/tarek/distribute/issue/40.
* Fixed invalid bootstraping with easy_install installation (issue #40).
Thanks to Florian Schulze for the help.
* Removed buildout/bootstrap.py. A new repository will create a specific
......@@ -377,7 +535,7 @@ bootstrapping
* The boostrap process leave setuptools alone if detected in the system
and --root or --prefix is provided, but is not in the same location.
This closes http://bitbucket.org/tarek/distribute/issue/10.
This closes issue #10.
---
0.6
......@@ -387,45 +545,38 @@ setuptools
==========
* Packages required at build time where not fully present at install time.
This closes http://bitbucket.org/tarek/distribute/issue/12.
This closes issue #12.
* Protected against failures in tarfile extraction. This closes
http://bitbucket.org/tarek/distribute/issue/10.
* Protected against failures in tarfile extraction. This closes issue #10.
* Made Jython api_tests.txt doctest compatible. This closes
http://bitbucket.org/tarek/distribute/issue/7.
* Made Jython api_tests.txt doctest compatible. This closes issue #7.
* sandbox.py replaced builtin type file with builtin function open. This
closes http://bitbucket.org/tarek/distribute/issue/6.
closes issue #6.
* Immediately close all file handles. This closes
http://bitbucket.org/tarek/distribute/issue/3.
* Immediately close all file handles. This closes issue #3.
* Added compatibility with Subversion 1.6. This references
http://bitbucket.org/tarek/distribute/issue/1.
* Added compatibility with Subversion 1.6. This references issue #1.
pkg_resources
=============
* Avoid a call to /usr/bin/sw_vers on OSX and use the official platform API
instead. Based on a patch from ronaldoussoren. This closes
http://bitbucket.org/tarek/distribute/issue/5.
instead. Based on a patch from ronaldoussoren. This closes issue #5.
* Fixed a SandboxViolation for mkdir that could occur in certain cases.
This closes http://bitbucket.org/tarek/distribute/issue/13.
This closes issue #13.
* Allow to find_on_path on systems with tight permissions to fail gracefully.
This closes http://bitbucket.org/tarek/distribute/issue/9.
This closes issue #9.
* Corrected inconsistency between documentation and code of add_entry.
This closes http://bitbucket.org/tarek/distribute/issue/8.
This closes issue #8.
* Immediately close all file handles. This closes
http://bitbucket.org/tarek/distribute/issue/3.
* Immediately close all file handles. This closes issue #3.
easy_install
============
* Immediately close all file handles. This closes
http://bitbucket.org/tarek/distribute/issue/3.
* Immediately close all file handles. This closes issue #3.
......@@ -8,16 +8,21 @@ Contributors
* Christophe Combelles
* Daniel Stutzbach
* Daniel Holth
* Dirley Rodrigues
* Grigory Petrov
* Hanno Schlichting
* Jannis Leidel
* Jason R. Coombs
* Jim Fulton
* Jonathan Lange
* Justin Azoff
* Lennart Regebro
* Marc Abramowitz
* Martin von Löwis
* Noufal Ibrahim
* Pete Hollobon
* Philip Jenvey
* Philip Thiem
* Reinout van Rees
* Robert Myers
* Stefan H. Holek
......
recursive-include setuptools *.py *.txt *.exe
recursive-include setuptools *.py *.txt *.exe *.xml
recursive-include tests *.py *.c *.pyx *.txt
recursive-include setuptools/tests *.html
recursive-include docs *.py *.txt *.conf *.css *.css_t Makefile indexsidebar.html
......
......@@ -11,35 +11,14 @@ Disclaimers
About the fork
==============
`Distribute` is a fork of the `Setuptools` project.
`Distribute` is a now deprecated fork of the `Setuptools` project.
Distribute is intended to replace Setuptools as the standard method
for working with Python module distributions.
Distribute was intended to replace Setuptools as the standard method
for working with Python module distributions. The code has since been merged
back into the parent project as is being maintained by the community at large.
The fork has two goals:
- Providing a backward compatible version to replace Setuptools
and make all distributions that depend on Setuptools work as
before, but with less bugs and behaviorial issues.
This work is done in the 0.6.x series.
Starting with version 0.6.2, Distribute supports Python 3.
Installing and using distribute for Python 3 code works exactly
the same as for Python 2 code, but Distribute also helps you to support
Python 2 and Python 3 from the same source code by letting you run 2to3
on the code as a part of the build process, by setting the keyword parameter
``use_2to3`` to True. See http://packages.python.org/distribute for more
information.
- Refactoring the code, and releasing it in several distributions.
This work is being done in the 0.7.x series but not yet released.
The roadmap is still evolving, and the page that is up-to-date is
located at : `http://packages.python.org/distribute/roadmap`.
If you install `Distribute` and want to switch back for any reason to
`Setuptools`, get to the `Uninstallation instructions`_ section.
`Distribute` is now being maintained as a branch in the `Setuptools
repository <https://bitbucket.org/pypa/setuptools>`_.
More documentation
==================
......@@ -99,9 +78,9 @@ Source installation
Download the source tarball, uncompress it, then run the install command::
$ curl -O http://pypi.python.org/packages/source/d/distribute/distribute-0.6.29.tar.gz
$ tar -xzvf distribute-0.6.29.tar.gz
$ cd distribute-0.6.29
$ curl -O http://pypi.python.org/packages/source/d/distribute/distribute-0.6.46.tar.gz
$ tar -xzvf distribute-0.6.46.tar.gz
$ cd distribute-0.6.46
$ python setup.py install
---------------------------
......@@ -224,5 +203,4 @@ Feedback and getting involved
- Mailing list: http://mail.python.org/mailman/listinfo/distutils-sig
- Issue tracker: http://bitbucket.org/tarek/distribute/issues/
- Code Repository: http://bitbucket.org/tarek/distribute
- Code Repository: http://bitbucket.org/pypa/setuptools?at=distribute
......@@ -17,9 +17,7 @@ where EXPR belongs to any of those:
__all__ = ['default_environment', 'compile', 'interpret']
from ast import Compare, BoolOp, Attribute, Name, Load, Str, cmpop, boolop
from ast import parse, copy_location, NodeTransformer
import ast
import os
import platform
import sys
......@@ -27,7 +25,16 @@ import weakref
_builtin_compile = compile
from platform import python_implementation
try:
from platform import python_implementation
except ImportError:
if os.name == "java":
# Jython 2.5 has ast module, but not platform.python_implementation() function.
def python_implementation():
return "Jython"
else:
raise
# restricted set of variables
_VARS = {'sys.platform': sys.platform,
......@@ -46,11 +53,15 @@ def default_environment():
"""Return copy of default PEP 385 globals dictionary."""
return dict(_VARS)
class ASTWhitelist(NodeTransformer):
class ASTWhitelist(ast.NodeTransformer):
def __init__(self, statement):
self.statement = statement # for error messages
ALLOWED = (Compare, BoolOp, Attribute, Name, Load, Str, cmpop, boolop)
ALLOWED = (ast.Compare, ast.BoolOp, ast.Attribute, ast.Name, ast.Load, ast.Str)
# Bool operations
ALLOWED += (ast.And, ast.Or)
# Comparison operations
ALLOWED += (ast.Eq, ast.Gt, ast.GtE, ast.In, ast.Is, ast.IsNot, ast.Lt, ast.LtE, ast.NotEq, ast.NotIn)
def visit(self, node):
"""Ensure statement only contains allowed nodes."""
......@@ -58,15 +69,15 @@ class ASTWhitelist(NodeTransformer):
raise SyntaxError('Not allowed in environment markers.\n%s\n%s' %
(self.statement,
(' ' * node.col_offset) + '^'))
return NodeTransformer.visit(self, node)
return ast.NodeTransformer.visit(self, node)
def visit_Attribute(self, node):
"""Flatten one level of attribute access."""
new_node = Name("%s.%s" % (node.value.id, node.attr), node.ctx)
return copy_location(new_node, node)
new_node = ast.Name("%s.%s" % (node.value.id, node.attr), node.ctx)
return ast.copy_location(new_node, node)
def parse_marker(marker):
tree = parse(marker, mode='eval')
tree = ast.parse(marker, mode='eval')
new_tree = ASTWhitelist(marker).generic_visit(tree)
return new_tree
......
......@@ -14,6 +14,7 @@ the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import os
import shutil
import sys
import time
import fnmatch
......@@ -48,7 +49,7 @@ except ImportError:
args = [quote(arg) for arg in args]
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
DEFAULT_VERSION = "0.6.29"
DEFAULT_VERSION = "0.6.46"
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
SETUPTOOLS_FAKED_VERSION = "0.6c11"
......@@ -86,8 +87,11 @@ def _install(tarball, install_args=()):
if not _python_cmd('setup.py', 'install', *install_args):
log.warn('Something went wrong during the installation.')
log.warn('See the error message above.')
# exitcode will be 2
return 2
finally:
os.chdir(old_wd)
shutil.rmtree(tmpdir)
def _build_egg(egg, tarball, to_dir):
......@@ -112,6 +116,7 @@ def _build_egg(egg, tarball, to_dir):
finally:
os.chdir(old_wd)
shutil.rmtree(tmpdir)
# returning the result
log.warn(egg)
if not os.path.exists(egg):
......@@ -139,6 +144,16 @@ def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
try:
try:
import pkg_resources
# Setuptools 0.7b and later is a suitable (and preferable)
# substitute for any Distribute version.
try:
pkg_resources.require("setuptools>=0.7b")
return
except (pkg_resources.DistributionNotFound,
pkg_resources.VersionConflict):
pass
if not hasattr(pkg_resources, '_distribute'):
if not no_fake:
_fake_setuptools()
......@@ -234,7 +249,9 @@ def _no_sandbox(function):
def _patch_file(path, content):
"""Will backup the file then patch it"""
existing_content = open(path).read()
f = open(path)
existing_content = f.read()
f.close()
if existing_content == content:
# already patched
log.warn('Already patched.')
......@@ -252,7 +269,10 @@ _patch_file = _no_sandbox(_patch_file)
def _same_content(path, content):
return open(path).read() == content
f = open(path)
existing_content = f.read()
f.close()
return existing_content == content
def _rename_path(path):
......@@ -443,8 +463,9 @@ def _relaunch():
log.warn('Relaunching...')
# we have to relaunch the process
# pip marker to avoid a relaunch bug
_cmd = ['-c', 'install', '--single-version-externally-managed']
if sys.argv[:3] == _cmd:
_cmd1 = ['-c', 'install', '--single-version-externally-managed']
_cmd2 = ['-c', 'install', '--record']
if sys.argv[:3] == _cmd1 or sys.argv[:3] == _cmd2:
sys.argv[0] = 'setup.py'
args = [sys.executable] + sys.argv
sys.exit(subprocess.call(args))
......@@ -529,7 +550,7 @@ def main(version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
options = _parse_args()
tarball = download_setuptools(download_base=options.download_base)
_install(tarball, _build_install_args(options))
return _install(tarball, _build_install_args(options))
if __name__ == '__main__':
main()
sys.exit(main())
......@@ -48,9 +48,9 @@ copyright = u'2009-2011, The fellowship of the packaging'
# built documents.
#
# The short X.Y version.
version = '0.6.29'
version = '0.6.46'
# The full version, including alpha/beta/rc tags.
release = '0.6.29'
release = '0.6.46'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
......
......@@ -187,10 +187,11 @@ than ``2.4.1`` (which has a higher release number).
A pre-release tag is a series of letters that are alphabetically before
"final". Some examples of prerelease tags would include ``alpha``, ``beta``,
``a``, ``c``, ``dev``, and so on. You do not have to place a dot before
the prerelease tag if it's immediately after a number, but it's okay to do
so if you prefer. Thus, ``2.4c1`` and ``2.4.c1`` both represent release
candidate 1 of version ``2.4``, and are treated as identical by setuptools.
``a``, ``c``, ``dev``, and so on. You do not have to place a dot or dash
before the prerelease tag if it's immediately after a number, but it's okay to
do so if you prefer. Thus, ``2.4c1`` and ``2.4.c1`` and ``2.4-c1`` all
represent release candidate 1 of version ``2.4``, and are treated as identical
by setuptools.
In addition, there are three special prerelease tags that are treated as if
they were the letter ``c``: ``pre``, ``preview``, and ``rc``. So, version
......@@ -216,13 +217,6 @@ a post-release tag, so this version is *newer* than ``0.6a9.dev``.
For the most part, setuptools' interpretation of version numbers is intuitive,
but here are a few tips that will keep you out of trouble in the corner cases:
* Don't use ``-`` or any other character than ``.`` as a separator, unless you
really want a post-release. Remember that ``2.1-rc2`` means you've
*already* released ``2.1``, whereas ``2.1rc2`` and ``2.1.c2`` are candidates
you're putting out *before* ``2.1``. If you accidentally distribute copies
of a post-release that you meant to be a pre-release, the only safe fix is to
bump your main release number (e.g. to ``2.1.1``) and re-release the project.
* Don't stick adjoining pre-release tags together without a dot or number
between them. Version ``1.9adev`` is the ``adev`` prerelease of ``1.9``,
*not* a development pre-release of ``1.9a``. Use ``.dev`` instead, as in
......@@ -239,7 +233,7 @@ but here are a few tips that will keep you out of trouble in the corner cases:
>>> parse_version('1.9.a.dev') == parse_version('1.9a0dev')
True
>>> parse_version('2.1-rc2') < parse_version('2.1')
False
True
>>> parse_version('0.6a9dev-r41475') < parse_version('0.6a9')
True
......@@ -616,14 +610,20 @@ Dependencies that aren't in PyPI
If your project depends on packages that aren't registered in PyPI, you may
still be able to depend on them, as long as they are available for download
as an egg, in the standard distutils ``sdist`` format, or as a single ``.py``
file. You just need to add some URLs to the ``dependency_links`` argument to
as:
- an egg, in the standard distutils ``sdist`` format,
- a single ``.py`` file, or
- a VCS repository (Subversion, Mercurial, or Git).
You just need to add some URLs to the ``dependency_links`` argument to
``setup()``.
The URLs must be either:
1. direct download URLs, or
2. the URLs of web pages that contain direct download links
1. direct download URLs,
2. the URLs of web pages that contain direct download links, or
3. the repository's URL
In general, it's better to link to web pages, because it is usually less
complex to update a web page than to release a new version of your project.
......@@ -637,6 +637,27 @@ by replacing them with underscores.) EasyInstall will recognize this suffix
and automatically create a trivial ``setup.py`` to wrap the single ``.py`` file
as an egg.
In the case of a VCS checkout, you should also append ``#egg=project-version``
in order to identify for what package that checkout should be used. You can
append ``@REV`` to the URL's path (before the fragment) to specify a revision.
Additionally, you can also force the VCS being used by prepending the URL with
a certain prefix. Currently available are:
- ``svn+URL`` for Subversion,
- ``git+URL`` for Git, and
- ``hg+URL`` for Mercurial
A more complete example would be:
``vcs+proto://host/path@revision#egg=project-version``
Be careful with the version. It should match the one inside the project files.
If you want do disregard the version, you have to omit it both in the
``requires`` and in the URL's fragment.
This will do a checkout (or a clone, in Git and Mercurial parlance) to a
temporary folder and run ``setup.py bdist_egg``.
The ``dependency_links`` option takes the form of a list of URL strings. For
example, the below will cause EasyInstall to search the specified page for
eggs or source distributions, if the package's dependencies aren't already
......@@ -2423,7 +2444,12 @@ command::
python setup.py upload_docs --upload-dir=docs/build/html
As with any other ``setuptools`` based command, you can define useful
If no ``--upload-dir`` is given, ``upload_docs`` will attempt to run the
``build_sphinx`` command to generate uploadable documentation.
For the command to become available, `Sphinx <http://sphinx.pocoo.org/>`_
must be installed in the same environment as distribute.
As with other ``setuptools``-based commands, you can define useful
defaults in the ``setup.cfg`` of your Python project, e.g.:
.. code-block:: ini
......
......@@ -14,6 +14,14 @@
gcc -DGUI=0 -mno-cygwin -O -s -o setuptools/cli.exe launcher.c
gcc -DGUI=1 -mwindows -mno-cygwin -O -s -o setuptools/gui.exe launcher.c
To build for Windows RT, install both Visual Studio Express for Windows 8
and for Windows Desktop (both freeware), create "win32" application using
"Windows Desktop" version, create new "ARM" target via
"Configuration Manager" menu and modify ".vcxproj" file by adding
"<WindowsSDKDesktopARMSupport>true</WindowsSDKDesktopARMSupport>" tag
as child of "PropertyGroup" tags that has "Debug|ARM" and "Release|ARM"
properties.
It links to msvcrt.dll, but this shouldn't be a problem since it doesn't
actually run Python in the same process. Note that using 'exec' instead
of 'spawn' doesn't work, because on Windows this leads to the Python
......
@echo off
REM VCVARSALL may be in Program Files or Program Files (x86)
PATH=C:\Program Files\Microsoft Visual Studio 9.0\VC;%PATH%
PATH=C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC;%PATH%
REM Use old Windows SDK 6.1 so created .exe will be compatible with
REM old Windows versions.
REM Windows SDK 6.1 may be downloaded at:
REM http://www.microsoft.com/en-us/download/details.aspx?id=11310
set PATH_OLD=%PATH%
REM The SDK creates a false install of Visual Studio at one of these locations
set PATH=C:\Program Files\Microsoft Visual Studio 9.0\VC\bin;%PATH%
set PATH=C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\bin;%PATH%
REM set up the environment to compile to x86
call VCVARSALL x86
cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /out:setuptools/cli-32.exe
cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /out:setuptools/gui-32.exe
call VCVARS32
if "%ERRORLEVEL%"=="0" (
cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /SUBSYSTEM:CONSOLE /out:setuptools/cli-32.exe
cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x86 /SUBSYSTEM:WINDOWS /out:setuptools/gui-32.exe
) else (
echo Windows SDK 6.1 not found to build Windows 32-bit version
)
REM buildout (and possibly other implementations) currently depend on
REM the 32-bit launcher scripts without the -32 in the filename, so copy them
REM there for now.
copy setuptools/cli-32.exe setuptools/cli.exe
copy setuptools/gui-32.exe setuptools/gui.exe
REM now for 64-bit
call VCVARSALL x86_amd64
cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /out:setuptools/cli-64.exe
cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /out:setuptools/gui-64.exe
\ No newline at end of file
REM Use the x86_amd64 profile, which is the 32-bit cross compiler for amd64
call VCVARSx86_amd64
if "%ERRORLEVEL%"=="0" (
cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /SUBSYSTEM:CONSOLE /out:setuptools/cli-64.exe
cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" launcher.c /O2 /link /MACHINE:x64 /SUBSYSTEM:WINDOWS /out:setuptools/gui-64.exe
) else (
echo Windows SDK 6.1 not found to build Windows 64-bit version
)
REM Windows RT ARM build requires both freeware
REM "Visual Studio Express 2012 for Windows 8" and
REM "Visual Studio Express 2012 for Windows Desktop" to be installed from
REM http://www.microsoft.com/visualstudio/eng/products/visual-studio-express-products
set PATH=%PATH_OLD%
set PATH=C:\Program Files\Microsoft Visual Studio 11.0\VC;%PATH%
set PATH=C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC;%PATH%
call VCVARSALL x86_arm >nul 2>&1
if "%ERRORLEVEL%"=="0" (
echo Building Windows RT Version ...
cl /D "GUI=0" /D "WIN32_LEAN_AND_MEAN" /D _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE launcher.c /O2 /link /MACHINE:ARM /SUBSYSTEM:CONSOLE /out:setuptools/cli-arm-32.exe
cl /D "GUI=1" /D "WIN32_LEAN_AND_MEAN" /D _ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE launcher.c /O2 /link /MACHINE:ARM /SUBSYSTEM:WINDOWS /out:setuptools/gui-arm-32.exe
) else (
echo Visual Studio ^(Express^) 2012 not found to build Windows RT Version
)
set PATH=%PATH_OLD%
......@@ -13,7 +13,7 @@ The package resource API is designed to work with normal filesystem packages,
method.
"""
import sys, os, zipimport, time, re, imp, types
import sys, os, time, re, imp, types, zipfile, zipimport
try:
from urlparse import urlparse, urlunparse
except ImportError:
......@@ -60,6 +60,12 @@ except ImportError:
from os import open as os_open
from os.path import isdir, split
# Avoid try/except due to potential problems with delayed import mechanisms.
if sys.version_info >= (3, 3) and sys.implementation.name == "cpython":
import importlib._bootstrap as importlib_bootstrap
else:
importlib_bootstrap = None
# This marker is used to simplify the process that checks is the
# setuptools package was installed by the Setuptools project
# or by the Distribute project, in case Setuptools creates
......@@ -1354,13 +1360,8 @@ class DefaultProvider(EggProvider):
register_loader_type(type(None), DefaultProvider)
try:
# CPython >=3.3
import _frozen_importlib
except ImportError:
pass
else:
register_loader_type(_frozen_importlib.SourceFileLoader, DefaultProvider)
if importlib_bootstrap is not None:
register_loader_type(importlib_bootstrap.SourceFileLoader, DefaultProvider)
class EmptyProvider(NullProvider):
......@@ -1377,6 +1378,37 @@ class EmptyProvider(NullProvider):
empty_provider = EmptyProvider()
def build_zipmanifest(path):
"""
This builds a similar dictionary to the zipimport directory
caches. However instead of tuples, ZipInfo objects are stored.
The translation of the tuple is as follows:
* [0] - zipinfo.filename on stock pythons this needs "/" --> os.sep
on pypy it is the same (one reason why distribute did work
in some cases on pypy and win32).
* [1] - zipinfo.compress_type
* [2] - zipinfo.compress_size
* [3] - zipinfo.file_size
* [4] - len(utf-8 encoding of filename) if zipinfo & 0x800
len(ascii encoding of filename) otherwise
* [5] - (zipinfo.date_time[0] - 1980) << 9 |
zipinfo.date_time[1] << 5 | zipinfo.date_time[2]
* [6] - (zipinfo.date_time[3] - 1980) << 11 |
zipinfo.date_time[4] << 5 | (zipinfo.date_time[5] // 2)
* [7] - zipinfo.CRC
"""
zipinfo = dict()
zfile = zipfile.ZipFile(path)
#Got ZipFile has not __exit__ on python 3.1
try:
for zitem in zfile.namelist():
zpath = zitem.replace('/', os.sep)
zipinfo[zpath] = zfile.getinfo(zitem)
assert zipinfo[zpath] is not None
finally:
zfile.close()
return zipinfo
class ZipProvider(EggProvider):
......@@ -1386,7 +1418,7 @@ class ZipProvider(EggProvider):
def __init__(self, module):
EggProvider.__init__(self,module)
self.zipinfo = zipimport._zip_directory_cache[self.loader.archive]
self.zipinfo = build_zipmanifest(self.loader.archive)
self.zip_pre = self.loader.archive+os.sep
def _zipinfo_name(self, fspath):
......@@ -1420,6 +1452,14 @@ class ZipProvider(EggProvider):
self._extract_resource(manager, self._eager_to_zip(name))
return self._extract_resource(manager, zip_path)
@staticmethod
def _get_date_and_size(zip_stat):
size = zip_stat.file_size
date_time = zip_stat.date_time + (0, 0, -1) # ymdhms+wday, yday, dst
#1980 offset already done
timestamp = time.mktime(date_time)
return timestamp, size
def _extract_resource(self, manager, zip_path):
if zip_path in self._index():
......@@ -1429,27 +1469,18 @@ class ZipProvider(EggProvider):
)
return os.path.dirname(last) # return the extracted directory name
zip_stat = self.zipinfo[zip_path]
t,d,size = zip_stat[5], zip_stat[6], zip_stat[3]
date_time = (
(d>>9)+1980, (d>>5)&0xF, d&0x1F, # ymd
(t&0xFFFF)>>11, (t>>5)&0x3F, (t&0x1F) * 2, 0, 0, -1 # hms, etc.
)
timestamp = time.mktime(date_time)
timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])
try:
if not WRITE_SUPPORT:
raise IOError('"os.rename" and "os.unlink" are not supported '
'on this platform')
try:
real_path = manager.get_cache_path(
self.egg_name, self._parts(zip_path)
)
if os.path.isfile(real_path):
stat = os.stat(real_path)
if stat.st_size==size and stat.st_mtime==timestamp:
# size and stamp match, don't bother extracting
if self._is_current(real_path, zip_path):
return real_path
outf, tmpnam = _mkstemp(".$extract", dir=os.path.dirname(real_path))
......@@ -1463,11 +1494,9 @@ class ZipProvider(EggProvider):
except os.error:
if os.path.isfile(real_path):
stat = os.stat(real_path)
if stat.st_size==size and stat.st_mtime==timestamp:
# size and stamp match, somebody did it just ahead of
# us, so we're done
if self._is_current(real_path, zip_path):
# the file became current since it was checked above,
# so proceed.
return real_path
elif os.name=='nt': # Windows, del old file and retry
unlink(real_path)
......@@ -1480,6 +1509,23 @@ class ZipProvider(EggProvider):
return real_path
def _is_current(self, file_path, zip_path):
"""
Return True if the file_path is current for this zip_path
"""
timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])
if not os.path.isfile(file_path):
return False
stat = os.stat(file_path)
if stat.st_size!=size or stat.st_mtime!=timestamp:
return False
# check that the contents match
zip_contents = self.loader.get_data(zip_path)
f = open(file_path, 'rb')
file_contents = f.read()
f.close()
return zip_contents == file_contents
def _get_eager_resources(self):
if self.eagers is None:
eagers = []
......@@ -1622,7 +1668,7 @@ class EggMetadata(ZipProvider):
def __init__(self, importer):
"""Create a metadata provider from a zipimporter"""
self.zipinfo = zipimport._zip_directory_cache[importer.archive]
self.zipinfo = build_zipmanifest(importer.archive)
self.zip_pre = importer.archive+os.sep
self.loader = importer
if importer.prefix:
......@@ -1789,20 +1835,20 @@ def find_on_path(importer, path_item, only=False):
for dist in find_distributions(os.path.join(path_item, entry)):
yield dist
elif not only and lower.endswith('.egg-link'):
for line in open(os.path.join(path_item, entry)):
entry_file = open(os.path.join(path_item, entry))
try:
entry_lines = entry_file.readlines()
finally:
entry_file.close()
for line in entry_lines:
if not line.strip(): continue
for item in find_distributions(os.path.join(path_item,line.rstrip())):
yield item
break
register_finder(ImpWrapper,find_on_path)
try:
# CPython >=3.3
import _frozen_importlib
except ImportError:
pass
else:
register_finder(_frozen_importlib.FileFinder, find_on_path)
if importlib_bootstrap is not None:
register_finder(importlib_bootstrap.FileFinder, find_on_path)
_declare_state('dict', _namespace_handlers={})
_declare_state('dict', _namespace_packages={})
......@@ -1903,13 +1949,8 @@ def file_ns_handler(importer, path_item, packageName, module):
register_namespace_handler(ImpWrapper,file_ns_handler)
register_namespace_handler(zipimport.zipimporter,file_ns_handler)
try:
# CPython >=3.3
import _frozen_importlib
except ImportError:
pass
else:
register_namespace_handler(_frozen_importlib.FileFinder, file_ns_handler)
if importlib_bootstrap is not None:
register_namespace_handler(importlib_bootstrap.FileFinder, file_ns_handler)
def null_ns_handler(importer, path_item, packageName, module):
......@@ -1969,7 +2010,7 @@ replace = {'pre':'c', 'preview':'c','-':'final-','rc':'c','dev':'@'}.get
def _parse_version_parts(s):
for part in component_re.split(s):
part = replace(part,part)
if part in ['', '.']:
if not part or part=='.':
continue
if part[:1] in '0123456789':
yield part.zfill(8) # pad for numeric comparison
......@@ -2012,6 +2053,8 @@ def parse_version(s):
parts = []
for part in _parse_version_parts(s.lower()):
if part.startswith('*'):
if part<'*final': # remove '-' before a prerelease tag
while parts and parts[-1]=='*final-': parts.pop()
# remove trailing zeros from each series of numeric parts
while parts and parts[-1]=='00000000':
parts.pop()
......@@ -2856,3 +2899,4 @@ run_main = run_script # backward compatibility
add_activation_listener(lambda dist: dist.activate())
working_set.entries=[]; list(map(working_set.add_entry,sys.path)) # match order
......@@ -14,13 +14,17 @@ import sys
import urllib2
import getpass
import collections
import itertools
import re
try:
import keyring
except Exception:
pass
VERSION = '0.6.29'
VERSION = '0.6.46'
PACKAGE_INDEX = 'https://pypi.python.org/pypi'
PACKAGE_INDEX = 'https://pypi.python.org/pypi'
def get_next_version():
digits = map(int, VERSION.split('.'))
......@@ -50,7 +54,7 @@ def get_mercurial_creds(system='https://bitbucket.org', username=None):
# todo: consider getting this from .hgrc
username = username or getpass.getuser()
keyring_username = '@@'.join((username, system))
system = '@'.join((keyring_username, 'Mercurial'))
system = 'Mercurial'
password = (
keyring.get_password(system, keyring_username)
if 'keyring' in globals()
......@@ -99,7 +103,7 @@ def do_release():
print("Please do that")
raise SystemExit(1)
print("Travis-CI tests: http://travis-ci.org/#!/jaraco/distribute")
print("Travis-CI tests: http://travis-ci.org/#!/jaraco/setuptools")
res = raw_input('Have you or has someone verified that the tests '
'pass on this revision? ')
if not res.lower().startswith('y'):
......@@ -110,13 +114,20 @@ def do_release():
subprocess.check_call(['hg', 'update', VERSION])
linkify('CHANGES.txt', 'CHANGES (links).txt')
has_docs = build_docs()
if os.path.isdir('./dist'):
shutil.rmtree('./dist')
cmd = [sys.executable, 'setup.py', '-q', 'egg_info', '-RD', '-b', '',
'sdist', 'register', 'upload']
cmd = [
sys.executable, 'setup.py', '-q',
'egg_info', '-RD', '-b', '',
'sdist',
'register', '-r', PACKAGE_INDEX,
'upload', '-r', PACKAGE_INDEX,
]
if has_docs:
cmd.append('upload_docs')
cmd.extend(['upload_docs', '-r', PACKAGE_INDEX])
subprocess.check_call(cmd)
upload_bootstrap_script()
......@@ -148,14 +159,14 @@ def build_docs():
return
if os.path.isdir('docs/build'):
shutil.rmtree('docs/build')
subprocess.check_call([
cmd = [
'sphinx-build',
'-b', 'html',
'-d', 'build/doctrees',
'.',
'build/html',
],
cwd='docs')
]
subprocess.check_call(cmd, cwd='docs')
return True
def upload_bootstrap_script():
......@@ -166,5 +177,59 @@ def upload_bootstrap_script():
except:
print("Unable to upload bootstrap script. Ask Tarek to do it.")
def linkify(source, dest):
with open(source) as source:
out = _linkified_text(source.read())
with open(dest, 'w') as dest:
dest.write(out)
def _linkified(rst_path):
"return contents of reStructureText file with linked issue references"
rst_file = open(rst_path)
rst_content = rst_file.read()
rst_file.close()
return _linkified_text(rst_content)
def _linkified_text(rst_content):
# first identify any existing HREFs so they're not changed
HREF_pattern = re.compile('`.*?`_', re.MULTILINE | re.DOTALL)
# split on the HREF pattern, returning the parts to be linkified
plain_text_parts = HREF_pattern.split(rst_content)
anchors = []
linkified_parts = [_linkified_part(part, anchors)
for part in plain_text_parts]
pairs = itertools.izip_longest(
linkified_parts,
HREF_pattern.findall(rst_content),
fillvalue='',
)
rst_content = ''.join(flatten(pairs))
anchors = sorted(anchors)
bitroot = 'http://bitbucket.org/tarek/distribute'
rst_content += "\n"
for x in anchors:
issue = re.findall(r'\d+', x)[0]
rst_content += '.. _`%s`: %s/issue/%s\n' % (x, bitroot, issue)
rst_content += "\n"
return rst_content
def flatten(listOfLists):
"Flatten one level of nesting"
return itertools.chain.from_iterable(listOfLists)
def _linkified_part(text, anchors):
"""
Linkify a part and collect any anchors generated
"""
revision = re.compile(r'\b(issue\s+#?\d+)\b', re.M | re.I)
anchors.extend(revision.findall(text)) # ['Issue #43', ...]
return revision.sub(r'`\1`_', text)
if __name__ == '__main__':
do_release()
......@@ -3,6 +3,7 @@
import sys
import os
import textwrap
import re
# Allow to run setup.py from another directory.
os.chdir(os.path.dirname(os.path.abspath(__file__)))
......@@ -15,8 +16,10 @@ if sys.version_info >= (3,) and do_2to3:
from distutils import dir_util, file_util, util, log
log.set_verbosity(1)
fl = FileList()
for line in open("MANIFEST.in"):
manifest_file = open("MANIFEST.in")
for line in manifest_file:
fl.process_template_line(line)
manifest_file.close()
dir_util.create_tree(tmp_src, fl.files)
outfiles_2to3 = []
dist_script = os.path.join("build", "src", "distribute_setup.py")
......@@ -39,10 +42,12 @@ from distutils.util import convert_path
d = {}
init_path = convert_path('setuptools/command/__init__.py')
exec(open(init_path).read(), d)
init_file = open(init_path)
exec(init_file.read(), d)
init_file.close()
SETUP_COMMANDS = d['__all__']
VERSION = "0.6.29"
VERSION = "0.6.46"
from setuptools import setup, find_packages
from setuptools.command.build_py import build_py as _build_py
......@@ -82,10 +87,8 @@ class test(_test):
entry_points = os.path.join('distribute.egg-info', 'entry_points.txt')
if not os.path.exists(entry_points):
try:
_test.run(self)
finally:
return
return # even though _test.run will raise SystemExit
f = open(entry_points)
......@@ -132,6 +135,16 @@ if _being_installed():
from distribute_setup import _before_install
_before_install()
readme_file = open('README.txt')
# the release script adds hyperlinks to issues
if os.path.exists('CHANGES (links).txt'):
changes_file = open('CHANGES (links).txt')
else:
# but if the release script has not run, fall back to the source file
changes_file = open('CHANGES.txt')
long_description = readme_file.read() + changes_file.read()
readme_file.close()
changes_file.close()
dist = setup(
name="distribute",
......@@ -141,26 +154,24 @@ dist = setup(
author="The fellowship of the packaging",
author_email="distutils-sig@python.org",
license="PSF or ZPL",
long_description = open('README.txt').read() + open('CHANGES.txt').read(),
long_description = long_description,
keywords = "CPAN PyPI distutils eggs package management",
url = "http://packages.python.org/distribute",
test_suite = 'setuptools.tests',
src_root = src_root,
packages = find_packages(),
package_data = {'setuptools':['*.exe']},
package_data = {'setuptools':['*.exe', 'site-patch.py'], 'setuptools.command':['*.xml']},
py_modules = ['pkg_resources', 'easy_install', 'site'],
py_modules = ['pkg_resources', 'easy_install'],
zip_safe = (sys.version>="2.5"), # <2.5 needs unzipped for -m to work
cmdclass = {'test': test},
entry_points = {
"distutils.commands" : [
"%(cmd)s = setuptools.command.%(cmd)s:%(cmd)s" % locals()
for cmd in SETUP_COMMANDS
],
"distutils.setup_keywords": [
"eager_resources = setuptools.dist:assert_string_list",
"namespace_packages = setuptools.dist:check_nsp",
......@@ -181,7 +192,6 @@ dist = setup(
"use_2to3_fixers = setuptools.dist:assert_string_list",
"use_2to3_exclude_fixers = setuptools.dist:assert_string_list",
],
"egg_info.writers": [
"PKG-INFO = setuptools.command.egg_info:write_pkg_info",
"requires.txt = setuptools.command.egg_info:write_requirements",
......@@ -192,7 +202,6 @@ dist = setup(
"depends.txt = setuptools.command.egg_info:warn_depends_obsolete",
"dependency_links.txt = setuptools.command.egg_info:overwrite_arg",
],
"console_scripts": console_scripts,
"setuptools.file_finders":
......
......@@ -158,6 +158,9 @@ def unpack_zipfile(filename, extract_dir, progress_filter=default_filter):
finally:
f.close()
del data
unix_attributes = info.external_attr >> 16
if unix_attributes:
os.chmod(target, unix_attributes)
finally:
z.close()
......
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):
def uninstall_link(self):
if os.path.exists(self.egg_link):
log.info("Removing %s (link to %s)", self.egg_link, self.egg_base)
contents = [line.rstrip() for line in open(self.egg_link)]
egg_link_file = open(self.egg_link)
contents = [line.rstrip() for line in egg_link_file]
egg_link_file.close()
if contents not in ([self.egg_path], [self.egg_path, self.setup_path]):
log.warn("Link points to %s: uninstall aborted", contents)
return
......
......@@ -19,7 +19,9 @@ import zipfile
import re
import stat
import random
import platform
from glob import glob
import pkg_resources
from setuptools import Command, _dont_write_bytecode
from setuptools.sandbox import run_setup
from distutils import log, dir_util
......@@ -493,7 +495,7 @@ Please make the appropriate changes for your system and try again.
self.cant_write_to_target()
else:
try:
f.write("import os;open(%r,'w').write('OK')\n" % (ok_file,))
f.write("import os; f = open(%r, 'w'); f.write('OK'); f.close()\n" % (ok_file,))
f.close(); f=None
executable = sys.executable
if os.name=='nt':
......@@ -524,6 +526,10 @@ Please make the appropriate changes for your system and try again.
"""Write all the scripts for `dist`, unless scripts are excluded"""
if not self.exclude_scripts and dist.metadata_isdir('scripts'):
for script_name in dist.metadata_listdir('scripts'):
if dist.metadata_isdir('scripts/' + script_name):
# The "script" is a directory, likely a Python 3
# __pycache__ directory, so skip it.
continue
self.install_script(
dist, script_name,
dist.get_metadata('scripts/'+script_name)
......@@ -1276,7 +1282,7 @@ Please make the appropriate changes for your system and try again.""" % (
return # already did it, or don't need to
sitepy = os.path.join(self.install_dir, "site.py")
source = resource_string(Requirement.parse("distribute"), "site.py")
source = resource_string("setuptools", "site-patch.py")
current = ""
if os.path.exists(sitepy):
......@@ -1529,7 +1535,10 @@ def get_exe_prefixes(exe_filename):
if name.endswith('-nspkg.pth'):
continue
if parts[0].upper() in ('PURELIB','PLATLIB'):
for pth in yield_lines(z.read(name)):
contents = z.read(name)
if sys.version_info >= (3,):
contents = contents.decode()
for pth in yield_lines(contents):
pth = pth.strip().replace('\\','/')
if not pth.startswith('import'):
prefixes.append((('%s/%s/' % (parts[0],pth)), ''))
......@@ -1794,6 +1803,11 @@ def chmod(path, mode):
def fix_jython_executable(executable, options):
if sys.platform.startswith('java') and is_sh(executable):
# Workaround for Jython is not needed on Linux systems.
import java
if java.lang.System.getProperty("os.name") == "Linux":
return executable
# Workaround Jython's sys.executable being a .sh (an invalid
# shebang line interpreter)
if options:
......@@ -1828,31 +1842,61 @@ def get_script_args(dist, executable=sys_executable, wininst=False):
if sys.platform=='win32' or wininst:
# On Windows/wininst, add a .py extension and an .exe launcher
if group=='gui_scripts':
ext, launcher = '-script.pyw', 'gui.exe'
launcher_type = 'gui'
ext = '-script.pyw'
old = ['.pyw']
new_header = re.sub('(?i)python.exe','pythonw.exe',header)
else:
ext, launcher = '-script.py', 'cli.exe'
launcher_type = 'cli'
ext = '-script.py'
old = ['.py','.pyc','.pyo']
new_header = re.sub('(?i)pythonw.exe','python.exe',header)
if is_64bit():
launcher = launcher.replace(".", "-64.")
else:
launcher = launcher.replace(".", "-32.")
if os.path.exists(new_header[2:-1]) or sys.platform!='win32':
hdr = new_header
else:
hdr = header
yield (name+ext, hdr+script_text, 't', [name+x for x in old])
yield (
name+'.exe', resource_string('setuptools', launcher),
name+'.exe', get_win_launcher(launcher_type),
'b' # write in binary mode
)
if not is_64bit():
# install a manifest for the launcher to prevent Windows
# from detecting it as an installer (which it will for
# launchers like easy_install.exe). Consider only
# adding a manifest for launchers detected as installers.
# See Distribute #143 for details.
m_name = name + '.exe.manifest'
yield (m_name, load_launcher_manifest(name), 't')
else:
# On other platforms, we assume the right thing to do is to
# just write the stub with no extension.
yield (name, header+script_text)
def get_win_launcher(type):
"""
Load the Windows launcher (executable) suitable for launching a script.
`type` should be either 'cli' or 'gui'
Returns the executable as a byte string.
"""
launcher_fn = '%s.exe' % type
if platform.machine().lower()=='arm':
launcher_fn = launcher_fn.replace(".", "-arm.")
if is_64bit():
launcher_fn = launcher_fn.replace(".", "-64.")
else:
launcher_fn = launcher_fn.replace(".", "-32.")
return resource_string('setuptools', launcher_fn)
def load_launcher_manifest(name):
manifest = pkg_resources.resource_string(__name__, 'launcher manifest.xml')
if sys.version_info[0] < 3:
return manifest % vars()
else:
return manifest.decode('utf-8') % vars()
def rmtree(path, ignore_errors=False, onerror=auto_chmod):
"""Recursively delete a directory tree.
......
......@@ -10,7 +10,7 @@ from distutils import log
from setuptools.command.sdist import sdist
from setuptools.compat import basestring
from distutils.util import convert_path
from distutils.filelist import FileList
from distutils.filelist import FileList as _FileList
from pkg_resources import parse_requirements, safe_name, parse_version, \
safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename
from setuptools.command.sdist import walk_revctrl
......@@ -275,13 +275,26 @@ class egg_info(Command):
self.broken_egg_info = self.egg_info
self.egg_info = bei # make it work for now
class FileList(FileList):
class FileList(_FileList):
"""File list that accepts only existing, platform-independent paths"""
def append(self, item):
if item.endswith('\r'): # Fix older sdists built on Windows
item = item[:-1]
path = convert_path(item)
if sys.version_info >= (3,):
try:
if os.path.exists(path) or os.path.exists(path.encode('utf-8')):
self.files.append(path)
except UnicodeEncodeError:
# Accept UTF-8 filenames even if LANG=C
if os.path.exists(path.encode('utf-8')):
self.files.append(path)
else:
log.warn("'%s' not %s encodable -- skipping", path,
sys.getfilesystemencoding())
else:
if os.path.exists(path):
self.files.append(path)
......@@ -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):
template = "MANIFEST.in"
......@@ -330,7 +329,6 @@ class manifest_maker(sdist):
self.prune_file_list()
self.filelist.sort()
self.filelist.remove_duplicates()
self.filelist.files = [compose(path) for path in self.filelist.files]
self.write_manifest()
def write_manifest (self):
......@@ -338,6 +336,18 @@ class manifest_maker(sdist):
by 'add_defaults()' and 'read_template()') to the manifest file
named by 'self.manifest'.
"""
# The manifest must be UTF-8 encodable. See #303.
if sys.version_info >= (3,):
files = []
for file in self.filelist.files:
try:
file.encode("utf-8")
except UnicodeEncodeError:
log.warn("'%s' not UTF-8 encodable -- skipping" % file)
else:
files.append(file)
self.filelist.files = files
files = self.filelist.files
if os.sep!='/':
files = [f.replace(os.sep,'/') for f in files]
......
<?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):
log.info("reading manifest file '%s'", self.manifest)
manifest = open(self.manifest, 'rbU')
for line in manifest:
# The manifest must contain UTF-8. See #303.
if sys.version_info >= (3,):
try:
line = line.decode('UTF-8')
except UnicodeDecodeError:
log.warn("%r not UTF-8 decodable -- skipping" % line)
continue
# ignore comments and blank lines
line = line.strip()
if line.startswith('#') or not line:
......
......@@ -108,8 +108,9 @@ class upload(Command):
data['comment'] = comment
if self.sign:
data['gpg_signature'] = (os.path.basename(filename) + ".asc",
open(filename+".asc").read())
asc_file = open(filename + ".asc")
data['gpg_signature'] = (os.path.basename(filename) + ".asc", asc_file.read())
asc_file.close()
# set up the authentication
auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip()
......
......@@ -8,9 +8,12 @@ PyPI's packages.python.org).
import os
import socket
import zipfile
import base64
import tempfile
import sys
import shutil
from base64 import standard_b64encode
from pkg_resources import iter_entry_points
from distutils import log
from distutils.errors import DistutilsOptionError
......@@ -24,18 +27,12 @@ from setuptools.compat import httplib, urlparse
_IS_PYTHON3 = sys.version > '3'
try:
bytes
except NameError:
bytes = str
def b(str_or_bytes):
"""Return bytes by either encoding the argument as ASCII or simply return
the argument as-is."""
if not isinstance(str_or_bytes, bytes):
return str_or_bytes.encode('ascii')
else:
return str_or_bytes
# This is not just a replacement for byte literals
# but works as a general purpose encoder
def b(s, encoding='utf-8'):
if isinstance(s, unicode):
return s.encode(encoding)
return s
class upload_docs(upload):
......@@ -51,43 +48,67 @@ class upload_docs(upload):
]
boolean_options = upload.boolean_options
def has_sphinx(self):
if self.upload_dir is None:
for ep in iter_entry_points('distutils.commands', 'build_sphinx'):
return True
sub_commands = [('build_sphinx', has_sphinx)]
def initialize_options(self):
upload.initialize_options(self)
self.upload_dir = None
self.target_dir = None
def finalize_options(self):
upload.finalize_options(self)
if self.upload_dir is None:
if self.has_sphinx():
build_sphinx = self.get_finalized_command('build_sphinx')
self.target_dir = build_sphinx.builder_target_dir
else:
build = self.get_finalized_command('build')
self.upload_dir = os.path.join(build.build_base, 'docs')
self.mkpath(self.upload_dir)
self.target_dir = os.path.join(build.build_base, 'docs')
else:
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):
name = self.distribution.metadata.get_name()
tmp_dir = tempfile.mkdtemp()
tmp_file = os.path.join(tmp_dir, "%s.zip" % name)
zip_file = zipfile.ZipFile(tmp_file, "w")
for root, dirs, files in os.walk(self.upload_dir):
if root == self.upload_dir and not files:
def create_zipfile(self, filename):
zip_file = zipfile.ZipFile(filename, "w")
try:
self.mkpath(self.target_dir) # just in case
for root, dirs, files in os.walk(self.target_dir):
if root == self.target_dir and not files:
raise DistutilsOptionError(
"no files found in upload directory '%s'"
% self.upload_dir)
% self.target_dir)
for name in files:
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)
zip_file.write(full, dest)
finally:
zip_file.close()
return tmp_file
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)
finally:
shutil.rmtree(tmp_dir)
def upload_file(self, filename):
content = open(filename, 'rb').read()
f = open(filename, 'rb')
content = f.read()
f.close()
meta = self.distribution.metadata
data = {
':action': 'doc_upload',
......@@ -95,36 +116,33 @@ class upload_docs(upload):
'content': (os.path.basename(filename), content),
}
# set up the authentication
credentials = self.username + ':' + self.password
if _IS_PYTHON3: # base64 only works with bytes in Python 3.
encoded_creds = base64.encodebytes(credentials.encode('utf8'))
auth = bytes("Basic ")
else:
encoded_creds = base64.encodestring(credentials)
auth = "Basic "
auth += encoded_creds.strip()
credentials = b(self.username + ':' + self.password)
credentials = standard_b64encode(credentials)
if sys.version_info >= (3,):
credentials = credentials.decode('ascii')
auth = "Basic " + credentials
# Build up the MIME payload for the POST data
boundary = b('--------------GHSKFJDLGDS7543FJKLFHRE75642756743254')
sep_boundary = b('\n--') + boundary
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
sep_boundary = b('\n--') + b(boundary)
end_boundary = sep_boundary + b('--')
body = []
for key, values in data.items():
for key, values in data.iteritems():
title = '\nContent-Disposition: form-data; name="%s"' % key
# handle multiple entries for the same name
if type(values) != type([]):
values = [values]
for value in values:
if type(value) is tuple:
fn = b(';filename="%s"' % value[0])
title += '; filename="%s"' % value[0]
value = value[1]
else:
fn = b("")
value = b(value)
body.append(sep_boundary)
body.append(b('\nContent-Disposition: form-data; name="%s"'%key))
body.append(fn)
body.append(b(title))
body.append(b("\n\n"))
body.append(b(value))
if value and value[-1] == b('\r'):
body.append(value)
if value and value[-1:] == b('\r'):
body.append(b('\n')) # write an extra newline (lurve Macs)
body.append(end_boundary)
body.append(b("\n"))
......
......@@ -660,6 +660,11 @@ class Distribution(_Distribution):
if not isinstance(sys.stdout, io.TextIOWrapper):
return _Distribution.handle_display_options(self, option_order)
# Don't wrap stdout if utf-8 is already the encoding. Provides
# workaround for #334.
if sys.stdout.encoding.lower() in ('utf-8', 'utf8'):
return _Distribution.handle_display_options(self, option_order)
# Print metadata in UTF-8 no matter the platform
encoding = sys.stdout.encoding
errors = sys.stdout.errors
......
No preview for this file type
No preview for this file type
No preview for this file type
"""PyPI and direct package downloading"""
import sys, os.path, re, shutil, random, socket
import itertools
import base64
from pkg_resources import *
from distutils import log
......@@ -14,6 +15,8 @@ except ImportError:
from md5 import md5
from fnmatch import translate
from setuptools.py24compat import wraps
EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$')
HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I)
# this is here to fix emacs' cruddy broken syntax highlighting
......@@ -137,9 +140,38 @@ def interpret_distro_name(location, basename, metadata,
platform = platform
)
# From Python 2.7 docs
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in itertools.ifilterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
def unique_values(func):
"""
Wrap a function returning an iterable such that the resulting iterable
only ever yields unique items.
"""
@wraps(func)
def wrapper(*args, **kwargs):
return unique_everseen(func(*args, **kwargs))
return wrapper
REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I)
# this line is here to fix emacs' cruddy broken syntax highlighting
@unique_values
def find_external_links(url, page):
"""Find rel="homepage" and rel="download" links in `page`, yielding URLs"""
......@@ -157,6 +189,7 @@ def find_external_links(url, page):
if match:
yield urljoin(url, htmldecode(match.group(1)))
user_agent = "Python-urllib/%s distribute/%s" % (
sys.version[:3], require('distribute')[0].version
)
......@@ -666,6 +699,10 @@ class PackageIndex(Environment):
#
if scheme=='svn' or scheme.startswith('svn+'):
return self._download_svn(url, filename)
elif scheme=='git' or scheme.startswith('git+'):
return self._download_git(url, filename)
elif scheme.startswith('hg+'):
return self._download_hg(url, filename)
elif scheme=='file':
return url2pathname(urlparse.urlparse(url)[2])
else:
......@@ -706,6 +743,55 @@ class PackageIndex(Environment):
os.system("svn checkout -q %s %s" % (url, filename))
return filename
def _vcs_split_rev_from_url(self, url, pop_prefix=False):
scheme, netloc, path, query, frag = urlparse.urlsplit(url)
scheme = scheme.split('+', 1)[-1]
# Some fragment identification fails
path = path.split('#',1)[0]
rev = None
if '@' in path:
path, rev = path.rsplit('@', 1)
# Also, discard fragment
url = urlparse.urlunsplit((scheme, netloc, path, query, ''))
return url, rev
def _download_git(self, url, filename):
filename = filename.split('#',1)[0]
url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True)
self.info("Doing git clone from %s to %s", url, filename)
os.system("git clone --quiet %s %s" % (url, filename))
if rev is not None:
self.info("Checking out %s", rev)
os.system("(cd %s && git checkout --quiet %s)" % (
filename,
rev,
))
return filename
def _download_hg(self, url, filename):
filename = filename.split('#',1)[0]
url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True)
self.info("Doing hg clone from %s to %s", url, filename)
os.system("hg clone --quiet %s %s" % (url, filename))
if rev is not None:
self.info("Updating to %s", rev)
os.system("(cd %s && hg up -C -r %s >&-)" % (
filename,
rev,
))
return filename
def debug(self, msg, *args):
log.debug(msg, *args)
......@@ -789,7 +875,6 @@ def open_with_auth(url):
# Double scheme does not raise on Mac OS X as revealed by a
# failing test. We would expect "nonnumeric port". Refs #20.
if sys.platform == 'darwin':
if netloc.endswith(':'):
raise httplib.InvalidURL("nonnumeric port: ''")
......
"""
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
_os = sys.modules[os.name]
if os.name == "java":
import org.python.modules.posix.PosixModule as _os
else:
_os = sys.modules[os.name]
try:
_file = file
except NameError:
......
......@@ -1968,7 +1968,9 @@ def testfile(filename, module_relative=True, name=None, package=None,
runner = DocTestRunner(verbose=verbose, optionflags=optionflags)
# Read the file, convert it to a test, and run it.
s = open(filename).read()
f = open(filename)
s = f.read()
f.close()
test = parser.get_doctest(s, globs, name, filename, 0)
runner.run(test)
......@@ -2353,7 +2355,9 @@ def DocFileTest(path, module_relative=True, package=None,
# Find the file and read it.
name = os.path.basename(path)
doc = open(path).read()
f = open(path)
doc = f.read()
f.close()
# Convert it to a test, and wrap it in a DocFileCase.
test = parser.get_doctest(doc, globs, name, path, 0)
......
......@@ -89,16 +89,16 @@ class TestDevelopTest(unittest.TestCase):
self.assertEqual(content, ['easy-install.pth', 'foo.egg-link'])
# Check that we are using the right code.
f = open(os.path.join(site.USER_SITE, 'foo.egg-link'), 'rt')
egg_link_file = open(os.path.join(site.USER_SITE, 'foo.egg-link'), 'rt')
try:
path = f.read().split()[0].strip()
path = egg_link_file.read().split()[0].strip()
finally:
f.close()
f = open(os.path.join(path, 'foo', '__init__.py'), 'rt')
egg_link_file.close()
init_file = open(os.path.join(path, 'foo', '__init__.py'), 'rt')
try:
init = f.read().strip()
init = init_file.read().strip()
finally:
f.close()
init_file.close()
if sys.version < "3":
self.assertEqual(init, 'print "foo"')
else:
......
......@@ -50,9 +50,9 @@ class TestDistInfo(unittest.TestCase):
versioned = os.path.join(self.tmpdir,
'VersionedDistribution-2.718.dist-info')
os.mkdir(versioned)
f = open(os.path.join(versioned, 'METADATA'), 'w+')
metadata_file = open(os.path.join(versioned, 'METADATA'), 'w+')
try:
f.write(DALS(
metadata_file.write(DALS(
"""
Metadata-Version: 1.2
Name: VersionedDistribution
......@@ -61,13 +61,13 @@ class TestDistInfo(unittest.TestCase):
Requires-Dist: quux (>=1.1); extra == 'baz'
"""))
finally:
f.close()
metadata_file.close()
unversioned = os.path.join(self.tmpdir,
'UnversionedDistribution.dist-info')
os.mkdir(unversioned)
f = open(os.path.join(unversioned, 'METADATA'), 'w+')
metadata_file = open(os.path.join(unversioned, 'METADATA'), 'w+')
try:
f.write(DALS(
metadata_file.write(DALS(
"""
Metadata-Version: 1.2
Name: UnversionedDistribution
......@@ -77,7 +77,7 @@ class TestDistInfo(unittest.TestCase):
Requires-Dist: quux (>=1.1); extra == 'baz'
"""))
finally:
f.close()
metadata_file.close()
def tearDown(self):
shutil.rmtree(self.tmpdir)
......@@ -13,7 +13,7 @@ import tarfile
import distutils.core
from setuptools.sandbox import run_setup, SandboxViolation
from setuptools.command.easy_install import easy_install, get_script_args, main
from setuptools.command.easy_install import easy_install, fix_jython_executable, get_script_args, main
from setuptools.command.easy_install import PthDistributions
from setuptools.command import easy_install as easy_install_pkg
from setuptools.dist import Distribution
......@@ -51,7 +51,7 @@ if __name__ == '__main__':
sys.exit(
load_entry_point('spec', 'console_scripts', 'name')()
)
""" % sys.executable
""" % fix_jython_executable(sys.executable, "")
SETUP_PY = """\
from setuptools import setup
......
......@@ -16,15 +16,15 @@ class TestMarkerlib(unittest.TestCase):
os_name = os.name
self.assert_(interpret(""))
self.assertTrue(interpret(""))
self.assert_(interpret("os.name != 'buuuu'"))
self.assert_(interpret("python_version > '1.0'"))
self.assert_(interpret("python_version < '5.0'"))
self.assert_(interpret("python_version <= '5.0'"))
self.assert_(interpret("python_version >= '1.0'"))
self.assert_(interpret("'%s' in os.name" % os_name))
self.assert_(interpret("'buuuu' not in os.name"))
self.assertTrue(interpret("os.name != 'buuuu'"))
self.assertTrue(interpret("python_version > '1.0'"))
self.assertTrue(interpret("python_version < '5.0'"))
self.assertTrue(interpret("python_version <= '5.0'"))
self.assertTrue(interpret("python_version >= '1.0'"))
self.assertTrue(interpret("'%s' in os.name" % os_name))
self.assertTrue(interpret("'buuuu' not in os.name"))
self.assertFalse(interpret("os.name == 'buuuu'"))
self.assertFalse(interpret("python_version < '1.0'"))
......@@ -36,7 +36,7 @@ class TestMarkerlib(unittest.TestCase):
environment = default_environment()
environment['extra'] = 'test'
self.assert_(interpret("extra == 'test'", environment))
self.assertTrue(interpret("extra == 'test'", environment))
self.assertFalse(interpret("extra == 'doc'", environment))
def raises_nameError():
......
......@@ -478,13 +478,14 @@ class ParseTests(TestCase):
p1, p2 = parse_version(s1),parse_version(s2)
self.assertEqual(p1,p2, (s1,s2,p1,p2))
c('1.2-rc1', '1.2rc1')
c('0.4', '0.4.0')
c('0.4.0.0', '0.4.0')
c('0.4.0-0', '0.4-0')
c('0pl1', '0.0pl1')
c('0pre1', '0.0c1')
c('0.0.0preview1', '0c1')
c('0.0c1', '0rc1')
c('0.0c1', '0-rc1')
c('1.2a1', '1.2.a.1'); c('1.2...a', '1.2a')
def testVersionOrdering(self):
......@@ -493,14 +494,11 @@ class ParseTests(TestCase):
self.assertTrue(p1<p2, (s1,s2,p1,p2))
c('2.1','2.1.1')
c('2.1.0','2.10')
c('2a1','2b0')
c('2b1','2c0')
c('2a1','2.1')
c('2.3a1', '2.3')
c('2.1-1', '2.1-2')
c('2.1-1', '2.1.1')
c('2.1', '2.1.1-1')
c('2.1', '2.1pl4')
c('2.1a0-20040501', '2.1')
c('1.1', '02.1')
......@@ -511,20 +509,8 @@ class ParseTests(TestCase):
c('0.4', '4.0')
c('0.0.4', '0.4.0')
c('0pl1', '0.4pl1')
c('2.1.0-rc1','2.1.0')
c('2.1dev','2.1a0')
c('2.1.0rc1','2.1.0')
c('2.1.0','2.1.0-rc0')
c('2.1.0','2.1.0-a')
c('2.1.0','2.1.0-alpha')
c('2.1.0','2.1.0-foo')
c('1.0','1.0-1')
c('1.0-1','1.0.1')
c('1.0a','1.0b')
c('1.0dev','1.0rc1')
c('1.0pre','1.0')
c('1.0pre','1.0')
c('1.0a','1.0-a')
c('1.0rc1','1.0-rc1')
torture ="""
0.80.1-3 0.80.1-2 0.80.1-1 0.79.9999+0.80.0pre4-1
......@@ -562,6 +548,15 @@ class ScriptHeaderTests(TestCase):
if (sys.version_info >= (3,) and os.environ.get("LC_CTYPE")
in (None, "C", "POSIX")):
return
class java:
class lang:
class System:
@staticmethod
def getProperty(property):
return ""
sys.modules["java"] = java
platform = sys.platform
sys.platform = 'java1.5.0_13'
stdout = sys.stdout
......@@ -585,6 +580,7 @@ class ScriptHeaderTests(TestCase):
'#!%s -x\n' % self.non_ascii_exe)
self.assertTrue('Unable to adapt shebang line' in sys.stdout.getvalue())
finally:
del sys.modules["java"]
sys.platform = platform
sys.stdout = stdout
......
......@@ -7,9 +7,11 @@ import shutil
import sys
import tempfile
import unittest
import unicodedata
from setuptools.compat import StringIO
from setuptools.compat import StringIO, urllib
from setuptools.command.sdist import sdist
from setuptools.command.egg_info import manifest_maker
from setuptools.dist import Distribution
......@@ -28,7 +30,52 @@ setup(**%r)
""" % SETUP_ATTRS
if sys.version_info >= (3,):
LATIN1_FILENAME = 'smörbröd.py'.encode('latin-1')
else:
LATIN1_FILENAME = 'sm\xf6rbr\xf6d.py'
# Cannot use context manager because of Python 2.4
def quiet():
global old_stdout, old_stderr
old_stdout, old_stderr = sys.stdout, sys.stderr
sys.stdout, sys.stderr = StringIO(), StringIO()
def unquiet():
sys.stdout, sys.stderr = old_stdout, old_stderr
# Fake byte literals for Python <= 2.5
def b(s, encoding='utf-8'):
if sys.version_info >= (3,):
return s.encode(encoding)
return s
# Convert to POSIX path
def posix(path):
if sys.version_info >= (3,) and not isinstance(path, unicode):
return path.replace(os.sep.encode('ascii'), b('/'))
else:
return path.replace(os.sep, '/')
# HFS Plus uses decomposed UTF-8
def decompose(path):
if isinstance(path, unicode):
return unicodedata.normalize('NFD', path)
try:
path = path.decode('utf-8')
path = unicodedata.normalize('NFD', path)
path = path.encode('utf-8')
except UnicodeError:
pass # Not UTF-8
return path
class TestSdistTest(unittest.TestCase):
def setUp(self):
self.temp_dir = tempfile.mkdtemp()
f = open(os.path.join(self.temp_dir, 'setup.py'), 'w')
......@@ -62,106 +109,286 @@ class TestSdistTest(unittest.TestCase):
cmd.ensure_finalized()
# squelch output
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = StringIO()
sys.stderr = StringIO()
quiet()
try:
cmd.run()
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
unquiet()
manifest = cmd.filelist.files
self.assertTrue(os.path.join('sdist_test', 'a.txt') in manifest)
self.assertTrue(os.path.join('sdist_test', 'b.txt') in manifest)
self.assertTrue(os.path.join('sdist_test', 'c.rst') not in manifest)
def test_filelist_is_fully_composed(self):
# Test for #303. Requires HFS Plus to fail.
def test_manifest_is_written_with_utf8_encoding(self):
# Test for #303.
dist = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py'
mm = manifest_maker(dist)
mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
os.mkdir('sdist_test.egg-info')
# Add file with non-ASCII filename
# UTF-8 filename
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.script_name = 'setup.py'
cmd = sdist(dist)
cmd.ensure_finalized()
# squelch output
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = StringIO()
sys.stderr = StringIO()
# Create manifest
quiet()
try:
cmd.run()
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
self.assertTrue(filename in cmd.filelist.files)
unquiet()
def test_manifest_is_written_in_utf8(self):
# Test for #303.
# Add UTF-8 filename to manifest
filename = os.path.join(b('sdist_test'), b('smörbröd.py'))
cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
manifest = open(cmd.manifest, 'ab')
manifest.write(b('\n')+filename)
manifest.close()
# Add file with non-ASCII filename
filename = os.path.join('sdist_test', 'smörbröd.py')
# The file must exist to be included in the filelist
open(filename, 'w').close()
# Re-read manifest
cmd.filelist.files = []
quiet()
try:
cmd.read_manifest()
finally:
unquiet()
# The filelist should contain the UTF-8 filename
if sys.version_info >= (3,):
filename = filename.decode('utf-8')
self.assertTrue(filename in cmd.filelist.files)
# Python 3 only
if sys.version_info >= (3,):
def test_read_manifest_skips_non_utf8_filenames(self):
# Test for #303.
dist = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py'
cmd = sdist(dist)
cmd.ensure_finalized()
# squelch output
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = StringIO()
sys.stderr = StringIO()
# Create manifest
quiet()
try:
cmd.run()
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
unquiet()
manifest = open(os.path.join('sdist_test.egg-info', 'SOURCES.txt'), 'rbU')
contents = manifest.read()
# Add Latin-1 filename to manifest
filename = os.path.join(b('sdist_test'), LATIN1_FILENAME)
cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
manifest = open(cmd.manifest, 'ab')
manifest.write(b('\n')+filename)
manifest.close()
self.assertTrue(len(contents))
# This must not fail:
contents.decode('UTF-8')
# The file must exist to be included in the filelist
open(filename, 'w').close()
def test_manifest_is_read_in_utf8(self):
# Re-read manifest
cmd.filelist.files = []
quiet()
try:
try:
cmd.read_manifest()
except UnicodeDecodeError, e:
self.fail(e)
finally:
unquiet()
# The Latin-1 filename should have been skipped
filename = filename.decode('latin-1')
self.assertFalse(filename in cmd.filelist.files)
def test_sdist_with_utf8_encoded_filename(self):
# Test for #303.
dist = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py'
cmd = sdist(dist)
cmd.ensure_finalized()
# Add file with non-ASCII filename
filename = os.path.join('sdist_test', 'smörbröd.py')
# UTF-8 filename
filename = os.path.join(b('sdist_test'), b('smörbröd.py'))
open(filename, 'w').close()
quiet()
try:
cmd.run()
finally:
unquiet()
if sys.platform == 'darwin':
filename = decompose(filename)
if sys.version_info >= (3,):
fs_enc = sys.getfilesystemencoding()
if sys.platform == 'win32':
if fs_enc == 'cp1252':
# Python 3 mangles the UTF-8 filename
filename = filename.decode('cp1252')
self.assertTrue(filename in cmd.filelist.files)
else:
filename = filename.decode('mbcs')
self.assertTrue(filename in cmd.filelist.files)
else:
filename = filename.decode('utf-8')
self.assertTrue(filename in cmd.filelist.files)
else:
self.assertTrue(filename in cmd.filelist.files)
def test_sdist_with_latin1_encoded_filename(self):
# Test for #303.
dist = Distribution(SETUP_ATTRS)
dist.script_name = 'setup.py'
cmd = sdist(dist)
cmd.ensure_finalized()
# squelch output
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = StringIO()
sys.stderr = StringIO()
# Latin-1 filename
filename = os.path.join(b('sdist_test'), LATIN1_FILENAME)
open(filename, 'w').close()
self.assertTrue(os.path.isfile(filename))
quiet()
try:
cmd.run()
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
cmd.filelist.files = []
cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt')
cmd.read_manifest()
unquiet()
if sys.version_info >= (3,):
#not all windows systems have a default FS encoding of cp1252
if sys.platform == 'win32':
# Latin-1 is similar to Windows-1252 however
# on mbcs filesys it is not in latin-1 encoding
fs_enc = sys.getfilesystemencoding()
if fs_enc == 'mbcs':
filename = filename.decode('mbcs')
else:
filename = filename.decode('latin-1')
self.assertTrue(filename in cmd.filelist.files)
else:
# The Latin-1 filename should have been skipped
filename = filename.decode('latin-1')
self.assertFalse(filename in cmd.filelist.files)
else:
# No conversion takes place under Python 2 and the file
# is included. We shall keep it that way for BBB.
self.assertTrue(filename in cmd.filelist.files)
def test_suite():
......
......@@ -54,12 +54,19 @@ class TestUploadDocsTest(unittest.TestCase):
cmd = upload_docs(dist)
cmd.upload_dir = self.upload_dir
zip_file = cmd.create_zipfile()
cmd.target_dir = self.upload_dir
tmp_dir = tempfile.mkdtemp()
tmp_file = os.path.join(tmp_dir, 'foo.zip')
try:
zip_file = cmd.create_zipfile(tmp_file)
assert zipfile.is_zipfile(zip_file)
assert zipfile.is_zipfile(tmp_file)
zip_f = zipfile.ZipFile(zip_file) # woh...
zip_file = zipfile.ZipFile(tmp_file) # woh...
assert zip_f.namelist() == ['index.html']
assert zip_file.namelist() == ['index.html']
zip_file.close()
finally:
shutil.rmtree(tmp_dir)
......@@ -17,7 +17,7 @@ Let's create a simple script, foo-script.py:
>>> from setuptools.command.easy_install import nt_quote_arg
>>> sample_directory = tempfile.mkdtemp()
>>> f = open(os.path.join(sample_directory, 'foo-script.py'), 'w')
>>> bytes = f.write(
>>> bytes_written = f.write(
... """#!%(python_exe)s
... import sys
... input = repr(sys.stdin.read())
......@@ -37,8 +37,8 @@ We'll also copy cli.exe to the sample-directory with the name foo.exe:
>>> import pkg_resources
>>> f = open(os.path.join(sample_directory, 'foo.exe'), 'wb')
>>> bytes = f.write(
... pkg_resources.resource_string('setuptools', 'cli.exe')
>>> bytes_written = f.write(
... pkg_resources.resource_string('setuptools', 'cli-32.exe')
... )
>>> f.close()
......@@ -82,7 +82,7 @@ options as usual. For example, to run in optimized mode and
enter the interpreter after running the script, you could use -Oi:
>>> f = open(os.path.join(sample_directory, 'foo-script.py'), 'w')
>>> bytes = f.write(
>>> bytes_written = f.write(
... """#!%(python_exe)s -Oi
... import sys
... input = repr(sys.stdin.read())
......@@ -112,10 +112,12 @@ Now let's test the GUI version with the simple scipt, bar-script.py:
>>> from setuptools.command.easy_install import nt_quote_arg
>>> sample_directory = tempfile.mkdtemp()
>>> f = open(os.path.join(sample_directory, 'bar-script.pyw'), 'w')
>>> bytes = f.write(
>>> bytes_written = f.write(
... """#!%(python_exe)s
... import sys
... open(sys.argv[1], 'wb').write(repr(sys.argv[2]).encode('ascii'))
... f = open(sys.argv[1], 'wb')
... bytes_written = f.write(repr(sys.argv[2]).encode('utf-8'))
... f.close()
... """ % dict(python_exe=nt_quote_arg(sys.executable)))
>>> f.close()
......@@ -123,8 +125,8 @@ We'll also copy gui.exe to the sample-directory with the name bar.exe:
>>> import pkg_resources
>>> f = open(os.path.join(sample_directory, 'bar.exe'), 'wb')
>>> bytes = f.write(
... pkg_resources.resource_string('setuptools', 'gui.exe')
>>> bytes_written = f.write(
... pkg_resources.resource_string('setuptools', 'gui-32.exe')
... )
>>> f.close()
......
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