summaryrefslogtreecommitdiff
path: root/setuptools
diff options
context:
space:
mode:
authorPJ Eby <distutils-sig@python.org>2009-10-12 20:00:02 +0000
committerPJ Eby <distutils-sig@python.org>2009-10-12 20:00:02 +0000
commit9294929b0028f551a54dd48cc3325581933b3c5f (patch)
treee07221c46c7f32ac39160b3f2cb50e649cd5cc2e /setuptools
parent20a0abd3d0f10198d6c21033ad2c11db2edbad71 (diff)
downloadpython-setuptools-git-9294929b0028f551a54dd48cc3325581933b3c5f.tar.gz
Major updates and fixes include:
* Fix for the Python 2.6.3 build_ext API change * Support for the most recent Sourceforge download link insanity * Support for SVN 1.6 * Stop crashing on certain types of HTTP error * Stop re-trying URLs that already failed retrieval once * Fixes for various dependency management problems such as looping builds, re-downloading packages already present on sys.path (but not in a registered "site" directory), and randomly preferring local -f packages over local installed packages * Prevent lots of spurious "already imported from another path" warnings (e.g. when pkg_resources is imported late) * Ensure C libraries (as opposed to extensions) are also built when doing bdist_egg Other changes: * Misc. documentation fixes * Improved Jython support * Fewer warnings under Python 2.6+ * Warn when 'packages' uses paths instead of package names (because it causes other problems, like spurious "already imported" warnings) * Stop using /usr/bin/sw_vers on Mac OS (replaced w/'platform' module calls) Note: This is NOT a merge from Distribute; upon review, many of the tracker-submitted patches used as a basis for forking were incorrect, incomplete, introduced new bugs, or were not addressing the root causes. (E.g., one of the changes in this patch fixes three superficially unrelated issues in the setuptools bug tracker.) Careful review will be required if you want to merge this work back into Distribute. --HG-- branch : setuptools-0.6 extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/branches/setuptools-0.6%4075385
Diffstat (limited to 'setuptools')
-rw-r--r--setuptools/__init__.py2
-rwxr-xr-xsetuptools/command/alias.py4
-rw-r--r--setuptools/command/bdist_egg.py6
-rw-r--r--setuptools/command/build_ext.py18
-rwxr-xr-xsetuptools/command/easy_install.py14
-rwxr-xr-xsetuptools/command/egg_info.py22
-rwxr-xr-xsetuptools/command/sdist.py83
-rw-r--r--setuptools/depends.py2
-rw-r--r--setuptools/dist.py53
-rwxr-xr-xsetuptools/package_index.py62
-rwxr-xr-xsetuptools/sandbox.py75
11 files changed, 232 insertions, 109 deletions
diff --git a/setuptools/__init__.py b/setuptools/__init__.py
index 56cbf767..a9f6544d 100644
--- a/setuptools/__init__.py
+++ b/setuptools/__init__.py
@@ -7,7 +7,7 @@ from distutils.core import Command as _Command
from distutils.util import convert_path
import os.path
-__version__ = '0.6c9'
+__version__ = '0.6c10'
__all__ = [
'setup', 'Distribution', 'Feature', 'Command', 'Extension', 'Require',
'find_packages'
diff --git a/setuptools/command/alias.py b/setuptools/command/alias.py
index f5368b29..40c00b55 100755
--- a/setuptools/command/alias.py
+++ b/setuptools/command/alias.py
@@ -9,7 +9,7 @@ def shquote(arg):
"""Quote an argument for later parsing by shlex.split()"""
for c in '"', "'", "\\", "#":
if c in arg: return repr(arg)
- if arg.split()<>[arg]:
+ if arg.split()!=[arg]:
return repr(arg)
return arg
@@ -33,7 +33,7 @@ class alias(option_base):
def finalize_options(self):
option_base.finalize_options(self)
- if self.remove and len(self.args)<>1:
+ if self.remove and len(self.args)!=1:
raise DistutilsOptionError(
"Must specify exactly one argument (the alias name) when "
"using --remove"
diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py
index 9e852a3f..7e5a3799 100644
--- a/setuptools/command/bdist_egg.py
+++ b/setuptools/command/bdist_egg.py
@@ -165,12 +165,13 @@ class bdist_egg(Command):
def run(self):
# Generate metadata first
self.run_command("egg_info")
-
# We run install_lib before install_data, because some data hacks
# pull their data path from the install_lib command.
log.info("installing library code to %s" % self.bdist_dir)
instcmd = self.get_finalized_command('install')
old_root = instcmd.root; instcmd.root = None
+ if self.distribution.has_c_libraries() and not self.skip_build:
+ self.run_command('build_clib')
cmd = self.call_command('install_lib', warn_dir=0)
instcmd.root = old_root
@@ -190,7 +191,6 @@ class bdist_egg(Command):
to_compile.extend(self.make_init_files())
if to_compile:
cmd.byte_compile(to_compile)
-
if self.distribution.data_files:
self.do_install_data()
@@ -398,7 +398,7 @@ def write_safety_flag(egg_dir, safe):
for flag,fn in safety_flags.items():
fn = os.path.join(egg_dir, fn)
if os.path.exists(fn):
- if safe is None or bool(safe)<>flag:
+ if safe is None or bool(safe)!=flag:
os.unlink(fn)
elif safe is not None and bool(safe)==flag:
f=open(fn,'wb'); f.write('\n'); f.close()
diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py
index c0aaa8e8..f6f3355d 100644
--- a/setuptools/command/build_ext.py
+++ b/setuptools/command/build_ext.py
@@ -82,15 +82,15 @@ class build_ext(_build_ext):
def get_ext_filename(self, fullname):
filename = _build_ext.get_ext_filename(self,fullname)
- ext = self.ext_map[fullname]
- if isinstance(ext,Library):
- fn, ext = os.path.splitext(filename)
- return self.shlib_compiler.library_filename(fn,libtype)
- elif use_stubs and ext._links_to_dynamic:
- d,fn = os.path.split(filename)
- return os.path.join(d,'dl-'+fn)
- else:
- return filename
+ if fullname in self.ext_map:
+ ext = self.ext_map[fullname]
+ if isinstance(ext,Library):
+ fn, ext = os.path.splitext(filename)
+ return self.shlib_compiler.library_filename(fn,libtype)
+ elif use_stubs and ext._links_to_dynamic:
+ d,fn = os.path.split(filename)
+ return os.path.join(d,'dl-'+fn)
+ return filename
def initialize_options(self):
_build_ext.initialize_options(self)
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
index f06b6ddd..a5a23ad4 100755
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -204,7 +204,7 @@ class easy_install(Command):
self.outputs = []
def run(self):
- if self.verbose<>self.distribution.verbose:
+ if self.verbose!=self.distribution.verbose:
log.set_verbosity(self.verbose)
try:
for spec in self.args:
@@ -252,7 +252,7 @@ class easy_install(Command):
# Is it a configured, PYTHONPATH, implicit, or explicit site dir?
is_site_dir = instdir in self.all_site_dirs
- if not is_site_dir:
+ if not is_site_dir and not self.multi_version:
# No? Then directly test whether it does .pth file processing
is_site_dir = self.check_pth_processing()
else:
@@ -430,9 +430,9 @@ Please make the appropriate changes for your system and try again.
self.check_editable(spec)
dist = self.package_index.fetch_distribution(
- spec, tmpdir, self.upgrade, self.editable, not self.always_copy
+ spec, tmpdir, self.upgrade, self.editable, not self.always_copy,
+ self.local_index
)
-
if dist is None:
msg = "Could not find suitable distribution for %r" % spec
if self.always_copy:
@@ -722,7 +722,7 @@ Please make the appropriate changes for your system and try again.
f = open(pkg_inf,'w')
f.write('Metadata-Version: 1.0\n')
for k,v in cfg.items('metadata'):
- if k<>'target_version':
+ if k!='target_version':
f.write('%s: %s\n' % (k.replace('_','-').title(), v))
f.close()
script_dir = os.path.join(egg_info,'scripts')
@@ -988,7 +988,6 @@ See the setuptools documentation for the "develop" command for more info.
def pf(src,dst):
if dst.endswith('.py') and not src.startswith('EGG-INFO/'):
to_compile.append(dst)
- to_chmod.append(dst)
elif dst.endswith('.dll') or dst.endswith('.so'):
to_chmod.append(dst)
self.unpack_progress(src,dst)
@@ -1023,6 +1022,7 @@ See the setuptools documentation for the "develop" command for more info.
+
def no_default_version_msg(self):
return """bad install directory or PYTHONPATH
@@ -1286,7 +1286,7 @@ def get_exe_prefixes(exe_filename):
if parts[1].endswith('.egg-info'):
prefixes.insert(0,('/'.join(parts[:2]), 'EGG-INFO/'))
break
- if len(parts)<>2 or not name.endswith('.pth'):
+ if len(parts)!=2 or not name.endswith('.pth'):
continue
if name.endswith('-nspkg.pth'):
continue
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
index 9741e26a..5a8b2db8 100755
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -217,18 +217,21 @@ class egg_info(Command):
data = f.read()
f.close()
- if data.startswith('9') or data.startswith('8'):
+ if data.startswith('<?xml'):
+ dirurl = urlre.search(data).group(1) # get repository URL
+ localrev = max([int(m.group(1)) for m in revre.finditer(data)]+[0])
+ else:
+ try: svnver = int(data.splitlines()[0])
+ except: svnver=-1
+ if data<8:
+ log.warn("unrecognized .svn/entries format; skipping %s", base)
+ dirs[:] = []
+ continue
+
data = map(str.splitlines,data.split('\n\x0c\n'))
del data[0][0] # get rid of the '8' or '9'
dirurl = data[0][3]
localrev = max([int(d[9]) for d in data if len(d)>9 and d[9]]+[0])
- elif data.startswith('<?xml'):
- dirurl = urlre.search(data).group(1) # get repository URL
- localrev = max([int(m.group(1)) for m in revre.finditer(data)]+[0])
- else:
- log.warn("unrecognized .svn/entries format; skipping %s", base)
- dirs[:] = []
- continue
if base==os.curdir:
base_url = dirurl+'/' # save the root url
elif not dirurl.startswith(base_url):
@@ -241,9 +244,6 @@ class egg_info(Command):
-
-
-
def find_sources(self):
"""Generate SOURCES.txt manifest file"""
manifest_filename = os.path.join(self.egg_info,"SOURCES.txt")
diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py
index 5bd5ebd4..d5121de5 100755
--- a/setuptools/command/sdist.py
+++ b/setuptools/command/sdist.py
@@ -1,6 +1,7 @@
from distutils.command.sdist import sdist as _sdist
from distutils.util import convert_path
from distutils import log
+from glob import glob
import os, re, sys, pkg_resources
entities = [
@@ -38,7 +39,6 @@ def joinpath(prefix,suffix):
-
def walk_revctrl(dirname=''):
"""Find all files under revision control"""
for ep in pkg_resources.iter_entry_points('setuptools.file_finders'):
@@ -86,17 +86,21 @@ def entries_finder(dirname, filename):
f = open(filename,'rU')
data = f.read()
f.close()
- if data.startswith('9') or data.startswith('8'): # subversion 1.5/1.4
+ 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", dirname)
+ return
for record in map(str.splitlines, data.split('\n\x0c\n')[1:]):
if not record or len(record)>=6 and record[5]=="delete":
continue # skip deleted
yield joinpath(dirname, record[0])
- elif data.startswith('<?xml'):
- for match in entries_pattern.finditer(data):
- yield joinpath(dirname,unescape(match.group(1)))
- else:
- log.warn("unrecognized .svn/entries format in %s", dirname)
-
+
finders = [
(convert_path('CVS/Entries'),
@@ -117,10 +121,6 @@ finders = [
-
-
-
-
class sdist(_sdist):
"""Smart sdist that finds anything supported by revision control"""
@@ -162,6 +162,56 @@ class sdist(_sdist):
sys.exc_info()[2].tb_next.tb_frame.f_locals['template'].close()
raise
+ # Cribbed from old distutils code, to work around new distutils code
+ # that tries to do some of the same stuff as we do, in a way that makes
+ # us loop.
+
+ def add_defaults (self):
+ standards = [('README', 'README.txt'), self.distribution.script_name]
+
+ for fn in standards:
+ if type(fn) is tuple:
+ alts = fn
+ got_it = 0
+ for fn in alts:
+ if os.path.exists(fn):
+ got_it = 1
+ self.filelist.append(fn)
+ break
+
+ if not got_it:
+ self.warn("standard file not found: should have one of " +
+ string.join(alts, ', '))
+ else:
+ if os.path.exists(fn):
+ self.filelist.append(fn)
+ else:
+ self.warn("standard file '%s' not found" % fn)
+
+ optional = ['test/test*.py', 'setup.cfg']
+
+ for pattern in optional:
+ files = filter(os.path.isfile, glob(pattern))
+ if files:
+ self.filelist.extend(files)
+
+ if self.distribution.has_pure_modules():
+ build_py = self.get_finalized_command('build_py')
+ self.filelist.extend(build_py.get_source_files())
+
+ if self.distribution.has_ext_modules():
+ build_ext = self.get_finalized_command('build_ext')
+ self.filelist.extend(build_ext.get_source_files())
+
+ if self.distribution.has_c_libraries():
+ build_clib = self.get_finalized_command('build_clib')
+ self.filelist.extend(build_clib.get_source_files())
+
+ if self.distribution.has_scripts():
+ build_scripts = self.get_finalized_command('build_scripts')
+ self.filelist.extend(build_scripts.get_source_files())
+
+
def check_readme(self):
alts = ("README", "README.txt")
for f in alts:
@@ -193,13 +243,4 @@ class sdist(_sdist):
-
-
-
-
-
-
-
-
-
#
diff --git a/setuptools/depends.py b/setuptools/depends.py
index 4b7b3437..5fdf2d7e 100644
--- a/setuptools/depends.py
+++ b/setuptools/depends.py
@@ -36,7 +36,7 @@ class Require:
def version_ok(self,version):
"""Is 'version' sufficiently up-to-date?"""
return self.attribute is None or self.format is None or \
- str(version)<>"unknown" and version >= self.requested_version
+ str(version)!="unknown" and version >= self.requested_version
def get_version(self, paths=None, default="unknown"):
diff --git a/setuptools/dist.py b/setuptools/dist.py
index 30ff35e3..c1218ef2 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -8,7 +8,7 @@ from setuptools.command.install_lib import install_lib
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 os, distutils.log, re
def _get_unpatched(cls):
"""Protect against re-patching the distutils if reloaded
@@ -61,8 +61,8 @@ def check_nsp(dist, attr, value):
parent = '.'.join(nsp.split('.')[:-1])
if parent not in value:
distutils.log.warn(
- "%r is declared as a package namespace, but %r is not:"
- " please correct this in setup.py", nsp, parent
+ "WARNING: %r is declared as a package namespace, but %r"
+ " is not: please correct this in setup.py", nsp, parent
)
def check_extras(dist, attr, value):
@@ -121,6 +121,47 @@ def check_package_data(dist, attr, value):
"wildcard patterns"
)
+def check_packages(dist, attr, value):
+ for pkgname in value:
+ if not re.match(r'\w+(\.\w+)*', pkgname):
+ distutils.log.warn(
+ "WARNING: %r not a valid package name; please use only"
+ ".-separated package names in setup.py", pkgname
+ )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
class Distribution(_Distribution):
"""Distribution with support for features, tests, and package data
@@ -415,19 +456,19 @@ class Distribution(_Distribution):
if self.packages:
self.packages = [
p for p in self.packages
- if p<>package and not p.startswith(pfx)
+ if p!=package and not p.startswith(pfx)
]
if self.py_modules:
self.py_modules = [
p for p in self.py_modules
- if p<>package and not p.startswith(pfx)
+ if p!=package and not p.startswith(pfx)
]
if self.ext_modules:
self.ext_modules = [
p for p in self.ext_modules
- if p.name<>package and not p.name.startswith(pfx)
+ if p.name!=package and not p.name.startswith(pfx)
]
diff --git a/setuptools/package_index.py b/setuptools/package_index.py
index da3fed12..70b75a6f 100755
--- a/setuptools/package_index.py
+++ b/setuptools/package_index.py
@@ -1,5 +1,6 @@
"""PyPI and direct package downloading"""
import sys, os.path, re, urlparse, urllib2, shutil, random, socket, cStringIO
+import httplib
from pkg_resources import *
from distutils import log
from distutils.errors import DistutilsError
@@ -8,7 +9,6 @@ try:
except ImportError:
from md5 import md5
from fnmatch import translate
-
EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$')
HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I)
# this is here to fix emacs' cruddy broken syntax highlighting
@@ -42,6 +42,8 @@ def parse_bdist_wininst(name):
def egg_info_for_url(url):
scheme, server, path, parameters, query, fragment = urlparse.urlparse(url)
base = urllib2.unquote(path.split('/')[-1])
+ if server=='sourceforge.net' and base=='download': # XXX Yuck
+ base = urllib2.unquote(path.split('/')[-2])
if '#' in base: base, fragment = base.split('#',1)
return base,fragment
@@ -64,14 +66,12 @@ def distros_for_location(location, basename, metadata=None):
if basename.endswith('.egg') and '-' in basename:
# only one, unambiguous interpretation
return [Distribution.from_location(location, basename, metadata)]
-
if basename.endswith('.exe'):
win_base, py_ver = parse_bdist_wininst(basename)
if win_base is not None:
return interpret_distro_name(
location, win_base, metadata, py_ver, BINARY_DIST, "win32"
)
-
# Try source distro extensions (.zip, .tgz, etc.)
#
for ext in EXTENSIONS:
@@ -186,10 +186,10 @@ class PackageIndex(Environment):
return
self.info("Reading %s", url)
+ self.fetched_urls[url] = True # prevent multiple fetch attempts
f = self.open_url(url, "Download error: %s -- Some packages may not be found!")
if f is None: return
- self.fetched_urls[url] = self.fetched_urls[f.url] = True
-
+ self.fetched_urls[f.url] = True
if 'html' not in f.headers.get('content-type', '').lower():
f.close() # not html, we can't process it
return
@@ -329,7 +329,7 @@ class PackageIndex(Environment):
def check_md5(self, cs, info, filename, tfp):
if re.match('md5=[0-9a-f]{32}$', info):
self.debug("Validating md5 checksum for %s", filename)
- if cs.hexdigest()<>info[4:]:
+ if cs.hexdigest()!=info[4:]:
tfp.close()
os.unlink(filename)
raise DistutilsError(
@@ -409,7 +409,8 @@ class PackageIndex(Environment):
def fetch_distribution(self,
- requirement, tmpdir, force_scan=False, source=False, develop_ok=False
+ requirement, tmpdir, force_scan=False, source=False, develop_ok=False,
+ local_index=None,
):
"""Obtain a distribution suitable for fulfilling `requirement`
@@ -427,15 +428,15 @@ class PackageIndex(Environment):
set, development and system eggs (i.e., those using the ``.egg-info``
format) will be ignored.
"""
-
# process a Requirement
self.info("Searching for %s", requirement)
skipped = {}
+ dist = None
- def find(req):
+ def find(env, req):
# Find a matching distribution; may be called more than once
- for dist in self[req.key]:
+ for dist in env[req.key]:
if dist.precedence==DEVELOP_DIST and not develop_ok:
if dist not in skipped:
@@ -444,23 +445,25 @@ class PackageIndex(Environment):
continue
if dist in req and (dist.precedence<=SOURCE_DIST or not source):
- self.info("Best match: %s", dist)
- return dist.clone(
- location=self.download(dist.location, tmpdir)
- )
+ return dist
+
+
if force_scan:
self.prescan()
self.find_packages(requirement)
+ dist = find(self, requirement)
+
+ if local_index is not None:
+ dist = dist or find(local_index, requirement)
- dist = find(requirement)
if dist is None and self.to_scan is not None:
self.prescan()
- dist = find(requirement)
+ dist = find(self, requirement)
if dist is None and not force_scan:
self.find_packages(requirement)
- dist = find(requirement)
+ dist = find(self, requirement)
if dist is None:
self.warn(
@@ -468,7 +471,9 @@ class PackageIndex(Environment):
(source and "a source distribution of " or ""),
requirement,
)
- return dist
+ 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`
@@ -485,11 +490,6 @@ class PackageIndex(Environment):
-
-
-
-
-
def gen_setup(self, filename, fragment, tmpdir):
match = EGG_FRAGMENT.match(fragment)
dists = match and [d for d in
@@ -573,17 +573,19 @@ class PackageIndex(Environment):
def open_url(self, url, warning=None):
- if url.startswith('file:'):
- return local_open(url)
+ if url.startswith('file:'): return local_open(url)
try:
return open_with_auth(url)
except urllib2.HTTPError, v:
return v
except urllib2.URLError, v:
- if warning: self.warn(warning, v.reason)
- else:
- raise DistutilsError("Download error for %s: %s"
- % (url, v.reason))
+ reason = v.reason
+ except httplib.HTTPException, v:
+ reason = "%s: %s" % (v.__doc__ or v.__class__.__name__, v)
+ if warning:
+ self.warn(warning, reason)
+ else:
+ raise DistutilsError("Download error for %s: %s" % (url, reason))
def _download_url(self, scheme, url, tmpdir):
# Determine download filename
@@ -611,8 +613,6 @@ class PackageIndex(Environment):
self.url_ok(url, True) # raises error if not allowed
return self._attempt_download(url, filename)
-
-
def scan_url(self, url):
self.process_url(url, True)
diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py
index 4db0dbdb..00eb0124 100755
--- a/setuptools/sandbox.py
+++ b/setuptools/sandbox.py
@@ -1,14 +1,46 @@
-import os, sys, __builtin__, tempfile, operator
+import os, sys, __builtin__, tempfile, operator, pkg_resources
_os = sys.modules[os.name]
_open = open
+_file = file
+
from distutils.errors import DistutilsError
+from pkg_resources import working_set
+
__all__ = [
"AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup",
]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
def run_setup(setup_script, args):
"""Run a distutils setup script, sandboxed in its directory"""
-
old_dir = os.getcwd()
save_argv = sys.argv[:]
save_path = sys.path[:]
@@ -16,13 +48,16 @@ def run_setup(setup_script, args):
temp_dir = os.path.join(setup_dir,'temp')
if not os.path.isdir(temp_dir): os.makedirs(temp_dir)
save_tmp = tempfile.tempdir
-
+ save_modules = sys.modules.copy()
+ pr_state = pkg_resources.__getstate__()
try:
- tempfile.tempdir = temp_dir
- os.chdir(setup_dir)
+ tempfile.tempdir = temp_dir; os.chdir(setup_dir)
try:
sys.argv[:] = [setup_script]+list(args)
sys.path.insert(0, setup_dir)
+ # reset to include setup dir, w/clean callback list
+ working_set.__init__()
+ working_set.callbacks.append(lambda dist:dist.activate())
DirectorySandbox(setup_dir).run(
lambda: execfile(
"setup.py",
@@ -34,11 +69,17 @@ def run_setup(setup_script, args):
raise
# Normal exit, just return
finally:
+ pkg_resources.__setstate__(pr_state)
+ sys.modules.update(save_modules)
+ for key in list(sys.modules):
+ if key not in save_modules: del sys.modules[key]
os.chdir(old_dir)
sys.path[:] = save_path
sys.argv[:] = save_argv
tempfile.tempdir = save_tmp
+
+
class AbstractSandbox:
"""Wrap 'os' module and 'open()' builtin for virtualizing setup scripts"""
@@ -58,15 +99,16 @@ class AbstractSandbox:
"""Run 'func' under os sandboxing"""
try:
self._copy(self)
- __builtin__.open = __builtin__.file = self._open
+ __builtin__.file = self._file
+ __builtin__.open = self._open
self._active = True
return func()
finally:
self._active = False
- __builtin__.open = __builtin__.file = _open
+ __builtin__.open = _file
+ __builtin__.file = _open
self._copy(_os)
-
def _mk_dual_path_wrapper(name):
original = getattr(_os,name)
def wrap(self,src,dst,*args,**kw):
@@ -75,7 +117,6 @@ class AbstractSandbox:
return original(src,dst,*args,**kw)
return wrap
-
for name in ["rename", "link", "symlink"]:
if hasattr(_os,name): locals()[name] = _mk_dual_path_wrapper(name)
@@ -88,7 +129,8 @@ class AbstractSandbox:
return original(path,*args,**kw)
return wrap
- _open = _mk_single_path_wrapper('file', _open)
+ _open = _mk_single_path_wrapper('open', _open)
+ _file = _mk_single_path_wrapper('file', _file)
for name in [
"stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir",
"remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat",
@@ -96,7 +138,6 @@ class AbstractSandbox:
]:
if hasattr(_os,name): locals()[name] = _mk_single_path_wrapper(name)
-
def _mk_single_with_return(name):
original = getattr(_os,name)
def wrap(self,path,*args,**kw):
@@ -187,22 +228,22 @@ class DirectorySandbox(AbstractSandbox):
self._violation(operation, src, dst, *args, **kw)
return (src,dst)
+ def _file(self, path, mode='r', *args, **kw):
+ if mode not in ('r', 'rt', 'rb', 'rU', 'U') and not self._ok(path):
+ self._violation("file", path, mode, *args, **kw)
+ return _file(path,mode,*args,**kw)
+
def open(self, file, flags, mode=0777):
"""Called for low-level os.open()"""
if flags & WRITE_FLAGS and not self._ok(file):
self._violation("os.open", file, flags, mode)
return _os.open(file,flags,mode)
-
WRITE_FLAGS = reduce(
- operator.or_,
- [getattr(_os, a, 0) for a in
+ operator.or_, [getattr(_os, a, 0) for a in
"O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()]
)
-
-
-
class SandboxViolation(DistutilsError):
"""A setup script attempted to modify the filesystem outside the sandbox"""