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

Merge branch 'master' into drop-py26

parents f14930e6 1b192005
......@@ -7,9 +7,11 @@ lib
distribute.egg-info
setuptools.egg-info
.coverage
.eggs
.tox
*.egg
*.py[cod]
*.swp
*~
.hg*
.cache
dist: trusty
sudo: false
language: python
python:
- 2.7
- 3.3
- 3.4
- 3.5
- "3.6-dev"
- nightly
- pypy
env:
- ""
- LC_ALL=C LC_CTYPE=C
script:
- pip install tox
jobs:
fast_finish: true
include:
- python: &latest_py2 2.7
- python: 3.3
- python: 3.4
- python: 3.5
- python: &latest_py3 3.6
- python: nightly
- python: pypy
- python: pypy3
- python: *latest_py3
env: LANG=C
- python: *latest_py2
env: LANG=C
- stage: deploy (to PyPI for tagged commits)
python: *latest_py3
install: skip
script: skip
before_deploy: python bootstrap.py
deploy:
provider: pypi
on:
tags: true
all_branches: true
user: jaraco
password:
secure: tfWrsQMH2bHrWjqnP+08IX1WlkbW94Q30f4d7lCyhWS1FIf/jBDx4jrEILNfMxQ1NCwuBRje5sihj1Ow0BFf0vVrkaeff2IdvnNDEGFduMejaEQJL3s3QrLfpiAvUbtqwyWaHfAdGfk48PovDKTx0ZTvXZKYGXZhxGCYSlG2CE6Y6RDvnEl6Tk8e+LqUohkcSOwxrRwUoyxSnUaavdGohXxDT8MJlfWOXgr2u+KsRrriZqp3l6Fdsnk4IGvy6pXpy42L1HYQyyVu9XyJilR2JTbC6eCp5f8p26093m1Qas49+t6vYb0VLqQe12dO+Jm3v4uztSS5pPQzS7PFyjEYd2Rdb6ijsdbsy1074S4q7G9Sz+T3RsPUwYEJ07lzez8cxP64dtj5j94RL8m35A1Fb1OE8hHN+4c1yLG1gudfXbem+fUhi2eqhJrzQo5vsvDv1xS5x5GIS5ZHgKHCsWcW1Tv+dsFkrhaup3uU6VkOuc9UN+7VPsGEY7NvquGpTm8O1CnGJRzuJg6nbYRGj8ORwDpI0KmrExx6akV92P72fMC/I5TCgbSQSZn370H3Jj40gz1SM30WAli9M+wFHFd4ddMVY65yxj0NLmrP+m1tvnWdKtNh/RHuoW92d9/UFtiA5IhMf1/3djfsjBq6S9NT1uaLkVkTttqrPYJ7hOql8+g=
distributions: release
skip_cleanup: true
skip_upload_docs: true
# Output the env, to verify behavior
- env
cache: pip
# update egg_info based on setup.py in checkout
- python bootstrap.py
install:
# need tox to get started
- pip install tox
#- python -m tox
- tox
# Output the env, to verify behavior
- env
deploy:
provider: pypi
# Also update server in setup.cfg
server: https://upload.pypi.org/legacy/
on:
tags: true
all_branches: true
python: 3.5
condition: $LC_ALL != "C"
user: jaraco
password:
secure: tfWrsQMH2bHrWjqnP+08IX1WlkbW94Q30f4d7lCyhWS1FIf/jBDx4jrEILNfMxQ1NCwuBRje5sihj1Ow0BFf0vVrkaeff2IdvnNDEGFduMejaEQJL3s3QrLfpiAvUbtqwyWaHfAdGfk48PovDKTx0ZTvXZKYGXZhxGCYSlG2CE6Y6RDvnEl6Tk8e+LqUohkcSOwxrRwUoyxSnUaavdGohXxDT8MJlfWOXgr2u+KsRrriZqp3l6Fdsnk4IGvy6pXpy42L1HYQyyVu9XyJilR2JTbC6eCp5f8p26093m1Qas49+t6vYb0VLqQe12dO+Jm3v4uztSS5pPQzS7PFyjEYd2Rdb6ijsdbsy1074S4q7G9Sz+T3RsPUwYEJ07lzez8cxP64dtj5j94RL8m35A1Fb1OE8hHN+4c1yLG1gudfXbem+fUhi2eqhJrzQo5vsvDv1xS5x5GIS5ZHgKHCsWcW1Tv+dsFkrhaup3uU6VkOuc9UN+7VPsGEY7NvquGpTm8O1CnGJRzuJg6nbYRGj8ORwDpI0KmrExx6akV92P72fMC/I5TCgbSQSZn370H3Jj40gz1SM30WAli9M+wFHFd4ddMVY65yxj0NLmrP+m1tvnWdKtNh/RHuoW92d9/UFtiA5IhMf1/3djfsjBq6S9NT1uaLkVkTttqrPYJ7hOql8+g=
distributions: release
# update egg_info based on setup.py in checkout
- python bootstrap.py
script: tox
This diff is collapsed.
......@@ -11,3 +11,4 @@ include LICENSE
include launcher.c
include msvc-build-launcher.cmd
include pytest.ini
include tox.ini
This diff is collapsed.
......@@ -5,10 +5,14 @@ environment by creating a minimal egg-info directory and then invoking the
egg-info command to flesh out the egg-info directory.
"""
from __future__ import unicode_literals
import os
import sys
import textwrap
import subprocess
import io
minimal_egg_info = textwrap.dedent("""
[distutils.commands]
......@@ -40,7 +44,7 @@ def build_egg_info():
"""
os.mkdir('setuptools.egg-info')
with open('setuptools.egg-info/entry_points.txt', 'w') as ep:
with io.open('setuptools.egg-info/entry_points.txt', 'w') as ep:
ep.write(minimal_egg_info)
......@@ -52,6 +56,9 @@ def run_egg_info():
subprocess.check_call(cmd)
if __name__ == '__main__':
def main():
ensure_egg_info()
run_egg_info()
__name__ == '__main__' and main()
import os
pytest_plugins = 'setuptools.tests.fixtures'
......@@ -9,17 +6,3 @@ def pytest_addoption(parser):
"--package_name", action="append", default=[],
help="list of package_name to pass to test functions",
)
def pytest_configure():
_issue_852_workaround()
def _issue_852_workaround():
"""
Patch 'setuptools.__file__' with an absolute path
for forward compatibility with Python 3.
Workaround for https://github.com/pypa/setuptools/issues/852
"""
setuptools = __import__('setuptools')
setuptools.__file__ = os.path.abspath(setuptools.__file__)
......@@ -5,4 +5,4 @@
<h3>Questions? Suggestions? Contributions?</h3>
<p>Visit the <a href="https://bitbucket.org/pypa/setuptools">Setuptools project page</a> </p>
<p>Visit the <a href="https://github.com/pypa/setuptools">Setuptools project page</a> </p>
......@@ -18,18 +18,23 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# Allow Sphinx to find the setup command that is imported below, as referenced above.
import os
import subprocess
import sys
sys.path.append(os.path.abspath('..'))
import os
import setup as setup_script
# hack to run the bootstrap script so that jaraco.packaging.sphinx
# can invoke setup.py
'READTHEDOCS' in os.environ and subprocess.check_call(
[sys.executable, 'bootstrap.py'],
cwd=os.path.join(os.path.dirname(__file__), os.path.pardir),
)
# -- General configuration -----------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ['rst.linker']
extensions = ['jaraco.packaging.sphinx', 'rst.linker', 'sphinx.ext.autosectionlabel']
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
......@@ -40,19 +45,6 @@ source_suffix = '.txt'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'Setuptools'
copyright = '2009-2014, The fellowship of the packaging'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = setup_script.setup_params['version']
# The full version, including alpha/beta/rc tags.
release = setup_script.setup_params['version']
# List of directories, relative to source directory, that shouldn't be searched
# for source files.
exclude_trees = []
......@@ -69,13 +61,6 @@ html_theme = 'nature'
# Add any paths that contain custom themes here, relative to this directory.
html_theme_path = ['_theme']
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
html_title = "Setuptools documentation"
# A shorter title for the navigation bar. Default is the same as html_title.
html_short_title = "Setuptools"
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
html_use_smartypants = True
......@@ -89,9 +74,6 @@ html_use_modindex = False
# If false, no index is generated.
html_use_index = False
# Output file base name for HTML help builder.
htmlhelp_basename = 'Setuptoolsdoc'
# -- Options for LaTeX output --------------------------------------------------
# Grouping the document tree into LaTeX files. List of tuples
......@@ -109,56 +91,60 @@ link_files = {
),
replace=[
dict(
pattern=r"(Issue )?#(?P<issue>\d+)",
url='{GH}/pypa/setuptools/issues/{issue}',
pattern=r'(Issue )?#(?P<issue>\d+)',
url='{package_url}/issues/{issue}',
),
dict(
pattern=r"BB Pull Request ?#(?P<bb_pull_request>\d+)",
pattern=r'BB Pull Request ?#(?P<bb_pull_request>\d+)',
url='{BB}/pypa/setuptools/pull-request/{bb_pull_request}',
),
dict(
pattern=r"Distribute #(?P<distribute>\d+)",
pattern=r'Distribute #(?P<distribute>\d+)',
url='{BB}/tarek/distribute/issue/{distribute}',
),
dict(
pattern=r"Buildout #(?P<buildout>\d+)",
pattern=r'Buildout #(?P<buildout>\d+)',
url='{GH}/buildout/buildout/issues/{buildout}',
),
dict(
pattern=r"Old Setuptools #(?P<old_setuptools>\d+)",
pattern=r'Old Setuptools #(?P<old_setuptools>\d+)',
url='http://bugs.python.org/setuptools/issue{old_setuptools}',
),
dict(
pattern=r"Jython #(?P<jython>\d+)",
pattern=r'Jython #(?P<jython>\d+)',
url='http://bugs.jython.org/issue{jython}',
),
dict(
pattern=r"Python #(?P<python>\d+)",
pattern=r'Python #(?P<python>\d+)',
url='http://bugs.python.org/issue{python}',
),
dict(
pattern=r"Interop #(?P<interop>\d+)",
pattern=r'Interop #(?P<interop>\d+)',
url='{GH}/pypa/interoperability-peps/issues/{interop}',
),
dict(
pattern=r"Pip #(?P<pip>\d+)",
pattern=r'Pip #(?P<pip>\d+)',
url='{GH}/pypa/pip/issues/{pip}',
),
dict(
pattern=r"Packaging #(?P<packaging>\d+)",
pattern=r'Packaging #(?P<packaging>\d+)',
url='{GH}/pypa/packaging/issues/{packaging}',
),
dict(
pattern=r"[Pp]ackaging (?P<packaging_ver>\d+(\.\d+)+)",
pattern=r'[Pp]ackaging (?P<packaging_ver>\d+(\.\d+)+)',
url='{GH}/pypa/packaging/blob/{packaging_ver}/CHANGELOG.rst',
),
dict(
pattern=r"PEP[- ](?P<pep_number>\d+)",
pattern=r'PEP[- ](?P<pep_number>\d+)',
url='https://www.python.org/dev/peps/pep-{pep_number:0>4}/',
),
dict(
pattern=r"^(?m)((?P<scm_version>v?\d+(\.\d+){1,2}))\n[-=]+\n",
with_scm="{text}\n{rev[timestamp]:%d %b %Y}\n",
pattern=r'setuptools_svn #(?P<setuptools_svn>\d+)',
url='{GH}/jaraco/setuptools_svn/issues/{setuptools_svn}',
),
dict(
pattern=r'^(?m)((?P<scm_version>v?\d+(\.\d+){1,2}))\n[-=]+\n',
with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n',
),
],
),
......
......@@ -13,11 +13,11 @@ Recommended Reading
-------------------
Please read `How to write the perfect pull request
<http://blog.jaraco.com/2014/04/how-to-write-perfect-pull-request.html>`_
for some tips on contributing to open source projects. Although the article
is not authoritative, it was authored by the maintainer of Setuptools, so
reflects his opinions and will improve the likelihood of acceptance and
quality of contribution.
<https://blog.jaraco.com/how-to-write-perfect-pull-request/>`_ for some tips
on contributing to open source projects. Although the article is not
authoritative, it was authored by the maintainer of Setuptools, so reflects
his opinions and will improve the likelihood of acceptance and quality of
contribution.
------------------
Project Management
......
......@@ -6,3 +6,41 @@ History
*******
.. include:: ../CHANGES (links).rst
Credits
*******
* The original design for the ``.egg`` format and the ``pkg_resources`` API was
co-created by Phillip Eby and Bob Ippolito. Bob also implemented the first
version of ``pkg_resources``, and supplied the OS X operating system version
compatibility algorithm.
* Ian Bicking implemented many early "creature comfort" features of
easy_install, including support for downloading via Sourceforge and
Subversion repositories. Ian's comments on the Web-SIG about WSGI
application deployment also inspired the concept of "entry points" in eggs,
and he has given talks at PyCon and elsewhere to inform and educate the
community about eggs and setuptools.
* Jim Fulton contributed time and effort to build automated tests of various
aspects of ``easy_install``, and supplied the doctests for the command-line
``.exe`` wrappers on Windows.
* Phillip J. Eby is the seminal author of setuptools, and
first proposed the idea of an importable binary distribution format for
Python application plug-ins.
* Significant parts of the implementation of setuptools were funded by the Open
Source Applications Foundation, to provide a plug-in infrastructure for the
Chandler PIM application. In addition, many OSAF staffers (such as Mike
"Code Bear" Taylor) contributed their time and stress as guinea pigs for the
use of eggs and setuptools, even before eggs were "cool". (Thanks, guys!)
* Tarek Ziadé is the principal author of the Distribute fork, which
re-invigorated the community on the project, encouraged renewed innovation,
and addressed many defects.
* Since the merge with Distribute, Jason R. Coombs is the
maintainer of setuptools. The project is maintained in coordination with
the Python Packaging Authority (PyPA) and the larger Python community.
......@@ -16,10 +16,10 @@ Documentation content:
.. toctree::
:maxdepth: 2
history
roadmap
python3
setuptools
easy_install
pkg_resources
python3
development
roadmap
history
......@@ -143,10 +143,10 @@ namespace package for Zope Corporation packages, and the ``peak`` namespace
package for the Python Enterprise Application Kit.
To create a namespace package, you list it in the ``namespace_packages``
argument to ``setup()``, in your project's ``setup.py``. (See the `setuptools
documentation on namespace packages`_ for more information on this.) Also,
you must add a ``declare_namespace()`` call in the package's ``__init__.py``
file(s):
argument to ``setup()``, in your project's ``setup.py``. (See the
:ref:`setuptools documentation on namespace packages <Namespace Packages>` for
more information on this.) Also, you must add a ``declare_namespace()`` call
in the package's ``__init__.py`` file(s):
``declare_namespace(name)``
Declare that the dotted package name `name` is a "namespace package" whose
......@@ -175,8 +175,6 @@ filesystem and zip importers, you can extend its support to other "importers"
compatible with PEP 302 using the ``register_namespace_handler()`` function.
See the section below on `Supporting Custom Importers`_ for details.
.. _setuptools documentation on namespace packages: http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
``WorkingSet`` Objects
======================
......
......@@ -7,25 +7,20 @@ mechanical technique for releases, enacted by Travis following a
successful build of a tagged release per
`PyPI deployment <https://docs.travis-ci.com/user/deployment/pypi>`_.
To cut a release, install and run ``bumpversion {part}`` where ``part``
Prior to cutting a release, please check that the CHANGES.rst reflects
the summary of changes since the last release.
Ideally, these changelog entries would have been added
along with the changes, but it's always good to check.
Think about it from the
perspective of a user not involved with the development--what would
that person want to know about what has changed--or from the
perspective of your future self wanting to know when a particular
change landed.
To cut a release, install and run ``bump2version {part}`` where ``part``
is major, minor, or patch based on the scope of the changes in the
release. Then, push the commits to the master branch. If tests pass,
the release will be uploaded to PyPI (from the Python 3.5 tests).
Bootstrap Branch
----------------
Setuptools has a bootstrap script (ez_setup.py), which is hosted in the
repository in the ``bootstrap`` branch.
Therefore, the latest bootstrap script can be retrieved by checking out
that branch.
The officially-published location of the bootstrap script is hosted on Python
infrastructure (#python-infra on freenode) at https://bootstrap.pypa.io and
is updated every fifteen minutes from the bootstrap branch. Sometimes,
especially when the bootstrap script is rolled back, this
process doesn't work as expected and requires manual intervention.
the release will be uploaded to PyPI (from the Python 3.6 tests).
Release Frequency
-----------------
......
rst.linker>=1.6.1
sphinx
rst.linker>=1.9
jaraco.packaging>=3.2
setuptools>=34
This diff is collapsed.
This diff is collapsed.
import os
import errno
import sys
def _makedirs_31(path, exist_ok=False):
try:
os.makedirs(path)
except OSError as exc:
if not exist_ok or exc.errno != errno.EEXIST:
raise
# rely on compatibility behavior until mode considerations
# and exists_ok considerations are disentangled.
# See https://github.com/pypa/setuptools/pull/1083#issuecomment-315168663
needs_makedirs = (
sys.version_info < (3, 2, 5) or
(3, 3) <= sys.version_info < (3, 3, 6) or
(3, 4) <= sys.version_info < (3, 4, 1)
)
makedirs = _makedirs_31 if needs_makedirs else os.makedirs
import subprocess
import sys
import pytest
import pkg_resources
SETUP_TEMPLATE = """
import setuptools
setuptools.setup(
name="my-test-package",
version="1.0",
zip_safe=True,
)
""".lstrip()
class TestFindDistributions:
@pytest.fixture
def target_dir(self, tmpdir):
target_dir = tmpdir.mkdir('target')
# place a .egg named directory in the target that is not an egg:
target_dir.mkdir('not.an.egg')
return str(target_dir)
@pytest.fixture
def project_dir(self, tmpdir):
project_dir = tmpdir.mkdir('my-test-package')
(project_dir / "setup.py").write(SETUP_TEMPLATE)
return str(project_dir)
def test_non_egg_dir_named_egg(self, target_dir):
dists = pkg_resources.find_distributions(target_dir)
assert not list(dists)
def test_standalone_egg_directory(self, project_dir, target_dir):
# install this distro as an unpacked egg:
args = [
sys.executable,
'-c', 'from setuptools.command.easy_install import main; main()',
'-mNx',
'-d', target_dir,
'--always-unzip',
project_dir,
]
subprocess.check_call(args)
dists = pkg_resources.find_distributions(target_dir)
assert [dist.project_name for dist in dists] == ['my-test-package']
dists = pkg_resources.find_distributions(target_dir, only=True)
assert not list(dists)
def test_zipped_egg(self, project_dir, target_dir):
# install this distro as an unpacked egg:
args = [
sys.executable,
'-c', 'from setuptools.command.easy_install import main; main()',
'-mNx',
'-d', target_dir,
'--zip-ok',
project_dir,
]
subprocess.check_call(args)
dists = pkg_resources.find_distributions(target_dir)
assert [dist.project_name for dist in dists] == ['my-test-package']
dists = pkg_resources.find_distributions(target_dir, only=True)
assert not list(dists)
try:
import unittest.mock as mock
except ImportError:
import mock
import mock
from pkg_resources import evaluate_marker
......
......@@ -585,7 +585,7 @@ class TestParsing:
[Requirement('Twis-Ted>=1.2-1')]
)
assert (
list(parse_requirements('Twisted >=1.2, \ # more\n<2.0'))
list(parse_requirements('Twisted >=1.2, \\ # more\n<2.0'))
==
[Requirement('Twisted>=1.2,<2.0')]
)
......
import inspect
import re
import textwrap
import pytest
import pkg_resources
from .test_resources import Metadata
def strip_comments(s):
return '\n'.join(
l for l in s.split('\n')
if l.strip() and not l.strip().startswith('#')
)
def parse_distributions(s):
'''
Parse a series of distribution specs of the form:
{project_name}-{version}
[optional, indented requirements specification]
Example:
foo-0.2
bar-1.0
foo>=3.0
[feature]
baz
yield 2 distributions:
- project_name=foo, version=0.2
- project_name=bar, version=1.0, requires=['foo>=3.0', 'baz; extra=="feature"']
'''
s = s.strip()
for spec in re.split('\n(?=[^\s])', s):
if not spec:
continue
fields = spec.split('\n', 1)
assert 1 <= len(fields) <= 2
name, version = fields.pop(0).split('-')
if fields:
requires = textwrap.dedent(fields.pop(0))
metadata=Metadata(('requires.txt', requires))
else:
metadata = None
dist = pkg_resources.Distribution(project_name=name,
version=version,
metadata=metadata)
yield dist
class FakeInstaller(object):
def __init__(self, installable_dists):
self._installable_dists = installable_dists
def __call__(self, req):
return next(iter(filter(lambda dist: dist in req,
self._installable_dists)), None)
def parametrize_test_working_set_resolve(*test_list):
idlist = []
argvalues = []
for test in test_list:
(
name,
installed_dists,
installable_dists,
requirements,
expected1, expected2
) = [
strip_comments(s.lstrip()) for s in
textwrap.dedent(test).lstrip().split('\n\n', 5)
]
installed_dists = list(parse_distributions(installed_dists))
installable_dists = list(parse_distributions(installable_dists))
requirements = list(pkg_resources.parse_requirements(requirements))
for id_, replace_conflicting, expected in (
(name, False, expected1),
(name + '_replace_conflicting', True, expected2),
):
idlist.append(id_)
expected = strip_comments(expected.strip())
if re.match('\w+$', expected):
expected = getattr(pkg_resources, expected)
assert issubclass(expected, Exception)
else:
expected = list(parse_distributions(expected))
argvalues.append(pytest.param(installed_dists, installable_dists,
requirements, replace_conflicting,
expected))
return pytest.mark.parametrize('installed_dists,installable_dists,'
'requirements,replace_conflicting,'
'resolved_dists_or_exception',
argvalues, ids=idlist)
@parametrize_test_working_set_resolve(
'''
# id
noop
# installed
# installable
# wanted
# resolved
# resolved [replace conflicting]
''',
'''
# id
already_installed
# installed
foo-3.0
# installable
# wanted
foo>=2.1,!=3.1,<4
# resolved
foo-3.0
# resolved [replace conflicting]
foo-3.0
''',
'''
# id
installable_not_installed
# installed
# installable
foo-3.0
foo-4.0
# wanted
foo>=2.1,!=3.1,<4
# resolved
foo-3.0
# resolved [replace conflicting]
foo-3.0
''',
'''
# id
not_installable
# installed
# installable
# wanted
foo>=2.1,!=3.1,<4
# resolved
DistributionNotFound
# resolved [replace conflicting]
DistributionNotFound
''',
'''
# id
no_matching_version
# installed
# installable
foo-3.1
# wanted
foo>=2.1,!=3.1,<4
# resolved
DistributionNotFound
# resolved [replace conflicting]
DistributionNotFound
''',
'''
# id
installable_with_installed_conflict
# installed
foo-3.1
# installable
foo-3.5
# wanted
foo>=2.1,!=3.1,<4
# resolved
VersionConflict
# resolved [replace conflicting]
foo-3.5
''',
'''
# id
not_installable_with_installed_conflict
# installed
foo-3.1
# installable
# wanted
foo>=2.1,!=3.1,<4
# resolved
VersionConflict
# resolved [replace conflicting]
DistributionNotFound
''',
'''
# id
installed_with_installed_require
# installed
foo-3.9
baz-0.1
foo>=2.1,!=3.1,<4
# installable
# wanted
baz
# resolved
foo-3.9
baz-0.1
# resolved [replace conflicting]
foo-3.9
baz-0.1
''',
'''
# id
installed_with_conflicting_installed_require
# installed
foo-5
baz-0.1
foo>=2.1,!=3.1,<4
# installable
# wanted
baz
# resolved
VersionConflict
# resolved [replace conflicting]
DistributionNotFound
''',
'''
# id
installed_with_installable_conflicting_require
# installed
foo-5
baz-0.1
foo>=2.1,!=3.1,<4
# installable
foo-2.9
# wanted
baz
# resolved
VersionConflict
# resolved [replace conflicting]
baz-0.1
foo-2.9
''',
'''
# id
installed_with_installable_require
# installed
baz-0.1
foo>=2.1,!=3.1,<4
# installable
foo-3.9
# wanted
baz
# resolved
foo-3.9
baz-0.1
# resolved [replace conflicting]
foo-3.9
baz-0.1
''',
'''
# id
installable_with_installed_require
# installed
foo-3.9
# installable
baz-0.1
foo>=2.1,!=3.1,<4
# wanted
baz
# resolved
foo-3.9
baz-0.1
# resolved [replace conflicting]
foo-3.9
baz-0.1
''',
'''
# id
installable_with_installable_require
# installed
# installable
foo-3.9
baz-0.1
foo>=2.1,!=3.1,<4
# wanted
baz
# resolved
foo-3.9
baz-0.1
# resolved [replace conflicting]
foo-3.9
baz-0.1
''',
'''
# id
installable_with_conflicting_installable_require
# installed
foo-5
# installable
foo-2.9
baz-0.1
foo>=2.1,!=3.1,<4
# wanted
baz
# resolved
VersionConflict
# resolved [replace conflicting]
baz-0.1
foo-2.9
''',
'''
# id
conflicting_installables
# installed
# installable
foo-2.9
foo-5.0
# wanted
foo>=2.1,!=3.1,<4
foo>=4
# resolved
VersionConflict
# resolved [replace conflicting]
VersionConflict
''',
'''
# id
installables_with_conflicting_requires
# installed
# installable
foo-2.9
dep==1.0
baz-5.0
dep==2.0
dep-1.0
dep-2.0
# wanted
foo
baz
# resolved
VersionConflict
# resolved [replace conflicting]
VersionConflict
''',
'''
# id
installables_with_conflicting_nested_requires
# installed
# installable
foo-2.9
dep1
dep1-1.0
subdep<1.0
baz-5.0
dep2
dep2-1.0
subdep>1.0
subdep-0.9
subdep-1.1
# wanted
foo
baz
# resolved
VersionConflict
# resolved [replace conflicting]
VersionConflict
''',
)
def test_working_set_resolve(installed_dists, installable_dists, requirements,
replace_conflicting, resolved_dists_or_exception):
ws = pkg_resources.WorkingSet([])
list(map(ws.add, installed_dists))
resolve_call = lambda: ws.resolve(
requirements, installer=FakeInstaller(installable_dists),
replace_conflicting=replace_conflicting,
)
if inspect.isclass(resolved_dists_or_exception):
with pytest.raises(resolved_dists_or_exception):
resolve_call()
else:
assert sorted(resolve_call()) == sorted(resolved_dists_or_exception)
[pytest]
addopts=--doctest-modules --ignore release.py --ignore setuptools/lib2to3_ex.py --ignore tests/manual_test.py --ignore tests/test_pypi.py --ignore tests/shlib_test --doctest-glob=pkg_resources/api_tests.txt --ignore scripts/upload-old-releases-as-zip.py --ignore pavement.py
addopts=--doctest-modules --ignore release.py --ignore setuptools/lib2to3_ex.py --ignore tests/manual_test.py --ignore tests/test_pypi.py --ignore tests/shlib_test --doctest-glob=pkg_resources/api_tests.txt --ignore scripts/upload-old-releases-as-zip.py --ignore pavement.py --ignore setuptools/tests/mod_with_constant.py -rsxX
norecursedirs=dist build *.egg setuptools/extern pkg_resources/extern .*
flake8-ignore =
setuptools/site-patch.py F821
......
[bumpversion]
current_version = 30.3.0
current_version = 36.4.0
commit = True
tag = True
......@@ -8,19 +8,18 @@ tag_build = .post
tag_date = 1
[aliases]
clean_egg_info = egg_info -RDb ''
clean_egg_info = egg_info -Db ''
release = clean_egg_info sdist bdist_wheel
source = register sdist binary
binary = bdist_egg upload --show-response
test = pytest
[upload]
repository = https://upload.pypi.org/legacy/
[sdist]
formats = gztar zip
formats = zip
[wheel]
[bdist_wheel]
universal = 1
[bumpversion:file:setup.py]
......
......@@ -15,8 +15,12 @@ here = os.path.dirname(__file__)
def require_metadata():
"Prevent improper installs without necessary metadata. See #659"
if not os.path.exists('setuptools.egg-info'):
msg = "Cannot build setuptools without metadata. Run bootstrap.py"
egg_info_dir = os.path.join(here, 'setuptools.egg-info')
if not os.path.exists(egg_info_dir):
msg = (
"Cannot build setuptools without metadata. "
"Run `bootstrap.py`."
)
raise RuntimeError(msg)
......@@ -85,12 +89,13 @@ def pypi_link(pkg_filename):
setup_params = dict(
name="setuptools",
version="30.3.0",
version="36.4.0",
description="Easily download, build, install, upgrade, and uninstall "
"Python packages",
author="Python Packaging Authority",
author_email="distutils-sig@python.org",
long_description=long_description,
long_description_content_type='text/x-rst; charset=UTF-8',
keywords="CPAN PyPI distutils eggs package management",
url="https://github.com/pypa/setuptools",
src_root=None,
......@@ -145,11 +150,13 @@ setup_params = dict(
Intended Audience :: Developers
License :: OSI Approved :: MIT License
Operating System :: OS Independent
Programming Language :: Python :: 2
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.3
Programming Language :: Python :: 3.4
Programming Language :: Python :: 3.5
Programming Language :: Python :: 3.6
Topic :: Software Development :: Libraries :: Python Modules
Topic :: System :: Archiving :: Packaging
Topic :: System :: Systems Administration
......
......@@ -7,7 +7,7 @@ import distutils.filelist
from distutils.util import convert_path
from fnmatch import fnmatchcase
from setuptools.extern.six.moves import filter, filterfalse, map
from setuptools.extern.six.moves import filter, map
import setuptools.version
from setuptools.extension import Extension
......
......@@ -2,7 +2,7 @@ __all__ = [
'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop',
'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts',
'sdist', 'setopt', 'test', 'install_egg_info', 'install_scripts',
'register', 'bdist_wininst', 'upload_docs', 'upload',
'register', 'bdist_wininst', 'upload_docs', 'upload', 'build_clib',
]
from distutils.command.bdist import bdist
......
......@@ -38,6 +38,14 @@ def strip_module(filename):
filename = filename[:-6]
return filename
def sorted_walk(dir):
"""Do os.walk in a reproducible way,
independent of indeterministic filesystem readdir order
"""
for base, dirs, files in os.walk(dir):
dirs.sort()
files.sort()
yield base, dirs, files
def write_stub(resource, pyfile):
_stub_template = textwrap.dedent("""
......@@ -302,7 +310,7 @@ class bdist_egg(Command):
ext_outputs = []
paths = {self.bdist_dir: ''}
for base, dirs, files in os.walk(self.bdist_dir):
for base, dirs, files in sorted_walk(self.bdist_dir):
for filename in files:
if os.path.splitext(filename)[1].lower() in NATIVE_EXTENSIONS:
all_outputs.append(paths[base] + filename)
......@@ -329,7 +337,7 @@ NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split())
def walk_egg(egg_dir):
"""Walk an unpacked egg's contents, skipping the metadata directory"""
walker = os.walk(egg_dir)
walker = sorted_walk(egg_dir)
base, dirs, files = next(walker)
if 'EGG-INFO' in dirs:
dirs.remove('EGG-INFO')
......@@ -463,10 +471,10 @@ def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=True,
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):
for dirname, dirs, files in sorted_walk(base_dir):
visit(z, dirname, files)
z.close()
else:
for dirname, dirs, files in os.walk(base_dir):
for dirname, dirs, files in sorted_walk(base_dir):
visit(None, dirname, files)
return zip_filename
import distutils.command.build_clib as orig
from distutils.errors import DistutilsSetupError
from distutils import log
from setuptools.dep_util import newer_pairwise_group
class build_clib(orig.build_clib):
"""
Override the default build_clib behaviour to do the following:
1. Implement a rudimentary timestamp-based dependency system
so 'compile()' doesn't run every time.
2. Add more keys to the 'build_info' dictionary:
* obj_deps - specify dependencies for each object compiled.
this should be a dictionary mapping a key
with the source filename to a list of
dependencies. Use an empty string for global
dependencies.
* cflags - specify a list of additional flags to pass to
the compiler.
"""
def build_libraries(self, libraries):
for (lib_name, build_info) in libraries:
sources = build_info.get('sources')
if sources is None or not isinstance(sources, (list, tuple)):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'sources' must be present and must be "
"a list of source filenames" % lib_name)
sources = list(sources)
log.info("building '%s' library", lib_name)
# Make sure everything is the correct type.
# obj_deps should be a dictionary of keys as sources
# and a list/tuple of files that are its dependencies.
obj_deps = build_info.get('obj_deps', dict())
if not isinstance(obj_deps, dict):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name)
dependencies = []
# Get the global dependencies that are specified by the '' key.
# These will go into every source's dependency list.
global_deps = obj_deps.get('', list())
if not isinstance(global_deps, (list, tuple)):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name)
# Build the list to be used by newer_pairwise_group
# each source will be auto-added to its dependencies.
for source in sources:
src_deps = [source]
src_deps.extend(global_deps)
extra_deps = obj_deps.get(source, list())
if not isinstance(extra_deps, (list, tuple)):
raise DistutilsSetupError(
"in 'libraries' option (library '%s'), "
"'obj_deps' must be a dictionary of "
"type 'source: list'" % lib_name)
src_deps.extend(extra_deps)
dependencies.append(src_deps)
expected_objects = self.compiler.object_filenames(
sources,
output_dir=self.build_temp
)
if newer_pairwise_group(dependencies, expected_objects) != ([], []):
# First, compile the source code to object files in the library
# directory. (This should probably change to putting object
# files in a temporary build directory.)
macros = build_info.get('macros')
include_dirs = build_info.get('include_dirs')
cflags = build_info.get('cflags')
objects = self.compiler.compile(
sources,
output_dir=self.build_temp,
macros=macros,
include_dirs=include_dirs,
extra_postargs=cflags,
debug=self.debug
)
# Now "link" the object files together into a static library.
# (On Unix at least, this isn't really linking -- it just
# builds an archive. Whatever.)
self.compiler.create_static_lib(
expected_objects,
lib_name,
output_dir=self.build_clib,
debug=self.debug
)
......@@ -9,10 +9,11 @@ from setuptools.extern import six
from pkg_resources import Distribution, PathMetadata, normalize_path
from setuptools.command.easy_install import easy_install
from setuptools import namespaces
import setuptools
class develop(easy_install):
class develop(namespaces.DevelopInstaller, easy_install):
"""Set up package for development"""
description = "install package in 'development mode'"
......@@ -30,6 +31,7 @@ class develop(easy_install):
if self.uninstall:
self.multi_version = True
self.uninstall_link()
self.uninstall_namespaces()
else:
self.install_for_development()
self.warn_deprecated_options()
......@@ -77,15 +79,28 @@ class develop(easy_install):
project_name=ei.egg_name
)
p = self.egg_base.replace(os.sep, '/')
if p != os.curdir:
p = '../' * (p.count('/') + 1)
self.setup_path = p
p = normalize_path(os.path.join(self.install_dir, self.egg_path, p))
if p != normalize_path(os.curdir):
self.setup_path = self._resolve_setup_path(
self.egg_base,
self.install_dir,
self.egg_path,
)
@staticmethod
def _resolve_setup_path(egg_base, install_dir, egg_path):
"""
Generate a path from egg_base back to '.' where the
setup script resides and ensure that path points to the
setup path from $install_dir/$egg_path.
"""
path_to_setup = egg_base.replace(os.sep, '/').rstrip('/')
if path_to_setup != os.curdir:
path_to_setup = '../' * (path_to_setup.count('/') + 1)
resolved = normalize_path(os.path.join(install_dir, egg_path, path_to_setup))
if resolved != normalize_path(os.curdir):
raise DistutilsOptionError(
"Can't get a consistent path to setup script from"
" installation directory", p, normalize_path(os.curdir))
" installation directory", resolved, normalize_path(os.curdir))
return path_to_setup
def install_for_development(self):
if six.PY3 and getattr(self.distribution, 'use_2to3', False):
......@@ -123,6 +138,8 @@ class develop(easy_install):
self.easy_install(setuptools.bootstrap_install_from)
setuptools.bootstrap_install_from = None
self.install_namespaces()
# 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:
......
......@@ -46,6 +46,7 @@ from setuptools.extern.six.moves import configparser, map
from setuptools import Command
from setuptools.sandbox import run_setup
from setuptools.py31compat import get_path, get_config_vars
from setuptools.py27compat import rmtree_safe
from setuptools.command import setopt
from setuptools.archive_util import unpack_archive
from setuptools.package_index import (
......@@ -58,7 +59,7 @@ from pkg_resources import (
Distribution, PathMetadata, EggMetadata, WorkingSet, DistributionNotFound,
VersionConflict, DEVELOP_DIST,
)
import pkg_resources
import pkg_resources.py31compat
# Turn on PEP440Warnings
warnings.filterwarnings("default", category=pkg_resources.PEP440Warning)
......@@ -473,8 +474,7 @@ class easy_install(Command):
else:
self.pth_file = None
PYTHONPATH = os.environ.get('PYTHONPATH', '').split(os.pathsep)
if instdir not in map(normalize_path, filter(None, PYTHONPATH)):
if instdir not in map(normalize_path, _pythonpath()):
# only PYTHONPATH dirs need a site.py, so pretend it's there
self.sitepy_installed = True
elif self.multi_version and not os.path.exists(pth_file):
......@@ -544,8 +544,7 @@ class easy_install(Command):
if ok_exists:
os.unlink(ok_file)
dirname = os.path.dirname(ok_file)
if not os.path.exists(dirname):
os.makedirs(dirname)
pkg_resources.py31compat.makedirs(dirname, exist_ok=True)
f = open(pth_file, 'w')
except (OSError, IOError):
self.cant_write_to_target()
......@@ -627,12 +626,20 @@ class easy_install(Command):
(spec.key, self.build_directory)
)
@contextlib.contextmanager
def _tmpdir(self):
tmpdir = tempfile.mkdtemp(prefix=six.u("easy_install-"))
try:
# cast to str as workaround for #709 and #710 and #712
yield str(tmpdir)
finally:
os.path.exists(tmpdir) and rmtree(rmtree_safe(tmpdir))
def easy_install(self, spec, deps=False):
tmpdir = tempfile.mkdtemp(prefix="easy_install-")
if not self.editable:
self.install_site_py()
try:
with self._tmpdir() as tmpdir:
if not isinstance(spec, Requirement):
if URL_SCHEME(spec):
# It's a url, download it to tmpdir and process
......@@ -664,10 +671,6 @@ class easy_install(Command):
else:
return self.install_item(spec, dist.location, tmpdir, deps)
finally:
if os.path.exists(tmpdir):
rmtree(tmpdir)
def install_item(self, spec, download, tmpdir, deps, install_needed=False):
# Installation is also needed if file in tmpdir or is not an egg
......@@ -1343,10 +1346,21 @@ class easy_install(Command):
setattr(self, attr, val)
def _pythonpath():
items = os.environ.get('PYTHONPATH', '').split(os.pathsep)
return filter(None, items)
def get_site_dirs():
# return a list of 'site' dirs
sitedirs = [_f for _f in os.environ.get('PYTHONPATH',
'').split(os.pathsep) if _f]
"""
Return a list of 'site' dirs
"""
sitedirs = []
# start with PYTHONPATH
sitedirs.extend(_pythonpath())
prefixes = [sys.prefix]
if sys.exec_prefix != sys.prefix:
prefixes.append(sys.exec_prefix)
......@@ -1670,7 +1684,7 @@ def _first_line_re():
def auto_chmod(func, arg, exc):
if func is os.remove and os.name == 'nt':
if func in [os.unlink, os.remove] and os.name == 'nt':
chmod(arg, stat.S_IWRITE)
return func(arg)
et, ev, _ = sys.exc_info()
......@@ -2008,7 +2022,7 @@ class ScriptWriter(object):
gui apps.
"""
template = textwrap.dedent("""
template = textwrap.dedent(r"""
# EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r
__requires__ = %(spec)r
import re
......
......@@ -32,11 +32,6 @@ from setuptools.glob import glob
from pkg_resources.extern import packaging
try:
from setuptools_svn import svn_utils
except ImportError:
pass
def translate_pattern(glob):
"""
......@@ -117,7 +112,8 @@ def translate_pattern(glob):
if not last_chunk:
pat += sep
return re.compile(pat + r'\Z(?ms)')
pat += r'\Z'
return re.compile(pat, flags=re.MULTILINE|re.DOTALL)
class egg_info(Command):
......@@ -126,18 +122,13 @@ class egg_info(Command):
user_options = [
('egg-base=', 'e', "directory containing .egg-info directories"
" (default: top of the source tree)"),
('tag-svn-revision', 'r',
"Add subversion revision ID to version number"),
('tag-date', 'd', "Add date stamp (e.g. 20050528) to version number"),
('tag-build=', 'b', "Specify explicit tag to add to version number"),
('no-svn-revision', 'R',
"Don't add subversion revision ID [default]"),
('no-date', 'D', "Don't include date stamp [default]"),
]
boolean_options = ['tag-date', 'tag-svn-revision']
boolean_options = ['tag-date']
negative_opt = {
'no-svn-revision': 'tag-svn-revision',
'no-date': 'tag-date',
}
......@@ -147,15 +138,26 @@ class egg_info(Command):
self.egg_base = None
self.egg_info = None
self.tag_build = None
self.tag_svn_revision = 0
self.tag_date = 0
self.broken_egg_info = False
self.vtags = None
####################################
# allow the 'tag_svn_revision' to be detected and
# set, supporting sdists built on older Setuptools.
@property
def tag_svn_revision(self):
pass
@tag_svn_revision.setter
def tag_svn_revision(self, value):
pass
####################################
def save_version_info(self, filename):
"""
Materialize the values of svn_revision and date into the
build tag. Install these keys in a deterministic order
Materialize the value of date into the
build tag. Install build keys in a deterministic order
to avoid arbitrary reordering on subsequent builds.
"""
egg_info = collections.OrderedDict()
......@@ -163,7 +165,6 @@ class egg_info(Command):
# when PYTHONHASHSEED=0
egg_info['tag_build'] = self.tags()
egg_info['tag_date'] = 0
egg_info['tag_svn_revision'] = 0
edit_config(filename, dict(egg_info=egg_info))
def finalize_options(self):
......@@ -280,22 +281,10 @@ class egg_info(Command):
version = ''
if self.tag_build:
version += self.tag_build
if self.tag_svn_revision:
warnings.warn(
"tag_svn_revision is deprecated and will not be honored "
"in a future release"
)
version += '-r%s' % self.get_svn_revision()
if self.tag_date:
version += time.strftime("-%Y%m%d")
return version
@staticmethod
def get_svn_revision():
if 'svn_utils' not in globals():
return "0"
return str(svn_utils.SvnInfo.load(os.curdir).get_revision())
def find_sources(self):
"""Generate SOURCES.txt manifest file"""
manifest_filename = os.path.join(self.egg_info, "SOURCES.txt")
......@@ -439,7 +428,11 @@ class FileList(_FileList):
def graft(self, dir):
"""Include all files from 'dir/'."""
found = distutils.filelist.findall(dir)
found = [
item
for match_dir in glob(dir)
for item in distutils.filelist.findall(match_dir)
]
self.extend(found)
return bool(found)
......@@ -455,7 +448,7 @@ class FileList(_FileList):
"""
if self.allfiles is None:
self.findall()
match = translate_pattern(os.path.join('**', '*' + pattern))
match = translate_pattern(os.path.join('**', pattern))
found = [f for f in self.allfiles if match.match(f)]
self.extend(found)
return bool(found)
......@@ -464,7 +457,7 @@ class FileList(_FileList):
"""
Exclude all files anywhere that match the pattern.
"""
match = translate_pattern(os.path.join('**', '*' + pattern))
match = translate_pattern(os.path.join('**', pattern))
return self._remove_files(match.match)
def append(self, item):
......@@ -604,6 +597,10 @@ def write_pkg_info(cmd, basename, filename):
metadata = cmd.distribution.metadata
metadata.version, oldver = cmd.egg_version, metadata.version
metadata.name, oldname = cmd.egg_name, metadata.name
metadata.long_description_content_type = getattr(
cmd.distribution,
'long_description_content_type'
)
try:
# write unescaped data to PKG-INFO, so older pkg_resources
# can still parse it
......
......@@ -37,7 +37,8 @@ class sdist(sdist_add_defaults, orig.sdist):
negative_opt = {}
READMES = 'README', 'README.rst', 'README.txt'
README_EXTENSIONS = ['', '.rst', '.txt', '.md']
READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS)
def run(self):
self.run_command('egg_info')
......
......@@ -4,14 +4,15 @@ import sys
import contextlib
import itertools
import unittest
from distutils.errors import DistutilsOptionError
from distutils.errors import DistutilsError, DistutilsOptionError
from distutils import log
from unittest import TestLoader
from setuptools.extern import six
from setuptools.extern.six.moves import map, filter
from pkg_resources import (resource_listdir, resource_exists, normalize_path,
working_set, _namespace_packages,
working_set, _namespace_packages, evaluate_marker,
add_activation_listener, require, EntryPoint)
from setuptools import Command
......@@ -66,7 +67,7 @@ class test(Command):
user_options = [
('test-module=', 'm', "Run 'test_suite' in specified module"),
('test-suite=', 's',
"Test suite to run (e.g. 'some_module.test_suite')"),
"Run single test, case or suite (e.g. 'module.test_suite')"),
('test-runner=', 'r', "Test runner to use"),
]
......@@ -190,9 +191,13 @@ class test(Command):
Install the requirements indicated by self.distribution and
return an iterable of the dists that were built.
"""
ir_d = dist.fetch_build_eggs(dist.install_requires or [])
ir_d = dist.fetch_build_eggs(dist.install_requires)
tr_d = dist.fetch_build_eggs(dist.tests_require or [])
return itertools.chain(ir_d, tr_d)
er_d = dist.fetch_build_eggs(
v for k, v in dist.extras_require.items()
if k.startswith(':') and evaluate_marker(k[1:])
)
return itertools.chain(ir_d, tr_d, er_d)
def run(self):
installed_dists = self.install_dists(self.distribution)
......@@ -225,12 +230,16 @@ class test(Command):
del_modules.append(name)
list(map(sys.modules.__delitem__, del_modules))
unittest.main(
test = unittest.main(
None, None, self._argv,
testLoader=self._resolve_as_ep(self.test_loader),
testRunner=self._resolve_as_ep(self.test_runner),
exit=False,
)
if not test.result.wasSuccessful():
msg = 'Test failed: %s' % test.result
self.announce(msg, log.ERROR)
raise DistutilsError(msg)
@property
def _argv(self):
......
......@@ -10,6 +10,10 @@ class upload(orig.upload):
def finalize_options(self):
orig.upload.finalize_options(self)
self.username = (
self.username or
getpass.getuser()
)
# Attempt to obtain password. Short circuit evaluation at the first
# sign of success.
self.password = (
......
......@@ -57,7 +57,6 @@ class upload_docs(upload):
self.target_dir = None
def finalize_options(self):
log.warn("Upload_docs command is deprecated. Use RTD instead.")
upload.finalize_options(self)
if self.upload_dir is None:
if self.has_sphinx():
......@@ -69,6 +68,8 @@ class upload_docs(upload):
else:
self.ensure_dirname('upload_dir')
self.target_dir = self.upload_dir
if 'pypi.python.org' in self.repository:
log.warn("Upload_docs command is deprecated. Use RTD instead.")
self.announce('Using upload directory %s' % self.target_dir)
def create_zipfile(self, filename):
......@@ -77,9 +78,8 @@ class upload_docs(upload):
self.mkpath(self.target_dir) # just in case
for root, dirs, files in os.walk(self.target_dir):
if root == self.target_dir and not files:
raise DistutilsOptionError(
"no files found in upload directory '%s'"
% self.target_dir)
tmpl = "no files found in upload directory '%s'"
raise DistutilsOptionError(tmpl % self.target_dir)
for name in files:
full = os.path.join(root, name)
relative = root[len(self.target_dir):].lstrip(os.path.sep)
......@@ -138,7 +138,7 @@ class upload_docs(upload):
part_groups = map(builder, data.items())
parts = itertools.chain.from_iterable(part_groups)
body_items = itertools.chain(parts, end_items)
content_type = 'multipart/form-data; boundary=%s' % boundary
content_type = 'multipart/form-data; boundary=%s' % boundary.decode('ascii')
return b''.join(body_items), content_type
def upload_file(self, filename):
......@@ -159,8 +159,8 @@ class upload_docs(upload):
body, ct = self._build_multipart(data)
self.announce("Submitting documentation to %s" % (self.repository),
log.INFO)
msg = "Submitting documentation to %s" % (self.repository)
self.announce(msg, log.INFO)
# build the Request
# We can't use urllib2 since we need to send the Basic
......@@ -191,16 +191,16 @@ class upload_docs(upload):
r = conn.getresponse()
if r.status == 200:
self.announce('Server response (%s): %s' % (r.status, r.reason),
log.INFO)
msg = 'Server response (%s): %s' % (r.status, r.reason)
self.announce(msg, log.INFO)
elif r.status == 301:
location = r.getheader('Location')
if location is None:
location = 'https://pythonhosted.org/%s/' % meta.get_name()
self.announce('Upload successful. Visit %s' % location,
log.INFO)
msg = 'Upload successful. Visit %s' % location
self.announce(msg, log.INFO)
else:
self.announce('Upload failed (%s): %s' % (r.status, r.reason),
log.ERROR)
msg = 'Upload failed (%s): %s' % (r.status, r.reason)
self.announce(msg, log.ERROR)
if self.show_response:
print('-' * 75, r.read(), '-' * 75)
......@@ -10,7 +10,8 @@ from distutils.errors import DistutilsOptionError, DistutilsFileError
from setuptools.extern.six import string_types
def read_configuration(filepath, find_others=False):
def read_configuration(
filepath, find_others=False, ignore_option_errors=False):
"""Read given configuration file and returns options from it as a dict.
:param str|unicode filepath: Path to configuration file
......@@ -19,6 +20,11 @@ def read_configuration(filepath, find_others=False):
:param bool find_others: Whether to search for other configuration files
which could be on in various places.
:param bool ignore_option_errors: Whether to silently ignore
options, values of which could not be resolved (e.g. due to exceptions
in directives such as file:, attr:, etc.).
If False exceptions are propagated as expected.
:rtype: dict
"""
from setuptools.dist import Distribution, _Distribution
......@@ -32,17 +38,21 @@ def read_configuration(filepath, find_others=False):
current_directory = os.getcwd()
os.chdir(os.path.dirname(filepath))
dist = Distribution()
try:
dist = Distribution()
filenames = dist.find_config_files() if find_others else []
if filepath not in filenames:
filenames.append(filepath)
filenames = dist.find_config_files() if find_others else []
if filepath not in filenames:
filenames.append(filepath)
_Distribution.parse_config_files(dist, filenames=filenames)
_Distribution.parse_config_files(dist, filenames=filenames)
handlers = parse_configuration(dist, dist.command_options)
handlers = parse_configuration(
dist, dist.command_options,
ignore_option_errors=ignore_option_errors)
os.chdir(current_directory)
finally:
os.chdir(current_directory)
return configuration_to_dict(handlers)
......@@ -76,7 +86,8 @@ def configuration_to_dict(handlers):
return config_dict
def parse_configuration(distribution, command_options):
def parse_configuration(
distribution, command_options, ignore_option_errors=False):
"""Performs additional parsing of configuration options
for a distribution.
......@@ -84,12 +95,18 @@ def parse_configuration(distribution, command_options):
:param Distribution distribution:
:param dict command_options:
:param bool ignore_option_errors: Whether to silently ignore
options, values of which could not be resolved (e.g. due to exceptions
in directives such as file:, attr:, etc.).
If False exceptions are propagated as expected.
:rtype: list
"""
meta = ConfigMetadataHandler(distribution.metadata, command_options)
meta = ConfigMetadataHandler(
distribution.metadata, command_options, ignore_option_errors)
meta.parse()
options = ConfigOptionsHandler(distribution, command_options)
options = ConfigOptionsHandler(
distribution, command_options, ignore_option_errors)
options.parse()
return [meta, options]
......@@ -111,7 +128,7 @@ class ConfigHandler(object):
"""
def __init__(self, target_obj, options):
def __init__(self, target_obj, options, ignore_option_errors=False):
sections = {}
section_prefix = self.section_prefix
......@@ -122,6 +139,7 @@ class ConfigHandler(object):
section_name = section_name.replace(section_prefix, '').strip('.')
sections[section_name] = section_options
self.ignore_option_errors = ignore_option_errors
self.target_obj = target_obj
self.sections = sections
self.set_options = []
......@@ -148,9 +166,19 @@ class ConfigHandler(object):
# Already inhabited. Skipping.
return
skip_option = False
parser = self.parsers.get(option_name)
if parser:
value = parser(value)
try:
value = parser(value)
except Exception:
skip_option = True
if not self.ignore_option_errors:
raise
if skip_option:
return
setter = getattr(target_obj, 'set_%s' % option_name, None)
if setter is None:
......@@ -217,33 +245,39 @@ class ConfigHandler(object):
directory with setup.py.
Examples:
include: LICENSE
include: src/file.txt
file: LICENSE
file: README.rst, CHANGELOG.md, src/file.txt
:param str value:
:rtype: str
"""
include_directive = 'file:'
if not isinstance(value, string_types):
return value
include_directive = 'file:'
if not value.startswith(include_directive):
return value
current_directory = os.getcwd()
filepath = value.replace(include_directive, '').strip()
filepath = os.path.abspath(filepath)
if not filepath.startswith(current_directory):
spec = value[len(include_directive):]
filepaths = (os.path.abspath(path.strip()) for path in spec.split(','))
return '\n'.join(
cls._read_file(path)
for path in filepaths
if (cls._assert_local(path) or True)
and os.path.isfile(path)
)
@staticmethod
def _assert_local(filepath):
if not filepath.startswith(os.getcwd()):
raise DistutilsOptionError(
'`file:` directive can not access %s' % filepath)
if os.path.isfile(filepath):
with io.open(filepath, encoding='utf-8') as f:
value = f.read()
return value
@staticmethod
def _read_file(filepath):
with io.open(filepath, encoding='utf-8') as f:
return f.read()
@classmethod
def _parse_attr(cls, value):
......@@ -335,7 +369,10 @@ class ConfigHandler(object):
method_postfix = '_%s' % section_name
section_parser_method = getattr(
self, 'parse_section%s' % method_postfix, None)
self,
# Dots in section names are tranlsated into dunderscores.
('parse_section%s' % method_postfix).replace('.', '__'),
None)
if section_parser_method is None:
raise DistutilsOptionError(
......@@ -381,17 +418,6 @@ class ConfigMetadataHandler(ConfigHandler):
'version': self._parse_version,
}
def parse_section_classifiers(self, section_options):
"""Parses configuration file section.
:param dict section_options:
"""
classifiers = []
for begin, (_, rest) in section_options.items():
classifiers.append('%s :%s' % (begin.title(), rest))
self['classifiers'] = classifiers
def _parse_version(self, value):
"""Parses `version` option value.
......@@ -442,6 +468,7 @@ class ConfigOptionsHandler(ConfigHandler):
'tests_require': parse_list_semicolon,
'packages': self._parse_packages,
'entry_points': self._parse_file,
'py_modules': parse_list,
}
def _parse_packages(self, value):
......@@ -455,8 +482,34 @@ class ConfigOptionsHandler(ConfigHandler):
if not value.startswith(find_directive):
return self._parse_list(value)
# Read function arguments from a dedicated section.
find_kwargs = self.parse_section_packages__find(
self.sections.get('packages.find', {}))
from setuptools import find_packages
return find_packages()
return find_packages(**find_kwargs)
def parse_section_packages__find(self, section_options):
"""Parses `packages.find` configuration file section.
To be used in conjunction with _parse_packages().
:param dict section_options:
"""
section_data = self._parse_section_to_dict(
section_options, self._parse_list)
valid_keys = ['where', 'include', 'exclude']
find_kwargs = dict(
[(k, v) for k, v in section_data.items() if k in valid_keys and v])
where = find_kwargs.get('where')
if where is not None:
find_kwargs['where'] = where[0] # cast list to single val
return find_kwargs
def parse_section_entry_points(self, section_options):
"""Parses `entry_points` configuration file section.
......
from distutils.dep_util import newer_group
# yes, this is was almost entirely copy-pasted from
# 'newer_pairwise()', this is just another convenience
# function.
def newer_pairwise_group(sources_groups, targets):
"""Walk both arguments in parallel, testing if each source group is newer
than its corresponding target. Returns a pair of lists (sources_groups,
targets) where sources is newer than target, according to the semantics
of 'newer_group()'.
"""
if len(sources_groups) != len(targets):
raise ValueError("'sources_group' and 'targets' must be the same length")
# build a pair of lists (sources_groups, targets) where source is newer
n_sources = []
n_targets = []
for i in range(len(sources_groups)):
if newer_group(sources_groups[i], targets[i]):
n_sources.append(sources_groups[i])
n_targets.append(targets[i])
return n_sources, n_targets
......@@ -4,7 +4,8 @@ import marshal
from distutils.version import StrictVersion
from imp import PKG_DIRECTORY, PY_COMPILED, PY_SOURCE, PY_FROZEN
from setuptools.extern import six
from .py33compat import Bytecode
__all__ = [
'Require', 'find_module', 'get_module_constant', 'extract_constant'
......@@ -78,39 +79,6 @@ class Require:
return self.version_ok(version)
def _iter_code(code):
"""Yield '(op,arg)' pair for each operation in code object 'code'"""
from array import array
from dis import HAVE_ARGUMENT, EXTENDED_ARG
bytes = array('b', code.co_code)
eof = len(code.co_code)
ptr = 0
extended_arg = 0
while ptr < eof:
op = bytes[ptr]
if op >= HAVE_ARGUMENT:
arg = bytes[ptr + 1] + bytes[ptr + 2] * 256 + extended_arg
ptr += 3
if op == EXTENDED_ARG:
long_type = six.integer_types[-1]
extended_arg = arg * long_type(65536)
continue
else:
arg = None
ptr += 1
yield op, arg
def find_module(module, paths=None):
"""Just like 'imp.find_module()', but with package support"""
......@@ -176,9 +144,8 @@ def extract_constant(code, symbol, default=-1):
only 'STORE_NAME' and 'STORE_GLOBAL' opcodes are checked, and 'symbol'
must be present in 'code.co_names'.
"""
if symbol not in code.co_names:
# name's not there, can't possibly be an assigment
# name's not there, can't possibly be an assignment
return None
name_idx = list(code.co_names).index(symbol)
......@@ -189,7 +156,9 @@ def extract_constant(code, symbol, default=-1):
const = default
for op, arg in _iter_code(code):
for byte_code in Bytecode(code):
op = byte_code.opcode
arg = byte_code.arg
if op == LOAD_CONST:
const = code.co_consts[arg]
......
This diff is collapsed.
......@@ -8,6 +8,7 @@ import platform
import types
import functools
from importlib import import_module
import inspect
from setuptools.extern import six
......@@ -20,6 +21,20 @@ if you think you need this functionality.
"""
def _get_mro(cls):
"""
Returns the bases classes for cls sorted by the MRO.
Works around an issue on Jython where inspect.getmro will not return all
base classes if multiple classes share the same name. Instead, this
function will return a tuple containing the class itself, and the contents
of cls.__bases__. See https://github.com/pypa/setuptools/issues/1024.
"""
if platform.python_implementation() == "Jython":
return (cls,) + cls.__bases__
return inspect.getmro(cls)
def get_unpatched(item):
lookup = (
get_unpatched_class if isinstance(item, six.class_types) else
......@@ -35,25 +50,23 @@ def get_unpatched_class(cls):
Also ensures that no other distutils extension monkeypatched the distutils
first.
"""
while cls.__module__.startswith('setuptools'):
cls, = cls.__bases__
if not cls.__module__.startswith('distutils'):
external_bases = (
cls
for cls in _get_mro(cls)
if not cls.__module__.startswith('setuptools')
)
base = next(external_bases)
if not base.__module__.startswith('distutils'):
msg = "distutils has already been patched by %r" % cls
raise AssertionError(msg)
return cls
return base
def patch_all():
# we can't patch distutils.cmd, alas
distutils.core.Command = setuptools.Command
has_issue_12885 = (
sys.version_info < (3, 4, 6)
or
(3, 5) < sys.version_info <= (3, 5, 3)
or
(3, 6) < sys.version_info
)
has_issue_12885 = sys.version_info <= (3, 5, 3)
if has_issue_12885:
# fix findall bug in distutils (http://bugs.python.org/issue12885)
......@@ -67,8 +80,6 @@ def patch_all():
(3, 4) < sys.version_info < (3, 4, 6)
or
(3, 5) < sys.version_info <= (3, 5, 3)
or
(3, 6) < sys.version_info
)
if needs_warehouse:
......
This diff is collapsed.
......@@ -30,15 +30,29 @@ class Installer:
with open(filename, 'wt') as f:
f.writelines(lines)
def uninstall_namespaces(self):
filename, ext = os.path.splitext(self._get_target())
filename += self.nspkg_ext
if not os.path.exists(filename):
return
log.info("Removing %s", filename)
os.remove(filename)
def _get_target(self):
return self.target
_nspkg_tmpl = (
"import sys, types, os",
"pep420 = sys.version_info > (3, 3)",
"has_mfs = sys.version_info > (3, 5)",
"p = os.path.join(%(root)s, *%(pth)r)",
"ie = os.path.exists(os.path.join(p,'__init__.py'))",
"m = not ie and not pep420 and "
"importlib = has_mfs and __import__('importlib.util')",
"has_mfs and __import__('importlib.machinery')",
"m = has_mfs and "
"sys.modules.setdefault(%(pkg)r, "
"importlib.util.module_from_spec("
"importlib.machinery.PathFinder.find_spec(%(pkg)r, "
"[os.path.dirname(p)])))",
"m = m or "
"sys.modules.setdefault(%(pkg)r, types.ModuleType(%(pkg)r))",
"mp = (m or []) and m.__dict__.setdefault('__path__',[])",
"(p not in mp) and mp.append(p)",
......
......@@ -20,7 +20,7 @@ from setuptools.extern.six.moves import urllib, http_client, configparser, map
import setuptools
from pkg_resources import (
CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST,
require, Environment, find_distributions, safe_name, safe_version,
Environment, find_distributions, safe_name, safe_version,
to_filename, Requirement, DEVELOP_DIST,
)
from setuptools import ssl_support
......@@ -29,12 +29,12 @@ from distutils.errors import DistutilsError
from fnmatch import translate
from setuptools.py27compat import get_all_headers
EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$')
EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$')
HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I)
# this is here to fix emacs' cruddy broken syntax highlighting
PYPI_MD5 = re.compile(
'<a href="([^"#]+)">([^<]+)</a>\n\s+\\(<a (?:title="MD5 hash"\n\s+)'
'href="[^?]+\?:action=show_md5&amp;digest=([0-9a-f]{32})">md5</a>\\)'
'<a href="([^"#]+)">([^<]+)</a>\n\\s+\\(<a (?:title="MD5 hash"\n\\s+)'
'href="[^?]+\\?:action=show_md5&amp;digest=([0-9a-f]{32})">md5</a>\\)'
)
URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match
EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split()
......@@ -47,7 +47,7 @@ __all__ = [
_SOCKET_TIMEOUT = 15
_tmpl = "setuptools/{setuptools.__version__} Python-urllib/{py_major}"
user_agent = _tmpl.format(py_major=sys.version[:3], **globals())
user_agent = _tmpl.format(py_major=sys.version[:3], setuptools=setuptools)
def parse_requirement_arg(spec):
......@@ -160,7 +160,7 @@ def interpret_distro_name(
# versions in distribution archive names (sdist and bdist).
parts = basename.split('-')
if not py_version and any(re.match('py\d\.\d$', p) for p in parts[2:]):
if not py_version and any(re.match(r'py\d\.\d$', p) for p in parts[2:]):
# it is a bdist_dumb, not an sdist -- bail out
return
......@@ -204,7 +204,7 @@ def unique_values(func):
return wrapper
REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I)
REL = re.compile(r"""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I)
# this line is here to fix emacs' cruddy broken syntax highlighting
......
......@@ -2,7 +2,9 @@
Compatibility Support for Python 2.7 and earlier
"""
import sys
import platform
from setuptools.extern import six
def get_all_headers(message, key):
......@@ -12,7 +14,15 @@ def get_all_headers(message, key):
return message.get_all(key)
if sys.version_info < (3,):
if six.PY2:
def get_all_headers(message, key):
return message.getheaders(key)
linux_py2_ascii = (
platform.system() == 'Linux' and
six.PY2
)
rmtree_safe = str if linux_py2_ascii else lambda x: x
"""Workaround for http://bugs.python.org/issue24672"""
import dis
import array
import collections
from setuptools.extern import six
OpArg = collections.namedtuple('OpArg', 'opcode arg')
class Bytecode_compat(object):
def __init__(self, code):
self.code = code
def __iter__(self):
"""Yield '(op,arg)' pair for each operation in code object 'code'"""
bytes = array.array('b', self.code.co_code)
eof = len(self.code.co_code)
ptr = 0
extended_arg = 0
while ptr < eof:
op = bytes[ptr]
if op >= dis.HAVE_ARGUMENT:
arg = bytes[ptr + 1] + bytes[ptr + 2] * 256 + extended_arg
ptr += 3
if op == dis.EXTENDED_ARG:
long_type = six.integer_types[-1]
extended_arg = arg * long_type(65536)
continue
else:
arg = None
ptr += 1
yield OpArg(op, arg)
Bytecode = getattr(dis, 'Bytecode', Bytecode_compat)
import sys
from distutils.errors import DistutilsOptionError
from distutils.util import strtobool
from distutils.debug import DEBUG
class Distribution_parse_config_files:
"""
Mix-in providing forward-compatibility for functionality to be
included by default on Python 3.7.
Do not edit the code in this class except to update functionality
as implemented in distutils.
"""
def parse_config_files(self, filenames=None):
from configparser import ConfigParser
# Ignore install directory options if we have a venv
if sys.prefix != sys.base_prefix:
ignore_options = [
'install-base', 'install-platbase', 'install-lib',
'install-platlib', 'install-purelib', 'install-headers',
'install-scripts', 'install-data', 'prefix', 'exec-prefix',
'home', 'user', 'root']
else:
ignore_options = []
ignore_options = frozenset(ignore_options)
if filenames is None:
filenames = self.find_config_files()
if DEBUG:
self.announce("Distribution.parse_config_files():")
parser = ConfigParser(interpolation=None)
for filename in filenames:
if DEBUG:
self.announce(" reading %s" % filename)
parser.read(filename)
for section in parser.sections():
options = parser.options(section)
opt_dict = self.get_option_dict(section)
for opt in options:
if opt != '__name__' and opt not in ignore_options:
val = parser.get(section,opt)
opt = opt.replace('-', '_')
opt_dict[opt] = (filename, val)
# Make the ConfigParser forget everything (so we retain
# the original filenames that options come from)
parser.__init__()
# If there was a "global" section in the config file, use it
# to set Distribution options.
if 'global' in self.command_options:
for (opt, (src, val)) in self.command_options['global'].items():
alias = self.negative_opt.get(opt)
try:
if alias:
setattr(self, alias, not strtobool(val))
elif opt in ('verbose', 'dry_run'): # ugh!
setattr(self, opt, strtobool(val))
else:
setattr(self, opt, val)
except ValueError as msg:
raise DistutilsOptionError(msg)
if sys.version_info < (3,):
# Python 2 behavior is sufficient
class Distribution_parse_config_files:
pass
if False:
# When updated behavior is available upstream,
# disable override here.
class Distribution_parse_config_files:
pass
......@@ -7,11 +7,12 @@ import itertools
import re
import contextlib
import pickle
import textwrap
from setuptools.extern import six
from setuptools.extern.six.moves import builtins, map
import pkg_resources
import pkg_resources.py31compat
if sys.platform.startswith('java'):
import org.python.modules.posix.PosixModule as _os
......@@ -25,6 +26,7 @@ _open = open
from distutils.errors import DistutilsError
from pkg_resources import working_set
__all__ = [
"AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup",
]
......@@ -68,8 +70,7 @@ def override_temp(replacement):
"""
Monkey-patch tempfile.tempdir with replacement, ensuring it exists
"""
if not os.path.isdir(replacement):
os.makedirs(replacement)
pkg_resources.py31compat.makedirs(replacement, exist_ok=True)
saved = tempfile.tempdir
......@@ -211,7 +212,7 @@ def _needs_hiding(mod_name):
>>> _needs_hiding('Cython')
True
"""
pattern = re.compile('(setuptools|pkg_resources|distutils|Cython)(\.|$)')
pattern = re.compile(r'(setuptools|pkg_resources|distutils|Cython)(\.|$)')
return bool(pattern.match(mod_name))
......@@ -237,11 +238,16 @@ def run_setup(setup_script, args):
working_set.__init__()
working_set.callbacks.append(lambda dist: dist.activate())
def runner():
ns = dict(__file__=setup_script, __name__='__main__')
_execfile(setup_script, ns)
# __file__ should be a byte string on Python 2 (#712)
dunder_file = (
setup_script
if isinstance(setup_script, str) else
setup_script.encode(sys.getfilesystemencoding())
)
DirectorySandbox(setup_dir).run(runner)
with DirectorySandbox(setup_dir):
ns = dict(__file__=dunder_file, __name__='__main__')
_execfile(setup_script, ns)
except SystemExit as v:
if v.args and v.args[0]:
raise
......@@ -263,21 +269,24 @@ class AbstractSandbox:
for name in self._attrs:
setattr(os, name, getattr(source, name))
def __enter__(self):
self._copy(self)
if _file:
builtins.file = self._file
builtins.open = self._open
self._active = True
def __exit__(self, exc_type, exc_value, traceback):
self._active = False
if _file:
builtins.file = _file
builtins.open = _open
self._copy(_os)
def run(self, func):
"""Run 'func' under os sandboxing"""
try:
self._copy(self)
if _file:
builtins.file = self._file
builtins.open = self._open
self._active = True
with self:
return func()
finally:
self._active = False
if _file:
builtins.file = _file
builtins.open = _open
self._copy(_os)
def _mk_dual_path_wrapper(name):
original = getattr(_os, name)
......@@ -380,7 +389,7 @@ class DirectorySandbox(AbstractSandbox):
_exception_patterns = [
# Allow lib2to3 to attempt to save a pickled grammar object (#121)
'.*lib2to3.*\.pickle$',
r'.*lib2to3.*\.pickle$',
]
"exempt writing to paths that match the pattern"
......@@ -465,16 +474,18 @@ WRITE_FLAGS = functools.reduce(
class SandboxViolation(DistutilsError):
"""A setup script attempted to modify the filesystem outside the sandbox"""
def __str__(self):
return """SandboxViolation: %s%r %s
The package setup script has attempted to modify files on your system
that are not within the EasyInstall build area, and has been aborted.
tmpl = textwrap.dedent("""
SandboxViolation: {cmd}{args!r} {kwargs}
This package cannot be safely installed by EasyInstall, and may not
support alternate installation locations even if you run its setup
script by hand. Please inform the package's author and the EasyInstall
maintainers to find out if a fix or workaround is available.""" % self.args
The package setup script has attempted to modify files on your system
that are not within the EasyInstall build area, and has been aborted.
This package cannot be safely installed by EasyInstall, and may not
support alternate installation locations even if you run its setup
script by hand. Please inform the package's author and the EasyInstall
maintainers to find out if a fix or workaround is available.
""").lstrip()
#
def __str__(self):
cmd, args, kwargs = self.args
return self.tmpl.format(**locals())
......@@ -2,10 +2,10 @@ import os
import socket
import atexit
import re
import functools
from setuptools.extern.six.moves import urllib, http_client, map
from setuptools.extern.six.moves import urllib, http_client, map, filter
import pkg_resources
from pkg_resources import ResolutionError, ExtractionError
try:
......@@ -204,47 +204,52 @@ def opener_for(ca_bundle=None):
).open
_wincerts = None
# from jaraco.functools
def once(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if not hasattr(func, 'always_returns'):
func.always_returns = func(*args, **kwargs)
return func.always_returns
return wrapper
@once
def get_win_certfile():
global _wincerts
if _wincerts is not None:
return _wincerts.name
try:
from wincertstore import CertFile
import wincertstore
except ImportError:
return None
class MyCertFile(CertFile):
def __init__(self, stores=(), certs=()):
CertFile.__init__(self)
for store in stores:
self.addstore(store)
self.addcerts(certs)
class CertFile(wincertstore.CertFile):
def __init__(self):
super(CertFile, self).__init__()
atexit.register(self.close)
def close(self):
try:
super(MyCertFile, self).close()
super(CertFile, self).close()
except OSError:
pass
_wincerts = MyCertFile(stores=['CA', 'ROOT'])
_wincerts = CertFile()
_wincerts.addstore('CA')
_wincerts.addstore('ROOT')
return _wincerts.name
def find_ca_bundle():
"""Return an existing CA bundle path, or None"""
if os.name == 'nt':
return get_win_certfile()
else:
for cert_path in cert_paths:
if os.path.isfile(cert_path):
return cert_path
extant_cert_paths = filter(os.path.isfile, cert_paths)
return (
get_win_certfile()
or next(extant_cert_paths, None)
or _certifi_where()
)
def _certifi_where():
try:
import certifi
return certifi.where()
return __import__('certifi').where()
except (ImportError, ResolutionError, ExtractionError):
return None
pass
"""Tests for the 'setuptools' package"""
import locale
import sys
import os
import distutils.core
......@@ -16,8 +17,7 @@ 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")
is_ascii = locale.getpreferredencoding() == 'ANSI_X3.4-1968'
fail_on_ascii = pytest.mark.xfail(is_ascii, reason="Test fails in this locale")
......
import os
import pkg_resources.py31compat
def build_files(file_defs, prefix=""):
"""
Build a set of files/directories, as described by the file_defs dictionary.
......@@ -24,8 +27,7 @@ def build_files(file_defs, prefix=""):
for name, contents in file_defs.items():
full_name = os.path.join(prefix, name)
if isinstance(contents, dict):
if not os.path.exists(full_name):
os.makedirs(full_name)
pkg_resources.py31compat.makedirs(full_name, exist_ok=True)
build_files(contents, prefix=full_name)
else:
with open(full_name, 'w') as f:
......
......@@ -41,4 +41,4 @@ class Test:
# let's see if we got our egg link at the right place
[content] = os.listdir('dist')
assert re.match('foo-0.0.0-py[23].\d.egg$', content)
assert re.match(r'foo-0.0.0-py[23].\d.egg$', content)
import pytest
import os
import shutil
import mock
from distutils.errors import DistutilsSetupError
from setuptools.command.build_clib import build_clib
from setuptools.dist import Distribution
class TestBuildCLib:
@mock.patch(
'setuptools.command.build_clib.newer_pairwise_group'
)
def test_build_libraries(self, mock_newer):
dist = Distribution()
cmd = build_clib(dist)
# this will be a long section, just making sure all
# exceptions are properly raised
libs = [('example', {'sources': 'broken.c'})]
with pytest.raises(DistutilsSetupError):
cmd.build_libraries(libs)
obj_deps = 'some_string'
libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})]
with pytest.raises(DistutilsSetupError):
cmd.build_libraries(libs)
obj_deps = {'': ''}
libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})]
with pytest.raises(DistutilsSetupError):
cmd.build_libraries(libs)
obj_deps = {'source.c': ''}
libs = [('example', {'sources': ['source.c'], 'obj_deps': obj_deps})]
with pytest.raises(DistutilsSetupError):
cmd.build_libraries(libs)
# with that out of the way, let's see if the crude dependency
# system works
cmd.compiler = mock.MagicMock(spec=cmd.compiler)
mock_newer.return_value = ([],[])
obj_deps = {'': ('global.h',), 'example.c': ('example.h',)}
libs = [('example', {'sources': ['example.c'] ,'obj_deps': obj_deps})]
cmd.build_libraries(libs)
assert [['example.c', 'global.h', 'example.h']] in mock_newer.call_args[0]
assert not cmd.compiler.compile.called
assert cmd.compiler.create_static_lib.call_count == 1
# reset the call numbers so we can test again
cmd.compiler.reset_mock()
mock_newer.return_value = '' # anything as long as it's not ([],[])
cmd.build_libraries(libs)
assert cmd.compiler.compile.call_count == 1
assert cmd.compiler.create_static_lib.call_count == 1
This diff is collapsed.
from setuptools.dep_util import newer_pairwise_group
import os
import pytest
@pytest.fixture
def groups_target(tmpdir):
"""Sets up some older sources, a target and newer sources.
Returns a 3-tuple in this order.
"""
creation_order = ['older.c', 'older.h', 'target.o', 'newer.c', 'newer.h']
mtime = 0
for i in range(len(creation_order)):
creation_order[i] = os.path.join(str(tmpdir), creation_order[i])
with open(creation_order[i], 'w'):
pass
# make sure modification times are sequential
os.utime(creation_order[i], (mtime, mtime))
mtime += 1
return creation_order[:2], creation_order[2], creation_order[3:]
def test_newer_pairwise_group(groups_target):
older = newer_pairwise_group([groups_target[0]], [groups_target[1]])
newer = newer_pairwise_group([groups_target[2]], [groups_target[1]])
assert older == ([], [])
assert newer == ([groups_target[2]], [groups_target[1]])
import sys
from setuptools import depends
class TestGetModuleConstant:
def test_basic(self):
"""
Invoke get_module_constant on a module in
the test package.
"""
mod_name = 'setuptools.tests.mod_with_constant'
val = depends.get_module_constant(mod_name, 'value')
assert val == 'three, sir!'
assert 'setuptools.tests.mod_with_constant' not in sys.modules
This diff is collapsed.
from setuptools import Distribution
from setuptools.extern.six.moves.urllib.request import pathname2url
from setuptools.extern.six.moves.urllib_parse import urljoin
from .textwrap import DALS
from .test_easy_install import make_nspkg_sdist
def test_dist_fetch_build_egg(tmpdir):
"""
Check multiple calls to `Distribution.fetch_build_egg` work as expected.
"""
index = tmpdir.mkdir('index')
index_url = urljoin('file://', pathname2url(str(index)))
def sdist_with_index(distname, version):
dist_dir = index.mkdir(distname)
dist_sdist = '%s-%s.tar.gz' % (distname, version)
make_nspkg_sdist(str(dist_dir.join(dist_sdist)), distname, version)
with dist_dir.join('index.html').open('w') as fp:
fp.write(DALS(
'''
<!DOCTYPE html><html><body>
<a href="{dist_sdist}" rel="internal">{dist_sdist}</a><br/>
</body></html>
'''
).format(dist_sdist=dist_sdist))
sdist_with_index('barbazquux', '3.2.0')
sdist_with_index('barbazquux-runner', '2.11.1')
with tmpdir.join('setup.cfg').open('w') as fp:
fp.write(DALS(
'''
[easy_install]
index_url = {index_url}
'''
).format(index_url=index_url))
reqs = '''
barbazquux-runner
barbazquux
'''.split()
with tmpdir.as_cwd():
dist = Distribution()
resolved_dists = [
dist.fetch_build_egg(r)
for r in reqs
]
assert [dist.key for dist in resolved_dists if dist] == reqs
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -5,12 +5,9 @@ Tests for msvc support module.
import os
import contextlib
import distutils.errors
import mock
import pytest
try:
from unittest import mock
except ImportError:
import mock
from . import contexts
......
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.
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