second commit

This commit is contained in:
2024-12-27 22:31:23 +09:00
parent 2353324570
commit 10a0f110ca
8819 changed files with 1307198 additions and 28 deletions

View File

@ -0,0 +1,90 @@
# coding=utf-8
"""
past: compatibility with Python 2 from Python 3
===============================================
``past`` is a package to aid with Python 2/3 compatibility. Whereas ``future``
contains backports of Python 3 constructs to Python 2, ``past`` provides
implementations of some Python 2 constructs in Python 3 and tools to import and
run Python 2 code in Python 3. It is intended to be used sparingly, as a way of
running old Python 2 code from Python 3 until the code is ported properly.
Potential uses for libraries:
- as a step in porting a Python 2 codebase to Python 3 (e.g. with the ``futurize`` script)
- to provide Python 3 support for previously Python 2-only libraries with the
same APIs as on Python 2 -- particularly with regard to 8-bit strings (the
``past.builtins.str`` type).
- to aid in providing minimal-effort Python 3 support for applications using
libraries that do not yet wish to upgrade their code properly to Python 3, or
wish to upgrade it gradually to Python 3 style.
Here are some code examples that run identically on Python 3 and 2::
>>> from past.builtins import str as oldstr
>>> philosopher = oldstr(u'\u5b54\u5b50'.encode('utf-8'))
>>> # This now behaves like a Py2 byte-string on both Py2 and Py3.
>>> # For example, indexing returns a Python 2-like string object, not
>>> # an integer:
>>> philosopher[0]
'\xe5'
>>> type(philosopher[0])
<past.builtins.oldstr>
>>> # List-producing versions of range, reduce, map, filter
>>> from past.builtins import range, reduce
>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> reduce(lambda x, y: x+y, [1, 2, 3, 4, 5])
15
>>> # Other functions removed in Python 3 are resurrected ...
>>> from past.builtins import execfile
>>> execfile('myfile.py')
>>> from past.builtins import raw_input
>>> name = raw_input('What is your name? ')
What is your name? [cursor]
>>> from past.builtins import reload
>>> reload(mymodule) # equivalent to imp.reload(mymodule) in Python 3
>>> from past.builtins import xrange
>>> for i in xrange(10):
... pass
It also provides import hooks so you can import and use Python 2 modules like
this::
$ python3
>>> from past.translation import autotranslate
>>> authotranslate('mypy2module')
>>> import mypy2module
until the authors of the Python 2 modules have upgraded their code. Then, for
example::
>>> mypy2module.func_taking_py2_string(oldstr(b'abcd'))
Credits
-------
:Author: Ed Schofield, Jordan M. Adler, et al
:Sponsor: Python Charmers: https://pythoncharmers.com
Licensing
---------
Copyright 2013-2024 Python Charmers, Australia.
The software is distributed under an MIT licence. See LICENSE.txt.
"""
from future import __version__, __copyright__, __license__
__title__ = 'past'
__author__ = 'Ed Schofield'

View File

@ -0,0 +1,72 @@
"""
A resurrection of some old functions from Python 2 for use in Python 3. These
should be used sparingly, to help with porting efforts, since code using them
is no longer standard Python 3 code.
This module provides the following:
1. Implementations of these builtin functions which have no equivalent on Py3:
- apply
- chr
- cmp
- execfile
2. Aliases:
- intern <- sys.intern
- raw_input <- input
- reduce <- functools.reduce
- reload <- imp.reload
- unichr <- chr
- unicode <- str
- xrange <- range
3. List-producing versions of the corresponding Python 3 iterator-producing functions:
- filter
- map
- range
- zip
4. Forward-ported Py2 types:
- basestring
- dict
- str
- long
- unicode
"""
from future.utils import PY3
from past.builtins.noniterators import (filter, map, range, reduce, zip)
# from past.builtins.misc import (ascii, hex, input, oct, open)
if PY3:
from past.types import (basestring,
olddict as dict,
oldstr as str,
long,
unicode)
else:
from __builtin__ import (basestring, dict, str, long, unicode)
from past.builtins.misc import (apply, chr, cmp, execfile, intern, oct,
raw_input, reload, unichr, unicode, xrange)
from past import utils
if utils.PY3:
# We only import names that shadow the builtins on Py3. No other namespace
# pollution on Py3.
# Only shadow builtins on Py3; no new names
__all__ = ['filter', 'map', 'range', 'reduce', 'zip',
'basestring', 'dict', 'str', 'long', 'unicode',
'apply', 'chr', 'cmp', 'execfile', 'intern', 'raw_input',
'reload', 'unichr', 'xrange'
]
else:
# No namespace pollution on Py2
__all__ = []

View File

