Commits

Vinay Sajip committed 42abd6d

Initial commit.

Comments (0)

Files changed (13)

+(build|dist|__pycache__)/
+MANIFEST
+
+:orphan:
+
+.. _whats-new:
+
+What's New in uprefix
+=====================
+
+Version 0.1
+-----------
+
+First public release.
+Copyright (c) 2012 by Vinay Sajip.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+    * The name(s) of the copyright holder(s) may not be used to endorse or
+      promote products derived from this software without specific prior
+      written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+uprefix 0.1
+===========
+The uprefix module provides an import hook for Python 3 which replaces
+'u' prefixes on Unicode literal strings in Python source code.
+
+It is intended to assist users when porting Python 2.x code to 3.x.
+
+Requirements & Installation
+---------------------------
+The uprefix module requires Python 3.1 or greater, and can be
+installed with the standard Python installation procedure:
+
+  python setup.py install
+
+There is a set of unit tests which you can invoke with
+
+  python setup.py test
+  
+before running the installation.
+
+Usage
+-----
+
+Once installed on the path, the import hook is invoked as follows:
+
+    >>> import uprefix
+    >>> uprefix.register_hook()
+
+You can unregister the hook by
+
+    >>> uprefix.unregister_hook()
+
+Availability & Documentation
+----------------------------
+The latest version of uprefix can be found at
+
+https://bitbucket.org/vinay.sajip/uprefix/
+
+foo = u'foo'

foo/bar/__init__.py

+foo_bar = u'foo.bar'

foo/bar/baz/__init__.py

+foo_bar_baz = u'foo.bar.baz'

foo/bar/baz/level3.py

+foo_bar_baz_level3 = u'foo.bar.baz.level3'

foo/bar/level2.py

