diff options
Diffstat (limited to 'src')
49 files changed, 565 insertions, 357 deletions
diff --git a/src/DEVNOTES.txt b/src/DEVNOTES.txt index e59656a..6f53a17 100644 --- a/src/DEVNOTES.txt +++ b/src/DEVNOTES.txt @@ -15,7 +15,8 @@ Notes for developers - Renaming to do: - - DistributionMetadata > Metadata or ReleaseMetadata + - DistributionMetadata > Metadata + - Distribution > Release or Project - pkgutil > pkgutil.__init__ + new pkgutil.database (or better name) - RationalizedVersion > Version - suggest_rationalized_version > suggest diff --git a/src/distutils2/command/build.py b/src/distutils2/command/build.py index e1ad18a..3f7aad7 100644 --- a/src/distutils2/command/build.py +++ b/src/distutils2/command/build.py @@ -43,6 +43,12 @@ class build(Command): "forcibly build everything (ignore file timestamps)"), ('executable=', 'e', "specify final destination interpreter path (build.py)"), + ('use-2to3', None, + "use 2to3 to make source python 3.x compatible"), + ('convert-2to3-doctests', None, + "use 2to3 to convert doctests in seperate text files"), + ('use-2to3-fixers', None, + "list additional fixers opted for during 2to3 conversion"), ] boolean_options = ['debug', 'force'] @@ -66,6 +72,9 @@ class build(Command): self.debug = None self.force = 0 self.executable = None + self.use_2to3 = False + self.convert_2to3_doctests = None + self.use_2to3_fixers = None def finalize_options(self): if self.plat_name is None: diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py index 1a9d18b..98b1619 100644 --- a/src/distutils2/command/build_py.py +++ b/src/distutils2/command/build_py.py @@ -13,57 +13,10 @@ import distutils2 from distutils2.core import Command from distutils2.errors import DistutilsOptionError, DistutilsFileError from distutils2.util import convert_path -from distutils2.converter.refactor import DistutilsRefactoringTool +from distutils2.compat import Mixin2to3 # marking public APIs -__all__ = ['Mixin2to3', 'build_py'] - -try: - from distutils2.util import Mixin2to3 as _Mixin2to3 - from lib2to3.refactor import get_fixers_from_package - _CONVERT = True - _KLASS = _Mixin2to3 -except ImportError: - _CONVERT = False - _KLASS = object - -class Mixin2to3(_KLASS): - """ The base class which can be used for refactoring. When run under - Python 3.0, the run_2to3 method provided by Mixin2to3 is overridden. - When run on Python 2.x, it merely creates a class which overrides run_2to3, - yet does nothing in particular with it. - """ - if _CONVERT: - def _run_2to3(self, files, doctests=[]): - """ Takes a list of files and doctests, and performs conversion - on those. - - First, the files which contain the code(`files`) are converted. - - Second, the doctests in `files` are converted. - - Thirdly, the doctests in `doctests` are converted. - """ - - # Convert the ".py" files. - logging.info("Converting Python code") - _KLASS.run_2to3(self, files) - - # Convert the doctests in the ".py" files. - logging.info("Converting doctests with '.py' files") - _KLASS.run_2to3(self, files, doctests_only=True) - - # If the following conditions are met, then convert:- - # 1. User has specified the 'convert_2to3_doctests' option. So, we - # can expect that the list 'doctests' is not empty. - # 2. The default is allow distutils2 to allow conversion of text files - # containing doctests. It is set as - # distutils2.run_2to3_on_doctests - - if doctests != [] and distutils2.run_2to3_on_doctests: - logging.info("Converting text files which contain doctests") - _KLASS.run_2to3(self, doctests, doctests_only=True) - else: - # If run on Python 2.x, there is nothing to do. - def _run_2to3(self, files, doctests=[]): - pass +__all__ = ['build_py'] class build_py(Command, Mixin2to3): @@ -77,6 +30,12 @@ class build_py(Command, Mixin2to3): "also compile with optimization: -O1 for \"python -O\", " "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), ('force', 'f', "forcibly build everything (ignore file timestamps)"), + ('use-2to3', None, + "use 2to3 to make source python 3.x compatible"), + ('convert-2to3-doctests', None, + "use 2to3 to convert doctests in seperate text files"), + ('use-2to3-fixers', None, + "list additional fixers opted for during 2to3 conversion"), ] boolean_options = ['compile', 'force'] @@ -93,9 +52,15 @@ class build_py(Command, Mixin2to3): self.force = None self._updated_files = [] self._doctests_2to3 = [] + self.use_2to3 = False + self.convert_2to3_doctests = None + self.use_2to3_fixers = None def finalize_options(self): - self.set_undefined_options('build', 'build_lib', 'force') + self.set_undefined_options('build', + 'use_2to3', 'use_2to3_fixers', + 'convert_2to3_doctests', 'build_lib', + 'force') # Get the distribution options that are aliases for build_py # options -- list of packages and list of modules. @@ -145,8 +110,9 @@ class build_py(Command, Mixin2to3): self.build_packages() self.build_package_data() - if self.distribution.use_2to3 and self_updated_files: - self.run_2to3(self._updated_files, self._doctests_2to3) + if self.use_2to3 and self._updated_files: + self.run_2to3(self._updated_files, self._doctests_2to3, + self.use_2to3_fixers) self.byte_compile(self.get_outputs(include_bytecode=0)) diff --git a/src/distutils2/command/build_scripts.py b/src/distutils2/command/build_scripts.py index b62ade7..758e4d6 100644 --- a/src/distutils2/command/build_scripts.py +++ b/src/distutils2/command/build_scripts.py @@ -13,11 +13,12 @@ try: import sysconfig except ImportError: from distutils2._backport import sysconfig +from distutils2.compat import Mixin2to3 # check if Python is called on the first line with this expression first_line_re = re.compile('^#!.*python[0-9.]*([ \t].*)?$') -class build_scripts (Command): +class build_scripts (Command, Mixin2to3): description = "\"build\" scripts (copy and fixup #! line)" @@ -36,11 +37,16 @@ class build_scripts (Command): self.force = None self.executable = None self.outfiles = None + self.use_2to3 = False + self.convert_2to3_doctests = None + self.use_2to3_fixers = None def finalize_options (self): self.set_undefined_options('build', ('build_scripts', 'build_dir'), - 'force', 'executable') + 'use_2to3', 'use_2to3_fixers', + 'convert_2to3_doctests', 'force', + 'executable') self.scripts = self.distribution.scripts def get_source_files(self): @@ -49,8 +55,9 @@ class build_scripts (Command): def run (self): if not self.scripts: return - self.copy_scripts() - + copied_files = self.copy_scripts() + if self.use_2to3 and self.copied_files: + self._run_2to3(self.copied_files, fixers=self.use_2to3_fixers) def copy_scripts (self): """Copy each script listed in 'self.scripts'; if it's marked as a @@ -126,7 +133,7 @@ class build_scripts (Command): log.info("changing mode of %s from %o to %o", file, oldmode, newmode) os.chmod(file, newmode) - + return outfiles # copy_scripts () # class build_scripts diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py index a4e4b9b..17a4379 100644 --- a/src/distutils2/command/cmd.py +++ b/src/distutils2/command/cmd.py @@ -53,7 +53,7 @@ class Command(object): # Pre and post command hooks are run just before or just after the command # itself. They are simple functions that receive the command instance. They - # should be specified as dotted strings. + # are specified as callable objects or dotted strings (for lazy loading). pre_hook = None post_hook = None @@ -162,13 +162,12 @@ class Command(object): def dump_options(self, header=None, indent=""): - from distutils2.fancy_getopt import longopt_xlate if header is None: header = "command options for '%s':" % self.get_command_name() self.announce(indent + header, level=log.INFO) indent = indent + " " for (option, _, _) in self.user_options: - option = option.translate(longopt_xlate) + option = option.replace('-', '_') if option[-1] == "=": option = option[:-1] value = getattr(self, option) @@ -331,7 +330,6 @@ class Command(object): setattr(self, dst_option, getattr(src_cmd_obj, src_option)) - def get_finalized_command(self, command, create=1): """Wrapper around Distribution's 'get_command_obj()' method: find (create if necessary and 'create' is true) the command object for diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py index 1a6bd0e..c0c6fac 100644 --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -18,7 +18,6 @@ from distutils2.util import write_file from distutils2.util import convert_path, change_root, get_platform from distutils2.errors import DistutilsOptionError - class install(Command): description = "install everything from build directory" @@ -193,6 +192,7 @@ class install(Command): # array of user input is decided. Yes, it's quite complex!) def finalize_options(self): + """Finalizes options.""" # This method (and its pliant slaves, like 'finalize_unix()', # 'finalize_other()', and 'select_scheme()') is where the default # installation directories for modules, extension modules, and diff --git a/src/distutils2/command/install_distinfo.py b/src/distutils2/command/install_distinfo.py index 0e30f09..d39b7a1 100644 --- a/src/distutils2/command/install_distinfo.py +++ b/src/distutils2/command/install_distinfo.py @@ -25,9 +25,8 @@ except ImportError: class install_distinfo(Command): - """Install a .dist-info directory for the package""" - description = 'Install a .dist-info directory for the package' + description = 'install a .dist-info directory for the distribution' user_options = [ ('distinfo-dir=', None, diff --git a/src/distutils2/compat.py b/src/distutils2/compat.py new file mode 100644 index 0000000..86c733a --- /dev/null +++ b/src/distutils2/compat.py @@ -0,0 +1,64 @@ +""" distutils2.compat + +Used to provide classes, variables and imports which can be used to +support distutils2 across versions(2.x and 3.x) +""" + +import logging + + +try: + from distutils2.util import Mixin2to3 as _Mixin2to3 + from distutils2 import run_2to3_on_doctests + from lib2to3.refactor import get_fixers_from_package + _CONVERT = True + _KLASS = _Mixin2to3 +except ImportError: + _CONVERT = False + _KLASS = object + +# marking public APIs +__all__ = ['Mixin2to3'] + +class Mixin2to3(_KLASS): + """ The base class which can be used for refactoring. When run under + Python 3.0, the run_2to3 method provided by Mixin2to3 is overridden. + When run on Python 2.x, it merely creates a class which overrides run_2to3, + yet does nothing in particular with it. + """ + if _CONVERT: + def _run_2to3(self, files, doctests=[], fixers=[]): + """ Takes a list of files and doctests, and performs conversion + on those. + - First, the files which contain the code(`files`) are converted. + - Second, the doctests in `files` are converted. + - Thirdly, the doctests in `doctests` are converted. + """ + # if additional fixers are present, use them + if fixers: + self.fixer_names = fixers + + # Convert the ".py" files. + logging.info("Converting Python code") + _KLASS.run_2to3(self, files) + + # Convert the doctests in the ".py" files. + logging.info("Converting doctests with '.py' files") + _KLASS.run_2to3(self, files, doctests_only=True) + + # If the following conditions are met, then convert:- + # 1. User has specified the 'convert_2to3_doctests' option. So, we + # can expect that the list 'doctests' is not empty. + # 2. The default is allow distutils2 to allow conversion of text files + # containing doctests. It is set as + # distutils2.run_2to3_on_doctests + + if doctests != [] and run_2to3_on_doctests: + logging.info("Converting text files which contain doctests") + _KLASS.run_2to3(self, doctests, doctests_only=True) + else: + # If run on Python 2.x, there is nothing to do. + def _run_2to3(self, files, doctests=[], fixers=[]): + pass + + diff --git a/src/distutils2/converter/__init__.py b/src/distutils2/converter/__init__.py index a0f784e..e69de29 100644 --- a/src/distutils2/converter/__init__.py +++ b/src/distutils2/converter/__init__.py @@ -1,8 +0,0 @@ -"""distutils2.converter - -This package provide a refactoring tool to transform a -setuptools or distutils project into a distutils2 one. -""" - -from distutils2.converter.refactor import DistutilsRefactoringTool - diff --git a/src/distutils2/converter/refactor.py b/src/distutils2/converter/refactor.py index cab71ca..28fbd44 100644 --- a/src/distutils2/converter/refactor.py +++ b/src/distutils2/converter/refactor.py @@ -1,7 +1,5 @@ """distutils2.converter.refactor -Provides DistutilsRefactoringTool, a class that register fixers used -to refactor distutils or setuptools packages into distutils2 ones. """ try: from lib2to3.refactor import RefactoringTool @@ -11,18 +9,4 @@ except ImportError: _LIB2TO3 = False _DISTUTILS_FIXERS = ['distutils2.converter.fixers.fix_imports', - 'distutils2.converter.fixers.fix_setup_options'] - -if _LIB2TO3: - class DistutilsRefactoringTool(RefactoringTool): - - def __init__(self, fixer_names=_DISTUTILS_FIXERS, options=None, - explicit=None): - - super(DistutilsRefactoringTool, self).__init__(fixer_names, options, - explicit) -else: - class DistutilsRefactoringTool(object): - def __init__(self, *args, **kw): - raise NotImplementedError('Not available if run from Python < 2.6') - + 'distutils2.converter.fixers.fix_setup_options']
\ No newline at end of file diff --git a/src/distutils2/core.py b/src/distutils2/core.py index d84d16f..2a76a70 100644 --- a/src/distutils2/core.py +++ b/src/distutils2/core.py @@ -49,8 +49,8 @@ setup_keywords = ('distclass', 'script_name', 'script_args', 'options', 'maintainer', 'maintainer_email', 'url', 'license', 'description', 'long_description', 'keywords', 'platforms', 'classifiers', 'download_url', - 'requires', 'provides', 'obsoletes', 'use_2to3', - 'convert_2to3_doctests', + 'requires', 'provides', 'obsoletes', 'use_2to3_fixers', + 'convert_2to3_doctests', 'use_2to3_fixers' ) # Legal keyword arguments for the Extension constructor diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py index 6dd1b6e..65ce631 100644 --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -375,14 +375,13 @@ Common commands: (see '--help-commands' for more) val = parser.get(section, opt) opt = opt.replace('-', '_') - # ... although practicality beats purity :( + # Hooks use a suffix system to prevent being overriden + # by a config file processed later (i.e. a hook set in + # the user config file cannot be replaced by a hook + # set in a project config file, unless they have the + # same suffix). if opt.startswith("pre_hook.") or opt.startswith("post_hook."): hook_type, alias = opt.split(".") - # Hooks are somewhat exceptional, as they are - # gathered from many config files (not overriden as - # other options). - # The option_dict expects {"command": ("filename", # "value")} - # so for hooks, we store only the last config file processed hook_dict = opt_dict.setdefault(hook_type, (filename, {}))[1] hook_dict[alias] = val else: @@ -963,12 +962,34 @@ Common commands: (see '--help-commands' for more) self.have_run[command] = 1 def run_command_hooks(self, cmd_obj, hook_kind): + """Run hooks registered for that command and phase. + + *cmd_obj* is a finalized command object; *hook_kind* is either + 'pre_hook' or 'post_hook'. + """ + if hook_kind not in ('pre_hook', 'post_hook'): + raise ValueError('invalid hook kind: %r' % hook_kind) + hooks = getattr(cmd_obj, hook_kind) + if hooks is None: return - for hook in hooks.values(): - hook_func = resolve_name(hook) - hook_func(cmd_obj) + + for hook in hooks.itervalues(): + if isinstance(hook, basestring): + try: + hook_obj = resolve_name(hook) + except ImportError, e: + raise DistutilsModuleError(e) + else: + hook_obj = hook + + if not hasattr(hook_obj, '__call__'): + raise DistutilsOptionError('hook %r is not callable' % hook) + + log.info('running %s %s for command %s', + hook_kind, hook, cmd_obj.get_command_name()) + hook_obj(cmd_obj) # -- Distribution query methods ------------------------------------ def has_pure_modules(self): diff --git a/src/distutils2/fancy_getopt.py b/src/distutils2/fancy_getopt.py index 0eaa42f..fef85fc 100644 --- a/src/distutils2/fancy_getopt.py +++ b/src/distutils2/fancy_getopt.py @@ -26,11 +26,6 @@ longopt_re = re.compile(r'^%s$' % longopt_pat) # For recognizing "negative alias" options, eg. "quiet=!verbose" neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat)) -# This is used to translate long options to legitimate Python identifiers -# (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: @@ -115,8 +110,7 @@ class FancyGetopt(object): """Translate long option name 'long_option' to the form it has as an attribute of some object: ie., translate hyphens to underscores.""" - return string.translate(long_option, longopt_xlate) - + return long_option.replace('-', '_') def _check_alias_dict (self, aliases, what): assert isinstance(aliases, dict) @@ -472,7 +466,7 @@ def translate_longopt(opt): """Convert a long option name to a valid Python identifier by changing "-" to "_". """ - return string.translate(opt, longopt_xlate) + return opt.replace('-', '_') class OptionDummy(object): diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py index ea990f3..75fb851 100644 --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -160,12 +160,12 @@ class Crawler(BaseClient): the results. """ predicate = get_version_predicate(requirements) - if self._projects.has_key(predicate.name.lower()) and not force_update: + if predicate.name.lower() in self._projects and not force_update: return self._projects.get(predicate.name.lower()) prefer_final = self._get_prefer_final(prefer_final) self._process_index_page(predicate.name) - if not self._projects.has_key(predicate.name.lower()): + if predicate.name.lower() not in self._projects: raise ProjectNotFound() releases = self._projects.get(predicate.name.lower()) diff --git a/src/distutils2/manifest.py b/src/distutils2/manifest.py index 0d838ae..27b3dce 100644 --- a/src/distutils2/manifest.py +++ b/src/distutils2/manifest.py @@ -96,9 +96,24 @@ class Manifest(object): by 'add_defaults()' and 'read_template()') to the manifest file named by 'self.manifest'. """ + if os.path.isfile(path): + fp = open(path) + try: + first_line = fp.readline() + finally: + fp.close() + + if first_line != '# file GENERATED by distutils, do NOT edit\n': + logging.info("not writing to manually maintained " + "manifest file '%s'", path) + return + self.sort() self.remove_duplicates() - write_file(path, self.files) + content = self.files[:] + content.insert(0, '# file GENERATED by distutils, do NOT edit') + logging.info("writing manifest file '%s'", path) + write_file(path, content) def read(self, path): """Read the manifest file (named by 'self.manifest') and use it to diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py index 5fa1d88..4831fac 100644 --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -384,6 +384,7 @@ class DistributionMetadata(object): if not is_valid_predicate(v.split(';')[0]): warn('"%s" is not a valid predicate (field "%s")' % (v, name)) + # FIXME this rejects UNKNOWN, is that right? elif name in _VERSIONS_FIELDS and value is not None: if not is_valid_versions(value): warn('"%s" is not a valid version (field "%s")' % diff --git a/src/distutils2/tests/Setup.sample b/src/distutils2/tests/Setup.sample deleted file mode 100755 index 36c4290..0000000 --- a/src/distutils2/tests/Setup.sample +++ /dev/null @@ -1,67 +0,0 @@ -# Setup file from the pygame project - -#--StartConfig -SDL = -I/usr/include/SDL -D_REENTRANT -lSDL -FONT = -lSDL_ttf -IMAGE = -lSDL_image -MIXER = -lSDL_mixer -SMPEG = -lsmpeg -PNG = -lpng -JPEG = -ljpeg -SCRAP = -lX11 -PORTMIDI = -lportmidi -PORTTIME = -lporttime -#--EndConfig - -#DEBUG = -C-W -C-Wall -DEBUG = - -#the following modules are optional. you will want to compile -#everything you can, but you can ignore ones you don't have -#dependencies for, just comment them out - -imageext src/imageext.c $(SDL) $(IMAGE) $(PNG) $(JPEG) $(DEBUG) -font src/font.c $(SDL) $(FONT) $(DEBUG) -mixer src/mixer.c $(SDL) $(MIXER) $(DEBUG) -mixer_music src/music.c $(SDL) $(MIXER) $(DEBUG) -_numericsurfarray src/_numericsurfarray.c $(SDL) $(DEBUG) -_numericsndarray src/_numericsndarray.c $(SDL) $(MIXER) $(DEBUG) -movie src/movie.c $(SDL) $(SMPEG) $(DEBUG) -scrap src/scrap.c $(SDL) $(SCRAP) $(DEBUG) -_camera src/_camera.c src/camera_v4l2.c src/camera_v4l.c $(SDL) $(DEBUG) -pypm src/pypm.c $(SDL) $(PORTMIDI) $(PORTTIME) $(DEBUG) - -GFX = src/SDL_gfx/SDL_gfxPrimitives.c -#GFX = src/SDL_gfx/SDL_gfxBlitFunc.c src/SDL_gfx/SDL_gfxPrimitives.c -gfxdraw src/gfxdraw.c $(SDL) $(GFX) $(DEBUG) - - - -#these modules are required for pygame to run. they only require -#SDL as a dependency. these should not be altered - -base src/base.c $(SDL) $(DEBUG) -cdrom src/cdrom.c $(SDL) $(DEBUG) -color src/color.c $(SDL) $(DEBUG) -constants src/constants.c $(SDL) $(DEBUG) -display src/display.c $(SDL) $(DEBUG) -event src/event.c $(SDL) $(DEBUG) -fastevent src/fastevent.c src/fastevents.c $(SDL) $(DEBUG) -key src/key.c $(SDL) $(DEBUG) -mouse src/mouse.c $(SDL) $(DEBUG) -rect src/rect.c $(SDL) $(DEBUG) -rwobject src/rwobject.c $(SDL) $(DEBUG) -surface src/surface.c src/alphablit.c src/surface_fill.c $(SDL) $(DEBUG) -surflock src/surflock.c $(SDL) $(DEBUG) -time src/time.c $(SDL) $(DEBUG) -joystick src/joystick.c $(SDL) $(DEBUG) -draw src/draw.c $(SDL) $(DEBUG) -image src/image.c $(SDL) $(DEBUG) -overlay src/overlay.c $(SDL) $(DEBUG) -transform src/transform.c src/rotozoom.c src/scale2x.c src/scale_mmx.c $(SDL) $(DEBUG) -mask src/mask.c src/bitmask.c $(SDL) $(DEBUG) -bufferproxy src/bufferproxy.c $(SDL) $(DEBUG) -pixelarray src/pixelarray.c $(SDL) $(DEBUG) -_arraysurfarray src/_arraysurfarray.c $(SDL) $(DEBUG) - - diff --git a/src/distutils2/tests/fixer/__init__.py b/src/distutils2/tests/fixer/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/distutils2/tests/fixer/__init__.py diff --git a/src/distutils2/tests/fixer/fix_idioms.py b/src/distutils2/tests/fixer/fix_idioms.py new file mode 100644 index 0000000..9cfac51 --- /dev/null +++ b/src/distutils2/tests/fixer/fix_idioms.py @@ -0,0 +1,134 @@ +"""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.set_prefix("") + T.set_prefix(" ") + test = Call(Name("isinstance"), [x, Comma(), T]) + if "n" in results: + test.set_prefix(" ") + test = Node(syms.not_test, [Name("not"), test]) + test.set_prefix(node.get_prefix()) + return test + + def transform_while(self, node, results): + one = results["while"] + one.replace(Name("True", prefix=one.get_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.get_prefix())) + elif simple_expr: + new = simple_expr.clone() + new.set_prefix("") + simple_expr.replace(Call(Name("sorted"), [new], + prefix=simple_expr.get_prefix())) + else: + raise RuntimeError("should not have reached here") + sort_stmt.remove() + if next_stmt: + next_stmt[0].set_prefix(sort_stmt.get_prefix()) diff --git a/src/distutils2/tests/support.py b/src/distutils2/tests/support.py index 0c69e58..1ff2217 100644 --- a/src/distutils2/tests/support.py +++ b/src/distutils2/tests/support.py @@ -4,13 +4,14 @@ Always import unittest from this module, it will be the right version (standard library unittest for 3.2 and higher, third-party unittest2 release for older versions). -Four helper classes are provided: LoggingSilencer, TempdirManager, EnvironGuard -and WarningsCatcher. They are written to be used as mixins, e.g. :: +Four helper classes are provided: LoggingCatcher, TempdirManager, +EnvironGuard and WarningsCatcher. They are written to be used as mixins, +e.g. :: from distutils2.tests.support import unittest - from distutils2.tests.support import LoggingSilencer + from distutils2.tests.support import LoggingCatcher - class SomeTestCase(LoggingSilencer, unittest.TestCase): + class SomeTestCase(LoggingCatcher, unittest.TestCase): If you need to define a setUp method on your test class, you have to call the mixin class' setUp method or it won't work (same thing for @@ -43,11 +44,11 @@ else: # external release of same package for older versions import unittest2 as unittest -__all__ = ['LoggingSilencer', 'TempdirManager', 'EnvironGuard', - 'DummyCommand', 'unittest'] +__all__ = ['LoggingCatcher', 'WarningsCatcher', 'TempdirManager', + 'EnvironGuard', 'DummyCommand', 'unittest'] -class LoggingSilencer(object): +class LoggingCatcher(object): """TestCase-compatible mixin to catch logging calls. Every log message that goes through distutils2.log will get appended to @@ -57,7 +58,7 @@ class LoggingSilencer(object): """ def setUp(self): - super(LoggingSilencer, self).setUp() + super(LoggingCatcher, self).setUp() self.threshold = log.set_threshold(FATAL) # when log is replaced by logging we won't need # such monkey-patching anymore @@ -68,7 +69,7 @@ class LoggingSilencer(object): def tearDown(self): log.set_threshold(self.threshold) log.Log._log = self._old_log - super(LoggingSilencer, self).tearDown() + super(LoggingCatcher, self).tearDown() def _log(self, level, msg, args): if level not in (DEBUG, INFO, WARN, ERROR, FATAL): @@ -92,6 +93,30 @@ class LoggingSilencer(object): del self.logs[:] +class LoggingSilencer(object): + "Class that raises an exception to make sure the renaming is noticed." + + def __init__(self, *args): + raise DeprecationWarning("LoggingSilencer renamed to LoggingCatcher") + + +class WarningsCatcher(object): + + def setUp(self): + self._orig_showwarning = warnings.showwarning + warnings.showwarning = self._record_showwarning + self.warnings = [] + + def _record_showwarning(self, message, category, filename, lineno, + file=None, line=None): + self.warnings.append({"message": message, "category": category, + "filename": filename, "lineno": lineno, + "file": file, "line": line}) + + def tearDown(self): + warnings.showwarning = self._orig_showwarning + + class TempdirManager(object): """TestCase-compatible mixin to create temporary directories and files. @@ -172,25 +197,6 @@ class EnvironGuard(object): super(EnvironGuard, self).tearDown() -class WarningsCatcher(object): - - def setUp(self): - self._orig_showwarning = warnings.showwarning - warnings.showwarning = self._record_showwarning - self.warnings = [] - - def _record_showwarning(self, message, category, filename, lineno, file=None, line=None): - self.warnings.append({"message": message, - "category": category, - "filename": filename, - "lineno": lineno, - "file": file, - "line": line}) - - def tearDown(self): - warnings.showwarning = self._orig_showwarning - - class DummyCommand(object): """Class to store options for retrieval via set_undefined_options(). diff --git a/src/distutils2/tests/test_Mixin2to3.py b/src/distutils2/tests/test_Mixin2to3.py index 4b397c7..186d128 100644 --- a/src/distutils2/tests/test_Mixin2to3.py +++ b/src/distutils2/tests/test_Mixin2to3.py @@ -1,10 +1,11 @@ """Tests for distutils.command.build_py.""" import sys +import logging import distutils2 from distutils2.tests import support from distutils2.tests.support import unittest -from distutils2.command.build_py import Mixin2to3 +from distutils2.compat import Mixin2to3 class Mixin2to3TestCase(support.TempdirManager, unittest.TestCase): @@ -46,6 +47,25 @@ class Mixin2to3TestCase(support.TempdirManager, unittest.TestCase): self.assertEquals(new_doctest_content, converted_doctest_content) + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') + def test_additional_fixers(self): + # used to check if use_2to3_fixers works + from distutils2.tests import fixer + code_content = "type(x) is T" + code_handle = self.mktempfile() + code_name = code_handle.name + + code_handle.write(code_content) + code_handle.flush() + + mixin2to3 = Mixin2to3() + + mixin2to3._run_2to3(files=[code_name], + fixers=['distutils2.tests.fixer']) + converted_code_content = "isinstance(x, T)" + new_code_content = "".join(open(code_name).readlines()) + self.assertEquals(new_code_content, converted_code_content) + def test_suite(): return unittest.makeSuite(Mixin2to3TestCase) diff --git a/src/distutils2/tests/test_bdist_dumb.py b/src/distutils2/tests/test_bdist_dumb.py index 770827c..a33210e 100644 --- a/src/distutils2/tests/test_bdist_dumb.py +++ b/src/distutils2/tests/test_bdist_dumb.py @@ -27,7 +27,7 @@ setup(name='foo', version='0.1', py_modules=['foo'], """ class BuildDumbTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, support.EnvironGuard, unittest.TestCase): diff --git a/src/distutils2/tests/test_bdist_msi.py b/src/distutils2/tests/test_bdist_msi.py index 4ad54ef..0d3bda8 100644 --- a/src/distutils2/tests/test_bdist_msi.py +++ b/src/distutils2/tests/test_bdist_msi.py @@ -7,7 +7,7 @@ from distutils2.tests import support from distutils2.tests.support import unittest class BDistMSITestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, unittest.TestCase): @unittest.skipUnless(sys.platform == "win32", "runs only on win32") diff --git a/src/distutils2/tests/test_bdist_wininst.py b/src/distutils2/tests/test_bdist_wininst.py index bd4f87e..f154463 100644 --- a/src/distutils2/tests/test_bdist_wininst.py +++ b/src/distutils2/tests/test_bdist_wininst.py @@ -7,7 +7,7 @@ from distutils2.tests import support from distutils2.tests.support import unittest class BuildWinInstTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, unittest.TestCase): def test_get_exe_bytes(self): diff --git a/src/distutils2/tests/test_build.py b/src/distutils2/tests/test_build.py index abf9ef8..1245111 100644 --- a/src/distutils2/tests/test_build.py +++ b/src/distutils2/tests/test_build.py @@ -11,7 +11,7 @@ except ImportError: from distutils2._backport.sysconfig import get_platform class BuildTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, unittest.TestCase): def test_finalize_options(self): diff --git a/src/distutils2/tests/test_build_clib.py b/src/distutils2/tests/test_build_clib.py index 348f700..b8d51d4 100644 --- a/src/distutils2/tests/test_build_clib.py +++ b/src/distutils2/tests/test_build_clib.py @@ -9,7 +9,7 @@ from distutils2.util import find_executable from distutils2.tests.support import unittest class BuildCLibTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, unittest.TestCase): def test_check_library_dist(self): diff --git a/src/distutils2/tests/test_build_ext.py b/src/distutils2/tests/test_build_ext.py index 5c1f8fe..cde943a 100644 --- a/src/distutils2/tests/test_build_ext.py +++ b/src/distutils2/tests/test_build_ext.py @@ -26,7 +26,7 @@ def _get_source_filename(): return os.path.join(CURDIR, 'xxmodule.c') class BuildExtTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, unittest.TestCase): def setUp(self): # Create a simple test environment diff --git a/src/distutils2/tests/test_build_py.py b/src/distutils2/tests/test_build_py.py index aed24d3..4ee15f1 100644 --- a/src/distutils2/tests/test_build_py.py +++ b/src/distutils2/tests/test_build_py.py @@ -13,7 +13,7 @@ from distutils2.tests.support import unittest class BuildPyTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, unittest.TestCase): def test_package_data(self): @@ -37,7 +37,10 @@ class BuildPyTestCase(support.TempdirManager, dist.script_name = os.path.join(sources, "setup.py") dist.command_obj["build"] = support.DummyCommand( force=0, - build_lib=destination) + build_lib=destination, + use_2to3_fixers=None, + convert_2to3_doctests=None, + use_2to3=False) dist.packages = ["pkg"] dist.package_data = {"pkg": ["README.txt"]} dist.package_dir = {"pkg": sources} diff --git a/src/distutils2/tests/test_build_scripts.py b/src/distutils2/tests/test_build_scripts.py index 6a724bd..3982d99 100644 --- a/src/distutils2/tests/test_build_scripts.py +++ b/src/distutils2/tests/test_build_scripts.py @@ -14,7 +14,7 @@ from distutils2.tests.support import unittest class BuildScriptsTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, unittest.TestCase): def test_default_settings(self): @@ -49,7 +49,10 @@ class BuildScriptsTestCase(support.TempdirManager, dist.command_obj["build"] = support.DummyCommand( build_scripts=target, force=1, - executable=sys.executable + executable=sys.executable, + use_2to3=False, + use_2to3_fixers=None, + convert_2to3_doctests=None ) return build_scripts(dist) diff --git a/src/distutils2/tests/test_check.py b/src/distutils2/tests/test_check.py index 02d171f..e6242cf 100644 --- a/src/distutils2/tests/test_check.py +++ b/src/distutils2/tests/test_check.py @@ -6,7 +6,7 @@ from distutils2.tests import support from distutils2.tests.support import unittest from distutils2.errors import DistutilsSetupError -class CheckTestCase(support.LoggingSilencer, +class CheckTestCase(support.LoggingCatcher, support.TempdirManager, unittest.TestCase): diff --git a/src/distutils2/tests/test_clean.py b/src/distutils2/tests/test_clean.py index dacf20a..acc5b66 100755..100644 --- a/src/distutils2/tests/test_clean.py +++ b/src/distutils2/tests/test_clean.py @@ -8,7 +8,7 @@ from distutils2.tests import support from distutils2.tests.support import unittest class cleanTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, unittest.TestCase): def test_simple_run(self): diff --git a/src/distutils2/tests/test_config_cmd.py b/src/distutils2/tests/test_config_cmd.py index 1995b0c..eab12fd 100644 --- a/src/distutils2/tests/test_config_cmd.py +++ b/src/distutils2/tests/test_config_cmd.py @@ -7,7 +7,7 @@ from distutils2.tests import support from distutils2.tests.support import unittest from distutils2 import log -class ConfigTestCase(support.LoggingSilencer, +class ConfigTestCase(support.LoggingCatcher, support.TempdirManager, unittest.TestCase): diff --git a/src/distutils2/tests/test_converter.py b/src/distutils2/tests/test_converter.py deleted file mode 100644 index c4cb387..0000000 --- a/src/distutils2/tests/test_converter.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Tests for distutils.converter.""" -import os -import sys -from distutils2.converter import DistutilsRefactoringTool -from distutils2.tests.support import unittest - -_CURDIR = os.path.dirname(__file__) - -def _read_file(path): - # yes, distutils2 is 2.4 compatible, so, no with... - f = open(path) - try: - return f.read() - finally: - f.close() - - -class ConverterTestCase(unittest.TestCase): - - @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') - def test_conversions(self): - # for all XX_before in the conversions/ dir - # we run the refactoring tool - ref = DistutilsRefactoringTool() - convdir = os.path.join(_CURDIR, 'conversions') - for file_ in os.listdir(convdir): - if 'after' in file_ or not file_.endswith('py'): - continue - original = _read_file(os.path.join(convdir, file_)) - wanted = file_.replace('before', 'after') - wanted = _read_file(os.path.join(convdir, wanted)) - res = ref.refactor_string(original, 'setup.py') - self.assertEqual(str(res), wanted) - -def test_suite(): - return unittest.makeSuite(ConverterTestCase) - -if __name__ == '__main__': - unittest.main(defaultTest="test_suite") diff --git a/src/distutils2/tests/test_depgraph.py b/src/distutils2/tests/test_depgraph.py index 6ab265a..fa38b91 100644 --- a/src/distutils2/tests/test_depgraph.py +++ b/src/distutils2/tests/test_depgraph.py @@ -13,7 +13,7 @@ try: except ImportError: import StringIO -class DepGraphTestCase(support.LoggingSilencer, +class DepGraphTestCase(support.LoggingCatcher, unittest.TestCase): DISTROS_DIST = ('choxie', 'grammar', 'towel-stuff') diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py index 5858ad5..ba86274 100644 --- a/src/distutils2/tests/test_dist.py +++ b/src/distutils2/tests/test_dist.py @@ -4,15 +4,18 @@ import os import StringIO import sys +import warnings import textwrap -from distutils2.dist import Distribution, fix_help_options, DistributionMetadata -from distutils2.command.cmd import Command import distutils2.dist +from distutils2.dist import Distribution, fix_help_options +from distutils2.command.cmd import Command +from distutils2.errors import DistutilsModuleError, DistutilsOptionError from distutils2.tests import TESTFN, captured_stdout from distutils2.tests import support from distutils2.tests.support import unittest + class test_dist(Command): """Sample distutils2 extension command.""" @@ -22,7 +25,9 @@ class test_dist(Command): def initialize_options(self): self.sample_option = None - def finalize_options(self): pass + + def finalize_options(self): + pass class TestDistribution(Distribution): @@ -38,7 +43,7 @@ class TestDistribution(Distribution): class DistributionTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, support.WarningsCatcher, support.EnvironGuard, unittest.TestCase): @@ -63,7 +68,7 @@ class DistributionTestCase(support.TempdirManager, def test_debug_mode(self): f = open(TESTFN, "w") try: - f.write("[global]") + f.write("[global]\n") f.write("command_packages = foo.bar, splat") finally: f.close() @@ -104,8 +109,8 @@ class DistributionTestCase(support.TempdirManager, sys.argv.append("build") f = open(TESTFN, "w") try: - print >>f, "[global]" - print >>f, "command_packages = foo.bar, splat" + print >> f, "[global]" + print >> f, "command_packages = foo.bar, splat" f.close() d = self.create_distribution([TESTFN]) self.assertEqual(d.get_command_packages(), @@ -138,7 +143,6 @@ class DistributionTestCase(support.TempdirManager, 'summary': u'Café torréfié', 'description': u'Héhéhé'}) - # let's make sure the file can be written # with Unicode fields. they are encoded with # PKG_INFO_ENCODING @@ -186,6 +190,7 @@ class DistributionTestCase(support.TempdirManager, # Here is an example of how it's used out there: # http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html#specifying-customizations + # index.html#specifying-customizations cls = Distribution dist = cls(attrs={'author': 'xxx', 'name': 'xxx', @@ -218,7 +223,6 @@ class DistributionTestCase(support.TempdirManager, cmds = dist.get_command_packages() self.assertEqual(cmds, ['distutils2.command', 'one', 'two']) - def test_announce(self): # make sure the level is known dist = Distribution() @@ -256,7 +260,7 @@ class DistributionTestCase(support.TempdirManager, os.path.expanduser = old_expander # make sure --no-user-cfg disables the user cfg file - self.assertEqual(len(all_files)-1, len(files)) + self.assertEqual(len(all_files) - 1, len(files)) def test_special_hooks_parsing(self): temp_home = self.mkdtemp() @@ -264,46 +268,67 @@ class DistributionTestCase(support.TempdirManager, os.path.join(temp_home, "config2.cfg")] # Store two aliased hooks in config files - self.write_file((temp_home, "config1.cfg"), '[test_dist]\npre-hook.a = type') - self.write_file((temp_home, "config2.cfg"), '[test_dist]\npre-hook.b = type') + self.write_file((temp_home, "config1.cfg"), + '[test_dist]\npre-hook.a = type') + self.write_file((temp_home, "config2.cfg"), + '[test_dist]\npre-hook.b = type') sys.argv.extend(["--command-packages", "distutils2.tests", "test_dist"]) - cmd = self.create_distribution(config_files).get_command_obj("test_dist") + dist = self.create_distribution(config_files) + cmd = dist.get_command_obj("test_dist") + self.assertEqual(cmd.pre_hook, {"a": 'type', "b": 'type'}) def test_hooks_get_run(self): temp_home = self.mkdtemp() config_file = os.path.join(temp_home, "config1.cfg") + hooks_module = os.path.join(temp_home, "testhooks.py") - self.write_file((temp_home, "config1.cfg"), textwrap.dedent(''' + self.write_file(config_file, textwrap.dedent(''' [test_dist] - pre-hook.test = distutils2.tests.test_dist.DistributionTestCase.log_pre_call - post-hook.test = distutils2.tests.test_dist.DistributionTestCase.log_post_call''')) + pre-hook.test = testhooks.log_pre_call + post-hook.test = testhooks.log_post_call''')) + + self.write_file(hooks_module, textwrap.dedent(''' + record = [] + + def log_pre_call(cmd): + record.append('pre-%s' % cmd.get_command_name()) + + def log_post_call(cmd): + record.append('post-%s' % cmd.get_command_name()) + ''')) sys.argv.extend(["--command-packages", "distutils2.tests", "test_dist"]) + d = self.create_distribution([config_file]) cmd = d.get_command_obj("test_dist") # prepare the call recorders - record = [] - DistributionTestCase.log_pre_call = staticmethod(lambda _cmd: record.append(('pre', _cmd))) - DistributionTestCase.log_post_call = staticmethod(lambda _cmd: record.append(('post', _cmd))) - test_dist.run = lambda _cmd: record.append(('run', _cmd)) - test_dist.finalize_options = lambda _cmd: record.append(('finalize_options', _cmd)) + sys.path.append(temp_home) + from testhooks import record + + self.addCleanup(setattr, cmd, 'run', cmd.run) + self.addCleanup(setattr, cmd, 'finalize_options', + cmd.finalize_options) + + cmd.run = lambda: record.append('run') + cmd.finalize_options = lambda: record.append('finalize') d.run_command('test_dist') - self.assertEqual(record, [('finalize_options', cmd), - ('pre', cmd), - ('run', cmd), - ('post', cmd)]) + + self.assertEqual(record, ['finalize', + 'pre-test_dist', + 'run', + 'post-test_dist']) class MetadataTestCase(support.TempdirManager, support.EnvironGuard, - support.LoggingSilencer, unittest.TestCase): + support.LoggingCatcher, unittest.TestCase): def setUp(self): super(MetadataTestCase, self).setUp() @@ -484,6 +509,7 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard, self.assertEqual(metadata['obsoletes'], []) self.assertEqual(metadata['requires-dist'], ['foo']) + def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(DistributionTestCase)) diff --git a/src/distutils2/tests/test_extension.py b/src/distutils2/tests/test_extension.py index d98ce43..d98ce43 100755..100644 --- a/src/distutils2/tests/test_extension.py +++ b/src/distutils2/tests/test_extension.py diff --git a/src/distutils2/tests/test_install.py b/src/distutils2/tests/test_install.py index f3d9c59..5ec5f91 100644 --- a/src/distutils2/tests/test_install.py +++ b/src/distutils2/tests/test_install.py @@ -25,7 +25,7 @@ from distutils2.tests.support import unittest class InstallTestCase(support.TempdirManager, support.EnvironGuard, - support.LoggingSilencer, + support.LoggingCatcher, unittest.TestCase): def test_home_installation_scheme(self): diff --git a/src/distutils2/tests/test_install_data.py b/src/distutils2/tests/test_install_data.py index 48cea63..9cae44a 100644 --- a/src/distutils2/tests/test_install_data.py +++ b/src/distutils2/tests/test_install_data.py @@ -8,7 +8,7 @@ from distutils2.tests import support from distutils2.tests.support import unittest class InstallDataTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, support.EnvironGuard, unittest.TestCase): diff --git a/src/distutils2/tests/test_install_distinfo.py b/src/distutils2/tests/test_install_distinfo.py index fbfa722..1bad24b 100644 --- a/src/distutils2/tests/test_install_distinfo.py +++ b/src/distutils2/tests/test_install_distinfo.py @@ -34,7 +34,7 @@ class DummyInstallCmd(Command): class InstallDistinfoTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, support.EnvironGuard, unittest.TestCase): diff --git a/src/distutils2/tests/test_install_headers.py b/src/distutils2/tests/test_install_headers.py index 4e0dce7..5e40257 100644 --- a/src/distutils2/tests/test_install_headers.py +++ b/src/distutils2/tests/test_install_headers.py @@ -8,7 +8,7 @@ from distutils2.tests import support from distutils2.tests.support import unittest class InstallHeadersTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, support.EnvironGuard, unittest.TestCase): diff --git a/src/distutils2/tests/test_install_lib.py b/src/distutils2/tests/test_install_lib.py index a96c889..75eb69e 100644 --- a/src/distutils2/tests/test_install_lib.py +++ b/src/distutils2/tests/test_install_lib.py @@ -16,7 +16,7 @@ except AttributeError: bytecode_support = False class InstallLibTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, support.EnvironGuard, unittest.TestCase): diff --git a/src/distutils2/tests/test_install_scripts.py b/src/distutils2/tests/test_install_scripts.py index be65d65..2cfab03 100644 --- a/src/distutils2/tests/test_install_scripts.py +++ b/src/distutils2/tests/test_install_scripts.py @@ -10,7 +10,7 @@ from distutils2.tests.support import unittest class InstallScriptsTestCase(support.TempdirManager, - support.LoggingSilencer, + support.LoggingCatcher, unittest.TestCase): def test_default_settings(self): diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py index 49a4757..931f1ba 100644 --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -7,11 +7,11 @@ from StringIO import StringIO from distutils2.metadata import (DistributionMetadata, _interpret, PKG_INFO_PREFERRED_VERSION) from distutils2.tests import run_unittest -from distutils2.tests.support import unittest, LoggingSilencer +from distutils2.tests.support import unittest, LoggingCatcher from distutils2.errors import (MetadataConflictError, MetadataUnrecognizedVersionError) -class DistributionMetadataTestCase(LoggingSilencer, unittest.TestCase): +class DistributionMetadataTestCase(LoggingCatcher, unittest.TestCase): def test_instantiation(self): PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') diff --git a/src/distutils2/tests/test_register.py b/src/distutils2/tests/test_register.py index bd4b732..d232978 100644 --- a/src/distutils2/tests/test_register.py +++ b/src/distutils2/tests/test_register.py @@ -167,6 +167,8 @@ class RegisterTestCase(support.TempdirManager, support.EnvironGuard, register_module.raw_input = inputs.__call__ try: # let's run the command + # FIXME does this send a real request? use a mock server + # also, silence self.announce (with LoggingCatcher) cmd.run() finally: del register_module.raw_input diff --git a/src/distutils2/tests/test_sdist.py b/src/distutils2/tests/test_sdist.py index 68ee33b..2bd1dfd 100644 --- a/src/distutils2/tests/test_sdist.py +++ b/src/distutils2/tests/test_sdist.py @@ -44,6 +44,7 @@ setup(name='fake') """ MANIFEST = """\ +# file GENERATED by distutils, do NOT edit README inroot.txt setup.py @@ -56,7 +57,7 @@ somecode%(sep)sdoc.dat somecode%(sep)sdoc.txt """ -class SDistTestCase(support.TempdirManager, support.LoggingSilencer, +class SDistTestCase(support.TempdirManager, support.LoggingCatcher, support.EnvironGuard, unittest.TestCase): def setUp(self): @@ -344,17 +345,76 @@ class SDistTestCase(support.TempdirManager, support.LoggingSilencer, archive.close() def test_get_file_list(self): + # make sure MANIFEST is recalculated dist, cmd = self.get_cmd() - cmd.finalize_options() - cmd.template = os.path.join(self.tmp_dir, 'MANIFEST.in') - f = open(cmd.template, 'w') + + # filling data_files by pointing files in package_data + dist.package_data = {'somecode': ['*.txt']} + self.write_file((self.tmp_dir, 'somecode', 'doc.txt'), '#') + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEquals(len(manifest), 5) + + # adding a file + self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') + + # make sure build_py is reinitinialized, like a fresh run + build_py = dist.get_command_obj('build_py') + build_py.finalized = False + build_py.ensure_finalized() + + cmd.run() + + f = open(cmd.manifest) + try: + manifest2 = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + # do we have the new file in MANIFEST ? + self.assertEquals(len(manifest2), 6) + self.assertIn('doc2.txt', manifest2[-1]) + + def test_manifest_marker(self): + # check that autogenerated MANIFESTs have a marker + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + cmd.run() + + f = open(cmd.manifest) + try: + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] + finally: + f.close() + + self.assertEqual(manifest[0], + '# file GENERATED by distutils, do NOT edit') + + def test_manual_manifest(self): + # check that a MANIFEST without a marker is left alone + dist, cmd = self.get_cmd() + cmd.ensure_finalized() + self.write_file((self.tmp_dir, cmd.manifest), 'README.manual') + cmd.run() + + f = open(cmd.manifest) try: - f.write('include MANIFEST.in\n') + manifest = [line.strip() for line in f.read().split('\n') + if line.strip() != ''] finally: f.close() - cmd.get_file_list() - self.assertIn('MANIFEST.in', cmd.filelist.files) + self.assertEqual(manifest, ['README.manual']) def test_suite(): return unittest.makeSuite(SDistTestCase) diff --git a/src/distutils2/tests/test_upload.py b/src/distutils2/tests/test_upload.py index 3959872..a12e227 100644 --- a/src/distutils2/tests/test_upload.py +++ b/src/distutils2/tests/test_upload.py @@ -41,7 +41,7 @@ repository:http://another.pypi/ class UploadTestCase(support.TempdirManager, support.EnvironGuard, - PyPIServerTestCase): + support.LoggingCatcher, PyPIServerTestCase): def setUp(self): super(UploadTestCase, self).setUp() diff --git a/src/distutils2/tests/test_upload_docs.py b/src/distutils2/tests/test_upload_docs.py index e47e1aa..bccc0d5 100644 --- a/src/distutils2/tests/test_upload_docs.py +++ b/src/distutils2/tests/test_upload_docs.py @@ -53,7 +53,7 @@ password = long_island """ class UploadDocsTestCase(support.TempdirManager, support.EnvironGuard, - PyPIServerTestCase): + support.LoggingCatcher, PyPIServerTestCase): def setUp(self): super(UploadDocsTestCase, self).setUp() diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py index ea07636..85352d4 100644 --- a/src/distutils2/tests/test_util.py +++ b/src/distutils2/tests/test_util.py @@ -343,9 +343,13 @@ class UtilTestCase(support.EnvironGuard, self.assertEqual(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3', 'pkg1.pkg3.pkg6'])) def test_resolve_name(self): - self.assertEqual(UtilTestCase, resolve_name("distutils2.tests.test_util.UtilTestCase")) - self.assertEqual(UtilTestCase.test_resolve_name, - resolve_name("distutils2.tests.test_util.UtilTestCase.test_resolve_name")) + self.assertEqual(str(42), resolve_name('__builtin__.str')(42)) + self.assertEqual( + UtilTestCase.__name__, + resolve_name("distutils2.tests.test_util.UtilTestCase").__name__) + self.assertEqual( + UtilTestCase.test_resolve_name.__name__, + resolve_name("distutils2.tests.test_util.UtilTestCase.test_resolve_name").__name__) self.assertRaises(ImportError, resolve_name, "distutils2.tests.test_util.UtilTestCaseNot") diff --git a/src/distutils2/util.py b/src/distutils2/util.py index 5673432..12b1022 100644 --- a/src/distutils2/util.py +++ b/src/distutils2/util.py @@ -637,7 +637,10 @@ def find_packages(paths=(os.curdir,), exclude=()): return packages def resolve_name(name): - """Resolve a name like ``module.object`` to an object and return it.""" + """Resolve a name like ``module.object`` to an object and return it. + + Raise ImportError if the module or name is not found. + """ parts = name.split('.') cursor = len(parts) module_name, rest = parts[:cursor], parts[cursor:] @@ -652,61 +655,15 @@ def resolve_name(name): cursor -= 1 module_name = parts[:cursor] rest = parts[cursor:] + ret = '' for part in parts[1:]: try: ret = getattr(ret, part) except AttributeError: raise ImportError - return ret - -# utility functions for 2to3 support - -def run_2to3(files, doctests_only=False, fixer_names=None, options=None, - explicit=None): - """ Wrapper function around the refactor() class which - performs the conversions on a list of python files. - Invoke 2to3 on a list of Python files. The files should all come - from the build area, as the modification is done in-place.""" - - if not files: - return - - # Make this class local, to delay import of 2to3 - from lib2to3.refactor import get_fixers_from_package - from distutils2.converter.refactor import DistutilsRefactoringTool - - if fixer_names is None: - fixer_names = get_fixers_from_package('lib2to3.fixes') - - r = DistutilsRefactoringTool(fixer_names, options=options) - if doctests_only: - r.refactor(files, doctests_only=True, write=True) - else: - r.refactor(files, write=True) - - -class Mixin2to3: - """ Wrapper class for commands that run 2to3. - To configure 2to3, setup scripts may either change - the class variables, or inherit from this class - to override how 2to3 is invoked. - """ - # provide list of fixers to run. - # defaults to all from lib2to3.fixers - fixer_names = None - - # options dictionary - options = None - - # list of fixers to invoke even though they are marked as explicit - explicit = None - - def run_2to3(self, files, doctests_only=False): - """ Issues a call to util.run_2to3. """ - return run_2to3(files, doctests_only, self.fixer_names, - self.options, self.explicit) + return ret def splitext(path): """Like os.path.splitext, but take off .tar too""" @@ -757,7 +714,7 @@ def untar_file(filename, location): os.makedirs(location) if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): mode = 'r:gz' - elif (filename.lower().endswith('.bz2') + elif (filename.lower().endswith('.bz2') or filename.lower().endswith('.tbz')): mode = 'r:bz2' elif filename.lower().endswith('.tar'): @@ -1122,3 +1079,51 @@ def metadata_to_dict(meta): data['obsoletes'] = meta['Obsoletes'] return data + +# utility functions for 2to3 support + +def run_2to3(files, doctests_only=False, fixer_names=None, options=None, + explicit=None): + """ Wrapper function around the refactor() class which + performs the conversions on a list of python files. + Invoke 2to3 on a list of Python files. The files should all come + from the build area, as the modification is done in-place.""" + + #if not files: + # return + + # Make this class local, to delay import of 2to3 + from lib2to3.refactor import get_fixers_from_package, RefactoringTool + fixers = [] + fixers = get_fixers_from_package('lib2to3.fixes') + + + if fixer_names: + for fixername in fixer_names: + fixers.extend([fixer for fixer in get_fixers_from_package(fixername)]) + r = RefactoringTool(fixers, options=options) + if doctests_only: + r.refactor(files, doctests_only=True, write=True) + else: + r.refactor(files, write=True) + +class Mixin2to3: + """ Wrapper class for commands that run 2to3. + To configure 2to3, setup scripts may either change + the class variables, or inherit from this class + to override how 2to3 is invoked. + """ + # provide list of fixers to run. + # defaults to all from lib2to3.fixers + fixer_names = None + + # options dictionary + options = None + + # list of fixers to invoke even though they are marked as explicit + explicit = None + + def run_2to3(self, files, doctests_only=False): + """ Issues a call to util.run_2to3. """ + return run_2to3(files, doctests_only, self.fixer_names, + self.options, self.explicit) |