@ -0,0 +1,162 @@
from __future__ import unicode_literals
import inspect
import sys
import math
import numbers
from future.utils import PY2, PY3, exec_
if PY2:
from collections import Mapping
else:
from collections.abc import Mapping
if PY3:
import builtins
from collections.abc import Mapping
def apply(f, *args, **kw):
return f(*args, **kw)
from past.builtins import str as oldstr
def chr(i):
"""
Return a byte-string of one character with ordinal i; 0 <= i <= 256
"""
return oldstr(bytes((i,)))
def cmp(x, y):
"""
cmp(x, y) -> integer
Return negative if x<y, zero if x==y, positive if x>y.
Python2 had looser comparison allowing cmp None and non Numerical types and collections.
Try to match the old behavior
"""
if isinstance(x, set) and isinstance(y, set):
raise TypeError('cannot compare sets using cmp()',)
try:
if isinstance(x, numbers.Number) and math.isnan(x):
if not isinstance(y, numbers.Number):
raise TypeError('cannot compare float("nan"), {type_y} with cmp'.format(type_y=type(y)))
if isinstance(y, int):
return 1
else:
return -1
if isinstance(y, numbers.Number) and math.isnan(y):
if not isinstance(x, numbers.Number):
raise TypeError('cannot compare {type_x}, float("nan") with cmp'.format(type_x=type(x)))
if isinstance(x, int):
return -1
else:
return 1
return (x > y) - (x < y)
except TypeError:
if x == y:
return 0
type_order = [
type(None),
numbers.Number,
dict, list,
set,
(str, bytes),
]
x_type_index = y_type_index = None
for i, type_match in enumerate(type_order):
if isinstance(x, type_match):
x_type_index = i
if isinstance(y, type_match):
y_type_index = i
if cmp(x_type_index, y_type_index) == 0:
if isinstance(x, bytes) and isinstance(y, str):
return cmp(x.decode('ascii'), y)
if isinstance(y, bytes) and isinstance(x, str):
return cmp(x, y.decode('ascii'))
elif isinstance(x, list):
# if both arguments are lists take the comparison of the first non equal value
for x_elem, y_elem in zip(x, y):
elem_cmp_val = cmp(x_elem, y_elem)
if elem_cmp_val != 0:
return elem_cmp_val
# if all elements are equal, return equal/0
return 0
elif isinstance(x, dict):
if len(x) != len(y):
return cmp(len(x), len(y))
else:
x_key = min(a for a in x if a not in y or x[a] != y[a])
y_key = min(b for b in y if b not in x or x[b] != y[b])
if x_key != y_key:
return cmp(x_key, y_key)
else:
return cmp(x[x_key], y[y_key])
return cmp(x_type_index, y_type_index)
from sys import intern
def oct(number):
"""oct(number) -> string
Return the octal representation of an integer
"""
return '0' + builtins.oct(number)[2:]
raw_input = input
# imp was deprecated in python 3.6
if sys.version_info >= (3, 6):
from importlib import reload
else:
# for python2, python3 <= 3.4
from imp import reload
unicode = str
unichr = chr
xrange = range
else:
import __builtin__
from collections import Mapping
apply = __builtin__.apply
chr = __builtin__.chr
cmp = __builtin__.cmp
execfile = __builtin__.execfile
intern = __builtin__.intern
oct = __builtin__.oct
raw_input = __builtin__.raw_input
reload = __builtin__.reload
unicode = __builtin__.unicode
unichr = __builtin__.unichr
xrange = __builtin__.xrange
if PY3:
def execfile(filename, myglobals=None, mylocals=None):
"""
Read and execute a Python script from a file in the given namespaces.
The globals and locals are dictionaries, defaulting to the current
globals and locals. If only globals is given, locals defaults to it.
"""
if myglobals is None:
# There seems to be no alternative to frame hacking here.
caller_frame = inspect.stack()[1]
myglobals = caller_frame[0].f_globals
mylocals = caller_frame[0].f_locals
elif mylocals is None:
# Only if myglobals is given do we set mylocals to it.
mylocals = myglobals
if not isinstance(myglobals, Mapping):
raise TypeError('globals must be a mapping')
if not isinstance(mylocals, Mapping):
raise TypeError('locals must be a mapping')
with open(filename, "rb") as fin:
source = fin.read()
code = compile(source, filename, "exec")
exec_(code, myglobals, mylocals)
if PY3:
__all__ = ['apply', 'chr', 'cmp', 'execfile', 'intern', 'raw_input',
'reload', 'unichr', 'unicode', 'xrange']
else:
__all__ = []

View File