+foo_bar_level2 = u'foo.bar.level2'
+foo_level1 = u'foo.level1'
+#!/usr/bin/env python
+
+from distutils.core import setup, Command
+from os.path import join, dirname, abspath
+import re
+
+import uprefix
+
+class TestCommand(Command):
+    user_options = []
+
+    def run(self):
+        import sys
+        import unittest
+
+        import test_uprefix
+        loader = unittest.TestLoader()
+        runner = unittest.TextTestRunner()
+        runner.run(loader.loadTestsFromModule(test_uprefix))
+
+    def initialize_options(self):
+        pass
+
+    def finalize_options(self):
+        pass
+
+def description():
+    f = open(join(dirname(__file__), 'README'))
+    read_me = f.read()
+    f.close()
+    regexp = r'^uprefix\s*[\d.]*\s*\n=======+\s*\n(.*)Requirements '
+    requires, = re.findall(regexp, read_me, re.DOTALL)
+    regexp = r'Availability & Documentation\s*\n-----+\s*\n(.*)'
+    avail, = re.findall(regexp, read_me, re.DOTALL)
+    return requires + avail
+
+setup(
+    name='uprefix',
+    description=('An import hook for Python 3 that removes u prefixes '
+                 'from Python source code before compiling it.'),
+    long_description=description(),
+    version=uprefix.__version__,
+    author='Vinay Sajip',
+    author_email='vinay_sajip@yahoo.co.uk',
+    maintainer='Vinay Sajip',
+    maintainer_email='vinay_sajip@yahoo.co.uk',
+    url='https://bitbucket.org/vinay.sajip/uprefix/',
+    py_modules=['uprefix'],
+    classifiers=[
+        'Development Status :: 3 - Alpha',
+        'Environment :: Console',
+        'Environment :: MacOS X',
+        'Environment :: Win32 (MS Windows)',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: MacOS :: MacOS X',
+        'Operating System :: Microsoft :: Windows',
+        'Operating System :: POSIX',
+        'Operating System :: Unix',
+        'Programming Language :: Python',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.1',
+        'Programming Language :: Python :: 3.2',
+        'Programming Language :: Python :: Implementation',
+        'Topic :: Software Development :: Libraries',
+        'Topic :: Software Development :: Libraries :: Python Modules',
+    ],
+    cmdclass={ 'test': TestCommand },
+)
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 Vinay M. Sajip. See LICENSE for licensing information.
+#
+# Test harness for uprefix.
+#
+
+from __future__ import unicode_literals
+
+import logging
+import os
+import sys
+import unittest
+
+if sys.version_info[0] < 3:
+    raise ImportError('This module can only be used with Python 3')
+
+from uprefix import register_hook, unregister_hook
+
+logger = logging.getLogger(__name__)
+
+class UnicodePrefixRemover(unittest.TestCase):
+    def setUp(self):
+        register_hook()
+
+    def tearDown(self):
+        unregister_hook()
+
+    def test_dummy(self):
+        package_names = (
+            'foo',
+            'foo.level1', 'foo.bar',
+            'foo.bar.level2', 'foo.bar.baz',
+            'foo.bar.baz.level3',
+        )
+        for package_name in package_names:
+            attr_name = package_name.replace('.', '_')
+            # pass in __file__ so that the correct module is returned
+            mod = __import__(package_name, fromlist=['__file__'])
+            attr_value = getattr(mod, attr_name)
+            self.assertEqual(attr_value, package_name)
+
+if __name__ == '__main__':  #pragma: no cover
+    # switch the level to DEBUG for in-depth logging.
+    fn = 'test_uprefix-%d.%d.log' % sys.version_info[:2]
+    logging.basicConfig(level=logging.DEBUG, filename=fn, filemode='w',
+                        format='%(threadName)s %(funcName)s %(lineno)d '
+                               '%(message)s')
+    unittest.main()
+
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 Vinay M. Sajip. See LICENSE for licensing information.
+#
+# An import hook for Python 3.x which removes 'u' prefixes before compilation.
+#
+import imp
+from lib2to3.refactor import RefactoringTool
+import logging
+import marshal
+import os
+import sys
+
+__version__ = '0.1'
+
+if sys.version_info[0] < 3:
+    raise NotImplementedError('This hook is implemented for Python 3 only')
+
+try:
+    import sysconfig
+    _platstdlib = sysconfig.get_path('platstdlib')
+except ImportError:
+    import distutils.sysconfig
+    _platstdlib = distutils.sysconfig.get_config_var('LIBDEST')
+
+logger = logging.getLogger(__name__)
+
+rt = RefactoringTool(['lib2to3.fixes.fix_unicode'])
+
+
+class UnicodePrefixRemover(object):
+    def __init__(self):
+        self.found = None
+
+    def find_module(self, fullname, path=None):
+        if '.' in fullname:
+            parent, child = fullname.rsplit('.', 1)
+            if path is None:
+                loader = self.find_module(parent, path)
+                mod = loader.load_module(parent)
+                path = mod.__path__
+            fullname = child
+
+        self.found = imp.find_module(fullname, path)
+        self.kind = self.found[-1][-1]
+        if self.kind == imp.PKG_DIRECTORY:
+            self.pathname = os.path.join(self.found[1], '__init__.py')
+        elif self.kind == imp.PY_SOURCE:
+            self.pathname = self.found[1]
+        return self
+
+    def load_module(self, fullname):
+        if fullname in sys.modules:
+            mod = sys.modules[fullname]
+        else:
+            if self.kind in (imp.PY_COMPILED, imp.C_EXTENSION, imp.C_BUILTIN,
+                             imp.PY_FROZEN):
+                convert = False
+            elif self.pathname.startswith(_platstdlib):
+                convert = False
+            # in theory, other paths could be configured to be excluded here, too
+            else:
+                convert = True
+            if not convert:
+                mod = imp.load_module(fullname, *self.found)
+            else:
+                mod = imp.new_module(fullname)
+                sys.modules[fullname] = mod
+
+                # required by PEP 302
+                mod.__file__ = self.pathname
+                mod.__name__ = fullname
+                mod.__loader__ = self
+                mod.__package__ = '.'.join(fullname.split('.')[:-1])
+                
+                if self.kind == imp.PKG_DIRECTORY:
+                    mod.__path__ = [ os.path.dirname(self.pathname) ]
+                #else, regular module
+                try:
+                    cachename = imp.cache_from_source(self.pathname)
+                    if not os.path.exists(cachename):
+                        update_cache = True
+                    else:
+                        sourcetime = os.stat(self.pathname).st_mtime
+                        cachetime = os.stat(cachename).st_mtime
+                        update_cache = cachetime < sourcetime
+                    if not update_cache:
+                        with open(cachename, 'rb') as f:
+                            data = f.read()
+                            try:
+                                code = marshal.loads(data)
+                            except Exception:
+                                # pyc could be corrupt. Regenerate it
+                                update_cache = True
+                    if update_cache:
+                        if self.found[0]:
+                            source = self.found[0].read()
+                        elif self.kind == imp.PKG_DIRECTORY:
+                            with open(self.pathname) as f:
+                                source = f.read()
+                        #refactor - lib2to3 likes a newline at the end
+                        source += '\n'
+                        tree = rt.refactor_string(source, self.pathname)
+                        source = str(tree)[:-1] # remove added newline
+                        code = compile(source, self.pathname, 'exec')
+                        dirname = os.path.dirname(cachename)
+                        if not os.path.exists(dirname):
+                            os.makedirs(dirname)
+                        try:
+                            with open(cachename, 'wb') as f:
+                                data = marshal.dumps(code)
+                                f.write(data)
+                        except Exception:   # could be write-protected
+                            pass
+                    exec(code, mod.__dict__)
+                except Exception as e:
+                    # failed somewhere, remove module from sys.modules
+                    del sys.modules[fullname]
+                    raise ImportError
+
+        if self.found[0]:
+            self.found[0].close()
+        return mod
+
+_hook = UnicodePrefixRemover()
+
+def register_hook():
+    if _hook not in sys.meta_path:
+        sys.meta_path.append(_hook)
+    # could return the hook when there are ways of configuring it
+    #return _hook
+
+def unregister_hook():
+    if _hook in sys.meta_path:
+        sys.meta_path.remove(_hook)
+