Branch merge

This commit is contained in:
Éric Araujo 2012-02-10 05:12:58 +01:00
commit ea0b1edf45
11 changed files with 168 additions and 254 deletions

View File

@ -6,38 +6,28 @@ from packaging.util import resolve_name
__all__ = ['get_command_names', 'set_command', 'get_command_class', __all__ = ['get_command_names', 'set_command', 'get_command_class',
'STANDARD_COMMANDS'] 'STANDARD_COMMANDS']
_COMMANDS = {
'check': 'packaging.command.check.check',
'test': 'packaging.command.test.test',
'build': 'packaging.command.build.build',
'build_py': 'packaging.command.build_py.build_py',
'build_ext': 'packaging.command.build_ext.build_ext',
'build_clib': 'packaging.command.build_clib.build_clib',
'build_scripts': 'packaging.command.build_scripts.build_scripts',
'clean': 'packaging.command.clean.clean',
'install_dist': 'packaging.command.install_dist.install_dist',
'install_lib': 'packaging.command.install_lib.install_lib',
'install_headers': 'packaging.command.install_headers.install_headers',
'install_scripts': 'packaging.command.install_scripts.install_scripts',
'install_data': 'packaging.command.install_data.install_data',
'install_distinfo':
'packaging.command.install_distinfo.install_distinfo',
'sdist': 'packaging.command.sdist.sdist',
'bdist': 'packaging.command.bdist.bdist',
'bdist_dumb': 'packaging.command.bdist_dumb.bdist_dumb',
'bdist_wininst': 'packaging.command.bdist_wininst.bdist_wininst',
'register': 'packaging.command.register.register',
'upload': 'packaging.command.upload.upload',
'upload_docs': 'packaging.command.upload_docs.upload_docs',
}
# XXX this is crappy STANDARD_COMMANDS = [
# packaging
'check', 'test',
# building
'build', 'build_py', 'build_ext', 'build_clib', 'build_scripts', 'clean',
# installing
'install_dist', 'install_lib', 'install_headers', 'install_scripts',
'install_data', 'install_distinfo',
# distributing
'sdist', 'bdist', 'bdist_dumb', 'bdist_wininst',
'register', 'upload', 'upload_docs',
]
if os.name == 'nt': if os.name == 'nt':
_COMMANDS['bdist_msi'] = 'packaging.command.bdist_msi.bdist_msi' STANDARD_COMMANDS.insert(STANDARD_COMMANDS.index('bdist_wininst'),
'bdist_msi')
# XXX use OrderedDict to preserve the grouping (build-related, install-related, # XXX maybe we need more than one registry, so that --list-comands can display
# distribution-related) # standard, custom and overriden standard commands differently
STANDARD_COMMANDS = set(_COMMANDS) _COMMANDS = dict((name, 'packaging.command.%s.%s' % (name, name))
for name in STANDARD_COMMANDS)
def get_command_names(): def get_command_names():

View File

@ -1,4 +1,4 @@
"""Compatibility helpers.""" """Support for build-time 2to3 conversion."""
from packaging import logger from packaging import logger
@ -25,7 +25,7 @@ class Mixin2to3(_KLASS):
""" """
if _CONVERT: if _CONVERT:
def _run_2to3(self, files, doctests=[], fixers=[]): def _run_2to3(self, files=[], doctests=[], fixers=[]):
""" Takes a list of files and doctests, and performs conversion """ Takes a list of files and doctests, and performs conversion
on those. on those.
- First, the files which contain the code(`files`) are converted. - First, the files which contain the code(`files`) are converted.
@ -35,17 +35,16 @@ class Mixin2to3(_KLASS):
if fixers: if fixers:
self.fixer_names = fixers self.fixer_names = fixers
logger.info('converting Python code') if files:
logger.info('converting Python code and doctests')
_KLASS.run_2to3(self, files) _KLASS.run_2to3(self, files)
logger.info('converting doctests in Python files')
_KLASS.run_2to3(self, files, doctests_only=True) _KLASS.run_2to3(self, files, doctests_only=True)
if doctests != []: if doctests:
logger.info('converting doctest in text files') logger.info('converting doctests in text files')
_KLASS.run_2to3(self, doctests, doctests_only=True) _KLASS.run_2to3(self, doctests, doctests_only=True)
else: else:
# If run on Python 2.x, there is nothing to do. # If run on Python 2.x, there is nothing to do.
def _run_2to3(self, files, doctests=[], fixers=[]): def _run_2to3(self, files=[], doctests=[], fixers=[]):
pass pass

View File

