aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xEasyInstall.txt11
-rw-r--r--pkg_resources.py18
-rw-r--r--setuptools/command/__init__.py2
-rwxr-xr-xsetuptools/command/develop.py164
-rwxr-xr-xsetuptools/command/easy_install.py129
-rw-r--r--setuptools/command/test.py4
6 files changed, 270 insertions, 58 deletions
diff --git a/EasyInstall.txt b/EasyInstall.txt
index b1aecf6f..f23a23b8 100755
--- a/EasyInstall.txt
+++ b/EasyInstall.txt
@@ -482,10 +482,17 @@ Known Issues
time out or be missing a file.
0.5a5
+ * Added ``develop`` command to ``setuptools``-based packages. This command
+ installs an ``.egg-link`` pointing to the package's source directory, and
+ script wrappers that ``execfile()`` the source versions of the package's
+ scripts. This lets you put your development checkout(s) on sys.path without
+ having to actually install them. (To uninstall the link, use
+ use ``setup.py develop --uninstall``.)
+
* Added ``egg_info`` command to ``setuptools``-based packages. This command
just creates or updates the "projectname.egg-info" directory, without
- building an egg. It's used by the ``bdist_egg`` and ``test`` commands now,
- and will be used by the ``develop`` command later on.
+ building an egg. (It's used by the ``bdist_egg``, ``test``, and ``develop``
+ commands.)
* Enhanced the ``test`` command so that it doesn't install the package, but
instead builds any C extensions in-place, updates the ``.egg-info``
diff --git a/pkg_resources.py b/pkg_resources.py
index 7aba57ce..7b047c19 100644
--- a/pkg_resources.py
+++ b/pkg_resources.py
@@ -984,10 +984,8 @@ def StringIO(*args, **kw):
def find_nothing(importer,path_item):
return ()
-
register_finder(object,find_nothing)
-
def find_on_path(importer,path_item):
"""Yield distributions accessible on a sys.path directory"""
if not os.path.exists(path_item):
@@ -1004,25 +1002,27 @@ def find_on_path(importer,path_item):
# scan for .egg and .egg-info in directory
for entry in os.listdir(path_item):
fullpath = os.path.join(path_item, entry)
- if entry.lower().endswith('.egg'):
+ lower = entry.lower()
+ if lower.endswith('.egg'):
for dist in find_on_path(importer,fullpath):
yield dist
- elif entry.lower().endswith('.egg-info'):
+ elif lower.endswith('.egg-info'):
if os.path.isdir(fullpath):
# development egg
metadata = PathMetadata(path_item, fullpath)
dist_name = os.path.splitext(entry)[0]
yield Distribution(path_item,metadata,name=dist_name)
- elif path_item.lower().endswith('.egg'):
- # packed egg
+ elif lower.endswith('.egg-link'):
+ for line in file(fullpath):
+ if not line.strip(): continue
+ for item in find_distributions(line.rstrip()):
+ yield item
+ elif path_item.lower().endswith('.egg'): # packed egg
metadata = EggMetadata(zipimport.zipimporter(path_item))
yield Distribution.from_filename(path_item, metadata=metadata)
register_finder(ImpWrapper,find_on_path)
-
-
-
_namespace_handlers = {}
_namespace_packages = {}
diff --git a/setuptools/command/__init__.py b/setuptools/command/__init__.py
index f9c5b19b..bc5bc878 100644
--- a/setuptools/command/__init__.py
+++ b/setuptools/command/__init__.py
@@ -1,6 +1,6 @@
import distutils.command
-__all__ = ['test', 'depends', 'bdist_egg']
+__all__ = ['test', 'develop', 'bdist_egg']
# Make our commands available as though they were part of the distutils
diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py
new file mode 100755
index 00000000..fd9f2dfd
--- /dev/null
+++ b/setuptools/command/develop.py
@@ -0,0 +1,164 @@
+from setuptools.command.easy_install import easy_install
+from distutils.util import convert_path
+from pkg_resources import Distribution, PathMetadata
+from distutils import log
+import sys, os
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class develop(easy_install):
+ """Set up package for development"""
+
+ description = "install package in 'development mode'"
+
+ user_options = [
+ ("install-dir=", "d", "link package from DIR"),
+ ("script-dir=", "s", "create script wrappers in DIR"),
+ ("multi-version", "m", "make apps have to require() a version"),
+ ("exclude-scripts", "x", "Don't install scripts"),
+ ("always-copy", "a", "Copy all needed dependencies to install dir"),
+ ("uninstall", "u", "Uninstall this source package"),
+ ]
+
+ boolean_options = [
+ 'multi-version', 'exclude-scripts', 'always-copy', 'uninstall'
+ ]
+
+ command_consumes_arguments = False # override base
+
+ def initialize_options(self):
+ self.uninstall = None
+ easy_install.initialize_options(self)
+
+ def finalize_options(self):
+ ei = self.get_finalized_command("egg_info")
+ self.args = [ei.egg_name]
+ easy_install.finalize_options(self)
+ self.egg_link = os.path.join(self.install_dir, ei.egg_name+'.egg-link')
+ self.egg_base = ei.egg_base
+ self.egg_path = os.path.abspath(ei.egg_base)
+
+ # Make a distribution for the package's source
+ self.dist = Distribution(
+ self.egg_path,
+ PathMetadata(self.egg_path, os.path.abspath(ei.egg_info)),
+ name = ei.egg_name
+ )
+
+
+
+ def run(self):
+ if self.uninstall:
+ self.multi_version = True
+ self.uninstall_link()
+ else:
+ self.install_for_development()
+
+ def install_for_development(self):
+ # Ensure metadata is up-to-date
+ self.run_command('egg_info')
+ ei = self.get_finalized_command("egg_info")
+
+ # Build extensions in-place
+ self.reinitialize_command('build_ext', inplace=1)
+ self.run_command('build_ext')
+
+
+ # create an .egg-link in the installation dir, pointing to our egg
+ log.info("Creating %s (link to %s)", self.egg_link, self.egg_base)
+ if not self.dry_run:
+ f = open(self.egg_link,"w")
+ f.write(self.egg_path)
+ f.close()
+
+ # postprocess the installed distro, fixing up .pth, installing scripts,
+ # and handling requirements
+ self.process_distribution(None, self.dist)
+
+ 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)]
+ if contents != [self.egg_path]:
+ log.warn("Link points to %s: uninstall aborted", contents)
+ return
+ if not self.dry_run:
+ os.unlink(self.egg_link)
+ self.update_pth(self.dist) # remove any .pth link to us
+ if self.distribution.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
+ return easy_install.install_egg_scripts(self,dist)
+
+ # create wrapper scripts in the script dir, pointing to dist.scripts
+ for script_name in self.distribution.scripts or []:
+ script_path = os.path.abspath(convert_path(script_name))
+ script_name = os.path.basename(script_path)
+ f = open(script_path,'rU')
+ script_text = f.read()
+ 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 145b7395..62b5cd70 100755
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -126,7 +126,7 @@ class easy_install(Command):
for path_item in self.install_dir, self.script_dir:
if path_item not in self.shadow_path:
self.shadow_path.insert(0, self.install_dir)
-
+
if self.package_index is None:
self.package_index = self.create_index(
self.index_url, search_path = self.shadow_path
@@ -207,12 +207,12 @@ class easy_install(Command):
tmpdir = self.alloc_tmp()
download = None
- try:
+ try:
if not isinstance(spec,Requirement):
if URL_SCHEME(spec):
# It's a url, download it to tmpdir and process
download = self.package_index.download(spec, tmpdir)
- return self.install_item(None, download, tmpdir, True)
+ return self.install_item(None, download, tmpdir, True)
elif os.path.exists(spec):
# Existing file or directory, just process it directly
@@ -290,40 +290,81 @@ class easy_install(Command):
if self.exclude_scripts or not metadata.metadata_isdir('scripts'):
return
+ for script_name in metadata.metadata_listdir('scripts'):
+ self.install_script(
+ dist, script_name,
+ metadata.get_metadata('scripts/'+script_name).replace('\r','\n')
+ )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ def install_script(self, dist, script_name, script_text, dev_path=None):
+ log.info("Installing %s script to %s", script_name,self.script_dir)
+ target = os.path.join(self.script_dir, script_name)
+ first, rest = script_text.split('\n',1)
from distutils.command.build_scripts import first_line_re
+ match = first_line_re.match(first)
+ options = ''
+ if match:
+ options = match.group(1) or ''
+ if options:
+ options = ' '+options
+ spec = '%s==%s' % (dist.name,dist.version)
+ executable = os.path.normpath(sys.executable)
+
+ if dev_path:
+ script_text = (
+ "#!%(executable)s%(options)s\n"
+ "# EASY-INSTALL-DEV-SCRIPT: %(spec)r,%(script_name)r\n"
+ "from pkg_resources import require; require(%(spec)r)\n"
+ "del require\n"
+ "__file__ = %(dev_path)r\n"
+ "execfile(__file__)\n"
+ ) % locals()
+ else:
+ script_text = (
+ "#!%(executable)s%(options)s\n"
+ "# EASY-INSTALL-SCRIPT: %(spec)r,%(script_name)r\n"
+ "import pkg_resources\n"
+ "pkg_resources.run_main(%(spec)r, %(script_name)r)\n"
+ ) % locals()
- for script_name in metadata.metadata_listdir('scripts'):
- target = os.path.join(self.script_dir, script_name)
-
- log.info("Installing %s script to %s", script_name,self.script_dir)
-
- script_text = metadata.get_metadata('scripts/'+script_name)
- script_text = script_text.replace('\r','\n')
- first, rest = script_text.split('\n',1)
-
- match = first_line_re.match(first)
- options = ''
- if match:
- options = match.group(1) or ''
- if options:
- options = ' '+options
-
- spec = '%s==%s' % (dist.name,dist.version)
-
- script_text = '\n'.join([
- "#!%s%s" % (os.path.normpath(sys.executable),options),
- "# EASY-INSTALL-SCRIPT: %r,%r" % (spec, script_name),
- "import pkg_resources",
- "pkg_resources.run_main(%r, %r)" % (spec, script_name)
- ])
- if not self.dry_run:
- f = open(target,"w")
- f.write(script_text)
- f.close()
- try:
- os.chmod(target,0755)
- except (AttributeError, os.error):
- pass
+ if not self.dry_run:
+ f = open(target,"w")
+ f.write(script_text)
+ f.close()
+ try:
+ os.chmod(target,0755)
+ except (AttributeError, os.error):
+ pass
def install_eggs(self, dist_filename, zip_ok, tmpdir):
@@ -373,7 +414,7 @@ class easy_install(Command):
else:
metadata = EggMetadata(zipimport.zipimporter(egg_path))
return Distribution.from_filename(egg_path,metadata=metadata)
-
+
def install_egg(self, egg_path, zip_ok, tmpdir):
destination = os.path.join(self.install_dir,os.path.basename(egg_path))
destination = os.path.abspath(destination)
@@ -443,7 +484,7 @@ class easy_install(Command):
verbose=self.verbose, dry_run=self.dry_run
)
- # install the .egg
+ # install the .egg
return self.install_egg(egg_path, self.zip_ok, tmpdir)
@@ -474,7 +515,7 @@ class easy_install(Command):
# extract, tracking .pyd/.dll->native_libs and .py -> to_compile
unpack_archive(dist_filename, egg_tmp, process)
-
+
for res in native_libs:
if res.lower().endswith('.pyd'): # create stubs for .pyd's
parts = res.split('/')
@@ -482,9 +523,9 @@ class easy_install(Command):
pyfile = os.path.join(egg_tmp, *parts)
to_compile.append(pyfile)
bdist_egg.write_stub(resource, pyfile)
-
+
self.byte_compile(to_compile) # compile .py's
-
+
if native_libs: # write EGG-INFO/native_libs.txt
nl_txt = os.path.join(egg_tmp, 'EGG-INFO', 'native_libs.txt')
ensure_directory(nl_txt)
@@ -599,6 +640,7 @@ PYTHONPATH, or by being added to sys.path by your code.)
if dist.name=='setuptools':
# Ensure that setuptools itself never becomes unavailable!
+ # XXX should this check for latest version?
f = open(os.path.join(self.install_dir,'setuptools.pth'), 'w')
f.write(dist.path+'\n')
f.close()
@@ -612,7 +654,6 @@ PYTHONPATH, or by being added to sys.path by your code.)
-
def unpack_and_compile(self, egg_path, destination):
to_compile = []
@@ -632,7 +673,7 @@ PYTHONPATH, or by being added to sys.path by your code.)
# try to make the byte compile messages quieter
log.set_verbosity(self.verbose - 1)
- byte_compile(to_compile, optimize=0, force=1, dry_run=self.dry_run)
+ byte_compile(to_compile, optimize=0, force=1, dry_run=self.dry_run)
if self.optimize:
byte_compile(
to_compile, optimize=self.optimize, force=1,
@@ -667,7 +708,7 @@ def extract_wininst_cfg(dist_filename):
prepended = (endrec[9] - endrec[5]) - endrec[6]
if prepended < 12: # no wininst data here
- return None
+ return None
f.seek(prepended-12)
import struct, StringIO, ConfigParser
@@ -683,7 +724,7 @@ def extract_wininst_cfg(dist_filename):
return None
if not cfg.has_section('metadata') or not cfg.has_section('Setup'):
return None
- return cfg
+ return cfg
finally:
f.close()
diff --git a/setuptools/command/test.py b/setuptools/command/test.py
index 37aeac68..2b0d9484 100644
--- a/setuptools/command/test.py
+++ b/setuptools/command/test.py
@@ -4,9 +4,9 @@ import sys
class test(Command):
- """Command to run unit tests after installation"""
+ """Command to run unit tests after in-place build"""
- description = "run unit tests after installation"
+ description = "run unit tests after in-place build"
user_options = [
('test-module=','m', "Run 'test_suite' in specified module"),