@ -0,0 +1,272 @@
"""
This module is designed to be used as follows::
from past.builtins.noniterators import filter, map, range, reduce, zip
And then, for example::
assert isinstance(range(5), list)
The list-producing functions this brings in are::
- ``filter``
- ``map``
- ``range``
- ``reduce``
- ``zip``
"""
from __future__ import division, absolute_import, print_function
from itertools import chain, starmap
import itertools # since zip_longest doesn't exist on Py2
from past.types import basestring
from past.utils import PY3
def flatmap(f, items):
return chain.from_iterable(map(f, items))
if PY3:
import builtins
# list-producing versions of the major Python iterating functions
def oldfilter(*args):
"""
filter(function or None, sequence) -> list, tuple, or string
Return those items of sequence for which function(item) is true.
If function is None, return the items that are true. If sequence
is a tuple or string, return the same type, else return a list.
"""
mytype = type(args[1])
if isinstance(args[1], basestring):
return mytype().join(builtins.filter(*args))
elif isinstance(args[1], (tuple, list)):
return mytype(builtins.filter(*args))
else:
# Fall back to list. Is this the right thing to do?
return list(builtins.filter(*args))
# This is surprisingly difficult to get right. For example, the
# solutions here fail with the test cases in the docstring below:
# http://stackoverflow.com/questions/8072755/
def oldmap(func, *iterables):
"""
map(function, sequence[, sequence, ...]) -> list
Return a list of the results of applying the function to the
items of the argument sequence(s). If more than one sequence is
given, the function is called with an argument list consisting of
the corresponding item of each sequence, substituting None for
missing values when not all sequences have the same length. If
the function is None, return a list of the items of the sequence
(or a list of tuples if more than one sequence).
Test cases:
>>> oldmap(None, 'hello world')
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
>>> oldmap(None, range(4))
[0, 1, 2, 3]
More test cases are in test_past.test_builtins.
"""
zipped = itertools.zip_longest(*iterables)
l = list(zipped)
if len(l) == 0:
return []
if func is None:
result = l
else:
result = list(starmap(func, l))
# Inspect to see whether it's a simple sequence of tuples
try:
if max([len(item) for item in result]) == 1:
return list(chain.from_iterable(result))
# return list(flatmap(func, result))
except TypeError as e:
# Simple objects like ints have no len()
pass
return result
############################
### For reference, the source code for Py2.7 map function:
# static PyObject *
# builtin_map(PyObject *self, PyObject *args)
# {
# typedef struct {
# PyObject *it; /* the iterator object */
# int saw_StopIteration; /* bool: did the iterator end? */
# } sequence;
#
# PyObject *func, *result;
# sequence *seqs = NULL, *sqp;
# Py_ssize_t n, len;
# register int i, j;
#
# n = PyTuple_Size(args);
# if (n < 2) {
# PyErr_SetString(PyExc_TypeError,
# "map() requires at least two args");
# return NULL;
# }
#
# func = PyTuple_GetItem(args, 0);
# n--;
#
# if (func == Py_None) {
# if (PyErr_WarnPy3k("map(None, ...) not supported in 3.x; "
# "use list(...)", 1) < 0)
# return NULL;
# if (n == 1) {
# /* map(None, S) is the same as list(S). */
# return PySequence_List(PyTuple_GetItem(args, 1));
# }
# }
#
# /* Get space for sequence descriptors. Must NULL out the iterator
# * pointers so that jumping to Fail_2 later doesn't see trash.
# */
# if ((seqs = PyMem_NEW(sequence, n)) == NULL) {
# PyErr_NoMemory();
# return NULL;
# }
# for (i = 0; i < n; ++i) {
# seqs[i].it = (PyObject*)NULL;
# seqs[i].saw_StopIteration = 0;
# }
#
# /* Do a first pass to obtain iterators for the arguments, and set len
# * to the largest of their lengths.
# */
# len = 0;
# for (i = 0, sqp = seqs; i < n; ++i, ++sqp) {
# PyObject *curseq;
# Py_ssize_t curlen;
#
# /* Get iterator. */
# curseq = PyTuple_GetItem(args, i+1);
# sqp->it = PyObject_GetIter(curseq);
# if (sqp->it == NULL) {
# static char errmsg[] =
# "argument %d to map() must support iteration";
# char errbuf[sizeof(errmsg) + 25];
# PyOS_snprintf(errbuf, sizeof(errbuf), errmsg, i+2);
# PyErr_SetString(PyExc_TypeError, errbuf);
# goto Fail_2;
# }
#
# /* Update len. */
# curlen = _PyObject_LengthHint(curseq, 8);
# if (curlen > len)
# len = curlen;
# }
#
# /* Get space for the result list. */
# if ((result = (PyObject *) PyList_New(len)) == NULL)
# goto Fail_2;
#
# /* Iterate over the sequences until all have stopped. */
# for (i = 0; ; ++i) {
# PyObject *alist, *item=NULL, *value;
# int numactive = 0;
#
# if (func == Py_None && n == 1)
# alist = NULL;
# else if ((alist = PyTuple_New(n)) == NULL)
# goto Fail_1;
#
# for (j = 0, sqp = seqs; j < n; ++j, ++sqp) {
# if (sqp->saw_StopIteration) {
# Py_INCREF(Py_None);
# item = Py_None;
# }
# else {
# item = PyIter_Next(sqp->it);
# if (item)
# ++numactive;
# else {
# if (PyErr_Occurred()) {
# Py_XDECREF(alist);
# goto Fail_1;
# }
# Py_INCREF(Py_None);
# item = Py_None;
# sqp->saw_StopIteration = 1;
# }
# }
# if (alist)
# PyTuple_SET_ITEM(alist, j, item);
# else
# break;
# }
#
# if (!alist)
# alist = item;
#
# if (numactive == 0) {
# Py_DECREF(alist);
# break;
# }
#
# if (func == Py_None)
# value = alist;
# else {
# value = PyEval_CallObject(func, alist);
# Py_DECREF(alist);
# if (value == NULL)
# goto Fail_1;
# }
# if (i >= len) {
# int status = PyList_Append(result, value);
# Py_DECREF(value);
# if (status < 0)
# goto Fail_1;
# }
# else if (PyList_SetItem(result, i, value) < 0)
# goto Fail_1;
# }
#
# if (i < len && PyList_SetSlice(result, i, len, NULL) < 0)
# goto Fail_1;
#
# goto Succeed;
#
# Fail_1:
# Py_DECREF(result);
# Fail_2:
# result = NULL;
# Succeed:
# assert(seqs);
# for (i = 0; i < n; ++i)
# Py_XDECREF(seqs[i].it);
# PyMem_DEL(seqs);
# return result;
# }
def oldrange(*args, **kwargs):
return list(builtins.range(*args, **kwargs))
def oldzip(*args, **kwargs):
return list(builtins.zip(*args, **kwargs))
filter = oldfilter
map = oldmap
range = oldrange
from functools import reduce
zip = oldzip
__all__ = ['filter', 'map', 'range', 'reduce', 'zip']
else:
import __builtin__
# Python 2-builtin ranges produce lists
filter = __builtin__.filter
map = __builtin__.map
range = __builtin__.range
reduce = __builtin__.reduce
zip = __builtin__.zip
__all__ = []