@ -254,16 +254,13 @@ def _run(dispatcher, args, **kw):
parser = dispatcher.parser parser = dispatcher.parser
args = args[1:] args = args[1:]
commands = STANDARD_COMMANDS # + extra commands commands = STANDARD_COMMANDS # FIXME display extra commands
if args == ['--list-commands']: if args == ['--list-commands']:
print('List of available commands:') print('List of available commands:')
cmds = sorted(commands) for cmd in commands:
for cmd in cmds:
cls = dispatcher.cmdclass.get(cmd) or get_command_class(cmd) cls = dispatcher.cmdclass.get(cmd) or get_command_class(cmd)
desc = getattr(cls, 'description', desc = getattr(cls, 'description', '(no description available)')
'(no description available)')
print(' %s: %s' % (cmd, desc)) print(' %s: %s' % (cmd, desc))
return return

View File

@ -0,0 +1,16 @@
# Example custom fixer, derived from fix_raw_input by Andre Roberge
from lib2to3 import fixer_base
from lib2to3.fixer_util import Name
class FixEcho(fixer_base.BaseFix):
BM_compatible = True
PATTERN = """
power< name='echo' trailer< '(' [any] ')' > any* >
"""
def transform(self, node, results):
name = results['name']
name.replace(Name('print', prefix=name.prefix))

View File

@ -0,0 +1,16 @@
# Example custom fixer, derived from fix_raw_input by Andre Roberge
from lib2to3 import fixer_base
from lib2to3.fixer_util import Name
class FixEcho2(fixer_base.BaseFix):
BM_compatible = True
PATTERN = """
power< name='echo2' trailer< '(' [any] ')' > any* >
"""
def transform(self, node, results):
name = results['name']
name.replace(Name('print', prefix=name.prefix))

View File

@ -1,134 +0,0 @@
"""Adjust some old Python 2 idioms to their modern counterparts.
* Change some type comparisons to isinstance() calls:
type(x) == T -> isinstance(x, T)
type(x) is T -> isinstance(x, T)
type(x) != T -> not isinstance(x, T)
type(x) is not T -> not isinstance(x, T)
* Change "while 1:" into "while True:".
* Change both
v = list(EXPR)
v.sort()
foo(v)
and the more general
v = EXPR
v.sort()
foo(v)
into
v = sorted(EXPR)
foo(v)
"""
# Author: Jacques Frechet, Collin Winter
# Local imports
from lib2to3 import fixer_base
from lib2to3.fixer_util import Call, Comma, Name, Node, syms
CMP = "(n='!=' | '==' | 'is' | n=comp_op< 'is' 'not' >)"
TYPE = "power< 'type' trailer< '(' x=any ')' > >"
class FixIdioms(fixer_base.BaseFix):
explicit = False # The user must ask for this fixer
PATTERN = r"""
isinstance=comparison< %s %s T=any >
|
isinstance=comparison< T=any %s %s >
|
while_stmt< 'while' while='1' ':' any+ >
|
sorted=any<
any*
simple_stmt<
expr_stmt< id1=any '='
power< list='list' trailer< '(' (not arglist<any+>) any ')' > >
>
'\n'
>
sort=
simple_stmt<
power< id2=any
trailer< '.' 'sort' > trailer< '(' ')' >
>
'\n'
>
next=any*
>
|
sorted=any<
any*
simple_stmt< expr_stmt< id1=any '=' expr=any > '\n' >
sort=
simple_stmt<
power< id2=any
trailer< '.' 'sort' > trailer< '(' ')' >
>
'\n'
>
next=any*
>
""" % (TYPE, CMP, CMP, TYPE)
def match(self, node):
r = super(FixIdioms, self).match(node)
# If we've matched one of the sort/sorted subpatterns above, we
# want to reject matches where the initial assignment and the
# subsequent .sort() call involve different identifiers.
if r and "sorted" in r:
if r["id1"] == r["id2"]:
return r
return None
return r
def transform(self, node, results):
if "isinstance" in results:
return self.transform_isinstance(node, results)
elif "while" in results:
return self.transform_while(node, results)
elif "sorted" in results:
return self.transform_sort(node, results)
else:
raise RuntimeError("Invalid match")
def transform_isinstance(self, node, results):
x = results["x"].clone() # The thing inside of type()
T = results["T"].clone() # The type being compared against
x.prefix = ""
T.prefix = " "
test = Call(Name("isinstance"), [x, Comma(), T])
if "n" in results:
test.prefix = " "
test = Node(syms.not_test, [Name("not"), test])
test.prefix = node.prefix
return test
def transform_while(self, node, results):
one = results["while"]
one.replace(Name("True", prefix=one.prefix))
def transform_sort(self, node, results):
sort_stmt = results["sort"]
next_stmt = results["next"]
list_call = results.get("list")
simple_expr = results.get("expr")
if list_call:
list_call.replace(Name("sorted", prefix=list_call.prefix))
elif simple_expr:
new = simple_expr.clone()
new.prefix = ""
simple_expr.replace(Call(Name("sorted"), [new],
prefix=simple_expr.prefix))
else:
raise RuntimeError("should not have reached here")
sort_stmt.remove()
if next_stmt:
next_stmt[0].prefix = sort_stmt._prefix

