diff options
-rw-r--r-- | .hgtags | 2 | ||||
-rw-r--r-- | CHANGES.txt | 31 | ||||
-rw-r--r-- | DEVGUIDE.txt | 1 | ||||
-rw-r--r-- | docs/developer-guide.txt | 5 | ||||
-rw-r--r-- | docs/pkg_resources.txt | 2 | ||||
-rw-r--r-- | docs/setuptools.txt | 10 | ||||
-rw-r--r-- | ez_setup.py | 4 | ||||
-rw-r--r-- | pkg_resources/__init__.py | 17 | ||||
-rwxr-xr-x | pytest.ini | 2 | ||||
-rw-r--r-- | scripts/upload-old-releases-as-zip.py | 242 | ||||
-rwxr-xr-x | setup.py | 4 | ||||
-rwxr-xr-x | setuptools/command/develop.py | 27 | ||||
-rwxr-xr-x | setuptools/command/easy_install.py | 28 | ||||
-rw-r--r-- | setuptools/command/install_lib.py | 2 | ||||
-rw-r--r-- | setuptools/command/test.py | 13 | ||||
-rw-r--r-- | setuptools/py31compat.py | 2 | ||||
-rw-r--r-- | setuptools/tests/test_develop.py | 131 | ||||
-rw-r--r-- | setuptools/version.py | 2 | ||||
-rw-r--r-- | tox.ini | 6 |
19 files changed, 439 insertions, 92 deletions
@@ -223,3 +223,5 @@ b59320212c8371d0be9e5e6c5f7eec392124c009 18.3 1e120f04bcaa2421c4df0eb6678c3019ba4a82f6 18.3.2 6203335278be7543d31790d9fba55739469a4c6c 18.4 31dc6d2ac0f5ab766652602fe6ca716fff7180e7 18.5 +dfe190b09908f6b953209d13573063809de451b8 18.6 +804f87045a901f1dc121cf9149143d654228dc13 18.6.1 diff --git a/CHANGES.txt b/CHANGES.txt index bd05b8bd..fa89b09b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,37 @@ CHANGES ======= + +---- +18.7 +---- + +* Update dependency on certify. +* Pull Request #160: Improve detection of gui script in + ``easy_install._adjust_header``. +* Made ``test.test_args`` a non-data property; alternate fix + for the issue reported in Pull Request #155. +* Issue #453: In ``ez_setup`` bootstrap module, unload all + ``pkg_resources`` modules following download. +* Pull Request #158: Honor `PEP-488 + <https://www.python.org/dev/peps/pep-0488/>`_ when excluding + files for namespace packages. + +------ +18.6.1 +------ + +* Issue #464: Correct regression in invocation of superclass on old-style + class on Python 2. + +---- +18.6 +---- + +* Issue #439: When installing entry_point scripts under development, + omit the version number of the package, allowing any version of the + package to be used. + ---- 18.5 ---- diff --git a/DEVGUIDE.txt b/DEVGUIDE.txt deleted file mode 100644 index 066a3a6b..00000000 --- a/DEVGUIDE.txt +++ /dev/null @@ -1 +0,0 @@ -The canonical development guide can be found in docs/developer-guide.txt. diff --git a/docs/developer-guide.txt b/docs/developer-guide.txt index 27c304e5..ae33649b 100644 --- a/docs/developer-guide.txt +++ b/docs/developer-guide.txt @@ -92,9 +92,10 @@ Testing The primary tests are run using py.test. To run the tests:: - $ python setup.py ptr + $ python setup.py test -Or install py.test into your environment and run ``py.test``. +Or install py.test into your environment and run ``PYTHONPATH=. py.test`` +or ``python -m pytest``. Under continuous integration, additional tests may be run. See the ``.travis.yml`` file for full details on the tests run under Travis-CI. diff --git a/docs/pkg_resources.txt b/docs/pkg_resources.txt index 6c6405a8..3d40a1a2 100644 --- a/docs/pkg_resources.txt +++ b/docs/pkg_resources.txt @@ -592,7 +592,7 @@ Requirements Parsing The syntax of a requirement specifier can be defined in EBNF as follows:: - requirement ::= project_name versionspec? extras? + requirement ::= project_name extras? versionspec? versionspec ::= comparison version (',' comparison version)* comparison ::= '<' | '<=' | '!=' | '==' | '>=' | '>' | '~=' | '===' extras ::= '[' extralist? ']' diff --git a/docs/setuptools.txt b/docs/setuptools.txt index e2753cba..d6a62de8 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -112,10 +112,16 @@ the distutils. Here's a minimal setup script using setuptools:: ) As you can see, it doesn't take much to use setuptools in a project. -Just by doing the above, this project will be able to produce eggs, upload to +Run that script in your project folder, alongside the Python packages +you have developed. + +Invoke that script to produce eggs, upload to PyPI, and automatically include all packages in the directory where the setup.py lives. See the `Command Reference`_ section below to see what -commands you can give to this setup script. +commands you can give to this setup script. For example, +to produce a source distribution, simply invoke:: + + python setup.py sdist Of course, before you release your project to PyPI, you'll want to add a bit more information to your setup script to help people find or learn about your diff --git a/ez_setup.py b/ez_setup.py index c41c10e9..2f5e4856 100644 --- a/ez_setup.py +++ b/ez_setup.py @@ -30,7 +30,7 @@ try: except ImportError: USER_SITE = None -DEFAULT_VERSION = "18.6" +DEFAULT_VERSION = "18.6.2" DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/" DEFAULT_SAVE_DIR = os.curdir @@ -125,7 +125,7 @@ def _do_download(version, download_base, to_dir, download_delay): # Remove previously-imported pkg_resources if present (see # https://bitbucket.org/pypa/setuptools/pull-request/7/ for details). if 'pkg_resources' in sys.modules: - del sys.modules['pkg_resources'] + _unload_pkg_resources() import setuptools setuptools.bootstrap_install_from = egg diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 7c3e18b4..46f09a2d 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1716,7 +1716,7 @@ class EggProvider(NullProvider): path = self.module_path old = None while path!=old: - if path.lower().endswith('.egg'): + if _is_unpacked_egg(path): self.egg_name = os.path.basename(path) self.egg_info = os.path.join(path, 'EGG-INFO') self.egg_root = path @@ -2099,7 +2099,7 @@ def find_eggs_in_zip(importer, path_item, only=False): # don't yield nested distros return for subitem in metadata.resource_listdir('/'): - if subitem.endswith('.egg'): + if _is_unpacked_egg(subitem): subpath = os.path.join(path_item, subitem) for dist in find_eggs_in_zip(zipimport.zipimporter(subpath), subpath): yield dist @@ -2115,8 +2115,7 @@ def find_on_path(importer, path_item, only=False): path_item = _normalize_cached(path_item) if os.path.isdir(path_item) and os.access(path_item, os.R_OK): - if path_item.lower().endswith('.egg'): - # unpacked egg + if _is_unpacked_egg(path_item): yield Distribution.from_filename( path_item, metadata=PathMetadata( path_item, os.path.join(path_item,'EGG-INFO') @@ -2136,7 +2135,7 @@ def find_on_path(importer, path_item, only=False): yield Distribution.from_location( path_item, entry, metadata, precedence=DEVELOP_DIST ) - elif not only and lower.endswith('.egg'): + elif not only and _is_unpacked_egg(entry): dists = find_distributions(os.path.join(path_item, entry)) for dist in dists: yield dist @@ -2283,6 +2282,14 @@ def _normalize_cached(filename, _cache={}): _cache[filename] = result = normalize_path(filename) return result +def _is_unpacked_egg(path): + """ + Determine if given path appears to be an unpacked egg. + """ + return ( + path.lower().endswith('.egg') + ) + def _set_parent_ns(packageName): parts = packageName.split('.') name = parts.pop() @@ -1,3 +1,3 @@ [pytest] -addopts=--doctest-modules --ignore release.py --ignore setuptools/lib2to3_ex.py --ignore tests/manual_test.py --ignore tests/shlib_test --doctest-glob=pkg_resources/api_tests.txt +addopts=--doctest-modules --ignore release.py --ignore setuptools/lib2to3_ex.py --ignore tests/manual_test.py --ignore tests/shlib_test --doctest-glob=pkg_resources/api_tests.txt --ignore scripts/upload-old-releases-as-zip.py norecursedirs=dist build *.egg diff --git a/scripts/upload-old-releases-as-zip.py b/scripts/upload-old-releases-as-zip.py new file mode 100644 index 00000000..38cfcd55 --- /dev/null +++ b/scripts/upload-old-releases-as-zip.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# declare and require dependencies +__requires__ = [ + 'twine', +]; __import__('pkg_resources') + +import errno +import glob +import hashlib +import json +import os +import shutil +import tarfile +import codecs +import urllib.request +import urllib.parse +import urllib.error +from distutils.version import LooseVersion + +from twine.commands import upload + + +OK = '\033[92m' +FAIL = '\033[91m' +END = '\033[0m' +DISTRIBUTION = "setuptools" + + +class SetuptoolsOldReleasesWithoutZip: + """docstring for SetuptoolsOldReleases""" + + def __init__(self): + self.dirpath = './dist' + os.makedirs(self.dirpath, exist_ok=True) + print("Downloading %s releases..." % DISTRIBUTION) + print("All releases will be downloaded to %s" % self.dirpath) + self.data_json_setuptools = self.get_json_data(DISTRIBUTION) + self.valid_releases_numbers = sorted([ + release + for release in self.data_json_setuptools['releases'] + # This condition is motivated by 13.0 release, which + # comes as "13.0": [], in the json + if self.data_json_setuptools['releases'][release] + ], key=LooseVersion) + self.total_downloaded_ok = 0 + + def get_json_data(self, package_name): + """ + "releases": { + "0.7.2": [ + { + "has_sig": false, + "upload_time": "2013-06-09T16:10:00", + "comment_text": "", + "python_version": "source", + "url": "https://pypi.python.org/packages/source/s/setuptools/setuptools-0.7.2.tar.gz", # NOQA + "md5_digest": "de44cd90f8a1c713d6c2bff67bbca65d", + "downloads": 159014, + "filename": "setuptools-0.7.2.tar.gz", + "packagetype": "sdist", + "size": 633077 + } + ], + "0.7.3": [ + { + "has_sig": false, + "upload_time": "2013-06-18T21:08:56", + "comment_text": "", + "python_version": "source", + "url": "https://pypi.python.org/packages/source/s/setuptools/setuptools-0.7.3.tar.gz", # NOQA + "md5_digest": "c854adacbf9067d330a847f06f7a8eba", + "downloads": 30594, + "filename": "setuptools-0.7.3.tar.gz", + "packagetype": "sdist", + "size": 751152 + } + ], + "12.3": [ + { + "has_sig": false, + "upload_time": "2015-02-26T19:15:51", + "comment_text": "", + "python_version": "3.4", + "url": "https://pypi.python.org/packages/3.4/s/setuptools/setuptools-12.3-py2.py3-none-any.whl", # NOQA + "md5_digest": "31f51a38497a70efadf5ce8d4c2211ab", + "downloads": 288451, + "filename": "setuptools-12.3-py2.py3-none-any.whl", + "packagetype": "bdist_wheel", + "size": 501904 + }, + { + "has_sig": false, + "upload_time": "2015-02-26T19:15:43", + "comment_text": "", + "python_version": "source", + "url": "https://pypi.python.org/packages/source/s/setuptools/setuptools-12.3.tar.gz", # NOQA + "md5_digest": "67614b6d560fa4f240e99cd553ec7f32", + "downloads": 110109, + "filename": "setuptools-12.3.tar.gz", + "packagetype": "sdist", + "size": 635025 + }, + { + "has_sig": false, + "upload_time": "2015-02-26T19:15:47", + "comment_text": "", + "python_version": "source", + "url": "https://pypi.python.org/packages/source/s/setuptools/setuptools-12.3.zip", # NOQA + "md5_digest": "abc799e7db6e7281535bf342bfc41a12", + "downloads": 67539, + "filename": "setuptools-12.3.zip", + "packagetype": "sdist", + "size": 678783 + } + ], + """ + url = "https://pypi.python.org/pypi/%s/json" % (package_name,) + resp = urllib.request.urlopen(urllib.request.Request(url)) + charset = resp.info().get_content_charset() + reader = codecs.getreader(charset)(resp) + data = json.load(reader) + + # Mainly for debug. + json_filename = "%s/%s.json" % (self.dirpath, DISTRIBUTION) + with open(json_filename, 'w') as outfile: + json.dump( + data, + outfile, + sort_keys=True, + indent=4, + separators=(',', ': '), + ) + + return data + + def get_setuptools_releases_without_zip_counterpart(self): + # Get set(all_valid_releases) - set(releases_with_zip), so now we have + # the releases without zip. + return set(self.valid_releases_numbers) - set([ + release + for release in self.valid_releases_numbers + for same_version_release_dict in self.data_json_setuptools['releases'][release] # NOQA + if 'zip' in same_version_release_dict['filename'] + ]) + + def download_setuptools_releases_without_zip_counterpart(self): + try: + releases_without_zip = self.get_setuptools_releases_without_zip_counterpart() # NOQA + failed_md5_releases = [] + # This is a "strange" loop, going through all releases and + # testing only the release I need to download, but I thought it + # would be mouch more readable than trying to iterate through + # releases I need and get into traverse hell values inside dicts + # inside dicts of the json to get the distribution's url to + # download. + for release in self.valid_releases_numbers: + if release in releases_without_zip: + for same_version_release_dict in self.data_json_setuptools['releases'][release]: # NOQA + if 'tar.gz' in same_version_release_dict['filename']: + print("Downloading %s..." % release) + local_file = '%s/%s' % ( + self.dirpath, + same_version_release_dict["filename"] + ) + urllib.request.urlretrieve( + same_version_release_dict["url"], + local_file + ) + targz = open(local_file, 'rb').read() + hexdigest = hashlib.md5(targz).hexdigest() + if (hexdigest != same_version_release_dict['md5_digest']): # NOQA + print(FAIL + "FAIL: md5 for %s didn't match!" % release + END) # NOQA + failed_md5_releases.append(release) + else: + self.total_downloaded_ok += 1 + print('Total releases without zip: %s' % len(releases_without_zip)) + print('Total downloaded: %s' % self.total_downloaded_ok) + if failed_md5_releases: + msg = FAIL + ( + "FAIL: these releases %s failed the md5 check!" % + ','.join(failed_md5_releases) + ) + END + raise Exception(msg) + elif self.total_downloaded_ok != len(releases_without_zip): + msg = FAIL + ( + "FAIL: Unknown error occured. Please check the logs." + ) + END + raise Exception(msg) + else: + print(OK + "All releases downloaded and md5 checked." + END) + + except OSError as e: + if e.errno != errno.EEXIST: + raise e + + def convert_targz_to_zip(self): + print("Converting the tar.gz to zip...") + files = glob.glob('%s/*.tar.gz' % self.dirpath) + total_converted = 0 + for targz in sorted(files, key=LooseVersion): + # Extract and remove tar. + tar = tarfile.open(targz) + tar.extractall(path=self.dirpath) + tar.close() + os.remove(targz) + + # Zip the extracted tar. + setuptools_folder_path = targz.replace('.tar.gz', '') + setuptools_folder_name = setuptools_folder_path.split("/")[-1] + print(setuptools_folder_name) + shutil.make_archive( + setuptools_folder_path, + 'zip', + self.dirpath, + setuptools_folder_name + ) + # Exclude extracted tar folder. + shutil.rmtree(setuptools_folder_path.replace('.zip', '')) + total_converted += 1 + print('Total converted: %s' % total_converted) + if self.total_downloaded_ok != total_converted: + msg = FAIL + ( + "FAIL: Total number of downloaded releases is different" + " from converted ones. Please check the logs." + ) + END + raise Exception(msg) + print("Done with the tar.gz->zip. Check folder %s." % main.dirpath) + + def upload_zips_to_pypi(self): + print('Uploading to pypi...') + zips = sorted(glob.glob('%s/*.zip' % self.dirpath), key=LooseVersion) + print("simulated upload of", zips); return + upload.upload(dists=zips) + + +if __name__ == '__main__': + main = SetuptoolsOldReleasesWithoutZip() + main.download_setuptools_releases_without_zip_counterpart() + main.convert_targz_to_zip() + main.upload_zips_to_pypi() @@ -150,10 +150,10 @@ setup_params = dict( """).strip().splitlines(), extras_require={ "ssl:sys_platform=='win32'": "wincertstore==0.2", - "certs": "certifi==2015.04.28", + "certs": "certifi==2015.11.20", }, dependency_links=[ - 'https://pypi.python.org/packages/source/c/certifi/certifi-2015.04.28.tar.gz#md5=12c7c3a063b2ff97a0f8291d8de41e8c', + 'https://pypi.python.org/packages/source/c/certifi/certifi-2015.11.20.tar.gz#md5=25134646672c695c1ff1593c2dd75d08', 'https://pypi.python.org/packages/source/w/wincertstore/wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2', ], scripts=[], diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index 368b64fe..5ae25d71 100755 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -167,3 +167,30 @@ class develop(easy_install): script_text = f.read() f.close() self.install_script(dist, script_name, script_text, script_path) + + def install_wrapper_scripts(self, dist): + dist = VersionlessRequirement(dist) + return easy_install.install_wrapper_scripts(self, dist) + + +class VersionlessRequirement(object): + """ + Adapt a pkg_resources.Distribution to simply return the project + name as the 'requirement' so that scripts will work across + multiple versions. + + >>> dist = Distribution(project_name='foo', version='1.0') + >>> str(dist.as_requirement()) + 'foo==1.0' + >>> adapted_dist = VersionlessRequirement(dist) + >>> str(adapted_dist.as_requirement()) + 'foo' + """ + def __init__(self, dist): + self.__dist = dist + + def __getattr__(self, name): + return getattr(self.__dist, name) + + def as_requirement(self): + return self.project_name diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 45d180bb..9e9c5e54 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -20,6 +20,7 @@ from distutils.errors import DistutilsArgError, DistutilsOptionError, \ from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS from distutils import log, dir_util from distutils.command.build_scripts import first_line_re +from distutils.spawn import find_executable import sys import os import zipimport @@ -760,9 +761,10 @@ class easy_install(Command): return dst def install_wrapper_scripts(self, dist): - if not self.exclude_scripts: - for args in ScriptWriter.best().get_args(dist): - self.write_script(*args) + if self.exclude_scripts: + return + for args in ScriptWriter.best().get_args(dist): + self.write_script(*args) def install_script(self, dist, script_name, script_text, dev_path=None): """Generate a legacy script wrapper and install it""" @@ -2125,8 +2127,8 @@ class WindowsScriptWriter(ScriptWriter): blockers = [name + x for x in old] yield name + ext, header + script_text, 't', blockers - @staticmethod - def _adjust_header(type_, orig_header): + @classmethod + def _adjust_header(cls, type_, orig_header): """ Make sure 'pythonw' is used for gui and and 'python' is used for console (regardless of what sys.executable is). @@ -2137,11 +2139,19 @@ class WindowsScriptWriter(ScriptWriter): pattern, repl = repl, pattern pattern_ob = re.compile(re.escape(pattern), re.IGNORECASE) new_header = pattern_ob.sub(string=orig_header, repl=repl) + return new_header if cls._use_header(new_header) else orig_header + + @staticmethod + def _use_header(new_header): + """ + Should _adjust_header use the replaced header? + + On non-windows systems, always use. On + Windows systems, only use the replaced header if it resolves + to an executable on the system. + """ clean_header = new_header[2:-1].strip('"') - if sys.platform == 'win32' and not os.path.exists(clean_header): - # the adjusted version doesn't exist, so return the original - return orig_header - return new_header + return sys.platform != 'win32' or find_executable(clean_header) class WindowsExecutableLauncherWriter(WindowsScriptWriter): diff --git a/setuptools/command/install_lib.py b/setuptools/command/install_lib.py index 9b772227..78fe6891 100644 --- a/setuptools/command/install_lib.py +++ b/setuptools/command/install_lib.py @@ -79,6 +79,8 @@ class install_lib(orig.install_lib): base = os.path.join('__pycache__', '__init__.' + imp.get_tag()) yield base + '.pyc' yield base + '.pyo' + yield base + '.opt-1.pyc' + yield base + '.opt-2.pyc' def copy_tree( self, infile, outfile, diff --git a/setuptools/command/test.py b/setuptools/command/test.py index 160e21c9..c26f5fc9 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -41,6 +41,17 @@ class ScanningLoader(TestLoader): return tests[0] # don't create a nested suite for only one return +# adapted from jaraco.classes.properties:NonDataProperty +class NonDataProperty(object): + def __init__(self, fget): + self.fget = fget + + def __get__(self, obj, objtype=None): + if obj is None: + return self + return self.fget(obj) + + class test(Command): """Command to run unit tests after in-place build""" @@ -78,7 +89,7 @@ class test(Command): if self.test_runner is None: self.test_runner = getattr(self.distribution, 'test_runner', None) - @property + @NonDataProperty def test_args(self): return list(self._test_args()) diff --git a/setuptools/py31compat.py b/setuptools/py31compat.py index c487ac04..8fe6dd9d 100644 --- a/setuptools/py31compat.py +++ b/setuptools/py31compat.py @@ -20,7 +20,7 @@ except ImportError: import shutil import tempfile class TemporaryDirectory(object): - """" + """ Very simple temporary directory context manager. Will try to delete afterward, but will also ignore OS and similar errors on deletion. diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index ed1b194a..ab5da00e 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -1,13 +1,17 @@ """develop tests """ import os -import shutil import site import sys -import tempfile +import io + +import pytest from setuptools.command.develop import develop from setuptools.dist import Distribution +from . import contexts +from setuptools.compat import PY3 + SETUP_PY = """\ from setuptools import setup @@ -21,65 +25,52 @@ setup(name='foo', INIT_PY = """print "foo" """ -class TestDevelopTest: +@pytest.yield_fixture +def temp_user(monkeypatch): + with contexts.tempdir() as user_base: + with contexts.tempdir() as user_site: + monkeypatch.setattr('site.USER_BASE', user_base) + monkeypatch.setattr('site.USER_SITE', user_site) + yield - def setup_method(self, method): - if hasattr(sys, 'real_prefix'): - return - # Directory structure - self.dir = tempfile.mkdtemp() - os.mkdir(os.path.join(self.dir, 'foo')) - # setup.py - setup = os.path.join(self.dir, 'setup.py') - f = open(setup, 'w') +@pytest.yield_fixture +def test_env(tmpdir, temp_user): + target = tmpdir + foo = target.mkdir('foo') + setup = target / 'setup.py' + if setup.isfile(): + raise ValueError(dir(target)) + with setup.open('w') as f: f.write(SETUP_PY) - f.close() - self.old_cwd = os.getcwd() - # foo/__init__.py - init = os.path.join(self.dir, 'foo', '__init__.py') - f = open(init, 'w') + init = foo / '__init__.py' + with init.open('w') as f: f.write(INIT_PY) - f.close() - - os.chdir(self.dir) - self.old_base = site.USER_BASE - site.USER_BASE = tempfile.mkdtemp() - self.old_site = site.USER_SITE - site.USER_SITE = tempfile.mkdtemp() + with target.as_cwd(): + yield target - def teardown_method(self, method): - if hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix): - return - os.chdir(self.old_cwd) - shutil.rmtree(self.dir) - shutil.rmtree(site.USER_BASE) - shutil.rmtree(site.USER_SITE) - site.USER_BASE = self.old_base - site.USER_SITE = self.old_site - - def test_develop(self): - if hasattr(sys, 'real_prefix'): - return - dist = Distribution( - dict(name='foo', - packages=['foo'], - use_2to3=True, - version='0.0', - )) +class TestDevelop: + in_virtualenv = hasattr(sys, 'real_prefix') + in_venv = hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix + @pytest.mark.skipif(in_virtualenv or in_venv, + reason="Cannot run when invoked in a virtualenv or venv") + def test_2to3_user_mode(self, test_env): + settings = dict( + name='foo', + packages=['foo'], + use_2to3=True, + version='0.0', + ) + dist = Distribution(settings) dist.script_name = 'setup.py' cmd = develop(dist) cmd.user = 1 cmd.ensure_finalized() cmd.install_dir = site.USER_SITE cmd.user = 1 - old_stdout = sys.stdout - #sys.stdout = StringIO() - try: + with contexts.quiet(): cmd.run() - finally: - sys.stdout = old_stdout # let's see if we got our egg link at the right place content = os.listdir(site.USER_SITE) @@ -87,17 +78,37 @@ class TestDevelopTest: assert content == ['easy-install.pth', 'foo.egg-link'] # Check that we are using the right code. - egg_link_file = open(os.path.join(site.USER_SITE, 'foo.egg-link'), 'rt') - try: + fn = os.path.join(site.USER_SITE, 'foo.egg-link') + with io.open(fn) as egg_link_file: path = egg_link_file.read().split()[0].strip() - finally: - egg_link_file.close() - init_file = open(os.path.join(path, 'foo', '__init__.py'), 'rt') - try: + fn = os.path.join(path, 'foo', '__init__.py') + with io.open(fn) as init_file: init = init_file.read().strip() - finally: - init_file.close() - if sys.version < "3": - assert init == 'print "foo"' - else: - assert init == 'print("foo")' + + expected = 'print("foo")' if PY3 else 'print "foo"' + assert init == expected + + def test_console_scripts(self, tmpdir): + """ + Test that console scripts are installed and that they reference + only the project by name and not the current version. + """ + pytest.skip("TODO: needs a fixture to cause 'develop' " + "to be invoked without mutating environment.") + settings = dict( + name='foo', + packages=['foo'], + version='0.0', + entry_points={ + 'console_scripts': [ + 'foocmd = foo:foo', + ], + }, + ) + dist = Distribution(settings) + dist.script_name = 'setup.py' + cmd = develop(dist) + cmd.ensure_finalized() + cmd.install_dir = tmpdir + cmd.run() + #assert '0.0' not in foocmd_text diff --git a/setuptools/version.py b/setuptools/version.py index 74e48b04..86ad21c6 100644 --- a/setuptools/version.py +++ b/setuptools/version.py @@ -1 +1 @@ -__version__ = '18.6' +__version__ = '18.6.2' @@ -1,7 +1,5 @@ [tox] envlist = py26,py27,py31,py32,py33,py34 + [testenv] -deps= - pytest - mock -commands=py.test setuptools {posargs} +commands=python setup.py test |