View File

@ -0,0 +1,453 @@
# -*- coding: utf-8 -*-
"""
past.translation
==================
The ``past.translation`` package provides an import hook for Python 3 which
transparently runs ``futurize`` fixers over Python 2 code on import to convert
print statements into functions, etc.
It is intended to assist users in migrating to Python 3.x even if some
dependencies still only support Python 2.x.
Usage
-----
Once your Py2 package is installed in the usual module search path, the import
hook is invoked as follows:
>>> from past.translation import autotranslate
>>> autotranslate('mypackagename')
Or:
>>> autotranslate(['mypackage1', 'mypackage2'])
You can unregister the hook using::
>>> from past.translation import remove_hooks
>>> remove_hooks()
Author: Ed Schofield.
Inspired by and based on ``uprefix`` by Vinay M. Sajip.
"""
import sys
# imp was deprecated in python 3.6
if sys.version_info >= (3, 6):
import importlib as imp
else:
import imp
import logging
import os
import copy
from lib2to3.pgen2.parse import ParseError
from lib2to3.refactor import RefactoringTool
from libfuturize import fixes
try:
from importlib.machinery import (
PathFinder,
SourceFileLoader,
)
except ImportError:
PathFinder = None
SourceFileLoader = object
if sys.version_info[:2] < (3, 4):
import imp
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
myfixes = (list(fixes.libfuturize_fix_names_stage1) +
list(fixes.lib2to3_fix_names_stage1) +
list(fixes.libfuturize_fix_names_stage2) +
list(fixes.lib2to3_fix_names_stage2))
# We detect whether the code is Py2 or Py3 by applying certain lib2to3 fixers
# to it. If the diff is empty, it's Python 3 code.
py2_detect_fixers = [
# From stage 1:
'lib2to3.fixes.fix_apply',
# 'lib2to3.fixes.fix_dict', # TODO: add support for utils.viewitems() etc. and move to stage2
'lib2to3.fixes.fix_except',
'lib2to3.fixes.fix_execfile',
'lib2to3.fixes.fix_exitfunc',
'lib2to3.fixes.fix_funcattrs',
'lib2to3.fixes.fix_filter',
'lib2to3.fixes.fix_has_key',
'lib2to3.fixes.fix_idioms',
'lib2to3.fixes.fix_import', # makes any implicit relative imports explicit. (Use with ``from __future__ import absolute_import)
'lib2to3.fixes.fix_intern',
'lib2to3.fixes.fix_isinstance',
'lib2to3.fixes.fix_methodattrs',
'lib2to3.fixes.fix_ne',
'lib2to3.fixes.fix_numliterals', # turns 1L into 1, 0755 into 0o755
'lib2to3.fixes.fix_paren',
'lib2to3.fixes.fix_print',
'lib2to3.fixes.fix_raise', # uses incompatible with_traceback() method on exceptions
'lib2to3.fixes.fix_renames',
'lib2to3.fixes.fix_reduce',
# 'lib2to3.fixes.fix_set_literal', # this is unnecessary and breaks Py2.6 support
'lib2to3.fixes.fix_repr',
'lib2to3.fixes.fix_standarderror',
'lib2to3.fixes.fix_sys_exc',
'lib2to3.fixes.fix_throw',
'lib2to3.fixes.fix_tuple_params',
'lib2to3.fixes.fix_types',
'lib2to3.fixes.fix_ws_comma',
'lib2to3.fixes.fix_xreadlines',
# From stage 2:
'lib2to3.fixes.fix_basestring',
# 'lib2to3.fixes.fix_buffer', # perhaps not safe. Test this.
# 'lib2to3.fixes.fix_callable', # not needed in Py3.2+
# 'lib2to3.fixes.fix_dict', # TODO: add support for utils.viewitems() etc.
'lib2to3.fixes.fix_exec',
# 'lib2to3.fixes.fix_future', # we don't want to remove __future__ imports
'lib2to3.fixes.fix_getcwdu',
# 'lib2to3.fixes.fix_imports', # called by libfuturize.fixes.fix_future_standard_library
# 'lib2to3.fixes.fix_imports2', # we don't handle this yet (dbm)
# 'lib2to3.fixes.fix_input',
# 'lib2to3.fixes.fix_itertools',
# 'lib2to3.fixes.fix_itertools_imports',
'lib2to3.fixes.fix_long',
# 'lib2to3.fixes.fix_map',
# 'lib2to3.fixes.fix_metaclass', # causes SyntaxError in Py2! Use the one from ``six`` instead
'lib2to3.fixes.fix_next',
'lib2to3.fixes.fix_nonzero', # TODO: add a decorator for mapping __bool__ to __nonzero__
# 'lib2to3.fixes.fix_operator', # we will need support for this by e.g. extending the Py2 operator module to provide those functions in Py3
'lib2to3.fixes.fix_raw_input',
# 'lib2to3.fixes.fix_unicode', # strips off the u'' prefix, which removes a potentially helpful source of information for disambiguating unicode/byte strings
# 'lib2to3.fixes.fix_urllib',
'lib2to3.fixes.fix_xrange',
# 'lib2to3.fixes.fix_zip',
]
class RTs:
"""
A namespace for the refactoring tools. This avoids creating these at
the module level, which slows down the module import. (See issue #117).
There are two possible grammars: with or without the print statement.
Hence we have two possible refactoring tool implementations.
"""
_rt = None
_rtp = None
_rt_py2_detect = None
_rtp_py2_detect = None
@staticmethod
def setup():
"""
Call this before using the refactoring tools to create them on demand
if needed.
"""
if None in [RTs._rt, RTs._rtp]:
RTs._rt = RefactoringTool(myfixes)
RTs._rtp = RefactoringTool(myfixes, {'print_function': True})
@staticmethod
def setup_detect_python2():
"""
Call this before using the refactoring tools to create them on demand
if needed.
"""
if None in [RTs._rt_py2_detect, RTs._rtp_py2_detect]:
RTs._rt_py2_detect = RefactoringTool(py2_detect_fixers)
RTs._rtp_py2_detect = RefactoringTool(py2_detect_fixers,
{'print_function': True})
# We need to find a prefix for the standard library, as we don't want to
# process any files there (they will already be Python 3).
#
# The following method is used by Sanjay Vinip in uprefix. This fails for
# ``conda`` environments:
# # In a non-pythonv virtualenv, sys.real_prefix points to the installed Python.
# # In a pythonv venv, sys.base_prefix points to the installed Python.
# # Outside a virtual environment, sys.prefix points to the installed Python.
# if hasattr(sys, 'real_prefix'):
# _syslibprefix = sys.real_prefix
# else:
# _syslibprefix = getattr(sys, 'base_prefix', sys.prefix)
# Instead, we use the portion of the path common to both the stdlib modules
# ``math`` and ``urllib``.
def splitall(path):
"""
Split a path into all components. From Python Cookbook.
"""
allparts = []
while True:
parts = os.path.split(path)
if parts[0] == path: # sentinel for absolute paths
allparts.insert(0, parts[0])
break
elif parts[1] == path: # sentinel for relative paths
allparts.insert(0, parts[1])
break
else:
path = parts[0]
allparts.insert(0, parts[1])
return allparts
def common_substring(s1, s2):
"""
Returns the longest common substring to the two strings, starting from the
left.
"""
chunks = []
path1 = splitall(s1)
path2 = splitall(s2)
for (dir1, dir2) in zip(path1, path2):
if dir1 != dir2:
break
chunks.append(dir1)
return os.path.join(*chunks)
# _stdlibprefix = common_substring(math.__file__, urllib.__file__)
def detect_python2(source, pathname):
"""
Returns a bool indicating whether we think the code is Py2
"""
RTs.setup_detect_python2()
try:
tree = RTs._rt_py2_detect.refactor_string(source, pathname)
except ParseError as e:
if e.msg != 'bad input' or e.value != '=':
raise
tree = RTs._rtp.refactor_string(source, pathname)
if source != str(tree)[:-1]: # remove added newline
# The above fixers made changes, so we conclude it's Python 2 code
logger.debug('Detected Python 2 code: {0}'.format(pathname))
return True
else:
logger.debug('Detected Python 3 code: {0}'.format(pathname))
return False
def transform(source, pathname):
# This implementation uses lib2to3,
# you can override and use something else
# if that's better for you
# lib2to3 likes a newline at the end
RTs.setup()
source += '\n'
try:
tree = RTs._rt.refactor_string(source, pathname)
except ParseError as e:
if e.msg != 'bad input' or e.value != '=':
raise
tree = RTs._rtp.refactor_string(source, pathname)
# could optimise a bit for only doing str(tree) if
# getattr(tree, 'was_changed', False) returns True
return str(tree)[:-1] # remove added newline
class PastSourceFileLoader(SourceFileLoader):
exclude_paths = []
include_paths = []
def _convert_needed(self):
fullname = self.name
if any(fullname.startswith(path) for path in self.exclude_paths):
convert = False
elif any(fullname.startswith(path) for path in self.include_paths):
convert = True
else:
convert = False
return convert
def _exec_transformed_module(self, module):
source = self.get_source(self.name)
pathname = self.path
if detect_python2(source, pathname):
source = transform(source, pathname)
code = compile(source, pathname, "exec")
exec(code, module.__dict__)
# For Python 3.3
def load_module(self, fullname):
logger.debug("Running load_module for %s", fullname)
if fullname in sys.modules:
mod = sys.modules[fullname]
else:
if self._convert_needed():
logger.debug("Autoconverting %s", fullname)
mod = imp.new_module(fullname)
sys.modules[fullname] = mod
# required by PEP 302
mod.__file__ = self.path
mod.__loader__ = self
if self.is_package(fullname):
mod.__path__ = []
mod.__package__ = fullname
else:
mod.__package__ = fullname.rpartition('.')[0]
self._exec_transformed_module(mod)
else:
mod = super().load_module(fullname)
return mod
# For Python >=3.4
def exec_module(self, module):
logger.debug("Running exec_module for %s", module)
if self._convert_needed():
logger.debug("Autoconverting %s", self.name)
self._exec_transformed_module(module)
else:
super().exec_module(module)
class Py2Fixer(object):
"""
An import hook class that uses lib2to3 for source-to-source translation of
Py2 code to Py3.
"""
# See the comments on :class:future.standard_library.RenameImport.
# We add this attribute here so remove_hooks() and install_hooks() can
# unambiguously detect whether the import hook is installed:
PY2FIXER = True
def __init__(self):
self.found = None
self.base_exclude_paths = ['future', 'past']
self.exclude_paths = copy.copy(self.base_exclude_paths)
self.include_paths = []
def include(self, paths):
"""
Pass in a sequence of module names such as 'plotrique.plotting' that,
if present at the leftmost side of the full package name, would
specify the module to be transformed from Py2 to Py3.
"""
self.include_paths += paths
def exclude(self, paths):
"""
Pass in a sequence of strings such as 'mymodule' that, if
present at the leftmost side of the full package name, would cause
the module not to undergo any source transformation.
"""
self.exclude_paths += paths
# For Python 3.3
def find_module(self, fullname, path=None):
logger.debug("Running find_module: (%s, %s)", fullname, path)
loader = PathFinder.find_module(fullname, path)
if not loader:
logger.debug("Py2Fixer could not find %s", fullname)
return None
loader.__class__ = PastSourceFileLoader
loader.exclude_paths = self.exclude_paths
loader.include_paths = self.include_paths
return loader
# For Python >=3.4
def find_spec(self, fullname, path=None, target=None):
logger.debug("Running find_spec: (%s, %s, %s)", fullname, path, target)
spec = PathFinder.find_spec(fullname, path, target)
if not spec:
logger.debug("Py2Fixer could not find %s", fullname)
return None
spec.loader.__class__ = PastSourceFileLoader
spec.loader.exclude_paths = self.exclude_paths
spec.loader.include_paths = self.include_paths
return spec
_hook = Py2Fixer()
def install_hooks(include_paths=(), exclude_paths=()):
if isinstance(include_paths, str):
include_paths = (include_paths,)
if isinstance(exclude_paths, str):
exclude_paths = (exclude_paths,)
assert len(include_paths) + len(exclude_paths) > 0, 'Pass at least one argument'
_hook.include(include_paths)
_hook.exclude(exclude_paths)
# _hook.debug = debug
enable = sys.version_info[0] >= 3 # enabled for all 3.x+
if enable and _hook not in sys.meta_path:
sys.meta_path.insert(0, _hook) # insert at beginning. This could be made a parameter
# We could return the hook when there are ways of configuring it
#return _hook
def remove_hooks():
if _hook in sys.meta_path:
sys.meta_path.remove(_hook)
def detect_hooks():
"""
Returns True if the import hooks are installed, False if not.
"""
return _hook in sys.meta_path
# present = any([hasattr(hook, 'PY2FIXER') for hook in sys.meta_path])
# return present
class hooks(object):
"""
Acts as a context manager. Use like this:
>>> from past import translation
>>> with translation.hooks():
... import mypy2module
>>> import requests # py2/3 compatible anyway
>>> # etc.
"""
def __enter__(self):
self.hooks_were_installed = detect_hooks()
install_hooks()
return self
def __exit__(self, *args):
if not self.hooks_were_installed:
remove_hooks()
class suspend_hooks(object):
"""
Acts as a context manager. Use like this:
>>> from past import translation
>>> translation.install_hooks()
>>> import http.client
>>> # ...
>>> with translation.suspend_hooks():
>>> import requests # or others that support Py2/3
If the hooks were disabled before the context, they are not installed when
the context is left.
"""
def __enter__(self):
self.hooks_were_installed = detect_hooks()
remove_hooks()
return self
def __exit__(self, *args):
if self.hooks_were_installed:
install_hooks()
# alias
autotranslate = install_hooks

