diff options
Diffstat (limited to 'setuptools')
25 files changed, 1321 insertions, 328 deletions
diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 9de373f9..a8e7617a 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -8,22 +8,12 @@ from distutils.util import convert_path import os import sys -__version__ = '0.6' +__version__ = '0.8' __all__ = [ 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', 'find_packages' ] -# This marker is used to simplify the process that checks is the -# setuptools package was installed by the Setuptools project -# or by the Distribute project, in case Setuptools creates -# a distribution with the same version. -# -# The distribute_setup script for instance, will check if this -# attribute is present to decide whether to reinstall the package -# or not. -_distribute = True - bootstrap_install_from = None # If we run 2to3 on .py files, should we also convert docstrings? @@ -51,7 +41,7 @@ def find_packages(where='.', exclude=()): os.path.isfile(os.path.join(fn,'__init__.py')) ): out.append(prefix+name); stack.append((fn,prefix+name+'.')) - for pat in list(exclude)+['ez_setup', 'distribute_setup']: + for pat in list(exclude)+['ez_setup']: from fnmatch import fnmatchcase out = [item for item in out if not fnmatchcase(item,pat)] return out diff --git a/setuptools/archive_util.py b/setuptools/archive_util.py index e22b25c0..1109f346 100755 --- a/setuptools/archive_util.py +++ b/setuptools/archive_util.py @@ -6,7 +6,7 @@ __all__ = [ "UnrecognizedFormat", "extraction_drivers", "unpack_directory", ] -import zipfile, tarfile, os, shutil +import zipfile, tarfile, os, shutil, posixpath from pkg_resources import ensure_directory from distutils.errors import DistutilsError @@ -138,7 +138,7 @@ def unpack_zipfile(filename, extract_dir, progress_filter=default_filter): name = info.filename # don't extract absolute paths or ones with .. in them - if name.startswith('/') or '..' in name: + if name.startswith('/') or '..' in name.split('/'): continue target = os.path.join(extract_dir, *name.split('/')) @@ -172,43 +172,39 @@ def unpack_tarfile(filename, extract_dir, progress_filter=default_filter): by ``tarfile.open()``). See ``unpack_archive()`` for an explanation of the `progress_filter` argument. """ - try: tarobj = tarfile.open(filename) except tarfile.TarError: raise UnrecognizedFormat( "%s is not a compressed or uncompressed tar file" % (filename,) ) - try: tarobj.chown = lambda *args: None # don't do any chowning! for member in tarobj: name = member.name # don't extract absolute paths or ones with .. in them - if not name.startswith('/') and '..' not in name: + if not name.startswith('/') and '..' not in name.split('/'): prelim_dst = os.path.join(extract_dir, *name.split('/')) - final_dst = progress_filter(name, prelim_dst) - # If progress_filter returns None, then we do not extract - # this file - # TODO: Do we really need to limit to just these file types? - # tarobj.extract() will handle all files on all platforms, - # turning file types that aren't allowed on that platform into - # regular files. - if final_dst and (member.isfile() or member.isdir() or - member.islnk() or member.issym()): - tarobj.extract(member, extract_dir) - if final_dst != prelim_dst: - shutil.move(prelim_dst, final_dst) + + # resolve any links and to extract the link targets as normal files + while member is not None and (member.islnk() or member.issym()): + linkpath = member.linkname + if member.issym(): + linkpath = posixpath.join(posixpath.dirname(member.name), linkpath) + linkpath = posixpath.normpath(linkpath) + member = tarobj._getmember(linkpath) + + if member is not None and (member.isfile() or member.isdir()): + final_dst = progress_filter(name, prelim_dst) + if final_dst: + if final_dst.endswith(os.sep): + final_dst = final_dst[:-1] + try: + tarobj._extract_member(member, final_dst) # XXX Ugh + except tarfile.ExtractError: + pass # chown/chmod/mkfifo/mknode/makedev failed return True finally: tarobj.close() - - - extraction_drivers = unpack_directory, unpack_zipfile, unpack_tarfile - - - - - diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 875971f0..c5776158 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -7,10 +7,14 @@ import sys, os, marshal from setuptools import Command from distutils.dir_util import remove_tree, mkpath try: - from distutils.sysconfig import get_python_version, get_python_lib + # Python 2.7 or >=3.2 + from sysconfig import get_path, get_python_version + def _get_purelib(): + return get_path("purelib") except ImportError: - from sysconfig import get_python_version - from distutils.sysconfig import get_python_lib + from distutils.sysconfig import get_python_lib, get_python_version + def _get_purelib(): + return get_python_lib(False) from distutils import log from distutils.errors import DistutilsSetupError @@ -131,7 +135,7 @@ class bdist_egg(Command): # Hack for packages that install data to install's --install-lib self.get_finalized_command('install').install_lib = self.bdist_dir - site_packages = os.path.normcase(os.path.realpath(get_python_lib())) + site_packages = os.path.normcase(os.path.realpath(_get_purelib())) old, self.distribution.data_files = self.distribution.data_files,[] for item in old: @@ -171,12 +175,13 @@ class bdist_egg(Command): def run(self): # Generate metadata first self.run_command("egg_info") - # We run install_lib before install_data, because some data hacks # pull their data path from the install_lib command. log.info("installing library code to %s" % self.bdist_dir) instcmd = self.get_finalized_command('install') old_root = instcmd.root; instcmd.root = None + if self.distribution.has_c_libraries() and not self.skip_build: + self.run_command('build_clib') cmd = self.call_command('install_lib', warn_dir=0) instcmd.root = old_root @@ -196,7 +201,6 @@ class bdist_egg(Command): to_compile.extend(self.make_init_files()) if to_compile: cmd.byte_compile(to_compile) - if self.distribution.data_files: self.do_install_data() diff --git a/setuptools/command/bdist_wininst.py b/setuptools/command/bdist_wininst.py index 93e6846d..e8521f83 100755 --- a/setuptools/command/bdist_wininst.py +++ b/setuptools/command/bdist_wininst.py @@ -2,26 +2,24 @@ from distutils.command.bdist_wininst import bdist_wininst as _bdist_wininst import os, sys class bdist_wininst(_bdist_wininst): + _good_upload = _bad_upload = None def create_exe(self, arcname, fullname, bitmap=None): _bdist_wininst.create_exe(self, arcname, fullname, bitmap) - dist_files = getattr(self.distribution, 'dist_files', []) - + installer_name = self.get_installer_filename(fullname) if self.target_version: - installer_name = os.path.join(self.dist_dir, - "%s.win32-py%s.exe" % - (fullname, self.target_version)) pyversion = self.target_version - - # fix 2.5 bdist_wininst ignoring --target-version spec - bad = ('bdist_wininst','any',installer_name) - if bad in dist_files: - dist_files.remove(bad) + # fix 2.5+ bdist_wininst ignoring --target-version spec + self._bad_upload = ('bdist_wininst', 'any', installer_name) else: - installer_name = os.path.join(self.dist_dir, - "%s.win32.exe" % fullname) pyversion = 'any' - good = ('bdist_wininst', pyversion, installer_name) + self._good_upload = ('bdist_wininst', pyversion, installer_name) + + def _fix_upload_names(self): + good, bad = self._good_upload, self._bad_upload + dist_files = getattr(self.distribution, 'dist_files', []) + if bad in dist_files: + dist_files.remove(bad) if good not in dist_files: dist_files.append(good) @@ -36,6 +34,49 @@ class bdist_wininst(_bdist_wininst): self._is_running = True try: _bdist_wininst.run(self) + self._fix_upload_names() finally: self._is_running = False + + if not hasattr(_bdist_wininst, 'get_installer_filename'): + def get_installer_filename(self, fullname): + # Factored out to allow overriding in subclasses + if self.target_version: + # if we create an installer for a specific python version, + # it's better to include this in the name + installer_name = os.path.join(self.dist_dir, + "%s.win32-py%s.exe" % + (fullname, self.target_version)) + else: + installer_name = os.path.join(self.dist_dir, + "%s.win32.exe" % fullname) + return installer_name + # get_installer_filename() + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 4a94572c..50a039ce 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -9,9 +9,15 @@ import os, sys from distutils.file_util import copy_file from setuptools.extension import Library from distutils.ccompiler import new_compiler -from distutils.sysconfig import customize_compiler, get_config_var -get_config_var("LDSHARED") # make sure _config_vars is initialized -from distutils.sysconfig import _config_vars +from distutils.sysconfig import customize_compiler +try: + # Python 2.7 or >=3.2 + from sysconfig import _CONFIG_VARS +except ImportError: + from distutils.sysconfig import get_config_var + get_config_var("LDSHARED") # make sure _config_vars is initialized + del get_config_var + from distutils.sysconfig import _config_vars as _CONFIG_VARS from distutils import log from distutils.errors import * @@ -82,17 +88,15 @@ class build_ext(_build_ext): def get_ext_filename(self, fullname): filename = _build_ext.get_ext_filename(self,fullname) - if fullname not in self.ext_map: - return filename - ext = self.ext_map[fullname] - if isinstance(ext,Library): - fn, ext = os.path.splitext(filename) - return self.shlib_compiler.library_filename(fn,libtype) - elif use_stubs and ext._links_to_dynamic: - d,fn = os.path.split(filename) - return os.path.join(d,'dl-'+fn) - else: - return filename + if fullname in self.ext_map: + ext = self.ext_map[fullname] + if isinstance(ext,Library): + fn, ext = os.path.splitext(filename) + return self.shlib_compiler.library_filename(fn,libtype) + elif use_stubs and ext._links_to_dynamic: + d,fn = os.path.split(filename) + return os.path.join(d,'dl-'+fn) + return filename def initialize_options(self): _build_ext.initialize_options(self) @@ -133,16 +137,16 @@ class build_ext(_build_ext): compiler=self.compiler, dry_run=self.dry_run, force=self.force ) if sys.platform == "darwin": - tmp = _config_vars.copy() + tmp = _CONFIG_VARS.copy() try: # XXX Help! I don't have any idea whether these are right... - _config_vars['LDSHARED'] = "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup" - _config_vars['CCSHARED'] = " -dynamiclib" - _config_vars['SO'] = ".dylib" + _CONFIG_VARS['LDSHARED'] = "gcc -Wl,-x -dynamiclib -undefined dynamic_lookup" + _CONFIG_VARS['CCSHARED'] = " -dynamiclib" + _CONFIG_VARS['SO'] = ".dylib" customize_compiler(compiler) finally: - _config_vars.clear() - _config_vars.update(tmp) + _CONFIG_VARS.clear() + _CONFIG_VARS.update(tmp) else: customize_compiler(compiler) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 50339e8f..3194644e 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -7,7 +7,7 @@ A tool for doing automatic download/extract/build of distutils-based Python packages. For detailed documentation, see the accompanying EasyInstall.txt file, or visit the `EasyInstall home page`__. -__ http://packages.python.org/distribute/easy_install.html +__ https://pythonhosted.org/setuptools/easy_install.html """ import sys @@ -25,9 +25,22 @@ import pkg_resources from setuptools import Command, _dont_write_bytecode from setuptools.sandbox import run_setup from distutils import log, dir_util +try: + # Python 2.7 or >=3.2 + from sysconfig import get_config_vars, get_path + def _get_platlib(): + return get_path("platlib") + def _get_purelib(): + return get_path("purelib") +except ImportError: + from distutils.sysconfig import get_config_vars, get_python_lib + def _get_platlib(): + return get_python_lib(True) + def _get_purelib(): + return get_python_lib(False) + from distutils.util import get_platform from distutils.util import convert_path, subst_vars -from distutils.sysconfig import get_python_lib, get_config_vars from distutils.errors import DistutilsArgError, DistutilsOptionError, \ DistutilsError, DistutilsPlatformError from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS @@ -45,7 +58,10 @@ from pkg_resources import yield_lines, normalize_path, resource_string, \ DistributionNotFound, VersionConflict, \ DEVELOP_DIST -sys_executable = os.path.normpath(sys.executable) +if '__VENV_LAUNCHER__' in os.environ: + sys_executable = os.environ['__VENV_LAUNCHER__'] +else: + sys_executable = os.path.normpath(sys.executable) __all__ = [ 'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg', @@ -204,7 +220,7 @@ class easy_install(Command): def finalize_options(self): if self.version: - print('distribute %s' % get_distribution('distribute').version) + print('setuptools %s' % get_distribution('setuptools').version) sys.exit() py_version = sys.version.split()[0] @@ -269,6 +285,8 @@ class easy_install(Command): self.script_dir = self.install_scripts # default --record from the install command self.set_undefined_options('install', ('record', 'record')) + # Should this be moved to the if statement below? It's not used + # elsewhere normpath = map(normalize_path, sys.path) self.all_site_dirs = get_site_dirs() if self.site_dirs is not None: @@ -285,7 +303,7 @@ class easy_install(Command): else: self.all_site_dirs.append(normalize_path(d)) if not self.editable: self.check_site_dir() - self.index_url = self.index_url or "http://pypi.python.org/simple" + self.index_url = self.index_url or "https://pypi.python.org/simple" self.shadow_path = self.all_site_dirs[:] for path_item in self.install_dir, normalize_path(self.script_dir): if path_item not in self.shadow_path: @@ -404,7 +422,7 @@ class easy_install(Command): # Is it a configured, PYTHONPATH, implicit, or explicit site dir? is_site_dir = instdir in self.all_site_dirs - if not is_site_dir: + if not is_site_dir and not self.multi_version: # No? Then directly test whether it does .pth file processing is_site_dir = self.check_pth_processing() else: @@ -469,7 +487,7 @@ variable. For information on other options, you may wish to consult the documentation at: - http://packages.python.org/distribute/easy_install.html + https://pythonhosted.org/setuptools/easy_install.html Please make the appropriate changes for your system and try again. """ @@ -592,7 +610,6 @@ Please make the appropriate changes for your system and try again. spec, tmpdir, self.upgrade, self.editable, not self.always_copy, self.local_index ) - if dist is None: msg = "Could not find suitable distribution for %r" % spec if self.always_copy: @@ -663,8 +680,7 @@ Please make the appropriate changes for your system and try again. self.update_pth(dist) self.package_index.add(dist) self.local_index.add(dist) - if not self.editable: - self.install_egg_scripts(dist) + self.install_egg_scripts(dist) self.installed_projects[dist.key] = dist log.info(self.installation_report(requirement, dist, *info)) if (dist.has_metadata('dependency_links.txt') and @@ -714,7 +730,7 @@ Please make the appropriate changes for your system and try again. return True if not dist.has_metadata('zip-safe'): return True - return True + return False def maybe_move(self, spec, dist_filename, setup_base): dst = os.path.join(self.build_directory, spec.key) @@ -1175,7 +1191,8 @@ See the setuptools documentation for the "develop" command for more info. if not self.dry_run: self.pth_file.save() - if dist.key=='distribute': + + if dist.key=='setuptools': # Ensure that setuptools itself never becomes unavailable! # XXX should this check for latest version? filename = os.path.join(self.install_dir,'setuptools.pth') @@ -1195,7 +1212,6 @@ See the setuptools documentation for the "develop" command for more info. def pf(src,dst): if dst.endswith('.py') and not src.startswith('EGG-INFO/'): to_compile.append(dst) - to_chmod.append(dst) elif dst.endswith('.dll') or dst.endswith('.so'): to_chmod.append(dst) self.unpack_progress(src,dst) @@ -1234,6 +1250,7 @@ See the setuptools documentation for the "develop" command for more info. + def no_default_version_msg(self): return """bad install directory or PYTHONPATH @@ -1260,7 +1277,7 @@ Here are some of your options for correcting the problem: * You can set up the installation directory to support ".pth" files by using one of the approaches described here: - http://packages.python.org/distribute/easy_install.html#custom-installation-locations + https://pythonhosted.org/setuptools/easy_install.html#custom-installation-locations Please make the appropriate changes for your system and try again.""" % ( self.install_dir, os.environ.get('PYTHONPATH','') @@ -1405,8 +1422,7 @@ def get_site_dirs(): 'Python', sys.version[:3], 'site-packages')) - for plat_specific in (0,1): - site_lib = get_python_lib(plat_specific) + for site_lib in (_get_purelib(), _get_platlib()): if site_lib not in sitedirs: sitedirs.append(site_lib) if HAS_USER_SITE: @@ -1519,7 +1535,7 @@ def get_exe_prefixes(exe_filename): ('PURELIB/', ''), ('PLATLIB/pywin32_system32', ''), ('PLATLIB/', ''), ('SCRIPTS/', 'EGG-INFO/scripts/'), - ('DATA/LIB/site-packages', ''), + ('DATA/lib/site-packages', ''), ] z = zipfile.ZipFile(exe_filename) try: @@ -1851,7 +1867,7 @@ def get_script_args(dist, executable=sys_executable, wininst=False): ext = '-script.py' old = ['.py','.pyc','.pyo'] new_header = re.sub('(?i)pythonw.exe','python.exe',header) - if os.path.exists(new_header[2:-1]) or sys.platform!='win32': + if os.path.exists(new_header[2:-1].strip('"')) or sys.platform!='win32': hdr = new_header else: hdr = header @@ -1970,12 +1986,6 @@ usage: %(script)s [options] requirement_or_url ... def _show_help(self,*args,**kw): with_ei_usage(lambda: Distribution._show_help(self,*args,**kw)) - def find_config_files(self): - files = Distribution.find_config_files(self) - if 'setup.cfg' in files: - files.remove('setup.cfg') - return files - if argv is None: argv = sys.argv[1:] diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 12acc422..642687b2 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -211,7 +211,8 @@ class egg_info(Command): - def get_svn_revision(self): + @staticmethod + def get_svn_revision(): revision = 0 urlre = re.compile('url="([^"]+)"') revre = re.compile('committed-rev="(\d+)"') @@ -225,18 +226,21 @@ class egg_info(Command): data = f.read() f.close() - if data.startswith('10') or data.startswith('9') or data.startswith('8'): + if data.startswith('<?xml'): + dirurl = urlre.search(data).group(1) # get repository URL + localrev = max([int(m.group(1)) for m in revre.finditer(data)]+[0]) + else: + try: svnver = int(data.splitlines()[0]) + except: svnver=-1 + if svnver<8: + log.warn("unrecognized .svn/entries format; skipping %s", base) + dirs[:] = [] + continue + data = list(map(str.splitlines,data.split('\n\x0c\n'))) del data[0][0] # get rid of the '8' or '9' or '10' dirurl = data[0][3] localrev = max([int(d[9]) for d in data if len(d)>9 and d[9]]+[0]) - elif data.startswith('<?xml'): - dirurl = urlre.search(data).group(1) # get repository URL - localrev = max([int(m.group(1)) for m in revre.finditer(data)]+[0]) - else: - log.warn("unrecognized .svn/entries format; skipping %s", base) - dirs[:] = [] - continue if base==os.curdir: base_url = dirurl+'/' # save the root url elif not dirurl.startswith(base_url): @@ -249,9 +253,6 @@ class egg_info(Command): - - - def find_sources(self): """Generate SOURCES.txt manifest file""" manifest_filename = os.path.join(self.egg_info,"SOURCES.txt") diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index 6f3f48c8..39cd6043 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -1,6 +1,7 @@ from distutils.command.sdist import sdist as _sdist from distutils.util import convert_path from distutils import log +from glob import glob import os, re, sys, pkg_resources from glob import glob @@ -41,7 +42,6 @@ def joinpath(prefix,suffix): - def walk_revctrl(dirname=''): """Find all files under revision control""" for ep in pkg_resources.iter_entry_points('setuptools.file_finders'): @@ -89,18 +89,22 @@ def entries_finder(dirname, filename): f = open(filename,'rU') data = f.read() f.close() - if data.startswith('10') or data.startswith('9') or data.startswith('8'): + if data.startswith('<?xml'): + for match in entries_pattern.finditer(data): + yield joinpath(dirname,unescape(match.group(1))) + else: + svnver=-1 + try: svnver = int(data.splitlines()[0]) + except: pass + if svnver<8: + log.warn("unrecognized .svn/entries format in %s", os.path.abspath(dirname)) + return for record in map(str.splitlines, data.split('\n\x0c\n')[1:]): # subversion 1.6/1.5/1.4 if not record or len(record)>=6 and record[5]=="delete": continue # skip deleted yield joinpath(dirname, record[0]) - elif data.startswith('<?xml'): - for match in entries_pattern.finditer(data): - yield joinpath(dirname,unescape(match.group(1))) - else: - log.warn("unrecognized .svn/entries format in %s", os.path.abspath(dirname)) - + finders = [ (convert_path('CVS/Entries'), @@ -121,10 +125,6 @@ finders = [ - - - - class sdist(_sdist): """Smart sdist that finds anything supported by revision control""" @@ -157,7 +157,7 @@ class sdist(_sdist): import distutils.command if 'check' not in distutils.command.__all__: self.check_metadata() - + self.make_distribution() dist_files = getattr(self.distribution,'dist_files',[]) @@ -166,6 +166,26 @@ class sdist(_sdist): if data not in dist_files: dist_files.append(data) + def __read_template_hack(self): + # This grody hack closes the template file (MANIFEST.in) if an + # exception occurs during read_template. + # Doing so prevents an error when easy_install attempts to delete the + # file. + try: + _sdist.read_template(self) + except: + sys.exc_info()[2].tb_next.tb_frame.f_locals['template'].close() + raise + # Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle + # has been fixed, so only override the method if we're using an earlier + # Python. + if ( + sys.version_info < (2,7,2) + or (3,0) <= sys.version_info < (3,1,4) + or (3,2) <= sys.version_info < (3,2,1) + ): + read_template = __read_template_hack + def add_defaults(self): standards = [READMES, self.distribution.script_name] @@ -219,26 +239,6 @@ class sdist(_sdist): build_scripts = self.get_finalized_command('build_scripts') self.filelist.extend(build_scripts.get_source_files()) - def __read_template_hack(self): - # This grody hack closes the template file (MANIFEST.in) if an - # exception occurs during read_template. - # Doing so prevents an error when easy_install attempts to delete the - # file. - try: - _sdist.read_template(self) - except: - sys.exc_info()[2].tb_next.tb_frame.f_locals['template'].close() - raise - # Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle - # has been fixed, so only override the method if we're using an earlier - # Python. - if ( - sys.version_info < (2,7,2) - or (3,0) <= sys.version_info < (3,1,4) - or (3,2) <= sys.version_info < (3,2,1) - ): - read_template = __read_template_hack - def check_readme(self): for f in READMES: if os.path.exists(f): @@ -301,13 +301,4 @@ class sdist(_sdist): - - - - - - - - - # diff --git a/setuptools/command/test.py b/setuptools/command/test.py index a02ac142..db2fc7b1 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -154,7 +154,7 @@ class test(Command): for name in sys.modules: if name.startswith(module): del_modules.append(name) - map(sys.modules.__delitem__, del_modules) + list(map(sys.modules.__delitem__, del_modules)) loader_ep = EntryPoint.parse("x="+self.test_loader) loader_class = loader_ep.load(require=False) diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 7edbf56a..02d955ed 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -22,7 +22,7 @@ class upload(Command): description = "upload binary package to PyPI" - DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' + DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi' user_options = [ ('repository=', 'r', diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py index 178a8793..12bc916b 100644 --- a/setuptools/command/upload_docs.py +++ b/setuptools/command/upload_docs.py @@ -2,7 +2,7 @@ """upload_docs Implements a Distutils 'upload_docs' subcommand (upload documentation to -PyPI's packages.python.org). +PyPI's pythonhosted.org). """ import os @@ -23,15 +23,21 @@ try: except ImportError: from setuptools.command.upload import upload -from setuptools.compat import httplib, urlparse +from setuptools.compat import httplib, urlparse, unicode, iteritems _IS_PYTHON3 = sys.version > '3' +if _IS_PYTHON3: + errors = 'surrogateescape' +else: + errors = 'strict' + + # This is not just a replacement for byte literals # but works as a general purpose encoder def b(s, encoding='utf-8'): if isinstance(s, unicode): - return s.encode(encoding) + return s.encode(encoding, errors) return s @@ -127,7 +133,7 @@ class upload_docs(upload): sep_boundary = b('\n--') + b(boundary) end_boundary = sep_boundary + b('--') body = [] - for key, values in data.iteritems(): + for key, values in iteritems(data): title = '\nContent-Disposition: form-data; name="%s"' % key # handle multiple entries for the same name if type(values) != type([]): @@ -187,7 +193,7 @@ class upload_docs(upload): elif r.status == 301: location = r.getheader('Location') if location is None: - location = 'http://packages.python.org/%s/' % meta.get_name() + location = 'https://pythonhosted.org/%s/' % meta.get_name() self.announce('Upload successful. Visit %s' % location, log.INFO) else: diff --git a/setuptools/dist.py b/setuptools/dist.py index 62dd0eb2..01889215 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -50,7 +50,6 @@ def assert_string_list(dist, attr, value): raise DistutilsSetupError( "%r must be a list of strings (got %r)" % (attr,value) ) - def check_nsp(dist, attr, value): """Verify that namespace packages are valid""" assert_string_list(dist,attr,value) @@ -64,14 +63,18 @@ def check_nsp(dist, attr, value): parent = '.'.join(nsp.split('.')[:-1]) if parent not in value: distutils.log.warn( - "%r is declared as a package namespace, but %r is not:" - " please correct this in setup.py", nsp, parent + "WARNING: %r is declared as a package namespace, but %r" + " is not: please correct this in setup.py", nsp, parent ) def check_extras(dist, attr, value): """Verify that extras_require mapping is valid""" try: for k,v in value.items(): + if ':' in k: + k,m = k.split(':',1) + if pkg_resources.invalid_marker(m): + raise DistutilsSetupError("Invalid environment marker: "+m) list(pkg_resources.parse_requirements(v)) except (TypeError,ValueError,AttributeError): raise DistutilsSetupError( @@ -80,9 +83,6 @@ def check_extras(dist, attr, value): "requirement specifiers." ) - - - def assert_bool(dist, attr, value): """Verify that value is True, False, 0, or 1""" if bool(value) != value: @@ -125,6 +125,47 @@ def check_package_data(dist, attr, value): "wildcard patterns" ) +def check_packages(dist, attr, value): + for pkgname in value: + if not re.match(r'\w+(\.\w+)*', pkgname): + distutils.log.warn( + "WARNING: %r not a valid package name; please use only" + ".-separated package names in setup.py", pkgname + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + class Distribution(_Distribution): """Distribution with support for features, tests, and package data @@ -848,11 +889,4 @@ class Feature: -def check_packages(dist, attr, value): - for pkgname in value: - if not re.match(r'\w+(\.\w+)*', pkgname): - distutils.log.warn( - "WARNING: %r not a valid package name; please use only" - ".-separated package names in setup.py", pkgname - ) diff --git a/setuptools/extension.py b/setuptools/extension.py index eb8b836c..d7892d3d 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -35,7 +35,7 @@ class Extension(_Extension): if source.endswith('.pyx'): source = source[:-4] + '.c' return source - self.sources = map(pyx_to_c, self.sources) + self.sources = list(map(pyx_to_c, self.sources)) class Library(Extension): """Just like a regular Extension, but built as a library instead""" diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 1e2a086d..61a66c6d 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -1,6 +1,8 @@ """PyPI and direct package downloading""" import sys, os.path, re, shutil, random, socket +import itertools import base64 +from setuptools import ssl_support from pkg_resources import * from distutils import log from distutils.errors import DistutilsError @@ -14,8 +16,8 @@ try: except ImportError: from md5 import md5 from fnmatch import translate - from setuptools.py24compat import wraps +from setuptools.py27compat import get_all_headers EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$') HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I) @@ -61,6 +63,8 @@ def parse_bdist_wininst(name): def egg_info_for_url(url): scheme, server, path, parameters, query, fragment = urlparse(url) base = unquote(path.split('/')[-1]) + if server=='sourceforge.net' and base=='download': # XXX Yuck + base = unquote(path.split('/')[-2]) if '#' in base: base, fragment = base.split('#',1) return base,fragment @@ -83,14 +87,12 @@ def distros_for_location(location, basename, metadata=None): if basename.endswith('.egg') and '-' in basename: # only one, unambiguous interpretation return [Distribution.from_location(location, basename, metadata)] - if basename.endswith('.exe'): win_base, py_ver, platform = parse_bdist_wininst(basename) if win_base is not None: return interpret_distro_name( location, win_base, metadata, py_ver, BINARY_DIST, platform ) - # Try source distro extensions (.zip, .tgz, etc.) # for ext in EXTENSIONS: @@ -177,7 +179,7 @@ def find_external_links(url, page): for match in REL.finditer(page): tag, rel = match.groups() - rels = map(str.strip, rel.lower().split(',')) + rels = set(map(str.strip, rel.lower().split(','))) if 'homepage' in rels or 'download' in rels: for match in HREF.finditer(tag): yield urljoin(url, htmldecode(match.group(1))) @@ -189,17 +191,15 @@ def find_external_links(url, page): if match: yield urljoin(url, htmldecode(match.group(1))) - -user_agent = "Python-urllib/%s distribute/%s" % ( - sys.version[:3], require('distribute')[0].version +user_agent = "Python-urllib/%s setuptools/%s" % ( + sys.version[:3], require('setuptools')[0].version ) - class PackageIndex(Environment): """A distribution index that scans web pages for download URLs""" - def __init__(self, index_url="http://pypi.python.org/simple", hosts=('*',), - *args, **kw + 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('/')] @@ -208,8 +208,9 @@ class PackageIndex(Environment): self.package_pages = {} self.allows = re.compile('|'.join(map(translate,hosts))).match self.to_scan = [] - - + if verify_ssl and ssl_support.is_available and (ca_bundle or ssl_support.find_ca_bundle()): + self.opener = ssl_support.opener_for(ca_bundle) + else: self.opener = urllib2.urlopen def process_url(self, url, retrieve=False): """Evaluate a URL as a possible download, and maybe retrieve it""" @@ -235,10 +236,10 @@ class PackageIndex(Environment): return self.info("Reading %s", url) + self.fetched_urls[url] = True # prevent multiple fetch attempts f = self.open_url(url, "Download error on %s: %%s -- Some packages may not be found!" % url) if f is None: return - self.fetched_urls[url] = self.fetched_urls[f.url] = True - + self.fetched_urls[f.url] = True if 'html' not in f.headers.get('content-type', '').lower(): f.close() # not html, we can't process it return @@ -488,7 +489,6 @@ class PackageIndex(Environment): set, development and system eggs (i.e., those using the ``.egg-info`` format) will be ignored. """ - # process a Requirement self.info("Searching for %s", requirement) skipped = {} @@ -508,10 +508,9 @@ class PackageIndex(Environment): continue if dist in req and (dist.precedence<=SOURCE_DIST or not source): - self.info("Best match: %s", dist) - return dist.clone( - location=self.download(dist.location, tmpdir) - ) + return dist + + if force_scan: self.prescan() @@ -535,7 +534,10 @@ class PackageIndex(Environment): (source and "a source distribution of " or ""), requirement, ) - return dist + else: + self.info("Best match: %s", dist) + return dist.clone(location=self.download(dist.location, tmpdir)) + def fetch(self, requirement, tmpdir, force_scan=False, source=False): """Obtain a file suitable for fulfilling `requirement` @@ -551,12 +553,6 @@ class PackageIndex(Environment): return None - - - - - - def gen_setup(self, filename, fragment, tmpdir): match = EGG_FRAGMENT.match(fragment) dists = match and [d for d in @@ -618,8 +614,8 @@ class PackageIndex(Environment): size = -1 if "content-length" in headers: # Some servers return multiple Content-Length headers :( - content_length = headers.get("Content-Length") - size = int(content_length) + sizes = get_all_headers(headers, 'Content-Length') + size = max(map(int, sizes)) self.reporthook(url, filename, blocknum, bs, size) tfp = open(filename,'wb') while True: @@ -645,7 +641,7 @@ class PackageIndex(Environment): if url.startswith('file:'): return local_open(url) try: - return open_with_auth(url) + return open_with_auth(url, self.opener) except (ValueError, httplib.InvalidURL): v = sys.exc_info()[1] msg = ' '.join([str(arg) for arg in v.args]) @@ -682,9 +678,8 @@ class PackageIndex(Environment): def _download_url(self, scheme, url, tmpdir): # Determine download filename # - name = [_f for _f in urlparse(url)[2].split('/') if _f] + name, fragment = egg_info_for_url(url) if name: - name = name[-1] while '..' in name: name = name.replace('..','.').replace('\\','_') else: @@ -704,13 +699,11 @@ class PackageIndex(Environment): elif scheme.startswith('hg+'): return self._download_hg(url, filename) elif scheme=='file': - return url2pathname(urlparse.urlparse(url)[2]) + return url2pathname(urlparse(url)[2]) else: self.url_ok(url, True) # raises error if not allowed return self._attempt_download(url, filename) - - def scan_url(self, url): self.process_url(url, True) @@ -737,14 +730,43 @@ class PackageIndex(Environment): os.unlink(filename) raise DistutilsError("Unexpected HTML page found at "+url) + + + + + + + + + + + + + + + def _download_svn(self, url, filename): url = url.split('#',1)[0] # remove any fragment for svn's sake + creds = '' + if url.lower().startswith('svn:') and '@' in url: + scheme, netloc, path, p, q, f = urlparse(url) + if not netloc and path.startswith('//') and '/' in path[2:]: + netloc, path = path[2:].split('/',1) + auth, host = splituser(netloc) + if auth: + if ':' in auth: + user, pw = auth.split(':',1) + creds = " --username=%s --password=%s" % (user, pw) + else: + creds = " --username="+auth + netloc = host + url = urlunparse((scheme, netloc, url, p, q, f)) self.info("Doing subversion checkout from %s to %s", url, filename) - os.system("svn checkout -q %s %s" % (url, filename)) + os.system("svn checkout%s -q %s %s" % (creds, url, filename)) return filename def _vcs_split_rev_from_url(self, url, pop_prefix=False): - scheme, netloc, path, query, frag = urlparse.urlsplit(url) + scheme, netloc, path, query, frag = urlsplit(url) scheme = scheme.split('+', 1)[-1] @@ -756,7 +778,7 @@ class PackageIndex(Environment): path, rev = path.rsplit('@', 1) # Also, discard fragment - url = urlparse.urlunsplit((scheme, netloc, path, query, '')) + url = urlunsplit((scheme, netloc, path, query, '')) return url, rev @@ -801,6 +823,18 @@ class PackageIndex(Environment): def warn(self, msg, *args): log.warn(msg, *args) + + + + + + + + + + + + # This pattern matches a character entity reference (a decimal numeric # references, a hexadecimal numeric reference, or a named reference). entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub @@ -868,7 +902,7 @@ def _encode_auth(auth): # strip the trailing carriage return return encoded.rstrip() -def open_with_auth(url): +def open_with_auth(url, opener=urllib2.urlopen): """Open a urllib2 request, handling HTTP authentication""" scheme, netloc, path, params, query, frag = urlparse(url) @@ -892,7 +926,7 @@ def open_with_auth(url): request = urllib2.Request(url) request.add_header('User-Agent', user_agent) - fp = urllib2.urlopen(request) + fp = opener(request) if auth: # Put authentication info back into request URL if same host, diff --git a/setuptools/py27compat.py b/setuptools/py27compat.py new file mode 100644 index 00000000..9d2886db --- /dev/null +++ b/setuptools/py27compat.py @@ -0,0 +1,15 @@ +""" +Compatibility Support for Python 2.7 and earlier +""" + +import sys + +def get_all_headers(message, key): + """ + Given an HTTPMessage, return all headers matching a given key. + """ + return message.get_all(key) + +if sys.version_info < (3,): + def get_all_headers(message, key): + return message.getheaders(key) diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 05b73d84..4e527446 100755 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -9,11 +9,42 @@ except NameError: _file = None _open = open from distutils.errors import DistutilsError +from pkg_resources import working_set + from setuptools.compat import builtins, execfile, reduce __all__ = [ "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup", ] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + def run_setup(setup_script, args): """Run a distutils setup script, sandboxed in its directory""" old_dir = os.getcwd() @@ -31,6 +62,9 @@ def run_setup(setup_script, args): try: sys.argv[:] = [setup_script]+list(args) sys.path.insert(0, setup_dir) + # reset to include setup dir, w/clean callback list + working_set.__init__() + working_set.callbacks.append(lambda dist:dist.activate()) DirectorySandbox(setup_dir).run( lambda: execfile( "setup.py", @@ -52,12 +86,14 @@ def run_setup(setup_script, args): # exclude any encodings modules. See #285 and not mod_name.startswith('encodings.') ] - map(sys.modules.__delitem__, del_modules) + list(map(sys.modules.__delitem__, del_modules)) os.chdir(old_dir) sys.path[:] = save_path sys.argv[:] = save_argv tempfile.tempdir = save_tmp + + class AbstractSandbox: """Wrap 'os' module and 'open()' builtin for virtualizing setup scripts""" @@ -89,7 +125,6 @@ class AbstractSandbox: builtins.open = _open self._copy(_os) - def _mk_dual_path_wrapper(name): original = getattr(_os,name) def wrap(self,src,dst,*args,**kw): @@ -98,7 +133,6 @@ class AbstractSandbox: return original(src,dst,*args,**kw) return wrap - for name in ["rename", "link", "symlink"]: if hasattr(_os,name): locals()[name] = _mk_dual_path_wrapper(name) @@ -121,7 +155,6 @@ class AbstractSandbox: ]: if hasattr(_os,name): locals()[name] = _mk_single_path_wrapper(name) - def _mk_single_with_return(name): original = getattr(_os,name) def wrap(self,path,*args,**kw): @@ -243,16 +276,11 @@ class DirectorySandbox(AbstractSandbox): self._violation("os.open", file, flags, mode) return _os.open(file,flags,mode) - WRITE_FLAGS = reduce( - operator.or_, - [getattr(_os, a, 0) for a in + operator.or_, [getattr(_os, a, 0) for a in "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()] ) - - - class SandboxViolation(DistutilsError): """A setup script attempted to modify the filesystem outside the sandbox""" diff --git a/setuptools/script template (dev).py b/setuptools/script template (dev).py index 6dd9dd45..901790e7 100644 --- a/setuptools/script template (dev).py +++ b/setuptools/script template (dev).py @@ -3,4 +3,7 @@ __requires__ = """%(spec)r""" from pkg_resources import require; require("""%(spec)r""") del require __file__ = """%(dev_path)r""" -execfile(__file__) +try: + execfile(__file__) +except NameError: + exec(compile(open(__file__).read(), __file__, 'exec')) diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py new file mode 100644 index 00000000..2aec655a --- /dev/null +++ b/setuptools/ssl_support.py @@ -0,0 +1,248 @@ +import sys, os, socket, atexit, re +import pkg_resources +from pkg_resources import ResolutionError, ExtractionError +from setuptools.compat import urllib2 + +try: + import ssl +except ImportError: + ssl = None + +__all__ = [ + 'VerifyingHTTPSHandler', 'find_ca_bundle', 'is_available', 'cert_paths', + 'opener_for' +] + +cert_paths = """ +/etc/pki/tls/certs/ca-bundle.crt +/etc/ssl/certs/ca-certificates.crt +/usr/share/ssl/certs/ca-bundle.crt +/usr/local/share/certs/ca-root.crt +/etc/ssl/cert.pem +/System/Library/OpenSSL/certs/cert.pem +""".strip().split() + + +HTTPSHandler = HTTPSConnection = object + +for what, where in ( + ('HTTPSHandler', ['urllib2','urllib.request']), + ('HTTPSConnection', ['httplib', 'http.client']), +): + for module in where: + try: + exec("from %s import %s" % (module, what)) + except ImportError: + pass + +is_available = ssl is not None and object not in (HTTPSHandler, HTTPSConnection) + + + + + +try: + from socket import create_connection +except ImportError: + _GLOBAL_DEFAULT_TIMEOUT = getattr(socket, '_GLOBAL_DEFAULT_TIMEOUT', object()) + def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, + source_address=None): + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + host, port = address + err = None + for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + if timeout is not _GLOBAL_DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + sock.connect(sa) + return sock + + except error: + err = True + if sock is not None: + sock.close() + if err: + raise + else: + raise error("getaddrinfo returns an empty list") + + +try: + from ssl import CertificateError, match_hostname +except ImportError: + class CertificateError(ValueError): + pass + + def _dnsname_to_pat(dn): + pats = [] + for frag in dn.split(r'.'): + if frag == '*': + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append('[^.]+') + else: + # Otherwise, '*' matches any dotless fragment. + frag = re.escape(frag) + pats.append(frag.replace(r'\*', '[^.]*')) + return re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + + def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 rules + are mostly followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError("empty or no certificate") + dnsnames = [] + san = cert.get('subjectAltName', ()) + for key, value in san: + if key == 'DNS': + if _dnsname_to_pat(value).match(hostname): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get('subject', ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == 'commonName': + if _dnsname_to_pat(value).match(hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError("hostname %r " + "doesn't match either of %s" + % (hostname, ', '.join(map(repr, dnsnames)))) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r " + "doesn't match %r" + % (hostname, dnsnames[0])) + else: + raise CertificateError("no appropriate commonName or " + "subjectAltName fields were found") + + + + + + + + + + + + + + + + + + + + + + + + +class VerifyingHTTPSHandler(HTTPSHandler): + """Simple verifying handler: no auth, subclasses, timeouts, etc.""" + + def __init__(self, ca_bundle): + self.ca_bundle = ca_bundle + HTTPSHandler.__init__(self) + + def https_open(self, req): + return self.do_open( + lambda host, **kw: VerifyingHTTPSConn(host, self.ca_bundle, **kw), req + ) + + +class VerifyingHTTPSConn(HTTPSConnection): + """Simple verifying connection: no auth, subclasses, timeouts, etc.""" + def __init__(self, host, ca_bundle, **kw): + HTTPSConnection.__init__(self, host, **kw) + self.ca_bundle = ca_bundle + + def connect(self): + sock = create_connection( + (self.host, self.port), getattr(self,'source_address',None) + ) + self.sock = ssl.wrap_socket( + sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=self.ca_bundle + ) + try: + match_hostname(self.sock.getpeercert(), self.host) + except CertificateError: + self.sock.shutdown(socket.SHUT_RDWR) + self.sock.close() + raise + +def opener_for(ca_bundle=None): + """Get a urlopen() replacement that uses ca_bundle for verification""" + return urllib2.build_opener( + VerifyingHTTPSHandler(ca_bundle or find_ca_bundle()) + ).open + + + +_wincerts = None + +def get_win_certfile(): + global _wincerts + if _wincerts is not None: + return _wincerts.name + + try: + from wincertstore import CertFile + 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) + atexit.register(self.close) + + _wincerts = MyCertFile(stores=['CA', '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 + try: + return pkg_resources.resource_filename('certifi', 'cacert.pem') + except (ImportError, ResolutionError, ExtractionError): + return None + + + + + diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index 298141a7..b5328ce6 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -2,7 +2,7 @@ import sys import os import unittest -import doctest +from setuptools.tests import doctest import distutils.core import distutils.cmd from distutils.errors import DistutilsOptionError, DistutilsPlatformError @@ -11,6 +11,7 @@ from distutils.core import Extension from distutils.version import LooseVersion from setuptools.compat import func_code +from setuptools.compat import func_code import setuptools.dist import setuptools.depends as dep from setuptools import Feature diff --git a/setuptools/tests/entries-v10 b/setuptools/tests/entries-v10 new file mode 100644 index 00000000..4446c501 --- /dev/null +++ b/setuptools/tests/entries-v10 @@ -0,0 +1,615 @@ +10 + +dir +89001 +http://svn.python.org/projects/sandbox/branches/setuptools-0.6 +http://svn.python.org/projects + + + +2013-06-03T17:26:03.052972Z +89000 +phillip.eby + + + + + + + + + + + + + + +6015fed2-1504-0410-9fe1-9d1591cc4771 + +api_tests.txt +file + + + + +2013-06-19T13:20:47.948712Z +dec366372ca14fbeaeb26f492bcf5725 +2013-05-15T22:04:59.389374Z +88997 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +12312 + +setuptools.egg-info +dir + +README.txt +file + + + + +2013-06-19T13:20:47.948712Z +26f0dd5d095522ba3ad999b6b6777b92 +2011-05-31T20:10:56.416725Z +88846 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +7615 + +easy_install.py +file + + + + +2013-06-19T13:20:47.948712Z +97b52fe7253bf4683f9f626f015eb72e +2006-09-20T20:48:18.716070Z +51935 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +126 + +setuptools +dir + +launcher.c +file + + + + +2013-06-19T13:20:47.924700Z +e5a8e77de9022688b80f77fc6d742fee +2009-10-19T21:03:29.785400Z +75544 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +7476 + +ez_setup.py +file + + + + +2013-06-19T13:20:47.924700Z +17e8ec5e08faccfcb08b5f8d5167ca14 +2011-01-20T18:50:00.815420Z +88124 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +8350 + +version +file + + + + +2013-06-19T13:20:47.924700Z +e456da09e0c9e224a56302f8316b6dbf +2007-01-09T19:21:05.921317Z +53317 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +1143 + +setup.py +file + + + + +2013-06-19T13:20:47.924700Z +d4e5b3c16bd61bfef6c0bb9377a3a3ea +2013-05-15T22:04:59.389374Z +88997 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +5228 + +release.sh +file + + + + +2013-06-19T13:20:47.932704Z +b1fd4054a1c107ff0f27baacd97be94c +2009-10-28T17:12:45.227140Z +75925 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +1044 + +pkg_resources.txt +file + + + + +2013-06-19T13:20:47.928702Z +f497e7c92a4de207cbd9ab1943f93388 +2009-10-12T20:00:02.336146Z +75385 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +94518 + +site.py +file + + + + +2013-06-19T13:20:47.932704Z +ebaac6fb6525f77ca950d22e6f8315df +2006-03-11T00:39:09.666740Z +42965 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +2362 + +version.dat +file + + + + +2013-06-19T13:20:47.932704Z +8e14ecea32b9874cd7d29277494554c0 +2009-10-28T17:12:45.227140Z +75925 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +80 + +virtual-python.py +file + + + + +2013-06-19T13:20:47.932704Z +aa857add3b5563238f0a904187f5ded9 +2005-10-17T02:26:39.000000Z +41262 +pje +has-props + + + + + + + + + + + + + + + + + + + + +3898 + +setup.cfg +file + + + + +2013-06-19T13:20:47.932704Z +eda883e744fce83f8107ad8dc8303536 +2006-09-21T22:26:48.050256Z +51965 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +296 + +setuptools.txt +file + + + + +2013-06-19T13:20:47.940708Z +11926256f06046b196eaf814772504e7 +2013-05-15T22:04:59.389374Z +88997 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +149832 + +pkg_resources.py +file + + + + +2013-06-19T13:20:47.940708Z +b63a30f5f0f0225a788c2c0e3430b3cf +2013-05-15T22:04:59.389374Z +88997 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +90397 + +tests +dir + +wikiup.cfg +file + + + + +2013-06-19T13:20:47.944710Z +34ad845a5e0a0b46458557fa910bf429 +2008-08-21T17:23:50.797633Z +65935 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +136 + +EasyInstall.txt +file + + + + +2013-06-19T13:20:47.944710Z +e97387c517f70fc18a377e42d19d64d4 +2013-05-15T22:04:59.389374Z +88997 +phillip.eby +has-props + + + + + + + + + + + + + + + + + + + + +82495 + diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index 9d7ce711..7b90161a 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -51,7 +51,7 @@ class TestDevelopTest(unittest.TestCase): site.USER_SITE = tempfile.mkdtemp() def tearDown(self): - if sys.version < "2.6" or hasattr(sys, 'real_prefix'): + if sys.version < "2.6" or hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix): return os.chdir(self.old_cwd) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index d76025d2..2732bb3e 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -12,8 +12,9 @@ import textwrap import tarfile import distutils.core +from setuptools.compat import StringIO, BytesIO, next, urlparse from setuptools.sandbox import run_setup, SandboxViolation -from setuptools.command.easy_install import easy_install, fix_jython_executable, get_script_args, main +from setuptools.command.easy_install import easy_install, fix_jython_executable, get_script_args from setuptools.command.easy_install import PthDistributions from setuptools.command import easy_install as easy_install_pkg from setuptools.dist import Distribution @@ -84,41 +85,6 @@ class TestEasyInstallTest(unittest.TestCase): self.assertEqual(script, WANTED) - def test_no_setup_cfg(self): - # makes sure easy_install as a command (main) - # doesn't use a setup.cfg file that is located - # in the current working directory - dir = tempfile.mkdtemp() - setup_cfg = open(os.path.join(dir, 'setup.cfg'), 'w') - setup_cfg.write('[easy_install]\nfind_links = http://example.com') - setup_cfg.close() - setup_py = open(os.path.join(dir, 'setup.py'), 'w') - setup_py.write(SETUP_PY) - setup_py.close() - - from setuptools.dist import Distribution - - def _parse_command_line(self): - msg = 'Error: a local setup.cfg was used' - opts = self.command_options - if 'easy_install' in opts: - assert 'find_links' not in opts['easy_install'], msg - return self._old_parse_command_line() - - Distribution._old_parse_command_line = Distribution.parse_command_line - Distribution.parse_command_line = _parse_command_line - - old_wd = os.getcwd() - try: - os.chdir(dir) - reset_setup_stop_context( - lambda: self.assertRaises(SystemExit, main, []) - ) - finally: - os.chdir(old_wd) - shutil.rmtree(dir) - Distribution.parse_command_line = Distribution._old_parse_command_line - def test_no_find_links(self): # new option '--no-find-links', that blocks find-links added at # the project level @@ -265,10 +231,10 @@ class TestUserInstallTest(unittest.TestCase): del os.environ['PYTHONPATH'] def test_setup_requires(self): - """Regression test for issue #318 + """Regression test for Distribute issue #318 - Ensures that a package with setup_requires can be installed when - distribute is installed in the user site-packages without causing a + Ensure that a package with setup_requires can be installed when + setuptools is installed in the user site-packages without causing a SandboxViolation. """ @@ -306,11 +272,12 @@ class TestUserInstallTest(unittest.TestCase): sys.stdout = StringIO() sys.stderr = StringIO() try: - reset_setup_stop_context( - lambda: run_setup(test_setup_py, ['install']) - ) - except SandboxViolation: - self.fail('Installation caused SandboxViolation') + try: + reset_setup_stop_context( + lambda: run_setup(test_setup_py, ['install']) + ) + except SandboxViolation: + self.fail('Installation caused SandboxViolation') finally: sys.stdout = old_stdout sys.stderr = old_stderr @@ -372,13 +339,13 @@ class TestSetupRequires(unittest.TestCase): doesn't exist) and invoke installer on it. """ def build_sdist(dir): - dist_path = os.path.join(dir, 'distribute-test-fetcher-1.0.tar.gz') + dist_path = os.path.join(dir, 'setuptools-test-fetcher-1.0.tar.gz') make_trivial_sdist( dist_path, textwrap.dedent(""" import setuptools setuptools.setup( - name="distribute-test-fetcher", + name="setuptools-test-fetcher", version="1.0", setup_requires = ['does-not-exist'], ) @@ -446,7 +413,7 @@ def argv_context(f, repl): def reset_setup_stop_context(f): """ - When the distribute tests are run using setup.py test, and then + When the setuptools tests are run using setup.py test, and then one wants to invoke another setup() command (such as easy_install) within those tests, it's necessary to reset the global variable in distutils.core so that the setup() command will run naturally. diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py new file mode 100644 index 00000000..f26a1f51 --- /dev/null +++ b/setuptools/tests/test_egg_info.py @@ -0,0 +1,40 @@ +import os +import tempfile +import shutil +import unittest + +import pkg_resources +from setuptools.command import egg_info + +ENTRIES_V10 = pkg_resources.resource_string(__name__, 'entries-v10') +"An entries file generated with svn 1.6.17 against the legacy Setuptools repo" + +class TestEggInfo(unittest.TestCase): + + def setUp(self): + self.test_dir = tempfile.mkdtemp() + os.mkdir(os.path.join(self.test_dir, '.svn')) + + self.old_cwd = os.getcwd() + os.chdir(self.test_dir) + + def tearDown(self): + os.chdir(self.old_cwd) + shutil.rmtree(self.test_dir) + + def _write_entries(self, entries): + fn = os.path.join(self.test_dir, '.svn', 'entries') + entries_f = open(fn, 'wb') + entries_f.write(entries) + entries_f.close() + + def test_version_10_format(self): + """ + """ + self._write_entries(ENTRIES_V10) + rev = egg_info.egg_info.get_svn_revision() + self.assertEqual(rev, '89000') + + +def test_suite(): + return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index b596d37f..92d1e2e0 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -46,19 +46,15 @@ class TestPackageIndex(unittest.TestCase): def _urlopen(*args): raise httplib.BadStatusLine('line') - old_urlopen = urllib2.urlopen - urllib2.urlopen = _urlopen + index.opener = _urlopen url = 'http://example.com' try: - try: - v = index.open_url(url) - except Exception: - v = sys.exc_info()[1] - self.assertTrue('line' in str(v)) - else: - raise AssertionError('Should have raise here!') - finally: - urllib2.urlopen = old_urlopen + v = index.open_url(url) + except Exception: + v = sys.exc_info()[1] + self.assertTrue('line' in str(v)) + else: + raise AssertionError('Should have raise here!') def test_bad_url_double_scheme(self): """ @@ -103,7 +99,7 @@ class TestPackageIndex(unittest.TestCase): """ Download links from the pypi simple index should be used before external download links. - http://bitbucket.org/tarek/distribute/issue/163/md5-validation-error + https://bitbucket.org/tarek/distribute/issue/163 Usecase : - someone uploads a package on pypi, a md5 is generated @@ -112,7 +108,7 @@ class TestPackageIndex(unittest.TestCase): - someone reuploads the package (with a different md5) - while easy_installing, an MD5 error occurs because the external link is used - -> Distribute should use the link from pypi, not the external one. + -> Setuptools should use the link from pypi, not the external one. """ if sys.platform.startswith('java'): # Skip this test on jython because binding to :0 fails diff --git a/setuptools/tests/test_resources.py b/setuptools/tests/test_resources.py index a2ec03c0..df5261d1 100644 --- a/setuptools/tests/test_resources.py +++ b/setuptools/tests/test_resources.py @@ -198,29 +198,6 @@ class DistroTests(TestCase): ) self.assertRaises(UnknownExtra, d.requires, ["foo"]) - def testSetuptoolsDistributeCombination(self): - # Ensure that installing a 0.7-series setuptools fails. PJE says that - # it will not co-exist. - ws = WorkingSet([]) - d = Distribution( - "/some/path", - project_name="setuptools", - version="0.7a1") - self.assertRaises(ValueError, ws.add, d) - # A 0.6-series is no problem - d2 = Distribution( - "/some/path", - project_name="setuptools", - version="0.6c9") - ws.add(d2) - - # a unexisting version needs to work - ws = WorkingSet([]) - d3 = Distribution( - "/some/path", - project_name="setuptools") - ws.add(d3) - class EntryPointTests(TestCase): @@ -373,21 +350,13 @@ class RequirementsTests(TestCase): self.assertTrue(d("foo-0.3a3.egg") in r2) self.assertTrue(d("foo-0.3a5.egg") in r2) - def testDistributeSetuptoolsOverride(self): - # Plain setuptools or distribute mean we return distribute. - self.assertEqual( - Requirement.parse('setuptools').project_name, 'distribute') - self.assertEqual( - Requirement.parse('distribute').project_name, 'distribute') - # setuptools lower than 0.7 means distribute - self.assertEqual( - Requirement.parse('setuptools==0.6c9').project_name, 'distribute') - self.assertEqual( - Requirement.parse('setuptools==0.6c10').project_name, 'distribute') - self.assertEqual( - Requirement.parse('setuptools>=0.6').project_name, 'distribute') + def testSetuptoolsProjectName(self): + """ + The setuptools project should implement the setuptools package. + """ + self.assertEqual( - Requirement.parse('setuptools < 0.7').project_name, 'distribute') + Requirement.parse('setuptools').project_name, 'setuptools') # setuptools 0.7 and higher means setuptools. self.assertEqual( Requirement.parse('setuptools == 0.7').project_name, 'setuptools') @@ -559,7 +528,7 @@ class ScriptHeaderTests(TestCase): platform = sys.platform sys.platform = 'java1.5.0_13' - stdout = sys.stdout + stdout, stderr = sys.stdout, sys.stderr try: # A mock sys.executable that uses a shebang line (this file) exe = os.path.normpath(os.path.splitext(__file__)[0] + '.py') @@ -582,7 +551,7 @@ class ScriptHeaderTests(TestCase): finally: del sys.modules["java"] sys.platform = platform - sys.stdout = stdout + sys.stdout, sys.stderr = stdout, stderr @@ -591,7 +560,7 @@ class NamespaceTests(TestCase): def setUp(self): self._ns_pkgs = pkg_resources._namespace_packages.copy() - self._tmpdir = tempfile.mkdtemp(prefix="tests-distribute-") + self._tmpdir = tempfile.mkdtemp(prefix="tests-setuptools-") os.makedirs(os.path.join(self._tmpdir, "site-pkgs")) self._prev_sys_path = sys.path[:] sys.path.append(os.path.join(self._tmpdir, "site-pkgs")) @@ -635,7 +604,7 @@ class NamespaceTests(TestCase): try: import pkg1.pkg2 except ImportError: - self.fail("Distribute tried to import the parent namespace package") + self.fail("Setuptools tried to import the parent namespace package") # check the _namespace_packages dict self._assertIn("pkg1.pkg2", pkg_resources._namespace_packages.keys()) self.assertEqual(pkg_resources._namespace_packages["pkg1"], ["pkg1.pkg2"]) |