diff options
Diffstat (limited to 'setuptools')
50 files changed, 2308 insertions, 466 deletions
diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 45b95f90..fc9b7b93 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -1,19 +1,24 @@ """Extensions to the 'distutils' for large or complex distributions""" -from setuptools.extension import Extension, Library -from setuptools.dist import Distribution, Feature, _get_unpatched -import distutils.core, setuptools.command -from setuptools.depends import Require -from distutils.core import Command as _Command -from distutils.util import convert_path + import os import sys -from setuptools.version import __version__ +import distutils.core +import distutils.filelist +from distutils.core import Command as _Command +from distutils.util import convert_path + +import setuptools.version +from setuptools.extension import Extension +from setuptools.dist import Distribution, Feature, _get_unpatched +from setuptools.depends import Require __all__ = [ 'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require', 'find_packages' ] +__version__ = setuptools.version.__version__ + bootstrap_install_from = None # If we run 2to3 on .py files, should we also convert docstrings? @@ -37,10 +42,14 @@ def find_packages(where='.', exclude=()): where,prefix = stack.pop(0) for name in os.listdir(where): fn = os.path.join(where,name) - if ('.' not in name and os.path.isdir(fn) and - os.path.isfile(os.path.join(fn,'__init__.py')) - ): - out.append(prefix+name); stack.append((fn,prefix+name+'.')) + looks_like_package = ( + '.' not in name + and os.path.isdir(fn) + and os.path.isfile(os.path.join(fn, '__init__.py')) + ) + if looks_like_package: + out.append(prefix+name) + stack.append((fn, prefix+name+'.')) for pat in list(exclude)+['ez_setup']: from fnmatch import fnmatchcase out = [item for item in out if not fnmatchcase(item,pat)] @@ -67,7 +76,6 @@ class Command(_Command): setattr(cmd,k,v) # update command with keywords return cmd -import distutils.core distutils.core.Command = Command # we can't patch distutils.cmd, alas def findall(dir = os.curdir): @@ -83,12 +91,8 @@ def findall(dir = os.curdir): all_files.extend(filter(os.path.isfile, files)) return all_files -import distutils.filelist distutils.filelist.findall = findall # fix findall bug in distutils. # sys.dont_write_bytecode was introduced in Python 2.6. -if ((hasattr(sys, "dont_write_bytecode") and sys.dont_write_bytecode) or - (not hasattr(sys, "dont_write_bytecode") and os.environ.get("PYTHONDONTWRITEBYTECODE"))): - _dont_write_bytecode = True -else: - _dont_write_bytecode = False +_dont_write_bytecode = getattr(sys, 'dont_write_bytecode', + bool(os.environ.get("PYTHONDONTWRITEBYTECODE"))) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 6a45e596..6ce19fa4 100755 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -23,6 +23,9 @@ import stat import random import platform import textwrap +import warnings +import site +import struct from glob import glob from distutils import log, dir_util @@ -72,22 +75,19 @@ __all__ = [ 'main', 'get_exe_prefixes', ] -import site HAS_USER_SITE = not sys.version < "2.6" and site.ENABLE_USER_SITE -import struct def is_64bit(): return struct.calcsize("P") == 8 def samefile(p1, p2): - if hasattr(os.path,'samefile') and ( - os.path.exists(p1) and os.path.exists(p2) - ): - return os.path.samefile(p1,p2) - return ( - os.path.normpath(os.path.normcase(p1)) == - os.path.normpath(os.path.normcase(p2)) - ) + both_exist = os.path.exists(p1) and os.path.exists(p2) + use_samefile = hasattr(os.path, 'samefile') and both_exist + if use_samefile: + return os.path.samefile(p1, p2) + norm_p1 = os.path.normpath(os.path.normcase(p1)) + norm_p2 = os.path.normpath(os.path.normcase(p2)) + return norm_p1 == norm_p2 if sys.version_info <= (3,): def _to_ascii(s): @@ -124,9 +124,6 @@ class easy_install(Command): ("always-copy", "a", "Copy all needed packages to install dir"), ("index-url=", "i", "base URL of Python Package Index"), ("find-links=", "f", "additional URL(s) to search for packages"), - ("delete-conflicting", "D", "no longer needed; don't use this"), - ("ignore-conflicts-at-my-risk", None, - "no longer needed; don't use this"), ("build-directory=", "b", "download/extract/build in DIR; keep the results"), ('optimize=', 'O', @@ -147,7 +144,7 @@ class easy_install(Command): ] boolean_options = [ 'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy', - 'delete-conflicting', 'ignore-conflicts-at-my-risk', 'editable', + 'editable', 'no-deps', 'local-snapshots-ok', 'version' ] @@ -196,8 +193,6 @@ class easy_install(Command): # Options not specifiable via command line self.package_index = None self.pth_file = self.always_copy_from = None - self.delete_conflicting = None - self.ignore_conflicts_at_my_risk = None self.site_dirs = None self.installed_projects = {} self.sitepy_installed = False @@ -341,11 +336,6 @@ class easy_install(Command): except ValueError: raise DistutilsOptionError("--optimize must be 0, 1, or 2") - if self.delete_conflicting and self.ignore_conflicts_at_my_risk: - raise DistutilsOptionError( - "Can't use both --delete-conflicting and " - "--ignore-conflicts-at-my-risk at the same time" - ) if self.editable and not self.build_directory: raise DistutilsArgError( "Must specify a build directory (-b) when using --editable" @@ -410,12 +400,7 @@ class easy_install(Command): return os.path.join(self.install_dir, "test-easy-install-%s" % pid) def warn_deprecated_options(self): - if self.delete_conflicting or self.ignore_conflicts_at_my_risk: - log.warn( - "Note: The -D, --delete-conflicting and" - " --ignore-conflicts-at-my-risk no longer have any purpose" - " and should not be used." - ) + pass def check_site_dir(self): """Verify that self.install_dir is .pth-capable dir, if needed""" @@ -516,7 +501,8 @@ Please make the appropriate changes for your system and try again. else: try: f.write("import os; f = open(%r, 'w'); f.write('OK'); f.close()\n" % (ok_file,)) - f.close(); f=None + f.close() + f=None executable = sys.executable if os.name=='nt': dirname,basename = os.path.split(executable) @@ -535,9 +521,12 @@ Please make the appropriate changes for your system and try again. ) return True finally: - if f: f.close() - if os.path.exists(ok_file): os.unlink(ok_file) - if os.path.exists(pth_file): os.unlink(pth_file) + if f: + f.close() + if os.path.exists(ok_file): + os.unlink(ok_file) + if os.path.exists(pth_file): + os.unlink(pth_file) if not self.multi_version: log.warn("TEST FAILED: %s does NOT support .pth files", instdir) return False @@ -651,7 +640,7 @@ Please make the appropriate changes for your system and try again. for dist in dists: self.process_distribution(spec, dist, deps) else: - dists = [self.check_conflicts(self.egg_distribution(download))] + dists = [self.egg_distribution(download)] self.process_distribution(spec, dists[0], deps, "Using") if spec is not None: @@ -849,7 +838,6 @@ Please make the appropriate changes for your system and try again. ensure_directory(destination) dist = self.egg_distribution(egg_path) - self.check_conflicts(dist) if not samefile(egg_path, destination): if os.path.isdir(destination) and not os.path.islink(destination): dir_util.remove_tree(destination, dry_run=self.dry_run) @@ -893,10 +881,10 @@ Please make the appropriate changes for your system and try again. # Convert the .exe to an unpacked egg egg_path = dist.location = os.path.join(tmpdir, dist.egg_name()+'.egg') egg_tmp = egg_path + '.tmp' - egg_info = os.path.join(egg_tmp, 'EGG-INFO') - pkg_inf = os.path.join(egg_info, 'PKG-INFO') + _egg_info = os.path.join(egg_tmp, 'EGG-INFO') + pkg_inf = os.path.join(_egg_info, 'PKG-INFO') ensure_directory(pkg_inf) # make sure EGG-INFO dir exists - dist._provider = PathMetadata(egg_tmp, egg_info) # XXX + dist._provider = PathMetadata(egg_tmp, _egg_info) # XXX self.exe_to_egg(dist_filename, egg_tmp) # Write EGG-INFO/PKG-INFO @@ -907,7 +895,7 @@ Please make the appropriate changes for your system and try again. if k != 'target_version': f.write('%s: %s\n' % (k.replace('_','-').title(), v)) f.close() - script_dir = os.path.join(egg_info,'scripts') + script_dir = os.path.join(_egg_info,'scripts') self.delete_blockers( # delete entry-point scripts to avoid duping [os.path.join(script_dir,args[0]) for args in get_script_args(dist)] ) @@ -968,82 +956,6 @@ Please make the appropriate changes for your system and try again. f.write('\n'.join(locals()[name])+'\n') f.close() - def check_conflicts(self, dist): - """Verify that there are no conflicting "old-style" packages""" - - return dist # XXX temporarily disable until new strategy is stable - from imp import find_module, get_suffixes - from glob import glob - - blockers = [] - names = dict.fromkeys(dist._get_metadata('top_level.txt')) # XXX private attr - - exts = {'.pyc':1, '.pyo':1} # get_suffixes() might leave one out - for ext,mode,typ in get_suffixes(): - exts[ext] = 1 - - for path,files in expand_paths([self.install_dir]+self.all_site_dirs): - for filename in files: - base,ext = os.path.splitext(filename) - if base in names: - if not ext: - # no extension, check for package - try: - f, filename, descr = find_module(base, [path]) - except ImportError: - continue - else: - if f: f.close() - if filename not in blockers: - blockers.append(filename) - elif ext in exts and base!='site': # XXX ugh - blockers.append(os.path.join(path,filename)) - if blockers: - self.found_conflicts(dist, blockers) - - return dist - - def found_conflicts(self, dist, blockers): - if self.delete_conflicting: - log.warn("Attempting to delete conflicting packages:") - return self.delete_blockers(blockers) - - msg = """\ -------------------------------------------------------------------------- -CONFLICT WARNING: - -The following modules or packages have the same names as modules or -packages being installed, and will be *before* the installed packages in -Python's search path. You MUST remove all of the relevant files and -directories before you will be able to use the package(s) you are -installing: - - %s - -""" % '\n '.join(blockers) - - if self.ignore_conflicts_at_my_risk: - msg += """\ -(Note: you can run EasyInstall on '%s' with the ---delete-conflicting option to attempt deletion of the above files -and/or directories.) -""" % dist.project_name - else: - msg += """\ -Note: you can attempt this installation again with EasyInstall, and use -either the --delete-conflicting (-D) option or the ---ignore-conflicts-at-my-risk option, to either delete the above files -and directories, or to ignore the conflicts, respectively. Note that if -you ignore the conflicts, the installed package(s) may not work. -""" - msg += """\ -------------------------------------------------------------------------- -""" - sys.stderr.write(msg) - sys.stderr.flush() - if not self.ignore_conflicts_at_my_risk: - raise DistutilsError("Installation aborted due to conflicts") - def installation_report(self, req, dist, what="Installed"): """Helpful installation message for display to package users""" msg = "\n%(what)s %(eggloc)s%(extras)s" @@ -1261,7 +1173,7 @@ Here are some of your options for correcting the problem: https://pythonhosted.org/setuptools/easy_install.html#custom-installation-locations Please make the appropriate changes for your system and try again.""" - return template % self.install_dir, os.environ.get('PYTHONPATH','') + return template % (self.install_dir, os.environ.get('PYTHONPATH','')) def install_site_py(self): """Make sure there's a site.py in the target dir, if needed""" @@ -1833,19 +1745,22 @@ class WindowsScriptWriter(ScriptWriter): """ Get a script writer suitable for Windows """ - # for compatibility, return the writer that creates exe launchers - # unless the SETUPTOOLS_USE_PYLAUNCHER is set, indicating - # future behavior. - use_legacy = 'SETUPTOOLS_USE_PYLAUNCHER' not in os.environ - if use_legacy: - return WindowsLauncherScriptWriter - return cls + writer_lookup = dict( + executable=WindowsExecutableLauncherWriter, + natural=cls, + ) + # for compatibility, use the executable launcher by default + launcher = os.environ.get('SETUPTOOLS_LAUNCHER', 'executable') + return writer_lookup[launcher] @classmethod def _get_script_args(cls, type_, name, header, script_text): "For Windows, add a .py extension" - ext = dict(console='.py', gui='.pyw')[type_] - old = ['.py', '.pyc', '.pyo', '.pyw', '.exe'] + ext = dict(console='.pya', gui='.pyw')[type_] + if ext not in os.environ['PATHEXT'].lower().split(';'): + warnings.warn("%s not listed in PATHEXT; scripts will not be " + "recognized as executables." % ext, UserWarning) + old = ['.pya', '.py', '-script.py', '.pyc', '.pyo', '.pyw', '.exe'] old.remove(ext) header = cls._adjust_header(type_, header) blockers = [name+x for x in old] @@ -1870,7 +1785,7 @@ class WindowsScriptWriter(ScriptWriter): return new_header -class WindowsLauncherScriptWriter(WindowsScriptWriter): +class WindowsExecutableLauncherWriter(WindowsScriptWriter): @classmethod def _get_script_args(cls, type_, name, header, script_text): """ diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 642687b2..a0ba5305 100755 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -9,12 +9,14 @@ from distutils.errors import * from distutils import log from setuptools.command.sdist import sdist from setuptools.compat import basestring +from setuptools import svn_utils from distutils.util import convert_path from distutils.filelist import FileList as _FileList from pkg_resources import parse_requirements, safe_name, parse_version, \ safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename from setuptools.command.sdist import walk_revctrl + class egg_info(Command): description = "create a distribution's .egg-info directory" @@ -213,42 +215,9 @@ class egg_info(Command): @staticmethod def get_svn_revision(): - revision = 0 - urlre = re.compile('url="([^"]+)"') - revre = re.compile('committed-rev="(\d+)"') - - for base,dirs,files in os.walk(os.curdir): - if '.svn' not in dirs: - dirs[:] = [] - continue # no sense walking uncontrolled subdirs - dirs.remove('.svn') - f = open(os.path.join(base,'.svn','entries')) - data = f.read() - f.close() + return str(svn_utils.SvnInfo.load(os.curdir).get_revision()) + - 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]) - if base==os.curdir: - base_url = dirurl+'/' # save the root url - elif not dirurl.startswith(base_url): - dirs[:] = [] - continue # not part of the same svn tree, skip it - revision = max(revision, localrev) - - return str(revision or get_pkg_info_revision()) diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index 39cd6043..6249e75c 100755 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -1,46 +1,16 @@ +import os +import re +import sys +from glob import glob + +import pkg_resources 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 +from setuptools import svn_utils READMES = ('README', 'README.rst', 'README.txt') -entities = [ - ("<","<"), (">", ">"), (""", '"'), ("'", "'"), - ("&", "&") -] - -def unescape(data): - for old,new in entities: - data = data.replace(old,new) - return data - -def re_finder(pattern, postproc=None): - def find(dirname, filename): - f = open(filename,'rU') - data = f.read() - f.close() - for match in pattern.finditer(data): - path = match.group(1) - if postproc: - path = postproc(path) - yield joinpath(dirname,path) - return find - -def joinpath(prefix,suffix): - if not prefix: - return suffix - return os.path.join(prefix,suffix) - - - - - - - - def walk_revctrl(dirname=''): """Find all files under revision control""" @@ -48,80 +18,51 @@ def walk_revctrl(dirname=''): for item in ep.load()(dirname): yield item -def _default_revctrl(dirname=''): - for path, finder in finders: - path = joinpath(dirname,path) + +#TODO will need test case +class re_finder(object): + + def __init__(self, path, pattern, postproc=None): + self.pattern = pattern + self.postproc = postproc + self.path = convert_path(path) + + def _finder(self, dirname, filename): + f = open(filename,'rU') + try: + data = f.read() + finally: + f.close() + for match in self.pattern.finditer(data): + path = match.group(1) + if postproc: + #postproc used to be used when the svn finder + #was an re_finder for calling unescape + path = postproc(path) + yield svn_utils.joinpath(dirname,path) + def __call__(self, dirname=''): + path = svn_utils.joinpath(dirname, self.path) + if os.path.isfile(path): - for path in finder(dirname,path): + for path in self._finder(dirname,path): if os.path.isfile(path): yield path elif os.path.isdir(path): - for item in _default_revctrl(path): + for item in self.find(path): yield item -def externals_finder(dirname, filename): - """Find any 'svn:externals' directories""" - found = False - f = open(filename,'rt') - for line in iter(f.readline, ''): # can't use direct iter! - parts = line.split() - if len(parts)==2: - kind,length = parts - data = f.read(int(length)) - if kind=='K' and data=='svn:externals': - found = True - elif kind=='V' and found: - f.close() - break - else: - f.close() - return - - for line in data.splitlines(): - parts = line.split() - if parts: - yield joinpath(dirname, parts[0]) - - -entries_pattern = re.compile(r'name="([^"]+)"(?![^>]+deleted="true")', re.I) - -def entries_finder(dirname, filename): - f = open(filename,'rU') - data = f.read() - f.close() - 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]) - - -finders = [ - (convert_path('CVS/Entries'), - re_finder(re.compile(r"^\w?/([^/]+)/", re.M))), - (convert_path('.svn/entries'), entries_finder), - (convert_path('.svn/dir-props'), externals_finder), - (convert_path('.svn/dir-prop-base'), externals_finder), # svn 1.4 -] - - - - - - +def _default_revctrl(dirname=''): + 'Primary svn_cvs entry point' + for finder in finders: + for item in finder(dirname): + yield item +finders = [ + re_finder('CVS/Entries', re.compile(r"^\w?/([^/]+)/", re.M)), + svn_utils.svn_finder, +] @@ -179,11 +120,12 @@ class sdist(_sdist): # 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) - ): + has_leaky_handle = ( + sys.version_info < (2,7,2) + or (3,0) <= sys.version_info < (3,1,4) + or (3,2) <= sys.version_info < (3,2,1) + ) + if has_leaky_handle: read_template = __read_template_hack def add_defaults(self): @@ -248,7 +190,6 @@ class sdist(_sdist): "standard file not found: should have one of " +', '.join(READMES) ) - def make_release_tree(self, base_dir, files): _sdist.make_release_tree(self, base_dir, files) @@ -295,10 +236,3 @@ class sdist(_sdist): continue self.filelist.append(line) manifest.close() - - - - - - -# diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py index 02d955ed..a6eff385 100755 --- a/setuptools/command/upload.py +++ b/setuptools/command/upload.py @@ -2,10 +2,10 @@ Implements the Distutils 'upload' subcommand (upload package to PyPI).""" -from distutils.errors import * +from distutils import errors +from distutils import log from distutils.core import Command from distutils.spawn import spawn -from distutils import log try: from hashlib import md5 except ImportError: @@ -45,7 +45,7 @@ class upload(Command): def finalize_options(self): if self.identity and not self.sign: - raise DistutilsOptionError( + raise errors.DistutilsOptionError( "Must use --sign for --identity to have meaning" ) if 'HOME' in os.environ: @@ -68,7 +68,7 @@ class upload(Command): def run(self): if not self.distribution.dist_files: - raise DistutilsOptionError("No dist file created in earlier command") + raise errors.DistutilsOptionError("No dist file created in earlier command") for command, pyversion, filename in self.distribution.dist_files: self.upload_file(command, pyversion, filename) @@ -119,10 +119,10 @@ class upload(Command): boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' sep_boundary = '\n--' + boundary end_boundary = sep_boundary + '--' - body = StringIO.StringIO() + body = StringIO() for key, value in data.items(): # handle multiple entries for the same name - if type(value) != type([]): + if not isinstance(value, list): value = [value] for value in value: if type(value) is tuple: @@ -158,7 +158,6 @@ class upload(Command): raise AssertionError("unsupported schema " + schema) data = '' - loglevel = log.INFO try: http.connect() http.putrequest("POST", url) diff --git a/setuptools/compat.py b/setuptools/compat.py index 3a961c0e..bbc98d66 100644 --- a/setuptools/compat.py +++ b/setuptools/compat.py @@ -26,7 +26,8 @@ if sys.version_info[0] < 3: reduce = reduce unichr = unichr unicode = unicode - from urllib import url2pathname + bytes = str + from urllib import url2pathname, splittag import urllib2 from urllib2 import urlopen, HTTPError, URLError, unquote, splituser from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit @@ -69,12 +70,13 @@ else: from functools import reduce unichr = chr unicode = str + bytes = bytes from urllib.error import HTTPError, URLError import urllib.request as urllib2 from urllib.request import urlopen, url2pathname from urllib.parse import ( urlparse, urlunparse, unquote, splituser, urljoin, urlsplit, - urlunsplit, + urlunsplit, splittag, ) xrange = range filterfalse = itertools.filterfalse @@ -84,7 +86,7 @@ else: globs = globals() if locs is None: locs = globs - f = open(fn) + f = open(fn, 'rb') try: source = f.read() finally: diff --git a/setuptools/dist.py b/setuptools/dist.py index 01889215..c5b02f99 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -1,17 +1,19 @@ __all__ = ['Distribution'] import re +import os import sys +import warnings +import distutils.log +import distutils.core +import distutils.cmd from distutils.core import Distribution as _Distribution +from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, + DistutilsSetupError) + from setuptools.depends import Require -from setuptools.command.install import install -from setuptools.command.sdist import sdist -from setuptools.command.install_lib import install_lib from setuptools.compat import numeric_types, basestring -from distutils.errors import DistutilsOptionError, DistutilsPlatformError -from distutils.errors import DistutilsSetupError -import setuptools, pkg_resources, distutils.core, distutils.dist, distutils.cmd -import os, distutils.log +import pkg_resources def _get_unpatched(cls): """Protect against re-patching the distutils if reloaded @@ -132,38 +134,6 @@ def check_packages(dist, attr, value): "WARNING: %r not a valid package name; please use only" ".-separated package names in setup.py", pkgname ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - class Distribution(_Distribution): @@ -194,7 +164,8 @@ class Distribution(_Distribution): EasyInstall and requests one of your extras, the corresponding additional requirements will be installed if needed. - 'features' -- a dictionary mapping option names to 'setuptools.Feature' + 'features' **deprecated** -- a dictionary mapping option names to + 'setuptools.Feature' objects. Features are a portion of the distribution that can be included or excluded based on user options, inter-feature dependencies, and availability on the current system. Excluded features are omitted @@ -248,10 +219,13 @@ class Distribution(_Distribution): dist._version = pkg_resources.safe_version(str(attrs['version'])) self._patched_dist = dist - def __init__ (self, attrs=None): + def __init__(self, attrs=None): 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: + Feature.warn_deprecated() self.require_features = [] self.features = {} self.dist_files = [] @@ -362,23 +336,6 @@ class Distribution(_Distribution): self.global_options = self.feature_options = go + self.global_options self.negative_opt = self.feature_negopt = no - - - - - - - - - - - - - - - - - def _finalize_features(self): """Add/remove features and resolve dependencies between them""" @@ -396,7 +353,6 @@ class Distribution(_Distribution): feature.exclude_from(self) self._set_feature(name,0) - def get_command_class(self, command): """Pluggable version of get_command_class()""" if command in self.cmdclass: @@ -416,10 +372,6 @@ class Distribution(_Distribution): self.cmdclass[ep.name] = cmdclass return _Distribution.print_commands(self) - - - - def _set_feature(self,name,status): """Set feature's inclusion status""" setattr(self,self._feature_attrname(name),status) @@ -434,8 +386,8 @@ class Distribution(_Distribution): if self.feature_is_included(name)==0: descr = self.features[name].description raise DistutilsOptionError( - descr + " is required, but was excluded or is not available" - ) + descr + " is required, but was excluded or is not available" + ) self.features[name].include_in(self) self._set_feature(name,1) @@ -483,7 +435,6 @@ class Distribution(_Distribution): if p.name != package and not p.name.startswith(pfx) ] - def has_contents_for(self,package): """Return true if 'exclude_package(package)' would do something""" @@ -493,15 +444,6 @@ class Distribution(_Distribution): if p==package or p.startswith(pfx): return True - - - - - - - - - def _exclude_misc(self,name,value): """Handle 'exclude()' for list/tuple attrs without a special handler""" if not isinstance(value,sequence): @@ -573,17 +515,6 @@ class Distribution(_Distribution): ) list(map(self.exclude_package, packages)) - - - - - - - - - - - def _parse_command_opts(self, parser, args): # Remove --with-X/--without-X options when processing command args self.global_options = self.__class__.global_options @@ -610,21 +541,6 @@ class Distribution(_Distribution): return nargs - - - - - - - - - - - - - - - def get_cmdline_options(self): """Return a '{cmd: {opt:val}}' map of all command-line options @@ -665,7 +581,6 @@ class Distribution(_Distribution): return d - def iter_distribution_names(self): """Yield all packages, modules, and extension names in distribution""" @@ -684,7 +599,6 @@ class Distribution(_Distribution): name = name[:-6] yield name - def handle_display_options(self, option_order): """If there were any non-global "display-only" options (--help-commands or the metadata display options) on the command @@ -726,26 +640,14 @@ for module in distutils.dist, distutils.core, distutils.cmd: module.Distribution = Distribution - - - - - - - - - - - - - - - - - - class Feature: - """A subset of the distribution that can be excluded if unneeded/wanted + """ + **deprecated** -- The `Feature` facility was never completely implemented + or supported, `has reported issues + <https://bitbucket.org/pypa/setuptools/issue/58>`_ and will be removed in + a future version. + + A subset of the distribution that can be excluded if unneeded/wanted Features are created using these keyword arguments: @@ -794,9 +696,19 @@ class Feature: Aside from the methods, the only feature attributes that distributions look at are 'description' and 'optional'. """ + + @staticmethod + def warn_deprecated(): + warnings.warn( + "Features are deprecated and will be removed in a future " + "version. See http://bitbucket.org/pypa/setuptools/65.", + DeprecationWarning, + stacklevel=3, + ) + def __init__(self, description, standard=False, available=True, - optional=True, require_features=(), remove=(), **extras - ): + optional=True, require_features=(), remove=(), **extras): + self.warn_deprecated() self.description = description self.standard = standard @@ -847,8 +759,6 @@ class Feature: for f in self.require_features: dist.include_feature(f) - - def exclude_from(self,dist): """Ensure feature is excluded from distribution @@ -865,8 +775,6 @@ class Feature: for item in self.remove: dist.exclude_package(item) - - def validate(self,dist): """Verify that feature makes sense in context of distribution @@ -886,7 +794,3 @@ class Feature: " doesn't contain any packages or modules under %s" % (self.description, item, item) ) - - - - diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 231311ec..26d1d4ad 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -23,6 +23,7 @@ from setuptools.compat import filterfalse from fnmatch import translate from setuptools.py24compat import hashlib from setuptools.py24compat import wraps +from setuptools.py26compat import strip_fragment from setuptools.py27compat import get_all_headers EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$') @@ -344,7 +345,8 @@ class PackageIndex(Environment): s = URL_SCHEME(url) if (s and s.group(1).lower()=='file') or self.allows(urlparse(url)[1]): return True - msg = "\nLink to % s ***BLOCKED*** by --allow-hosts\n" + msg = ("\nNote: Bypassing %s (disallowed host; see " + "http://bit.ly/1dg9ijs for details).\n") if fatal: raise DistutilsError(msg % url) else: @@ -660,7 +662,7 @@ class PackageIndex(Environment): fp, tfp, info = None, None, None try: checker = HashChecker.from_url(url) - fp = self.open_url(url) + fp = self.open_url(strip_fragment(url)) if isinstance(fp, HTTPError): raise DistutilsError( "Can't download %s: %s %s" % (url, fp.code,fp.msg) diff --git a/setuptools/py26compat.py b/setuptools/py26compat.py new file mode 100644 index 00000000..738b0cc4 --- /dev/null +++ b/setuptools/py26compat.py @@ -0,0 +1,19 @@ +""" +Compatibility Support for Python 2.6 and earlier +""" + +import sys + +from setuptools.compat import splittag + +def strip_fragment(url): + """ + In `Python 8280 <http://bugs.python.org/issue8280>`_, Python 2.7 and + later was patched to disregard the fragment when making URL requests. + Do the same for Python 2.6 and earlier. + """ + url, fragment = splittag(url) + return url + +if sys.version_info >= (2,7): + strip_fragment = lambda x: x diff --git a/setuptools/script template (dev).py b/setuptools/script template (dev).py index 901790e7..b3fe209e 100644 --- a/setuptools/script template (dev).py +++ b/setuptools/script template (dev).py @@ -1,9 +1,11 @@ # EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r __requires__ = """%(spec)r""" -from pkg_resources import require; require("""%(spec)r""") +import sys +from pkg_resources import require +require("""%(spec)r""") del require __file__ = """%(dev_path)r""" -try: +if sys.version_info < (3, 0): execfile(__file__) -except NameError: +else: exec(compile(open(__file__).read(), __file__, 'exec')) diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py index f8a780a9..346188f2 100644 --- a/setuptools/ssl_support.py +++ b/setuptools/ssl_support.py @@ -44,6 +44,7 @@ is_available = ssl is not None and object not in (HTTPSHandler, HTTPSConnection) try: from socket import create_connection except ImportError: + from socket import error _GLOBAL_DEFAULT_TIMEOUT = getattr(socket, '_GLOBAL_DEFAULT_TIMEOUT', object()) def create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, source_address=None): @@ -85,33 +86,74 @@ except ImportError: try: from ssl import CertificateError, match_hostname except ImportError: + try: + from backports.ssl_match_hostname import CertificateError + from backports.ssl_match_hostname import match_hostname + except ImportError: + CertificateError = None + match_hostname = None + +if not CertificateError: class CertificateError(ValueError): pass - def _dnsname_to_pat(dn, max_wildcards=1): +if not match_hostname: + def _dnsname_match(dn, hostname, max_wildcards=1): + """Matching according to RFC 6125, section 6.4.3 + + http://tools.ietf.org/html/rfc6125#section-6.4.3 + """ pats = [] - for frag in dn.split(r'.'): - if frag.count('*') > max_wildcards: - # Issue #17980: avoid denials of service by refusing more - # than one wildcard per fragment. A survery of established - # policy among SSL implementations showed it to be a - # reasonable choice. - raise CertificateError( - "too many wildcards in certificate DNS name: " + repr(dn)) - 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) + if not dn: + return False + + # Ported from python3-syntax: + # leftmost, *remainder = dn.split(r'.') + parts = dn.split(r'.') + leftmost = parts[0] + remainder = parts[1:] + + wildcards = leftmost.count('*') + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survey of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn)) + + # speed up common case w/o wildcards + if not wildcards: + return dn.lower() == hostname.lower() + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == '*': + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append('[^.]+') + elif leftmost.startswith('xn--') or hostname.startswith('xn--'): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + return pat.match(hostname) 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*. + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed, but IP addresses are not accepted for *hostname*. CertificateError is raised on failure. On success, the function returns nothing. @@ -122,7 +164,7 @@ except ImportError: san = cert.get('subjectAltName', ()) for key, value in san: if key == 'DNS': - if _dnsname_to_pat(value).match(hostname): + if _dnsname_match(value, hostname): return dnsnames.append(value) if not dnsnames: @@ -133,7 +175,7 @@ except ImportError: # XXX according to RFC 2818, the most specific Common Name # must be used. if key == 'commonName': - if _dnsname_to_pat(value).match(hostname): + if _dnsname_match(value, hostname): return dnsnames.append(value) if len(dnsnames) > 1: @@ -194,6 +236,12 @@ class VerifyingHTTPSConn(HTTPSConnection): sock = create_connection( (self.host, self.port), getattr(self,'source_address',None) ) + + # Handle the socket if a (proxy) tunnel is present + if hasattr(self, '_tunnel') and getattr(self, '_tunnel_host', None): + self.sock = sock + self._tunnel() + self.sock = ssl.wrap_socket( sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=self.ca_bundle ) diff --git a/setuptools/svn_utils.py b/setuptools/svn_utils.py new file mode 100644 index 00000000..224d11ea --- /dev/null +++ b/setuptools/svn_utils.py @@ -0,0 +1,534 @@ +import os
+import re
+import sys
+from distutils import log
+import xml.dom.pulldom
+import shlex
+import locale
+import unicodedata
+import warnings
+from setuptools.compat import unicode, bytes
+from xml.sax.saxutils import unescape
+
+try:
+ import urlparse
+except ImportError:
+ import urllib.parse as urlparse
+
+from subprocess import Popen as _Popen, PIPE as _PIPE
+
+#NOTE: Use of the command line options require SVN 1.3 or newer (December 2005)
+# and SVN 1.3 hasn't been supported by the developers since mid 2008.
+
+#subprocess is called several times with shell=(sys.platform=='win32')
+#see the follow for more information:
+# http://bugs.python.org/issue8557
+# http://stackoverflow.com/questions/5658622/
+# python-subprocess-popen-environment-path
+
+
+def _run_command(args, stdout=_PIPE, stderr=_PIPE, encoding=None, stream=0):
+ #regarding the shell argument, see: http://bugs.python.org/issue8557
+ try:
+ proc = _Popen(args, stdout=stdout, stderr=stderr,
+ shell=(sys.platform == 'win32'))
+
+ data = proc.communicate()[stream]
+ except OSError:
+ return 1, ''
+
+ #doubled checked and
+ data = decode_as_string(data, encoding)
+
+ #communciate calls wait()
+ return proc.returncode, data
+
+
+def _get_entry_schedule(entry):
+ schedule = entry.getElementsByTagName('schedule')[0]
+ return "".join([t.nodeValue
+ for t in schedule.childNodes
+ if t.nodeType == t.TEXT_NODE])
+
+
+def _get_target_property(target):
+ property_text = target.getElementsByTagName('property')[0]
+ return "".join([t.nodeValue
+ for t in property_text.childNodes
+ if t.nodeType == t.TEXT_NODE])
+
+
+def _get_xml_data(decoded_str):
+ if sys.version_info < (3, 0):
+ #old versions want an encoded string
+ data = decoded_str.encode('utf-8')
+ else:
+ data = decoded_str
+ return data
+
+
+def joinpath(prefix, *suffix):
+ if not prefix or prefix == '.':
+ return os.path.join(*suffix)
+ return os.path.join(prefix, *suffix)
+
+
+def decode_as_string(text, encoding=None):
+ """
+ Decode the console or file output explicitly using getpreferredencoding.
+ The text paraemeter should be a encoded string, if not no decode occurs
+ If no encoding is given, getpreferredencoding is used. If encoding is
+ specified, that is used instead. This would be needed for SVN --xml
+ output. Unicode is explicitly put in composed NFC form.
+
+ --xml should be UTF-8 (SVN Issue 2938) the discussion on the Subversion
+ DEV List from 2007 seems to indicate the same.
+ """
+ #text should be a byte string
+
+ if encoding is None:
+ encoding = locale.getpreferredencoding()
+
+ if not isinstance(text, unicode):
+ text = text.decode(encoding)
+
+ text = unicodedata.normalize('NFC', text)
+
+ return text
+
+
+def parse_dir_entries(decoded_str):
+ '''Parse the entries from a recursive info xml'''
+ doc = xml.dom.pulldom.parseString(_get_xml_data(decoded_str))
+ entries = list()
+
+ for event, node in doc:
+ if event == 'START_ELEMENT' and node.nodeName == 'entry':
+ doc.expandNode(node)
+ if not _get_entry_schedule(node).startswith('delete'):
+ entries.append((node.getAttribute('path'),
+ node.getAttribute('kind')))
+
+ return entries[1:] # do not want the root directory
+
+
+def parse_externals_xml(decoded_str, prefix=''):
+ '''Parse a propget svn:externals xml'''
+ prefix = os.path.normpath(prefix)
+ prefix = os.path.normcase(prefix)
+
+ doc = xml.dom.pulldom.parseString(_get_xml_data(decoded_str))
+ externals = list()
+
+ for event, node in doc:
+ if event == 'START_ELEMENT' and node.nodeName == 'target':
+ doc.expandNode(node)
+ path = os.path.normpath(node.getAttribute('path'))
+
+ if os.path.normcase(path).startswith(prefix):
+ path = path[len(prefix)+1:]
+
+ data = _get_target_property(node)
+ #data should be decoded already
+ for external in parse_external_prop(data):
+ externals.append(joinpath(path, external))
+
+ return externals # do not want the root directory
+
+
+def parse_external_prop(lines):
+ """
+ Parse the value of a retrieved svn:externals entry.
+
+ possible token setups (with quotng and backscaping in laters versions)
+ URL[@#] EXT_FOLDERNAME
+ [-r#] URL EXT_FOLDERNAME
+ EXT_FOLDERNAME [-r#] URL
+ """
+ externals = []
+ for line in lines.splitlines():
+ line = line.lstrip() # there might be a "\ "
+ if not line:
+ continue
+
+ if sys.version_info < (3, 0):
+ #shlex handles NULLs just fine and shlex in 2.7 tries to encode
+ #as ascii automatiically
+ line = line.encode('utf-8')
+ line = shlex.split(line)
+ if sys.version_info < (3, 0):
+ line = [x.decode('utf-8') for x in line]
+
+ #EXT_FOLDERNAME is either the first or last depending on where
+ #the URL falls
+ if urlparse.urlsplit(line[-1])[0]:
+ external = line[0]
+ else:
+ external = line[-1]
+
+ external = decode_as_string(external, encoding="utf-8")
+ externals.append(os.path.normpath(external))
+
+ return externals
+
+
+def parse_prop_file(filename, key):
+ found = False
+ f = open(filename, 'rt')
+ data = ''
+ try:
+ for line in iter(f.readline, ''): # can't use direct iter!
+ parts = line.split()
+ if len(parts) == 2:
+ kind, length = parts
+ data = f.read(int(length))
+ if kind == 'K' and data == key:
+ found = True
+ elif kind == 'V' and found:
+ break
+ finally:
+ f.close()
+
+ return data
+
+
+class SvnInfo(object):
+ '''
+ Generic svn_info object. No has little knowledge of how to extract
+ information. Use cls.load to instatiate according svn version.
+
+ Paths are not filesystem encoded.
+ '''
+
+ @staticmethod
+ def get_svn_version():
+ code, data = _run_command(['svn', '--version', '--quiet'])
+ if code == 0 and data:
+ return data.strip()
+ else:
+ return ''
+
+ #svnversion return values (previous implementations return max revision)
+ # 4123:4168 mixed revision working copy
+ # 4168M modified working copy
+ # 4123S switched working copy
+ # 4123:4168MS mixed revision, modified, switched working copy
+ revision_re = re.compile(r'(?:([\-0-9]+):)?(\d+)([a-z]*)\s*$', re.I)
+
+ @classmethod
+ def load(cls, dirname=''):
+ normdir = os.path.normpath(dirname)
+ code, data = _run_command(['svn', 'info', normdir])
+ # Must check for some contents, as some use empty directories
+ # in testcases
+ svn_dir = os.path.join(normdir, '.svn')
+ has_svn = (os.path.isfile(os.path.join(svn_dir, 'entries')) or
+ os.path.isfile(os.path.join(svn_dir, 'dir-props')) or
+ os.path.isfile(os.path.join(svn_dir, 'dir-prop-base')))
+
+ svn_version = tuple(cls.get_svn_version().split('.'))
+
+ try:
+ base_svn_version = tuple(int(x) for x in svn_version[:2])
+ except ValueError:
+ base_svn_version = tuple()
+
+ if has_svn and (code or not base_svn_version
+ or base_svn_version < (1, 3)):
+ warnings.warn(("No SVN 1.3+ command found: falling back "
+ "on pre 1.7 .svn parsing"), DeprecationWarning)
+ return SvnFileInfo(dirname)
+ elif not has_svn:
+ return SvnInfo(dirname)
+ elif base_svn_version < (1, 5):
+ return Svn13Info(dirname)
+ else:
+ return Svn15Info(dirname)
+
+ def __init__(self, path=''):
+ self.path = path
+ self._entries = None
+ self._externals = None
+
+ def get_revision(self):
+ 'Retrieve the directory revision informatino using svnversion'
+ code, data = _run_command(['svnversion', '-c', self.path])
+ if code:
+ log.warn("svnversion failed")
+ return 0
+
+ parsed = self.revision_re.match(data)
+ if parsed:
+ return int(parsed.group(2))
+ else:
+ return 0
+
+ @property
+ def entries(self):
+ if self._entries is None:
+ self._entries = self.get_entries()
+ return self._entries
+
+ @property
+ def externals(self):
+ if self._externals is None:
+ self._externals = self.get_externals()
+ return self._externals
+
+ def iter_externals(self):
+ '''
+ Iterate over the svn:external references in the repository path.
+ '''
+ for item in self.externals:
+ yield item
+
+ def iter_files(self):
+ '''
+ Iterate over the non-deleted file entries in the repository path
+ '''
+ for item, kind in self.entries:
+ if kind.lower() == 'file':
+ yield item
+
+ def iter_dirs(self, include_root=True):
+ '''
+ Iterate over the non-deleted file entries in the repository path
+ '''
+ if include_root:
+ yield self.path
+ for item, kind in self.entries:
+ if kind.lower() == 'dir':
+ yield item
+
+ def get_entries(self):
+ return []
+
+ def get_externals(self):
+ return []
+
+
+class Svn13Info(SvnInfo):
+ def get_entries(self):
+ code, data = _run_command(['svn', 'info', '-R', '--xml', self.path],
+ encoding="utf-8")
+
+ if code:
+ log.debug("svn info failed")
+ return []
+
+ return parse_dir_entries(data)
+
+ def get_externals(self):
+ #Previous to 1.5 --xml was not supported for svn propget and the -R
+ #output format breaks the shlex compatible semantics.
+ cmd = ['svn', 'propget', 'svn:externals']
+ result = []
+ for folder in self.iter_dirs():
+ code, lines = _run_command(cmd + [folder], encoding="utf-8")
+ if code != 0:
+ log.warn("svn propget failed")
+ return []
+ #lines should a str
+ for external in parse_external_prop(lines):
+ if folder:
+ external = os.path.join(folder, external)
+ result.append(os.path.normpath(external))
+
+ return result
+
+
+class Svn15Info(Svn13Info):
+ def get_externals(self):
+ cmd = ['svn', 'propget', 'svn:externals', self.path, '-R', '--xml']
+ code, lines = _run_command(cmd, encoding="utf-8")
+ if code:
+ log.debug("svn propget failed")
+ return []
+ return parse_externals_xml(lines, prefix=os.path.abspath(self.path))
+
+
+class SvnFileInfo(SvnInfo):
+
+ def __init__(self, path=''):
+ super(SvnFileInfo, self).__init__(path)
+ self._directories = None
+ self._revision = None
+
+ def _walk_svn(self, base):
+ entry_file = joinpath(base, '.svn', 'entries')
+ if os.path.isfile(entry_file):
+ entries = SVNEntriesFile.load(base)
+ yield (base, False, entries.parse_revision())
+ for path in entries.get_undeleted_records():
+ path = decode_as_string(path)
+ path = joinpath(base, path)
+ if os.path.isfile(path):
+ yield (path, True, None)
+ elif os.path.isdir(path):
+ for item in self._walk_svn(path):
+ yield item
+
+ def _build_entries(self):
+ entries = list()
+
+ rev = 0
+ for path, isfile, dir_rev in self._walk_svn(self.path):
+ if isfile:
+ entries.append((path, 'file'))
+ else:
+ entries.append((path, 'dir'))
+ rev = max(rev, dir_rev)
+
+ self._entries = entries
+ self._revision = rev
+
+ def get_entries(self):
+ if self._entries is None:
+ self._build_entries()
+ return self._entries
+
+ def get_revision(self):
+ if self._revision is None:
+ self._build_entries()
+ return self._revision
+
+ def get_externals(self):
+ prop_files = [['.svn', 'dir-prop-base'],
+ ['.svn', 'dir-props']]
+ externals = []
+
+ for dirname in self.iter_dirs():
+ prop_file = None
+ for rel_parts in prop_files:
+ filename = joinpath(dirname, *rel_parts)
+ if os.path.isfile(filename):
+ prop_file = filename
+
+ if prop_file is not None:
+ ext_prop = parse_prop_file(prop_file, 'svn:externals')
+ #ext_prop should be utf-8 coming from svn:externals
+ ext_prop = decode_as_string(ext_prop, encoding="utf-8")
+ externals.extend(parse_external_prop(ext_prop))
+
+ return externals
+
+
+def svn_finder(dirname=''):
+ #combined externals due to common interface
+ #combined externals and entries due to lack of dir_props in 1.7
+ info = SvnInfo.load(dirname)
+ for path in info.iter_files():
+ yield path
+
+ for path in info.iter_externals():
+ sub_info = SvnInfo.load(path)
+ for sub_path in sub_info.iter_files():
+ yield sub_path
+
+
+class SVNEntriesFile(object):
+ def __init__(self, data):
+ self.data = data
+
+ @classmethod
+ def load(class_, base):
+ filename = os.path.join(base, '.svn', 'entries')
+ f = open(filename)
+ try:
+ result = SVNEntriesFile.read(f)
+ finally:
+ f.close()
+ return result
+
+ @classmethod
+ def read(class_, fileobj):
+ data = fileobj.read()
+ is_xml = data.startswith('<?xml')
+ class_ = [SVNEntriesFileText, SVNEntriesFileXML][is_xml]
+ return class_(data)
+
+ def parse_revision(self):
+ all_revs = self.parse_revision_numbers() + [0]
+ return max(all_revs)
+
+
+class SVNEntriesFileText(SVNEntriesFile):
+ known_svn_versions = {
+ '1.4.x': 8,
+ '1.5.x': 9,
+ '1.6.x': 10,
+ }
+
+ def __get_cached_sections(self):
+ return self.sections
+
+ def get_sections(self):
+ SECTION_DIVIDER = '\f\n'
+ sections = self.data.split(SECTION_DIVIDER)
+ sections = [x for x in map(str.splitlines, sections)]
+ try:
+ # remove the SVN version number from the first line
+ svn_version = int(sections[0].pop(0))
+ if not svn_version in self.known_svn_versions.values():
+ log.warn("Unknown subversion verson %d", svn_version)
+ except ValueError:
+ return
+ self.sections = sections
+ self.get_sections = self.__get_cached_sections
+ return self.sections
+
+ def is_valid(self):
+ return bool(self.get_sections())
+
+ def get_url(self):
+ return self.get_sections()[0][4]
+
+ def parse_revision_numbers(self):
+ revision_line_number = 9
+ rev_numbers = [
+ int(section[revision_line_number])
+ for section in self.get_sections()
+ if (len(section) > revision_line_number
+ and section[revision_line_number])
+ ]
+ return rev_numbers
+
+ def get_undeleted_records(self):
+ undeleted = lambda s: s and s[0] and (len(s) < 6 or s[5] != 'delete')
+ result = [
+ section[0]
+ for section in self.get_sections()
+ if undeleted(section)
+ ]
+ return result
+
+
+class SVNEntriesFileXML(SVNEntriesFile):
+ def is_valid(self):
+ return True
+
+ def get_url(self):
+ "Get repository URL"
+ urlre = re.compile('url="([^"]+)"')
+ return urlre.search(self.data).group(1)
+
+ def parse_revision_numbers(self):
+ revre = re.compile(r'committed-rev="(\d+)"')
+ return [
+ int(m.group(1))
+ for m in revre.finditer(self.data)
+ ]
+
+ def get_undeleted_records(self):
+ entries_pattern = \
+ re.compile(r'name="([^"]+)"(?![^>]+deleted="true")', re.I)
+ results = [
+ unescape(match.group(1))
+ for match in entries_pattern.finditer(self.data)
+ ]
+ return results
+
+
+if __name__ == '__main__':
+ for name in svn_finder(sys.argv[1]):
+ print(name)
diff --git a/setuptools/tests/environment.py b/setuptools/tests/environment.py new file mode 100644 index 00000000..bd2c53d2 --- /dev/null +++ b/setuptools/tests/environment.py @@ -0,0 +1,158 @@ +import os +import zipfile +import sys +import tempfile +import unittest +import shutil +import stat +import unicodedata + +from subprocess import Popen as _Popen, PIPE as _PIPE + + +def _extract(self, member, path=None, pwd=None): + """for zipfile py2.5 borrowed from cpython""" + if not isinstance(member, zipfile.ZipInfo): + member = self.getinfo(member) + + if path is None: + path = os.getcwd() + + return _extract_member(self, member, path, pwd) + + +def _extract_from_zip(self, name, dest_path): + dest_file = open(dest_path, 'wb') + try: + dest_file.write(self.read(name)) + finally: + dest_file.close() + + +def _extract_member(self, member, targetpath, pwd): + """for zipfile py2.5 borrowed from cpython""" + # build the destination pathname, replacing + # forward slashes to platform specific separators. + # Strip trailing path separator, unless it represents the root. + if (targetpath[-1:] in (os.path.sep, os.path.altsep) + and len(os.path.splitdrive(targetpath)[1]) > 1): + targetpath = targetpath[:-1] + + # don't include leading "/" from file name if present + if member.filename[0] == '/': + targetpath = os.path.join(targetpath, member.filename[1:]) + else: + targetpath = os.path.join(targetpath, member.filename) + + targetpath = os.path.normpath(targetpath) + + # Create all upper directories if necessary. + upperdirs = os.path.dirname(targetpath) + if upperdirs and not os.path.exists(upperdirs): + os.makedirs(upperdirs) + + if member.filename[-1] == '/': + if not os.path.isdir(targetpath): + os.mkdir(targetpath) + return targetpath + + _extract_from_zip(self, member.filename, targetpath) + + return targetpath + + +def _remove_dir(target): + + #on windows this seems to a problem + for dir_path, dirs, files in os.walk(target): + os.chmod(dir_path, stat.S_IWRITE) + for filename in files: + os.chmod(os.path.join(dir_path, filename), stat.S_IWRITE) + shutil.rmtree(target) + + +class ZippedEnvironment(unittest.TestCase): + + datafile = None + dataname = None + old_cwd = None + + def setUp(self): + if not os.path.isfile(self.datafile): + self.old_cwd = None + return + + self.old_cwd = os.getcwd() + + self.temp_dir = tempfile.mkdtemp() + zip_file, source, target = [None, None, None] + try: + zip_file = zipfile.ZipFile(self.datafile) + for files in zip_file.namelist(): + _extract(zip_file, files, self.temp_dir) + finally: + if zip_file: + zip_file.close() + del zip_file + + os.chdir(os.path.join(self.temp_dir, self.dataname)) + + def tearDown(self): + try: + if self.old_cwd: + os.chdir(self.old_cwd) + _remove_dir(self.temp_dir) + except OSError: + #sigh? + pass + + +def _which_dirs(cmd): + result = set() + for path in os.environ.get('PATH', '').split(os.pathsep): + filename = os.path.join(path, cmd) + if os.access(filename, os.X_OK): + result.add(path) + return result + + +def run_setup_py(cmd, pypath=None, path=None, + data_stream=0, env=None): + """ + Execution command for tests, separate from those used by the + code directly to prevent accidental behavior issues + """ + if env is None: + env = dict() + for envname in os.environ: + env[envname] = os.environ[envname] + + #override the python path if needed + if pypath is not None: + env["PYTHONPATH"] = pypath + + #overide the execution path if needed + if path is not None: + env["PATH"] = path + if not env.get("PATH", ""): + env["PATH"] = _which_dirs("tar").union(_which_dirs("gzip")) + env["PATH"] = os.pathsep.join(env["PATH"]) + + cmd = [sys.executable, "setup.py"] + list(cmd) + + #regarding the shell argument, see: http://bugs.python.org/issue8557 + try: + proc = _Popen(cmd, stdout=_PIPE, stderr=_PIPE, + shell=(sys.platform == 'win32'), env=env) + + data = proc.communicate()[data_stream] + except OSError: + return 1, '' + + #decode the console string if needed + if hasattr(data, "decode"): + data = data.decode() # should use the preffered encoding + data = unicodedata.normalize('NFC', data) + + #communciate calls wait() + return proc.returncode, data diff --git a/setuptools/tests/script-with-bom.py b/setuptools/tests/script-with-bom.py new file mode 100644 index 00000000..22dee0d2 --- /dev/null +++ b/setuptools/tests/script-with-bom.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +result = 'passed' diff --git a/setuptools/tests/svn_data/dummy.zip b/setuptools/tests/svn_data/dummy.zip Binary files differnew file mode 100644 index 00000000..1347be53 --- /dev/null +++ b/setuptools/tests/svn_data/dummy.zip diff --git a/setuptools/tests/svn_data/dummy13.zip b/setuptools/tests/svn_data/dummy13.zip Binary files differnew file mode 100644 index 00000000..47764342 --- /dev/null +++ b/setuptools/tests/svn_data/dummy13.zip diff --git a/setuptools/tests/svn_data/dummy14.zip b/setuptools/tests/svn_data/dummy14.zip Binary files differnew file mode 100644 index 00000000..02ed8cf0 --- /dev/null +++ b/setuptools/tests/svn_data/dummy14.zip diff --git a/setuptools/tests/svn_data/dummy15.zip b/setuptools/tests/svn_data/dummy15.zip Binary files differnew file mode 100644 index 00000000..ed8daeeb --- /dev/null +++ b/setuptools/tests/svn_data/dummy15.zip diff --git a/setuptools/tests/svn_data/dummy16.zip b/setuptools/tests/svn_data/dummy16.zip Binary files differnew file mode 100644 index 00000000..b6e98d6c --- /dev/null +++ b/setuptools/tests/svn_data/dummy16.zip diff --git a/setuptools/tests/svn_data/dummy17.zip b/setuptools/tests/svn_data/dummy17.zip Binary files differnew file mode 100644 index 00000000..d96e1513 --- /dev/null +++ b/setuptools/tests/svn_data/dummy17.zip diff --git a/setuptools/tests/svn_data/dummy18.zip b/setuptools/tests/svn_data/dummy18.zip Binary files differnew file mode 100644 index 00000000..a7267838 --- /dev/null +++ b/setuptools/tests/svn_data/dummy18.zip diff --git a/setuptools/tests/svn_data/svn13_example.zip b/setuptools/tests/svn_data/svn13_example.zip Binary files differnew file mode 100644 index 00000000..d85fb84f --- /dev/null +++ b/setuptools/tests/svn_data/svn13_example.zip diff --git a/setuptools/tests/svn_data/svn13_ext_list.txt b/setuptools/tests/svn_data/svn13_ext_list.txt new file mode 100644 index 00000000..0bb0f438 --- /dev/null +++ b/setuptools/tests/svn_data/svn13_ext_list.txt @@ -0,0 +1,3 @@ +third_party3 file:///C:/development/svn_example/repos/svn13/extra1
+third_party2 -r3 file:///C:/development/svn_example/repos/svn13/extra1
+third_party -r1 file:///C:/development/svn_example/repos/svn13/extra1
diff --git a/setuptools/tests/svn_data/svn13_ext_list.xml b/setuptools/tests/svn_data/svn13_ext_list.xml new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/setuptools/tests/svn_data/svn13_ext_list.xml diff --git a/setuptools/tests/svn_data/svn13_info.xml b/setuptools/tests/svn_data/svn13_info.xml new file mode 100644 index 00000000..5c96520a --- /dev/null +++ b/setuptools/tests/svn_data/svn13_info.xml @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="utf-8"?>
+<info>
+<entry
+ kind="dir"
+ path="svn13_example"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn13/main</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn13/main</root>
+<uuid>d2996769-47b0-9946-b618-da1aa3eceda3</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<prop-updated>2013-07-13T15:33:23.187500Z</prop-updated>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:33:28.359375Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn13_example\a file"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn13/main/a%20file</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn13/main</root>
+<uuid>d2996769-47b0-9946-b618-da1aa3eceda3</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<text-updated>2013-07-13T15:33:21.109375Z</text-updated>
+<checksum>a6166e5e98a5a503089cde9bc8031293</checksum>
+</wc-info>
+<commit
+ revision="3">
+<author>ptt</author>
+<date>2013-07-13T15:33:21.312500Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn13_example\to_delete"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn13/main/to_delete</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn13/main</root>
+<uuid>d2996769-47b0-9946-b618-da1aa3eceda3</uuid>
+</repository>
+<wc-info>
+<schedule>delete</schedule>
+<text-updated>2013-07-13T15:33:28.140625Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:33:28.359375Z</date>
+</commit>
+</entry>
+<entry
+ kind="dir"
+ path="svn13_example\folder"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn13/main/folder</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn13/main</root>
+<uuid>d2996769-47b0-9946-b618-da1aa3eceda3</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<prop-updated>2013-07-13T15:33:26.187500Z</prop-updated>
+</wc-info>
+<commit
+ revision="5">
+<author>ptt</author>
+<date>2013-07-13T15:33:26.312500Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn13_example\folder\quest.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn13/main/folder/quest.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn13/main</root>
+<uuid>d2996769-47b0-9946-b618-da1aa3eceda3</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<text-updated>2013-07-13T15:33:20.109375Z</text-updated>
+<checksum>795240c6a830c14f83961e57e07dad12</checksum>
+</wc-info>
+<commit
+ revision="2">
+<author>ptt</author>
+<date>2013-07-13T15:33:20.312500Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn13_example\folder\lalala.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn13/main/folder/lalala.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn13/main</root>
+<uuid>d2996769-47b0-9946-b618-da1aa3eceda3</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<text-updated>2013-07-13T15:33:19.375000Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="1">
+<author>ptt</author>
+<date>2013-07-13T15:33:19.609375Z</date>
+</commit>
+</entry>
+</info>
diff --git a/setuptools/tests/svn_data/svn14_example.zip b/setuptools/tests/svn_data/svn14_example.zip Binary files differnew file mode 100644 index 00000000..57093c0b --- /dev/null +++ b/setuptools/tests/svn_data/svn14_example.zip diff --git a/setuptools/tests/svn_data/svn14_ext_list.txt b/setuptools/tests/svn_data/svn14_ext_list.txt new file mode 100644 index 00000000..800a0965 --- /dev/null +++ b/setuptools/tests/svn_data/svn14_ext_list.txt @@ -0,0 +1,4 @@ +third_party3 file:///C:/development/svn_example/repos/svn13/extra1
+third_party2 -r3 file:///C:/development/svn_example/repos/svn13/extra1
+third_party -r1 file:///C:/development/svn_example/repos/svn13/extra1
+
diff --git a/setuptools/tests/svn_data/svn14_ext_list.xml b/setuptools/tests/svn_data/svn14_ext_list.xml new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/setuptools/tests/svn_data/svn14_ext_list.xml diff --git a/setuptools/tests/svn_data/svn14_info.xml b/setuptools/tests/svn_data/svn14_info.xml new file mode 100644 index 00000000..a896a77f --- /dev/null +++ b/setuptools/tests/svn_data/svn14_info.xml @@ -0,0 +1,119 @@ +<?xml version="1.0"?>
+<info>
+<entry
+ kind="dir"
+ path="svn14_example"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn14/main</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn14/main</root>
+<uuid>c75942e5-8b7a-354d-b1cf-73dee23fa94f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:34:14.406250Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn14_example\a file"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn14/main/a%20file</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn14/main</root>
+<uuid>c75942e5-8b7a-354d-b1cf-73dee23fa94f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<text-updated>2013-07-13T15:34:08.109375Z</text-updated>
+<checksum>a6166e5e98a5a503089cde9bc8031293</checksum>
+</wc-info>
+<commit
+ revision="3">
+<author>ptt</author>
+<date>2013-07-13T15:34:08.390625Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn14_example\to_delete"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn14/main/to_delete</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn14/main</root>
+<uuid>c75942e5-8b7a-354d-b1cf-73dee23fa94f</uuid>
+</repository>
+<wc-info>
+<schedule>delete</schedule>
+<text-updated>2013-07-13T15:34:14.125000Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:34:14.406250Z</date>
+</commit>
+</entry>
+<entry
+ kind="dir"
+ path="svn14_example\folder"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn14/main/folder</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn14/main</root>
+<uuid>c75942e5-8b7a-354d-b1cf-73dee23fa94f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+</wc-info>
+<commit
+ revision="5">
+<author>ptt</author>
+<date>2013-07-13T15:34:12.390625Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn14_example\folder\quest.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn14/main/folder/quest.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn14/main</root>
+<uuid>c75942e5-8b7a-354d-b1cf-73dee23fa94f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<text-updated>2013-07-13T15:34:07.109375Z</text-updated>
+<checksum>795240c6a830c14f83961e57e07dad12</checksum>
+</wc-info>
+<commit
+ revision="2">
+<author>ptt</author>
+<date>2013-07-13T15:34:07.390625Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn14_example\folder\lalala.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn14/main/folder/lalala.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn14/main</root>
+<uuid>c75942e5-8b7a-354d-b1cf-73dee23fa94f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<text-updated>2013-07-13T15:34:06.250000Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="1">
+<author>ptt</author>
+<date>2013-07-13T15:34:06.531250Z</date>
+</commit>
+</entry>
+</info>
diff --git a/setuptools/tests/svn_data/svn15_example.zip b/setuptools/tests/svn_data/svn15_example.zip Binary files differnew file mode 100644 index 00000000..52a1d45b --- /dev/null +++ b/setuptools/tests/svn_data/svn15_example.zip diff --git a/setuptools/tests/svn_data/svn15_ext_list.txt b/setuptools/tests/svn_data/svn15_ext_list.txt new file mode 100644 index 00000000..75fde4e6 --- /dev/null +++ b/setuptools/tests/svn_data/svn15_ext_list.txt @@ -0,0 +1,4 @@ +third_party3 file:///C:/development/svn_example/repos/svn15/extra1
+-r3 file:///C:/development/svn_example/repos/svn15/extra1 third_party2
+file:///C:/development/svn_example/repos/svn15/extra1@r1 third_party
+
diff --git a/setuptools/tests/svn_data/svn15_ext_list.xml b/setuptools/tests/svn_data/svn15_ext_list.xml new file mode 100644 index 00000000..6950b3c5 --- /dev/null +++ b/setuptools/tests/svn_data/svn15_ext_list.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?>
+<properties>
+<target
+ path="C:/development/svn_example/svn15_example/folder">
+<property
+ name="svn:externals">third_party3 file:///C:/development/svn_example/repos/svn15/extra2
+-r3 file:///C:/development/svn_example/repos/svn15/extra2 third_party2
+file:///C:/development/svn_example/repos/svn15/extra2@r1 third_party大介
+</property>
+</target>
+<target
+ path="C:/development/svn_example/svn15_example">
+<property
+ name="svn:externals">third_party3 file:///C:/development/svn_example/repos/svn15/extra1
+-r3 file:///C:/development/svn_example/repos/svn15/extra1 third_party2
+file:///C:/development/svn_example/repos/svn15/extra1@r1 third_party大介
+</property>
+</target>
+</properties>
diff --git a/setuptools/tests/svn_data/svn15_info.xml b/setuptools/tests/svn_data/svn15_info.xml new file mode 100644 index 00000000..0b3550af --- /dev/null +++ b/setuptools/tests/svn_data/svn15_info.xml @@ -0,0 +1,125 @@ +<?xml version="1.0"?>
+<info>
+<entry
+ kind="dir"
+ path="svn15_example"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn15/main</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn15/main</root>
+<uuid>4eab6983-54fe-384b-a282-9306f52d948f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:34:49.562500Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn15_example\a file"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn15/main/a%20file</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn15/main</root>
+<uuid>4eab6983-54fe-384b-a282-9306f52d948f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:34:43.109375Z</text-updated>
+<checksum>a6166e5e98a5a503089cde9bc8031293</checksum>
+</wc-info>
+<commit
+ revision="3">
+<author>ptt</author>
+<date>2013-07-13T15:34:43.484375Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn15_example\to_delete"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn15/main/to_delete</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn15/main</root>
+<uuid>4eab6983-54fe-384b-a282-9306f52d948f</uuid>
+</repository>
+<wc-info>
+<schedule>delete</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:34:49.125000Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:34:49.562500Z</date>
+</commit>
+</entry>
+<entry
+ kind="dir"
+ path="svn15_example\folder"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn15/main/folder</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn15/main</root>
+<uuid>4eab6983-54fe-384b-a282-9306f52d948f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="5">
+<author>ptt</author>
+<date>2013-07-13T15:34:47.515625Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn15_example\folder\quest.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn15/main/folder/quest.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn15/main</root>
+<uuid>4eab6983-54fe-384b-a282-9306f52d948f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:34:42.109375Z</text-updated>
+<checksum>795240c6a830c14f83961e57e07dad12</checksum>
+</wc-info>
+<commit
+ revision="2">
+<author>ptt</author>
+<date>2013-07-13T15:34:42.484375Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn15_example\folder\lalala.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn15/main/folder/lalala.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn15/main</root>
+<uuid>4eab6983-54fe-384b-a282-9306f52d948f</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:34:41.375000Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="1">
+<author>ptt</author>
+<date>2013-07-13T15:34:41.734375Z</date>
+</commit>
+</entry>
+</info>
diff --git a/setuptools/tests/svn_data/svn16_example.zip b/setuptools/tests/svn_data/svn16_example.zip Binary files differnew file mode 100644 index 00000000..e886b2af --- /dev/null +++ b/setuptools/tests/svn_data/svn16_example.zip diff --git a/setuptools/tests/svn_data/svn16_ext_list.txt b/setuptools/tests/svn_data/svn16_ext_list.txt new file mode 100644 index 00000000..3ca54893 --- /dev/null +++ b/setuptools/tests/svn_data/svn16_ext_list.txt @@ -0,0 +1,4 @@ +"third party3" file:///C:/development/svn_example/repos/svn16/extra1
+'third party3b' file:///C:/development/svn_example/repos/svn16/extra1
+-r3 file:///C:/development/svn_example/repos/svn16/extra1 third\ party2
+file:///C:/development/svn_example/repos/svn16/extra1@r1 third_party
diff --git a/setuptools/tests/svn_data/svn16_ext_list.xml b/setuptools/tests/svn_data/svn16_ext_list.xml new file mode 100644 index 00000000..8ddaed0a --- /dev/null +++ b/setuptools/tests/svn_data/svn16_ext_list.xml @@ -0,0 +1,19 @@ +<?xml version="1.0"?>
+<properties>
+<target
+ path="C:/development/svn_example/svn16_example/folder">
+<property
+ name="svn:externals">"third party3" file:///C:/development/svn_example/repos/svn16/extra2
+-r3 file:///C:/development/svn_example/repos/svn16/extra2 third\ party2
+file:///C:/development/svn_example/repos/svn16/extra2@r1 third_party大介
+</property>
+</target>
+<target
+ path="C:/development/svn_example/svn16_example">
+<property
+ name="svn:externals">"third party3" file:///C:/development/svn_example/repos/svn16/extra1
+-r3 file:///C:/development/svn_example/repos/svn16/extra1 third\ party2
+file:///C:/development/svn_example/repos/svn16/extra1@r1 third_party大介
+</property>
+</target>
+</properties>
diff --git a/setuptools/tests/svn_data/svn16_info.xml b/setuptools/tests/svn_data/svn16_info.xml new file mode 100644 index 00000000..745469c9 --- /dev/null +++ b/setuptools/tests/svn_data/svn16_info.xml @@ -0,0 +1,125 @@ +<?xml version="1.0"?>
+<info>
+<entry
+ kind="dir"
+ path="svn16_example"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn16/main</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn16/main</root>
+<uuid>bd8d2cfc-1a74-de45-b166-262010c17c0a</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:35:17.390625Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn16_example\a file"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn16/main/a%20file</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn16/main</root>
+<uuid>bd8d2cfc-1a74-de45-b166-262010c17c0a</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:14.578125Z</text-updated>
+<checksum>a6166e5e98a5a503089cde9bc8031293</checksum>
+</wc-info>
+<commit
+ revision="3">
+<author>ptt</author>
+<date>2013-07-13T15:35:14.906250Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn16_example\to_delete"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn16/main/to_delete</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn16/main</root>
+<uuid>bd8d2cfc-1a74-de45-b166-262010c17c0a</uuid>
+</repository>
+<wc-info>
+<schedule>delete</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:17.046875Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:35:17.390625Z</date>
+</commit>
+</entry>
+<entry
+ kind="dir"
+ path="svn16_example\folder"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn16/main/folder</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn16/main</root>
+<uuid>bd8d2cfc-1a74-de45-b166-262010c17c0a</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="5">
+<author>ptt</author>
+<date>2013-07-13T15:35:16.406250Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn16_example\folder\quest.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn16/main/folder/quest.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn16/main</root>
+<uuid>bd8d2cfc-1a74-de45-b166-262010c17c0a</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:14.078125Z</text-updated>
+<checksum>795240c6a830c14f83961e57e07dad12</checksum>
+</wc-info>
+<commit
+ revision="2">
+<author>ptt</author>
+<date>2013-07-13T15:35:14.421875Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn16_example\folder\lalala.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn16/main/folder/lalala.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn16/main</root>
+<uuid>bd8d2cfc-1a74-de45-b166-262010c17c0a</uuid>
+</repository>
+<wc-info>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:12.171875Z</text-updated>
+<checksum>d41d8cd98f00b204e9800998ecf8427e</checksum>
+</wc-info>
+<commit
+ revision="1">
+<author>ptt</author>
+<date>2013-07-13T15:35:13.906250Z</date>
+</commit>
+</entry>
+</info>
diff --git a/setuptools/tests/svn_data/svn17_example.zip b/setuptools/tests/svn_data/svn17_example.zip Binary files differnew file mode 100644 index 00000000..ba0e8823 --- /dev/null +++ b/setuptools/tests/svn_data/svn17_example.zip diff --git a/setuptools/tests/svn_data/svn17_ext_list.txt b/setuptools/tests/svn_data/svn17_ext_list.txt new file mode 100644 index 00000000..a8b832a8 --- /dev/null +++ b/setuptools/tests/svn_data/svn17_ext_list.txt @@ -0,0 +1,4 @@ +"third party3" file:///C:/development/svn_example/repos/svn17/extra1
+'third party3b' file:///C:/development/svn_example/repos/svn17/extra1
+-r3 file:///C:/development/svn_example/repos/svn17/extra1 third\ party2
+file:///C:/development/svn_example/repos/svn17/extra1@r1 third_party
diff --git a/setuptools/tests/svn_data/svn17_ext_list.xml b/setuptools/tests/svn_data/svn17_ext_list.xml new file mode 100644 index 00000000..2879bb65 --- /dev/null +++ b/setuptools/tests/svn_data/svn17_ext_list.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<properties>
+<target
+ path="C:/development/svn_example/svn17_example">
+<property
+ name="svn:externals">"third party3" file:///C:/development/svn_example/repos/svn16/extra1
+-r3 file:///C:/development/svn_example/repos/svn16/extra1 third\ party2
+file:///C:/development/svn_example/repos/svn16/extra1@r1 third_party大介
+</property>
+</target>
+<target
+ path="C:/development/svn_example/svn17_example/folder">
+<property
+ name="svn:externals">"third party3" file:///C:/development/svn_example/repos/svn17/extra2
+-r3 file:///C:/development/svn_example/repos/svn17/extra2 third\ party2
+file:///C:/development/svn_example/repos/svn17/extra2@r1 third_party大介
+</property>
+</target>
+</properties>
diff --git a/setuptools/tests/svn_data/svn17_info.xml b/setuptools/tests/svn_data/svn17_info.xml new file mode 100644 index 00000000..6cffeffd --- /dev/null +++ b/setuptools/tests/svn_data/svn17_info.xml @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<info>
+<entry
+ kind="dir"
+ path="svn17_example"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn17/main</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn17/main</root>
+<uuid>5ba45434-5197-164e-afab-81923f4744f5</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn17_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:35:36.171875Z</date>
+</commit>
+</entry>
+<entry
+ path="svn17_example\folder"
+ revision="6"
+ kind="dir">
+<url>file:///C:/development/svn_example/repos/svn17/main/folder</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn17/main</root>
+<uuid>5ba45434-5197-164e-afab-81923f4744f5</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn17_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="5">
+<author>ptt</author>
+<date>2013-07-13T15:35:34.859375Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn17_example\folder\quest.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn17/main/folder/quest.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn17/main</root>
+<uuid>5ba45434-5197-164e-afab-81923f4744f5</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn17_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:32.812500Z</text-updated>
+<checksum>bc80eba9e7a10c0a571a4678c520bc9683f3bac2</checksum>
+</wc-info>
+<commit
+ revision="2">
+<author>ptt</author>
+<date>2013-07-13T15:35:33.109375Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn17_example\folder\lalala.txt"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn17/main/folder/lalala.txt</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn17/main</root>
+<uuid>5ba45434-5197-164e-afab-81923f4744f5</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn17_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:32.343750Z</text-updated>
+<checksum>da39a3ee5e6b4b0d3255bfef95601890afd80709</checksum>
+</wc-info>
+<commit
+ revision="1">
+<author>ptt</author>
+<date>2013-07-13T15:35:32.687500Z</date>
+</commit>
+</entry>
+<entry
+ path="svn17_example\a file"
+ revision="6"
+ kind="file">
+<url>file:///C:/development/svn_example/repos/svn17/main/a%20file</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn17/main</root>
+<uuid>5ba45434-5197-164e-afab-81923f4744f5</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn17_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:33.187500Z</text-updated>
+<checksum>43785ab4b1816b49f242990883292813cd4f486c</checksum>
+</wc-info>
+<commit
+ revision="3">
+<author>ptt</author>
+<date>2013-07-13T15:35:33.515625Z</date>
+</commit>
+</entry>
+<entry
+ path="svn17_example\to_delete"
+ revision="6"
+ kind="file">
+<url>file:///C:/development/svn_example/repos/svn17/main/to_delete</url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn17/main</root>
+<uuid>5ba45434-5197-164e-afab-81923f4744f5</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn17_example</wcroot-abspath>
+<schedule>delete</schedule>
+<depth>infinity</depth>
+<checksum>da39a3ee5e6b4b0d3255bfef95601890afd80709</checksum>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:35:36.171875Z</date>
+</commit>
+</entry>
+</info>
diff --git a/setuptools/tests/svn_data/svn18_example.zip b/setuptools/tests/svn_data/svn18_example.zip Binary files differnew file mode 100644 index 00000000..4362f8e9 --- /dev/null +++ b/setuptools/tests/svn_data/svn18_example.zip diff --git a/setuptools/tests/svn_data/svn18_ext_list.txt b/setuptools/tests/svn_data/svn18_ext_list.txt new file mode 100644 index 00000000..c90a5f11 --- /dev/null +++ b/setuptools/tests/svn_data/svn18_ext_list.txt @@ -0,0 +1,4 @@ +"third party3" file:///C:/development/svn_example/repos/svn18/extra1
+'third party3b' file:///C:/development/svn_example/repos/svn18/extra1
+-r3 file:///C:/development/svn_example/repos/svn18/extra1 third\ party2
+file:///C:/development/svn_example/repos/svn18/extra1@r1 third_party
diff --git a/setuptools/tests/svn_data/svn18_ext_list.xml b/setuptools/tests/svn_data/svn18_ext_list.xml new file mode 100644 index 00000000..9b5e9e96 --- /dev/null +++ b/setuptools/tests/svn_data/svn18_ext_list.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<properties>
+<target
+ path="C:/development/svn_example/svn18_example">
+<property
+ name="svn:externals">"third party3" file:///C:/development/svn_example/repos/svn16/extra1
+-r3 file:///C:/development/svn_example/repos/svn16/extra1 third\ party2
+file:///C:/development/svn_example/repos/svn16/extra1@r1 third_party大介
+</property>
+</target>
+<target
+ path="C:/development/svn_example/svn18_example/folder">
+<property
+ name="svn:externals">"third party3" file:///C:/development/svn_example/repos/svn18/extra2
+-r3 file:///C:/development/svn_example/repos/svn18/extra2 third\ party2
+file:///C:/development/svn_example/repos/svn18/extra2@r1 third_party大介
+</property>
+</target>
+</properties>
diff --git a/setuptools/tests/svn_data/svn18_info.xml b/setuptools/tests/svn_data/svn18_info.xml new file mode 100644 index 00000000..7ca55995 --- /dev/null +++ b/setuptools/tests/svn_data/svn18_info.xml @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<info>
+<entry
+ path="svn18_example"
+ revision="6"
+ kind="dir">
+<url>file:///C:/development/svn_example/repos/svn18/main</url>
+<relative-url>^/</relative-url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn18/main</root>
+<uuid>3c5e3929-c92b-7045-9ba9-5e65d3dd1ee9</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn18_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:35:57.796875Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn18_example\a file"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn18/main/a%20file</url>
+<relative-url>^/a%20file</relative-url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn18/main</root>
+<uuid>3c5e3929-c92b-7045-9ba9-5e65d3dd1ee9</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn18_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:54.906250Z</text-updated>
+<checksum>43785ab4b1816b49f242990883292813cd4f486c</checksum>
+</wc-info>
+<commit
+ revision="3">
+<author>ptt</author>
+<date>2013-07-13T15:35:55.265625Z</date>
+</commit>
+</entry>
+<entry
+ kind="file"
+ path="svn18_example\to_delete"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn18/main/to_delete</url>
+<relative-url>^/to_delete</relative-url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn18/main</root>
+<uuid>3c5e3929-c92b-7045-9ba9-5e65d3dd1ee9</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn18_example</wcroot-abspath>
+<schedule>delete</schedule>
+<depth>infinity</depth>
+<checksum>da39a3ee5e6b4b0d3255bfef95601890afd80709</checksum>
+</wc-info>
+<commit
+ revision="6">
+<author>ptt</author>
+<date>2013-07-13T15:35:57.796875Z</date>
+</commit>
+</entry>
+<entry
+ kind="dir"
+ path="svn18_example\folder"
+ revision="6">
+<url>file:///C:/development/svn_example/repos/svn18/main/folder</url>
+<relative-url>^/folder</relative-url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn18/main</root>
+<uuid>3c5e3929-c92b-7045-9ba9-5e65d3dd1ee9</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn18_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+</wc-info>
+<commit
+ revision="5">
+<author>ptt</author>
+<date>2013-07-13T15:35:56.750000Z</date>
+</commit>
+</entry>
+<entry
+ path="svn18_example\folder\quest.txt"
+ revision="6"
+ kind="file">
+<url>file:///C:/development/svn_example/repos/svn18/main/folder/quest.txt</url>
+<relative-url>^/folder/quest.txt</relative-url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn18/main</root>
+<uuid>3c5e3929-c92b-7045-9ba9-5e65d3dd1ee9</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn18_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:54.484375Z</text-updated>
+<checksum>bc80eba9e7a10c0a571a4678c520bc9683f3bac2</checksum>
+</wc-info>
+<commit
+ revision="2">
+<author>ptt</author>
+<date>2013-07-13T15:35:54.843750Z</date>
+</commit>
+</entry>
+<entry
+ path="svn18_example\folder\lalala.txt"
+ revision="6"
+ kind="file">
+<url>file:///C:/development/svn_example/repos/svn18/main/folder/lalala.txt</url>
+<relative-url>^/folder/lalala.txt</relative-url>
+<repository>
+<root>file:///C:/development/svn_example/repos/svn18/main</root>
+<uuid>3c5e3929-c92b-7045-9ba9-5e65d3dd1ee9</uuid>
+</repository>
+<wc-info>
+<wcroot-abspath>C:/development/svn_example/svn18_example</wcroot-abspath>
+<schedule>normal</schedule>
+<depth>infinity</depth>
+<text-updated>2013-07-13T15:35:54.015625Z</text-updated>
+<checksum>da39a3ee5e6b4b0d3255bfef95601890afd80709</checksum>
+</wc-info>
+<commit
+ revision="1">
+<author>ptt</author>
+<date>2013-07-13T15:35:54.375000Z</date>
+</commit>
+</entry>
+</info>
diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index f26a1f51..28682dfd 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -1,14 +1,20 @@ + import os +import sys import tempfile import shutil import unittest import pkg_resources +import warnings from setuptools.command import egg_info +from setuptools.tests import environment +from setuptools import svn_utils 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): @@ -31,10 +37,132 @@ class TestEggInfo(unittest.TestCase): def test_version_10_format(self): """ """ + #keeping this set for 1.6 is a good check on the get_svn_revision + #to ensure I return using svnversion what would had been returned + version_str = svn_utils.SvnInfo.get_svn_version() + version = [int(x) for x in version_str.split('.')[:2]] + if version != [1, 6]: + if hasattr(self, 'skipTest'): + self.skipTest('') + else: + sys.stderr.write('\n Skipping due to SVN Version\n') + return + self._write_entries(ENTRIES_V10) rev = egg_info.egg_info.get_svn_revision() self.assertEqual(rev, '89000') + def test_version_10_format_legacy_parser(self): + """ + """ + path_variable = None + for env in os.environ: + if env.lower() == 'path': + path_variable = env + + if path_variable is None: + self.skipTest('Cannot figure out how to modify path') + + old_path = os.environ[path_variable] + os.environ[path_variable] = '' + #catch_warnings not available until py26 + warning_filters = warnings.filters + warnings.filters = warning_filters[:] + try: + warnings.simplefilter("ignore", DeprecationWarning) + self._write_entries(ENTRIES_V10) + rev = egg_info.egg_info.get_svn_revision() + finally: + #restore the warning filters + warnings.filters = warning_filters + #restore the os path + os.environ[path_variable] = old_path + + self.assertEqual(rev, '89000') + +DUMMY_SOURCE_TXT = """CHANGES.txt +CONTRIBUTORS.txt +HISTORY.txt +LICENSE +MANIFEST.in +README.txt +setup.py +dummy/__init__.py +dummy/test.txt +dummy.egg-info/PKG-INFO +dummy.egg-info/SOURCES.txt +dummy.egg-info/dependency_links.txt +dummy.egg-info/top_level.txt""" + + +class TestSvnDummy(environment.ZippedEnvironment): + + def setUp(self): + version = svn_utils.SvnInfo.get_svn_version() + self.base_version = tuple([int(x) for x in version.split('.')][:2]) + + if not self.base_version: + raise ValueError('No SVN tools installed') + elif self.base_version < (1, 3): + raise ValueError('Insufficient SVN Version %s' % version) + elif self.base_version >= (1, 9): + #trying the latest version + self.base_version = (1, 8) + + self.dataname = "dummy%i%i" % self.base_version + self.datafile = os.path.join('setuptools', 'tests', + 'svn_data', self.dataname + ".zip") + super(TestSvnDummy, self).setUp() + + def test_sources(self): + code, data = environment.run_setup_py(["sdist"], + pypath=self.old_cwd, + data_stream=1) + if code: + raise AssertionError(data) + + sources = os.path.join('dummy.egg-info', 'SOURCES.txt') + infile = open(sources, 'r') + try: + read_contents = infile.read() + finally: + infile.close() + del infile + + self.assertEqual(DUMMY_SOURCE_TXT, read_contents) + + return data + + +class TestSvnDummyLegacy(environment.ZippedEnvironment): + + def setUp(self): + self.base_version = (1, 6) + self.dataname = "dummy%i%i" % self.base_version + self.datafile = os.path.join('setuptools', 'tests', + 'svn_data', self.dataname + ".zip") + super(TestSvnDummyLegacy, self).setUp() + + def test_sources(self): + code, data = environment.run_setup_py(["sdist"], + pypath=self.old_cwd, + path="", + data_stream=1) + if code: + raise AssertionError(data) + + sources = os.path.join('dummy.egg-info', 'SOURCES.txt') + infile = open(sources, 'r') + try: + read_contents = infile.read() + finally: + infile.close() + del infile + + self.assertEqual(DUMMY_SOURCE_TXT, read_contents) + + return data + def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/setuptools/tests/test_sandbox.py b/setuptools/tests/test_sandbox.py index 1609ee86..3dad1376 100644 --- a/setuptools/tests/test_sandbox.py +++ b/setuptools/tests/test_sandbox.py @@ -5,7 +5,10 @@ import os import shutil import unittest import tempfile +import types +import pkg_resources +import setuptools.sandbox from setuptools.sandbox import DirectorySandbox, SandboxViolation def has_win32com(): @@ -62,5 +65,15 @@ class TestSandbox(unittest.TestCase): finally: if os.path.exists(target): os.remove(target) + def test_setup_py_with_BOM(self): + """ + It should be possible to execute a setup.py with a Byte Order Mark + """ + target = pkg_resources.resource_filename(__name__, + 'script-with-bom.py') + namespace = types.ModuleType('namespace') + setuptools.sandbox.execfile(target, vars(namespace)) + assert namespace.result == 'passed' + if __name__ == '__main__': unittest.main() diff --git a/setuptools/tests/test_sdist.py b/setuptools/tests/test_sdist.py index 438f7ced..b42dcc57 100644 --- a/setuptools/tests/test_sdist.py +++ b/setuptools/tests/test_sdist.py @@ -1,19 +1,22 @@ # -*- coding: utf-8 -*- """sdist tests""" - +import locale import os import shutil import sys import tempfile import unittest import unicodedata +import re +from setuptools.tests import environment from setuptools.compat import StringIO, unicode -from setuptools.command.sdist import sdist +from setuptools.tests.py26compat import skipIf +from setuptools.command.sdist import sdist, walk_revctrl from setuptools.command.egg_info import manifest_maker from setuptools.dist import Distribution - +from setuptools import svn_utils SETUP_ATTRS = { 'name': 'sdist_test', @@ -318,6 +321,8 @@ class TestSdistTest(unittest.TestCase): filename = filename.decode('latin-1') self.assertFalse(filename in cmd.filelist.files) + @skipIf(sys.version_info >= (3,) and locale.getpreferredencoding() != 'UTF-8', + 'Unittest fails if locale is not utf-8 but the manifests is recorded correctly') def test_sdist_with_utf8_encoded_filename(self): # Test for #303. dist = Distribution(SETUP_ATTRS) @@ -395,6 +400,131 @@ class TestSdistTest(unittest.TestCase): self.assertTrue(filename in cmd.filelist.files) +class TestDummyOutput(environment.ZippedEnvironment): + + def setUp(self): + self.datafile = os.path.join('setuptools', 'tests', + 'svn_data', "dummy.zip") + self.dataname = "dummy" + super(TestDummyOutput, self).setUp() + + def _run(self): + code, data = environment.run_setup_py(["sdist"], + pypath=self.old_cwd, + data_stream=0) + if code: + info = "DIR: " + os.path.abspath('.') + info += "\n SDIST RETURNED: %i\n\n" % code + info += data + raise AssertionError(info) + + datalines = data.splitlines() + + possible = ( + "running sdist", + "running egg_info", + "creating dummy\.egg-info", + "writing dummy\.egg-info", + "writing top-level names to dummy\.egg-info", + "writing dependency_links to dummy\.egg-info", + "writing manifest file 'dummy\.egg-info", + "reading manifest file 'dummy\.egg-info", + "reading manifest template 'MANIFEST\.in'", + "writing manifest file 'dummy\.egg-info", + "creating dummy-0.1.1", + "making hard links in dummy-0\.1\.1", + "copying files to dummy-0\.1\.1", + "copying \S+ -> dummy-0\.1\.1", + "copying dummy", + "copying dummy\.egg-info", + "hard linking \S+ -> dummy-0\.1\.1", + "hard linking dummy", + "hard linking dummy\.egg-info", + "Writing dummy-0\.1\.1", + "creating dist", + "creating 'dist", + "Creating tar archive", + "running check", + "adding 'dummy-0\.1\.1", + "tar .+ dist/dummy-0\.1\.1\.tar dummy-0\.1\.1", + "gzip .+ dist/dummy-0\.1\.1\.tar", + "removing 'dummy-0\.1\.1' \\(and everything under it\\)", + ) + + print(" DIR: " + os.path.abspath('.')) + for line in datalines: + found = False + for pattern in possible: + if re.match(pattern, line): + print(" READ: " + line) + found = True + break + if not found: + raise AssertionError("Unexpexected: %s\n-in-\n%s" + % (line, data)) + + return data + + def test_sources(self): + self._run() + + +class TestSvn(environment.ZippedEnvironment): + + def setUp(self): + version = svn_utils.SvnInfo.get_svn_version() + self.base_version = tuple([int(x) for x in version.split('.')][:2]) + + if not self.base_version: + raise ValueError('No SVN tools installed') + elif self.base_version < (1, 3): + raise ValueError('Insufficient SVN Version %s' % version) + elif self.base_version >= (1, 9): + #trying the latest version + self.base_version = (1, 8) + + self.dataname = "svn%i%i_example" % self.base_version + self.datafile = os.path.join('setuptools', 'tests', + 'svn_data', self.dataname + ".zip") + super(TestSvn, self).setUp() + + def test_walksvn(self): + if self.base_version >= (1, 6): + folder2 = 'third party2' + folder3 = 'third party3' + else: + folder2 = 'third_party2' + folder3 = 'third_party3' + + #TODO is this right + expected = set([ + os.path.join('a file'), + os.path.join(folder2, 'Changes.txt'), + os.path.join(folder2, 'MD5SUMS'), + os.path.join(folder2, 'README.txt'), + os.path.join(folder3, 'Changes.txt'), + os.path.join(folder3, 'MD5SUMS'), + os.path.join(folder3, 'README.txt'), + os.path.join(folder3, 'TODO.txt'), + os.path.join(folder3, 'fin'), + os.path.join('third_party', 'README.txt'), + os.path.join('folder', folder2, 'Changes.txt'), + os.path.join('folder', folder2, 'MD5SUMS'), + os.path.join('folder', folder2, 'WatashiNiYomimasu.txt'), + os.path.join('folder', folder3, 'Changes.txt'), + os.path.join('folder', folder3, 'fin'), + os.path.join('folder', folder3, 'MD5SUMS'), + os.path.join('folder', folder3, 'oops'), + os.path.join('folder', folder3, 'WatashiNiYomimasu.txt'), + os.path.join('folder', folder3, 'ZuMachen.txt'), + os.path.join('folder', 'third_party', 'WatashiNiYomimasu.txt'), + os.path.join('folder', 'lalala.txt'), + os.path.join('folder', 'quest.txt'), + # The example will have a deleted file + # (or should) but shouldn't return it + ]) + self.assertEqual(set(x for x in walk_revctrl()), expected) + + def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) - diff --git a/setuptools/tests/test_svn.py b/setuptools/tests/test_svn.py new file mode 100644 index 00000000..5418b9a4 --- /dev/null +++ b/setuptools/tests/test_svn.py @@ -0,0 +1,223 @@ +# -*- coding: utf-8 -*- +"""svn tests""" + + +import os +import unittest +import codecs +from setuptools.tests import environment +from setuptools.compat import unicode, unichr + +from setuptools import svn_utils + + +class TestSvnVersion(unittest.TestCase): + + def test_no_svn_found(self): + path_variable = None + for env in os.environ: + if env.lower() == 'path': + path_variable = env + + if path_variable is None: + self.skipTest('Cannot figure out how to modify path') + + old_path = os.environ[path_variable] + os.environ[path_variable] = '' + try: + version = svn_utils.SvnInfo.get_svn_version() + self.assertEqual(version, '') + finally: + os.environ[path_variable] = old_path + + def test_svn_should_exist(self): + version = svn_utils.SvnInfo.get_svn_version() + self.assertNotEqual(version, '') + +def _read_utf8_file(path): + fileobj = None + try: + fileobj = codecs.open(path, 'r', 'utf-8') + data = fileobj.read() + return data + finally: + if fileobj: + fileobj.close() + + +class ParserInfoXML(unittest.TestCase): + + def parse_tester(self, svn_name, ext_spaces): + path = os.path.join('setuptools', 'tests', + 'svn_data', svn_name + '_info.xml') + #Remember these are pre-generated to test XML parsing + # so these paths might not valid on your system + example_base = "%s_example" % svn_name + + data = _read_utf8_file(path) + + expected = set([ + ("\\".join((example_base, 'a file')), 'file'), + ("\\".join((example_base, 'folder')), 'dir'), + ("\\".join((example_base, 'folder', 'lalala.txt')), 'file'), + ("\\".join((example_base, 'folder', 'quest.txt')), 'file'), + ]) + self.assertEqual(set(x for x in svn_utils.parse_dir_entries(data)), + expected) + + def test_svn13(self): + self.parse_tester('svn13', False) + + def test_svn14(self): + self.parse_tester('svn14', False) + + def test_svn15(self): + self.parse_tester('svn15', False) + + def test_svn16(self): + self.parse_tester('svn16', True) + + def test_svn17(self): + self.parse_tester('svn17', True) + + def test_svn18(self): + self.parse_tester('svn18', True) + +class ParserExternalXML(unittest.TestCase): + + def parse_tester(self, svn_name, ext_spaces): + path = os.path.join('setuptools', 'tests', + 'svn_data', svn_name + '_ext_list.xml') + example_base = svn_name + '_example' + data = _read_utf8_file(path) + + if ext_spaces: + folder2 = 'third party2' + folder3 = 'third party3' + else: + folder2 = 'third_party2' + folder3 = 'third_party3' + + expected = set([ + os.sep.join((example_base, folder2)), + os.sep.join((example_base, folder3)), + # folder is third_party大介 + os.sep.join((example_base, + unicode('third_party') + + unichr(0x5927) + unichr(0x4ecb))), + os.sep.join((example_base, 'folder', folder2)), + os.sep.join((example_base, 'folder', folder3)), + os.sep.join((example_base, 'folder', + unicode('third_party') + + unichr(0x5927) + unichr(0x4ecb))), + ]) + + expected = set(os.path.normpath(x) for x in expected) + dir_base = os.sep.join(('C:', 'development', 'svn_example')) + self.assertEqual(set(x for x + in svn_utils.parse_externals_xml(data, dir_base)), expected) + + def test_svn15(self): + self.parse_tester('svn15', False) + + def test_svn16(self): + self.parse_tester('svn16', True) + + def test_svn17(self): + self.parse_tester('svn17', True) + + def test_svn18(self): + self.parse_tester('svn18', True) + + +class ParseExternal(unittest.TestCase): + + def parse_tester(self, svn_name, ext_spaces): + path = os.path.join('setuptools', 'tests', + 'svn_data', svn_name + '_ext_list.txt') + data = _read_utf8_file(path) + + if ext_spaces: + expected = set(['third party2', 'third party3', + 'third party3b', 'third_party']) + else: + expected = set(['third_party2', 'third_party3', 'third_party']) + + self.assertEqual(set(x for x in svn_utils.parse_external_prop(data)), + expected) + + def test_svn13(self): + self.parse_tester('svn13', False) + + def test_svn14(self): + self.parse_tester('svn14', False) + + def test_svn15(self): + self.parse_tester('svn15', False) + + def test_svn16(self): + self.parse_tester('svn16', True) + + def test_svn17(self): + self.parse_tester('svn17', True) + + def test_svn18(self): + self.parse_tester('svn18', True) + + +class TestSvn(environment.ZippedEnvironment): + + def setUp(self): + version = svn_utils.SvnInfo.get_svn_version() + self.base_version = tuple([int(x) for x in version.split('.')[:2]]) + + if not self.base_version: + raise ValueError('No SVN tools installed') + elif self.base_version < (1,3): + raise ValueError('Insufficient SVN Version %s' % version) + elif self.base_version >= (1,9): + #trying the latest version + self.base_version = (1,8) + + self.dataname = "svn%i%i_example" % self.base_version + self.datafile = os.path.join('setuptools', 'tests', + 'svn_data', self.dataname + ".zip") + super(TestSvn, self).setUp() + + def test_revision(self): + rev = svn_utils.SvnInfo.load('.').get_revision() + self.assertEqual(rev, 6) + + def test_entries(self): + expected = set([ + (os.path.join('a file'), 'file'), + (os.path.join('folder'), 'dir'), + (os.path.join('folder', 'lalala.txt'), 'file'), + (os.path.join('folder', 'quest.txt'), 'file'), + #The example will have a deleted file (or should) + #but shouldn't return it + ]) + info = svn_utils.SvnInfo.load('.') + self.assertEqual(set(x for x in info.entries), expected) + + def test_externals(self): + if self.base_version >= (1,6): + folder2 = 'third party2' + folder3 = 'third party3' + else: + folder2 = 'third_party2' + folder3 = 'third_party3' + + expected = set([ + os.path.join(folder2), + os.path.join(folder3), + os.path.join('third_party'), + os.path.join('folder', folder2), + os.path.join('folder', folder3), + os.path.join('folder', 'third_party'), + ]) + info = svn_utils.SvnInfo.load('.') + self.assertEqual(set([x for x in info.externals]), expected) + +def test_suite(): + return unittest.defaultTestLoader.loadTestsFromName(__name__) diff --git a/setuptools/version.py b/setuptools/version.py index 7e49527e..07f744ca 100644 --- a/setuptools/version.py +++ b/setuptools/version.py @@ -1 +1 @@ -__version__ = '1.0' +__version__ = '1.3.3' |