View File

@ -0,0 +1,29 @@
"""
Forward-ports of types from Python 2 for use with Python 3:
- ``basestring``: equivalent to ``(str, bytes)`` in ``isinstance`` checks
- ``dict``: with list-producing .keys() etc. methods
- ``str``: bytes-like, but iterating over them doesn't product integers
- ``long``: alias of Py3 int with ``L`` suffix in the ``repr``
- ``unicode``: alias of Py3 str with ``u`` prefix in the ``repr``
"""
from past import utils
if utils.PY2:
import __builtin__
basestring = __builtin__.basestring
dict = __builtin__.dict
str = __builtin__.str
long = __builtin__.long
unicode = __builtin__.unicode
__all__ = []
else:
from .basestring import basestring
from .olddict import olddict
from .oldstr import oldstr
long = int
unicode = str
# from .unicode import unicode
__all__ = ['basestring', 'olddict', 'oldstr', 'long', 'unicode']

View File

@ -0,0 +1,38 @@
"""
An implementation of the basestring type for Python 3
Example use:
>>> s = b'abc'
>>> assert isinstance(s, basestring)
>>> from past.types import str as oldstr
>>> s2 = oldstr(b'abc')
>>> assert isinstance(s2, basestring)
"""
import sys
from past.utils import with_metaclass, PY2
if PY2:
str = unicode
ver = sys.version_info[:2]
class BaseBaseString(type):
def __instancecheck__(cls, instance):
return isinstance(instance, (bytes, str))
def __subclasscheck__(cls, subclass):
return super(BaseBaseString, cls).__subclasscheck__(subclass) or issubclass(subclass, (bytes, str))
class basestring(with_metaclass(BaseBaseString)):
"""
A minimal backport of the Python 2 basestring type to Py3
"""
__all__ = ['basestring']

