aboutsummaryrefslogtreecommitdiffstats
path: root/setuptools
diff options
context:
space:
mode:
authorPJ Eby <distutils-sig@python.org>2005-07-11 04:12:48 +0000
committerPJ Eby <distutils-sig@python.org>2005-07-11 04:12:48 +0000
commitd73eb6d059ce9ef94848b918c52453e39a0d213d (patch)
treeddaa814c00bbb7023e250eb7ee3c2034aba80844 /setuptools
parent4b0b1262dced5aab98a18fda75e8e43ae40e28d8 (diff)
downloadexternal_python_setuptools-d73eb6d059ce9ef94848b918c52453e39a0d213d.tar.gz
external_python_setuptools-d73eb6d059ce9ef94848b918c52453e39a0d213d.tar.bz2
external_python_setuptools-d73eb6d059ce9ef94848b918c52453e39a0d213d.zip
Enhanced "zip safety" analysis (including scan of win32.exe's) and have
EasyInstall act on zip safety flags. Add a lot more docs for setuptools. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041115
Diffstat (limited to 'setuptools')
-rw-r--r--setuptools/command/bdist_egg.py163
-rwxr-xr-xsetuptools/command/easy_install.py58
2 files changed, 131 insertions, 90 deletions
diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py
index 621bbb18..e75a4a9c 100644
--- a/setuptools/command/bdist_egg.py
+++ b/setuptools/command/bdist_egg.py
@@ -221,9 +221,7 @@ class bdist_egg(Command):
if os.path.isfile(path):
self.copy_file(path,os.path.join(egg_info,filename))
- # Write a zip safety flag file
- flag = self.zip_safe() and 'zip-safe' or 'not-zip-safe'
- open(os.path.join(archive_root,'EGG-INFO',flag),'w').close()
+ write_safety_flag(archive_root, self.zip_safe())
if os.path.exists(os.path.join(self.egg_info,'depends.txt')):
log.warn(
@@ -231,8 +229,9 @@ class bdist_egg(Command):
"Use the install_requires/extras_require setup() args instead."
)
- if self.exclude_source_files: self.zap_pyfiles()
-
+ if self.exclude_source_files:
+ self.zap_pyfiles()
+
# Make the archive
make_zipfile(self.egg_output, archive_root, verbose=self.verbose,
dry_run=self.dry_run)
@@ -244,77 +243,119 @@ class bdist_egg(Command):
('bdist_egg',get_python_version(),self.egg_output))
+
def zap_pyfiles(self):
log.info("Removing .py files from temporary directory")
- for base,dirs,files in self.walk_contents():
+ for base,dirs,files in walk_egg(self.bdist_dir):
for name in files:
if name.endswith('.py'):
path = os.path.join(base,name)
log.debug("Deleting %s", path)
os.unlink(path)
- def walk_contents(self):
- """Walk files about to be archived, skipping the metadata directory"""
- walker = os.walk(self.bdist_dir)
- base,dirs,files = walker.next()
- if 'EGG-INFO' in dirs:
- dirs.remove('EGG-INFO')
-
- yield base,dirs,files
- for bdf in walker:
- yield bdf
-
def zip_safe(self):
safe = getattr(self.distribution,'zip_safe',None)
if safe is not None:
return safe
log.warn("zip_safe flag not set; analyzing archive contents...")
- safe = True
- for base, dirs, files in self.walk_contents():
- for name in files:
- if name.endswith('.py') or name.endswith('.pyw'):
- continue
- elif name.endswith('.pyc') or name.endswith('.pyo'):
- # always scan, even if we already know we're not safe
- safe = self.scan_module(base, name) and safe
- elif safe:
- log.warn(
- "Distribution contains data or extensions; assuming "
- "it's unsafe (set zip_safe=True in setup() to change"
- )
- safe = False # XXX
- return safe
-
- def scan_module(self, base, name):
- """Check whether module possibly uses unsafe-for-zipfile stuff"""
- filename = os.path.join(base,name)
- if filename[:-1] in self.stubs:
- return True # Extension module
-
- pkg = base[len(self.bdist_dir)+1:].replace(os.sep,'.')
- module = pkg+(pkg and '.' or '')+os.path.splitext(name)[0]
-
- f = open(filename,'rb'); f.read(8) # skip magic & date
- code = marshal.load(f); f.close()
- safe = True
-
- symbols = dict.fromkeys(iter_symbols(code))
- for bad in ['__file__', '__path__']:
- if bad in symbols:
- log.warn("%s: module references %s", module, bad)
- safe = False
- if 'inspect' in symbols:
- for bad in [
- 'getsource', 'getabsfile', 'getsourcefile', 'getfile'
- 'getsourcelines', 'findsource', 'getcomments', 'getframeinfo',
- 'getinnerframes', 'getouterframes', 'stack', 'trace'
- ]:
- if bad in symbols:
- log.warn("%s: module MAY be using inspect.%s", module, bad)
- safe = False
- return safe
+ return analyze_egg(self.bdist_dir, self.stubs)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def walk_egg(egg_dir):
+ """Walk an unpacked egg's contents, skipping the metadata directory"""
+ walker = os.walk(egg_dir)
+ base,dirs,files = walker.next()
+ if 'EGG-INFO' in dirs:
+ dirs.remove('EGG-INFO')
+ yield base,dirs,files
+ for bdf in walker:
+ yield bdf
+
+def analyze_egg(egg_dir, stubs):
+ safe = True
+ for base, dirs, files in walk_egg(egg_dir):
+ for name in files:
+ if name.endswith('.py') or name.endswith('.pyw'):
+ continue
+ elif name.endswith('.pyc') or name.endswith('.pyo'):
+ # always scan, even if we already know we're not safe
+ safe = scan_module(egg_dir, base, name, stubs) and safe
+ '''elif safe:
+ log.warn(
+ "Distribution contains data or extensions; assuming "
+ "it's unsafe (set zip_safe=True in setup() to change"
+ )
+ safe = False # XXX'''
+ return safe
+
+def write_safety_flag(egg_dir, safe):
+ # Write a zip safety flag file
+ flag = safe and 'zip-safe' or 'not-zip-safe'
+ open(os.path.join(egg_dir,'EGG-INFO',flag),'w').close()
+
+
+
+
+
+
+
+
+
+
+def scan_module(egg_dir, base, name, stubs):
+ """Check whether module possibly uses unsafe-for-zipfile stuff"""
+
+ filename = os.path.join(base,name)
+ if filename[:-1] in stubs:
+ return True # Extension module
+
+ pkg = base[len(egg_dir)+1:].replace(os.sep,'.')
+ module = pkg+(pkg and '.' or '')+os.path.splitext(name)[0]
+
+ f = open(filename,'rb'); f.read(8) # skip magic & date
+ code = marshal.load(f); f.close()
+ safe = True
+
+ symbols = dict.fromkeys(iter_symbols(code))
+ for bad in ['__file__', '__path__']:
+ if bad in symbols:
+ log.warn("%s: module references %s", module, bad)
+ safe = False
+ if 'inspect' in symbols:
+ for bad in [
+ 'getsource', 'getabsfile', 'getsourcefile', 'getfile'
+ 'getsourcelines', 'findsource', 'getcomments', 'getframeinfo',
+ 'getinnerframes', 'getouterframes', 'stack', 'trace'
+ ]:
+ if bad in symbols:
+ log.warn("%s: module MAY be using inspect.%s", module, bad)
+ safe = False
+ return safe
+
def iter_symbols(code):
"""Yield names and strings used by `code` and its nested code objects"""
for name in code.co_names: yield name
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
index b80dcb8d..1b3b72a6 100755
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -67,6 +67,7 @@ class easy_install(Command):
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
('record=', None,
"filename in which to record list of installed files"),
+ ('always-unzip', 'Z', "don't install as a zipfile, no matter what")
]
boolean_options = [
@@ -74,12 +75,11 @@ class easy_install(Command):
'delete-conflicting', 'ignore-conflicts-at-my-risk',
]
+ negative_opt = {'always-unzip': 'zip-ok'}
create_index = PackageIndex
-
-
def initialize_options(self):
self.zip_ok = None
self.install_dir = self.script_dir = self.exclude_scripts = None
@@ -291,7 +291,7 @@ class easy_install(Command):
install_needed = install_needed or not download.endswith('.egg')
log.info("Processing %s", os.path.basename(download))
if install_needed or self.always_copy:
- dists = self.install_eggs(download, self.zip_ok, tmpdir)
+ dists = self.install_eggs(download, tmpdir)
for dist in dists:
self.process_distribution(spec, dist)
else:
@@ -337,14 +337,14 @@ class easy_install(Command):
metadata.get_metadata('scripts/'+script_name).replace('\r','\n')
)
-
-
-
-
-
-
-
-
+ def should_unzip(self, dist):
+ if self.zip_ok is not None:
+ return not self.zip_ok
+ if dist.metadata.has_metadata('not-zip-safe'):
+ return True
+ if not dist.metadata.has_metadata('zip-safe'):
+ return True
+ return False
@@ -408,10 +408,10 @@ class easy_install(Command):
pass
- def install_eggs(self, dist_filename, zip_ok, tmpdir):
+ def install_eggs(self, dist_filename, tmpdir):
# .egg dirs or files are already built, so just return them
if dist_filename.lower().endswith('.egg'):
- return [self.install_egg(dist_filename, True, tmpdir)]
+ return [self.install_egg(dist_filename, tmpdir)]
elif dist_filename.lower().endswith('.exe'):
return [self.install_exe(dist_filename, tmpdir)]
@@ -438,7 +438,7 @@ class easy_install(Command):
setup_script = setups[0]
# Now run it, and return the result
- return self.build_and_install(setup_script, setup_base, zip_ok)
+ return self.build_and_install(setup_script, setup_base)
@@ -456,13 +456,14 @@ class easy_install(Command):
metadata = EggMetadata(zipimport.zipimporter(egg_path))
return Distribution.from_filename(egg_path,metadata=metadata)
- def install_egg(self, egg_path, zip_ok, tmpdir):
+ def install_egg(self, egg_path, tmpdir):
destination = os.path.join(self.install_dir,os.path.basename(egg_path))
destination = os.path.abspath(destination)
if not self.dry_run:
ensure_directory(destination)
- self.check_conflicts(self.egg_distribution(egg_path))
+ dist = self.egg_distribution(egg_path)
+ self.check_conflicts(dist)
if not samefile(egg_path, destination):
if os.path.isdir(destination):
dir_util.remove_tree(destination, dry_run=self.dry_run)
@@ -474,14 +475,13 @@ class easy_install(Command):
f,m = shutil.move, "Moving"
else:
f,m = shutil.copytree, "Copying"
- elif zip_ok:
- if egg_path.startswith(tmpdir):
- f,m = shutil.move, "Moving"
- else:
- f,m = shutil.copy2, "Copying"
- else:
+ elif self.should_unzip(dist):
self.mkpath(destination)
f,m = self.unpack_and_compile, "Extracting"
+ elif egg_path.startswith(tmpdir):
+ f,m = shutil.move, "Moving"
+ else:
+ f,m = shutil.copy2, "Copying"
self.execute(f, (egg_path, destination),
(m+" %s to %s") %
@@ -526,17 +526,15 @@ class easy_install(Command):
)
# install the .egg
- return self.install_egg(egg_path, self.zip_ok, tmpdir)
+ return self.install_egg(egg_path, tmpdir)
def exe_to_egg(self, dist_filename, egg_tmp):
"""Extract a bdist_wininst to the directories an egg would use"""
-
# Check for .pth file and set up prefix translations
prefixes = get_exe_prefixes(dist_filename)
-
to_compile = []
native_libs = []
top_level = {}
@@ -561,16 +559,18 @@ class easy_install(Command):
# extract, tracking .pyd/.dll->native_libs and .py -> to_compile
unpack_archive(dist_filename, egg_tmp, process)
-
+ stubs = []
for res in native_libs:
if res.lower().endswith('.pyd'): # create stubs for .pyd's
parts = res.split('/')
resource, parts[-1] = parts[-1], parts[-1][:-1]
pyfile = os.path.join(egg_tmp, *parts)
- to_compile.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(egg_tmp,
+ bdist_egg.analyze_egg(egg_tmp, stubs)) # write zip-safety flag
for name in 'top_level','native_libs':
if locals()[name]:
@@ -695,7 +695,7 @@ PYTHONPATH, or by being added to sys.path by your code.)
- def build_and_install(self, setup_script, setup_base, zip_ok):
+ def build_and_install(self, setup_script, setup_base):
sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg)
sys.modules.setdefault('distutils.command.bdist_egg', egg_info)
@@ -724,7 +724,7 @@ PYTHONPATH, or by being added to sys.path by your code.)
eggs = []
for egg in glob(os.path.join(dist_dir,'*.egg')):
- eggs.append(self.install_egg(egg, zip_ok, setup_base))
+ eggs.append(self.install_egg(egg, setup_base))
if not eggs and not self.dry_run:
log.warn("No eggs found in %s (setup script problem?)",