summaryrefslogtreecommitdiff
path: root/setuptools
diff options
context:
space:
mode:
Diffstat (limited to 'setuptools')
-rw-r--r--setuptools/__init__.py40
-rwxr-xr-xsetuptools/command/easy_install.py545
-rwxr-xr-xsetuptools/command/sdist.py39
-rwxr-xr-xsetuptools/command/upload.py13
-rw-r--r--setuptools/compat.py11
-rw-r--r--setuptools/dist.py168
-rwxr-xr-xsetuptools/package_index.py157
-rw-r--r--setuptools/py26compat.py19
-rw-r--r--setuptools/script template (dev).py8
-rw-r--r--setuptools/ssl_support.py6
-rw-r--r--setuptools/tests/script-with-bom.py3
-rw-r--r--setuptools/tests/test_easy_install.py4
-rw-r--r--setuptools/tests/test_packageindex.py15
-rw-r--r--setuptools/tests/test_resources.py133
-rw-r--r--setuptools/tests/test_sandbox.py13
-rw-r--r--setuptools/version.py1
16 files changed, 490 insertions, 685 deletions
diff --git a/setuptools/__init__.py b/setuptools/__init__.py
index 18dd363d..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
+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
-__version__ = '0.9.7'
__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 3194644e..6ce19fa4 100755
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -1,5 +1,6 @@
#!python
-"""\
+
+"""
Easy Install
------------
@@ -10,6 +11,7 @@ file, or visit the `EasyInstall home page`__.
__ https://pythonhosted.org/setuptools/easy_install.html
"""
+
import sys
import os
import zipimport
@@ -20,11 +22,16 @@ import re
import stat
import random
import platform
+import textwrap
+import warnings
+import site
+import struct
from glob import glob
+from distutils import log, dir_util
+
import pkg_resources
from setuptools import Command, _dont_write_bytecode
from setuptools.sandbox import run_setup
-from distutils import log, dir_util
try:
# Python 2.7 or >=3.2
from sysconfig import get_config_vars, get_path
@@ -51,12 +58,12 @@ from setuptools.package_index import URL_SCHEME
from setuptools.command import bdist_egg, egg_info
from setuptools.compat import (iteritems, maxsize, xrange, basestring, unicode,
reraise)
-from pkg_resources import yield_lines, normalize_path, resource_string, \
- ensure_directory, get_distribution, find_distributions, \
- Environment, Requirement, Distribution, \
- PathMetadata, EggMetadata, WorkingSet, \
- DistributionNotFound, VersionConflict, \
- DEVELOP_DIST
+from pkg_resources import (
+ yield_lines, normalize_path, resource_string, ensure_directory,
+ get_distribution, find_distributions, Environment, Requirement,
+ Distribution, PathMetadata, EggMetadata, WorkingSet, DistributionNotFound,
+ VersionConflict, DEVELOP_DIST,
+)
if '__VENV_LAUNCHER__' in os.environ:
sys_executable = os.environ['__VENV_LAUNCHER__']
@@ -68,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))
- )
+def samefile(p1, 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):
@@ -120,38 +124,35 @@ 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',
- "also compile with optimization: -O1 for \"python -O\", "
- "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
+ "also compile with optimization: -O1 for \"python -O\", "
+ "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
('record=', None,
- "filename in which to record list of installed files"),
+ "filename in which to record list of installed files"),
('always-unzip', 'Z', "don't install as a zipfile, no matter what"),
('site-dirs=','S',"list of directories where .pth files work"),
('editable', 'e', "Install specified packages in editable form"),
('no-deps', 'N', "don't install dependencies"),
('allow-hosts=', 'H', "pattern(s) that hostnames must match"),
- ('local-snapshots-ok', 'l', "allow building eggs from local checkouts"),
+ ('local-snapshots-ok', 'l',
+ "allow building eggs from local checkouts"),
('version', None, "print version information and exit"),
('no-find-links', None,
- "Don't load find-links defined in packages being installed")
+ "Don't load find-links defined in packages being installed")
]
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'
]
if HAS_USER_SITE:
- user_options.append(('user', None,
- "install in user site-package '%s'" % site.USER_SITE))
+ help_msg = "install in user site-package '%s'" % site.USER_SITE
+ user_options.append(('user', None, help_msg))
boolean_options.append('user')
-
negative_opt = {'always-unzip': 'zip-ok'}
create_index = PackageIndex
@@ -192,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
@@ -226,19 +225,20 @@ class easy_install(Command):
py_version = sys.version.split()[0]
prefix, exec_prefix = get_config_vars('prefix', 'exec_prefix')
- self.config_vars = {'dist_name': self.distribution.get_name(),
- 'dist_version': self.distribution.get_version(),
- 'dist_fullname': self.distribution.get_fullname(),
- 'py_version': py_version,
- 'py_version_short': py_version[0:3],
- 'py_version_nodot': py_version[0] + py_version[2],
- 'sys_prefix': prefix,
- 'prefix': prefix,
- 'sys_exec_prefix': exec_prefix,
- 'exec_prefix': exec_prefix,
- # Only python 3.2+ has abiflags
- 'abiflags': getattr(sys, 'abiflags', ''),
- }
+ self.config_vars = {
+ 'dist_name': self.distribution.get_name(),
+ 'dist_version': self.distribution.get_version(),
+ 'dist_fullname': self.distribution.get_fullname(),
+ 'py_version': py_version,
+ 'py_version_short': py_version[0:3],
+ 'py_version_nodot': py_version[0] + py_version[2],
+ 'sys_prefix': prefix,
+ 'prefix': prefix,
+ 'sys_exec_prefix': exec_prefix,
+ 'exec_prefix': exec_prefix,
+ # Only python 3.2+ has abiflags
+ 'abiflags': getattr(sys, 'abiflags', ''),
+ }
if HAS_USER_SITE:
self.config_vars['userbase'] = self.install_userbase
@@ -336,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"
@@ -351,7 +346,6 @@ class easy_install(Command):
self.outputs = []
-
def _expand_attrs(self, attrs):
for attr in attrs:
val = getattr(self, attr)
@@ -406,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"""
@@ -456,7 +445,7 @@ class easy_install(Command):
self.install_dir = instdir
def cant_write_to_target(self):
- msg = """can't create or remove files in install directory
+ template = """can't create or remove files in install directory
The following error occurred while trying to add or remove files in the
installation directory:
@@ -467,7 +456,8 @@ The installation directory you specified (via --install-dir, --prefix, or
the distutils default setting) was:
%s
-""" % (sys.exc_info()[1], self.install_dir,)
+"""
+ msg = template % (sys.exc_info()[1], self.install_dir,)
if not os.path.exists(self.install_dir):
msg += """
@@ -493,9 +483,6 @@ Please make the appropriate changes for your system and try again.
"""
raise DistutilsError(msg)
-
-
-
def check_pth_processing(self):
"""Empirically verify whether .pth files are supported in inst. dir"""
instdir = self.install_dir
@@ -514,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)
@@ -533,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
@@ -580,11 +571,6 @@ Please make the appropriate changes for your system and try again.
(spec.key, self.build_directory)
)
-
-
-
-
-
def easy_install(self, spec, deps=False):
tmpdir = tempfile.mkdtemp(prefix="easy_install-")
download = None
@@ -654,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:
@@ -662,8 +648,6 @@ Please make the appropriate changes for your system and try again.
if dist in spec:
return dist
-
-
def select_scheme(self, name):
"""Sets the install directories by applying the install schemes."""
# it's the caller's problem if they supply a bad name!
@@ -673,9 +657,6 @@ Please make the appropriate changes for your system and try again.
if getattr(self, attrname) is None:
setattr(self, attrname, scheme[key])
-
-
-
def process_distribution(self, requirement, dist, deps=True, *info):
self.update_pth(dist)
self.package_index.add(dist)
@@ -684,7 +665,7 @@ Please make the appropriate changes for your system and try again.
self.installed_projects[dist.key] = dist
log.info(self.installation_report(requirement, dist, *info))
if (dist.has_metadata('dependency_links.txt') and
- not self.no_find_links):
+ not self.no_find_links):
self.package_index.add_find_links(
dist.get_metadata_lines('dependency_links.txt')
)
@@ -735,10 +716,8 @@ Please make the appropriate changes for your system and try again.
def maybe_move(self, spec, dist_filename, setup_base):
dst = os.path.join(self.build_directory, spec.key)
if os.path.exists(dst):
- log.warn(
- "%r already exists in %s; build directory %s will not be kept",
- spec.key, self.build_directory, setup_base
- )
+ msg = "%r already exists in %s; build directory %s will not be kept"
+ log.warn(msg, spec.key, self.build_directory, setup_base)
return setup_base
if os.path.isdir(dist_filename):
setup_base = dist_filename
@@ -751,7 +730,8 @@ Please make the appropriate changes for your system and try again.
if os.path.isdir(dist_filename):
# if the only thing there is a directory, move it instead
setup_base = dist_filename
- ensure_directory(dst); shutil.move(setup_base, dst)
+ ensure_directory(dst)
+ shutil.move(setup_base, dst)
return dst
def install_wrapper_scripts(self, dist):
@@ -759,8 +739,6 @@ Please make the appropriate changes for your system and try again.
for args in get_script_args(dist):
self.write_script(*args)
-
-
def install_script(self, dist, script_name, script_text, dev_path=None):
"""Generate a legacy script wrapper and install it"""
spec = str(dist.as_requirement())
@@ -799,14 +777,13 @@ Please make the appropriate changes for your system and try again.
mask = current_umask()
if not self.dry_run:
ensure_directory(target)
+ if os.path.exists(target):
+ os.unlink(target)
f = open(target,"w"+mode)
f.write(contents)
f.close()
chmod(target, 0x1FF-mask) # 0777
-
-
-
def install_eggs(self, spec, dist_filename, tmpdir):
# .egg dirs or files are already built, so just return them
if dist_filename.lower().endswith('.egg'):
@@ -822,8 +799,7 @@ Please make the appropriate changes for your system and try again.
setup_base = os.path.abspath(dist_filename)
if (setup_base.startswith(tmpdir) # something we downloaded
- and self.build_directory and spec is not None
- ):
+ and self.build_directory and spec is not None):
setup_base = self.maybe_move(spec, dist_filename, setup_base)
# Find the setup.py file
@@ -862,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)
@@ -897,18 +872,19 @@ Please make the appropriate changes for your system and try again.
"%s is not a valid distutils Windows .exe" % dist_filename
)
# Create a dummy distribution object until we build the real distro
- dist = Distribution(None,
+ dist = Distribution(
+ None,
project_name=cfg.get('metadata','name'),
- version=cfg.get('metadata','version'), platform=get_platform()
+ version=cfg.get('metadata','version'), platform=get_platform(),
)
# 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_tmp = egg_path + '.tmp'
+ _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
@@ -919,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)]
)
@@ -965,7 +941,8 @@ Please make the appropriate changes for your system and try again.
resource = parts[-1]
parts[-1] = bdist_egg.strip_module(parts[-1])+'.py'
pyfile = os.path.join(egg_tmp, *parts)
- to_compile.append(pyfile); stubs.append(pyfile)
+ to_compile.append(pyfile)
+ stubs.append(pyfile)
bdist_egg.write_stub(resource, pyfile)
self.byte_compile(to_compile) # compile .py's
bdist_egg.write_safety_flag(os.path.join(egg_tmp,'EGG-INFO'),
@@ -979,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"
@@ -1164,8 +1065,7 @@ See the setuptools documentation for the "develop" command for more info.
cfg_filename = os.path.join(base, 'setup.cfg')
setopt.edit_config(cfg_filename, settings)
-
- def update_pth(self,dist):
+ def update_pth(self, dist):
if self.pth_file is None:
return
@@ -1207,9 +1107,10 @@ See the setuptools documentation for the "develop" command for more info.
return dst # only unpack-and-compile skips files for dry run
def unpack_and_compile(self, egg_path, destination):
- to_compile = []; to_chmod = []
+ to_compile = []
+ to_chmod = []
- def pf(src,dst):
+ def pf(src, dst):
if dst.endswith('.py') and not src.startswith('EGG-INFO/'):
to_compile.append(dst)
elif dst.endswith('.dll') or dst.endswith('.so'):
@@ -1243,16 +1144,8 @@ See the setuptools documentation for the "develop" command for more info.
finally:
log.set_verbosity(self.verbose) # restore original verbosity
-
-
-
-
-
-
-
-
def no_default_version_msg(self):
- return """bad install directory or PYTHONPATH
+ template = """bad install directory or PYTHONPATH
You are attempting to install a package to a directory that is not
on PYTHONPATH and which Python does not read ".pth" files from. The
@@ -1279,18 +1172,8 @@ 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.""" % (
- self.install_dir, os.environ.get('PYTHONPATH','')
- )
-
-
-
-
-
-
-
-
-
+Please make the appropriate changes for your system and try again."""
+ 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"""
@@ -1328,9 +1211,6 @@ Please make the appropriate changes for your system and try again.""" % (
self.sitepy_installed = True
-
-
-
def create_home_path(self):
"""Create directories under ~."""
if not self.user:
@@ -1341,22 +1221,16 @@ Please make the appropriate changes for your system and try again.""" % (
self.debug_print("os.makedirs('%s', 0700)" % path)
os.makedirs(path, 0x1C0) # 0700
-
-
-
-
-
-
INSTALL_SCHEMES = dict(
posix = dict(
install_dir = '$base/lib/python$py_version_short/site-packages',
- script_dir = '$base/bin',
+ script_dir = '$base/bin',
),
)
DEFAULT_SCHEME = dict(
install_dir = '$base/Lib/site-packages',
- script_dir = '$base/Scripts',
+ script_dir = '$base/Scripts',
)
def _expand(self, *attrs):
@@ -1380,14 +1254,6 @@ Please make the appropriate changes for your system and try again.""" % (
val = os.path.expanduser(val)
setattr(self, attr, val)
-
-
-
-
-
-
-
-
def get_site_dirs():
# return a list of 'site' dirs
sitedirs = [_f for _f in os.environ.get('PYTHONPATH',
@@ -1522,12 +1388,6 @@ def extract_wininst_cfg(dist_filename):
f.close()
-
-
-
-
-
-
def get_exe_prefixes(exe_filename):
"""Get exe->egg path translations for a given .exe file"""
@@ -1561,7 +1421,8 @@ def get_exe_prefixes(exe_filename):
finally:
z.close()
prefixes = [(x.lower(),y) for x, y in prefixes]
- prefixes.sort(); prefixes.reverse()
+ prefixes.sort()
+ prefixes.reverse()
return prefixes
@@ -1582,7 +1443,8 @@ class PthDistributions(Environment):
self.filename = filename
self.sitedirs = list(map(normalize_path, sitedirs))
self.basedir = normalize_path(os.path.dirname(self.filename))
- self._load(); Environment.__init__(self, [], None, None)
+ self._load()
+ Environment.__init__(self, [], None, None)
for path in yield_lines(self.paths):
list(map(self.add, find_distributions(path, True)))
@@ -1637,7 +1499,8 @@ class PthDistributions(Environment):
if os.path.islink(self.filename):
os.unlink(self.filename)
f = open(self.filename,'wt')
- f.write(data); f.close()
+ f.write(data)
+ f.close()
elif os.path.exists(self.filename):
log.debug("Deleting empty %s", self.filename)
@@ -1645,22 +1508,22 @@ class PthDistributions(Environment):
self.dirty = False
- def add(self,dist):
+ def add(self, dist):
"""Add `dist` to the distribution map"""
if (dist.location not in self.paths and (
dist.location not in self.sitedirs or
- dist.location == os.getcwd() #account for '.' being in PYTHONPATH
+ dist.location == os.getcwd() # account for '.' being in PYTHONPATH
)):
self.paths.append(dist.location)
self.dirty = True
- Environment.add(self,dist)
+ Environment.add(self, dist)
- def remove(self,dist):
+ def remove(self, dist):
"""Remove `dist` from the distribution map"""
while dist.location in self.paths:
- self.paths.remove(dist.location); self.dirty = True
- Environment.remove(self,dist)
-
+ self.paths.remove(dist.location)
+ self.dirty = True
+ Environment.remove(self, dist)
def make_relative(self,path):
npath, last = os.path.split(normalize_path(path))
@@ -1782,14 +1645,6 @@ def nt_quote_arg(arg):
return ''.join(result)
-
-
-
-
-
-
-
-
def is_python_script(script_text, filename):
"""Is this text, as a whole, a Python script? (as opposed to shell/bat/etc.
"""
@@ -1828,66 +1683,140 @@ def fix_jython_executable(executable, options):
# shebang line interpreter)
if options:
# Can't apply the workaround, leave it broken
- log.warn("WARNING: Unable to adapt shebang line for Jython,"
- " the following script is NOT executable\n"
- " see http://bugs.jython.org/issue1112 for"
- " more information.")
+ log.warn(
+ "WARNING: Unable to adapt shebang line for Jython,"
+ " the following script is NOT executable\n"
+ " see http://bugs.jython.org/issue1112 for"
+ " more information.")
else:
return '/usr/bin/env %s' % executable
return executable
-def get_script_args(dist, executable=sys_executable, wininst=False):
- """Yield write_script() argument tuples for a distribution's entrypoints"""
- spec = str(dist.as_requirement())
- header = get_script_header("", executable, wininst)
- for group in 'console_scripts', 'gui_scripts':
- for name, ep in dist.get_entry_map(group).items():
- script_text = (
- "# EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r\n"
- "__requires__ = %(spec)r\n"
- "import sys\n"
- "from pkg_resources import load_entry_point\n"
- "\n"
- "if __name__ == '__main__':"
- "\n"
- " sys.exit(\n"
- " load_entry_point(%(spec)r, %(group)r, %(name)r)()\n"
- " )\n"
- ) % locals()
- if sys.platform=='win32' or wininst:
- # On Windows/wininst, add a .py extension and an .exe launcher
- if group=='gui_scripts':
- launcher_type = 'gui'
- ext = '-script.pyw'
- old = ['.pyw']
- new_header = re.sub('(?i)python.exe','pythonw.exe',header)
- else:
- launcher_type = 'cli'
- ext = '-script.py'
- old = ['.py','.pyc','.pyo']
- new_header = re.sub('(?i)pythonw.exe','python.exe',header)
- if os.path.exists(new_header[2:-1].strip('"')) or sys.platform!='win32':
- hdr = new_header
- else:
- hdr = header
- yield (name+ext, hdr+script_text, 't', [name+x for x in old])
- yield (
- name+'.exe', get_win_launcher(launcher_type),
- 'b' # write in binary mode
- )
- if not is_64bit():
- # install a manifest for the launcher to prevent Windows
- # from detecting it as an installer (which it will for
- # launchers like easy_install.exe). Consider only
- # adding a manifest for launchers detected as installers.
- # See Distribute #143 for details.
- m_name = name + '.exe.manifest'
- yield (m_name, load_launcher_manifest(name), 't')
- else:
- # On other platforms, we assume the right thing to do is to
- # just write the stub with no extension.
- yield (name, header+script_text)
+class ScriptWriter(object):
+ """
+ Encapsulates behavior around writing entry point scripts for console and
+ gui apps.
+ """
+
+ template = textwrap.dedent("""
+ # EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r
+ __requires__ = %(spec)r
+ import sys
+ from pkg_resources import load_entry_point
+
+ if __name__ == '__main__':
+ sys.exit(
+ load_entry_point(%(spec)r, %(group)r, %(name)r)()
+ )
+ """).lstrip()
+
+ @classmethod
+ def get_script_args(cls, dist, executable=sys_executable, wininst=False):
+ """
+ Yield write_script() argument tuples for a distribution's entrypoints
+ """
+ gen_class = cls.get_writer(wininst)
+ spec = str(dist.as_requirement())
+ header = get_script_header("", executable, wininst)
+ for type_ in 'console', 'gui':
+ group = type_ + '_scripts'
+ for name, ep in dist.get_entry_map(group).items():
+ script_text = gen_class.template % locals()
+ for res in gen_class._get_script_args(type_, name, header,
+ script_text):
+ yield res
+
+ @classmethod
+ def get_writer(cls, force_windows):
+ if force_windows or sys.platform=='win32':
+ return WindowsScriptWriter.get_writer()
+ return cls
+
+ @classmethod
+ def _get_script_args(cls, type_, name, header, script_text):
+ # Simply write the stub with no extension.
+ yield (name, header+script_text)
+
+
+class WindowsScriptWriter(ScriptWriter):
+ @classmethod
+ def get_writer(cls):
+ """
+ Get a script writer suitable for Windows
+ """
+ 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='.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]
+ yield name+ext, header+script_text, 't', blockers
+
+ @staticmethod
+ def _adjust_header(type_, orig_header):
+ """
+ Make sure 'pythonw' is used for gui and and 'python' is used for
+ console (regardless of what sys.executable is).
+ """
+ pattern = 'pythonw.exe'
+ repl = 'python.exe'
+ if type_ == 'gui':
+ pattern, repl = repl, pattern
+ pattern_ob = re.compile(re.escape(pattern), re.IGNORECASE)
+ new_header = pattern_ob.sub(string=orig_header, repl=repl)
+ clean_header = new_header[2:-1].strip('"')
+ if sys.platform == 'win32' and not os.path.exists(clean_header):
+ # the adjusted version doesn't exist, so return the original
+ return orig_header
+ return new_header
+
+
+class WindowsExecutableLauncherWriter(WindowsScriptWriter):
+ @classmethod
+ def _get_script_args(cls, type_, name, header, script_text):
+ """
+ For Windows, add a .py extension and an .exe launcher
+ """
+ if type_=='gui':
+ launcher_type = 'gui'
+ ext = '-script.pyw'
+ old = ['.pyw']
+ else:
+ launcher_type = 'cli'
+ ext = '-script.py'
+ old = ['.py','.pyc','.pyo']
+ hdr = cls._adjust_header(type_, header)
+ blockers = [name+x for x in old]
+ yield (name+ext, hdr+script_text, 't', blockers)
+ yield (
+ name+'.exe', get_win_launcher(launcher_type),
+ 'b' # write in binary mode
+ )
+ if not is_64bit():
+ # install a manifest for the launcher to prevent Windows
+ # from detecting it as an installer (which it will for
+ # launchers like easy_install.exe). Consider only
+ # adding a manifest for launchers detected as installers.
+ # See Distribute #143 for details.
+ m_name = name + '.exe.manifest'
+ yield (m_name, load_launcher_manifest(name), 't')
+
+# for backward-compatibility
+get_script_args = ScriptWriter.get_script_args
def get_win_launcher(type):
"""
@@ -1955,8 +1884,11 @@ def current_umask():
def bootstrap():
# This function is called when setuptools*.egg is run using /bin/sh
- import setuptools; argv0 = os.path.dirname(setuptools.__path__[0])
- sys.argv[0] = argv0; sys.argv.append(argv0); main()
+ import setuptools
+ argv0 = os.path.dirname(setuptools.__path__[0])
+ sys.argv[0] = argv0
+ sys.argv.append(argv0)
+ main()
def main(argv=None, **kw):
from setuptools import setup
@@ -1968,9 +1900,10 @@ usage: %(script)s [options] requirement_or_url ...
or: %(script)s --help
"""
- def gen_usage (script_name):
- script = os.path.basename(script_name)
- return USAGE % vars()
+ def gen_usage(script_name):
+ return USAGE % dict(
+ script=os.path.basename(script_name),
+ )
def with_ei_usage(f):
old_gen_usage = distutils.core.gen_usage
@@ -1996,7 +1929,3 @@ usage: %(script)s [options] requirement_or_url ...
distclass=DistributionWithoutHelpCommands, **kw
)
)
-
-
-
-
diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py
index e1112ff9..6249e75c 100755
--- a/setuptools/command/sdist.py
+++ b/setuptools/command/sdist.py
@@ -1,9 +1,12 @@
+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')
@@ -37,7 +40,6 @@ class re_finder(object):
#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)
@@ -64,16 +66,6 @@ finders = [
-
-
-
-
-
-
-
-
-
-
class sdist(_sdist):
"""Smart sdist that finds anything supported by revision control"""
@@ -128,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):
@@ -197,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)
@@ -244,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 94fe23e9..bbc98d66 100644
--- a/setuptools/compat.py
+++ b/setuptools/compat.py
@@ -27,10 +27,10 @@ if sys.version_info[0] < 3:
unichr = unichr
unicode = unicode
bytes = str
- from urllib import url2pathname
+ from urllib import url2pathname, splittag
import urllib2
from urllib2 import urlopen, HTTPError, URLError, unquote, splituser
- from urlparse import urlparse, urlunparse, urljoin
+ from urlparse import urlparse, urlunparse, urljoin, urlsplit, urlunsplit
xrange = xrange
filterfalse = itertools.ifilterfalse
@@ -74,7 +74,10 @@ else:
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
+ from urllib.parse import (
+ urlparse, urlunparse, unquote, splituser, urljoin, urlsplit,
+ urlunsplit, splittag,
+ )
xrange = range
filterfalse = itertools.filterfalse
@@ -83,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 47f00c00..4c9e40a7 100755
--- a/setuptools/package_index.py
+++ b/setuptools/package_index.py
@@ -1,19 +1,28 @@
"""PyPI and direct package downloading"""
-import sys, os.path, re, shutil, random, socket
-import itertools
+import sys
+import os
+import re
+import shutil
+import socket
import base64
+
+from pkg_resources import (
+ CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST,
+ require, Environment, find_distributions, safe_name, safe_version,
+ to_filename, Requirement, DEVELOP_DIST,
+)
from setuptools import ssl_support
-from pkg_resources import *
from distutils import log
from distutils.errors import DistutilsError
from setuptools.compat import (urllib2, httplib, StringIO, HTTPError,
urlparse, urlunparse, unquote, splituser,
url2pathname, name2codepoint,
- unichr, urljoin)
+ unichr, urljoin, urlsplit, urlunsplit)
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_.]+)$')
@@ -105,9 +114,10 @@ def distros_for_filename(filename, metadata=None):
)
-def interpret_distro_name(location, basename, metadata,
- py_version=None, precedence=SOURCE_DIST, platform=None
-):
+def interpret_distro_name(
+ location, basename, metadata, py_version=None, precedence=SOURCE_DIST,
+ platform=None
+ ):
"""Generate alternative interpretations of a source distro name
Note: if `location` is a filesystem filename, you should call
@@ -222,6 +232,7 @@ class HashChecker(ContentChecker):
)
def __init__(self, hash_name, expected):
+ self.hash_name = hash_name
self.hash = hashlib.new(hash_name)
self.expected = expected
@@ -242,32 +253,18 @@ class HashChecker(ContentChecker):
def is_valid(self):
return self.hash.hexdigest() == self.expected
- def _get_hash_name(self):
- """
- Python 2.4 implementation of MD5 doesn't supply a .name attribute
- so provide that name.
-
- When Python 2.4 is no longer required, replace invocations of this
- method with simply 'self.hash.name'.
- """
- try:
- return self.hash.name
- except AttributeError:
- if 'md5' in str(type(self.hash)):
- return 'md5'
- raise
-
def report(self, reporter, template):
- msg = template % self._get_hash_name()
+ msg = template % self.hash_name
return reporter(msg)
class PackageIndex(Environment):
"""A distribution index that scans web pages for download URLs"""
- def __init__(self, index_url="https://pypi.python.org/simple", hosts=('*',),
- ca_bundle=None, verify_ssl=True, *args, **kw
- ):
+ def __init__(
+ self, index_url="https://pypi.python.org/simple", hosts=('*',),
+ ca_bundle=None, verify_ssl=True, *args, **kw
+ ):
Environment.__init__(self,*args,**kw)
self.index_url = index_url + "/"[:not index_url.endswith('/')]
self.scanned_urls = {}
@@ -347,7 +344,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:
@@ -388,7 +386,7 @@ class PackageIndex(Environment):
# process an index page into the package-page index
for match in HREF.finditer(page):
try:
- scan( urljoin(url, htmldecode(match.group(1))) )
+ scan(urljoin(url, htmldecode(match.group(1))))
except ValueError:
pass
@@ -411,8 +409,6 @@ class PackageIndex(Environment):
else:
return "" # no sense double-scanning non-package pages
-
-
def need_version_info(self, url):
self.scan_all(
"Page at %s links to .py file(s) without version info; an index "
@@ -443,17 +439,14 @@ class PackageIndex(Environment):
self.scan_url(url)
def obtain(self, requirement, installer=None):
- self.prescan(); self.find_packages(requirement)
+ self.prescan()
+ self.find_packages(requirement)
for dist in self[requirement.key]:
if dist in requirement:
return dist
self.debug("%s does not match %s", requirement, dist)
return super(PackageIndex, self).obtain(requirement,installer)
-
-
-
-
def check_hash(self, checker, filename, tfp):
"""
checker is a ContentChecker
@@ -539,11 +532,10 @@ class PackageIndex(Environment):
)
return getattr(self.fetch_distribution(spec, tmpdir),'location',None)
-
- def fetch_distribution(self,
- requirement, tmpdir, force_scan=False, source=False, develop_ok=False,
- local_index=None
- ):
+ def fetch_distribution(
+ self, requirement, tmpdir, force_scan=False, source=False,
+ develop_ok=False, local_index=None
+ ):
"""Obtain a distribution suitable for fulfilling `requirement`
`requirement` must be a ``pkg_resources.Requirement`` instance.
@@ -581,8 +573,6 @@ class PackageIndex(Environment):
if dist in req and (dist.precedence<=SOURCE_DIST or not source):
return dist
-
-
if force_scan:
self.prescan()
self.find_packages(requirement)
@@ -609,7 +599,6 @@ class PackageIndex(Environment):
self.info("Best match: %s", dist)
return dist.clone(location=self.download(dist.location, tmpdir))
-
def fetch(self, requirement, tmpdir, force_scan=False, source=False):
"""Obtain a file suitable for fulfilling `requirement`
@@ -623,10 +612,10 @@ class PackageIndex(Environment):
return dist.location
return None
-
def gen_setup(self, filename, fragment, tmpdir):
match = EGG_FRAGMENT.match(fragment)
- dists = match and [d for d in
+ dists = match and [
+ d for d in
interpret_distro_name(filename, match.group(1), None) if d.version
] or []
@@ -672,7 +661,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)
@@ -732,9 +721,11 @@ class PackageIndex(Environment):
if warning:
self.warn(warning, v.line)
else:
- raise DistutilsError('%s returned a bad status line. '
- 'The server might be down, %s' % \
- (url, v.line))
+ raise DistutilsError(
+ '%s returned a bad status line. The server might be '
+ 'down, %s' %
+ (url, v.line)
+ )
except httplib.HTTPException:
v = sys.exc_info()[1]
if warning:
@@ -775,7 +766,6 @@ class PackageIndex(Environment):
def scan_url(self, url):
self.process_url(url, True)
-
def _attempt_download(self, url, filename):
headers = self._download_to(url, filename)
if 'html' in headers.get('content-type','').lower():
@@ -798,21 +788,6 @@ class PackageIndex(Environment):
os.unlink(filename)
raise DistutilsError("Unexpected HTML page found at "+url)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
def _download_svn(self, url, filename):
url = url.split('#',1)[0] # remove any fragment for svn's sake
creds = ''
@@ -833,7 +808,8 @@ class PackageIndex(Environment):
os.system("svn checkout%s -q %s %s" % (creds, url, filename))
return filename
- def _vcs_split_rev_from_url(self, url, pop_prefix=False):
+ @staticmethod
+ def _vcs_split_rev_from_url(url, pop_prefix=False):
scheme, netloc, path, query, frag = urlsplit(url)
scheme = scheme.split('+', 1)[-1]
@@ -891,18 +867,6 @@ class PackageIndex(Environment):
def warn(self, msg, *args):
log.warn(msg, *args)
-
-
-
-
-
-
-
-
-
-
-
-
# This pattern matches a character entity reference (a decimal numeric
# references, a hexadecimal numeric reference, or a named reference).
entity_sub = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub
@@ -927,20 +891,6 @@ def htmldecode(text):
"""Decode HTML entities in the given text."""
return entity_sub(decode_entity, text)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
def socket_timeout(timeout=15):
def _socket_timeout(func):
def _socket_timeout(*args, **kwargs):
@@ -1009,15 +959,6 @@ def open_with_auth(url, opener=urllib2.urlopen):
open_with_auth = socket_timeout(_SOCKET_TIMEOUT)(open_with_auth)
-
-
-
-
-
-
-
-
-
def fix_sf_url(url):
return url # backward compatibility
@@ -1047,17 +988,3 @@ def local_open(url):
return HTTPError(url, status, message,
{'content-type':'text/html'}, StringIO(body))
-
-
-
-
-
-
-
-
-
-
-
-
-
-# this line is a kludge to keep the trailing blank lines for pje's editor
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..90359b2c 100644
--- a/setuptools/ssl_support.py
+++ b/setuptools/ssl_support.py
@@ -194,6 +194,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/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/test_easy_install.py b/setuptools/tests/test_easy_install.py
index 2732bb3e..189e3d55 100644
--- a/setuptools/tests/test_easy_install.py
+++ b/setuptools/tests/test_easy_install.py
@@ -14,7 +14,7 @@ import distutils.core
from setuptools.compat import StringIO, BytesIO, next, urlparse
from setuptools.sandbox import run_setup, SandboxViolation
-from setuptools.command.easy_install import easy_install, fix_jython_executable, get_script_args
+from setuptools.command.easy_install import easy_install, fix_jython_executable, get_script_args, nt_quote_arg
from setuptools.command.easy_install import PthDistributions
from setuptools.command import easy_install as easy_install_pkg
from setuptools.dist import Distribution
@@ -52,7 +52,7 @@ if __name__ == '__main__':
sys.exit(
load_entry_point('spec', 'console_scripts', 'name')()
)
-""" % fix_jython_executable(sys.executable, "")
+""" % nt_quote_arg(fix_jython_executable(sys.executable, ""))
SETUP_PY = """\
from setuptools import setup
diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py
index 3791914a..08969b7e 100644
--- a/setuptools/tests/test_packageindex.py
+++ b/setuptools/tests/test_packageindex.py
@@ -142,6 +142,15 @@ class TestPackageIndex(unittest.TestCase):
self.assertEqual(setuptools.package_index.parse_bdist_wininst(
'reportlab-2.5.win-amd64.exe'), ('reportlab-2.5', None, 'win-amd64'))
+ def test__vcs_split_rev_from_url(self):
+ """
+ Test the basic usage of _vcs_split_rev_from_url
+ """
+ vsrfu = setuptools.package_index.PackageIndex._vcs_split_rev_from_url
+ url, rev = vsrfu('https://example.com/bar@2995')
+ self.assertEqual(url, 'https://example.com/bar')
+ self.assertEqual(rev, '2995')
+
class TestContentCheckers(unittest.TestCase):
def test_md5(self):
@@ -169,11 +178,7 @@ class TestContentCheckers(unittest.TestCase):
def test_get_hash_name_md5(self):
checker = setuptools.package_index.HashChecker.from_url(
'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478')
- if sys.version_info >= (2,5):
- self.assertEqual(checker.hash.name, 'md5')
- else:
- # Python 2.4 compatability
- self.assertEqual(checker._get_hash_name(), 'md5')
+ self.assertEqual(checker.hash_name, 'md5')
def test_report(self):
checker = setuptools.package_index.HashChecker.from_url(
diff --git a/setuptools/tests/test_resources.py b/setuptools/tests/test_resources.py
index df5261d1..c9fcf76c 100644
--- a/setuptools/tests/test_resources.py
+++ b/setuptools/tests/test_resources.py
@@ -1,11 +1,24 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
-# NOTE: the shebang and encoding lines are for ScriptHeaderTests; do not remove
-from unittest import TestCase, makeSuite; from pkg_resources import *
-from setuptools.command.easy_install import get_script_header, is_sh
+# NOTE: the shebang and encoding lines are for ScriptHeaderTests do not remove
+
+import os
+import sys
+import tempfile
+import shutil
+from unittest import TestCase
+
+import pkg_resources
+from pkg_resources import (parse_requirements, VersionConflict, parse_version,
+ Distribution, EntryPoint, Requirement, safe_version, safe_name,
+ WorkingSet)
+
+from setuptools.command.easy_install import (get_script_header, is_sh,
+ nt_quote_arg)
from setuptools.compat import StringIO, iteritems
-import os, pkg_resources, sys, tempfile, shutil
-try: frozenset
+
+try:
+ frozenset
except NameError:
from sets import ImmutableSet as frozenset
@@ -15,11 +28,11 @@ def safe_repr(obj, short=False):
result = repr(obj)
except Exception:
result = object.__repr__(obj)
- if not short or len(result) < _MAX_LENGTH:
+ if not short or len(result) < pkg_resources._MAX_LENGTH:
return result
- return result[:_MAX_LENGTH] + ' [truncated]...'
+ return result[:pkg_resources._MAX_LENGTH] + ' [truncated]...'
-class Metadata(EmptyProvider):
+class Metadata(pkg_resources.EmptyProvider):
"""Mock object to return metadata as if from an on-disk distribution"""
def __init__(self,*pairs):
@@ -32,18 +45,20 @@ class Metadata(EmptyProvider):
return self.metadata[name]
def get_metadata_lines(self,name):
- return yield_lines(self.get_metadata(name))
+ return pkg_resources.yield_lines(self.get_metadata(name))
+
+dist_from_fn = pkg_resources.Distribution.from_filename
class DistroTests(TestCase):
def testCollection(self):
# empty path should produce no distributions
- ad = Environment([], platform=None, python=None)
+ ad = pkg_resources.Environment([], platform=None, python=None)
self.assertEqual(list(ad), [])
self.assertEqual(ad['FooPkg'],[])
- ad.add(Distribution.from_filename("FooPkg-1.3_1.egg"))
- ad.add(Distribution.from_filename("FooPkg-1.4-py2.4-win32.egg"))
- ad.add(Distribution.from_filename("FooPkg-1.2-py2.4.egg"))
+ ad.add(dist_from_fn("FooPkg-1.3_1.egg"))
+ ad.add(dist_from_fn("FooPkg-1.4-py2.4-win32.egg"))
+ ad.add(dist_from_fn("FooPkg-1.2-py2.4.egg"))
# Name is in there now
self.assertTrue(ad['FooPkg'])
@@ -60,27 +75,33 @@ class DistroTests(TestCase):
[dist.version for dist in ad['FooPkg']], ['1.4','1.2']
)
# And inserting adds them in order
- ad.add(Distribution.from_filename("FooPkg-1.9.egg"))
+ ad.add(dist_from_fn("FooPkg-1.9.egg"))
self.assertEqual(
[dist.version for dist in ad['FooPkg']], ['1.9','1.4','1.2']
)
ws = WorkingSet([])
- foo12 = Distribution.from_filename("FooPkg-1.2-py2.4.egg")
- foo14 = Distribution.from_filename("FooPkg-1.4-py2.4-win32.egg")
+ foo12 = dist_from_fn("FooPkg-1.2-py2.4.egg")
+ foo14 = dist_from_fn("FooPkg-1.4-py2.4-win32.egg")
req, = parse_requirements("FooPkg>=1.3")
# Nominal case: no distros on path, should yield all applicable
self.assertEqual(ad.best_match(req,ws).version, '1.9')
# If a matching distro is already installed, should return only that
- ws.add(foo14); self.assertEqual(ad.best_match(req,ws).version, '1.4')
+ ws.add(foo14)
+ self.assertEqual(ad.best_match(req,ws).version, '1.4')
# If the first matching distro is unsuitable, it's a version conflict
- ws = WorkingSet([]); ws.add(foo12); ws.add(foo14)
+ ws = WorkingSet([])
+ ws.add(foo12)
+ ws.add(foo14)
self.assertRaises(VersionConflict, ad.best_match, req, ws)
# If more than one match on the path, the first one takes precedence
- ws = WorkingSet([]); ws.add(foo14); ws.add(foo12); ws.add(foo14);
+ ws = WorkingSet([])
+ ws.add(foo14)
+ ws.add(foo12)
+ ws.add(foo14)
self.assertEqual(ad.best_match(req,ws).version, '1.4')
def checkFooPkg(self,d):
@@ -103,9 +124,9 @@ class DistroTests(TestCase):
self.assertEqual(d.platform, None)
def testDistroParse(self):
- d = Distribution.from_filename("FooPkg-1.3_1-py2.4-win32.egg")
+ d = dist_from_fn("FooPkg-1.3_1-py2.4-win32.egg")
self.checkFooPkg(d)
- d = Distribution.from_filename("FooPkg-1.3_1-py2.4-win32.egg-info")
+ d = dist_from_fn("FooPkg-1.3_1-py2.4-win32.egg-info")
self.checkFooPkg(d)
def testDistroMetadata(self):
@@ -117,7 +138,6 @@ class DistroTests(TestCase):
)
self.checkFooPkg(d)
-
def distRequires(self, txt):
return Distribution("/foo", metadata=Metadata(('depends.txt', txt)))
@@ -131,20 +151,21 @@ class DistroTests(TestCase):
for v in "Twisted>=1.5", "Twisted>=1.5\nZConfig>=2.0":
self.checkRequires(self.distRequires(v), v)
-
def testResolve(self):
- ad = Environment([]); ws = WorkingSet([])
+ ad = pkg_resources.Environment([])
+ ws = WorkingSet([])
# Resolving no requirements -> nothing to install
- self.assertEqual( list(ws.resolve([],ad)), [] )
+ self.assertEqual(list(ws.resolve([],ad)), [])
# Request something not in the collection -> DistributionNotFound
self.assertRaises(
- DistributionNotFound, ws.resolve, parse_requirements("Foo"), ad
+ pkg_resources.DistributionNotFound, ws.resolve, parse_requirements("Foo"), ad
)
Foo = Distribution.from_filename(
"/foo_dir/Foo-1.2.egg",
metadata=Metadata(('depends.txt', "[bar]\nBaz>=2.0"))
)
- ad.add(Foo); ad.add(Distribution.from_filename("Foo-0.9.egg"))
+ ad.add(Foo)
+ ad.add(Distribution.from_filename("Foo-0.9.egg"))
# Request thing(s) that are available -> list to activate
for i in range(3):
@@ -157,7 +178,7 @@ class DistroTests(TestCase):
# Request an extra that causes an unresolved dependency for "Baz"
self.assertRaises(
- DistributionNotFound, ws.resolve,parse_requirements("Foo[bar]"), ad
+ pkg_resources.DistributionNotFound, ws.resolve,parse_requirements("Foo[bar]"), ad
)
Baz = Distribution.from_filename(
"/foo_dir/Baz-2.1.egg", metadata=Metadata(('depends.txt', "Foo"))
@@ -169,9 +190,8 @@ class DistroTests(TestCase):
list(ws.resolve(parse_requirements("Foo[bar]"), ad)), [Foo,Baz]
)
# Requests for conflicting versions produce VersionConflict
- self.assertRaises( VersionConflict,
- ws.resolve, parse_requirements("Foo==1.2\nFoo!=1.2"), ad
- )
+ self.assertRaises(VersionConflict,
+ ws.resolve, parse_requirements("Foo==1.2\nFoo!=1.2"), ad)
def testDistroDependsOptions(self):
d = self.distRequires("""
@@ -196,7 +216,7 @@ class DistroTests(TestCase):
d,"Twisted>=1.5 fcgiapp>=0.1 ZConfig>=2.0 docutils>=0.3".split(),
["fastcgi", "docgen"]
)
- self.assertRaises(UnknownExtra, d.requires, ["foo"])
+ self.assertRaises(pkg_resources.UnknownExtra, d.requires, ["foo"])
class EntryPointTests(TestCase):
@@ -304,8 +324,8 @@ class RequirementsTests(TestCase):
def testBasicContains(self):
r = Requirement("Twisted", [('>=','1.2')], ())
foo_dist = Distribution.from_filename("FooPkg-1.3_1.egg")
- twist11 = Distribution.from_filename("Twisted-1.1.egg")
- twist12 = Distribution.from_filename("Twisted-1.2.egg")
+ twist11 = Distribution.from_filename("Twisted-1.1.egg")
+ twist12 = Distribution.from_filename("Twisted-1.2.egg")
self.assertTrue(parse_version('1.2') in r)
self.assertTrue(parse_version('1.1') not in r)
self.assertTrue('1.2' in r)
@@ -321,7 +341,6 @@ class RequirementsTests(TestCase):
for v in ('1.2c1','1.3.1','1.5','1.9.1','2.0','2.5','3.0','4.0'):
self.assertTrue(v not in r, (v,r))
-
def testOptionsAndHashing(self):
r1 = Requirement.parse("Twisted[foo,bar]>=1.2")
r2 = Requirement.parse("Twisted[bar,FOO]>=1.2")
@@ -366,15 +385,6 @@ class RequirementsTests(TestCase):
Requirement.parse('setuptools >= 0.7').project_name, 'setuptools')
-
-
-
-
-
-
-
-
-
class ParseTests(TestCase):
def testEmptyParse(self):
@@ -388,9 +398,7 @@ class ParseTests(TestCase):
self.assertEqual(list(pkg_resources.yield_lines(inp)),out)
def testSplitting(self):
- self.assertEqual(
- list(
- pkg_resources.split_sections("""
+ sample = """
x
[Y]
z
@@ -403,8 +411,7 @@ class ParseTests(TestCase):
[q]
v
"""
- )
- ),
+ self.assertEqual(list(pkg_resources.split_sections(sample)),
[(None,["x"]), ("Y",["z","a"]), ("b",["c"]), ("d",[]), ("q",["v"])]
)
self.assertRaises(ValueError,list,pkg_resources.split_sections("[foo"))
@@ -455,7 +462,8 @@ class ParseTests(TestCase):
c('0pre1', '0.0c1')
c('0.0.0preview1', '0c1')
c('0.0c1', '0-rc1')
- c('1.2a1', '1.2.a.1'); c('1.2...a', '1.2a')
+ c('1.2a1', '1.2.a.1')
+ c('1.2...a', '1.2a')
def testVersionOrdering(self):
def c(s1,s2):
@@ -492,30 +500,30 @@ class ParseTests(TestCase):
c(v2,v1)
-
-
-
-
-
-
class ScriptHeaderTests(TestCase):
non_ascii_exe = '/Users/José/bin/python'
+ exe_with_spaces = r'C:\Program Files\Python33\python.exe'
def test_get_script_header(self):
if not sys.platform.startswith('java') or not is_sh(sys.executable):
# This test is for non-Jython platforms
+ expected = '#!%s\n' % nt_quote_arg(os.path.normpath(sys.executable))
self.assertEqual(get_script_header('#!/usr/local/bin/python'),
- '#!%s\n' % os.path.normpath(sys.executable))
+ expected)
+ expected = '#!%s -x\n' % nt_quote_arg(os.path.normpath(sys.executable))
self.assertEqual(get_script_header('#!/usr/bin/python -x'),
- '#!%s -x\n' % os.path.normpath(sys.executable))
+ expected)
self.assertEqual(get_script_header('#!/usr/bin/python',
executable=self.non_ascii_exe),
'#!%s -x\n' % self.non_ascii_exe)
+ candidate = get_script_header('#!/usr/bin/python',
+ executable=self.exe_with_spaces)
+ self.assertEqual(candidate, '#!"%s"\n' % self.exe_with_spaces)
def test_get_script_header_jython_workaround(self):
# This test doesn't work with Python 3 in some locales
if (sys.version_info >= (3,) and os.environ.get("LC_CTYPE")
- in (None, "C", "POSIX")):
+ in (None, "C", "POSIX")):
return
class java:
@@ -554,8 +562,6 @@ class ScriptHeaderTests(TestCase):
sys.stdout, sys.stderr = stdout, stderr
-
-
class NamespaceTests(TestCase):
def setUp(self):
@@ -610,6 +616,5 @@ class NamespaceTests(TestCase):
self.assertEqual(pkg_resources._namespace_packages["pkg1"], ["pkg1.pkg2"])
# check the __path__ attribute contains both paths
self.assertEqual(pkg1.pkg2.__path__, [
- os.path.join(self._tmpdir, "site-pkgs", "pkg1", "pkg2"),
- os.path.join(self._tmpdir, "site-pkgs2", "pkg1", "pkg2") ])
-
+ os.path.join(self._tmpdir, "site-pkgs", "pkg1", "pkg2"),
+ os.path.join(self._tmpdir, "site-pkgs2", "pkg1", "pkg2")])
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/version.py b/setuptools/version.py
new file mode 100644
index 00000000..9910ac22
--- /dev/null
+++ b/setuptools/version.py
@@ -0,0 +1 @@
+__version__ = '1.1.7'