View File

@ -0,0 +1,96 @@
"""
A dict subclass for Python 3 that behaves like Python 2's dict
Example use:
>>> from past.builtins import dict
>>> d1 = dict() # instead of {} for an empty dict
>>> d2 = dict(key1='value1', key2='value2')
The keys, values and items methods now return lists on Python 3.x and there are
methods for iterkeys, itervalues, iteritems, and viewkeys etc.
>>> for d in (d1, d2):
... assert isinstance(d.keys(), list)
... assert isinstance(d.values(), list)
... assert isinstance(d.items(), list)
"""
import sys
from past.utils import with_metaclass
_builtin_dict = dict
ver = sys.version_info[:2]
class BaseOldDict(type):
def __instancecheck__(cls, instance):
return isinstance(instance, _builtin_dict)
class olddict(with_metaclass(BaseOldDict, _builtin_dict)):
"""
A backport of the Python 3 dict object to Py2
"""
iterkeys = _builtin_dict.keys
viewkeys = _builtin_dict.keys
def keys(self):
return list(super(olddict, self).keys())
itervalues = _builtin_dict.values
viewvalues = _builtin_dict.values
def values(self):
return list(super(olddict, self).values())
iteritems = _builtin_dict.items
viewitems = _builtin_dict.items
def items(self):
return list(super(olddict, self).items())
def has_key(self, k):
"""
D.has_key(k) -> True if D has a key k, else False
"""
return k in self
# def __new__(cls, *args, **kwargs):
# """
# dict() -> new empty dictionary
# dict(mapping) -> new dictionary initialized from a mapping object's
# (key, value) pairs
# dict(iterable) -> new dictionary initialized as if via:
# d = {}
# for k, v in iterable:
# d[k] = v
# dict(**kwargs) -> new dictionary initialized with the name=value pairs
# in the keyword argument list. For example: dict(one=1, two=2)
# """
#
# if len(args) == 0:
# return super(olddict, cls).__new__(cls)
# # Was: elif isinstance(args[0], newbytes):
# # We use type() instead of the above because we're redefining
# # this to be True for all unicode string subclasses. Warning:
# # This may render newstr un-subclassable.
# elif type(args[0]) == olddict:
# return args[0]
# # elif isinstance(args[0], _builtin_dict):
# # value = args[0]
# else:
# value = args[0]
# return super(olddict, cls).__new__(cls, value)
def __native__(self):
"""
Hook for the past.utils.native() function
"""
return super(oldbytes, self)
__all__ = ['olddict']

