Commit c8ebbe36 authored by PJ Eby's avatar PJ Eby

Added ``--site-dirs`` option to allow adding custom "site" directories.

Made ``easy-install.pth`` work in platform-specific alternate site
directories (e.g. ``~/Library/Python/2.x/site-packages``).

--HG--
branch : setuptools
extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041131
parent 8fffda12
...@@ -575,6 +575,31 @@ Command-Line Options ...@@ -575,6 +575,31 @@ Command-Line Options
is included for compatibility with tools that expect to pass this option is included for compatibility with tools that expect to pass this option
to "setup.py install". to "setup.py install".
``--site-dirs=DIRLIST, -S DIRLIST`` (New in 0.6a1)
Specify one or more custom "site" directories (separated by commas).
"Site" directories are directories where ``.pth`` files are processed, such
as the main Python ``site-packages`` directory. By default, EasyInstall
only knows about Python-defined "site" directories, not those that may be
added by an OS distribution or site administrator calling
``site.addsitedir()``. You should not normally need to use this option
directly, as your system administrator should configure it in the
``distutils.cfg`` file of the Python installation. For example, if the
administrator wants to make each user's ``~/lib/python2.3`` directory be a
"site" directory, he or she should create an ``altinstall.pth`` file in the
normal site-packages directory, containing this::
import os; addsitedir(os.path.expanduser('~/lib/python2.3'))
and a ``distutils.cfg`` file inside the ``distutils`` package directory,
containing this::
[easy_install]
site_dirs = ~/lib/python23
This will ensure that EasyInstall knows about the additional "site"
directory, thereby allowing individual users to install their own
Python packages via EasyInstall.
Release Notes/Change History Release Notes/Change History
============================ ============================
...@@ -593,6 +618,11 @@ Known Issues ...@@ -593,6 +618,11 @@ Known Issues
in Exemaker. So, don't use Exemaker to wrap ``easy_install.py``, or at any in Exemaker. So, don't use Exemaker to wrap ``easy_install.py``, or at any
rate don't expect it to work with all packages. rate don't expect it to work with all packages.
0.6a1
* Added ``--site-dirs`` option to allow adding custom "site" directories.
Made ``easy-install.pth`` work in platform-specific alternate site
directories (e.g. ``~/Library/Python/2.x/site-packages``).
0.5a12 0.5a12
* Fix ``python -m easy_install`` not working due to setuptools being installed * Fix ``python -m easy_install`` not working due to setuptools being installed
as a zipfile. Update safety scanner to check for modules that might be used as a zipfile. Update safety scanner to check for modules that might be used
......
...@@ -67,7 +67,8 @@ class easy_install(Command): ...@@ -67,7 +67,8 @@ class easy_install(Command):
"-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"),
('record=', None, ('record=', None,
"filename in which to record list of installed files"), "filename in which to record list of installed files"),
('always-unzip', 'Z', "don't install as a zipfile, no matter what") ('always-unzip', 'Z', "don't install as a zipfile, no matter what"),
('site-dirs=','S',"list of directories where .pth files work"),
] ]
boolean_options = [ boolean_options = [
...@@ -79,7 +80,6 @@ class easy_install(Command): ...@@ -79,7 +80,6 @@ class easy_install(Command):
create_index = PackageIndex create_index = PackageIndex
def initialize_options(self): def initialize_options(self):
self.zip_ok = None self.zip_ok = None
self.install_dir = self.script_dir = self.exclude_scripts = None self.install_dir = self.script_dir = self.exclude_scripts = None
...@@ -94,7 +94,7 @@ class easy_install(Command): ...@@ -94,7 +94,7 @@ class easy_install(Command):
self.pth_file = None self.pth_file = None
self.delete_conflicting = None self.delete_conflicting = None
self.ignore_conflicts_at_my_risk = None self.ignore_conflicts_at_my_risk = None
self.site_dirs = None
def delete_blockers(self, blockers): def delete_blockers(self, blockers):
for filename in blockers: for filename in blockers:
...@@ -139,17 +139,28 @@ class easy_install(Command): ...@@ -139,17 +139,28 @@ class easy_install(Command):
) )
# default --record from the install command # default --record from the install command
self.set_undefined_options('install', ('record', 'record')) self.set_undefined_options('install', ('record', 'record'))
normpath = map(normalize,sys.path)
self.all_site_dirs = get_site_dirs()
if self.site_dirs is not None:
site_dirs = [
os.path.expanduser(s.strip()) for s in self.site_dirs.split(',')
]
for d in site_dirs:
if not os.path.isdir(d):
log.warn("%s (in --site-dirs) does not exist", d)
elif normalize(d) not in normpath:
raise DistutilsOptionError(
d+" (in --site-dirs) is not on sys.path"
)
else:
self.all_site_dirs.append(normalize(d))
site_packages = get_python_lib() instdir = self.install_dir or self.all_site_dirs[-1]
instdir = self.install_dir if normalize(instdir) in self.all_site_dirs:
if instdir is None or samefile(site_packages,instdir):
instdir = site_packages
if self.pth_file is None: if self.pth_file is None:
self.pth_file = PthDistributions( self.pth_file = PthDistributions(
os.path.join(instdir,'easy-install.pth') os.path.join(instdir,'easy-install.pth')
) )
self.install_dir = instdir
elif self.multi_version is None: elif self.multi_version is None:
self.multi_version = True self.multi_version = True
...@@ -157,15 +168,15 @@ class easy_install(Command): ...@@ -157,15 +168,15 @@ class easy_install(Command):
elif not self.multi_version: elif not self.multi_version:
# explicit false set from Python code; raise an error # explicit false set from Python code; raise an error
raise DistutilsArgError( raise DistutilsArgError(
"Can't do single-version installs outside site-packages" "Can't do single-version installs outside 'site-package' dirs"
) )
self.install_dir = normalize(instdir)
self.index_url = self.index_url or "http://www.python.org/pypi" self.index_url = self.index_url or "http://www.python.org/pypi"
self.shadow_path = map(normalize,sys.path)
self.shadow_path = sys.path[:]
for path_item in self.install_dir, self.script_dir: for path_item in self.install_dir, self.script_dir:
if path_item not in self.shadow_path: if normalize(path_item) not in self.shadow_path:
self.shadow_path.insert(0, self.install_dir) self.shadow_path.insert(0, normalize(path_item))
if self.package_index is None: if self.package_index is None:
self.package_index = self.create_index( self.package_index = self.create_index(
self.index_url, search_path = self.shadow_path self.index_url, search_path = self.shadow_path
...@@ -179,7 +190,6 @@ class easy_install(Command): ...@@ -179,7 +190,6 @@ class easy_install(Command):
self.find_links = [] self.find_links = []
self.set_undefined_options('install_lib', ('optimize','optimize')) self.set_undefined_options('install_lib', ('optimize','optimize'))
if not isinstance(self.optimize,int): if not isinstance(self.optimize,int):
try: try:
self.optimize = int(self.optimize) self.optimize = int(self.optimize)
...@@ -192,6 +202,7 @@ class easy_install(Command): ...@@ -192,6 +202,7 @@ class easy_install(Command):
"Can't use both --delete-conflicting and " "Can't use both --delete-conflicting and "
"--ignore-conflicts-at-my-risk at the same time" "--ignore-conflicts-at-my-risk at the same time"
) )
if not self.args: if not self.args:
raise DistutilsArgError( raise DistutilsArgError(
"No urls, filenames, or requirements specified (see --help)") "No urls, filenames, or requirements specified (see --help)")
...@@ -203,6 +214,7 @@ class easy_install(Command): ...@@ -203,6 +214,7 @@ class easy_install(Command):
self.outputs = [] self.outputs = []
def alloc_tmp(self): def alloc_tmp(self):
if self.build_directory is None: if self.build_directory is None:
return tempfile.mkdtemp(prefix="easy_install-") return tempfile.mkdtemp(prefix="easy_install-")
...@@ -231,6 +243,7 @@ class easy_install(Command): ...@@ -231,6 +243,7 @@ class easy_install(Command):
log.set_verbosity(self.distribution.verbose) log.set_verbosity(self.distribution.verbose)
def add_output(self, path): def add_output(self, path):
if os.path.isdir(path): if os.path.isdir(path):
for base, dirs, files in os.walk(path): for base, dirs, files in os.walk(path):
...@@ -244,6 +257,34 @@ class easy_install(Command): ...@@ -244,6 +257,34 @@ class easy_install(Command):
def easy_install(self, spec): def easy_install(self, spec):
tmpdir = self.alloc_tmp() tmpdir = self.alloc_tmp()
download = None download = None
...@@ -591,7 +632,7 @@ class easy_install(Command): ...@@ -591,7 +632,7 @@ class easy_install(Command):
for ext,mode,typ in get_suffixes(): for ext,mode,typ in get_suffixes():
exts[ext] = 1 exts[ext] = 1
for path,files in expand_paths([self.install_dir, get_python_lib()]): for path,files in expand_paths([self.install_dir]+self.all_site_dirs):
for filename in files: for filename in files:
base,ext = os.path.splitext(filename) base,ext = os.path.splitext(filename)
if base in names: if base in names:
...@@ -670,7 +711,7 @@ similar to one of these examples, in order to select the desired version: ...@@ -670,7 +711,7 @@ similar to one of these examples, in order to select the desired version:
pkg_resources.require("%(name)s==%(version)s") # this exact version pkg_resources.require("%(name)s==%(version)s") # this exact version
pkg_resources.require("%(name)s>=%(version)s") # this version or higher pkg_resources.require("%(name)s>=%(version)s") # this version or higher
""" """
if not samefile(get_python_lib(),self.install_dir): if self.install_dir not in map(normalize,sys.path):
msg += """ msg += """
Note also that the installation directory must be on sys.path at runtime for Note also that the installation directory must be on sys.path at runtime for
...@@ -741,14 +782,14 @@ PYTHONPATH, or by being added to sys.path by your code.) ...@@ -741,14 +782,14 @@ PYTHONPATH, or by being added to sys.path by your code.)
return return
for d in self.pth_file.get(dist.key,()): # drop old entries for d in self.pth_file.get(dist.key,()): # drop old entries
if self.multi_version or d.path != dist.path: if self.multi_version or normalize(d.path) != normalize(dist.path):
log.info("Removing %s from easy-install.pth file", d) log.info("Removing %s from easy-install.pth file", d)
self.pth_file.remove(d) self.pth_file.remove(d)
if d.path in self.shadow_path: if normalize(d.path) in self.shadow_path:
self.shadow_path.remove(d.path) self.shadow_path.remove(d.path)
if not self.multi_version: if not self.multi_version:
if dist.path in self.pth_file.paths: if normalize(dist.path) in map(normalize,self.pth_file.paths):
log.info( log.info(
"%s is already the active version in easy-install.pth", "%s is already the active version in easy-install.pth",
dist dist
...@@ -756,8 +797,8 @@ PYTHONPATH, or by being added to sys.path by your code.) ...@@ -756,8 +797,8 @@ PYTHONPATH, or by being added to sys.path by your code.)
else: else:
log.info("Adding %s to easy-install.pth file", dist) log.info("Adding %s to easy-install.pth file", dist)
self.pth_file.add(dist) # add new entry self.pth_file.add(dist) # add new entry
if dist.path not in self.shadow_path: if normalize(dist.path) not in self.shadow_path:
self.shadow_path.append(dist.path) self.shadow_path.append(normalize(dist.path))
self.pth_file.save() self.pth_file.save()
...@@ -818,12 +859,54 @@ PYTHONPATH, or by being added to sys.path by your code.) ...@@ -818,12 +859,54 @@ PYTHONPATH, or by being added to sys.path by your code.)
def get_site_dirs():
# return a list of 'site' dirs, based on 'site' module's code to do this
sitedirs = []
prefixes = [sys.prefix]
if sys.exec_prefix != sys.prefix:
prefixes.append(sys.exec_prefix)
for prefix in prefixes:
if prefix:
if sys.platform in ('os2emx', 'riscos'):
sitedirs.append(os.path.join(prefix, "Lib", "site-packages"))
elif os.sep == '/':
sitedirs.extend([os.path.join(prefix,
"lib",
"python" + sys.version[:3],
"site-packages"),
os.path.join(prefix, "lib", "site-python")])
else:
sitedirs.extend(
[prefix, os.path.join(prefix, "lib", "site-packages")]
)
if sys.platform == 'darwin':
# for framework builds *only* we add the standard Apple
# locations. Currently only per-user, but /Library and
# /Network/Library could be added too
if 'Python.framework' in prefix:
home = os.environ.get('HOME')
if home:
sitedirs.append(
os.path.join(home,
'Library',
'Python',
sys.version[:3],
'site-packages'))
sitedirs = filter(os.path.isdir, sitedirs)
sitedirs = map(normalize, sitedirs)
return sitedirs or [normalize(get_python_lib())] # ensure at least one
def expand_paths(inputs): def expand_paths(inputs):
"""Yield sys.path directories that might contain "old-style" packages""" """Yield sys.path directories that might contain "old-style" packages"""
seen = {} seen = {}
for dirname in inputs: for dirname in inputs:
dirname = normalize(dirname)
if dirname in seen: if dirname in seen:
continue continue
...@@ -850,7 +933,7 @@ def expand_paths(inputs): ...@@ -850,7 +933,7 @@ def expand_paths(inputs):
# Yield existing non-dupe, non-import directory lines from it # Yield existing non-dupe, non-import directory lines from it
for line in lines: for line in lines:
if not line.startswith("import"): if not line.startswith("import"):
line = line.rstrip() line = normalize(line.rstrip())
if line not in seen: if line not in seen:
seen[line] = 1 seen[line] = 1
if not os.path.isdir(line): if not os.path.isdir(line):
...@@ -858,7 +941,6 @@ def expand_paths(inputs): ...@@ -858,7 +941,6 @@ def expand_paths(inputs):
yield line, os.listdir(line) yield line, os.listdir(line)
def extract_wininst_cfg(dist_filename): def extract_wininst_cfg(dist_filename):
"""Extract configuration data from a bdist_wininst .exe """Extract configuration data from a bdist_wininst .exe
...@@ -987,8 +1069,8 @@ def main(argv, **kw): ...@@ -987,8 +1069,8 @@ def main(argv, **kw):
setup(script_args = ['-q','easy_install', '-v']+argv, **kw) setup(script_args = ['-q','easy_install', '-v']+argv, **kw)
def normalize(path):
return os.path.normcase(os.path.realpath(path))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment