diff options
| -rw-r--r-- | CHANGES.rst | 24 | ||||
| -rwxr-xr-x | setup.cfg | 3 | ||||
| -rwxr-xr-x | setup.py | 2 | ||||
| -rw-r--r-- | setuptools/__init__.py | 22 | ||||
| -rw-r--r-- | setuptools/build_meta.py | 34 | ||||
| -rw-r--r-- | setuptools/command/__init__.py | 3 | ||||
| -rw-r--r-- | setuptools/command/bdist_egg.py | 18 | ||||
| -rwxr-xr-x | setuptools/command/develop.py | 4 | ||||
| -rw-r--r-- | setuptools/command/dist_info.py | 8 | ||||
| -rwxr-xr-x | setuptools/command/easy_install.py | 2 | ||||
| -rw-r--r-- | setuptools/command/test.py | 11 | ||||
| -rw-r--r-- | setuptools/dist.py | 40 | ||||
| -rwxr-xr-x | setuptools/package_index.py | 43 | ||||
| -rw-r--r-- | setuptools/tests/test_bdist_egg.py | 26 | ||||
| -rw-r--r-- | setuptools/tests/test_build_meta.py | 65 | ||||
| -rw-r--r-- | setuptools/tests/test_dist.py | 1 | ||||
| -rw-r--r-- | setuptools/tests/test_easy_install.py | 85 | ||||
| -rw-r--r-- | setuptools/tests/test_test.py | 90 |
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 ------- @@ -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] + @@ -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' |