View File

@ -0,0 +1,135 @@
"""
Pure-Python implementation of a Python 2-like str object for Python 3.
"""
from numbers import Integral
from past.utils import PY2, with_metaclass
if PY2:
from collections import Iterable
else:
from collections.abc import Iterable
_builtin_bytes = bytes
class BaseOldStr(type):
def __instancecheck__(cls, instance):
return isinstance(instance, _builtin_bytes)
def unescape(s):
r"""
Interprets strings with escape sequences
Example:
>>> s = unescape(r'abc\\def') # i.e. 'abc\\\\def'
>>> print(s)
'abc\def'
>>> s2 = unescape('abc\\ndef')
>>> len(s2)
8
>>> print(s2)
abc
def
"""
return s.encode().decode('unicode_escape')
class oldstr(with_metaclass(BaseOldStr, _builtin_bytes)):
"""
A forward port of the Python 2 8-bit string object to Py3
"""
# Python 2 strings have no __iter__ method:
@property
def __iter__(self):
raise AttributeError
def __dir__(self):
return [thing for thing in dir(_builtin_bytes) if thing != '__iter__']
# def __new__(cls, *args, **kwargs):
# """
# From the Py3 bytes docstring:
# bytes(iterable_of_ints) -> bytes
# bytes(string, encoding[, errors]) -> bytes
# bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer
# bytes(int) -> bytes object of size given by the parameter initialized with null bytes
# bytes() -> empty bytes object
#
# Construct an immutable array of bytes from:
# - an iterable yielding integers in range(256)
# - a text string encoded using the specified encoding
# - any object implementing the buffer API.
# - an integer
# """
#
# if len(args) == 0:
# return super(newbytes, cls).__new__(cls)
# # Was: elif isinstance(args[0], newbytes):
# # We use type() instead of the above because we're redefining
# # this to be True for all unicode string subclasses. Warning:
# # This may render newstr un-subclassable.
# elif type(args[0]) == newbytes:
# return args[0]
# elif isinstance(args[0], _builtin_bytes):
# value = args[0]
# elif isinstance(args[0], unicode):
# if 'encoding' not in kwargs:
# raise TypeError('unicode string argument without an encoding')
# ###
# # Was: value = args[0].encode(**kwargs)
# # Python 2.6 string encode() method doesn't take kwargs:
# # Use this instead:
# newargs = [kwargs['encoding']]
# if 'errors' in kwargs:
# newargs.append(kwargs['errors'])
# value = args[0].encode(*newargs)
# ###
# elif isinstance(args[0], Iterable):
# if len(args[0]) == 0:
# # What is this?
# raise ValueError('unknown argument type')
# elif len(args[0]) > 0 and isinstance(args[0][0], Integral):
# # It's a list of integers
# value = b''.join([chr(x) for x in args[0]])
# else:
# raise ValueError('item cannot be interpreted as an integer')
# elif isinstance(args[0], Integral):
# if args[0] < 0:
# raise ValueError('negative count')
# value = b'\x00' * args[0]
# else:
# value = args[0]
# return super(newbytes, cls).__new__(cls, value)
def __repr__(self):
s = super(oldstr, self).__repr__() # e.g. b'abc' on Py3, b'abc' on Py3
return s[1:]
def __str__(self):
s = super(oldstr, self).__str__() # e.g. "b'abc'" or "b'abc\\ndef'
# TODO: fix this:
assert s[:2] == "b'" and s[-1] == "'"
return unescape(s[2:-1]) # e.g. 'abc' or 'abc\ndef'
def __getitem__(self, y):
if isinstance(y, Integral):
return super(oldstr, self).__getitem__(slice(y, y+1))
else:
return super(oldstr, self).__getitem__(y)
def __getslice__(self, *args):
return self.__getitem__(slice(*args))
def __contains__(self, key):
if isinstance(key, int):
return False
def __native__(self):
return bytes(self)
__all__ = ['oldstr']

