summaryrefslogtreecommitdiff
path: root/setuptools/command
diff options
context:
space:
mode:
Diffstat (limited to 'setuptools/command')
-rw-r--r--setuptools/command/__init__.py4
-rw-r--r--setuptools/command/bdist_egg.py29
-rw-r--r--setuptools/command/build_ext.py5
-rw-r--r--setuptools/command/build_py.py89
-rwxr-xr-xsetuptools/command/develop.py85
-rwxr-xr-xsetuptools/command/easy_install.py427
-rwxr-xr-xsetuptools/command/egg_info.py51
-rw-r--r--setuptools/command/install.py7
-rwxr-xr-xsetuptools/command/install_egg_info.py8
-rwxr-xr-xsetuptools/command/install_scripts.py46
-rw-r--r--setuptools/command/launcher manifest.xml15
-rwxr-xr-xsetuptools/command/sdist.py100
-rw-r--r--setuptools/command/test.py46
-rwxr-xr-xsetuptools/command/upload.py12
-rw-r--r--setuptools/command/upload_docs.py195
15 files changed, 888 insertions, 231 deletions
diff --git a/setuptools/command/__init__.py b/setuptools/command/__init__.py
index f898822b..b063fa19 100644
--- a/setuptools/command/__init__.py
+++ b/setuptools/command/__init__.py
@@ -2,10 +2,12 @@ __all__ = [
'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop',
'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts',
'sdist', 'setopt', 'test', 'upload', 'install_egg_info', 'install_scripts',
- 'register', 'bdist_wininst',
+ 'register', 'bdist_wininst', 'upload_docs',
]
+from setuptools.command import install_scripts
import sys
+
if sys.version>='2.5':
# In Python 2.5 and above, distutils includes its own upload command
__all__.remove('upload')
diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py
index 7e5a3799..c3356bb7 100644
--- a/setuptools/command/bdist_egg.py
+++ b/setuptools/command/bdist_egg.py
@@ -6,7 +6,12 @@ Build .egg distributions"""
import sys, os, marshal
from setuptools import Command
from distutils.dir_util import remove_tree, mkpath
-from distutils.sysconfig import get_python_version, get_python_lib
+try:
+ from distutils.sysconfig import get_python_version, get_python_lib
+except ImportError:
+ from sysconfig import get_python_version
+ from distutils.sysconfig import get_python_lib
+
from distutils import log
from distutils.errors import DistutilsSetupError
from pkg_resources import get_build_platform, Distribution, ensure_directory
@@ -327,7 +332,11 @@ class bdist_egg(Command):
def copy_metadata_to(self, target_dir):
- prefix = os.path.join(self.egg_info,'')
+ "Copy metadata (egg info) to the target_dir"
+ # normalize the path (so that a forward-slash in egg_info will
+ # match using startswith below)
+ norm_egg_info = os.path.normpath(self.egg_info)
+ prefix = os.path.join(norm_egg_info,'')
for path in self.ei_cmd.filelist.files:
if path.startswith(prefix):
target = os.path.join(target_dir, path[len(prefix):])
@@ -401,7 +410,7 @@ def write_safety_flag(egg_dir, safe):
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()
+ f=open(fn,'wt'); f.write('\n'); f.close()
safety_flags = {
True: 'zip-safe',
@@ -416,8 +425,12 @@ def scan_module(egg_dir, base, name, 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()
+ if sys.version_info < (3, 3):
+ skip = 8 # skip magic & date
+ else:
+ skip = 12 # skip magic & date & file size
+ f = open(filename,'rb'); f.read(skip)
+ code = marshal.load(f); f.close()
safe = True
symbols = dict.fromkeys(iter_symbols(code))
for bad in ['__file__', '__path__']:
@@ -525,9 +538,11 @@ def make_zipfile(zip_filename, base_dir, verbose=0, dry_run=0, compress=None,
compression = [zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED][bool(compress)]
if not dry_run:
z = zipfile.ZipFile(zip_filename, mode, compression=compression)
- os.path.walk(base_dir, visit, z)
+ for dirname, dirs, files in os.walk(base_dir):
+ visit(z, dirname, files)
z.close()
else:
- os.path.walk(base_dir, visit, None)
+ for dirname, dirs, files in os.walk(base_dir):
+ visit(None, dirname, files)
return zip_filename
#
diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py
index f6f3355d..f2a53258 100644
--- a/setuptools/command/build_ext.py
+++ b/setuptools/command/build_ext.py
@@ -111,6 +111,11 @@ class build_ext(_build_ext):
for ext in self.extensions:
fullname = ext._full_name
self.ext_map[fullname] = ext
+
+ # distutils 3.1 will also ask for module names
+ # XXX what to do with conflicts?
+ self.ext_map[fullname.split('.')[-1]] = ext
+
ltd = ext._links_to_dynamic = \
self.shlibs and self.links_to_dynamic(ext) or False
ext._needs_stub = ltd and use_stubs and not isinstance(ext,Library)
diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py
index 79570bc2..8751acd4 100644
--- a/setuptools/command/build_py.py
+++ b/setuptools/command/build_py.py
@@ -3,7 +3,64 @@ from distutils.command.build_py import build_py as _build_py
from distutils.util import convert_path
from glob import glob
-class build_py(_build_py):
+try:
+ from distutils.util import Mixin2to3 as _Mixin2to3
+ # add support for converting doctests that is missing in 3.1 distutils
+ from distutils import log
+ from lib2to3.refactor import RefactoringTool, get_fixers_from_package
+ import setuptools
+ class DistutilsRefactoringTool(RefactoringTool):
+ def log_error(self, msg, *args, **kw):
+ log.error(msg, *args)
+
+ def log_message(self, msg, *args):
+ log.info(msg, *args)
+
+ def log_debug(self, msg, *args):
+ log.debug(msg, *args)
+
+ class Mixin2to3(_Mixin2to3):
+ def run_2to3(self, files, doctests = False):
+ # See of the distribution option has been set, otherwise check the
+ # setuptools default.
+ if self.distribution.use_2to3 is not True:
+ return
+ if not files:
+ return
+ log.info("Fixing "+" ".join(files))
+ self.__build_fixer_names()
+ self.__exclude_fixers()
+ if doctests:
+ if setuptools.run_2to3_on_doctests:
+ r = DistutilsRefactoringTool(self.fixer_names)
+ r.refactor(files, write=True, doctests_only=True)
+ else:
+ _Mixin2to3.run_2to3(self, files)
+
+ def __build_fixer_names(self):
+ if self.fixer_names: return
+ self.fixer_names = []
+ for p in setuptools.lib2to3_fixer_packages:
+ self.fixer_names.extend(get_fixers_from_package(p))
+ if self.distribution.use_2to3_fixers is not None:
+ for p in self.distribution.use_2to3_fixers:
+ self.fixer_names.extend(get_fixers_from_package(p))
+
+ def __exclude_fixers(self):
+ excluded_fixers = getattr(self, 'exclude_fixers', [])
+ if self.distribution.use_2to3_exclude_fixers is not None:
+ excluded_fixers.extend(self.distribution.use_2to3_exclude_fixers)
+ for fixer_name in excluded_fixers:
+ if fixer_name in self.fixer_names:
+ self.fixer_names.remove(fixer_name)
+
+except ImportError:
+ class Mixin2to3:
+ def run_2to3(self, files, doctests=True):
+ # Nothing done in 2.x
+ pass
+
+class build_py(_build_py, Mixin2to3):
"""Enhanced 'build_py' command that includes data files with packages
The data files are specified via a 'package_data' argument to 'setup()'.
@@ -17,6 +74,8 @@ class build_py(_build_py):
self.package_data = self.distribution.package_data
self.exclude_package_data = self.distribution.exclude_package_data or {}
if 'data_files' in self.__dict__: del self.__dict__['data_files']
+ self.__updated_files = []
+ self.__doctests_2to3 = []
def run(self):
"""Build modules, packages, and copy data files to build directory"""
@@ -30,6 +89,10 @@ class build_py(_build_py):
self.build_packages()
self.build_package_data()
+ self.run_2to3(self.__updated_files, False)
+ self.run_2to3(self.__updated_files, True)
+ self.run_2to3(self.__doctests_2to3, True)
+
# Only compile actual .py files, using our base class' idea of what our
# output files are.
self.byte_compile(_build_py.get_outputs(self, include_bytecode=0))
@@ -39,6 +102,12 @@ class build_py(_build_py):
self.data_files = files = self._get_data_files(); return files
return _build_py.__getattr__(self,attr)
+ def build_module(self, module, module_file, package):
+ outfile, copied = _build_py.build_module(self, module, module_file, package)
+ if copied:
+ self.__updated_files.append(outfile)
+ return outfile, copied
+
def _get_data_files(self):
"""Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
self.analyze_manifest()
@@ -77,7 +146,11 @@ class build_py(_build_py):
for filename in filenames:
target = os.path.join(build_dir, filename)
self.mkpath(os.path.dirname(target))
- self.copy_file(os.path.join(src_dir, filename), target)
+ srcfile = os.path.join(src_dir, filename)
+ outf, copied = self.copy_file(srcfile, target)
+ srcfile = os.path.abspath(srcfile)
+ if copied and srcfile in self.distribution.convert_2to3_doctests:
+ self.__doctests_2to3.append(outf)
def analyze_manifest(self):
@@ -140,8 +213,8 @@ class build_py(_build_py):
else:
return init_py
- f = open(init_py,'rU')
- if 'declare_namespace' not in f.read():
+ f = open(init_py,'rbU')
+ if 'declare_namespace'.encode() not in f.read():
from distutils import log
log.warn(
"WARNING: %s is a namespace package, but its __init__.py does\n"
@@ -157,9 +230,11 @@ class build_py(_build_py):
_build_py.initialize_options(self)
-
-
-
+ def get_package_dir(self, package):
+ res = _build_py.get_package_dir(self, package)
+ if self.distribution.src_root is not None:
+ return os.path.join(self.distribution.src_root, res)
+ return res
def exclude_data_files(self, package, src_dir, files):
diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py
index f128b803..1d500040 100755
--- a/setuptools/command/develop.py
+++ b/setuptools/command/develop.py
@@ -1,9 +1,9 @@
from setuptools.command.easy_install import easy_install
-from distutils.util import convert_path
+from distutils.util import convert_path, subst_vars
from pkg_resources import Distribution, PathMetadata, normalize_path
from distutils import log
-from distutils.errors import *
-import sys, os, setuptools, glob
+from distutils.errors import DistutilsError, DistutilsOptionError
+import os, sys, setuptools, glob
class develop(easy_install):
"""Set up package for development"""
@@ -36,9 +36,6 @@ class develop(easy_install):
-
-
-
def finalize_options(self):
ei = self.get_finalized_command("egg_info")
if ei.broken_egg_info:
@@ -46,8 +43,14 @@ class develop(easy_install):
"Please rename %r to %r before using 'develop'"
% (ei.egg_info, ei.broken_egg_info)
)
- self.args = [ei.egg_name]
+ self.args = [ei.egg_name]
+
+
+
+
easy_install.finalize_options(self)
+ self.expand_basedirs()
+ self.expand_dirs()
# pick up setup-dir .egg files only: no .egg-info
self.package_index.scan(glob.glob('*.egg'))
@@ -62,7 +65,7 @@ class develop(easy_install):
"--egg-path must be a relative path from the install"
" directory to "+target
)
-
+
# Make a distribution for the package's source
self.dist = Distribution(
target,
@@ -81,11 +84,35 @@ class develop(easy_install):
" installation directory", p, normalize_path(os.curdir))
def install_for_development(self):
- # Ensure metadata is up-to-date
- self.run_command('egg_info')
- # Build extensions in-place
- self.reinitialize_command('build_ext', inplace=1)
- self.run_command('build_ext')
+ if sys.version_info >= (3,) and getattr(self.distribution, 'use_2to3', False):
+ # If we run 2to3 we can not do this inplace:
+
+ # Ensure metadata is up-to-date
+ self.reinitialize_command('build_py', inplace=0)
+ self.run_command('build_py')
+ bpy_cmd = self.get_finalized_command("build_py")
+ build_path = normalize_path(bpy_cmd.build_lib)
+
+ # Build extensions
+ self.reinitialize_command('egg_info', egg_base=build_path)
+ self.run_command('egg_info')
+
+ self.reinitialize_command('build_ext', inplace=0)
+ self.run_command('build_ext')
+
+ # Fixup egg-link and easy-install.pth
+ ei_cmd = self.get_finalized_command("egg_info")
+ self.egg_path = build_path
+ self.dist.location = build_path
+ self.dist._provider = PathMetadata(build_path, ei_cmd.egg_info) # XXX
+ else:
+ # Without 2to3 inplace works fine:
+ self.run_command('egg_info')
+
+ # Build extensions in-place
+ self.reinitialize_command('build_ext', inplace=1)
+ self.run_command('build_ext')
+
self.install_site_py() # ensure that target dir is site-safe
if setuptools.bootstrap_install_from:
self.easy_install(setuptools.bootstrap_install_from)
@@ -105,7 +132,9 @@ class develop(easy_install):
def uninstall_link(self):
if os.path.exists(self.egg_link):
log.info("Removing %s (link to %s)", self.egg_link, self.egg_base)
- contents = [line.rstrip() for line in file(self.egg_link)]
+ egg_link_file = open(self.egg_link)
+ contents = [line.rstrip() for line in egg_link_file]
+ egg_link_file.close()
if contents not in ([self.egg_path], [self.egg_path, self.setup_path]):
log.warn("Link points to %s: uninstall aborted", contents)
return
@@ -117,10 +146,6 @@ class develop(easy_install):
# XXX should also check for entry point scripts!
log.warn("Note: you must uninstall or replace scripts manually!")
-
-
-
-
def install_egg_scripts(self, dist):
if dist is not self.dist:
# Installing a dependency, so fall back to normal behavior
@@ -129,7 +154,7 @@ class develop(easy_install):
# create wrapper scripts in the script dir, pointing to dist.scripts
# new-style...
- self.install_wrapper_scripts(dist)
+ self.install_wrapper_scripts(dist)
# ...and old-style
for script_name in self.distribution.scripts or []:
@@ -140,25 +165,3 @@ class develop(easy_install):
f.close()
self.install_script(dist, script_name, script_text, script_path)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
index 71794865..146e1f47 100755
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -7,21 +7,42 @@ A tool for doing automatic download/extract/build of distutils-based Python
packages. For detailed documentation, see the accompanying EasyInstall.txt
file, or visit the `EasyInstall home page`__.
-__ http://peak.telecommunity.com/DevCenter/EasyInstall
+__ http://packages.python.org/setuptools/easy_install.html
+
"""
-import sys, os.path, zipimport, shutil, tempfile, zipfile, re, stat, random
+import sys
+import os
+import zipimport
+import shutil
+import tempfile
+import zipfile
+import re
+import stat
+import random
+import platform
from glob import glob
-from setuptools import Command
+import pkg_resources
+from setuptools import Command, _dont_write_bytecode
from setuptools.sandbox import run_setup
from distutils import log, dir_util
-from distutils.sysconfig import get_python_lib
+from distutils.util import get_platform
+from distutils.util import convert_path, subst_vars
+from distutils.sysconfig import get_python_lib, get_config_vars
from distutils.errors import DistutilsArgError, DistutilsOptionError, \
- DistutilsError
+ DistutilsError, DistutilsPlatformError
+from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS
+from setuptools.command import setopt
from setuptools.archive_util import unpack_archive
-from setuptools.package_index import PackageIndex, parse_bdist_wininst
+from setuptools.package_index import PackageIndex
from setuptools.package_index import URL_SCHEME
from setuptools.command import bdist_egg, egg_info
-from pkg_resources import *
+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
+
sys_executable = os.path.normpath(sys.executable)
__all__ = [
@@ -29,6 +50,13 @@ __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)
@@ -39,6 +67,25 @@ def samefile(p1,p2):
os.path.normpath(os.path.normcase(p2))
)
+if sys.version_info <= (3,):
+ def _to_ascii(s):
+ return s
+ def isascii(s):
+ try:
+ unicode(s, 'ascii')
+ return True
+ except UnicodeError:
+ return False
+else:
+ def _to_ascii(s):
+ return s.encode('ascii')
+ def isascii(s):
+ try:
+ s.encode('ascii')
+ return True
+ except UnicodeError:
+ return False
+
class easy_install(Command):
"""Manage a download/build/install process"""
description = "Find/get/install Python packages"
@@ -71,16 +118,32 @@ class easy_install(Command):
('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"),
+ ('version', None, "print version information and exit"),
+ ('no-find-links', None,
+ "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',
- 'no-deps', 'local-snapshots-ok',
+ 'no-deps', 'local-snapshots-ok', 'version'
]
+
+ if HAS_USER_SITE:
+ user_options.append(('user', None,
+ "install in user site-package '%s'" % site.USER_SITE))
+ boolean_options.append('user')
+
+
negative_opt = {'always-unzip': 'zip-ok'}
create_index = PackageIndex
def initialize_options(self):
+ if HAS_USER_SITE:
+ whereami = os.path.abspath(__file__)
+ self.user = whereami.startswith(site.USER_SITE)
+ else:
+ self.user = 0
+
self.zip_ok = self.local_snapshots_ok = None
self.install_dir = self.script_dir = self.exclude_scripts = None
self.index_url = None
@@ -91,6 +154,22 @@ class easy_install(Command):
self.upgrade = self.always_copy = self.multi_version = None
self.editable = self.no_deps = self.allow_hosts = None
self.root = self.prefix = self.no_report = None
+ self.version = None
+ self.install_purelib = None # for pure module distributions
+ self.install_platlib = None # non-pure (dists w/ extensions)
+ self.install_headers = None # for C/C++ headers
+ self.install_lib = None # set to either purelib or platlib
+ self.install_scripts = None
+ self.install_data = None
+ self.install_base = None
+ self.install_platbase = None
+ if HAS_USER_SITE:
+ self.install_userbase = site.USER_BASE
+ self.install_usersite = site.USER_SITE
+ else:
+ self.install_userbase = None
+ self.install_usersite = None
+ self.no_find_links = None
# Options not specifiable via command line
self.package_index = None
@@ -122,12 +201,56 @@ class easy_install(Command):
os.unlink(filename)
def finalize_options(self):
+ if self.version:
+ print 'setuptools %s' % get_distribution('setuptools').version
+ sys.exit()
+
+ 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', ''),
+ }
+
+ if HAS_USER_SITE:
+ self.config_vars['userbase'] = self.install_userbase
+ self.config_vars['usersite'] = self.install_usersite
+
+ # fix the install_dir if "--user" was used
+ #XXX: duplicate of the code in the setup command
+ if self.user and HAS_USER_SITE:
+ self.create_home_path()
+ if self.install_userbase is None:
+ raise DistutilsPlatformError(
+ "User base directory is not specified")
+ self.install_base = self.install_platbase = self.install_userbase
+ if os.name == 'posix':
+ self.select_scheme("unix_user")
+ else:
+ self.select_scheme(os.name + "_user")
+
+ self.expand_basedirs()
+ self.expand_dirs()
+
self._expand('install_dir','script_dir','build_directory','site_dirs')
# If a non-default installation directory was specified, default the
# script directory to match it.
if self.script_dir is None:
self.script_dir = self.install_dir
+ if self.no_find_links is None:
+ self.no_find_links = False
+
# Let install_dir get set by install_lib command, which in turn
# gets its info from the install command, and takes into account
# --prefix and --home and all that other crud.
@@ -138,6 +261,10 @@ class easy_install(Command):
self.set_undefined_options('install_scripts',
('install_dir', 'script_dir')
)
+
+ if self.user and self.install_purelib:
+ self.install_dir = self.install_purelib
+ self.script_dir = self.install_scripts
# default --record from the install command
self.set_undefined_options('install', ('record', 'record'))
normpath = map(normalize_path, sys.path)
@@ -179,7 +306,8 @@ class easy_install(Command):
self.find_links = []
if self.local_snapshots_ok:
self.package_index.scan_egg_links(self.shadow_path+sys.path)
- self.package_index.add_find_links(self.find_links)
+ if not self.no_find_links:
+ self.package_index.add_find_links(self.find_links)
self.set_undefined_options('install_lib', ('optimize','optimize'))
if not isinstance(self.optimize,int):
try:
@@ -203,8 +331,29 @@ class easy_install(Command):
self.outputs = []
+
+ def _expand_attrs(self, attrs):
+ for attr in attrs:
+ val = getattr(self, attr)
+ if val is not None:
+ if os.name == 'posix' or os.name == 'nt':
+ val = os.path.expanduser(val)
+ val = subst_vars(val, self.config_vars)
+ setattr(self, attr, val)
+
+ def expand_basedirs(self):
+ """Calls `os.path.expanduser` on install_base, install_platbase and
+ root."""
+ self._expand_attrs(['install_base', 'install_platbase', 'root'])
+
+ def expand_dirs(self):
+ """Calls `os.path.expanduser` on install dirs."""
+ self._expand_attrs(['install_purelib', 'install_platlib',
+ 'install_lib', 'install_headers',
+ 'install_scripts', 'install_data',])
+
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:
@@ -246,6 +395,7 @@ class easy_install(Command):
def check_site_dir(self):
"""Verify that self.install_dir is .pth-capable dir, if needed"""
+
instdir = normalize_path(self.install_dir)
pth_file = os.path.join(instdir,'easy-install.pth')
@@ -317,7 +467,7 @@ variable.
For information on other options, you may wish to consult the
documentation at:
- http://peak.telecommunity.com/EasyInstall.html
+ http://packages.python.org/setuptools/easy_install.html
Please make the appropriate changes for your system and try again.
"""
@@ -335,12 +485,15 @@ Please make the appropriate changes for your system and try again.
ok_exists = os.path.exists(ok_file)
try:
if ok_exists: os.unlink(ok_file)
+ dirname = os.path.dirname(ok_file)
+ if not os.path.exists(dirname):
+ os.makedirs(dirname)
f = open(pth_file,'w')
except (OSError,IOError):
self.cant_write_to_target()
else:
try:
- f.write("import os;open(%r,'w').write('OK')\n" % (ok_file,))
+ f.write("import os; f = open(%r, 'w'); f.write('OK'); f.close()\n" % (ok_file,))
f.close(); f=None
executable = sys.executable
if os.name=='nt':
@@ -371,6 +524,10 @@ Please make the appropriate changes for your system and try again.
"""Write all the scripts for `dist`, unless scripts are excluded"""
if not self.exclude_scripts and dist.metadata_isdir('scripts'):
for script_name in dist.metadata_listdir('scripts'):
+ if dist.metadata_isdir('scripts/' + script_name):
+ # The "script" is a directory, likely a Python 3
+ # __pycache__ directory, so skip it.
+ continue
self.install_script(
dist, script_name,
dist.get_metadata('scripts/'+script_name)
@@ -487,6 +644,15 @@ Please make the appropriate changes for your system and try again.
+ 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!
+ scheme = INSTALL_SCHEMES[name]
+ for key in SCHEME_KEYS:
+ attrname = 'install_' + key
+ if getattr(self, attrname) is None:
+ setattr(self, attrname, scheme[key])
+
@@ -497,7 +663,8 @@ Please make the appropriate changes for your system and try again.
self.install_egg_scripts(dist)
self.installed_projects[dist.key] = dist
log.info(self.installation_report(requirement, dist, *info))
- if dist.has_metadata('dependency_links.txt'):
+ if (dist.has_metadata('dependency_links.txt') and
+ not self.no_find_links):
self.package_index.add_find_links(
dist.get_metadata_lines('dependency_links.txt')
)
@@ -577,23 +744,27 @@ Please make the appropriate changes for your system and try again.
spec = str(dist.as_requirement())
is_script = is_python_script(script_text, script_name)
- if is_script and dev_path:
- script_text = get_script_header(script_text) + (
- "# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r\n"
- "__requires__ = %(spec)r\n"
- "from pkg_resources import require; require(%(spec)r)\n"
- "del require\n"
- "__file__ = %(dev_path)r\n"
- "execfile(__file__)\n"
- ) % locals()
- elif is_script:
- script_text = get_script_header(script_text) + (
- "# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r\n"
- "__requires__ = %(spec)r\n"
- "import pkg_resources\n"
- "pkg_resources.run_script(%(spec)r, %(script_name)r)\n"
- ) % locals()
- self.write_script(script_name, script_text, 'b')
+ def get_template(filename):
+ """
+ There are a couple of template scripts in the package. This
+ function loads one of them and prepares it for use.
+
+ These templates use triple-quotes to escape variable
+ substitutions so the scripts get the 2to3 treatment when build
+ on Python 3. The templates cannot use triple-quotes naturally.
+ """
+ raw_bytes = resource_string('setuptools', template_name)
+ template_str = raw_bytes.decode('utf-8')
+ clean_template = template_str.replace('"""', '')
+ return clean_template
+
+ if is_script:
+ template_name = 'script template.py'
+ if dev_path:
+ template_name = template_name.replace('.py', ' (dev).py')
+ script_text = (get_script_header(script_text) +
+ get_template(template_name) % locals())
+ self.write_script(script_name, _to_ascii(script_text), 'b')
def write_script(self, script_name, contents, mode="t", blockers=()):
"""Write an executable file to the scripts directory"""
@@ -603,12 +774,13 @@ Please make the appropriate changes for your system and try again.
target = os.path.join(self.script_dir, script_name)
self.add_output(target)
+ mask = current_umask()
if not self.dry_run:
ensure_directory(target)
f = open(target,"w"+mode)
f.write(contents)
f.close()
- chmod(target,0755)
+ chmod(target, 0777-mask)
@@ -705,7 +877,7 @@ Please make the appropriate changes for your system and try again.
# Create a dummy distribution object until we build the real distro
dist = Distribution(None,
project_name=cfg.get('metadata','name'),
- version=cfg.get('metadata','version'), platform="win32"
+ version=cfg.get('metadata','version'), platform=get_platform()
)
# Convert the .exe to an unpacked egg
@@ -781,7 +953,9 @@ Please make the appropriate changes for your system and try again.
if locals()[name]:
txt = os.path.join(egg_tmp, 'EGG-INFO', name+'.txt')
if not os.path.exists(txt):
- open(txt,'w').write('\n'.join(locals()[name])+'\n')
+ f = open(txt,'w')
+ f.write('\n'.join(locals()[name])+'\n')
+ f.close()
def check_conflicts(self, dist):
"""Verify that there are no conflicting "old-style" packages"""
@@ -922,11 +1096,14 @@ See the setuptools documentation for the "develop" command for more info.
def build_and_install(self, setup_script, setup_base):
args = ['bdist_egg', '--dist-dir']
+
dist_dir = tempfile.mkdtemp(
prefix='egg-dist-tmp-', dir=os.path.dirname(setup_script)
)
try:
+ self._set_fetcher_options(os.path.dirname(setup_script))
args.append(dist_dir)
+
self.run_setup(setup_script, setup_base, args)
all_eggs = Environment([dist_dir])
eggs = []
@@ -941,6 +1118,30 @@ See the setuptools documentation for the "develop" command for more info.
rmtree(dist_dir)
log.set_verbosity(self.verbose) # restore our log verbosity
+ def _set_fetcher_options(self, base):
+ """
+ When easy_install is about to run bdist_egg on a source dist, that
+ source dist might have 'setup_requires' directives, requiring
+ additional fetching. Ensure the fetcher options given to easy_install
+ are available to that command as well.
+ """
+ # find the fetch options from easy_install and write them out
+ # to the setup.cfg file.
+ ei_opts = self.distribution.get_option_dict('easy_install').copy()
+ fetch_directives = (
+ 'find_links', 'site_dirs', 'index_url', 'optimize',
+ 'site_dirs', 'allow_hosts',
+ )
+ fetch_options = {}
+ for key, val in ei_opts.iteritems():
+ if key not in fetch_directives: continue
+ fetch_options[key.replace('_', '-')] = val[1]
+ # create a settings dictionary suitable for `edit_config`
+ settings = dict(easy_install=fetch_options)
+ cfg_filename = os.path.join(base, 'setup.cfg')
+ setopt.edit_config(cfg_filename, settings)
+
+
def update_pth(self,dist):
if self.pth_file is None:
return
@@ -1001,6 +1202,10 @@ See the setuptools documentation for the "develop" command for more info.
chmod(f, mode)
def byte_compile(self, to_compile):
+ if _dont_write_bytecode:
+ self.warn('byte-compiling is disabled, skipping.')
+ return
+
from distutils.util import byte_compile
try:
# try to make the byte compile messages quieter
@@ -1049,7 +1254,7 @@ Here are some of your options for correcting the problem:
* You can set up the installation directory to support ".pth" files by
using one of the approaches described here:
- http://peak.telecommunity.com/EasyInstall.html#custom-installation-locations
+ http://packages.python.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','')
@@ -1076,7 +1281,13 @@ Please make the appropriate changes for your system and try again.""" % (
if os.path.exists(sitepy):
log.debug("Checking existing site.py in %s", self.install_dir)
- current = open(sitepy,'rb').read()
+ f = open(sitepy,'rb')
+ current = f.read()
+ # we want str, not bytes
+ if sys.version_info >= (3,):
+ current = current.decode()
+
+ f.close()
if not current.startswith('def __boot():'):
raise DistutilsError(
"%s is not a setuptools-generated site.py; please"
@@ -1097,7 +1308,15 @@ Please make the appropriate changes for your system and try again.""" % (
-
+ def create_home_path(self):
+ """Create directories under ~."""
+ if not self.user:
+ return
+ home = convert_path(os.path.expanduser("~"))
+ for name, path in self.config_vars.iteritems():
+ if path.startswith(home) and not os.path.isdir(path):
+ self.debug_print("os.makedirs('%s', 0700)" % path)
+ os.makedirs(path, 0700)
@@ -1183,7 +1402,11 @@ def get_site_dirs():
site_lib = get_python_lib(plat_specific)
if site_lib not in sitedirs: sitedirs.append(site_lib)
+ if HAS_USER_SITE:
+ sitedirs.append(site.USER_SITE)
+
sitedirs = map(normalize_path, sitedirs)
+
return sitedirs
@@ -1252,7 +1475,19 @@ def extract_wininst_cfg(dist_filename):
f.seek(prepended-(12+cfglen))
cfg = ConfigParser.RawConfigParser({'version':'','target_version':''})
try:
- cfg.readfp(StringIO.StringIO(f.read(cfglen).split(chr(0),1)[0]))
+ part = f.read(cfglen)
+ # part is in bytes, but we need to read up to the first null
+ # byte.
+ if sys.version_info >= (2,6):
+ null_byte = bytes([0])
+ else:
+ null_byte = chr(0)
+ config = part.split(null_byte, 1)[0]
+ # Now the config is in bytes, but on Python 3, it must be
+ # unicode for the RawConfigParser, so decode it. Is this the
+ # right encoding?
+ config = config.decode('ascii')
+ cfg.readfp(StringIO.StringIO(config))
except ConfigParser.Error:
return None
if not cfg.has_section('metadata') or not cfg.has_section('Setup'):
@@ -1274,8 +1509,9 @@ def get_exe_prefixes(exe_filename):
prefixes = [
('PURELIB/', ''), ('PLATLIB/pywin32_system32', ''),
- ('PLATLIB/', ''), ('DATA/lib/site-packages/', ''),
- ('SCRIPTS/', 'EGG-INFO/scripts/')
+ ('PLATLIB/', ''),
+ ('SCRIPTS/', 'EGG-INFO/scripts/'),
+ ('DATA/lib/site-packages', ''),
]
z = zipfile.ZipFile(exe_filename)
try:
@@ -1291,7 +1527,10 @@ def get_exe_prefixes(exe_filename):
if name.endswith('-nspkg.pth'):
continue
if parts[0].upper() in ('PURELIB','PLATLIB'):
- for pth in yield_lines(z.read(name)):
+ contents = z.read(name)
+ if sys.version_info >= (3,):
+ contents = contents.decode()
+ for pth in yield_lines(contents):
pth = pth.strip().replace('\\','/')
if not pth.startswith('import'):
prefixes.append((('%s/%s/' % (parts[0],pth)), ''))
@@ -1327,7 +1566,8 @@ class PthDistributions(Environment):
saw_import = False
seen = dict.fromkeys(self.sitedirs)
if os.path.isfile(self.filename):
- for line in open(self.filename,'rt'):
+ f = open(self.filename,'rt')
+ for line in f:
if line.startswith('import'):
saw_import = True
continue
@@ -1345,6 +1585,7 @@ class PthDistributions(Environment):
self.dirty = True # we cleaned up, so we're dirty now :)
continue
seen[path] = 1
+ f.close()
if self.paths and not saw_import:
self.dirty = True # ensure anything we touch has import wrappers
@@ -1370,7 +1611,7 @@ class PthDistributions(Environment):
if os.path.islink(self.filename):
os.unlink(self.filename)
- f = open(self.filename,'wb')
+ f = open(self.filename,'wt')
f.write(data); f.close()
elif os.path.exists(self.filename):
@@ -1381,8 +1622,12 @@ class PthDistributions(Environment):
def add(self,dist):
"""Add `dist` to the distribution map"""
- if dist.location not in self.paths and dist.location not in self.sitedirs:
- self.paths.append(dist.location); self.dirty = True
+ if (dist.location not in self.paths and (
+ dist.location not in self.sitedirs or
+ dist.location == os.getcwd() #account for '.' being in PYTHONPATH
+ )):
+ self.paths.append(dist.location)
+ self.dirty = True
Environment.add(self,dist)
def remove(self,dist):
@@ -1410,6 +1655,11 @@ class PthDistributions(Environment):
def get_script_header(script_text, executable=sys_executable, wininst=False):
"""Create a #! line, getting options (if any) from script_text"""
from distutils.command.build_scripts import first_line_re
+
+ # first_line_re in Python >=3.1.4 and >=3.2.1 is a bytes pattern.
+ if not isinstance(first_line_re.pattern, str):
+ first_line_re = re.compile(first_line_re.pattern.decode())
+
first = (script_text+'\n').splitlines()[0]
match = first_line_re.match(first)
options = ''
@@ -1421,7 +1671,7 @@ def get_script_header(script_text, executable=sys_executable, wininst=False):
else:
executable = nt_quote_arg(executable)
hdr = "#!%(executable)s%(options)s\n" % locals()
- if unicode(hdr,'ascii','ignore').encode('ascii') != hdr:
+ if not isascii(hdr):
# Non-ascii path to sys.executable, use -x to prevent warnings
if options:
if options.strip().startswith('-'):
@@ -1543,6 +1793,11 @@ def chmod(path, mode):
def fix_jython_executable(executable, options):
if sys.platform.startswith('java') and is_sh(executable):
+ # Workaround for Jython is not needed on Linux systems.
+ import java
+ if java.lang.System.getProperty("os.name") == "Linux":
+ return executable
+
# Workaround Jython's sys.executable being a .sh (an invalid
# shebang line interpreter)
if options:
@@ -1561,16 +1816,18 @@ def get_script_args(dist, executable=sys_executable, wininst=False):
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():
+ 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"
- "sys.exit(\n"
- " load_entry_point(%(spec)r, %(group)r, %(name)r)()\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
@@ -1582,7 +1839,12 @@ def get_script_args(dist, executable=sys_executable, wininst=False):
ext, launcher = '-script.py', 'cli.exe'
old = ['.py','.pyc','.pyo']
new_header = re.sub('(?i)pythonw.exe','python.exe',header)
-
+ if platform.machine().lower()=='arm':
+ launcher = launcher.replace(".", "-arm.")
+ if is_64bit():
+ launcher = launcher.replace(".", "-64.")
+ else:
+ launcher = launcher.replace(".", "-32.")
if os.path.exists(new_header[2:-1].strip('"')) or sys.platform!='win32':
hdr = new_header
else:
@@ -1590,53 +1852,27 @@ def get_script_args(dist, executable=sys_executable, wininst=False):
yield (name+ext, hdr+script_text, 't', [name+x for x in old])
yield (
name+'.exe', resource_string('setuptools', launcher),
- 'b') # write in binary mode
- yield (name+'.exe.manifest', _launcher_manifest % (name,), 't')
+ '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)
-_launcher_manifest = """
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
- <assemblyIdentity version="1.0.0.0"
- processorArchitecture="X86"
- name="%s.exe"
- type="win32"/>
-
- <!-- Identify the application security requirements. -->
- <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
- <security>
- <requestedPrivileges>
- <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
- </requestedPrivileges>
- </security>
- </trustInfo>
-</assembly>"""
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+def load_launcher_manifest(name):
+ manifest = pkg_resources.resource_string(__name__, 'launcher manifest.xml')
+ if sys.version_info[0] < 3:
+ return manifest % vars()
+ else:
+ return manifest.decode('utf-8') % vars()
def rmtree(path, ignore_errors=False, onerror=auto_chmod):
"""Recursively delete a directory tree.
@@ -1673,12 +1909,16 @@ def rmtree(path, ignore_errors=False, onerror=auto_chmod):
except os.error:
onerror(os.rmdir, path, sys.exc_info())
+def current_umask():
+ tmp = os.umask(022)
+ os.umask(tmp)
+ return tmp
+
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()
-
def main(argv=None, **kw):
from setuptools import setup
from setuptools.dist import Distribution
@@ -1703,6 +1943,7 @@ usage: %(script)s [options] requirement_or_url ...
class DistributionWithoutHelpCommands(Distribution):
common_usage = ""
+
def _show_help(self,*args,**kw):
with_ei_usage(lambda: Distribution._show_help(self,*args,**kw))
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
index 5a8b2db8..cd3ea198 100755
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -3,13 +3,13 @@
Create a distribution's .egg-info directory and contents"""
# This module should be kept compatible with Python 2.3
-import os, re
+import os, re, sys
from setuptools import Command
from distutils.errors import *
from distutils import log
from setuptools.command.sdist import sdist
from distutils.util import convert_path
-from distutils.filelist import FileList
+from distutils.filelist import FileList as _FileList
from pkg_resources import parse_requirements, safe_name, parse_version, \
safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename
from sdist import walk_revctrl
@@ -148,6 +148,8 @@ class egg_info(Command):
to the file.
"""
log.info("writing %s to %s", what, filename)
+ if sys.version_info >= (3,):
+ data = data.encode("utf-8")
if not self.dry_run:
f = open(filename, 'wb')
f.write(data)
@@ -160,7 +162,12 @@ class egg_info(Command):
os.unlink(filename)
def tagged_version(self):
- return safe_version(self.distribution.get_version() + self.vtags)
+ version = self.distribution.get_version()
+ # egg_info may be called more than once for a distribution,
+ # in which case the version string already contains all tags.
+ if self.vtags and version.endswith(self.vtags):
+ return safe_version(version)
+ return safe_version(version + self.vtags)
def run(self):
self.mkpath(self.egg_info)
@@ -229,7 +236,7 @@ class egg_info(Command):
continue
data = map(str.splitlines,data.split('\n\x0c\n'))
- del data[0][0] # get rid of the '8' or '9'
+ del data[0][0] # get rid of the '8' or '9' or '10'
dirurl = data[0][3]
localrev = max([int(d[9]) for d in data if len(d)>9 and d[9]]+[0])
if base==os.curdir:
@@ -267,16 +274,28 @@ class egg_info(Command):
self.broken_egg_info = self.egg_info
self.egg_info = bei # make it work for now
-class FileList(FileList):
+class FileList(_FileList):
"""File list that accepts only existing, platform-independent paths"""
def append(self, item):
if item.endswith('\r'): # Fix older sdists built on Windows
item = item[:-1]
path = convert_path(item)
- if os.path.exists(path):
- self.files.append(path)
+ if sys.version_info >= (3,):
+ try:
+ if os.path.exists(path) or os.path.exists(path.encode('utf-8')):
+ self.files.append(path)
+ except UnicodeEncodeError:
+ # Accept UTF-8 filenames even if LANG=C
+ if os.path.exists(path.encode('utf-8')):
+ self.files.append(path)
+ else:
+ log.warn("'%s' not %s encodable -- skipping", path,
+ sys.getfilesystemencoding())
+ else:
+ if os.path.exists(path):
+ self.files.append(path)
@@ -316,6 +335,18 @@ class manifest_maker(sdist):
by 'add_defaults()' and 'read_template()') to the manifest file
named by 'self.manifest'.
"""
+ # The manifest must be UTF-8 encodable. See #303.
+ if sys.version_info >= (3,):
+ files = []
+ for file in self.filelist.files:
+ try:
+ file.encode("utf-8")
+ except UnicodeEncodeError:
+ log.warn("'%s' not UTF-8 encodable -- skipping" % file)
+ else:
+ files.append(file)
+ self.filelist.files = files
+
files = self.filelist.files
if os.sep!='/':
files = [f.replace(os.sep,'/') for f in files]
@@ -351,8 +382,11 @@ def write_file (filename, contents):
"""Create a file with the specified name and write 'contents' (a
sequence of strings without line terminators) to it.
"""
+ contents = "\n".join(contents)
+ if sys.version_info >= (3,):
+ contents = contents.encode("utf-8")
f = open(filename, "wb") # always write POSIX-style manifest
- f.write("\n".join(contents))
+ f.write(contents)
f.close()
@@ -444,6 +478,7 @@ def get_pkg_info_revision():
match = re.match(r"Version:.*-r(\d+)\s*$", line)
if match:
return int(match.group(1))
+ f.close()
return 0
diff --git a/setuptools/command/install.py b/setuptools/command/install.py
index a150c435..247c4f25 100644
--- a/setuptools/command/install.py
+++ b/setuptools/command/install.py
@@ -18,9 +18,6 @@ class install(_install):
('install_scripts', lambda self: True),
]
_nc = dict(new_commands)
- sub_commands = [
- cmd for cmd in _install.sub_commands if cmd[0] not in _nc
- ] + new_commands
def initialize_options(self):
_install.initialize_options(self)
@@ -104,6 +101,10 @@ class install(_install):
cmd.run()
setuptools.bootstrap_install_from = None
+# XXX Python 3.1 doesn't see _nc if this is inside the class
+install.sub_commands = [
+ cmd for cmd in _install.sub_commands if cmd[0] not in install._nc
+ ] + install.new_commands
diff --git a/setuptools/command/install_egg_info.py b/setuptools/command/install_egg_info.py
index 939340c5..f44b34b5 100755
--- a/setuptools/command/install_egg_info.py
+++ b/setuptools/command/install_egg_info.py
@@ -87,8 +87,10 @@ class install_egg_info(Command):
filename += '-nspkg.pth'; self.outputs.append(filename)
log.info("Installing %s",filename)
if not self.dry_run:
- f = open(filename,'wb')
+ f = open(filename,'wt')
for pkg in nsp:
+ # ensure pkg is not a unicode string under Python 2.7
+ pkg = str(pkg)
pth = tuple(pkg.split('.'))
trailer = '\n'
if '.' in pkg:
@@ -97,12 +99,12 @@ class install_egg_info(Command):
% ('.'.join(pth[:-1]), pth[-1])
)
f.write(
- "import sys,new,os; "
+ "import sys,types,os; "
"p = os.path.join(sys._getframe(1).f_locals['sitedir'], "
"*%(pth)r); "
"ie = os.path.exists(os.path.join(p,'__init__.py')); "
"m = not ie and "
- "sys.modules.setdefault(%(pkg)r,new.module(%(pkg)r)); "
+ "sys.modules.setdefault(%(pkg)r,types.ModuleType(%(pkg)r)); "
"mp = (m or []) and m.__dict__.setdefault('__path__',[]); "
"(p not in mp) and mp.append(p)%(trailer)s"
% locals()
diff --git a/setuptools/command/install_scripts.py b/setuptools/command/install_scripts.py
index c2dc2d59..82456035 100755
--- a/setuptools/command/install_scripts.py
+++ b/setuptools/command/install_scripts.py
@@ -1,6 +1,5 @@
from distutils.command.install_scripts import install_scripts \
as _install_scripts
-from easy_install import get_script_args, sys_executable, chmod
from pkg_resources import Distribution, PathMetadata, ensure_directory
import os
from distutils import log
@@ -11,8 +10,11 @@ class install_scripts(_install_scripts):
def initialize_options(self):
_install_scripts.initialize_options(self)
self.no_ep = False
-
+
def run(self):
+ from setuptools.command.easy_install import get_script_args
+ from setuptools.command.easy_install import sys_executable
+
self.run_command("egg_info")
if self.distribution.scripts:
_install_scripts.run(self) # run first to set up self.outfiles
@@ -20,9 +22,9 @@ class install_scripts(_install_scripts):
self.outfiles = []
if self.no_ep:
# don't install entry point scripts into .egg file!
- return
+ return
- ei_cmd = self.get_finalized_command("egg_info")
+ ei_cmd = self.get_finalized_command("egg_info")
dist = Distribution(
ei_cmd.egg_base, PathMetadata(ei_cmd.egg_base, ei_cmd.egg_info),
ei_cmd.egg_name, ei_cmd.egg_version,
@@ -35,48 +37,18 @@ class install_scripts(_install_scripts):
for args in get_script_args(dist, executable, is_wininst):
self.write_script(*args)
-
-
-
-
def write_script(self, script_name, contents, mode="t", *ignored):
"""Write an executable file to the scripts directory"""
+ from setuptools.command.easy_install import chmod, current_umask
log.info("Installing %s script to %s", script_name, self.install_dir)
target = os.path.join(self.install_dir, script_name)
self.outfiles.append(target)
+ mask = current_umask()
if not self.dry_run:
ensure_directory(target)
f = open(target,"w"+mode)
f.write(contents)
f.close()
- chmod(target,0755)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ chmod(target, 0777-mask)
diff --git a/setuptools/command/launcher manifest.xml b/setuptools/command/launcher manifest.xml
new file mode 100644
index 00000000..844d2264
--- /dev/null
+++ b/setuptools/command/launcher manifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+<assemblyIdentity version="1.0.0.0"
+ processorArchitecture="X86"
+ name="%(name)s"
+ type="win32"/>
+ <!-- Identify the application security requirements. -->
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false"/>
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+</assembly>
diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py
index d84afdb8..f8f964b3 100755
--- a/setuptools/command/sdist.py
+++ b/setuptools/command/sdist.py
@@ -3,6 +3,9 @@ from distutils.util import convert_path
from distutils import log
from glob import glob
import os, re, sys, pkg_resources
+from glob import glob
+
+READMES = ('README', 'README.rst', 'README.txt')
entities = [
("&lt;","<"), ("&gt;", ">"), ("&quot;", '"'), ("&apos;", "'"),
@@ -59,7 +62,7 @@ def _default_revctrl(dirname=''):
def externals_finder(dirname, filename):
"""Find any 'svn:externals' directories"""
found = False
- f = open(filename,'rb')
+ f = open(filename,'rt')
for line in iter(f.readline, ''): # can't use direct iter!
parts = line.split()
if len(parts)==2:
@@ -94,9 +97,10 @@ def entries_finder(dirname, filename):
try: svnver = int(data.splitlines()[0])
except: pass
if svnver<8:
- log.warn("unrecognized .svn/entries format in %s", dirname)
+ log.warn("unrecognized .svn/entries format in %s", os.path.abspath(dirname))
return
for record in map(str.splitlines, data.split('\n\x0c\n')[1:]):
+ # subversion 1.6/1.5/1.4
if not record or len(record)>=6 and record[5]=="delete":
continue # skip deleted
yield joinpath(dirname, record[0])
@@ -143,7 +147,17 @@ class sdist(_sdist):
self.filelist = ei_cmd.filelist
self.filelist.append(os.path.join(ei_cmd.egg_info,'SOURCES.txt'))
self.check_readme()
- self.check_metadata()
+
+ # Run sub commands
+ for cmd_name in self.get_sub_commands():
+ self.run_command(cmd_name)
+
+ # Call check_metadata only if no 'check' command
+ # (distutils <= 2.6)
+ import distutils.command
+ if 'check' not in distutils.command.__all__:
+ self.check_metadata()
+
self.make_distribution()
dist_files = getattr(self.distribution,'dist_files',[])
@@ -152,25 +166,31 @@ class sdist(_sdist):
if data not in dist_files:
dist_files.append(data)
- def read_template(self):
+ def __read_template_hack(self):
+ # This grody hack closes the template file (MANIFEST.in) if an
+ # exception occurs during read_template.
+ # Doing so prevents an error when easy_install attempts to delete the
+ # file.
try:
_sdist.read_template(self)
except:
- # grody hack to close the template file (MANIFEST.in)
- # this prevents easy_install's attempt at deleting the file from
- # dying and thus masking the real error
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]
-
+ # Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle
+ # has been fixed, so only override the method if we're using an earlier
+ # Python.
+ if (
+ sys.version_info < (2,7,2)
+ or (3,0) <= sys.version_info < (3,1,4)
+ or (3,2) <= sys.version_info < (3,2,1)
+ ):
+ read_template = __read_template_hack
+
+ def add_defaults(self):
+ standards = [READMES,
+ self.distribution.script_name]
for fn in standards:
- if type(fn) is tuple:
+ if isinstance(fn, tuple):
alts = fn
got_it = 0
for fn in alts:
@@ -189,15 +209,23 @@ class sdist(_sdist):
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)
+ # getting python files
if self.distribution.has_pure_modules():
build_py = self.get_finalized_command('build_py')
self.filelist.extend(build_py.get_source_files())
+ # This functionality is incompatible with include_package_data, and
+ # will in fact create an infinite recursion if include_package_data
+ # is True. Use of include_package_data will imply that
+ # distutils-style automatic handling of package_data is disabled
+ if not self.distribution.include_package_data:
+ for _, src_dir, _, filenames in build_py.data_files:
+ self.filelist.extend([os.path.join(src_dir, filename)
+ for filename in filenames])
if self.distribution.has_ext_modules():
build_ext = self.get_finalized_command('build_ext')
@@ -211,15 +239,13 @@ class sdist(_sdist):
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:
+ for f in READMES:
if os.path.exists(f):
return
else:
self.warn(
- "standard file not found: should have one of " +', '.join(alts)
+ "standard file not found: should have one of " +', '.join(READMES)
)
@@ -236,7 +262,39 @@ class sdist(_sdist):
self.get_finalized_command('egg_info').save_version_info(dest)
+ def _manifest_is_not_generated(self):
+ # check for special comment used in 2.7.1 and higher
+ if not os.path.isfile(self.manifest):
+ return False
+ fp = open(self.manifest, 'rbU')
+ try:
+ first_line = fp.readline()
+ finally:
+ fp.close()
+ return first_line != '# file GENERATED by distutils, do NOT edit\n'.encode()
+
+ def read_manifest(self):
+ """Read the manifest file (named by 'self.manifest') and use it to
+ fill in 'self.filelist', the list of files to include in the source
+ distribution.
+ """
+ log.info("reading manifest file '%s'", self.manifest)
+ manifest = open(self.manifest, 'rbU')
+ for line in manifest:
+ # The manifest must contain UTF-8. See #303.
+ if sys.version_info >= (3,):
+ try:
+ line = line.decode('UTF-8')
+ except UnicodeDecodeError:
+ log.warn("%r not UTF-8 decodable -- skipping" % line)
+ continue
+ # ignore comments and blank lines
+ line = line.strip()
+ if line.startswith('#') or not line:
+ continue
+ self.filelist.append(line)
+ manifest.close()
diff --git a/setuptools/command/test.py b/setuptools/command/test.py
index db918dae..a02ac142 100644
--- a/setuptools/command/test.py
+++ b/setuptools/command/test.py
@@ -2,6 +2,7 @@ from setuptools import Command
from distutils.errors import DistutilsOptionError
import sys
from pkg_resources import *
+from pkg_resources import _namespace_packages
from unittest import TestLoader, main
class ScanningLoader(TestLoader):
@@ -81,12 +82,28 @@ class test(Command):
def with_project_on_sys_path(self, func):
- # Ensure metadata is up-to-date
- self.run_command('egg_info')
+ if sys.version_info >= (3,) and getattr(self.distribution, 'use_2to3', False):
+ # If we run 2to3 we can not do this inplace:
- # Build extensions in-place
- self.reinitialize_command('build_ext', inplace=1)
- self.run_command('build_ext')
+ # Ensure metadata is up-to-date
+ self.reinitialize_command('build_py', inplace=0)
+ self.run_command('build_py')
+ bpy_cmd = self.get_finalized_command("build_py")
+ build_path = normalize_path(bpy_cmd.build_lib)
+
+ # Build extensions
+ self.reinitialize_command('egg_info', egg_base=build_path)
+ self.run_command('egg_info')
+
+ self.reinitialize_command('build_ext', inplace=0)
+ self.run_command('build_ext')
+ else:
+ # Without 2to3 inplace works fine:
+ self.run_command('egg_info')
+
+ # Build extensions in-place
+ self.reinitialize_command('build_ext', inplace=1)
+ self.run_command('build_ext')
ei_cmd = self.get_finalized_command("egg_info")
@@ -123,11 +140,28 @@ class test(Command):
def run_tests(self):
import unittest
+
+ # Purge modules under test from sys.modules. The test loader will
+ # re-import them from the build location. Required when 2to3 is used
+ # with namespace packages.
+ if sys.version_info >= (3,) and getattr(self.distribution, 'use_2to3', False):
+ module = self.test_args[-1].split('.')[0]
+ if module in _namespace_packages:
+ del_modules = []
+ if module in sys.modules:
+ del_modules.append(module)
+ module += '.'
+ for name in sys.modules:
+ if name.startswith(module):
+ del_modules.append(name)
+ map(sys.modules.__delitem__, del_modules)
+
loader_ep = EntryPoint.parse("x="+self.test_loader)
loader_class = loader_ep.load(require=False)
+ cks = loader_class()
unittest.main(
None, None, [unittest.__file__]+self.test_args,
- testLoader = loader_class()
+ testLoader = cks
)
diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py
index 7ac08c22..21b9615c 100755
--- a/setuptools/command/upload.py
+++ b/setuptools/command/upload.py
@@ -83,14 +83,16 @@ class upload(Command):
dry_run=self.dry_run)
# Fill in the data
- content = open(filename,'rb').read()
+ f = open(filename,'rb')
+ content = f.read()
+ f.close()
basename = os.path.basename(filename)
comment = ''
if command=='bdist_egg' and self.distribution.has_ext_modules():
comment = "built on %s" % platform.platform(terse=1)
data = {
':action':'file_upload',
- 'protcol_version':'1',
+ 'protocol_version':'1',
'name':self.distribution.get_name(),
'version':self.distribution.get_version(),
'content':(basename,content),
@@ -107,8 +109,9 @@ class upload(Command):
data['comment'] = comment
if self.sign:
- data['gpg_signature'] = (os.path.basename(filename) + ".asc",
- open(filename+".asc").read())
+ asc_file = open(filename + ".asc")
+ data['gpg_signature'] = (os.path.basename(filename) + ".asc", asc_file.read())
+ asc_file.close()
# set up the authentication
auth = "Basic " + base64.encodestring(self.username + ":" + self.password).strip()
@@ -179,3 +182,4 @@ class upload(Command):
log.ERROR)
if self.show_response:
print '-'*75, r.read(), '-'*75
+
diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py
new file mode 100644
index 00000000..1d5a7445
--- /dev/null
+++ b/setuptools/command/upload_docs.py
@@ -0,0 +1,195 @@
+# -*- coding: utf-8 -*-
+"""upload_docs
+
+Implements a Distutils 'upload_docs' subcommand (upload documentation to
+PyPI's packages.python.org).
+"""
+
+import os
+import socket
+import zipfile
+import httplib
+import urlparse
+import tempfile
+import sys
+import shutil
+
+from base64 import standard_b64encode
+from pkg_resources import iter_entry_points
+
+from distutils import log
+from distutils.errors import DistutilsOptionError
+
+try:
+ from distutils.command.upload import upload
+except ImportError:
+ from setuptools.command.upload import upload
+
+
+# This is not just a replacement for byte literals
+# but works as a general purpose encoder
+def b(s, encoding='utf-8'):
+ if isinstance(s, unicode):
+ return s.encode(encoding)
+ return s
+
+
+class upload_docs(upload):
+
+ description = 'Upload documentation to PyPI'
+
+ user_options = [
+ ('repository=', 'r',
+ "url of repository [default: %s]" % upload.DEFAULT_REPOSITORY),
+ ('show-response', None,
+ 'display full response text from server'),
+ ('upload-dir=', None, 'directory to upload'),
+ ]
+ boolean_options = upload.boolean_options
+
+ def has_sphinx(self):
+ if self.upload_dir is None:
+ for ep in iter_entry_points('distutils.commands', 'build_sphinx'):
+ return True
+
+ sub_commands = [('build_sphinx', has_sphinx)]
+
+ def initialize_options(self):
+ upload.initialize_options(self)
+ self.upload_dir = None
+ self.target_dir = None
+
+ def finalize_options(self):
+ upload.finalize_options(self)
+ if self.upload_dir is None:
+ if self.has_sphinx():
+ build_sphinx = self.get_finalized_command('build_sphinx')
+ self.target_dir = build_sphinx.builder_target_dir
+ else:
+ build = self.get_finalized_command('build')
+ self.target_dir = os.path.join(build.build_base, 'docs')
+ else:
+ self.ensure_dirname('upload_dir')
+ self.target_dir = self.upload_dir
+ self.announce('Using upload directory %s' % self.target_dir)
+
+ def create_zipfile(self, filename):
+ zip_file = zipfile.ZipFile(filename, "w")
+ try:
+ self.mkpath(self.target_dir) # just in case
+ for root, dirs, files in os.walk(self.target_dir):
+ if root == self.target_dir and not files:
+ raise DistutilsOptionError(
+ "no files found in upload directory '%s'"
+ % self.target_dir)
+ for name in files:
+ full = os.path.join(root, name)
+ relative = root[len(self.target_dir):].lstrip(os.path.sep)
+ dest = os.path.join(relative, name)
+ zip_file.write(full, dest)
+ finally:
+ zip_file.close()
+
+ def run(self):
+ # Run sub commands
+ for cmd_name in self.get_sub_commands():
+ self.run_command(cmd_name)
+
+ tmp_dir = tempfile.mkdtemp()
+ name = self.distribution.metadata.get_name()
+ zip_file = os.path.join(tmp_dir, "%s.zip" % name)
+ try:
+ self.create_zipfile(zip_file)
+ self.upload_file(zip_file)
+ finally:
+ shutil.rmtree(tmp_dir)
+
+ def upload_file(self, filename):
+ f = open(filename, 'rb')
+ content = f.read()
+ f.close()
+ meta = self.distribution.metadata
+ data = {
+ ':action': 'doc_upload',
+ 'name': meta.get_name(),
+ 'content': (os.path.basename(filename), content),
+ }
+ # set up the authentication
+ credentials = b(self.username + ':' + self.password)
+ credentials = standard_b64encode(credentials)
+ if sys.version_info >= (3,):
+ credentials = credentials.decode('ascii')
+ auth = "Basic " + credentials
+
+ # Build up the MIME payload for the POST data
+ boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
+ sep_boundary = b('\n--') + b(boundary)
+ end_boundary = sep_boundary + b('--')
+ body = []
+ for key, values in data.iteritems():
+ title = '\nContent-Disposition: form-data; name="%s"' % key
+ # handle multiple entries for the same name
+ if type(values) != type([]):
+ values = [values]
+ for value in values:
+ if type(value) is tuple:
+ title += '; filename="%s"' % value[0]
+ value = value[1]
+ else:
+ value = b(value)
+ body.append(sep_boundary)
+ body.append(b(title))
+ body.append(b("\n\n"))
+ body.append(value)
+ if value and value[-1:] == b('\r'):
+ body.append(b('\n')) # write an extra newline (lurve Macs)
+ body.append(end_boundary)
+ body.append(b("\n"))
+ body = b('').join(body)
+
+ self.announce("Submitting documentation to %s" % (self.repository),
+ log.INFO)
+
+ # build the Request
+ # We can't use urllib2 since we need to send the Basic
+ # auth right with the first request
+ schema, netloc, url, params, query, fragments = \
+ urlparse.urlparse(self.repository)
+ assert not params and not query and not fragments
+ if schema == 'http':
+ conn = httplib.HTTPConnection(netloc)
+ elif schema == 'https':
+ conn = httplib.HTTPSConnection(netloc)
+ else:
+ raise AssertionError("unsupported schema "+schema)
+
+ data = ''
+ loglevel = log.INFO
+ try:
+ conn.connect()
+ conn.putrequest("POST", url)
+ conn.putheader('Content-type',
+ 'multipart/form-data; boundary=%s'%boundary)
+ conn.putheader('Content-length', str(len(body)))
+ conn.putheader('Authorization', auth)
+ conn.endheaders()
+ conn.send(body)
+ except socket.error, e:
+ self.announce(str(e), log.ERROR)
+ return
+
+ r = conn.getresponse()
+ if r.status == 200:
+ self.announce('Server response (%s): %s' % (r.status, r.reason),
+ log.INFO)
+ elif r.status == 301:
+ location = r.getheader('Location')
+ if location is None:
+ location = 'http://packages.python.org/%s/' % meta.get_name()
+ self.announce('Upload successful. Visit %s' % location,
+ log.INFO)
+ else:
+ self.announce('Upload failed (%s): %s' % (r.status, r.reason),
+ log.ERROR)
+ if self.show_response:
+ print '-'*75, r.read(), '-'*75