summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.rst24
-rwxr-xr-xsetup.cfg3
-rwxr-xr-xsetup.py2
-rw-r--r--setuptools/__init__.py22
-rw-r--r--setuptools/build_meta.py34
-rw-r--r--setuptools/command/__init__.py3
-rw-r--r--setuptools/command/bdist_egg.py18
-rwxr-xr-xsetuptools/command/develop.py4
-rw-r--r--setuptools/command/dist_info.py8
-rwxr-xr-xsetuptools/command/easy_install.py2
-rw-r--r--setuptools/command/test.py11
-rw-r--r--setuptools/dist.py40
-rwxr-xr-xsetuptools/package_index.py43
-rw-r--r--setuptools/tests/test_bdist_egg.py26
-rw-r--r--setuptools/tests/test_build_meta.py65
-rw-r--r--setuptools/tests/test_dist.py1
-rw-r--r--setuptools/tests/test_easy_install.py85
-rw-r--r--setuptools/tests/test_test.py90
18 files changed, 376 insertions, 105 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 6b84c03f..9ca45031 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,9 +1,33 @@
+v36.7.3
+-------
+
+* #1175: Bug fixes to ``build_meta`` module.
+
+v36.7.2
+-------
+
+* #701: Fixed duplicate test discovery on Python 3.
+
+v36.7.1
+-------
+
+* #1193: Avoid test failures in bdist_egg when
+ PYTHONDONTWRITEBYTECODE is set.
+
+v36.7.0
+-------
+
+* #1054: Support ``setup_requires`` in ``setup.cfg`` files.
+
v36.6.1
-------
* #1132: Removed redundant and costly serialization/parsing step
in ``EntryPoint.__init__``.
+* #844: ``bdist_egg --exclude-source-files`` now tested and works
+ on Python 3.
+
v36.6.0
-------
diff --git a/setup.cfg b/setup.cfg
index 8422a269..850f5762 100755
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 36.6.0
+current_version = 36.7.3
commit = True
tag = True
@@ -26,3 +26,4 @@ universal = 1
license_file = LICENSE
[bumpversion:file:setup.py]
+
diff --git a/setup.py b/setup.py
index 33003465..b5e7879b 100755
--- a/setup.py
+++ b/setup.py
@@ -89,7 +89,7 @@ def pypi_link(pkg_filename):
setup_params = dict(
name="setuptools",
- version="36.6.0",
+ version="36.7.3",
description="Easily download, build, install, upgrade, and uninstall "
"Python packages",
author="Python Packaging Authority",
diff --git a/setuptools/__init__.py b/setuptools/__init__.py
index 04f76740..7da47fbe 100644
--- a/setuptools/__init__.py
+++ b/setuptools/__init__.py
@@ -109,7 +109,27 @@ class PEP420PackageFinder(PackageFinder):
find_packages = PackageFinder.find
-setup = distutils.core.setup
+
+def _install_setup_requires(attrs):
+ # Note: do not use `setuptools.Distribution` directly, as
+ # our PEP 517 backend patch `distutils.core.Distribution`.
+ dist = distutils.core.Distribution(dict(
+ (k, v) for k, v in attrs.items()
+ if k in ('dependency_links', 'setup_requires')
+ ))
+ # Honor setup.cfg's options.
+ dist.parse_config_files(ignore_option_errors=True)
+ if dist.setup_requires:
+ dist.fetch_build_eggs(dist.setup_requires)
+
+
+def setup(**attrs):
+ # Make sure we have any requirements needed to interpret 'attrs'.
+ _install_setup_requires(attrs)
+ return distutils.core.setup(**attrs)
+
+setup.__doc__ = distutils.core.setup.__doc__
+
_Command = monkey.get_unpatched(distutils.core.Command)
diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py
index 54f2987b..609ea1e5 100644
--- a/setuptools/build_meta.py
+++ b/setuptools/build_meta.py
@@ -65,10 +65,11 @@ def _run_setup(setup_script='setup.py'):
# Note that we can reuse our build directory between calls
# Correctness comes first, then optimization later
__file__ = setup_script
+ __name__ = '__main__'
f = getattr(tokenize, 'open', open)(__file__)
code = f.read().replace('\\r\\n', '\\n')
f.close()
- exec(compile(code, __file__, 'exec'))
+ exec(compile(code, __file__, 'exec'), locals())
def _fix_config(config_settings):
@@ -92,6 +93,11 @@ def _get_build_requires(config_settings):
return requirements
+def _get_immediate_subdirectories(a_dir):
+ return [name for name in os.listdir(a_dir)
+ if os.path.isdir(os.path.join(a_dir, name))]
+
+
def get_requires_for_build_wheel(config_settings=None):
config_settings = _fix_config(config_settings)
return _get_build_requires(config_settings)
@@ -105,11 +111,29 @@ def get_requires_for_build_sdist(config_settings=None):
def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
sys.argv = sys.argv[:1] + ['dist_info', '--egg-base', metadata_directory]
_run_setup()
+
+ dist_info_directory = metadata_directory
+ while True:
+ dist_infos = [f for f in os.listdir(dist_info_directory)
+ if f.endswith('.dist-info')]
+
+ if len(dist_infos) == 0 and \
+ len(_get_immediate_subdirectories(dist_info_directory)) == 1:
+ dist_info_directory = os.path.join(
+ dist_info_directory, os.listdir(dist_info_directory)[0])
+ continue
+
+ assert len(dist_infos) == 1
+ break
+
+ # PEP 517 requires that the .dist-info directory be placed in the
+ # metadata_directory. To comply, we MUST copy the directory to the root
+ if dist_info_directory != metadata_directory:
+ shutil.move(
+ os.path.join(dist_info_directory, dist_infos[0]),
+ metadata_directory)
+ shutil.rmtree(dist_info_directory, ignore_errors=True)
- dist_infos = [f for f in os.listdir(metadata_directory)
- if f.endswith('.dist-info')]
-
- assert len(dist_infos) == 1
return dist_infos[0]
diff --git a/setuptools/command/__init__.py b/setuptools/command/__init__.py
index 4fe3bb56..fe619e2e 100644
--- a/setuptools/command/__init__.py
+++ b/setuptools/command/__init__.py
@@ -2,7 +2,8 @@ __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', 'build_clib', 'dist_info',
+ 'register', 'bdist_wininst', 'upload_docs', 'upload', 'build_clib',
+ 'dist_info',
]
from distutils.command.bdist import bdist
diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py
index 51755d52..5fdb62d9 100644
--- a/setuptools/command/bdist_egg.py
+++ b/setuptools/command/bdist_egg.py
@@ -8,6 +8,7 @@ from distutils import log
from types import CodeType
import sys
import os
+import re
import textwrap
import marshal
@@ -240,11 +241,26 @@ class bdist_egg(Command):
log.info("Removing .py files from temporary directory")
for base, dirs, files in walk_egg(self.bdist_dir):
for name in files:
+ path = os.path.join(base, name)
+
if name.endswith('.py'):
- path = os.path.join(base, name)
log.debug("Deleting %s", path)
os.unlink(path)
+ if base.endswith('__pycache__'):
+ path_old = path
+
+ pattern = r'(?P<name>.+)\.(?P<magic>[^.]+)\.pyc'
+ m = re.match(pattern, name)
+ path_new = os.path.join(base, os.pardir, m.group('name') + '.pyc')
+ log.info("Renaming file from [%s] to [%s]" % (path_old, path_new))
+ try:
+ os.remove(path_new)
+ except OSError:
+ pass
+ os.rename(path_old, path_new)
+
+
def zip_safe(self):
safe = getattr(self.distribution, 'zip_safe', None)
if safe is not None:
diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py
index 85b23c60..959c932a 100755
--- a/setuptools/command/develop.py
+++ b/setuptools/command/develop.py
@@ -95,7 +95,9 @@ class develop(namespaces.DevelopInstaller, easy_install):
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))
+ 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"
diff --git a/setuptools/command/dist_info.py b/setuptools/command/dist_info.py
index c6c6dacb..c45258fa 100644
--- a/setuptools/command/dist_info.py
+++ b/setuptools/command/dist_info.py
@@ -4,7 +4,6 @@ As defined in the wheel specification
"""
import os
-import shutil
from distutils.core import Command
from distutils import log
@@ -27,14 +26,11 @@ class dist_info(Command):
def run(self):
egg_info = self.get_finalized_command('egg_info')
+ egg_info.egg_base = self.egg_base
+ egg_info.finalize_options()
egg_info.run()
dist_info_dir = egg_info.egg_info[:-len('.egg-info')] + '.dist-info'
log.info("creating '{}'".format(os.path.abspath(dist_info_dir)))
bdist_wheel = self.get_finalized_command('bdist_wheel')
bdist_wheel.egg2dist(egg_info.egg_info, dist_info_dir)
-
- if self.egg_base:
- destination = os.path.join(self.egg_base, dist_info_dir)
- log.info("creating '{}'".format(os.path.abspath(destination)))
- shutil.move(dist_info_dir, destination)
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
index 8fba7b41..71991efa 100755
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -1817,7 +1817,7 @@ def _update_zipimporter_cache(normalized_path, cache, updater=None):
# get/del patterns instead. For more detailed information see the
# following links:
# https://github.com/pypa/setuptools/issues/202#issuecomment-202913420
- # https://bitbucket.org/pypy/pypy/src/dd07756a34a41f674c0cacfbc8ae1d4cc9ea2ae4/pypy/module/zipimport/interp_zipimport.py#cl-99
+ # http://bit.ly/2h9itJX
old_entry = cache[p]
del cache[p]
new_entry = updater and updater(p, old_entry)
diff --git a/setuptools/command/test.py b/setuptools/command/test.py
index 638d0c56..bfa71496 100644
--- a/setuptools/command/test.py
+++ b/setuptools/command/test.py
@@ -18,6 +18,11 @@ from setuptools.py31compat import unittest_main
class ScanningLoader(TestLoader):
+
+ def __init__(self):
+ TestLoader.__init__(self)
+ self._visited = set()
+
def loadTestsFromModule(self, module, pattern=None):
"""Return a suite of all tests cases contained in the given module
@@ -25,6 +30,10 @@ class ScanningLoader(TestLoader):
If the module has an ``additional_tests`` function, call it and add
the return value to the tests.
"""
+ if module in self._visited:
+ return None
+ self._visited.add(module)
+
tests = []
tests.append(TestLoader.loadTestsFromModule(self, module))
@@ -101,6 +110,8 @@ class test(Command):
return list(self._test_args())
def _test_args(self):
+ if not self.test_suite and sys.version_info >= (2, 7):
+ yield 'discover'
if self.verbose:
yield '--verbose'
if self.test_suite:
diff --git a/setuptools/dist.py b/setuptools/dist.py
index a2ca8795..aa304500 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -316,23 +316,19 @@ class Distribution(Distribution_parse_config_files, _Distribution):
have_package_data = hasattr(self, "package_data")
if not have_package_data:
self.package_data = {}
- _attrs_dict = attrs or {}
- if 'features' in _attrs_dict or 'require_features' in _attrs_dict:
+ attrs = attrs or {}
+ if 'features' in attrs or 'require_features' in attrs:
Feature.warn_deprecated()
self.require_features = []
self.features = {}
self.dist_files = []
- self.src_root = attrs and attrs.pop("src_root", None)
+ self.src_root = attrs.pop("src_root", None)
self.patch_missing_pkg_info(attrs)
- self.long_description_content_type = _attrs_dict.get(
+ self.long_description_content_type = attrs.get(
'long_description_content_type'
)
- # Make sure we have any eggs needed to interpret 'attrs'
- if attrs is not None:
- self.dependency_links = attrs.pop('dependency_links', [])
- assert_string_list(self, 'dependency_links', self.dependency_links)
- if attrs and 'setup_requires' in attrs:
- self.fetch_build_eggs(attrs['setup_requires'])
+ self.dependency_links = attrs.pop('dependency_links', [])
+ self.setup_requires = attrs.pop('setup_requires', [])
for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'):
vars(self).setdefault(ep.name, None)
_Distribution.__init__(self, attrs)
@@ -427,14 +423,15 @@ class Distribution(Distribution_parse_config_files, _Distribution):
req.marker = None
return req
- def parse_config_files(self, filenames=None):
+ def parse_config_files(self, filenames=None, ignore_option_errors=False):
"""Parses configuration files from various levels
and loads configuration.
"""
_Distribution.parse_config_files(self, filenames=filenames)
- parse_configuration(self, self.command_options)
+ parse_configuration(self, self.command_options,
+ ignore_option_errors=ignore_option_errors)
self._finalize_requires()
def parse_command_line(self):
@@ -497,19 +494,20 @@ class Distribution(Distribution_parse_config_files, _Distribution):
"""Fetch an egg needed for building"""
from setuptools.command.easy_install import easy_install
dist = self.__class__({'script_args': ['easy_install']})
- dist.parse_config_files()
opts = dist.get_option_dict('easy_install')
- keep = (
- 'find_links', 'site_dirs', 'index_url', 'optimize',
- 'site_dirs', 'allow_hosts'
- )
- for key in list(opts):
- if key not in keep:
- del opts[key] # don't use any other settings
+ opts.clear()
+ opts.update(
+ (k, v)
+ for k, v in self.get_option_dict('easy_install').items()
+ if k in (
+ # don't use any other settings
+ 'find_links', 'site_dirs', 'index_url',
+ 'optimize', 'site_dirs', 'allow_hosts',
+ ))
if self.dependency_links:
links = self.dependency_links[:]
if 'find_links' in opts:
- links = opts['find_links'][1].split() + links
+ links = opts['find_links'][1] + links
opts['find_links'] = ('setup', links)
install_dir = self.get_egg_cache_dir()
cmd = easy_install(
diff --git a/setuptools/package_index.py b/setuptools/package_index.py
index a6363b18..e0aeb309 100755
--- a/setuptools/package_index.py
+++ b/setuptools/package_index.py
@@ -141,7 +141,7 @@ def distros_for_filename(filename, metadata=None):
def interpret_distro_name(
location, basename, metadata, py_version=None, precedence=SOURCE_DIST,
platform=None
- ):
+):
"""Generate alternative interpretations of a source distro name
Note: if `location` is a filesystem filename, you should call
@@ -292,7 +292,7 @@ class PackageIndex(Environment):
def __init__(
self, index_url="https://pypi.python.org/simple", hosts=('*',),
ca_bundle=None, verify_ssl=True, *args, **kw
- ):
+ ):
Environment.__init__(self, *args, **kw)
self.index_url = index_url + "/" [:not index_url.endswith('/')]
self.scanned_urls = {}
@@ -346,7 +346,8 @@ class PackageIndex(Environment):
base = f.url # handle redirects
page = f.read()
- if not isinstance(page, str): # We are in Python 3 and got bytes. We want str.
+ if not isinstance(page, str):
+ # In Python 3 and got bytes but want str.
if isinstance(f, urllib.error.HTTPError):
# Errors have no charset, assume latin1:
charset = 'latin-1'
@@ -381,8 +382,9 @@ class PackageIndex(Environment):
is_file = s and s.group(1).lower() == 'file'
if is_file or self.allows(urllib.parse.urlparse(url)[1]):
return True
- msg = ("\nNote: Bypassing %s (disallowed host; see "
- "http://bit.ly/1dg9ijs for details).\n")
+ msg = (
+ "\nNote: Bypassing %s (disallowed host; see "
+ "http://bit.ly/2hrImnY for details).\n")
if fatal:
raise DistutilsError(msg % url)
else:
@@ -500,15 +502,16 @@ class PackageIndex(Environment):
"""
checker is a ContentChecker
"""
- checker.report(self.debug,
+ checker.report(
+ self.debug,
"Validating %%s checksum for %s" % filename)
if not checker.is_valid():
tfp.close()
os.unlink(filename)
raise DistutilsError(
"%s validation failed for %s; "
- "possible download problem?" % (
- checker.hash.name, os.path.basename(filename))
+ "possible download problem?"
+ % (checker.hash.name, os.path.basename(filename))
)
def add_find_links(self, urls):
@@ -536,7 +539,8 @@ class PackageIndex(Environment):
if self[requirement.key]: # we've seen at least one distro
meth, msg = self.info, "Couldn't retrieve index page for %r"
else: # no distros seen for this name, might be misspelled
- meth, msg = (self.warn,
+ meth, msg = (
+ self.warn,
"Couldn't find index page for %r (maybe misspelled?)")
meth(msg, requirement.unsafe_name)
self.scan_all()
@@ -577,8 +581,7 @@ class PackageIndex(Environment):
def fetch_distribution(
self, requirement, tmpdir, force_scan=False, source=False,
- develop_ok=False, local_index=None
- ):
+ develop_ok=False, local_index=None):
"""Obtain a distribution suitable for fulfilling `requirement`
`requirement` must be a ``pkg_resources.Requirement`` instance.
@@ -609,12 +612,19 @@ class PackageIndex(Environment):
if dist.precedence == DEVELOP_DIST and not develop_ok:
if dist not in skipped:
- self.warn("Skipping development or system egg: %s", dist)
+ self.warn(
+ "Skipping development or system egg: %s", dist,
+ )
skipped[dist] = 1
continue
- if dist in req and (dist.precedence <= SOURCE_DIST or not source):
- dist.download_location = self.download(dist.location, tmpdir)
+ test = (
+ dist in req
+ and (dist.precedence <= SOURCE_DIST or not source)
+ )
+ if test:
+ loc = self.download(dist.location, tmpdir)
+ dist.download_location = loc
if os.path.exists(dist.download_location):
return dist
@@ -704,7 +714,7 @@ class PackageIndex(Environment):
def _download_to(self, url, filename):
self.info("Downloading %s", url)
# Download the file
- fp, info = None, None
+ fp = None
try:
checker = HashChecker.from_url(url)
fp = self.open_url(strip_fragment(url))
@@ -1103,7 +1113,8 @@ def local_open(url):
f += '/'
files.append('<a href="{name}">{name}</a>'.format(name=f))
else:
- tmpl = ("<html><head><title>{url}</title>"
+ tmpl = (
+ "<html><head><title>{url}</title>"
"</head><body>{files}</body></html>")
body = tmpl.format(url=url, files='\n'.join(files))
status, message = 200, "OK"
diff --git a/setuptools/tests/test_bdist_egg.py b/setuptools/tests/test_bdist_egg.py
index d24aa366..54742aa6 100644
--- a/setuptools/tests/test_bdist_egg.py
+++ b/setuptools/tests/test_bdist_egg.py
@@ -2,6 +2,7 @@
"""
import os
import re
+import zipfile
import pytest
@@ -16,7 +17,7 @@ setup(name='foo', py_modules=['hi'])
"""
-@pytest.yield_fixture
+@pytest.fixture(scope='function')
def setup_context(tmpdir):
with (tmpdir / 'setup.py').open('w') as f:
f.write(SETUP_PY)
@@ -32,7 +33,7 @@ class Test:
script_name='setup.py',
script_args=['bdist_egg'],
name='foo',
- py_modules=['hi']
+ py_modules=['hi'],
))
os.makedirs(os.path.join('build', 'src'))
with contexts.quiet():
@@ -42,3 +43,24 @@ class Test:
# let's see if we got our egg link at the right place
[content] = os.listdir('dist')
assert re.match(r'foo-0.0.0-py[23].\d.egg$', content)
+
+ @pytest.mark.xfail(
+ os.environ.get('PYTHONDONTWRITEBYTECODE'),
+ reason="Byte code disabled",
+ )
+ def test_exclude_source_files(self, setup_context, user_override):
+ dist = Distribution(dict(
+ script_name='setup.py',
+ script_args=['bdist_egg', '--exclude-source-files'],
+ name='foo',
+ py_modules=['hi'],
+ ))
+ with contexts.quiet():
+ dist.parse_command_line()
+ dist.run_commands()
+ [dist_name] = os.listdir('dist')
+ dist_filename = os.path.join('dist', dist_name)
+ zip = zipfile.ZipFile(dist_filename)
+ names = list(zi.filename for zi in zip.filelist)
+ assert 'hi.pyc' in names
+ assert 'hi.py' not in names
diff --git a/setuptools/tests/test_build_meta.py b/setuptools/tests/test_build_meta.py
index 69a700c2..659c1a65 100644
--- a/setuptools/tests/test_build_meta.py
+++ b/setuptools/tests/test_build_meta.py
@@ -42,22 +42,55 @@ class BuildBackendCaller(BuildBackendBase):
return getattr(mod, name)(*args, **kw)
-@pytest.fixture
-def build_backend(tmpdir):
- defn = {
- 'setup.py': DALS("""
- __import__('setuptools').setup(
- name='foo',
- py_modules=['hello'],
- setup_requires=['six'],
- )
- """),
- 'hello.py': DALS("""
- def run():
- print('hello')
- """),
- }
- build_files(defn, prefix=str(tmpdir))
+defns = [{
+ 'setup.py': DALS("""
+ __import__('setuptools').setup(
+ name='foo',
+ py_modules=['hello'],
+ setup_requires=['six'],
+ )
+ """),
+ 'hello.py': DALS("""
+ def run():
+ print('hello')
+ """),
+ },
+ {
+ 'setup.py': DALS("""
+ assert __name__ == '__main__'
+ __import__('setuptools').setup(
+ name='foo',
+ py_modules=['hello'],
+ setup_requires=['six'],
+ )
+ """),
+ 'hello.py': DALS("""
+ def run():
+ print('hello')
+ """),
+ },
+ {
+ 'setup.py': DALS("""
+ variable = True
+ def function():
+ return variable
+ assert variable
+ __import__('setuptools').setup(
+ name='foo',
+ py_modules=['hello'],
+ setup_requires=['six'],
+ )
+ """),
+ 'hello.py': DALS("""
+ def run():
+ print('hello')
+ """),
+ }]
+
+
+@pytest.fixture(params=defns)
+def build_backend(tmpdir, request):
+ build_files(request.param, prefix=str(tmpdir))
with tmpdir.as_cwd():
yield BuildBackend(cwd='.')
diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py
index 435ffec0..c4c9bd03 100644
--- a/setuptools/tests/test_dist.py
+++ b/setuptools/tests/test_dist.py
@@ -39,6 +39,7 @@ def test_dist_fetch_build_egg(tmpdir):
'''.split()
with tmpdir.as_cwd():
dist = Distribution()
+ dist.parse_config_files()
resolved_dists = [
dist.fetch_build_egg(r)
for r in reqs
diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py
index c9d396f4..1d3390c5 100644
--- a/setuptools/tests/test_easy_install.py
+++ b/setuptools/tests/test_easy_install.py
@@ -381,7 +381,15 @@ class TestSetupRequires:
"""))])
yield dist_path
- def test_setup_requires_overrides_version_conflict(self):
+ use_setup_cfg = (
+ (),
+ ('dependency_links',),
+ ('setup_requires',),
+ ('dependency_links', 'setup_requires'),
+ )
+
+ @pytest.mark.parametrize('use_setup_cfg', use_setup_cfg)
+ def test_setup_requires_overrides_version_conflict(self, use_setup_cfg):
"""
Regression test for distribution issue 323:
https://bitbucket.org/tarek/distribute/issues/323
@@ -397,7 +405,7 @@ class TestSetupRequires:
with contexts.save_pkg_resources_state():
with contexts.tempdir() as temp_dir:
- test_pkg = create_setup_requires_package(temp_dir)
+ test_pkg = create_setup_requires_package(temp_dir, use_setup_cfg=use_setup_cfg)
test_setup_py = os.path.join(test_pkg, 'setup.py')
with contexts.quiet() as (stdout, stderr):
# Don't even need to install the package, just
@@ -406,9 +414,10 @@ class TestSetupRequires:
lines = stdout.readlines()
assert len(lines) > 0
- assert lines[-1].strip(), 'test_pkg'
+ assert lines[-1].strip() == 'test_pkg'
- def test_setup_requires_override_nspkg(self):
+ @pytest.mark.parametrize('use_setup_cfg', use_setup_cfg)
+ def test_setup_requires_override_nspkg(self, use_setup_cfg):
"""
Like ``test_setup_requires_overrides_version_conflict`` but where the
``setup_requires`` package is part of a namespace package that has
@@ -446,7 +455,8 @@ class TestSetupRequires:
""")
test_pkg = create_setup_requires_package(
- temp_dir, 'foo.bar', '0.2', make_nspkg_sdist, template)
+ temp_dir, 'foo.bar', '0.2', make_nspkg_sdist, template,
+ use_setup_cfg=use_setup_cfg)
test_setup_py = os.path.join(test_pkg, 'setup.py')
@@ -464,6 +474,38 @@ class TestSetupRequires:
assert len(lines) > 0
assert lines[-1].strip() == 'test_pkg'
+ @pytest.mark.parametrize('use_setup_cfg', use_setup_cfg)
+ def test_setup_requires_with_attr_version(self, use_setup_cfg):
+ def make_dependency_sdist(dist_path, distname, version):
+ make_sdist(dist_path, [
+ ('setup.py',
+ DALS("""
+ import setuptools
+ setuptools.setup(
+ name={name!r},
+ version={version!r},
+ py_modules=[{name!r}],
+ )
+ """.format(name=distname, version=version))),
+ (distname + '.py',
+ DALS("""
+ version = 42
+ """
+ ))])
+ with contexts.save_pkg_resources_state():
+ with contexts.tempdir() as temp_dir:
+ test_pkg = create_setup_requires_package(
+ temp_dir, setup_attrs=dict(version='attr: foobar.version'),
+ make_package=make_dependency_sdist,
+ use_setup_cfg=use_setup_cfg+('version',),
+ )
+ test_setup_py = os.path.join(test_pkg, 'setup.py')
+ with contexts.quiet() as (stdout, stderr):
+ run_setup(test_setup_py, ['--version'])
+ lines = stdout.readlines()
+ assert len(lines) > 0
+ assert lines[-1].strip() == '42'
+
def make_trivial_sdist(dist_path, distname, version):
"""
@@ -532,7 +574,8 @@ def make_sdist(dist_path, files):
def create_setup_requires_package(path, distname='foobar', version='0.1',
make_package=make_trivial_sdist,
- setup_py_template=None):
+ setup_py_template=None, setup_attrs={},
+ use_setup_cfg=()):
"""Creates a source tree under path for a trivial test package that has a
single requirement in setup_requires--a tarball for that requirement is
also created and added to the dependency_links argument.
@@ -547,11 +590,39 @@ def create_setup_requires_package(path, distname='foobar', version='0.1',
'setup_requires': ['%s==%s' % (distname, version)],
'dependency_links': [os.path.abspath(path)]
}
+ test_setup_attrs.update(setup_attrs)
test_pkg = os.path.join(path, 'test_pkg')
- test_setup_py = os.path.join(test_pkg, 'setup.py')
os.mkdir(test_pkg)
+ if use_setup_cfg:
+ test_setup_cfg = os.path.join(test_pkg, 'setup.cfg')
+ options = []
+ metadata = []
+ for name in use_setup_cfg:
+ value = test_setup_attrs.pop(name)
+ if name in 'name version'.split():
+ section = metadata
+ else:
+ section = options
+ if isinstance(value, (tuple, list)):
+ value = ';'.join(value)
+ section.append('%s: %s' % (name, value))
+ with open(test_setup_cfg, 'w') as f:
+ f.write(DALS(
+ """
+ [metadata]
+ {metadata}
+ [options]
+ {options}
+ """
+ ).format(
+ options='\n'.join(options),
+ metadata='\n'.join(metadata),
+ ))
+
+ test_setup_py = os.path.join(test_pkg, 'setup.py')
+
if setup_py_template is None:
setup_py_template = DALS("""\
import setuptools
diff --git a/setuptools/tests/test_test.py b/setuptools/tests/test_test.py
index 7ea43c57..960527bc 100644
--- a/setuptools/tests/test_test.py
+++ b/setuptools/tests/test_test.py
@@ -2,9 +2,9 @@
from __future__ import unicode_literals
+from distutils import log
import os
-import site
-from distutils.errors import DistutilsError
+import sys
import pytest
@@ -66,26 +66,66 @@ def sample_test(tmpdir_cwd):
f.write(TEST_PY)
-@pytest.mark.skipif('hasattr(sys, "real_prefix")')
-@pytest.mark.usefixtures('user_override')
-@pytest.mark.usefixtures('sample_test')
-class TestTestTest:
- def test_test(self):
- params = dict(
- name='foo',
- packages=['name', 'name.space', 'name.space.tests'],
- namespace_packages=['name'],
- test_suite='name.space.tests.test_suite',
- use_2to3=True,
- )
- dist = Distribution(params)
- dist.script_name = 'setup.py'
- cmd = test(dist)
- cmd.user = 1
- cmd.ensure_finalized()
- cmd.install_dir = site.USER_SITE
- cmd.user = 1
- with contexts.quiet():
- # The test runner calls sys.exit
- with contexts.suppress_exceptions(SystemExit):
- cmd.run()
+@pytest.fixture
+def quiet_log():
+ # Running some of the other tests will automatically
+ # change the log level to info, messing our output.
+ log.set_verbosity(0)
+
+
+@pytest.mark.usefixtures('sample_test', 'quiet_log')
+def test_test(capfd):
+ params = dict(
+ name='foo',
+ packages=['name', 'name.space', 'name.space.tests'],
+ namespace_packages=['name'],
+ test_suite='name.space.tests.test_suite',
+ use_2to3=True,
+ )
+ dist = Distribution(params)
+ dist.script_name = 'setup.py'
+ cmd = test(dist)
+ cmd.ensure_finalized()
+ # The test runner calls sys.exit
+ with contexts.suppress_exceptions(SystemExit):
+ cmd.run()
+ out, err = capfd.readouterr()
+ assert out == 'Foo\n'
+
+
+@pytest.mark.xfail(
+ sys.version_info < (2, 7),
+ reason="No discover support for unittest on Python 2.6",
+)
+@pytest.mark.usefixtures('tmpdir_cwd', 'quiet_log')
+def test_tests_are_run_once(capfd):
+ params = dict(
+ name='foo',
+ packages=['dummy'],
+ )
+ with open('setup.py', 'wt') as f:
+ f.write('from setuptools import setup; setup(\n')
+ for k, v in sorted(params.items()):
+ f.write(' %s=%r,\n' % (k, v))
+ f.write(')\n')
+ os.makedirs('dummy')
+ with open('dummy/__init__.py', 'wt'):
+ pass
+ with open('dummy/test_dummy.py', 'wt') as f:
+ f.write(DALS(
+ """
+ from __future__ import print_function
+ import unittest
+ class TestTest(unittest.TestCase):
+ def test_test(self):
+ print('Foo')
+ """))
+ dist = Distribution(params)
+ dist.script_name = 'setup.py'
+ cmd = test(dist)
+ cmd.ensure_finalized()
+ # The test runner calls sys.exit
+ with contexts.suppress_exceptions(SystemExit):
+ cmd.run()
+ out, err = capfd.readouterr()
+ assert out == 'Foo\n'