View File

@ -0,0 +1,97 @@
"""
Various non-built-in utility functions and definitions for Py2
compatibility in Py3.
For example:
>>> # The old_div() function behaves like Python 2's / operator
>>> # without "from __future__ import division"
>>> from past.utils import old_div
>>> old_div(3, 2) # like 3/2 in Py2
0
>>> old_div(3, 2.0) # like 3/2.0 in Py2
1.5
"""
import sys
import numbers
PY3 = sys.version_info[0] >= 3
PY2 = sys.version_info[0] == 2
PYPY = hasattr(sys, 'pypy_translation_info')
def with_metaclass(meta, *bases):
"""
Function from jinja2/_compat.py. License: BSD.
Use it like this::
class BaseForm(object):
pass
class FormType(type):
pass
class Form(with_metaclass(FormType, BaseForm)):
pass
This requires a bit of explanation: the basic idea is to make a
dummy metaclass for one level of class instantiation that replaces
itself with the actual metaclass. Because of internal type checks
we also need to make sure that we downgrade the custom metaclass
for one level to something closer to type (that's why __call__ and
__init__ comes back from type etc.).
This has the advantage over six.with_metaclass of not introducing
dummy classes into the final MRO.
"""
class metaclass(meta):
__call__ = type.__call__
__init__ = type.__init__
def __new__(cls, name, this_bases, d):
if this_bases is None:
return type.__new__(cls, name, (), d)
return meta(name, bases, d)
return metaclass('temporary_class', None, {})
def native(obj):
"""
On Py2, this is a no-op: native(obj) -> obj
On Py3, returns the corresponding native Py3 types that are
superclasses for forward-ported objects from Py2:
>>> from past.builtins import str, dict
>>> native(str(b'ABC')) # Output on Py3 follows. On Py2, output is 'ABC'
b'ABC'
>>> type(native(str(b'ABC')))
bytes
Existing native types on Py3 will be returned unchanged:
>>> type(native(b'ABC'))
bytes
"""
if hasattr(obj, '__native__'):
return obj.__native__()
else:
return obj
# An alias for future.utils.old_div():
def old_div(a, b):
"""
Equivalent to ``a / b`` on Python 2 without ``from __future__ import
division``.
TODO: generalize this to other objects (like arrays etc.)
"""
if isinstance(a, numbers.Integral) and isinstance(b, numbers.Integral):
return a // b
else:
return a / b
__all__ = ['PY3', 'PY2', 'PYPY', 'with_metaclass', 'native', 'old_div']