diff -r 8036d819b7d0 -r 2750cd9e2111 Lib/packaging/command/__init__.py --- a/Lib/packaging/command/__init__.py Thu Jun 16 18:51:42 2011 -0500 +++ b/Lib/packaging/command/__init__.py Tue Jul 12 21:44:19 2011 +0800 @@ -22,6 +22,7 @@ 'install_data': 'packaging.command.install_data.install_data', 'install_distinfo': 'packaging.command.install_distinfo.install_distinfo', + 'develop_source': 'packaging.command.develop_source.develop_source', 'sdist': 'packaging.command.sdist.sdist', 'bdist': 'packaging.command.bdist.bdist', 'bdist_dumb': 'packaging.command.bdist_dumb.bdist_dumb', diff -r 8036d819b7d0 -r 2750cd9e2111 Lib/packaging/command/cmd.py --- a/Lib/packaging/command/cmd.py Thu Jun 16 18:51:42 2011 -0500 +++ b/Lib/packaging/command/cmd.py Tue Jul 12 21:44:19 2011 +0800 @@ -317,9 +317,12 @@ cmd_obj.ensure_finalized() return cmd_obj - def get_reinitialized_command(self, command, reinit_subcommands=False): - return self.distribution.get_reinitialized_command( - command, reinit_subcommands) + def get_reinitialized_command(self, command, reinit_subcommands=False, **kw): + cmd_obj = self.distribution.get_reinitialized_command(command, + reinit_subcommands) + for k,v in kw.items(): + setattr(cmd_obj,k,v) #update command object with keywords + return cmd_obj def run_command(self, command): """Run some other command: uses the 'run_command()' method of diff -r 8036d819b7d0 -r 2750cd9e2111 Lib/packaging/command/develop_source.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/packaging/command/develop_source.py Tue Jul 12 21:44:19 2011 +0800 @@ -0,0 +1,147 @@ +"""Install a project in development mode. + +This command can make your project importable without copying files, +through creating a .pth file in your site-packages dir or a specified +dir. +""" +import os +import sys +from packaging import logger +from packaging.dist import Distribution +from packaging.util import write_file, convert_path +from packaging.errors import PackagingOptionError +from packaging.command.cmd import Command +from packaging.command.install_distinfo import (to_filename, safe_name) + + +class develop_source(Command): + + description = "install package in 'development mode'" + + user_options = [ + ('uninstall', None, + "uninstall this source package"), + ('multi-version', None, + "make apps have to import a version"), + ('install-dir=', None, + "install package to DIR"), + ('install-scripts=', None, + "install scripts to DIR"), + ('exclude-scripts', None, + "don't install scripts"), + ('always-copy', None, + "always copy all needed packages to install dir"), + ('distinfo-path=', None, + "set the specified relative path to be used in the .dist-link file")] + + boolean_options = ['multi-version', 'always-copy', 'uninstall'] + + def run(self): + if self.uninstall: + self.multi_version = True + self.uninstall_link() + else: + self.install_for_development() + + def initialize_options(self): + self.uninstall = None + self.distinfo_path = None # the .dist-info directory path + self.install_dir = None + self.multi_version = None + self.always_copy = None + + self.always_copy_from = '.' # always copy .dist-info directory installed in curdir + self.basename = None # the base name of .pth and .distinfo-link file + + def finalize_options(self): + working_dir = os.getcwd() + self.get_reinitialized_command('install_distinfo', distinfo_dir = working_dir) + di = self.get_finalized_command("install_distinfo") + + self.basename = to_filename(safe_name(di.distribution.metadata['Name'])) + + # old easy_install.finalize_options(self) here + # xxx should add other finalization, e.g. installing from pypi + # if a dependent project doesn't exist + + instdist = self.get_finalized_command("install_dist") + instdist._expand_attrs(['install_purelib']) + + if self.install_dir is None: + self.install_dir = convert_path(instdist.install_purelib) + + sys_path = [normalize_path(path) for path in sys.path] + instdir = normalize_path(self.install_dir) + if instdir not in sys_path: + raise PackagingOptionError("'install_dir' should be contained in 'sys.path' list") + + self.distinfo_link = os.path.join(self.install_dir, self.basename + '.distinfo-link') + self.distinfo_base = os.path.dirname(di.distinfo_dir)# create .dist-info under source dir + if self.distinfo_path is None: + self.distinfo_path = os.path.abspath(self.distinfo_base) + + target = normalize_path(self.distinfo_base) + if normalize_path(os.path.join(self.install_dir, self.distinfo_path)) != target: + raise PackagingOptionError("'distinfo-path' must be a relative path from the install" + " directory to " + target) + + p = self.distinfo_base.replace(os.sep, '/') + cur_working_dir = os.path.normcase(working_dir) + cur_working_dir = cur_working_dir.replace(os.sep, '/') + if p != cur_working_dir: + p = '../' * (p.count('/')+1) + self.setup_path = p + + p = normalize_path(os.path.join(self.install_dir, self.distinfo_path, p)) + if p != normalize_path(os.curdir): + raise PackagingOptionError("Can't get a consistent path to setup.cfg from" + " installation directory", p, normalize_path(os.curdir)) + + def install_for_development(self): + # Ensure metadata is up-to-date + self.run_command('install_distinfo') + # Build extensions in-place + self.get_reinitialized_command('build_ext', inplace=1) + self.run_command('build_ext') + # Create a .distinfo-link file in the installation dir, pointing to our .dist-info dir + + logger.info("creating %s (link to %s)", self.distinfo_link, self.distinfo_base) + + if not self.dry_run: + f = open(self.distinfo_link, "w") + f.write(self.distinfo_path + "\n" + self.setup_path) + f.close() + + # Update the .pth file + self.update_path_file() + + # xxx should fix: process_distribution() function should be added + # e.g. 1: install egg scripts (should check the 'exclude-scripts' option) + # e.g. 2: record all the projects is installed by 'develop' command + # e.g. 3: other purposes? + + def update_path_file(self): + """Updates the .pth file or create if necessary.""" + + filename = os.path.join(self.install_dir, self.basename + ".pth") + + # xxx make sure whether other locations should be added + self.distinfo_location = normalize_path(self.distinfo_base) + + # Remove old .pth file and create new one if necessary + if os.path.islink(filename): + os.path.unlink(filename) + logger.info('removing old path file %r', filename) + + if not self.multi_version: + self.execute(write_file, + (filename, [self.distinfo_location]), + "creating %s" % filename) + else: + logger.info("'multi-version' is set, old .pth file will be removed if exist") + +# The following functions are taken from setuptools' pkg_resources module. + +def normalize_path(filename): + """Normalize a file/dir name for comparison purposes""" + return os.path.normcase(os.path.realpath(filename)) \ No newline at end of file diff -r 8036d819b7d0 -r 2750cd9e2111 Lib/packaging/develop.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/packaging/develop.py Tue Jul 12 21:44:19 2011 +0800 @@ -0,0 +1,57 @@ +"""Building blocks for different tools when executing 'develop' command. + +""" +import os + +from packaging import logger +from packaging.util import (egginfo_to_distinfo, get_develop_method) + +from packaging.dist import Distribution +from packaging.errors import (PackagingError, CCompilerError) + + +# The function term is studying from 'install_local_project' of install.py +def develop_local_project(path): + """ Deploy a project source in the 'development mode' from a source directory. + + If the source directory contains a setup.py, using distutils1. + If a setup.cfg is found, then using the XXX command. + """ + path = os.path.abspath(path) + if os.path.isdir(path): + logger.info('deploying from source directory:%s', path) + _run_develop_from_dir(path) + else: + logger.warn('no projects to install') + +def _run_develop_from_dir(source_dir): + old_dir = os.getcwd() + os.chdir(source_dir) + develop_method = get_develop_method(source_dir) + try: + func = develop_methods[develop_method] + return func(source_dir) + finally: + os.chdir(old_dir) + +def _run_packaging_develop(path): + dist = Distribution() + dist.parse_config_files() + try: + dist.run_command('develop_source') + except (IOError, os.error, PackagingError, CCompilerError) as msg: + raise SystemExit("error: " + str(msg)) + +def _run_setuptools_develop(path): # check!!! + cmd = '%s setup.py develop --record=%s' # Note 5: check if there are other options should be supported? + record_file = os.path.join(path, 'RECORD') + os.system(cmd % (sys.executable, record_file)) + if not os.path.exists(record_file): + raise ValueError('failed to install') + else: + egginfo_to_distinfo(record_file, remove_egginfo = True) + +# different develop methods +develop_methods = { + 'packaging': _run_packaging_develop, + 'setuptools': _run_setuptools_develop} \ No newline at end of file diff -r 8036d819b7d0 -r 2750cd9e2111 Lib/packaging/run.py --- a/Lib/packaging/run.py Thu Jun 16 18:51:42 2011 -0500 +++ b/Lib/packaging/run.py Tue Jul 12 21:44:19 2011 +0800 @@ -11,6 +11,7 @@ from packaging.util import _is_archive_file, generate_setup_py from packaging.command import get_command_class, STANDARD_COMMANDS from packaging.install import install, install_local_project, remove +from packaging.develop import develop_local_project from packaging.database import get_distribution, get_distributions from packaging.depgraph import generate_graph from packaging.fancy_getopt import FancyGetopt @@ -70,6 +71,17 @@ """ +develop_usage = """\ +Usage: pysetup develop [src_dir] + or: pysetup develop --help + +Deploy a project source in 'development mode'. + +positional arguments: + scr_dir path to source directory + +""" + metadata_usage = """\ Usage: pysetup metadata [dist] [-f field ...] or: pysetup metadata [dist] [--all] @@ -255,6 +267,21 @@ else: return 1 +@action_help(develop_usage) +def _develop(dispatcher, args, **kw): + # first check if we are in a source directory + if len(args) < 2: + # are we inside a project source dir? + listing = os.listdir(os.getcwd()) + if 'setup.py' in listing or 'setup.cfg' in listing: + args.insert(1, os.getcwd()) + else: + logger.warning('no project to deploy') + return + # installing from a source dir + if os.path.isdir(args[1]): + develop_local_project(args[1]) + @action_help(metadata_usage) def _metadata(dispatcher, args, **kw): @@ -389,6 +416,7 @@ ('run', 'Run one or several commands', _run), ('metadata', 'Display the metadata of a project', _metadata), ('install', 'Install a project', _install), + ('develop', 'Deploy a project source in development mode', _develop), ('remove', 'Remove a project', _remove), ('search', 'Search for a project in the indexes', _search), ('list', 'List installed releases', _list), diff -r 8036d819b7d0 -r 2750cd9e2111 Lib/packaging/tests/test_command_develop_source.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/packaging/tests/test_command_develop_source.py Tue Jul 12 21:44:19 2011 +0800 @@ -0,0 +1,172 @@ +"""Tests for packaging.command.develop_source.""" + +import os +import sys + +from packaging.dist import Distribution +from packaging.command.develop_source import (develop_source, normalize_path) +from packaging.tests import unittest, support +from packaging.errors import PackagingOptionError + +class DevelopTestCase(support.TempdirManager, + support.LoggingCatcher, + unittest.TestCase): + def test_pth_file_installation(self): + # test if the .pth file can be succesfully generated and installed + pkgdir, dist = self.create_dist() + oldcwd = os.getcwd() + try: + cmd = develop_source(dist) + os.chdir(pkgdir) + cmd.ensure_finalized() + cmd.run() + finally: + os.chdir(oldcwd) + pth_file = os.path.join(cmd.install_dir,cmd.path_file+'.pth') + self.assertTrue(os.path.exists(pth_file)) + # check if it writes the right path + with open(pth_file) as f: + written_path = f.readline().rstrip() + self.assertEqual(written_path, normalize_path(pkgdir)) + # .pth not in temp dir, so need clean work + distinfo_link_file = os.path.join(cmd.install_dir, cmd.path_file+'.distinfo-link') + self._clean([pth_file, distinfo_link_file]) + + def test_distinfo_link_file_installation(self): + # test if the .distinfo-link file is installed succesfully and + # with correct content + pkgdir, dist = self.create_dist(name='xx',version='1.2.3') + oldcwd = os.getcwd() + try: + cmd = develop_source(dist) + os.chdir(pkgdir) + cmd.ensure_finalized() + cmd.run() + finally: + os.chdir(oldcwd) + distinfo_link_file = os.path.join(cmd.install_dir, cmd.path_file+'.distinfo-link') + self.assertTrue(os.path.exists(distinfo_link_file)) + # should find the .dist-info directory through this file + with open(distinfo_link_file) as f: + written_path = f.readline().rstrip() + directory_name = 'xx-1.2.3.dist-info' + distinfo_dir = os.path.join(cmd.install_dir, written_path, directory_name) + self.assertTrue(os.path.exists(distinfo_dir)) + # need clean work + pth_file = os.path.join(cmd.install_dir, 'xx.pth') + self._clean([pth_file, distinfo_link_file]) + + def test_distinfo_dir_installation(self): + # test if the .dist-info directory is installed under correct directory + pkgdir, dist = self.create_dist(name='xxx', version='1.2.3') + distinfo_name = 'xxx-1.2.3.dist-info' + # generated under source dir defaultly + distinfo_dir = os.path.join(pkgdir, distinfo_name) + # not exist + self.assertFalse(os.path.exists(distinfo_dir)) + + oldcwd = os.getcwd() + try: + cmd = develop_source(dist) + os.chdir(pkgdir) + cmd.ensure_finalized() + cmd.run() + finally: + os.chdir(oldcwd) + # now exist + self.assertTrue(os.path.exists(distinfo_dir)) + + pth_file = os.path.join(cmd.install_dir, 'xxx.pth') + distinfo_link_file = os.path.join(cmd.install_dir, 'xxx.distinfo-link') + self._clean([pth_file, distinfo_link_file]) + + def test_install_dir(self): + # test the install_dir option + pkgdir, dist = self.create_dist(name='abc') + cmd = develop_source(dist) + # making sure the install-dir option is there + options = [name for name, short, label in cmd.user_options] + self.assertIn('install-dir', options) + # install-dir should be None + self.assertEqual(cmd.install_dir, None) + # set a value + cmd.install_dir = self.mkdtemp() + # finalize_options will raise exception if install_dir in not + # in sys.path + self.assertRaises(PackagingOptionError, cmd.finalize_options) + # add into sys.path + sys.path.append(cmd.install_dir) + oldcwd = os.getcwd() + try: + os.chdir(pkgdir) + cmd.ensure_finalized() + cmd.run() + finally: + os.chdir(oldcwd) + # check if it works through checking .pth file is created or not + # does not need clean, because .pth is in a temp dir + pth_file = os.path.join(cmd.install_dir,'abc.pth') + self.assertTrue(os.path.exists(pth_file)) + + distinfo_link_file = os.path.join(cmd.install_dir, 'abc.distinfo-link') + self._clean([pth_file, distinfo_link_file]) + + def test_distinfo_path(self): + # test the distinfo_path option + pkgdir,dist = self.create_dist(name='def') + cmd = develop_source(dist) + # making sure the distinfo_path option is there + options = [name for name,short,label in cmd.user_options] + self.assertIn('distinfo-path', options) + # should be None + self.assertIsNone(cmd.distinfo_path) + # an absolute path or a wrong relative path, should raise exception + # but we only need to check one + cmd.distinfo_path = self.mkdtemp() + oldcwd = os.getcwd() + os.chdir(pkgdir) + self.assertRaises(PackagingOptionError, cmd.finalize_options) + os.chdir(oldcwd) + + # because the install_dir is None before ensure_finalized + # function called in the following try clause, so we should get + # its default value from another temp cmd + tmpdist = Distribution() + tmpcmd = develop_source(tmpdist) + tmpcmd.ensure_finalized() + self.install_dir = tmpcmd.install_dir + + # create a relative path which is correct + # and assign it to distinfo_path + p = self.install_dir.replace(os.sep, '/') + p = '../'*(p.count('/')+1) + cmd.distinfo_path = os.path.join(p, pkgdir) + + try: + os.chdir(pkgdir) + cmd.ensure_finalized() + distinfo_link_file = os.path.join(cmd.install_dir, 'def.distinfo-link') + # def.distinfo-link should not exist, because we haven't run cmd + self.assertFalse(os.path.exists(distinfo_link_file)) + # then run + cmd.run() + # should exist after cmd's running + self.assertTrue(os.path.exists(distinfo_link_file)) + finally: + os.chdir(oldcwd) + # clean work + pth_file = os.path.join(self.install_dir, 'def.pth') + self._clean([pth_file, distinfo_link_file]) + + def _clean(self, paths=[]): + # clean function to remove generated files in python's + # default site-package + for file in paths: + if os.path.isfile(file): + os.unlink(file) + +def test_suite(): + return unittest.makeSuite(DevelopTestCase) + +if __name__ == '__main__': + unittest.main(defaultTest="test_suite") diff -r 8036d819b7d0 -r 2750cd9e2111 Lib/packaging/util.py --- a/Lib/packaging/util.py Thu Jun 16 18:51:42 2011 -0500 +++ b/Lib/packaging/util.py Tue Jul 12 21:44:19 2011 +0800 @@ -1357,6 +1357,21 @@ else: raise InstallationException('Cannot detect install method') +def get_develop_method(path): + """Check if the project is based on packaging or setuptools + + :param path: path to source directory containing a setup.cfg file, + or setup.py + + Returns a string representing the best develop method to use. + """ + if is_packaging(path): + return "packaging" + elif is_setuptools(path): + return "setuptools" + else: + raise InstallationException('Cannot detect develop method') + # XXX to be replaced by shutil.copytree def copy_tree(src, dst, preserve_mode=True, preserve_times=True, diff -r 8036d819b7d0 -r 2750cd9e2111 Modules/faulthandler.c --- a/Modules/faulthandler.c Thu Jun 16 18:51:42 2011 -0500 +++ b/Modules/faulthandler.c Tue Jul 12 21:44:19 2011 +0800 @@ -819,7 +819,7 @@ #ifdef _MSC_VER /* Visual Studio: configure abort() to not display an error message nor open a popup asking to report the fault. */ - _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); + //_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); #endif abort(); Py_RETURN_NONE;