View File

@ -56,8 +56,9 @@ __all__ = [
# misc. functions and decorators # misc. functions and decorators
'fake_dec', 'create_distribution', 'use_command', 'fake_dec', 'create_distribution', 'use_command',
'copy_xxmodule_c', 'fixup_build_ext', 'copy_xxmodule_c', 'fixup_build_ext',
'skip_2to3_optimize',
# imported from this module for backport purposes # imported from this module for backport purposes
'unittest', 'requires_zlib', 'skip_2to3_optimize', 'skip_unless_symlink', 'unittest', 'requires_zlib', 'skip_unless_symlink',
] ]
@ -332,20 +333,16 @@ def copy_xxmodule_c(directory):
""" """
filename = _get_xxmodule_path() filename = _get_xxmodule_path()
if filename is None: if filename is None:
raise unittest.SkipTest('cannot find xxmodule.c (test must run in ' raise unittest.SkipTest('cannot find xxmodule.c')
'the python build dir)')
shutil.copy(filename, directory) shutil.copy(filename, directory)
def _get_xxmodule_path(): def _get_xxmodule_path():
srcdir = sysconfig.get_config_var('srcdir') if sysconfig.is_python_build():
candidates = [ srcdir = sysconfig.get_config_var('projectbase')
# use installed copy if available path = os.path.join(os.getcwd(), srcdir, 'Modules', 'xxmodule.c')
os.path.join(os.path.dirname(__file__), 'xxmodule.c'), else:
# otherwise try using copy from build directory os.path.join(os.path.dirname(__file__), 'xxmodule.c')
os.path.join(srcdir, 'Modules', 'xxmodule.c'),
]
for path in candidates:
if os.path.exists(path): if os.path.exists(path):
return path return path
@ -355,20 +352,21 @@ def fixup_build_ext(cmd):
When Python was built with --enable-shared on Unix, -L. is not enough to When Python was built with --enable-shared on Unix, -L. is not enough to
find libpython<blah>.so, because regrtest runs in a tempdir, not in the find libpython<blah>.so, because regrtest runs in a tempdir, not in the
source directory where the .so lives. source directory where the .so lives. (Mac OS X embeds absolute paths
to shared libraries into executables, so the fixup is a no-op on that
platform.)
When Python was built with in debug mode on Windows, build_ext commands When Python was built with in debug mode on Windows, build_ext commands
need their debug attribute set, and it is not done automatically for need their debug attribute set, and it is not done automatically for
some reason. some reason.
This function handles both of these things. Example use: This function handles both of these things, and also fixes
cmd.distribution.include_dirs if the running Python is an uninstalled
build. Example use:
cmd = build_ext(dist) cmd = build_ext(dist)
support.fixup_build_ext(cmd) support.fixup_build_ext(cmd)
cmd.ensure_finalized() cmd.ensure_finalized()
Unlike most other Unix platforms, Mac OS X embeds absolute paths
to shared libraries into executables, so the fixup is not needed there.
""" """
if os.name == 'nt': if os.name == 'nt':
cmd.debug = sys.executable.endswith('_d.exe') cmd.debug = sys.executable.endswith('_d.exe')
@ -386,12 +384,17 @@ def fixup_build_ext(cmd):
name, equals, value = runshared.partition('=') name, equals, value = runshared.partition('=')
cmd.library_dirs = value.split(os.pathsep) cmd.library_dirs = value.split(os.pathsep)
# Allow tests to run with an uninstalled Python
if sysconfig.is_python_build():
pysrcdir = sysconfig.get_config_var('projectbase')
cmd.distribution.include_dirs.append(os.path.join(pysrcdir, 'Include'))
try: try:
from test.support import skip_unless_symlink from test.support import skip_unless_symlink
except ImportError: except ImportError:
skip_unless_symlink = unittest.skip( skip_unless_symlink = unittest.skip(
'requires test.support.skip_unless_symlink') 'requires test.support.skip_unless_symlink')
skip_2to3_optimize = unittest.skipIf(sys.flags.optimize, skip_2to3_optimize = unittest.skipIf(sys.flags.optimize,
"2to3 doesn't work under -O") "2to3 doesn't work under -O")

