Commit 29fa0162 authored by Jason R. Coombs's avatar Jason R. Coombs

Merge with master. Ref #229.

--HG--
branch : feature/issue-229
parents 928324bd 6bdbe895
......@@ -182,3 +182,54 @@ fa069bf2411a150c9379d31a04d1c3836e2d3027 9.0.1
651d41db58849d4fc50e466f4dc458d448480c4e 10.2
1f5de53c079d577ead9d80265c9e006503b16457 10.2.1
b4b92805bc0e9802da0b597d00df4fa42b30bc40 11.0
6cd2b18f4be2a9c188fa505b34505b32f4a4554b 11.1
feb5971e7827483bbdeb67613126bb79ed09e6d9 11.2
a1a6a1ac9113b90009052ca7263174a488434099 11.3
1116e568f534ad8f4f41328a0f5fa183eb739c90 11.3.1
55666947c9eb7e3ba78081ad6ae004807c84aede 12.0
747018b2e35a40cb4b1c444f150f013d02197c64 12.0.1
a177ea34bf81662b904fe3af46f3c8719a947ef1 12.0.2
bf8c5bcacd49bf0f9648013a40ebfc8f7c727f7b 12.0.3
73dcfc90e3eecec6baddea19302c6b342e68e2fa 12.0.4
01fbfc9194a2bc502edd682eebbf4d2f1bc79eee 12.0.5
7bca8938434839dbb546b8bfccd9aab7a86d851e 12.1
5ff5c804a8fa580cff499ba0025ff2e6a5474fd0 12.2
8d50aac3b20793954121edb300b477cc75f3ec96 12.3
297931cb8cac7d44d970adb927efd6cb36ac3526 12.4
df34cc18624279faffdbc729c0a11e6ab0f46572 13.0
ae1a5c5cf78f4f9f98c054f1c8cec6168d1d19b4 13.0.1
e22a1d613bddf311e125eecd9c1e1cad02ab5063 13.0.2
a3a105f795f8362f26e84e9acbc237ee2d6bcca4 14.0
9751a1671a124e30ae344d1510b9c1dbb14f2775 14.1
07fcc3226782b979cedaaf456c7f1c5b2fdafd2c 14.1.1
d714fb731de779a1337d2d78cd413931f1f06193 14.2
e3c635a7d463c7713c647d1aa560f83fd8e27ef0 14.3
608948cef7e0ab8951691b149f5b6f0184a5635e 14.3.1
617699fd3e44e54b6f95b80bfcf78164df37f266 15.0b1
d2c4d84867154243993876d6248aafec1fd12679 15.0
10fde952613b7a3f650fb1f6b6ed58cbd232fa3c 15.1
df5dc9c7aa7521f552824dee1ed1315cfe180844 15.2
e0825f0c7d5963c498266fe3c175220c695ae83b 16.0
8e56240961015347fed477f00ca6a0783e81d3a2 17.0
a37bcaaeab367f2364ed8c070659d52a4c0ae38e 17.1
4a0d01d690ff184904293e7a3244ac24ec060a73 17.1.1
fac98a49bd984ef5accf7177674d693277bfbaef 18.0b1
0a49ee524b0a1d67d2a11c8c22f082b57acd7ae1 18.0
e364795c1b09c70b6abb53770e09763b52bf807d 18.0.1
c0395f556c35d8311fdfe2bda6846b91149819cd 18.1
1a981f2e5031f55267dc2a28fa1b42274a1b64b2 18.2
b59320212c8371d0be9e5e6c5f7eec392124c009 18.3
7a705b610abb1177ca169311c4ee261f3e4f0957 18.3.1
1e120f04bcaa2421c4df0eb6678c3019ba4a82f6 18.3.2
6203335278be7543d31790d9fba55739469a4c6c 18.4
31dc6d2ac0f5ab766652602fe6ca716fff7180e7 18.5
dfe190b09908f6b953209d13573063809de451b8 18.6
804f87045a901f1dc121cf9149143d654228dc13 18.6.1
67d07805606aead09349d5b91d7d26c68ddad2fc 18.7
3041e1fc409be90e885968b90faba405420fc161 18.7.1
c811801ffa1de758cf01fbf6a86e4c04ff0c0935 18.8
fbf06fa35f93a43f044b1645a7e4ff470edb462c 18.8.1
cc41477ecf92f221c113736fac2830bf8079d40c 19.0
834782ce49154e9744e499e00eb392c347f9e034 19.1
0a2a3d89416e1642cf6f41d22dbc07b3d3c15a4d 19.1.1
5d24cf9d1ced76c406ab3c4a94c25d1fe79b94bc 19.2
......@@ -2,14 +2,24 @@ language: python
python:
- 2.6
- 2.7
- 3.2
- 3.3
- 3.4
- 3.5
- pypy
# command to run tests
env:
- ""
- LC_ALL=C LC_CTYPE=C
script:
# avoid VersionConflict when newer version is required
- pip install -U pytest
# Output the env, because the travis docs just can't be trusted
- env
# update egg_info based on setup.py in checkout
- python bootstrap.py
- python setup.py ptr --addopts='-rs'
- python ez_setup.py --version 10.2.1
- python setup.py test --addopts='-rs'
# test the bootstrap script
- python ez_setup.py
This diff is collapsed.
The canonical development guide can be found in docs/developer-guide.txt.
recursive-include setuptools *.py *.exe *.xml
recursive-include tests *.py *.c *.pyx
recursive-include tests *.py
recursive-include setuptools/tests *.html
recursive-include docs *.py *.txt *.conf *.css *.css_t Makefile indexsidebar.html
recursive-include _markerlib *.py
......@@ -10,3 +10,4 @@ include *.txt
include MANIFEST.in
include launcher.c
include msvc-build-launcher.cmd
include pytest.ini
......@@ -2,6 +2,6 @@ empty:
exit 1
update-vendored:
rm -rf setuptools/_vendor/packaging
pip install -r setuptools/_vendor/vendored.txt -t setuptools/_vendor/
rm -rf setuptools/_vendor/*.{egg,dist}-info
rm -rf pkg_resources/_vendor/packaging
pip install -r pkg_resources/_vendor/vendored.txt -t pkg_resources/_vendor/
rm -rf pkg_resources/_vendor/*.{egg,dist}-info
......@@ -223,3 +223,14 @@ Credits
the Python Packaging Authority (PyPA) and the larger Python community.
.. _files:
---------------
Code of Conduct
---------------
Everyone interacting in the setuptools project's codebases, issue trackers,
chat rooms, and mailing lists is expected to follow the
`PyPA Code of Conduct`_.
.. _PyPA Code of Conduct: https://www.pypa.io/en/latest/code-of-conduct/
......@@ -28,7 +28,7 @@ import setup as setup_script
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['linkify']
extensions = ['rst.linker']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
......@@ -198,3 +198,58 @@ latex_documents = [
# If false, no module index is generated.
#latex_use_modindex = True
link_files = {
'CHANGES.txt': dict(
using=dict(
BB='https://bitbucket.org',
GH='https://github.com',
),
replace=[
dict(
pattern=r"(Issue )?#(?P<issue>\d+)",
url='{BB}/pypa/setuptools/issue/{issue}',
),
dict(
pattern=r"Pull Request ?#(?P<pull_request>\d+)",
url='{BB}/pypa/setuptools/pull-request/{pull_request}',
),
dict(
pattern=r"Distribute #(?P<distribute>\d+)",
url='{BB}/tarek/distribute/issue/{distribute}',
),
dict(
pattern=r"Buildout #(?P<buildout>\d+)",
url='{GH}/buildout/buildout/issues/{buildout}',
),
dict(
pattern=r"Old Setuptools #(?P<old_setuptools>\d+)",
url='http://bugs.python.org/setuptools/issue{old_setuptools}',
),
dict(
pattern=r"Jython #(?P<jython>\d+)",
url='http://bugs.jython.org/issue{jython}',
),
dict(
pattern=r"Python #(?P<python>\d+)",
url='http://bugs.python.org/issue{python}',
),
dict(
pattern=r"Interop #(?P<interop>\d+)",
url='{GH}/pypa/interoperability-peps/issues/{interop}',
),
dict(
pattern=r"Pip #(?P<pip>\d+)",
url='{GH}/pypa/pip/issues/{pip}',
),
dict(
pattern=r"Packaging #(?P<packaging>\d+)",
url='{GH}/pypa/packaging/issues/{packaging}',
),
dict(
pattern=r"[Pp]ackaging (?P<packaging_ver>\d+(\.\d+)+)",
url='{GH}/pypa/packaging/blob/{packaging_ver}/CHANGELOG.rst',
),
],
),
}
......@@ -92,9 +92,10 @@ Testing
The primary tests are run using py.test. To run the tests::
$ python setup.py ptr
$ python setup.py test
Or install py.test into your environment and run ``py.test``.
Or install py.test into your environment and run ``PYTHONPATH=. py.test``
or ``python -m pytest``.
Under continuous integration, additional tests may be run. See the
``.travis.yml`` file for full details on the tests run under Travis-CI.
......@@ -109,3 +110,20 @@ Setuptools follows ``semver`` with some exceptions:
- Omits 'v' prefix for tags.
.. explain value of reflecting meaning in versions.
----------------------
Building Documentation
----------------------
Setuptools relies on the Sphinx system for building documentation and in
particular the ``build_sphinx`` distutils command. To build the
documentation, invoke::
python setup.py build_sphinx
from the root of the repository. Setuptools will download a compatible
build of Sphinx and any requisite plugins and then build the
documentation in the build/sphinx directory.
Setuptools does not support invoking the doc builder from the docs/
directory as some tools expect.
......@@ -33,4 +33,3 @@ setuptools changes. You have been warned.
developer-guide
formats
releases
......@@ -19,9 +19,7 @@ Documentation content:
history
roadmap
python3
using
setuptools
easy_install
pkg_resources
development
merge
Setuptools/Distribute Merge FAQ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
How do I upgrade from Distribute?
=================================
Distribute specifically prohibits installation of Setuptools 0.7 from Distribute 0.6. There are then two options for upgrading.
Note that after upgrading using either technique, the only option to downgrade to either version is to completely uninstall Distribute and Setuptools 0.7 versions before reinstalling an 0.6 release.
Use Distribute 0.7
------------------
The PYPA has put together a compatibility wrapper, a new release of Distribute version 0.7. This package will install over Distribute 0.6.x installations and will replace Distribute with a simple wrapper that requires Setuptools 0.7 or later. This technique is experimental, but initial results indicate this technique is the easiest upgrade path.
Uninstall
---------
First, completely uninstall Distribute. Since Distribute does not have an automated installation routine, this process is manual. Follow the instructions in the README for uninstalling.
How do I upgrade from Setuptools 0.6?
=====================================
There are no special instructions for upgrading over older versions of Setuptools. Simply use `easy_install -U` or run the latest `ez_setup.py`.
Where does the merge occur?
========================================================
The merge is occurring between the heads of the default branch of Distribute and the setuptools-0.6 branch of Setuptools. The Setuptools SVN repo has been converted to a Mercurial repo hosted on Bitbucket. The work is still underway, so the exact changesets included may change, although the anticipated merge targets are Setuptools at 0.6c12 and Distribute at 0.6.35.
What happens to other branches?
========================================================
Distribute 0.7 was abandoned long ago and won't be included in the resulting code tree, but may be retained for posterity in the original repo.
Setuptools default branch (also 0.7 development) may also be abandoned or may be incorporated into the new merged line if desirable (and as resources allow).
What history is lost/changed?
========================================================
As setuptools was not on Mercurial when the fork occurred and as Distribute did not include the full setuptools history (prior to the creation of the setuptools-0.6 branch), the two source trees were not compatible. In order to most effectively communicate the code history, the Distribute code was grafted onto the (originally private) setuptools Mercurial repo. Although this grafting maintained the full code history with names, dates, and changes, it did lose the original hashes of those changes. Therefore, references to changes by hash (including tags) are lost.
Additionally, any heads that were not actively merged into the Distribute 0.6.35 release were also omitted. As a result, the changesets included in the merge repo are those from the original setuptools repo and all changesets ancestral to the Distribute 0.6.35 release.
What features will be in the merged code base?
========================================================
In general, all "features" added in distribute will be included in setuptools. Where there exist conflicts or undesirable features, we will be explicit about what these limitations are. Changes that are backward-incompatible from setuptools 0.6 to distribute will likely be removed, and these also will be well documented.
Bootstrapping scripts (ez_setup/distribute_setup) and docs, as with distribute, will be maintained in the repository and built as part of the release process. Documentation and bootstrapping scripts will be hosted at python.org, as they are with distribute now. Documentation at telecommunity will be updated to refer or redirect to the new, merged docs.
On the whole, the merged setuptools should be largely compatible with the latest releases of both setuptools and distribute and will be an easy transition for users of either library.
Who is invited to contribute? Who is excluded?
========================================================
While we've worked privately to initiate this merge due to the potential sensitivity of the topic, no one is excluded from this effort. We invite all members of the community, especially those most familiar with Python packaging and its challenges to join us in the effort.
We have lots of ideas for how we'd like to improve the codebase, release process, everything. Like distribute, the post-merge setuptools will have its source hosted on Bitbucket. (So if you're currently a distribute contributor, about the only thing that's going to change is the URL of the repository you follow.) Also like distribute, it'll support Python 3, and hopefully we'll soon merge Vinay Sajip's patches to make it run on Python 3 without needing 2to3 to be run on the code first.
While we've worked privately to initiate this merge due to the potential sensitivity of the topic, no one is excluded from this effort. We invite all members of the community, especially those most familiar with Python packaging and its challenges to join us in the effort.
Why Setuptools and not Distribute or another name?
========================================================
We do, however, understand that this announcement might be unsettling for some. The setuptools name has been subjected to a lot of deprecation in recent years, so the idea that it will now be the preferred name instead of distribute might be somewhat difficult or disorienting for some. We considered use of another name (Distribute or an entirely new name), but that would serve to only complicate matters further. Instead, our goal is to simplify the packaging landscape but without losing any hard-won advancements. We hope that the people who worked to spread the first message will be equally enthusiastic about spreading the new one, and we especially look forward to seeing the new posters and slogans celebrating setuptools.
What is the timeframe of release?
========================================================
There are no hard timeframes for any of this effort, although progress is underway and a draft merge is underway and being tested privately. As an unfunded volunteer effort, our time to put in on it is limited, and we've both had some recent health and other challenges that have made working on this difficult, which in part explains why we haven't met our original deadline of a completed merge before PyCon.
The final Setuptools 0.7 was cut on June 1, 2013 and will be released to PyPI shortly thereafter.
What version number can I expect for the new release?
========================================================
The new release will roughly follow the previous trend for setuptools and release the new release as 0.7. This number is somewhat arbitrary, but we wanted something other than 0.6 to distinguish it from its ancestor forks but not 1.0 to avoid putting too much emphasis on the release itself and to focus on merging the functionality. In the future, the project will likely adopt a versioning scheme similar to semver to convey semantic meaning about the release in the version number.
Merge with Distribute
~~~~~~~~~~~~~~~~~~~~~
In 2013, the fork of Distribute was merged back into Setuptools. This
document describes some of the details of the merge.
.. toctree::
:maxdepth: 2
merge-faq
Process
=======
In order to try to accurately reflect the fork and then re-merge of the
projects, the merge process brought both code trees together into one
repository and grafted the Distribute fork onto the Setuptools development
line (as if it had been created as a branch in the first place).
The rebase to get distribute onto setuptools went something like this::
hg phase -d -f -r 26b4c29b62db
hg rebase -s 26b4c29b62db -d 7a5cf59c78d7
The technique required a late version of mercurial (2.5) to work correctly.
The only code that was included was the code that was ancestral to the public
releases of Distribute 0.6. Additionally, because Setuptools was not hosted
on Mercurial at the time of the fork and because the Distribute fork did not
include a complete conversion of the Setuptools history, the Distribute
changesets had to be re-applied to a new, different conversion of the
Setuptools SVN repository. As a result, all of the hashes have changed.
Distribute was grafted in a 'distribute' branch and the 'setuptools-0.6'
branch was targeted for the merge. The 'setuptools' branch remains with
unreleased code and may be incorporated in the future.
Reconciling Differences
=======================
There were both technical and philosophical differences between Setuptools
and Distribute. To reconcile these differences in a manageable way, the
following technique was undertaken:
Create a 'Setuptools-Distribute merge' branch, based on a late release of
Distribute (0.6.35). This was done with a00b441856c4.
In that branch, first remove code that is no longer relevant to
Setuptools (such as the setuptools patching code).
Next, in the the merge branch, create another base from at the point where the
fork occurred (such that the code is still essentially an older but pristine
setuptools). This base can be found as 955792b069d0. This creates two heads
in the merge branch, each with a basis in the fork.
Then, repeatedly copy changes for a
single file or small group of files from a late revision of that file in the
'setuptools-0.6' branch (1aae1efe5733 was used) and commit those changes on
the setuptools-only head. That head is then merged with the head with
Distribute changes. It is in this Mercurial
merge operation that the fundamental differences between Distribute and
Setuptools are reconciled, but since only a single file or small set of files
are used, the scope is limited.
Finally, once all the challenging files have been reconciled and merged, the
remaining changes from the setuptools-0.6 branch are merged, deferring to the
reconciled changes (a1fa855a5a62 and 160ccaa46be0).
Originally, jaraco attempted all of this using anonymous heads in the
Distribute branch, but later realized this technique made for a somewhat
unclear merge process, so the changes were re-committed as described above
for clarity. In this way, the "distribute" and "setuptools" branches can
continue to track the official Distribute changesets.
Concessions
===========
With the merge of Setuptools and Distribute, the following concessions were
made:
Differences from setuptools 0.6c12:
Major Changes
-------------
* Python 3 support.
* Improved support for GAE.
* Support `PEP-370 <http://www.python.org/dev/peps/pep-0370/>`_ per-user site
packages.
* Sort order of Distributions in pkg_resources now prefers PyPI to external
links (Distribute issue 163).
* Python 2.4 or greater is required (drop support for Python 2.3).
Minor Changes
-------------
* Wording of some output has changed to replace contractions with their
canonical form (i.e. prefer "could not" to "couldn't").
* Manifest files are only written for 32-bit .exe launchers.
Differences from Distribute 0.6.36:
Major Changes
-------------
* The _distribute property of the setuptools module has been removed.
* Distributions are once again installed as zipped eggs by default, per the
rationale given in `the seminal bug report
<http://bugs.python.org/setuptools/issue33>`_ indicates that the feature
should remain and no substantial justification was given in the `Distribute
report <https://bitbucket.org/tarek/distribute/issue/19/>`_.
Minor Changes
-------------
* The patch for `#174 <https://bitbucket.org/tarek/distribute/issue/174>`_
has been rolled-back, as the comment on the ticket indicates that the patch
addressed a symptom and not the fundamental issue.
* ``easy_install`` (the command) once again honors setup.cfg if found in the
current directory. The "mis-behavior" characterized in `#99
<https://bitbucket.org/tarek/distribute/issue/99>`_ is actually intended
behavior, and no substantial rationale was given for the deviation.
......@@ -592,7 +592,7 @@ Requirements Parsing
The syntax of a requirement specifier can be defined in EBNF as follows::
requirement ::= project_name versionspec? extras?
requirement ::= project_name extras? versionspec?
versionspec ::= comparison version (',' comparison version)*
comparison ::= '<' | '<=' | '!=' | '==' | '>=' | '>' | '~=' | '==='
extras ::= '[' extralist? ']'
......
......@@ -5,16 +5,22 @@ Supporting both Python 2 and Python 3 with Setuptools
Starting with Distribute version 0.6.2 and Setuptools 0.7, the Setuptools
project supported Python 3. Installing and
using setuptools for Python 3 code works exactly the same as for Python 2
code, but Setuptools 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.
code.
Setuptools provides a facility to invoke 2to3 on the code as a part of the
build process, by setting the keyword parameter ``use_2to3`` to True, but
the Setuptools strongly recommends instead developing a unified codebase
using `six <https://pypi.python.org/pypi/six>`_,
`future <https://pypi.python.org/pypi/future>`_, or another compatibility
library.
Setuptools as help during porting
=================================
Setuptools can make the porting process much easier by automatically running
2to3 as a part of the test running. To do this you need to configure the
Using 2to3
==========
Setuptools attempts to make the porting process easier by automatically
running
2to3 as a part of running tests. To do so, you need to configure the
setup.py so that you can run the unit tests with ``python setup.py test``.
See :ref:`test` for more information on this.
......@@ -37,23 +43,23 @@ to a list of names of packages containing fixers. To exclude fixers, the
parameter ``use_2to3_exclude_fixers`` can be set to fixer names to be
skipped.
A typical setup.py can look something like this::
An example setup.py might look something like this::
from setuptools import setup
setup(
name='your.module',
version = '1.0',
version='1.0',
description='This is your awesome module',
author='You',
author_email='your@email',
package_dir = {'': 'src'},
packages = ['your', 'you.module'],
test_suite = 'your.module.tests',
use_2to3 = True,
convert_2to3_doctests = ['src/your/module/README.txt'],
use_2to3_fixers = ['your.fixers'],
use_2to3_exclude_fixers = ['lib2to3.fixes.fix_import'],
package_dir={'': 'src'},
packages=['your', 'you.module'],
test_suite='your.module.tests',
use_2to3=True,
convert_2to3_doctests=['src/your/module/README.txt'],
use_2to3_fixers=['your.fixers'],
use_2to3_exclude_fixers=['lib2to3.fixes.fix_import'],
)
Differential conversion
......@@ -86,39 +92,3 @@ Advanced features
If you don't want to run the 2to3 conversion on the doctests in Python files,
you can turn that off by setting ``setuptools.use_2to3_on_doctests = False``.
Note on compatibility with older versions of setuptools
=======================================================
Setuptools earlier than 0.7 does not know about the new keyword parameters to
support Python 3.
As a result it will warn about the unknown keyword parameters if you use
those versions of setuptools instead of Distribute under Python 2. This output
is not an error, and
install process will continue as normal, but if you want to get rid of that
error this is easy. Simply conditionally add the new parameters into an extra
dict and pass that dict into setup()::
from setuptools import setup
import sys
extra = {}
if sys.version_info >= (3,):
extra['use_2to3'] = True
extra['convert_2to3_doctests'] = ['src/your/module/README.txt']
extra['use_2to3_fixers'] = ['your.fixers']
setup(
name='your.module',
version = '1.0',
description='This is your awesome module',
author='You',
author_email='your@email',
package_dir = {'': 'src'},
packages = ['your', 'you.module'],
test_suite = 'your.module.tests',
**extra
)
This way the parameters will only be used under Python 3, where Distribute or
Setuptools 0.7 or later is required.
......@@ -112,10 +112,16 @@ the distutils. Here's a minimal setup script using setuptools::
)
As you can see, it doesn't take much to use setuptools in a project.
Just by doing the above, this project will be able to produce eggs, upload to
Run that script in your project folder, alongside the Python packages
you have developed.
Invoke that script to produce eggs, upload to
PyPI, and automatically include all packages in the directory where the
setup.py lives. See the `Command Reference`_ section below to see what
commands you can give to this setup script.
commands you can give to this setup script. For example,
to produce a source distribution, simply invoke::
python setup.py sdist
Of course, before you release your project to PyPI, you'll want to add a bit
more information to your setup script to help people find or learn about your
......@@ -1467,7 +1473,7 @@ are included in any source distribution you build. This is a big improvement
over having to manually write a ``MANIFEST.in`` file and try to keep it in
sync with your project. So, if you are using CVS or Subversion, and your
source distributions only need to include files that you're tracking in
revision control, don't create a a ``MANIFEST.in`` file for your project.
revision control, don't create a ``MANIFEST.in`` file for your project.
(And, if you already have one, you might consider deleting it the next time
you would otherwise have to change it.)
......@@ -2595,8 +2601,8 @@ those methods' docstrings for more details.
Adding Support for Other Revision Control Systems
-------------------------------------------------
If you would like to create a plugin for ``setuptools`` to find files in other
source control systems besides CVS and Subversion, you can do so by adding an
If you would like to create a plugin for ``setuptools`` to find files in
source control systems, you can do so by adding an
entry point to the ``setuptools.file_finders`` group. The entry point should
be a function accepting a single directory name, and should yield
all the filenames within that directory (and any subdirectories thereof) that
......@@ -2652,9 +2658,7 @@ Subclassing ``Command``
-----------------------
Sorry, this section isn't written yet, and neither is a lot of what's below
this point, except for the change log. You might want to `subscribe to changes
in this page <setuptools?action=subscribe>`_ to see when new documentation is
added or updated.
this point.
XXX
......
================================
Using Setuptools in your project
================================
To use Setuptools in your project, the recommended way is to ship
`ez_setup.py` alongside your `setup.py` script and call
it at the very beginning of `setup.py` like this::
from ez_setup import use_setuptools
use_setuptools()
More info on `ez_setup.py` can be found at `the project home page
<https://pypy.python.org/pypi/setuptools>`_.
This diff is collapsed.
"""
Sphinx plugin to add links to the changelog.
"""
import re
import os
link_patterns = [
r"(Issue )?#(?P<issue>\d+)",
r"Pull Request ?#(?P<pull_request>\d+)",
r"Distribute #(?P<distribute>\d+)",
r"Buildout #(?P<buildout>\d+)",
r"Old Setuptools #(?P<old_setuptools>\d+)",
r"Jython #(?P<jython>\d+)",
r"Python #(?P<python>\d+)",
r"Interop #(?P<interop>\d+)",
]
issue_urls = dict(
pull_request='https://bitbucket.org'
'/pypa/setuptools/pull-request/{pull_request}',
issue='https://bitbucket.org/pypa/setuptools/issue/{issue}',
distribute='https://bitbucket.org/tarek/distribute/issue/{distribute}',
buildout='https://github.com/buildout/buildout/issues/{buildout}',
old_setuptools='http://bugs.python.org/setuptools/issue{old_setuptools}',
jython='http://bugs.jython.org/issue{jython}',
python='http://bugs.python.org/issue{python}',
interop='https://github.com/pypa/interoperability-peps/issues/{interop}',
)
def _linkify(source, dest):
pattern = '|'.join(link_patterns)
with open(source) as source:
out = re.sub(pattern, replacer, source.read())
with open(dest, 'w') as dest:
dest.write(out)
def replacer(match):
text = match.group(0)
match_dict = match.groupdict()
for key in match_dict:
if match_dict[key]:
url = issue_urls[key].format(**match_dict)
return "`{text} <{url}>`_".format(text=text, url=url)
def setup(app):
_linkify('CHANGES.txt', 'CHANGES (links).txt')
app.connect('build-finished', remove_file)
def remove_file(app, exception):
os.remove('CHANGES (links).txt')
This diff is collapsed.
......@@ -22,7 +22,7 @@ __title__ = "packaging"
__summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging"
__version__ = "15.0"
__version__ = "15.3"
__author__ = "Donald Stufft"
__email__ = "donald@stufft.io"
......
......@@ -151,6 +151,14 @@ class _IndividualSpecifier(BaseSpecifier):
version = parse(version)
return version
@property
def operator(self):
return self._spec[0]
@property
def version(self):
return self._spec[1]
@property
def prereleases(self):
return self._prereleases
......@@ -159,6 +167,9 @@ class _IndividualSpecifier(BaseSpecifier):
def prereleases(self, value):
self._prereleases = value
def __contains__(self, item):
return self.contains(item)
def contains(self, item, prereleases=None):
# Determine if prereleases are to be allowed or not.
if prereleases is None:
......@@ -176,7 +187,7 @@ class _IndividualSpecifier(BaseSpecifier):
# Actually do the comparison to determine if this item is contained
# within this Specifier or not.
return self._get_operator(self._spec[0])(item, self._spec[1])
return self._get_operator(self.operator)(item, self.version)
def filter(self, iterable, prereleases=None):
yielded = False
......@@ -526,7 +537,7 @@ class Specifier(_IndividualSpecifier):
# operators, and if they are if they are including an explicit
# prerelease.
operator, version = self._spec
if operator in ["==", ">=", "<=", "~="]:
if operator in ["==", ">=", "<=", "~=", "==="]:
# The == specifier can include a trailing .*, if it does we
# want to remove before parsing.
if operator == "==" and version.endswith(".*"):
......@@ -666,6 +677,12 @@ class SpecifierSet(BaseSpecifier):
return self._specs != other._specs
def __len__(self):
return len(self._specs)
def __iter__(self):
return iter(self._specs)
@property
def prereleases(self):
# If we have been given an explicit prerelease modifier, then we'll
......@@ -673,42 +690,43 @@ class SpecifierSet(BaseSpecifier):
if self._prereleases is not None:
return self._prereleases
# If we don't have any specifiers, and we don't have a forced value,
# then we'll just return None since we don't know if this should have
# pre-releases or not.
if not self._specs:
return None
# Otherwise we'll see if any of the given specifiers accept
# prereleases, if any of them do we'll return True, otherwise False.
# Note: The use of any() here means that an empty set of specifiers
# will always return False, this is an explicit design decision.
return any(s.prereleases for s in self._specs)
@prereleases.setter
def prereleases(self, value):
self._prereleases = value
def __contains__(self, item):
return self.contains(item)
def contains(self, item, prereleases=None):
# Ensure that our item is a Version or LegacyVersion instance.
if not isinstance(item, (LegacyVersion, Version)):
item = parse(item)
# Determine if we're forcing a prerelease or not, if we're not forcing
# one for this particular filter call, then we'll use whatever the
# SpecifierSet thinks for whether or not we should support prereleases.
if prereleases is None:
prereleases = self.prereleases
# We can determine if we're going to allow pre-releases by looking to
# see if any of the underlying items supports them. If none of them do
# and this item is a pre-release then we do not allow it and we can
# short circuit that here.
# Note: This means that 1.0.dev1 would not be contained in something
# like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0
if (not (self.prereleases or prereleases)) and item.is_prerelease:
if not prereleases and item.is_prerelease:
return False
# Determine if we're forcing a prerelease or not, we bypass
# self.prereleases here and use self._prereleases because we want to
# only take into consideration actual *forced* values. The underlying
# specifiers will handle the other logic.
# The logic here is: If prereleases is anything but None, we'll just
# go aheand and continue to use that. However if
# prereleases is None, then we'll use whatever the
# value of self._prereleases is as long as it is not
# None itself.
if prereleases is None and self._prereleases is not None:
prereleases = self._prereleases
# We simply dispatch to the underlying specs here to make sure that the
# given version is contained within all of them.
# Note: This use of all() here means that an empty set of specifiers
......@@ -719,24 +737,18 @@ class SpecifierSet(BaseSpecifier):
)
def filter(self, iterable, prereleases=None):
# Determine if we're forcing a prerelease or not, we bypass
# self.prereleases here and use self._prereleases because we want to
# only take into consideration actual *forced* values. The underlying
# specifiers will handle the other logic.
# The logic here is: If prereleases is anything but None, we'll just
# go aheand and continue to use that. However if
# prereleases is None, then we'll use whatever the
# value of self._prereleases is as long as it is not
# None itself.
if prereleases is None and self._prereleases is not None:
prereleases = self._prereleases
# Determine if we're forcing a prerelease or not, if we're not forcing
# one for this particular filter call, then we'll use whatever the
# SpecifierSet thinks for whether or not we should support prereleases.
if prereleases is None:
prereleases = self.prereleases
# If we have any specifiers, then we want to wrap our iterable in the
# filter method for each one, this will act as a logical AND amongst
# each specifier.
if self._specs:
for spec in self._specs:
iterable = spec.filter(iterable, prereleases=prereleases)
iterable = spec.filter(iterable, prereleases=bool(prereleases))
return iterable
# If we do not have any specifiers, then we need to have a rough filter
# which will filter out any pre-releases, unless there are no final
......
......@@ -324,6 +324,8 @@ def _parse_letter_version(letter, number):
letter = "b"
elif letter in ["c", "pre", "preview"]:
letter = "rc"
elif letter in ["rev", "r"]:
letter = "post"
return letter, int(number)
if not letter and number:
......
......@@ -210,8 +210,7 @@ working set triggers a ``pkg_resources.VersionConflict`` error:
>>> try:
... ws.find(Requirement.parse("Bar==1.0"))
... except pkg_resources.VersionConflict:
... exc = sys.exc_info()[1]
... except pkg_resources.VersionConflict as exc:
... print(str(exc))
... else:
... raise AssertionError("VersionConflict was not raised")
......@@ -365,9 +364,6 @@ Environment Markers
>>> print(im("'x'=='x' or os.open('foo')=='y'")) # no short-circuit!
Language feature not supported in environment markers
>>> print(im("'x' < 'y'"))
'<' operator not allowed in environment markers
>>> print(im("'x' < 'y' < 'z'"))
Chained comparison not allowed in environment markers
......@@ -418,3 +414,12 @@ Environment Markers
>>> em("'yx' in 'x'")
False
>>> em("python_version >= '2.6'")
True
>>> em("python_version > '2.5'")
True
>>> im("platform_python_implementation=='CPython'")
False
try:
import unittest.mock as mock
except ImportError:
import mock
from pkg_resources import evaluate_marker
@mock.patch.dict('pkg_resources.MarkerEvaluation.values',
python_full_version=mock.Mock(return_value='2.7.10'))
def test_lexicographic_ordering():
"""
Although one might like 2.7.10 to be greater than 2.7.3,
the marker spec only supports lexicographic ordering.
"""
assert evaluate_marker("python_full_version > '2.7.3'") is False
# coding: utf-8
from __future__ import unicode_literals
import sys
import tempfile
import os
......@@ -5,9 +8,15 @@ import zipfile
import datetime
import time
import subprocess
import stat
import distutils.dist
import distutils.command.install_egg_info
import pytest
import pkg_resources
try:
unicode
except NameError:
......@@ -109,3 +118,50 @@ class TestIndependence:
)
cmd = [sys.executable, '-c', '; '.join(lines)]
subprocess.check_call(cmd)
class TestDeepVersionLookupDistutils(object):
@pytest.fixture
def env(self, tmpdir):
"""
Create a package environment, similar to a virtualenv,
in which packages are installed.
"""
class Environment(str):
pass
env = Environment(tmpdir)
tmpdir.chmod(stat.S_IRWXU)
subs = 'home', 'lib', 'scripts', 'data', 'egg-base'
env.paths = dict(
(dirname, str(tmpdir / dirname))
for dirname in subs
)
list(map(os.mkdir, env.paths.values()))
return env
def create_foo_pkg(self, env, version):
"""
Create a foo package installed (distutils-style) to env.paths['lib']
as version.
"""
ld = "This package has unicode metadata! ❄"
attrs = dict(name='foo', version=version, long_description=ld)
dist = distutils.dist.Distribution(attrs)
iei_cmd = distutils.command.install_egg_info.install_egg_info(dist)
iei_cmd.initialize_options()
iei_cmd.install_dir = env.paths['lib']
iei_cmd.finalize_options()
iei_cmd.run()
def test_version_resolved_from_egg_info(self, env):
version = '1.11.0.dev0+2329eae'
self.create_foo_pkg(env, version)
# this requirement parsing will raise a VersionConflict unless the
# .egg-info file is parsed (see #419 on BitBucket)
req = pkg_resources.Requirement.parse('foo>=1.9')
dist = pkg_resources.WorkingSet([env.paths['lib']]).find(req)
assert dist.version == version
......@@ -2,6 +2,7 @@ import os
import sys
import tempfile
import shutil
import string
import pytest
......@@ -25,21 +26,23 @@ def safe_repr(obj, short=False):
return result
return result[:pkg_resources._MAX_LENGTH] + ' [truncated]...'
class Metadata(pkg_resources.EmptyProvider):
"""Mock object to return metadata as if from an on-disk distribution"""
def __init__(self,*pairs):
def __init__(self, *pairs):
self.metadata = dict(pairs)
def has_metadata(self,name):
def has_metadata(self, name):
return name in self.metadata
def get_metadata(self,name):
def get_metadata(self, name):
return self.metadata[name]
def get_metadata_lines(self,name):
def get_metadata_lines(self, name):
return pkg_resources.yield_lines(self.get_metadata(name))
dist_from_fn = pkg_resources.Distribution.from_filename
class TestDistro:
......@@ -174,9 +177,12 @@ class TestDistro:
# Activation list now includes resolved dependency
assert list(ws.resolve(parse_requirements("Foo[bar]"), ad)) ==[Foo,Baz]
# Requests for conflicting versions produce VersionConflict
with pytest.raises(VersionConflict):
with pytest.raises(VersionConflict) as vc:
ws.resolve(parse_requirements("Foo==1.2\nFoo!=1.2"), ad)
msg = 'Foo 0.9 is installed but Foo==1.2 is required'
assert vc.value.report() == msg
def testDistroDependsOptions(self):
d = self.distRequires("""
Twisted>=1.5
......@@ -204,6 +210,49 @@ class TestDistro:
d.requires(["foo"])
class TestWorkingSet:
def test_find_conflicting(self):
ws = WorkingSet([])
Foo = Distribution.from_filename("/foo_dir/Foo-1.2.egg")
ws.add(Foo)
# create a requirement that conflicts with Foo 1.2
req = next(parse_requirements("Foo<1.2"))
with pytest.raises(VersionConflict) as vc:
ws.find(req)
msg = 'Foo 1.2 is installed but Foo<1.2 is required'
assert vc.value.report() == msg
def test_resolve_conflicts_with_prior(self):
"""
A ContextualVersionConflict should be raised when a requirement
conflicts with a prior requirement for a different package.
"""
# Create installation where Foo depends on Baz 1.0 and Bar depends on
# Baz 2.0.
ws = WorkingSet([])
md = Metadata(('depends.txt', "Baz==1.0"))
Foo = Distribution.from_filename("/foo_dir/Foo-1.0.egg", metadata=md)
ws.add(Foo)
md = Metadata(('depends.txt', "Baz==2.0"))
Bar = Distribution.from_filename("/foo_dir/Bar-1.0.egg", metadata=md)
ws.add(Bar)
Baz = Distribution.from_filename("/foo_dir/Baz-1.0.egg")
ws.add(Baz)
Baz = Distribution.from_filename("/foo_dir/Baz-2.0.egg")
ws.add(Baz)
with pytest.raises(VersionConflict) as vc:
ws.resolve(parse_requirements("Foo\nBar\n"))
msg = "Baz 1.0 is installed but Baz==2.0 is required by {'Bar'}"
if pkg_resources.PY2:
msg = msg.replace("{'Bar'}", "set(['Bar'])")
assert vc.value.report() == msg
class TestEntryPoints:
def assertfields(self, ep):
......@@ -212,10 +261,8 @@ class TestEntryPoints:
assert ep.attrs == ("TestEntryPoints",)
assert ep.extras == ("x",)
assert ep.load() is TestEntryPoints
assert (
str(ep) ==
"foo = pkg_resources.tests.test_resources:TestEntryPoints [x]"
)
expect = "foo = pkg_resources.tests.test_resources:TestEntryPoints [x]"
assert str(ep) == expect
def setup_method(self, method):
self.dist = Distribution.from_filename(
......@@ -250,13 +297,21 @@ class TestEntryPoints:
ep = EntryPoint.parse(spec)
assert ep.name == 'html+mako'
def testRejects(self):
for ep in [
"foo", "x=1=2", "x=a:b:c", "q=x/na", "fez=pish:tush-z", "x=f[a]>2",
]:
try: EntryPoint.parse(ep)
except ValueError: pass
else: raise AssertionError("Should've been bad", ep)
reject_specs = "foo", "x=a:b:c", "q=x/na", "fez=pish:tush-z", "x=f[a]>2"
@pytest.mark.parametrize("reject_spec", reject_specs)
def test_reject_spec(self, reject_spec):
with pytest.raises(ValueError):
EntryPoint.parse(reject_spec)
def test_printable_name(self):
"""
Allow any printable character in the name.
"""
# Create a name with all printable characters; strip the whitespace.
name = string.printable.strip()
spec = "{name} = module:attr".format(**locals())
ep = EntryPoint.parse(spec)
assert ep.name == name
def checkSubMap(self, m):
assert len(m) == len(self.submap_expect)
......@@ -595,10 +650,8 @@ class TestNamespaces:
pkg2_init.close()
import pkg1
assert "pkg1" in pkg_resources._namespace_packages
try:
import pkg1.pkg2
except ImportError:
self.fail("Setuptools tried to import the parent namespace package")
# attempt to import pkg2 from site-pkgs2
import pkg1.pkg2
# check the _namespace_packages dict
assert "pkg1.pkg2" in pkg_resources._namespace_packages
assert pkg_resources._namespace_packages["pkg1"] == ["pkg1.pkg2"]
......
[pytest]
addopts=--doctest-modules --ignore release.py --ignore setuptools/lib2to3_ex.py --ignore tests/manual_test.py --ignore tests/shlib_test --doctest-glob=pkg_resources/api_tests.txt --ignore scripts/upload-old-releases-as-zip.py
norecursedirs=dist build *.egg
......@@ -4,24 +4,13 @@ install jaraco.packaging and run 'python -m jaraco.packaging.release'
"""
import os
import subprocess
import pkg_resources
pkg_resources.require('jaraco.packaging>=2.0')
pkg_resources.require('wheel')
def before_upload():
BootstrapBookmark.add()
def after_push():
BootstrapBookmark.push()
files_with_versions = (
'ez_setup.py', 'setuptools/version.py',
)
files_with_versions = 'setuptools/version.py',
# bdist_wheel must be included or pip will break
dist_commands = 'sdist', 'bdist_wheel'
......@@ -29,22 +18,3 @@ dist_commands = 'sdist', 'bdist_wheel'
test_info = "Travis-CI tests: http://travis-ci.org/#!/jaraco/setuptools"
os.environ["SETUPTOOLS_INSTALL_WINDOWS_SPECIFIC_FILES"] = "1"
class BootstrapBookmark:
name = 'bootstrap'
@classmethod
def add(cls):
cmd = ['hg', 'bookmark', '-i', cls.name, '-f']
subprocess.Popen(cmd)
@classmethod
def push(cls):
"""
Push the bootstrap bookmark
"""
push_command = ['hg', 'push', '-B', cls.name]
# don't use check_call here because mercurial will return a non-zero
# code even if it succeeds at pushing the bookmark (because there are
# no changesets to be pushed). !dm mercurial
subprocess.call(push_command)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# declare and require dependencies
__requires__ = [
'twine',
]; __import__('pkg_resources')
import errno
import glob
import hashlib
import json
import os
import shutil
import tarfile
import codecs
import urllib.request
import urllib.parse
import urllib.error
from distutils.version import LooseVersion
from twine.commands import upload
OK = '\033[92m'
FAIL = '\033[91m'
END = '\033[0m'
DISTRIBUTION = "setuptools"
class SetuptoolsOldReleasesWithoutZip:
"""docstring for SetuptoolsOldReleases"""
def __init__(self):
self.dirpath = './dist'
os.makedirs(self.dirpath, exist_ok=True)
print("Downloading %s releases..." % DISTRIBUTION)
print("All releases will be downloaded to %s" % self.dirpath)
self.data_json_setuptools = self.get_json_data(DISTRIBUTION)
self.valid_releases_numbers = sorted([
release
for release in self.data_json_setuptools['releases']
# This condition is motivated by 13.0 release, which
# comes as "13.0": [], in the json
if self.data_json_setuptools['releases'][release]
], key=LooseVersion)
self.total_downloaded_ok = 0
def get_json_data(self, package_name):
"""
"releases": {
"0.7.2": [
{
"has_sig": false,
"upload_time": "2013-06-09T16:10:00",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/s/setuptools/setuptools-0.7.2.tar.gz", # NOQA
"md5_digest": "de44cd90f8a1c713d6c2bff67bbca65d",
"downloads": 159014,
"filename": "setuptools-0.7.2.tar.gz",
"packagetype": "sdist",
"size": 633077
}
],
"0.7.3": [
{
"has_sig": false,
"upload_time": "2013-06-18T21:08:56",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/s/setuptools/setuptools-0.7.3.tar.gz", # NOQA
"md5_digest": "c854adacbf9067d330a847f06f7a8eba",
"downloads": 30594,
"filename": "setuptools-0.7.3.tar.gz",
"packagetype": "sdist",
"size": 751152
}
],
"12.3": [
{
"has_sig": false,
"upload_time": "2015-02-26T19:15:51",
"comment_text": "",
"python_version": "3.4",
"url": "https://pypi.python.org/packages/3.4/s/setuptools/setuptools-12.3-py2.py3-none-any.whl", # NOQA
"md5_digest": "31f51a38497a70efadf5ce8d4c2211ab",
"downloads": 288451,
"filename": "setuptools-12.3-py2.py3-none-any.whl",
"packagetype": "bdist_wheel",
"size": 501904
},
{
"has_sig": false,
"upload_time": "2015-02-26T19:15:43",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/s/setuptools/setuptools-12.3.tar.gz", # NOQA
"md5_digest": "67614b6d560fa4f240e99cd553ec7f32",
"downloads": 110109,
"filename": "setuptools-12.3.tar.gz",
"packagetype": "sdist",
"size": 635025
},
{
"has_sig": false,
"upload_time": "2015-02-26T19:15:47",
"comment_text": "",
"python_version": "source",
"url": "https://pypi.python.org/packages/source/s/setuptools/setuptools-12.3.zip", # NOQA
"md5_digest": "abc799e7db6e7281535bf342bfc41a12",
"downloads": 67539,
"filename": "setuptools-12.3.zip",
"packagetype": "sdist",
"size": 678783
}
],
"""
url = "https://pypi.python.org/pypi/%s/json" % (package_name,)
resp = urllib.request.urlopen(urllib.request.Request(url))
charset = resp.info().get_content_charset()
reader = codecs.getreader(charset)(resp)
data = json.load(reader)
# Mainly for debug.
json_filename = "%s/%s.json" % (self.dirpath, DISTRIBUTION)
with open(json_filename, 'w') as outfile:
json.dump(
data,
outfile,
sort_keys=True,
indent=4,
separators=(',', ': '),
)
return data
def get_setuptools_releases_without_zip_counterpart(self):
# Get set(all_valid_releases) - set(releases_with_zip), so now we have
# the releases without zip.
return set(self.valid_releases_numbers) - set([
release
for release in self.valid_releases_numbers
for same_version_release_dict in self.data_json_setuptools['releases'][release] # NOQA
if 'zip' in same_version_release_dict['filename']
])
def download_setuptools_releases_without_zip_counterpart(self):
try:
releases_without_zip = self.get_setuptools_releases_without_zip_counterpart() # NOQA
failed_md5_releases = []
# This is a "strange" loop, going through all releases and
# testing only the release I need to download, but I thought it
# would be mouch more readable than trying to iterate through
# releases I need and get into traverse hell values inside dicts
# inside dicts of the json to get the distribution's url to
# download.
for release in self.valid_releases_numbers:
if release in releases_without_zip:
for same_version_release_dict in self.data_json_setuptools['releases'][release]: # NOQA
if 'tar.gz' in same_version_release_dict['filename']:
print("Downloading %s..." % release)
local_file = '%s/%s' % (
self.dirpath,
same_version_release_dict["filename"]
)
urllib.request.urlretrieve(
same_version_release_dict["url"],
local_file
)
targz = open(local_file, 'rb').read()
hexdigest = hashlib.md5(targz).hexdigest()
if (hexdigest != same_version_release_dict['md5_digest']): # NOQA
print(FAIL + "FAIL: md5 for %s didn't match!" % release + END) # NOQA
failed_md5_releases.append(release)
else:
self.total_downloaded_ok += 1
print('Total releases without zip: %s' % len(releases_without_zip))
print('Total downloaded: %s' % self.total_downloaded_ok)
if failed_md5_releases:
msg = FAIL + (
"FAIL: these releases %s failed the md5 check!" %
','.join(failed_md5_releases)
) + END
raise Exception(msg)
elif self.total_downloaded_ok != len(releases_without_zip):
msg = FAIL + (
"FAIL: Unknown error occured. Please check the logs."
) + END
raise Exception(msg)
else:
print(OK + "All releases downloaded and md5 checked." + END)
except OSError as e:
if e.errno != errno.EEXIST:
raise e
def convert_targz_to_zip(self):
print("Converting the tar.gz to zip...")
files = glob.glob('%s/*.tar.gz' % self.dirpath)
total_converted = 0
for targz in sorted(files, key=LooseVersion):
# Extract and remove tar.
tar = tarfile.open(targz)
tar.extractall(path=self.dirpath)
tar.close()
os.remove(targz)
# Zip the extracted tar.
setuptools_folder_path = targz.replace('.tar.gz', '')
setuptools_folder_name = setuptools_folder_path.split("/")[-1]
print(setuptools_folder_name)
shutil.make_archive(
setuptools_folder_path,
'zip',
self.dirpath,
setuptools_folder_name
)
# Exclude extracted tar folder.
shutil.rmtree(setuptools_folder_path.replace('.zip', ''))
total_converted += 1
print('Total converted: %s' % total_converted)
if self.total_downloaded_ok != total_converted:
msg = FAIL + (
"FAIL: Total number of downloaded releases is different"
" from converted ones. Please check the logs."
) + END
raise Exception(msg)
print("Done with the tar.gz->zip. Check folder %s." % main.dirpath)
def upload_zips_to_pypi(self):
print('Uploading to pypi...')
zips = sorted(glob.glob('%s/*.zip' % self.dirpath), key=LooseVersion)
print("simulated upload of", zips); return
upload.upload(dists=zips)
if __name__ == '__main__':
main = SetuptoolsOldReleasesWithoutZip()
main.download_setuptools_releases_without_zip_counterpart()
main.convert_targz_to_zip()
main.upload_zips_to_pypi()
......@@ -5,6 +5,7 @@ tag_build = dev
release = egg_info -RDb ''
source = register sdist binary
binary = bdist_egg upload --show-response
test = pytest
[build_sphinx]
source-dir = docs/
......@@ -19,7 +20,3 @@ formats = gztar zip
[wheel]
universal=1
[pytest]
addopts=--doctest-modules --ignore release.py --ignore setuptools/lib2to3_ex.py --ignore tests/manual_test.py --ignore tests/shlib_test --doctest-glob=pkg_resources/api_tests.txt
norecursedirs=dist build *.egg
#!/usr/bin/env python
"""Distutils setup file, used to install or test 'setuptools'"""
"""
Distutils setup file, used to install or test 'setuptools'
"""
import io
import os
import sys
......@@ -25,7 +28,6 @@ with open(ver_path) as ver_file:
exec(ver_file.read(), main_ns)
import setuptools
from setuptools.command.build_py import build_py as _build_py
scripts = []
......@@ -46,20 +48,6 @@ def _gen_console_scripts():
console_scripts = list(_gen_console_scripts())
# specific command that is used to generate windows .exe files
class build_py(_build_py):
def build_package_data(self):
"""Copy data files into build directory"""
for package, src_dir, build_dir, filenames in self.data_files:
for filename in filenames:
target = os.path.join(build_dir, filename)
self.mkpath(os.path.dirname(target))
srcfile = os.path.join(src_dir, filename)
outf, copied = self.copy_file(srcfile, target)
srcfile = os.path.abspath(srcfile)
readme_file = io.open('README.txt', encoding='utf-8')
with readme_file:
......@@ -75,7 +63,10 @@ if sys.platform == 'win32' or force_windows_specific_files:
package_data.setdefault('setuptools', []).extend(['*.exe'])
package_data.setdefault('setuptools.command', []).extend(['*.xml'])
pytest_runner = ['pytest-runner'] if 'ptr' in sys.argv else []
needs_pytest = set(['ptr', 'pytest', 'test']).intersection(sys.argv)
pytest_runner = ['pytest-runner'] if needs_pytest else []
needs_sphinx = set(['build_sphinx', 'upload_docs']).intersection(sys.argv)
sphinx = ['sphinx', 'rst.linker'] if needs_sphinx else []
setup_params = dict(
name="setuptools",
......@@ -89,7 +80,7 @@ setup_params = dict(
keywords="CPAN PyPI distutils eggs package management",
url="https://bitbucket.org/pypa/setuptools",
src_root=src_root,
packages=setuptools.find_packages(),
packages=setuptools.find_packages(exclude=['*.tests']),
package_data=package_data,
py_modules=['easy_install'],
......@@ -149,10 +140,9 @@ setup_params = dict(
Programming Language :: Python :: 2.6
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.1
Programming Language :: Python :: 3.2
Programming Language :: Python :: 3.3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
Topic :: Software Development :: Libraries :: Python Modules
Topic :: System :: Archiving :: Packaging
Topic :: System :: Systems Administration
......@@ -163,20 +153,19 @@ setup_params = dict(
],
extras_require={
"ssl:sys_platform=='win32'": "wincertstore==0.2",
"certs": "certifi==1.0.1",
"certs": "certifi==2015.11.20",
},
dependency_links=[
'https://pypi.python.org/packages/source/c/certifi/certifi-1.0.1.tar.gz#md5=45f5cb94b8af9e1df0f9450a8f61b790',
'https://pypi.python.org/packages/source/c/certifi/certifi-2015.11.20.tar.gz#md5=25134646672c695c1ff1593c2dd75d08',
'https://pypi.python.org/packages/source/w/wincertstore/wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2',
],
scripts=[],
tests_require=[
'setuptools[ssl]',
'pytest',
'mock',
],
'pytest>=2.8',
] + (['mock'] if sys.version_info[:2] < (3, 3) else []),
setup_requires=[
] + pytest_runner,
] + sphinx + pytest_runner,
)
if __name__ == '__main__':
......
......@@ -3,6 +3,7 @@
__import__('setuptools.bootstrap').bootstrap.ensure_deps()
import os
import functools
import distutils.core
import distutils.filelist
from distutils.core import Command as _Command
......@@ -76,21 +77,24 @@ class PackageFinder(object):
yield pkg
@staticmethod
def _all_dirs(base_path):
def _candidate_dirs(base_path):
"""
Return all dirs in base_path, relative to base_path
Return all dirs in base_path that might be packages.
"""
has_dot = lambda name: '.' in name
for root, dirs, files in os.walk(base_path, followlinks=True):
# Exclude directories that contain a period, as they cannot be
# packages. Mutate the list to avoid traversal.
dirs[:] = filterfalse(has_dot, dirs)
for dir in dirs:
yield os.path.relpath(os.path.join(root, dir), base_path)
@classmethod
def _find_packages_iter(cls, base_path):
dirs = cls._all_dirs(base_path)
suitable = filterfalse(lambda n: '.' in n, dirs)
candidates = cls._candidate_dirs(base_path)
return (
path.replace(os.path.sep, '.')
for path in suitable
for path in candidates
if cls._looks_like_package(os.path.join(base_path, path))
)
......@@ -123,30 +127,45 @@ class Command(_Command):
command_consumes_arguments = False
def __init__(self, dist, **kw):
# Add support for keyword arguments
_Command.__init__(self,dist)
for k,v in kw.items():
setattr(self,k,v)
"""
Construct the command for dist, updating
vars(self) with any keyword parameters.
"""
_Command.__init__(self, dist)
vars(self).update(kw)
def reinitialize_command(self, command, reinit_subcommands=0, **kw):
cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
for k,v in kw.items():
setattr(cmd,k,v) # update command with keywords
vars(cmd).update(kw)
return cmd
distutils.core.Command = Command # we can't patch distutils.cmd, alas
# we can't patch distutils.cmd, alas
distutils.core.Command = Command
def _find_all_simple(path):
"""
Find all files under 'path'
"""
results = (
os.path.join(base, file)
for base, dirs, files in os.walk(path, followlinks=True)
for file in files
)
return filter(os.path.isfile, results)
def findall(dir = os.curdir):
"""Find all files under 'dir' and return the list of full filenames
(relative to 'dir').
def findall(dir=os.curdir):
"""
Find all files under 'dir' and return the list of full filenames.
Unless dir is '.', return full filenames with dir prepended.
"""
all_files = []
for base, dirs, files in os.walk(dir, followlinks=True):
if base==os.curdir or base.startswith(os.curdir+os.sep):
base = base[2:]
if base:
files = [os.path.join(base, f) for f in files]
all_files.extend(filter(os.path.isfile, files))
return all_files
distutils.filelist.findall = findall # fix findall bug in distutils.
files = _find_all_simple(dir)
if dir == os.curdir:
make_rel = functools.partial(os.path.relpath, start=dir)
files = map(make_rel, files)
return list(files)
# fix findall bug in distutils (http://bugs.python.org/issue12885)
distutils.filelist.findall = findall
......@@ -2,7 +2,6 @@
Build .egg distributions"""
# This module should be kept compatible with Python 2.3
from distutils.errors import DistutilsSetupError
from distutils.dir_util import remove_tree, mkpath
from distutils import log
......@@ -407,10 +406,6 @@ def scan_module(egg_dir, base, name, stubs):
if bad in symbols:
log.warn("%s: module MAY be using inspect.%s", module, bad)
safe = False
if '__name__' in symbols and '__main__' in symbols and '.' not in module:
if sys.version[:3] == "2.4": # -m works w/zipfiles in 2.5
log.warn("%s: top-level module may be 'python -m' script", module)
safe = False
return safe
......@@ -442,7 +437,7 @@ INSTALL_DIRECTORY_ATTRS = [
]
def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=None,
def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=True,
mode='w'):
"""Create a zip file from all the files under 'base_dir'. The output
zip file will be named 'base_dir' + ".zip". Uses either the "zipfile"
......@@ -464,11 +459,7 @@ def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=None,
z.write(path, p)
log.debug("adding '%s'" % p)
if compress is None:
# avoid 2.3 zipimport bug when 64 bits
compress = (sys.version >= "2.4")
compression = [zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED][bool(compress)]
compression = zipfile.ZIP_DEFLATED if compress else zipfile.ZIP_STORED
if not dry_run:
z = zipfile.ZipFile(zip_filename, mode, compression=compression)
for dirname, dirs, files in os.walk(base_dir):
......
......@@ -11,8 +11,8 @@ import itertools
from setuptools.extension import Library
try:
# Attempt to use Pyrex for building extensions, if available
from Pyrex.Distutils.build_ext import build_ext as _build_ext
# Attempt to use Cython for building extensions, if available
from Cython.Distutils.build_ext import build_ext as _build_ext
except ImportError:
_build_ext = _du_build_ext
......@@ -42,7 +42,6 @@ elif os.name != 'nt':
if_dl = lambda s: s if have_rtld else ''
class build_ext(_build_ext):
def run(self):
"""Build extensions in build directory, then copy if --inplace"""
......@@ -74,15 +73,6 @@ class build_ext(_build_ext):
if ext._needs_stub:
self.write_stub(package_dir or os.curdir, ext, True)
if _build_ext is not _du_build_ext and not hasattr(_build_ext,
'pyrex_sources'):
# Workaround for problems using some Pyrex versions w/SWIG and/or 2.4
def swig_sources(self, sources, *otherargs):
# first do any Pyrex processing
sources = _build_ext.swig_sources(self, sources) or sources
# Then do any actual SWIG stuff on the remainder
return _du_build_ext.swig_sources(self, sources, *otherargs)
def get_ext_filename(self, fullname):
filename = _build_ext.get_ext_filename(self, fullname)
if fullname in self.ext_map:
......@@ -176,6 +166,7 @@ class build_ext(_build_ext):
return _build_ext.get_export_symbols(self, ext)
def build_extension(self, ext):
ext._convert_pyx_sources_to_lang()
_compiler = self.compiler
try:
if isinstance(ext, Library):
......
......@@ -2,9 +2,13 @@ from glob import glob
from distutils.util import convert_path
import distutils.command.build_py as orig
import os
import sys
import fnmatch
import textwrap
import io
import distutils.errors
import collections
import itertools
try:
from setuptools.lib2to3_ex import Mixin2to3
......@@ -136,22 +140,7 @@ class build_py(orig.build_py, Mixin2to3):
mf.setdefault(src_dirs[d], []).append(path)
def get_data_files(self):
pass # kludge 2.4 for lazy computation
if sys.version < "2.4": # Python 2.4 already has this code
def get_outputs(self, include_bytecode=1):
"""Return complete list of files copied to the build directory
This includes both '.py' files and data files, as well as '.pyc'
and '.pyo' files if 'include_bytecode' is true. (This method is
needed for the 'install_lib' command to do its job properly, and to
generate a correct installation manifest.)
"""
return orig.build_py.get_outputs(self, include_bytecode) + [
os.path.join(build_dir, filename)
for package, src_dir, build_dir, filenames in self.data_files
for filename in filenames
]
pass # Lazily compute data files in _get_data_files() function.
def check_package(self, package, package_dir):
"""Check namespace packages' __init__ for declare_namespace"""
......@@ -172,17 +161,15 @@ class build_py(orig.build_py, Mixin2to3):
else:
return init_py
f = open(init_py, 'rbU')
if 'declare_namespace'.encode() not in f.read():
from distutils.errors import DistutilsError
raise DistutilsError(
with io.open(init_py, 'rb') as f:
contents = f.read()
if b'declare_namespace' not in contents:
raise distutils.errors.DistutilsError(
"Namespace package problem: %s is a namespace package, but "
"its\n__init__.py does not call declare_namespace()! Please "
'fix it.\n(See the setuptools manual under '
'"Namespace Packages" for details.)\n"' % (package,)
)
f.close()
return init_py
def initialize_options(self):
......@@ -197,20 +184,25 @@ class build_py(orig.build_py, Mixin2to3):
def exclude_data_files(self, package, src_dir, files):
"""Filter filenames for package's data files in 'src_dir'"""
globs = (self.exclude_package_data.get('', [])
+ self.exclude_package_data.get(package, []))
bad = []
for pattern in globs:
bad.extend(
fnmatch.filter(
files, os.path.join(src_dir, convert_path(pattern))
)
globs = (
self.exclude_package_data.get('', [])
+ self.exclude_package_data.get(package, [])
)
bad = set(
item
for pattern in globs
for item in fnmatch.filter(
files,
os.path.join(src_dir, convert_path(pattern)),
)
bad = dict.fromkeys(bad)
seen = {}
)
seen = collections.defaultdict(itertools.count)
return [
f for f in files if f not in bad
and f not in seen and seen.setdefault(f, 1) # ditch dupes
fn
for fn in files
if fn not in bad
# ditch dupes
and not next(seen[fn])
]
......
......@@ -3,6 +3,7 @@ from distutils import log
from distutils.errors import DistutilsError, DistutilsOptionError
import os
import glob
import io
import six
......@@ -54,8 +55,8 @@ class develop(easy_install):
# pick up setup-dir .egg files only: no .egg-info
self.package_index.scan(glob.glob('*.egg'))
self.egg_link = os.path.join(self.install_dir, ei.egg_name +
'.egg-link')
egg_link_fn = ei.egg_name + '.egg-link'
self.egg_link = os.path.join(self.install_dir, egg_link_fn)
self.egg_base = ei.egg_base
if self.egg_path is None:
self.egg_path = os.path.abspath(ei.egg_base)
......@@ -125,9 +126,8 @@ class develop(easy_install):
# create an .egg-link in the installation dir, pointing to our egg
log.info("Creating %s (link to %s)", self.egg_link, self.egg_base)
if not self.dry_run:
f = open(self.egg_link, "w")
f.write(self.egg_path + "\n" + self.setup_path)
f.close()
with open(self.egg_link, "w") as f:
f.write(self.egg_path + "\n" + self.setup_path)
# postprocess the installed distro, fixing up .pth, installing scripts,
# and handling requirements
self.process_distribution(None, self.dist, not self.no_deps)
......@@ -164,7 +164,33 @@ class develop(easy_install):
for script_name in self.distribution.scripts or []:
script_path = os.path.abspath(convert_path(script_name))
script_name = os.path.basename(script_path)
f = open(script_path, 'rU')
script_text = f.read()
f.close()
with io.open(script_path) as strm:
script_text = strm.read()
self.install_script(dist, script_name, script_text, script_path)
def install_wrapper_scripts(self, dist):
dist = VersionlessRequirement(dist)
return easy_install.install_wrapper_scripts(self, dist)
class VersionlessRequirement(object):
"""
Adapt a pkg_resources.Distribution to simply return the project
name as the 'requirement' so that scripts will work across
multiple versions.
>>> dist = Distribution(project_name='foo', version='1.0')
>>> str(dist.as_requirement())
'foo==1.0'
>>> adapted_dist = VersionlessRequirement(dist)
>>> str(adapted_dist.as_requirement())
'foo'
"""
def __init__(self, dist):
self.__dist = dist
def __getattr__(self, name):
return getattr(self.__dist, name)
def as_requirement(self):
return self.project_name
This diff is collapsed.
......@@ -10,17 +10,17 @@ import distutils.filelist
import os
import re
import sys
import io
import warnings
import time
import six
try:
from setuptools_svn import svn_utils
except ImportError:
pass
from setuptools import Command
from setuptools.command.sdist import sdist
from setuptools.command.sdist import walk_revctrl
from setuptools.command.setopt import edit_config
from setuptools.command import bdist_egg
from pkg_resources import (
parse_requirements, safe_name, parse_version,
safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename)
......@@ -28,6 +28,12 @@ import setuptools.unicode_utils as unicode_utils
from pkg_resources import packaging
try:
from setuptools_svn import svn_utils
except ImportError:
pass
class egg_info(Command):
description = "create a distribution's .egg-info directory"
......@@ -59,8 +65,6 @@ class egg_info(Command):
self.vtags = None
def save_version_info(self, filename):
from setuptools.command.setopt import edit_config
values = dict(
egg_info=dict(
tag_svn_revision=0,
......@@ -169,7 +173,8 @@ class egg_info(Command):
self.mkpath(self.egg_info)
installer = self.distribution.fetch_build_egg
for ep in iter_entry_points('egg_info.writers'):
writer = ep.load(installer=installer)
ep.require(installer=installer)
writer = ep.resolve()
writer(self, ep.name, os.path.join(self.egg_info, ep.name))
# Get rid of native_libs.txt if it was put there by older bdist_egg
......@@ -184,12 +189,8 @@ class egg_info(Command):
if self.tag_build:
version += self.tag_build
if self.tag_svn_revision:
rev = self.get_svn_revision()
if rev: # is 0 if it's not an svn working copy
version += '-r%s' % rev
version += '-r%s' % self.get_svn_revision()
if self.tag_date:
import time
version += time.strftime("-%Y%m%d")
return version
......@@ -390,7 +391,6 @@ def write_pkg_info(cmd, basename, filename):
metadata.name, metadata.version = oldname, oldver
safe = getattr(cmd.distribution, 'zip_safe', None)
from setuptools.command import bdist_egg
bdist_egg.write_safety_flag(cmd.egg_info, safe)
......@@ -467,14 +467,15 @@ def write_entries(cmd, basename, filename):
def get_pkg_info_revision():
# See if we can get a -r### off of PKG-INFO, in case this is an sdist of
# a subversion revision
#
"""
Get a -r### off of PKG-INFO Version in case this is an sdist of
a subversion revision.
"""
warnings.warn("get_pkg_info_revision is deprecated.", DeprecationWarning)
if os.path.exists('PKG-INFO'):
f = open('PKG-INFO', 'rU')
for line in f:
match = re.match(r"Version:.*-r(\d+)\s*$", line)
if match:
return int(match.group(1))
f.close()
with io.open('PKG-INFO') as f:
for line in f:
match = re.match(r"Version:.*-r(\d+)\s*$", line)
if match:
return int(match.group(1))
return 0
......@@ -79,6 +79,8 @@ class install_lib(orig.install_lib):
base = os.path.join('__pycache__', '__init__.' + imp.get_tag())
yield base + '.pyc'
yield base + '.pyo'
yield base + '.opt-1.pyc'
yield base + '.opt-2.pyc'
def copy_tree(
self, infile, outfile,
......
......@@ -13,8 +13,7 @@ class install_scripts(orig.install_scripts):
self.no_ep = False
def run(self):
from setuptools.command.easy_install import get_script_args
from setuptools.command.easy_install import sys_executable
import setuptools.command.easy_install as ei
self.run_command("egg_info")
if self.distribution.scripts:
......@@ -31,11 +30,17 @@ class install_scripts(orig.install_scripts):
ei_cmd.egg_name, ei_cmd.egg_version,
)
bs_cmd = self.get_finalized_command('build_scripts')
executable = getattr(bs_cmd, 'executable', sys_executable)
is_wininst = getattr(
self.get_finalized_command("bdist_wininst"), '_is_running', False
)
for args in get_script_args(dist, executable, is_wininst):
exec_param = getattr(bs_cmd, 'executable', None)
bw_cmd = self.get_finalized_command("bdist_wininst")
is_wininst = getattr(bw_cmd, '_is_running', False)
writer = ei.ScriptWriter
if is_wininst:
exec_param = "python.exe"
writer = ei.WindowsScriptWriter
# resolve the writer to the environment
writer = writer.best()
cmd = writer.command_spec_class.best().from_param(exec_param)
for args in writer.get_args(dist, cmd.as_header()):
self.write_script(*args)
def write_script(self, script_name, contents, mode="t", *ignored):
......
......@@ -3,6 +3,7 @@ from distutils import log
import distutils.command.sdist as orig
import os
import sys
import io
import six
......@@ -71,7 +72,8 @@ class sdist(orig.sdist):
try:
orig.sdist.read_template(self)
except:
sys.exc_info()[2].tb_next.tb_frame.f_locals['template'].close()
_, _, tb = sys.exc_info()
tb.tb_next.tb_frame.f_locals['template'].close()
raise
# Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle
......@@ -166,11 +168,8 @@ class sdist(orig.sdist):
if not os.path.isfile(self.manifest):
return False
fp = open(self.manifest, 'rbU')
try:
with io.open(self.manifest, 'rb') as fp:
first_line = fp.readline()
finally:
fp.close()
return (first_line !=
'# file GENERATED by distutils, do NOT edit\n'.encode())
......
from distutils.errors import DistutilsOptionError
from unittest import TestLoader
import unittest
import sys
import six
......@@ -13,7 +12,7 @@ from setuptools.py31compat import unittest_main
class ScanningLoader(TestLoader):
def loadTestsFromModule(self, module):
def loadTestsFromModule(self, module, pattern=None):
"""Return a suite of all tests cases contained in the given module
If the module is a package, load tests from all the modules in it.
......@@ -43,6 +42,17 @@ class ScanningLoader(TestLoader):
return tests[0] # don't create a nested suite for only one return
# adapted from jaraco.classes.properties:NonDataProperty
class NonDataProperty(object):
def __init__(self, fget):
self.fget = fget
def __get__(self, obj, objtype=None):
if obj is None:
return self
return self.fget(obj)
class test(Command):
"""Command to run unit tests after in-place build"""
......@@ -63,20 +73,16 @@ class test(Command):
def finalize_options(self):
if self.test_suite and self.test_module:
msg = "You may specify a module or a suite, but not both"
raise DistutilsOptionError(msg)
if self.test_suite is None:
if self.test_module is None:
self.test_suite = self.distribution.test_suite
else:
self.test_suite = self.test_module + ".test_suite"
elif self.test_module:
raise DistutilsOptionError(
"You may specify a module or a suite, but not both"
)
self.test_args = [self.test_suite]
if self.verbose:
self.test_args.insert(0, '--verbose')
if self.test_loader is None:
self.test_loader = getattr(self.distribution, 'test_loader', None)
if self.test_loader is None:
......@@ -84,6 +90,16 @@ class test(Command):
if self.test_runner is None:
self.test_runner = getattr(self.distribution, 'test_runner', None)
@NonDataProperty
def test_args(self):
return list(self._test_args())
def _test_args(self):
if self.verbose:
yield '--verbose'
if self.test_suite:
yield self.test_suite
def with_project_on_sys_path(self, func):
with_2to3 = six.PY3 and getattr(self.distribution, 'use_2to3', False)
......@@ -134,20 +150,19 @@ class test(Command):
if self.distribution.tests_require:
self.distribution.fetch_build_eggs(self.distribution.tests_require)
if self.test_suite:
cmd = ' '.join(self.test_args)
if self.dry_run:
self.announce('skipping "unittest %s" (dry run)' % cmd)
else:
self.announce('running "unittest %s"' % cmd)
self.with_project_on_sys_path(self.run_tests)
cmd = ' '.join(self._argv)
if self.dry_run:
self.announce('skipping "%s" (dry run)' % cmd)
else:
self.announce('running "%s"' % cmd)
self.with_project_on_sys_path(self.run_tests)
def run_tests(self):
# Purge modules under test from sys.modules. The test loader will
# re-import them from the build location. Required when 2to3 is used
# with namespace packages.
if six.PY3 and getattr(self.distribution, 'use_2to3', False):
module = self.test_args[-1].split('.')[0]
module = self.test_suite.split('.')[0]
if module in _namespace_packages:
del_modules = []
if module in sys.modules:
......@@ -159,11 +174,15 @@ class test(Command):
list(map(sys.modules.__delitem__, del_modules))
unittest_main(
None, None, [unittest.__file__] + self.test_args,
None, None, self._argv,
testLoader=self._resolve_as_ep(self.test_loader),
testRunner=self._resolve_as_ep(self.test_runner),
)
@property
def _argv(self):
return ['unittest'] + self.test_args
@staticmethod
def _resolve_as_ep(val):
"""
......@@ -173,4 +192,4 @@ class test(Command):
if val is None:
return
parsed = EntryPoint.parse("x=" + val)
return parsed._load()()
return parsed.resolve()()
......@@ -171,8 +171,7 @@ class upload_docs(upload):
conn.putheader('Authorization', auth)
conn.endheaders()
conn.send(body)
except socket.error:
e = sys.exc_info()[1]
except socket.error as e:
self.announce(str(e), log.ERROR)
return
......
......@@ -116,24 +116,26 @@ def check_extras(dist, attr, value):
def assert_bool(dist, attr, value):
"""Verify that value is True, False, 0, or 1"""
if bool(value) != value:
raise DistutilsSetupError(
"%r must be a boolean value (got %r)" % (attr,value)
)
tmpl = "{attr!r} must be a boolean value (got {value!r})"
raise DistutilsSetupError(tmpl.format(attr=attr, value=value))
def check_requirements(dist, attr, value):
"""Verify that install_requires is a valid requirements list"""
try:
list(pkg_resources.parse_requirements(value))
except (TypeError,ValueError):
raise DistutilsSetupError(
"%r must be a string or list of strings "
"containing valid project/version requirement specifiers" % (attr,)
except (TypeError, ValueError) as error:
tmpl = (
"{attr!r} must be a string or list of strings "
"containing valid project/version requirement specifiers; {error}"
)
raise DistutilsSetupError(tmpl.format(attr=attr, error=error))
def check_entry_points(dist, attr, value):
"""Verify that entry_points map is parseable"""
try:
pkg_resources.EntryPoint.parse_map(value)
except ValueError:
e = sys.exc_info()[1]
except ValueError as e:
raise DistutilsSetupError(e)
def check_test_suite(dist, attr, value):
......@@ -159,7 +161,7 @@ def check_packages(dist, attr, value):
for pkgname in value:
if not re.match(r'\w+(\.\w+)*', pkgname):
distutils.log.warn(
"WARNING: %r not a valid package name; please use only"
"WARNING: %r not a valid package name; please use only "
".-separated package names in setup.py", pkgname
)
......@@ -266,8 +268,7 @@ class Distribution(_Distribution):
if attrs and 'setup_requires' in attrs:
self.fetch_build_eggs(attrs['setup_requires'])
for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
if not hasattr(self,ep.name):
setattr(self,ep.name,None)
vars(self).setdefault(ep.name, None)
_Distribution.__init__(self,attrs)
if isinstance(self.metadata.version, numbers.Number):
# Some people apparently take "version number" too literally :)
......@@ -279,10 +280,9 @@ class Distribution(_Distribution):
normalized_version = str(ver)
if self.metadata.version != normalized_version:
warnings.warn(
"The version specified requires normalization, "
"consider using '%s' instead of '%s'." % (
normalized_version,
"Normalizing '%s' to '%s'" % (
self.metadata.version,
normalized_version,
)
)
self.metadata.version = normalized_version
......@@ -436,10 +436,18 @@ class Distribution(_Distribution):
for ep in pkg_resources.iter_entry_points('distutils.commands'):
if ep.name not in self.cmdclass:
# don't require extras as the commands won't be invoked
cmdclass = ep._load()
cmdclass = ep.resolve()
self.cmdclass[ep.name] = cmdclass
return _Distribution.print_commands(self)
def get_command_list(self):
for ep in pkg_resources.iter_entry_points('distutils.commands'):
if ep.name not in self.cmdclass:
# don't require extras as the commands won't be invoked
cmdclass = ep.resolve()
self.cmdclass[ep.name] = cmdclass
return _Distribution.get_command_list(self)
def _set_feature(self,name,status):
"""Set feature's inclusion status"""
setattr(self,self._feature_attrname(name),status)
......@@ -818,7 +826,7 @@ class Feature:
if not self.available:
raise DistutilsPlatformError(
self.description+" is required,"
self.description+" is required, "
"but is not available on this platform"
)
......
......@@ -12,35 +12,33 @@ _Extension = _get_unpatched(distutils.core.Extension)
msvc9_support.patch_for_specialized_compiler()
def have_pyrex():
def _have_cython():
"""
Return True if Cython or Pyrex can be imported.
Return True if Cython can be imported.
"""
pyrex_impls = 'Cython.Distutils.build_ext', 'Pyrex.Distutils.build_ext'
for pyrex_impl in pyrex_impls:
try:
# from (pyrex_impl) import build_ext
__import__(pyrex_impl, fromlist=['build_ext']).build_ext
return True
except Exception:
pass
cython_impl = 'Cython.Distutils.build_ext',
try:
# from (cython_impl) import build_ext
__import__(cython_impl, fromlist=['build_ext']).build_ext
return True
except Exception:
pass
return False
# for compatibility
have_pyrex = _have_cython
class Extension(_Extension):
"""Extension that uses '.c' files in place of '.pyx' files"""
def __init__(self, *args, **kw):
_Extension.__init__(self, *args, **kw)
self._convert_pyx_sources_to_lang()
def _convert_pyx_sources_to_lang(self):
"""
Replace sources with .pyx extensions to sources with the target
language extension. This mechanism allows language authors to supply
pre-converted sources but to prefer the .pyx sources.
"""
if have_pyrex():
if _have_cython():
# the build has Cython, so allow it to compile the .pyx files
return
lang = self.language or ''
......
import sys
try:
import distutils.msvc9compiler
except ImportError:
......@@ -29,13 +27,15 @@ def patch_for_specialized_compiler():
def find_vcvarsall(version):
Reg = distutils.msvc9compiler.Reg
VC_BASE = r'Software\%sMicrosoft\DevDiv\VCForPython\%0.1f'
key = VC_BASE % ('', version)
try:
# Per-user installs register the compiler path here
productdir = Reg.get_value(VC_BASE % ('', version), "installdir")
productdir = Reg.get_value(key, "installdir")
except KeyError:
try:
# All-user installs on a 64-bit system register here
productdir = Reg.get_value(VC_BASE % ('Wow6432Node\\', version), "installdir")
key = VC_BASE % ('Wow6432Node\\', version)
productdir = Reg.get_value(key, "installdir")
except KeyError:
productdir = None
......@@ -50,8 +50,7 @@ def find_vcvarsall(version):
def query_vcvarsall(version, *args, **kwargs):
try:
return unpatched['query_vcvarsall'](version, *args, **kwargs)
except distutils.errors.DistutilsPlatformError:
exc = sys.exc_info()[1]
except distutils.errors.DistutilsPlatformError as exc:
if exc and "vcvarsall.bat" in exc.args[0]:
message = 'Microsoft Visual C++ %0.1f is required (%s).' % (version, exc.args[0])
if int(version) == 9:
......
......@@ -6,6 +6,7 @@ import shutil
import socket
import base64
import hashlib
import itertools
from functools import wraps
try:
......@@ -14,7 +15,7 @@ except ImportError:
from urllib2 import splituser
import six
from six.moves import urllib, http_client
from six.moves import urllib, http_client, configparser
from pkg_resources import (
CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST,
......@@ -141,10 +142,9 @@ def interpret_distro_name(
# versions in distribution archive names (sdist and bdist).
parts = basename.split('-')
if not py_version:
for i,p in enumerate(parts[2:]):
if len(p)==5 and p.startswith('py2.'):
return # It's a bdist_dumb, not an sdist -- bail out
if not py_version and any(re.match('py\d\.\d$', p) for p in parts[2:]):
# it is a bdist_dumb, not an sdist -- bail out
return
for p in range(1,len(parts)+1):
yield Distribution(
......@@ -356,20 +356,30 @@ class PackageIndex(Environment):
self.warn(msg, url)
def scan_egg_links(self, search_path):
for item in search_path:
if os.path.isdir(item):
for entry in os.listdir(item):
if entry.endswith('.egg-link'):
self.scan_egg_link(item, entry)
dirs = filter(os.path.isdir, search_path)
egg_links = (
(path, entry)
for path in dirs
for entry in os.listdir(path)
if entry.endswith('.egg-link')
)
list(itertools.starmap(self.scan_egg_link, egg_links))
def scan_egg_link(self, path, entry):
lines = [_f for _f in map(str.strip,
open(os.path.join(path, entry))) if _f]
if len(lines)==2:
for dist in find_distributions(os.path.join(path, lines[0])):
dist.location = os.path.join(path, *lines)
dist.precedence = SOURCE_DIST
self.add(dist)
with open(os.path.join(path, entry)) as raw_lines:
# filter non-empty lines
lines = list(filter(None, map(str.strip, raw_lines)))
if len(lines) != 2:
# format is not recognized; punt
return
egg_path, setup_path = lines
for dist in find_distributions(os.path.join(path, egg_path)):
dist.location = os.path.join(path, *lines)
dist.precedence = SOURCE_DIST
self.add(dist)
def process_index(self,url,page):
"""Process the contents of a PyPI page"""
......@@ -702,25 +712,21 @@ class PackageIndex(Environment):
return local_open(url)
try:
return open_with_auth(url, self.opener)
except (ValueError, http_client.InvalidURL):
v = sys.exc_info()[1]
except (ValueError, http_client.InvalidURL) as v:
msg = ' '.join([str(arg) for arg in v.args])
if warning:
self.warn(warning, msg)
else:
raise DistutilsError('%s %s' % (url, msg))
except urllib.error.HTTPError:
v = sys.exc_info()[1]
except urllib.error.HTTPError as v:
return v
except urllib.error.URLError:
v = sys.exc_info()[1]
except urllib.error.URLError as v:
if warning:
self.warn(warning, v.reason)
else:
raise DistutilsError("Download error for %s: %s"
% (url, v.reason))
except http_client.BadStatusLine:
v = sys.exc_info()[1]
except http_client.BadStatusLine as v:
if warning:
self.warn(warning, v.line)
else:
......@@ -729,8 +735,7 @@ class PackageIndex(Environment):
'down, %s' %
(url, v.line)
)
except http_client.HTTPException:
v = sys.exc_info()[1]
except http_client.HTTPException as v:
if warning:
self.warn(warning, v)
else:
......@@ -944,14 +949,14 @@ class Credential(object):
def __str__(self):
return '%(username)s:%(password)s' % vars(self)
class PyPIConfig(six.moves.configparser.ConfigParser):
class PyPIConfig(configparser.RawConfigParser):
def __init__(self):
"""
Load from ~/.pypirc
"""
defaults = dict.fromkeys(['username', 'password', 'repository'], '')
six.moves.configparser.ConfigParser.__init__(self, defaults)
configparser.RawConfigParser.__init__(self, defaults)
rc = os.path.join(os.path.expanduser('~'), '.pypirc')
if os.path.exists(rc):
......@@ -1043,16 +1048,18 @@ def local_open(url):
elif path.endswith('/') and os.path.isdir(filename):
files = []
for f in os.listdir(filename):
if f=='index.html':
with open(os.path.join(filename,f),'r') as fp:
filepath = os.path.join(filename, f)
if f == 'index.html':
with open(filepath, 'r') as fp:
body = fp.read()
break
elif os.path.isdir(os.path.join(filename,f)):
f+='/'
files.append("<a href=%r>%s</a>" % (f,f))
elif os.path.isdir(filepath):
f += '/'
files.append('<a href="{name}">{name}</a>'.format(name=f))
else:
body = ("<html><head><title>%s</title>" % url) + \
"</head><body>%s</body></html>" % '\n'.join(files)
tmpl = ("<html><head><title>{url}</title>"
"</head><body>{files}</body></html>")
body = tmpl.format(url=url, files='\n'.join(files))
status, message = 200, "OK"
else:
status, message, body = 404, "Path not found", "Not found"
......
......@@ -20,7 +20,7 @@ except ImportError:
import shutil
import tempfile
class TemporaryDirectory(object):
""""
"""
Very simple temporary directory context manager.
Will try to delete afterward, but will also ignore OS and similar
errors on deletion.
......
This diff is collapsed.
......@@ -218,6 +218,12 @@ def get_win_certfile():
self.addcerts(certs)
atexit.register(self.close)
def close(self):
try:
super(MyCertFile, self).close()
except OSError:
pass
_wincerts = MyCertFile(stores=['CA', 'ROOT'])
return _wincerts.name
......
......@@ -16,6 +16,11 @@ import setuptools.depends as dep
from setuptools import Feature
from setuptools.depends import Require
c_type = os.environ.get("LC_CTYPE", os.environ.get("LC_ALL"))
is_ascii = c_type in ("C", "POSIX")
fail_on_ascii = pytest.mark.xfail(is_ascii, reason="Test fails in this locale")
def makeSetup(**args):
"""Return distribution from 'setup(**args)', without executing commands"""
......
......@@ -27,7 +27,7 @@ def environment(**replacements):
to clear the values.
"""
saved = dict(
(key, os.environ['key'])
(key, os.environ[key])
for key in replacements
if key in os.environ
)
......@@ -48,14 +48,6 @@ def environment(**replacements):
os.environ.update(saved)
@contextlib.contextmanager
def argv(repl):
old_argv = sys.argv[:]
sys.argv[:] = repl
yield
sys.argv[:] = old_argv
@contextlib.contextmanager
def quiet():
"""
......
This diff is collapsed.
import mock
try:
from unittest import mock
except ImportError:
import mock
import pytest
from . import contexts
......
......@@ -8,4 +8,7 @@ def _tarfile_open_ex(*args, **kwargs):
"""
return contextlib.closing(tarfile.open(*args, **kwargs))
tarfile_open = _tarfile_open_ex if sys.version_info < (2,7) else tarfile.open
if sys.version_info[:2] < (2, 7) or (3, 0) <= sys.version_info[:2] < (3, 2):
tarfile_open = _tarfile_open_ex
else:
tarfile_open = tarfile.open
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
__version__ = '11.1'
__version__ = '19.3'
This diff is collapsed.
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