[Python-checkins] distutils2: Branch merge
tarek.ziade
python-checkins at python.org
Sun Aug 8 11:50:47 CEST 2010
tarek.ziade pushed d5aa0034f8ed to distutils2:
http://hg.python.org/distutils2/rev/d5aa0034f8ed
changeset: 493:d5aa0034f8ed
parent: 489:bce5ffe93848
parent: 492:21e341cd6b11
user: ?ric Araujo <merwok at netwok.org>
date: Fri Aug 06 06:18:02 2010 +0200
summary: Branch merge
files: src/distutils2/dist.py
diff --git a/src/distutils2/__init__.py b/src/distutils2/__init__.py
--- a/src/distutils2/__init__.py
+++ b/src/distutils2/__init__.py
@@ -20,7 +20,7 @@
__version__ = "1.0a2"
-# when set to True, converts doctests by default too
+# when set to True, converts doctests by default too
run_2to3_on_doctests = True
# Standard package names for fixer packages
lib2to3_fixer_packages = ['lib2to3.fixes']
diff --git a/src/distutils2/core.py b/src/distutils2/core.py
--- a/src/distutils2/core.py
+++ b/src/distutils2/core.py
@@ -33,6 +33,7 @@
or: %(script)s cmd --help
"""
+
def gen_usage(script_name):
script = os.path.basename(script_name)
return USAGE % {'script': script}
@@ -59,6 +60,7 @@
'extra_objects', 'extra_compile_args', 'extra_link_args',
'swig_opts', 'export_symbols', 'depends', 'language')
+
def setup(**attrs):
"""The gateway to the Distutils: do everything your setup script needs
to do, in a highly flexible and user-driven way. Briefly: create a
diff --git a/src/distutils2/depgraph.py b/src/distutils2/depgraph.py
--- a/src/distutils2/depgraph.py
+++ b/src/distutils2/depgraph.py
@@ -1,5 +1,5 @@
-"""Analyse the relationships between the distributions in the system and generate
-a dependency graph.
+"""Analyse the relationships between the distributions in the system
+and generate a dependency graph.
"""
from distutils2.errors import DistutilsError
diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py
--- a/src/distutils2/dist.py
+++ b/src/distutils2/dist.py
@@ -6,12 +6,10 @@
__revision__ = "$Id: dist.py 77717 2010-01-24 00:33:32Z tarek.ziade $"
-import sys, os, re
-
-try:
- import warnings
-except ImportError:
- warnings = None
+import sys
+import os
+import re
+import warnings
from ConfigParser import RawConfigParser
@@ -26,7 +24,8 @@
# the same as a Python NAME -- I don't allow leading underscores. The fact
# that they're very similar is no coincidence; the default naming scheme is
# to look for a Python module named after the command.
-command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$')
+command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$')
+
class Distribution(object):
"""The core of the Distutils. Most of the work hiding behind 'setup'
@@ -123,10 +122,8 @@
# negative options are options that exclude other options
negative_opt = {'quiet': 'verbose'}
-
# -- Creation/initialization methods -------------------------------
-
- def __init__ (self, attrs=None):
+ def __init__(self, attrs=None):
"""Construct a new Distribution instance: initialize all the
attributes of a Distribution, and then use 'attrs' (a dictionary
mapping attribute names to values) to assign some of those
@@ -251,10 +248,7 @@
setattr(self, key, val)
else:
msg = "Unknown distribution option: %r" % key
- if warnings is not None:
- warnings.warn(msg)
- else:
- sys.stderr.write(msg + "\n")
+ warnings.warn(msg)
# no-user-cfg is handled before other command line args
# because other args override the config files, and this
@@ -565,10 +559,10 @@
if (hasattr(cmd_class, 'help_options') and
isinstance(cmd_class.help_options, list)):
- help_option_found=0
+ help_option_found = 0
for (help_option, short, desc, func) in cmd_class.help_options:
if hasattr(opts, parser.get_attr_name(help_option)):
- help_option_found=1
+ help_option_found = 1
if hasattr(func, '__call__'):
func()
else:
@@ -807,7 +801,7 @@
class_name = command
try:
- __import__ (module_name)
+ __import__(module_name)
module = sys.modules[module_name]
except ImportError:
continue
@@ -824,7 +818,6 @@
raise DistutilsModuleError("invalid command '%s'" % command)
-
def get_command_obj(self, command, create=1):
"""Return the command object for 'command'. Normally this object
is cached on a previous call to 'get_command_obj()'; if no command
@@ -967,7 +960,6 @@
self.run_command_hooks(cmd_obj, 'post_hook')
self.have_run[command] = 1
-
def run_command_hooks(self, cmd_obj, hook_kind):
hooks = getattr(cmd_obj, hook_kind)
if hooks is None:
@@ -977,7 +969,6 @@
hook_func(cmd_obj)
# -- Distribution query methods ------------------------------------
-
def has_pure_modules(self):
return len(self.packages or self.py_modules or []) > 0
diff --git a/src/distutils2/errors.py b/src/distutils2/errors.py
--- a/src/distutils2/errors.py
+++ b/src/distutils2/errors.py
@@ -10,31 +10,38 @@
__revision__ = "$Id: errors.py 75901 2009-10-28 06:45:18Z tarek.ziade $"
+
class DistutilsError(Exception):
"""The root of all Distutils evil."""
+
class DistutilsModuleError(DistutilsError):
"""Unable to load an expected module, or to find an expected class
within some module (in particular, command modules and classes)."""
+
class DistutilsClassError(DistutilsError):
"""Some command class (or possibly distribution class, if anyone
feels a need to subclass Distribution) is found not to be holding
up its end of the bargain, ie. implementing some part of the
"command "interface."""
+
class DistutilsGetoptError(DistutilsError):
"""The option table provided to 'fancy_getopt()' is bogus."""
+
class DistutilsArgError(DistutilsError):
"""Raised by fancy_getopt in response to getopt.error -- ie. an
error in the command line usage."""
+
class DistutilsFileError(DistutilsError):
"""Any problems in the filesystem: expected file not found, etc.
Typically this is for problems that we detect before IOError or
OSError could be raised."""
+
class DistutilsOptionError(DistutilsError):
"""Syntactic/semantic errors in command options, such as use of
mutually conflicting options, or inconsistent options,
@@ -43,60 +50,76 @@
files, or what-have-you -- but if we *know* something originated in
the setup script, we'll raise DistutilsSetupError instead."""
+
class DistutilsSetupError(DistutilsError):
"""For errors that can be definitely blamed on the setup script,
such as invalid keyword arguments to 'setup()'."""
+
class DistutilsPlatformError(DistutilsError):
"""We don't know how to do something on the current platform (but
we do know how to do it on some platform) -- eg. trying to compile
C files on a platform not supported by a CCompiler subclass."""
+
class DistutilsExecError(DistutilsError):
"""Any problems executing an external program (such as the C
compiler, when compiling C files)."""
+
class DistutilsInternalError(DistutilsError):
"""Internal inconsistencies or impossibilities (obviously, this
should never be seen if the code is working!)."""
+
class DistutilsTemplateError(DistutilsError):
"""Syntax error in a file list template."""
+
class DistutilsByteCompileError(DistutilsError):
"""Byte compile error."""
+
# Exception classes used by the CCompiler implementation classes
class CCompilerError(Exception):
"""Some compile/link operation failed."""
+
class PreprocessError(CCompilerError):
"""Failure to preprocess one or more C/C++ files."""
+
class CompileError(CCompilerError):
"""Failure to compile one or more C/C++ source files."""
+
class LibError(CCompilerError):
"""Failure to create a static library from one or more C/C++ object
files."""
+
class LinkError(CCompilerError):
"""Failure to link one or more C/C++ object files into an executable
or shared library file."""
+
class UnknownFileError(CCompilerError):
"""Attempt to process an unknown file type."""
+
class MetadataConflictError(DistutilsError):
"""Attempt to read or write metadata fields that are conflictual."""
+
class MetadataUnrecognizedVersionError(DistutilsError):
"""Unknown metadata version number."""
+
class IrrationalVersionError(Exception):
"""This is an irrational version."""
pass
+
class HugeMajorVersionNumError(IrrationalVersionError):
"""An irrational version because the major version number is huge
(often because a year or date was used).
@@ -105,4 +128,3 @@
This guard can be disabled by setting that option False.
"""
pass
-
diff --git a/src/distutils2/extension.py b/src/distutils2/extension.py
--- a/src/distutils2/extension.py
+++ b/src/distutils2/extension.py
@@ -17,6 +17,7 @@
# import that large-ish module (indirectly, through distutils.core) in
# order to do anything.
+
class Extension(object):
"""Just a collection of attributes that describes an extension
module and everything needed to build it (hopefully in a portable
@@ -84,7 +85,7 @@
# When adding arguments to this constructor, be sure to update
# setup_keywords in core.py.
- def __init__ (self, name, sources,
+ def __init__(self, name, sources,
include_dirs=None,
define_macros=None,
undef_macros=None,
@@ -95,11 +96,11 @@
extra_compile_args=None,
extra_link_args=None,
export_symbols=None,
- swig_opts = None,
+ swig_opts=None,
depends=None,
language=None,
optional=None,
- **kw # To catch unknown keywords
+ **kw # To catch unknown keywords
):
if not isinstance(name, str):
raise AssertionError("'name' must be a string")
@@ -134,4 +135,3 @@
options = ', '.join(sorted(options))
msg = "Unknown Extension options: %s" % options
warnings.warn(msg)
-
diff --git a/src/distutils2/fancy_getopt.py b/src/distutils2/fancy_getopt.py
--- a/src/distutils2/fancy_getopt.py
+++ b/src/distutils2/fancy_getopt.py
@@ -30,6 +30,7 @@
# (for use as attributes of some object).
longopt_xlate = string.maketrans('-', '_')
+
class FancyGetopt(object):
"""Wrapper around the standard 'getopt()' module that provides some
handy extra functionality:
@@ -42,7 +43,7 @@
on the command line sets 'verbose' to false
"""
- def __init__ (self, option_table=None):
+ def __init__(self, option_table=None):
# The option table is (currently) a list of tuples. The
# tuples may have 3 or four values:
@@ -180,7 +181,8 @@
self.long_opts.append(long)
if long[-1] == '=': # option takes an argument?
- if short: short = short + ':'
+ if short:
+ short = short + ':'
long = long[0:-1]
self.takes_arg[long] = 1
else:
diff --git a/src/distutils2/manifest.py b/src/distutils2/manifest.py
--- a/src/distutils2/manifest.py
+++ b/src/distutils2/manifest.py
@@ -22,7 +22,7 @@
# a \ followed by some spaces + EOL
_COLLAPSE_PATTERN = re.compile('\\\w*\n', re.M)
-_COMMENTED_LINE = re.compile('#.*?(?=\n)|^\w*\n|\n(?=$)', re.M|re.S)
+_COMMENTED_LINE = re.compile('#.*?(?=\n)|^\w*\n|\n(?=$)', re.M | re.S)
class Manifest(object):
"""A list of files built by on exploring the filesystem and filtered by
diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py
--- a/src/distutils2/metadata.py
+++ b/src/distutils2/metadata.py
@@ -52,12 +52,12 @@
PKG_INFO_PREFERRED_VERSION = '1.0'
_LINE_PREFIX = re.compile('\n \|')
-_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
+_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
'Summary', 'Description',
'Keywords', 'Home-page', 'Author', 'Author-email',
'License')
-_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
+_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
'Supported-Platform', 'Summary', 'Description',
'Keywords', 'Home-page', 'Author', 'Author-email',
'License', 'Classifier', 'Download-URL', 'Obsoletes',
@@ -65,7 +65,7 @@
_314_MARKERS = ('Obsoletes', 'Provides', 'Requires')
-_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
+_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform',
'Supported-Platform', 'Summary', 'Description',
'Keywords', 'Home-page', 'Author', 'Author-email',
'Maintainer', 'Maintainer-email', 'License',
diff --git a/src/distutils2/mkpkg.py b/src/distutils2/mkpkg.py
--- a/src/distutils2/mkpkg.py
+++ b/src/distutils2/mkpkg.py
@@ -30,7 +30,11 @@
#
# Detect scripts (not sure how. #! outside of package?)
-import sys, os, re, shutil, ConfigParser
+import sys
+import os
+import re
+import shutil
+import ConfigParser
helpText = {
@@ -629,19 +633,20 @@
print '\nERROR: You must select "Y" or "N".\n'
-def ask(question, default = None, helptext = None, required = True,
- lengthy = False, multiline = False):
- prompt = '%s: ' % ( question, )
+def ask(question, default=None, helptext=None, required=True,
+ lengthy=False, multiline=False):
+ prompt = '%s: ' % (question,)
if default:
- prompt = '%s [%s]: ' % ( question, default )
+ prompt = '%s [%s]: ' % (question, default)
if default and len(question) + len(default) > 70:
- prompt = '%s\n [%s]: ' % ( question, default )
+ prompt = '%s\n [%s]: ' % (question, default)
if lengthy or multiline:
prompt += '\n >'
- if not helptext: helptext = 'No additional help available.'
- if helptext[0] == '\n': helptext = helptext[1:]
- if helptext[-1] == '\n': helptext = helptext[:-1]
+ if not helptext:
+ helptext = 'No additional help available.'
+
+ helptext = helptext.strip("\n")
while True:
sys.stdout.write(prompt)
@@ -653,12 +658,14 @@
print helptext
print '=' * 70
continue
- if default and not line: return(default)
+ if default and not line:
+ return(default)
if not line and required:
print '*' * 70
print 'This value cannot be empty.'
print '==========================='
- if helptext: print helptext
+ if helptext:
+ print helptext
print '*' * 70
continue
return(line)
@@ -669,7 +676,8 @@
for key in troveList:
subDict = dict
for subkey in key.split(' :: '):
- if not subkey in subDict: subDict[subkey] = {}
+ if not subkey in subDict:
+ subDict[subkey] = {}
subDict = subDict[subkey]
return(dict)
troveDict = buildTroveDict(troveList)
@@ -687,7 +695,8 @@
def lookupOption(self, key):
- if not self.config.has_option('DEFAULT', key): return(None)
+ if not self.config.has_option('DEFAULT', key):
+ return(None)
return(self.config.get('DEFAULT', key))
@@ -706,7 +715,8 @@
self.config.set('DEFAULT', compareKey,
self.setupData[compareKey])
- if not valuesDifferent: return
+ if not valuesDifferent:
+ return
self.config.write(open(os.path.expanduser('~/.pygiver'), 'w'))
@@ -718,7 +728,7 @@
def inspectFile(self, path):
fp = open(path, 'r')
try:
- for line in [ fp.readline() for x in range(10) ]:
+ for line in [fp.readline() for x in range(10)]:
m = re.match(r'^#!.*python((?P<major>\d)(\.\d+)?)?$', line)
if m:
if m.group('major') == '3':
@@ -761,16 +771,16 @@
self.setupData.get('version'), helpText['version'])
self.setupData['description'] = ask('Package description',
self.setupData.get('description'), helpText['description'],
- lengthy = True)
+ lengthy=True)
self.setupData['author'] = ask('Author name',
self.setupData.get('author'), helpText['author'])
self.setupData['author_email'] = ask('Author e-mail address',
self.setupData.get('author_email'), helpText['author_email'])
self.setupData['url'] = ask('Project URL',
- self.setupData.get('url'), helpText['url'], required = False)
+ self.setupData.get('url'), helpText['url'], required=False)
if (askYn('Do you want to set Trove classifiers?',
- helptext = helpText['do_classifier']) == 'y'):
+ helptext=helpText['do_classifier']) == 'y'):
self.setTroveClassifier()
@@ -781,8 +791,10 @@
def setTroveOther(self, classifierDict):
- if askYn('Do you want to set other trove identifiers', 'n',
- helpText['trove_generic']) != 'y': return
+ if askYn('Do you want to set other trove identifiers',
+ 'n',
+ helpText['trove_generic']) != 'y':
+ return
self.walkTrove(classifierDict, [troveDict], '')
@@ -799,7 +811,7 @@
continue
if askYn('Do you want to set items under\n "%s" (%d sub-items)'
- % ( key, len(trove[key]) ), 'n',
+ % (key, len(trove[key])), 'n',
helpText['trove_generic']) == 'y':
self.walkTrove(classifierDict, trovePath + [trove[key]],
desc + ' :: ' + key)
@@ -808,15 +820,18 @@
def setTroveLicense(self, classifierDict):
while True:
license = ask('What license do you use',
- helptext = helpText['trove_license'], required = False)
- if not license: return
+ helptext=helpText['trove_license'],
+ required=False)
+ if not license:
+ return
licenseWords = license.lower().split(' ')
foundList = []
for index in range(len(troveList)):
troveItem = troveList[index]
- if not troveItem.startswith('License :: '): continue
+ if not troveItem.startswith('License :: '):
+ continue
troveItem = troveItem[11:].lower()
allMatch = True
@@ -824,17 +839,20 @@
if not word in troveItem:
allMatch = False
break
- if allMatch: foundList.append(index)
+ if allMatch:
+ foundList.append(index)
question = 'Matching licenses:\n\n'
for i in xrange(1, len(foundList) + 1):
- question += ' %s) %s\n' % ( i, troveList[foundList[i - 1]] )
+ question += ' %s) %s\n' % (i, troveList[foundList[i - 1]])
question += ('\nType the number of the license you wish to use or '
'? to try again:')
- troveLicense = ask(question, required = False)
+ troveLicense = ask(question, required=False)
- if troveLicense == '?': continue
- if troveLicense == '': return
+ if troveLicense == '?':
+ continue
+ if troveLicense == '':
+ return
foundIndex = foundList[int(troveLicense) - 1]
classifierDict[troveList[foundIndex]] = 1
try:
@@ -856,7 +874,7 @@
6 - Mature
7 - Inactive
-Status''', required = False)
+Status''', required=False)
if devStatus:
try:
key = {
@@ -884,7 +902,8 @@
return modified_pkgs
def writeSetup(self):
- if os.path.exists('setup.py'): shutil.move('setup.py', 'setup.py.old')
+ if os.path.exists('setup.py'):
+ shutil.move('setup.py', 'setup.py.old')
fp = open('setup.py', 'w')
try:
diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py
--- a/src/distutils2/tests/test_dist.py
+++ b/src/distutils2/tests/test_dist.py
@@ -153,6 +153,27 @@
my_file2 = os.path.join(tmp_dir, 'f2')
dist.metadata.write_file(open(my_file, 'w'))
+ def test_bad_attr(self):
+ cls = Distribution
+
+ # catching warnings
+ warns = []
+ def _warn(msg):
+ warns.append(msg)
+
+ old_warn = warnings.warn
+ warnings.warn = _warn
+ try:
+ dist = cls(attrs={'author': 'xxx',
+ 'name': 'xxx',
+ 'version': 'xxx',
+ 'url': 'xxxx',
+ 'badoptname': 'xxx'})
+ finally:
+ warnings.warn = old_warn
+
+ self.assertTrue(len(warns)==1 and "Unknown distribution" in warns[0])
+
def test_empty_options(self):
# an empty options dictionary should not stay in the
# list of attributes
@@ -176,6 +197,21 @@
self.assertEqual(len(warns), 0)
+ def test_non_empty_options(self):
+ # TODO: how to actually use options is not documented except
+ # for a few cryptic comments in dist.py. If this is to stay
+ # in the public API, it deserves some better documentation.
+
+ # Here is an example of how it's used out there:
+ # http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html#specifying-customizations
+ cls = Distribution
+ dist = cls(attrs={'author': 'xxx',
+ 'name': 'xxx',
+ 'version': 'xxx',
+ 'url': 'xxxx',
+ 'options': dict(sdist=dict(owner="root"))})
+ self.assertTrue("owner" in dist.get_option_dict("sdist"))
+
def test_finalize_options(self):
attrs = {'keywords': 'one,two',
diff --git a/src/runtests-cov.py b/src/runtests-cov.py
--- a/src/runtests-cov.py
+++ b/src/runtests-cov.py
@@ -5,9 +5,19 @@
"""
import sys
-from os.path import dirname, islink, realpath
+from os.path import dirname, islink, realpath, join, abspath
from optparse import OptionParser
+COVERAGE_FILE = join(dirname(abspath(__file__)), '.coverage')
+
+def get_coverage():
+ """ Return a usable coverage object. """
+ # deferred import because coverage is optional
+ import coverage
+ cov = getattr(coverage, "the_coverage", None)
+ if not cov:
+ cov = coverage.coverage(COVERAGE_FILE)
+ return cov
def ignore_prefixes(module):
""" Return a list of prefixes to ignore in the coverage report if
@@ -44,10 +54,17 @@
def coverage_report(opts):
- import coverage
from distutils2.tests.support import unittest
- cov = coverage.coverage()
- cov.load()
+ cov = get_coverage()
+ if hasattr(cov, "load"):
+ # running coverage 3.x
+ cov.load()
+ morfs = None
+ else:
+ # running coverage 2.x
+ cov.cache = COVERAGE_FILE
+ cov.restore()
+ morfs = [m for m in cov.cexecuted.keys() if "distutils2" in m]
prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"]
prefixes += ignore_prefixes(unittest)
@@ -66,7 +83,7 @@
# that module is also completely optional
pass
- cov.report(omit_prefixes=prefixes, show_missing=opts.show_missing)
+ cov.report(morfs, omit_prefixes=prefixes, show_missing=opts.show_missing)
def test_main():
@@ -74,11 +91,8 @@
verbose = not opts.quiet
ret = 0
- if opts.coverage or opts.report:
- import coverage
-
if opts.coverage:
- cov = coverage.coverage()
+ cov = get_coverage()
cov.erase()
cov.start()
if not opts.report:
--
Repository URL: http://hg.python.org/distutils2
More information about the Python-checkins
mailing list