View File

@ -20,8 +20,6 @@ class MarkersTestCase(LoggingCatcher,
platform_python_implementation = platform.python_implementation() platform_python_implementation = platform.python_implementation()
self.assertTrue(interpret("sys.platform == '%s'" % sys_platform)) self.assertTrue(interpret("sys.platform == '%s'" % sys_platform))
self.assertTrue(interpret(
"sys.platform == '%s' or python_version == '2.4'" % sys_platform))
self.assertTrue(interpret( self.assertTrue(interpret(
"sys.platform == '%s' and python_full_version == '%s'" % "sys.platform == '%s' and python_full_version == '%s'" %
(sys_platform, version))) (sys_platform, version)))
@ -41,12 +39,18 @@ class MarkersTestCase(LoggingCatcher,
# combined operations # combined operations
OP = 'os.name == "%s"' % os_name OP = 'os.name == "%s"' % os_name
FALSEOP = 'os.name == "buuuu"'
AND = ' and ' AND = ' and '
OR = ' or ' OR = ' or '
self.assertTrue(interpret(OP + AND + OP)) self.assertTrue(interpret(OP + AND + OP))
self.assertTrue(interpret(OP + AND + OP + AND + OP)) self.assertTrue(interpret(OP + AND + OP + AND + OP))
self.assertTrue(interpret(OP + OR + OP)) self.assertTrue(interpret(OP + OR + OP))
self.assertTrue(interpret(OP + OR + OP + OR + OP)) self.assertTrue(interpret(OP + OR + FALSEOP))
self.assertTrue(interpret(OP + OR + OP + OR + FALSEOP))
self.assertTrue(interpret(OP + OR + FALSEOP + OR + FALSEOP))
self.assertTrue(interpret(FALSEOP + OR + OP))
self.assertFalse(interpret(FALSEOP + AND + FALSEOP))
self.assertFalse(interpret(FALSEOP + OR + FALSEOP))
# other operators # other operators
self.assertTrue(interpret("os.name != 'buuuu'")) self.assertTrue(interpret("os.name != 'buuuu'"))

View File

@ -8,71 +8,77 @@ class Mixin2to3TestCase(support.TempdirManager,
support.LoggingCatcher, support.LoggingCatcher,
unittest.TestCase): unittest.TestCase):
@support.skip_2to3_optimize def setUp(self):
def test_convert_code_only(self): super(Mixin2to3TestCase, self).setUp()
# used to check if code gets converted properly. self.filename = self.mktempfile().name
code = "print 'test'"
with self.mktempfile() as fp: def check(self, source, wanted, **kwargs):
fp.write(code) source = textwrap.dedent(source)
with open(self.filename, 'w') as fp:
fp.write(source)
mixin2to3 = Mixin2to3() Mixin2to3()._run_2to3(**kwargs)
mixin2to3._run_2to3([fp.name])
expected = "print('test')"
with open(fp.name) as fp: wanted = textwrap.dedent(wanted)
with open(self.filename) as fp:
converted = fp.read() converted = fp.read()
self.assertMultiLineEqual(converted, wanted)
self.assertEqual(expected, converted) def test_conversion(self):
# check that code and doctests get converted
def test_doctests_only(self): self.check('''\
# used to check if doctests gets converted properly.
doctest = textwrap.dedent('''\
"""Example docstring. """Example docstring.
>>> print test >>> print test
test test
It works. It works.
"""''') """
print 'test'
with self.mktempfile() as fp: ''',
fp.write(doctest) '''\
mixin2to3 = Mixin2to3()
mixin2to3._run_2to3([fp.name])
expected = textwrap.dedent('''\
"""Example docstring. """Example docstring.
>>> print(test) >>> print(test)
test test
It works. It works.
"""\n''') """
print('test')
with open(fp.name) as fp: ''', # 2to3 adds a newline here
converted = fp.read() files=[self.filename])
self.assertEqual(expected, converted) def test_doctests_conversion(self):
# check that doctest files are converted
self.check('''\
Welcome to the doc.
>>> print test
test
''',
'''\
Welcome to the doc.
>>> print(test)
test
''',
doctests=[self.filename])
def test_additional_fixers(self): def test_additional_fixers(self):
# used to check if use_2to3_fixers works # make sure the fixers argument works
code = 'type(x) is not T' self.check("""\
echo('42')
with self.mktempfile() as fp: echo2('oh no')
fp.write(code) """,
"""\
mixin2to3 = Mixin2to3() print('42')
mixin2to3._run_2to3(files=[fp.name], doctests=[fp.name], print('oh no')
""",
files=[self.filename],
fixers=['packaging.tests.fixer']) fixers=['packaging.tests.fixer'])
expected = 'not isinstance(x, T)'
with open(fp.name) as fp:
converted = fp.read()
self.assertEqual(expected, converted)
def test_suite(): def test_suite():
return unittest.makeSuite(Mixin2to3TestCase) return unittest.makeSuite(Mixin2to3TestCase)

View File

@ -67,6 +67,23 @@ class RunTestCase(support.TempdirManager,
self.assertGreater(out, b'') self.assertGreater(out, b'')
self.assertEqual(err, b'') self.assertEqual(err, b'')
def test_list_commands(self):
status, out, err = assert_python_ok('-m', 'packaging.run', 'run',
'--list-commands')
# check that something is displayed
self.assertEqual(status, 0)
self.assertGreater(out, b'')
self.assertEqual(err, b'')
# make sure the manual grouping of commands is respected
check_position = out.find(b' check: ')
build_position = out.find(b' build: ')
self.assertTrue(check_position, out) # "out" printed as debugging aid
self.assertTrue(build_position, out)
self.assertLess(check_position, build_position, out)
# TODO test that custom commands don't break --list-commands
def test_suite(): def test_suite():
return unittest.makeSuite(RunTestCase) return unittest.makeSuite(RunTestCase)

View File

@ -853,13 +853,11 @@ def run_2to3(files, doctests_only=False, fixer_names=None,
# Make this class local, to delay import of 2to3 # Make this class local, to delay import of 2to3
from lib2to3.refactor import get_fixers_from_package, RefactoringTool from lib2to3.refactor import get_fixers_from_package, RefactoringTool
fixers = []
fixers = get_fixers_from_package('lib2to3.fixes') fixers = get_fixers_from_package('lib2to3.fixes')
if fixer_names: if fixer_names:
for fixername in fixer_names: for fixername in fixer_names:
fixers.extend(fixer for fixer in fixers.extend(get_fixers_from_package(fixername))
get_fixers_from_package(fixername))
r = RefactoringTool(fixers, options=options) r = RefactoringTool(fixers, options=options)
r.refactor(files, write=True, doctests_only=doctests_only) r.refactor(files, write=True, doctests_only=doctests_only)
@ -870,21 +868,23 @@ class Mixin2to3:
the class variables, or inherit from this class the class variables, or inherit from this class
to override how 2to3 is invoked. to override how 2to3 is invoked.
""" """
# provide list of fixers to run. # list of fixers to run; defaults to all implicit from lib2to3.fixers
# defaults to all from lib2to3.fixers
fixer_names = None fixer_names = None
# dict of options
# options dictionary
options = None options = None
# list of extra fixers to invoke
# list of fixers to invoke even though they are marked as explicit
explicit = None explicit = None
# TODO need a better way to add just one fixer from a package
# TODO need a way to exclude individual fixers
def run_2to3(self, files, doctests_only=False): def run_2to3(self, files, doctests_only=False):
""" Issues a call to util.run_2to3. """ """ Issues a call to util.run_2to3. """
return run_2to3(files, doctests_only, self.fixer_names, return run_2to3(files, doctests_only, self.fixer_names,
self.options, self.explicit) self.options, self.explicit)
# TODO provide initialize/finalize_options
RICH_GLOB = re.compile(r'\{([^}]*)\}') RICH_GLOB = re.compile(r'\{([^}]*)\}')
_CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]') _CHECK_RECURSIVE_GLOB = re.compile(r'[^/\\,{]\*\*|\*\*[^/\\,}]')
_CHECK_MISMATCH_SET = re.compile(r'^[^{]*\}|\{[^}]*$') _CHECK_MISMATCH_SET = re.compile(r'^[^{]*\}|\{[^}]*$')
@ -1049,7 +1049,6 @@ def cfg_to_args(path='setup.cfg'):
SETUP_TEMPLATE = """\ SETUP_TEMPLATE = """\
# This script was automatically generated by packaging # This script was automatically generated by packaging
import os
import codecs import codecs
from distutils.core import setup from distutils.core import setup
try: try:
@ -1057,6 +1056,7 @@ try:
except ImportError: except ImportError:
from configparser import RawConfigParser from configparser import RawConfigParser
%(split_multiline)s %(split_multiline)s
%(cfg_to_args)s %(cfg_to_args)s