Author: Philip Jenvey <pjen...@underboss.org> Branch: py3.3 Changeset: r74304:50061af2dd26 Date: 2014-10-30 11:55 -0700 http://bitbucket.org/pypy/pypy/changeset/50061af2dd26/
Log: merge py3k diff too long, truncating to 2000 out of 3718 lines diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py --- a/pypy/config/pypyoption.py +++ b/pypy/config/pypyoption.py @@ -250,10 +250,6 @@ BoolOption("optimized_list_getitem", "special case the 'list[integer]' expressions", default=False), - BoolOption("builtinshortcut", - "a shortcut for operations between built-in types. XXX: " - "deprecated, not really a shortcut any more.", - default=False), BoolOption("getattributeshortcut", "track types that override __getattribute__", default=False, @@ -265,9 +261,6 @@ # weakrefs needed, because of get_subclasses() requires=[("translation.rweakref", True)]), - ChoiceOption("multimethods", "the multimethod implementation to use", - ["doubledispatch", "mrd"], - default="mrd"), BoolOption("withidentitydict", "track types that override __hash__, __eq__ or __cmp__ and use a special dict strategy for those which do not", default=False, diff --git a/pypy/doc/config/objspace.std.builtinshortcut.txt b/pypy/doc/config/objspace.std.builtinshortcut.txt deleted file mode 100644 --- a/pypy/doc/config/objspace.std.builtinshortcut.txt +++ /dev/null @@ -1,5 +0,0 @@ -A shortcut speeding up primitive operations between built-in types. - -This is a space-time trade-off: at the moment, this option makes a -translated pypy-c executable bigger by about 1.7 MB. (This can probably -be improved with careful analysis.) diff --git a/pypy/doc/config/objspace.std.multimethods.txt b/pypy/doc/config/objspace.std.multimethods.txt deleted file mode 100644 --- a/pypy/doc/config/objspace.std.multimethods.txt +++ /dev/null @@ -1,8 +0,0 @@ -Choose the multimethod implementation. - -* ``doubledispatch`` turns - a multimethod call into a sequence of normal method calls. - -* ``mrd`` uses a technique known as Multiple Row Displacement - which precomputes a few compact tables of numbers and - function pointers. diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -35,3 +35,7 @@ Split RPython documentation from PyPy documentation and clean up. There now is a clearer separation between documentation for users, developers and people interested in background information. + +.. branch: kill-multimethod + +Kill multimethod machinery, all multimethods were removed earlier. diff --git a/pypy/goal/targetpypystandalone.py b/pypy/goal/targetpypystandalone.py --- a/pypy/goal/targetpypystandalone.py +++ b/pypy/goal/targetpypystandalone.py @@ -215,23 +215,6 @@ from pypy.config.pypyoption import set_pypy_opt_level set_pypy_opt_level(config, translateconfig.opt) - # as of revision 27081, multimethod.py uses the InstallerVersion1 by default - # because it is much faster both to initialize and run on top of CPython. - # The InstallerVersion2 is optimized for making a translator-friendly - # structure for low level backends. However, InstallerVersion1 is still - # preferable for high level backends, so we patch here. - - from pypy.objspace.std import multimethod - if config.objspace.std.multimethods == 'mrd': - assert multimethod.InstallerVersion1.instance_counter == 0,\ - 'The wrong Installer version has already been instatiated' - multimethod.Installer = multimethod.InstallerVersion2 - elif config.objspace.std.multimethods == 'doubledispatch': - # don't rely on the default, set again here - assert multimethod.InstallerVersion2.instance_counter == 0,\ - 'The wrong Installer version has already been instatiated' - multimethod.Installer = multimethod.InstallerVersion1 - def print_help(self, config): self.opt_parser(config).print_help() diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py --- a/pypy/interpreter/typedef.py +++ b/pypy/interpreter/typedef.py @@ -630,6 +630,7 @@ from pypy.interpreter.nestedscope import Cell from pypy.interpreter.special import NotImplemented, Ellipsis + def descr_get_dict(space, w_obj): w_dict = w_obj.getdict(space) if w_dict is None: @@ -650,6 +651,11 @@ return space.w_None return lifeline.get_any_weakref(space) +dict_descr = GetSetProperty(descr_get_dict, descr_set_dict, descr_del_dict, + doc="dictionary for instance variables (if defined)") +dict_descr.name = '__dict__' + + def generic_ne(space, w_obj1, w_obj2): if space.eq_w(w_obj1, w_obj2): return space.w_False diff --git a/pypy/module/_io/interp_bufferedio.py b/pypy/module/_io/interp_bufferedio.py --- a/pypy/module/_io/interp_bufferedio.py +++ b/pypy/module/_io/interp_bufferedio.py @@ -52,16 +52,6 @@ class BlockingIOError(Exception): pass -def trap_EINTR(space, e): - """Return True if an EnvironmentError with errno == EINTR is set.""" - if not e.match(space, space.w_EnvironmentError): - return False - w_value = e.get_w_value(space) - w_errno = space.getattr(w_value, space.wrap("errno")) - if space.eq_w(w_errno, space.wrap(errno.EINTR)): - return True - return False - class W_BufferedIOBase(W_IOBase): def _check_init(self, space): raise NotImplementedError @@ -816,11 +806,6 @@ self._check_closed(space, "flush of closed file") with self.lock: self._flush_and_rewind_unlocked(space) - if self.readable: - # Rewind the raw stream so that its position corresponds to - # the current logical position. - self._raw_seek(space, -self._raw_offset(), 1) - self._reader_reset_buf() def _flush_and_rewind_unlocked(self, space): self._writer_flush_unlocked(space) diff --git a/pypy/module/_io/interp_iobase.py b/pypy/module/_io/interp_iobase.py --- a/pypy/module/_io/interp_iobase.py +++ b/pypy/module/_io/interp_iobase.py @@ -24,8 +24,7 @@ try: w_value = error.get_w_value(space) w_errno = space.getattr(w_value, space.wrap("errno")) - return space.is_true( - space.eq(w_errno, space.wrap(EINTR))) + return space.eq_w(w_errno, space.wrap(EINTR)) except OperationError: return False diff --git a/pypy/module/_io/test/test_io.py b/pypy/module/_io/test/test_io.py --- a/pypy/module/_io/test/test_io.py +++ b/pypy/module/_io/test/test_io.py @@ -390,3 +390,13 @@ assert mod == 'io' else: assert mod == '_io' + + def test_issue1902(self): + import _io + with _io.open(self.tmpfile, 'w+b', 4096) as f: + f.write(b'\xff' * 13569) + f.flush() + f.seek(0, 0) + f.read(1) + f.seek(-1, 1) + f.write(b'') diff --git a/pypy/module/array/interp_array.py b/pypy/module/array/interp_array.py --- a/pypy/module/array/interp_array.py +++ b/pypy/module/array/interp_array.py @@ -15,7 +15,6 @@ interp2app, interpindirect2app, unwrap_spec) from pypy.interpreter.typedef import ( GetSetProperty, TypeDef, make_weakref_descr) -from pypy.objspace.std.floatobject import W_FloatObject @unwrap_spec(typecode=str) @@ -691,7 +690,7 @@ try: item = unwrap(w_item) except OperationError, e: - if isinstance(w_item, W_FloatObject): + if space.isinstance_w(w_item, space.w_float): # Odd special case from cpython raise if mytype.method != '' and e.match(space, space.w_TypeError): diff --git a/pypy/module/array/test/test_array.py b/pypy/module/array/test/test_array.py --- a/pypy/module/array/test/test_array.py +++ b/pypy/module/array/test/test_array.py @@ -1038,11 +1038,6 @@ assert bytes(memoryview(self.array('i'))) == b'' -class AppTestArrayBuiltinShortcut(AppTestArray): - spaceconfig = AppTestArray.spaceconfig.copy() - spaceconfig['objspace.std.builtinshortcut'] = True - - class AppTestArrayReconstructor: spaceconfig = dict(usemodules=('array', 'struct')) diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py --- a/pypy/module/cpyext/typeobject.py +++ b/pypy/module/cpyext/typeobject.py @@ -303,11 +303,6 @@ @bootstrap_function def init_typeobject(space): - # Probably a hack - space.model.typeorder[W_PyCTypeObject] = [(W_PyCTypeObject, None), - (W_TypeObject, None), - (W_Root, None)] - make_typedescr(space.w_type.instancetypedef, basestruct=PyTypeObject, alloc=type_alloc, diff --git a/pypy/objspace/fake/objspace.py b/pypy/objspace/fake/objspace.py --- a/pypy/objspace/fake/objspace.py +++ b/pypy/objspace/fake/objspace.py @@ -2,7 +2,6 @@ from pypy.interpreter import argument, gateway from pypy.interpreter.baseobjspace import W_Root, ObjSpace, SpaceCache from pypy.interpreter.typedef import TypeDef, GetSetProperty -from pypy.objspace.std.stdtypedef import StdTypeDef from pypy.objspace.std.sliceobject import W_SliceObject from rpython.rlib.buffer import StringBuffer from rpython.rlib.objectmodel import instantiate, we_are_translated, specialize @@ -113,6 +112,10 @@ # ____________________________________________________________ +BUILTIN_TYPES = ['int', 'str', 'float', 'tuple', 'list', 'dict', 'bytes', + 'unicode', 'complex', 'slice', 'bool', 'text', 'object', + 'bytearray', 'memoryview'] + class FakeObjSpace(ObjSpace): def __init__(self, config=None): self._seen_extras = [] @@ -352,9 +355,7 @@ def setup(space): for name in (ObjSpace.ConstantTable + ObjSpace.ExceptionTable + - ['int', 'str', 'float', 'tuple', 'list', - 'dict', 'bytes', 'complex', 'slice', 'bool', - 'text', 'object', 'unicode', 'bytearray', 'memoryview']): + BUILTIN_TYPES): setattr(space, 'w_' + name, w_some_obj()) space.w_type = w_some_type() # @@ -385,7 +386,7 @@ @specialize.memo() def see_typedef(space, typedef): assert isinstance(typedef, TypeDef) - if not isinstance(typedef, StdTypeDef): + if typedef.name not in BUILTIN_TYPES: for name, value in typedef.rawdict.items(): space.wrap(value) diff --git a/pypy/objspace/std/boolobject.py b/pypy/objspace/std/boolobject.py --- a/pypy/objspace/std/boolobject.py +++ b/pypy/objspace/std/boolobject.py @@ -6,8 +6,8 @@ from rpython.tool.sourcetools import func_renamer, func_with_new_name from pypy.interpreter.gateway import WrappedDefault, interp2app, unwrap_spec +from pypy.interpreter.typedef import TypeDef from pypy.objspace.std.intobject import W_AbstractIntObject, W_IntObject -from pypy.objspace.std.stdtypedef import StdTypeDef class W_BoolObject(W_IntObject): @@ -80,7 +80,7 @@ W_BoolObject.w_True = W_BoolObject(True) -W_BoolObject.typedef = StdTypeDef("bool", W_IntObject.typedef, +W_BoolObject.typedef = TypeDef("bool", W_IntObject.typedef, __doc__ = """bool(x) -> bool Returns True when the argument x is true, False otherwise. diff --git a/pypy/objspace/std/builtinshortcut.py b/pypy/objspace/std/builtinshortcut.py deleted file mode 100644 --- a/pypy/objspace/std/builtinshortcut.py +++ /dev/null @@ -1,132 +0,0 @@ -from pypy.interpreter.baseobjspace import ObjSpace -from pypy.interpreter.error import OperationError -from pypy.objspace.descroperation import DescrOperation -from pypy.objspace.std.multimethod import FailedToImplement -from pypy.objspace.std.boolobject import W_BoolObject -from rpython.tool.sourcetools import func_with_new_name - -# ____________________________________________________________ -# -# The sole purpose of this file is performance. -# It speeds up the dispatch of operations between -# built-in objects. -# - -# this is a selection... a few operations are missing because they are -# thought to be very rare or most commonly used with non-builtin types -METHODS_WITH_SHORTCUT = dict.fromkeys( - ['add', 'sub', 'mul', 'truediv', 'floordiv', 'div', - 'mod', 'lshift', 'rshift', 'and_', 'or_', 'xor', 'pow', - 'lt', 'le', 'eq', 'ne', 'gt', 'ge', 'contains', - # unary - 'len', 'nonzero', 'repr', 'str', 'hash', - 'neg', 'invert', 'index', 'iter', 'next', 'buffer', - 'getitem', 'setitem', 'int', - # in-place - 'inplace_add', 'inplace_sub', 'inplace_mul', 'inplace_truediv', - 'inplace_floordiv', 'inplace_div', 'inplace_mod', 'inplace_pow', - 'inplace_lshift', 'inplace_rshift', 'inplace_and', 'inplace_or', - 'inplace_xor', - # other - 'format', - ]) - -KNOWN_MISSING = ['getattr', # mostly non-builtins or optimized by CALL_METHOD - 'setattr', 'delattr', 'userdel', # mostly for non-builtins - 'get', 'set', 'delete', # uncommon (except on functions) - 'delitem', 'trunc', # rare stuff? - 'abs', # rare stuff? - 'pos', 'divmod', # rare stuff? - 'float', 'long', # rare stuff? - 'isinstance', 'issubtype', - ] - -for _name, _, _, _specialmethods in ObjSpace.MethodTable: - if _specialmethods: - assert _name in METHODS_WITH_SHORTCUT or _name in KNOWN_MISSING, ( - "operation %r should be in METHODS_WITH_SHORTCUT or KNOWN_MISSING" - % (_name,)) - - -def filter_out_conversions(typeorder): - res = {} - for cls, order in typeorder.iteritems(): - res[cls] = [(target_type, converter) for (target_type, converter) in - order if converter is None] - return res - - -def install(space, mm, fallback_mm=None): - """Install a function <name>() on the space instance which invokes - a shortcut for built-in types. Returns the shortcutting multimethod - object or None. - """ - name = mm.name - if name not in METHODS_WITH_SHORTCUT: - return None - - # can be called multiple times without re-installing - if name in space.__dict__: - mm1, shortcut_method = space.__dict__[name].builtinshortcut - assert mm1 is mm - return shortcut_method - - #print 'installing shortcut for:', name - assert hasattr(DescrOperation, name) - - base_method = getattr(space.__class__, name) - - # Basic idea: we first try to dispatch the operation using purely - # the multimethod. If this is done naively, subclassing a built-in - # type like 'int' and overriding a special method like '__add__' - # doesn't work any more, because the multimethod will accept the int - # subclass and compute the result in the built-in way. To avoid - # this issue, we tweak the shortcut multimethods so that these ones - # (and only these ones) never match the interp-level subclasses - # built in pypy.interpreter.typedef.get_unique_interplevel_subclass. - expanded_order = space.model.get_typeorder_with_empty_usersubcls() - if fallback_mm: - mm = mm.merge_with(fallback_mm) - shortcut_method = mm.install_not_sliced(filter_out_conversions(expanded_order)) - - def operate(*args_w): - try: - return shortcut_method(space, *args_w) - except FailedToImplement: - pass - return base_method(space, *args_w) - - operate = func_with_new_name(operate, name) - operate.builtinshortcut = (mm, shortcut_method) - setattr(space, name, operate) - return shortcut_method - - -def install_is_true(space, mm_nonzero, mm_len): - shortcut = install(space, mm_nonzero, fallback_mm = mm_len) - assert 'is_true' not in space.__dict__ - - def is_true(w_obj): - # a bit of duplication of the logic from DescrOperation.is_true... - try: - w_res = shortcut(space, w_obj) - except FailedToImplement: - pass - else: - # the __bool__ method of built-in objects should - # always directly return a Bool; however, the __len__ method - # of built-in objects typically returns an unwrappable integer - if isinstance(w_res, W_BoolObject): - return bool(w_res.intval) - try: - return space.int_w(w_res) != 0 - except OperationError: - # I think no OperationError other than w_OverflowError - # could occur here - w_obj = w_res - - # general case fallback - return _DescrOperation_is_true(space, w_obj) - - _DescrOperation_is_true = DescrOperation.is_true.im_func - space.is_true = is_true diff --git a/pypy/objspace/std/bytearrayobject.py b/pypy/objspace/std/bytearrayobject.py --- a/pypy/objspace/std/bytearrayobject.py +++ b/pypy/objspace/std/bytearrayobject.py @@ -11,8 +11,8 @@ from pypy.objspace.std.bytesobject import ( getbytevalue, makebytesdata_w, newbytesdata_w) from pypy.interpreter.gateway import WrappedDefault, interp2app, unwrap_spec +from pypy.interpreter.typedef import TypeDef from pypy.objspace.std.sliceobject import W_SliceObject -from pypy.objspace.std.stdtypedef import StdTypeDef from pypy.objspace.std.stringmethods import StringMethods, _get_buffer from pypy.objspace.std.bytesobject import W_BytesObject from pypy.objspace.std.util import get_positive_index @@ -909,7 +909,7 @@ """ -W_BytearrayObject.typedef = StdTypeDef( +W_BytearrayObject.typedef = TypeDef( "bytearray", __doc__ = BytearrayDocstrings.__doc__, __new__ = interp2app(W_BytearrayObject.descr_new), diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py --- a/pypy/objspace/std/bytesobject.py +++ b/pypy/objspace/std/bytesobject.py @@ -11,7 +11,7 @@ from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.gateway import ( WrappedDefault, interp2app, interpindirect2app, unwrap_spec) -from pypy.objspace.std.stdtypedef import StdTypeDef +from pypy.interpreter.typedef import TypeDef from pypy.objspace.std.stringmethods import StringMethods @@ -769,7 +769,7 @@ return data -W_BytesObject.typedef = StdTypeDef( +W_BytesObject.typedef = TypeDef( "bytes", __new__ = interp2app(W_BytesObject.descr_new), __doc__ = """bytes(iterable_of_ints) -> bytes diff --git a/pypy/objspace/std/complexobject.py b/pypy/objspace/std/complexobject.py --- a/pypy/objspace/std/complexobject.py +++ b/pypy/objspace/std/complexobject.py @@ -3,9 +3,9 @@ from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault +from pypy.interpreter.typedef import GetSetProperty, TypeDef from pypy.objspace.std import newformat from pypy.objspace.std.floatobject import _hash_float -from pypy.objspace.std.stdtypedef import GetSetProperty, StdTypeDef from pypy.objspace.std.unicodeobject import unicode_to_decimal_w from rpython.rlib import jit, rcomplex from rpython.rlib.rarithmetic import intmask, r_ulonglong @@ -248,7 +248,7 @@ if self.user_overridden_class: return None from rpython.rlib.longlong2float import float2longlong - from pypy.objspace.std.model import IDTAG_COMPLEX as tag + from pypy.objspace.std.util import IDTAG_COMPLEX as tag real = space.float_w(space.getattr(self, space.wrap("real"))) imag = space.float_w(space.getattr(self, space.wrap("imag"))) real_b = rbigint.fromrarith_int(float2longlong(real)) @@ -511,7 +511,7 @@ return space.newfloat(getattr(w_obj, name)) return GetSetProperty(fget, doc=doc) -W_ComplexObject.typedef = StdTypeDef("complex", +W_ComplexObject.typedef = TypeDef("complex", __doc__ = """complex(real[, imag]) -> complex number Create a complex number from a real part and an optional imaginary part. diff --git a/pypy/objspace/std/dictmultiobject.py b/pypy/objspace/std/dictmultiobject.py --- a/pypy/objspace/std/dictmultiobject.py +++ b/pypy/objspace/std/dictmultiobject.py @@ -11,7 +11,7 @@ WrappedDefault, applevel, interp2app, unwrap_spec) from pypy.interpreter.mixedmodule import MixedModule from pypy.interpreter.signature import Signature -from pypy.objspace.std.stdtypedef import StdTypeDef +from pypy.interpreter.typedef import TypeDef from pypy.objspace.std.util import negate @@ -312,7 +312,7 @@ dictrepr = app.interphook("dictrepr") -W_DictMultiObject.typedef = StdTypeDef("dict", +W_DictMultiObject.typedef = TypeDef("dict", __doc__ = '''dict() -> new empty dictionary. dict(mapping) -> new dictionary initialized from a mapping object\'s (key, value) pairs. @@ -1146,8 +1146,6 @@ class W_BaseDictMultiIterObject(W_Root): _immutable_fields_ = ["iteratorimplementation"] - ignore_for_isinstance_cache = True - def __init__(self, space, iteratorimplementation): self.space = space self.iteratorimplementation = iteratorimplementation @@ -1238,7 +1236,7 @@ return space.newtuple([w_key, w_value]) raise OperationError(space.w_StopIteration, space.w_None) -W_DictMultiIterItemsObject.typedef = StdTypeDef( +W_DictMultiIterItemsObject.typedef = TypeDef( "dict_itemiterator", __iter__ = interp2app(W_DictMultiIterItemsObject.descr_iter), __next__ = interp2app(W_DictMultiIterItemsObject.descr_next), @@ -1246,7 +1244,7 @@ __reduce__ = interp2app(W_BaseDictMultiIterObject.descr_reduce), ) -W_DictMultiIterKeysObject.typedef = StdTypeDef( +W_DictMultiIterKeysObject.typedef = TypeDef( "dict_keyiterator", __iter__ = interp2app(W_DictMultiIterKeysObject.descr_iter), __next__ = interp2app(W_DictMultiIterKeysObject.descr_next), @@ -1254,7 +1252,7 @@ __reduce__ = interp2app(W_BaseDictMultiIterObject.descr_reduce), ) -W_DictMultiIterValuesObject.typedef = StdTypeDef( +W_DictMultiIterValuesObject.typedef = TypeDef( "dict_valueiterator", __iter__ = interp2app(W_DictMultiIterValuesObject.descr_iter), __next__ = interp2app(W_DictMultiIterValuesObject.descr_next), @@ -1383,7 +1381,7 @@ def descr_iter(self, space): return W_DictMultiIterValuesObject(space, self.w_dict.itervalues()) -W_DictViewItemsObject.typedef = StdTypeDef( +W_DictViewItemsObject.typedef = TypeDef( "dict_items", __repr__ = interp2app(W_DictViewItemsObject.descr_repr), __len__ = interp2app(W_DictViewItemsObject.descr_len), @@ -1407,7 +1405,7 @@ isdisjoint = interp2app(W_DictViewItemsObject.descr_isdisjoint), ) -W_DictViewKeysObject.typedef = StdTypeDef( +W_DictViewKeysObject.typedef = TypeDef( "dict_keys", __repr__ = interp2app(W_DictViewKeysObject.descr_repr), __len__ = interp2app(W_DictViewKeysObject.descr_len), @@ -1431,7 +1429,7 @@ isdisjoint = interp2app(W_DictViewKeysObject.descr_isdisjoint), ) -W_DictViewValuesObject.typedef = StdTypeDef( +W_DictViewValuesObject.typedef = TypeDef( "dict_values", __repr__ = interp2app(W_DictViewValuesObject.descr_repr), __len__ = interp2app(W_DictViewValuesObject.descr_len), diff --git a/pypy/objspace/std/floatobject.py b/pypy/objspace/std/floatobject.py --- a/pypy/objspace/std/floatobject.py +++ b/pypy/objspace/std/floatobject.py @@ -5,14 +5,13 @@ from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.gateway import interp2app, unwrap_spec, WrappedDefault -from pypy.interpreter.typedef import GetSetProperty +from pypy.interpreter.typedef import GetSetProperty, TypeDef from pypy.objspace.std import newformat from pypy.objspace.std.intobject import HASH_BITS, HASH_MODULUS, W_IntObject from pypy.objspace.std.longobject import ( W_AbstractLongObject, newlong_from_float) from rpython.rlib.rarithmetic import ( LONG_BIT, intmask, ovfcheck_float_to_int, r_uint) -from pypy.objspace.std.stdtypedef import StdTypeDef from pypy.objspace.std.util import wrap_parsestringerror from rpython.rlib import rarithmetic, rfloat from rpython.rlib.rbigint import rbigint @@ -191,7 +190,7 @@ if self.user_overridden_class: return None from rpython.rlib.longlong2float import float2longlong - from pypy.objspace.std.model import IDTAG_FLOAT as tag + from pypy.objspace.std.util import IDTAG_FLOAT as tag val = float2longlong(space.float_w(self)) b = rbigint.fromrarith_int(val) b = b.lshift(3).or_(rbigint.fromint(tag)) @@ -640,7 +639,7 @@ return space.wrap("0x%sp%s%d" % (s, sign, exp)) -W_FloatObject.typedef = StdTypeDef("float", +W_FloatObject.typedef = TypeDef("float", __doc__ = '''float(x) -> floating point number Convert a string or number to a floating point number, if possible.''', diff --git a/pypy/objspace/std/intobject.py b/pypy/objspace/std/intobject.py --- a/pypy/objspace/std/intobject.py +++ b/pypy/objspace/std/intobject.py @@ -25,11 +25,10 @@ from pypy.interpreter.error import OperationError, oefmt from pypy.interpreter.gateway import ( WrappedDefault, applevel, interp2app, interpindirect2app, unwrap_spec) +from pypy.interpreter.typedef import TypeDef from pypy.objspace.std import newformat -from pypy.objspace.std.model import ( - BINARY_OPS, CMP_OPS, COMMUTATIVE_OPS, IDTAG_INT) -from pypy.objspace.std.stdtypedef import StdTypeDef -from pypy.objspace.std.util import wrap_parsestringerror +from pypy.objspace.std.util import ( + BINARY_OPS, CMP_OPS, COMMUTATIVE_OPS, IDTAG_INT, wrap_parsestringerror) SENTINEL = object() @@ -761,6 +760,16 @@ _divmod, ovf2small=_divmod_ovf2small) +def setup_prebuilt(space): + if space.config.objspace.std.withprebuiltint: + W_IntObject.PREBUILT = [] + for i in range(space.config.objspace.std.prebuiltintfrom, + space.config.objspace.std.prebuiltintto): + W_IntObject.PREBUILT.append(W_IntObject(i)) + else: + W_IntObject.PREBUILT = None + + def wrapint(space, x): if not space.config.objspace.std.withprebuiltint: return W_IntObject(x) @@ -916,7 +925,7 @@ return newbigint(space, w_inttype, space.bigint_w(w_obj)) -W_AbstractIntObject.typedef = StdTypeDef("int", +W_AbstractIntObject.typedef = TypeDef("int", __doc__ = """int(x=0) -> integer int(x, base=10) -> integer diff --git a/pypy/objspace/std/iterobject.py b/pypy/objspace/std/iterobject.py --- a/pypy/objspace/std/iterobject.py +++ b/pypy/objspace/std/iterobject.py @@ -3,7 +3,7 @@ from pypy.interpreter.baseobjspace import W_Root from pypy.interpreter.gateway import interp2app, interpindirect2app from pypy.interpreter.error import OperationError -from pypy.objspace.std.stdtypedef import StdTypeDef +from pypy.interpreter.typedef import TypeDef class W_AbstractSeqIterObject(W_Root): @@ -40,7 +40,7 @@ def descr_length_hint(self, space): return self.getlength(space) -W_AbstractSeqIterObject.typedef = StdTypeDef( +W_AbstractSeqIterObject.typedef = TypeDef( "sequenceiterator", __doc__ = '''iter(collection) -> iterator iter(callable, sentinel) -> iterator @@ -159,7 +159,7 @@ raise OperationError(space.w_StopIteration, space.w_None) return w_item -W_ReverseSeqIterObject.typedef = StdTypeDef( +W_ReverseSeqIterObject.typedef = TypeDef( "reversesequenceiterator", __iter__ = interp2app(W_ReverseSeqIterObject.descr_iter), __next__ = interp2app(W_ReverseSeqIterObject.descr_next), diff --git a/pypy/objspace/std/listobject.py b/pypy/objspace/std/listobject.py --- a/pypy/objspace/std/listobject.py +++ b/pypy/objspace/std/listobject.py @@ -16,13 +16,13 @@ interp2app) from pypy.interpreter.generator import GeneratorIterator from pypy.interpreter.signature import Signature +from pypy.interpreter.typedef import TypeDef from pypy.objspace.std.bytesobject import W_BytesObject from pypy.objspace.std.floatobject import W_FloatObject from pypy.objspace.std.intobject import W_IntObject from pypy.objspace.std.iterobject import (W_FastListIterObject, W_ReverseSeqIterObject) from pypy.objspace.std.sliceobject import W_SliceObject, unwrap_start_stop -from pypy.objspace.std.stdtypedef import StdTypeDef from pypy.objspace.std.tupleobject import W_AbstractTupleObject from pypy.objspace.std.unicodeobject import W_UnicodeObject from pypy.objspace.std.util import get_positive_index, negate @@ -1801,7 +1801,7 @@ return space.is_true(space.lt(a.w_key, b.w_key)) -W_ListObject.typedef = StdTypeDef("list", +W_ListObject.typedef = TypeDef("list", __doc__ = """list() -> new empty list list(iterable) -> new list initialized from iterable's items""", __new__ = interp2app(W_ListObject.descr_new), diff --git a/pypy/objspace/std/longobject.py b/pypy/objspace/std/longobject.py --- a/pypy/objspace/std/longobject.py +++ b/pypy/objspace/std/longobject.py @@ -13,7 +13,7 @@ from pypy.objspace.std import newformat from pypy.objspace.std.intobject import ( HASH_BITS, HASH_MODULUS, W_AbstractIntObject, W_IntObject) -from pypy.objspace.std.model import COMMUTATIVE_OPS +from pypy.objspace.std.util import COMMUTATIVE_OPS def delegate_other(func): diff --git a/pypy/objspace/std/model.py b/pypy/objspace/std/model.py deleted file mode 100644 --- a/pypy/objspace/std/model.py +++ /dev/null @@ -1,353 +0,0 @@ -""" -The full list of which Python types and which implementation we want -to provide in this version of PyPy, along with conversion rules. -""" - -from pypy.objspace.std.multimethod import MultiMethodTable, FailedToImplement -from pypy.interpreter.baseobjspace import W_Root, ObjSpace -import pypy.interpreter.pycode -import pypy.interpreter.special - -_registered_implementations = set() -def registerimplementation(implcls): - """Hint to objspace.std.model to register the implementation class.""" - assert issubclass(implcls, W_Object) - _registered_implementations.add(implcls) - -option_to_typename = { - "withsmalllong" : ["smalllongobject.W_SmallLongObject"], - "withstrbuf" : ["strbufobject.W_StringBufferObject"], -} - -IDTAG_INT = 1 -IDTAG_FLOAT = 5 -IDTAG_COMPLEX = 7 - -class StdTypeModel: - - def __init__(self, config): - """NOT_RPYTHON: inititialization only""" - self.config = config - # All the Python types that we want to provide in this StdObjSpace - - # The object implementations that we want to 'link' into PyPy must be - # imported here. This registers them into the multimethod tables, - # *before* the type objects are built from these multimethod tables. - from pypy.objspace.std import objectobject - from pypy.objspace.std import boolobject - from pypy.objspace.std import intobject - from pypy.objspace.std import floatobject - from pypy.objspace.std import complexobject - from pypy.objspace.std import tupleobject - from pypy.objspace.std import listobject - from pypy.objspace.std import dictmultiobject - from pypy.objspace.std import setobject - from pypy.objspace.std import bytesobject - from pypy.objspace.std import bytearrayobject - from pypy.objspace.std import typeobject - from pypy.objspace.std import sliceobject - from pypy.objspace.std import longobject - from pypy.objspace.std import noneobject - from pypy.objspace.std import iterobject - from pypy.objspace.std import unicodeobject - from pypy.objspace.std import dictproxyobject - from pypy.objspace.std import proxyobject - from pypy.objspace.std import memoryobject - - - self.pythontypes = [] - self.pythontypes.append(objectobject.W_ObjectObject.typedef) - self.pythontypes.append(typeobject.W_TypeObject.typedef) - self.pythontypes.append(noneobject.W_NoneObject.typedef) - self.pythontypes.append(tupleobject.W_TupleObject.typedef) - self.pythontypes.append(listobject.W_ListObject.typedef) - self.pythontypes.append(dictmultiobject.W_DictMultiObject.typedef) - self.pythontypes.append(setobject.W_SetObject.typedef) - self.pythontypes.append(setobject.W_FrozensetObject.typedef) - self.pythontypes.append(iterobject.W_AbstractSeqIterObject.typedef) - self.pythontypes.append(bytesobject.W_BytesObject.typedef) - self.pythontypes.append(bytearrayobject.W_BytearrayObject.typedef) - self.pythontypes.append(unicodeobject.W_UnicodeObject.typedef) - self.pythontypes.append(intobject.W_IntObject.typedef) - self.pythontypes.append(boolobject.W_BoolObject.typedef) - self.pythontypes.append(longobject.W_LongObject.typedef) - self.pythontypes.append(floatobject.W_FloatObject.typedef) - self.pythontypes.append(complexobject.W_ComplexObject.typedef) - self.pythontypes.append(sliceobject.W_SliceObject.typedef) - self.pythontypes.append(memoryobject.W_MemoryView.typedef) - - # the set of implementation types - self.typeorder = { - objectobject.W_ObjectObject: [], - # XXX: Bool/Int/Long are pythontypes but still included here - # for delegation to Float/Complex - boolobject.W_BoolObject: [], - intobject.W_IntObject: [], - floatobject.W_FloatObject: [], - typeobject.W_TypeObject: [], - sliceobject.W_SliceObject: [], - longobject.W_LongObject: [], - noneobject.W_NoneObject: [], - complexobject.W_ComplexObject: [], - pypy.interpreter.pycode.PyCode: [], - pypy.interpreter.special.Ellipsis: [], - } - - self.imported_but_not_registered = { - bytesobject.W_BytesObject: True, - } - for option, value in config.objspace.std: - if option.startswith("with") and option in option_to_typename: - for classname in option_to_typename[option]: - modname = classname[:classname.index('.')] - classname = classname[classname.index('.')+1:] - d = {} - exec "from pypy.objspace.std.%s import %s" % ( - modname, classname) in d - implcls = d[classname] - if value: - self.typeorder[implcls] = [] - else: - self.imported_but_not_registered[implcls] = True - - # check if we missed implementations - for implcls in _registered_implementations: - if hasattr(implcls, 'register'): - implcls.register(self.typeorder) - assert (implcls in self.typeorder or - implcls in self.imported_but_not_registered), ( - "please add %r in StdTypeModel.typeorder" % (implcls,)) - - - for type in self.typeorder: - self.typeorder[type].append((type, None)) - - # register the order in which types are converted into each others - # when trying to dispatch multimethods. - # XXX build these lists a bit more automatically later - - if config.objspace.std.withsmalllong: - from pypy.objspace.std import smalllongobject - self.typeorder[smalllongobject.W_SmallLongObject] += [ - (floatobject.W_FloatObject, smalllongobject.delegate_SmallLong2Float), - (complexobject.W_ComplexObject, smalllongobject.delegate_SmallLong2Complex), - ] - - if config.objspace.std.withstrbuf: - from pypy.objspace.std import strbufobject - - # put W_Root everywhere - self.typeorder[W_Root] = [] - for type in self.typeorder: - from pypy.objspace.std import stdtypedef - if type is not W_Root and isinstance(type.typedef, stdtypedef.StdTypeDef): - self.typeorder[type].append((type.typedef.any, None)) - self.typeorder[type].append((W_Root, None)) - - self._typeorder_with_empty_usersubcls = None - - # ____________________________________________________________ - # Prebuilt common integer values - - if config.objspace.std.withprebuiltint: - # XXX: currently broken on py3k - intobject.W_IntObject.PREBUILT = [] - for i in range(config.objspace.std.prebuiltintfrom, - config.objspace.std.prebuiltintto): - intobject.W_IntObject.PREBUILT.append(intobject.W_IntObject(i)) - del i - else: - intobject.W_IntObject.PREBUILT = None - - # ____________________________________________________________ - - def get_typeorder_with_empty_usersubcls(self): - if self._typeorder_with_empty_usersubcls is None: - from pypy.interpreter.typedef import enum_interplevel_subclasses - from pypy.objspace.std import stdtypedef - result = self.typeorder.copy() - for cls in self.typeorder: - if (hasattr(cls, 'typedef') and cls.typedef is not None and - cls.typedef.acceptable_as_base_class): - subclslist = enum_interplevel_subclasses(self.config, cls) - for subcls in subclslist: - if cls in subcls.__bases__: # only direct subclasses - # for user subclasses we only accept "generic" - # matches: "typedef.any" is the applevel-type-based - # matching, and "W_Root" is ANY. - matches = [] - if isinstance(cls.typedef, stdtypedef.StdTypeDef): - matches.append((cls.typedef.any, None)) - matches.append((W_Root, None)) - result[subcls] = matches - self._typeorder_with_empty_usersubcls = result - return self._typeorder_with_empty_usersubcls - -def _op_negated(function): - def op(space, w_1, w_2): - return space.not_(function(space, w_1, w_2)) - return op - -def _op_swapped(function): - def op(space, w_1, w_2): - return function(space, w_2, w_1) - return op - -def _op_swapped_negated(function): - def op(space, w_1, w_2): - return space.not_(function(space, w_2, w_1)) - return op - - -CMP_OPS = dict(lt='<', le='<=', eq='==', ne='!=', gt='>', ge='>=') -CMP_CORRESPONDANCES = [ - ('eq', 'ne', _op_negated), - ('lt', 'gt', _op_swapped), - ('le', 'ge', _op_swapped), - ('lt', 'ge', _op_negated), - ('le', 'gt', _op_negated), - ('lt', 'le', _op_swapped_negated), - ('gt', 'ge', _op_swapped_negated), - ] -for op1, op2, value in CMP_CORRESPONDANCES[:]: - i = CMP_CORRESPONDANCES.index((op1, op2, value)) - CMP_CORRESPONDANCES.insert(i+1, (op2, op1, value)) -BINARY_BITWISE_OPS = {'and': '&', 'lshift': '<<', 'or': '|', 'rshift': '>>', - 'xor': '^'} -BINARY_OPS = dict(add='+', div='/', floordiv='//', mod='%', mul='*', sub='-', - truediv='/', **BINARY_BITWISE_OPS) -COMMUTATIVE_OPS = ('add', 'mul', 'and', 'or', 'xor') - -def add_extra_comparisons(): - """ - Add the missing comparison operators if they were not explicitly - defined: eq <-> ne and lt <-> le <-> gt <-> ge. - We try to add them in the order defined by the CMP_CORRESPONDANCES - table, thus favouring swapping the arguments over negating the result. - """ - originalentries = {} - for op in CMP_OPS.iterkeys(): - originalentries[op] = getattr(MM, op).signatures() - - for op1, op2, correspondance in CMP_CORRESPONDANCES: - mirrorfunc = getattr(MM, op2) - for types in originalentries[op1]: - t1, t2 = types - if t1 is t2: - if not mirrorfunc.has_signature(types): - functions = getattr(MM, op1).getfunctions(types) - assert len(functions) == 1, ('Automatic' - ' registration of comparison functions' - ' only work when there is a single method for' - ' the operation.') - mirrorfunc.register(correspondance(functions[0]), *types) - - -# ____________________________________________________________ - -W_ANY = W_Root - -class W_Object(W_Root): - "Parent base class for wrapped objects provided by the StdObjSpace." - # Note that not all wrapped objects in the interpreter inherit from - # W_Object. (They inherit from W_Root.) - __slots__ = () - - def __repr__(self): - name = getattr(self, 'name', '') - if not isinstance(name, str): - name = '' - s = '%s(%s)' % (self.__class__.__name__, name) - w_cls = getattr(self, 'w__class__', None) - if w_cls is not None and w_cls is not self: - s += ' instance of %s' % self.w__class__ - return '<%s>' % s - - -class UnwrapError(Exception): - pass - - -class StdObjSpaceMultiMethod(MultiMethodTable): - - def __init__(self, operatorsymbol, arity, specialnames=None, **extras): - """NOT_RPYTHON: cannot create new multimethods dynamically. - """ - MultiMethodTable.__init__(self, arity, W_ANY, - argnames_before = ['space']) - self.operatorsymbol = operatorsymbol - if specialnames is None: - specialnames = [operatorsymbol] - assert isinstance(specialnames, list) - self.specialnames = specialnames # e.g. ['__xxx__', '__rxxx__'] - self.extras = extras - # transform '+' => 'add' etc. - for line in ObjSpace.MethodTable: - realname, symbolname = line[:2] - if symbolname == operatorsymbol: - self.name = realname - break - else: - self.name = operatorsymbol - - if extras.get('general__args__', False): - self.argnames_after = ['__args__'] - if extras.get('varargs_w', False): - self.argnames_after = ['args_w'] - self.argnames_after += extras.get('extra_args', []) - - def install_not_sliced(self, typeorder, baked_perform_call=True): - return self.install(prefix = '__mm_' + self.name, - list_of_typeorders = [typeorder]*self.arity, - baked_perform_call=baked_perform_call) - - def merge_with(self, other): - # Make a new 'merged' multimethod including the union of the two - # tables. In case of conflict, pick the entry from 'self'. - if self.arity != other.arity: - return self # XXX that's the case of '**' - operatorsymbol = '%s_merge_%s' % (self.name, other.name) - assert self.extras == other.extras - mm = StdObjSpaceMultiMethod(operatorsymbol, self.arity, **self.extras) - # - def merge(node1, node2): - assert type(node1) is type(node2) - if isinstance(node1, dict): - d = node1.copy() - d.update(node2) - for key in node1: - if key in node2: - d[key] = merge(node1[key], node2[key]) - return d - else: - assert isinstance(node1, list) - assert node1 - return node1 # pick the entry from 'self' - # - mm.dispatch_tree = merge(self.dispatch_tree, other.dispatch_tree) - return mm - -NOT_MULTIMETHODS = set( - ['delattr', 'delete', 'get', 'id', 'inplace_div', 'inplace_floordiv', - 'inplace_lshift', 'inplace_mod', 'inplace_pow', 'inplace_rshift', - 'inplace_truediv', 'is_', 'set', 'setattr', 'type', 'userdel', - 'isinstance', 'issubtype', 'int', 'ord']) -# XXX should we just remove those from the method table or we're happy -# with just not having multimethods? - -class MM: - """StdObjSpace multimethods""" - - call = StdObjSpaceMultiMethod('call', 1, ['__call__'], - general__args__=True) - init = StdObjSpaceMultiMethod('__init__', 1, general__args__=True) - getnewargs = StdObjSpaceMultiMethod('__getnewargs__', 1) - - # add all regular multimethods here - for _name, _symbol, _arity, _specialnames in ObjSpace.MethodTable: - if _name not in locals() and _name not in NOT_MULTIMETHODS: - mm = StdObjSpaceMultiMethod(_symbol, _arity, _specialnames) - locals()[_name] = mm - del mm - - pow.extras['defaults'] = (None,) diff --git a/pypy/objspace/std/multimethod.py b/pypy/objspace/std/multimethod.py deleted file mode 100644 --- a/pypy/objspace/std/multimethod.py +++ /dev/null @@ -1,972 +0,0 @@ - -from rpython.tool.sourcetools import compile2 - -# This provide two compatible implementations of "multimethods". A -# multimethod is a callable object which chooses and calls a real -# function from a table of pre-registered functions. The choice depends -# on the '__class__' of all arguments. For example usages see -# test_multimethod. - -# These multimethods support delegation: for each class A we must -# provide a "typeorder", which is list of pairs (B, converter) where B -# is a class and 'converter' is a function that can convert from an -# instance of A to an instance of B. If 'converter' is None it is -# assumed that the instance needs no conversion. The first entry in the -# typeorder of a class A must almost always be (A, None). - -# A slightly non-standard feature of PyPy's multimethods is the way in -# which they interact with normal subclassing. Basically, they don't. -# Suppose that A is a parent class of B. Then a function registered for -# an argument class A only accepts an instance whose __class__ is A, not -# B. To make it accept an instance of B, the typeorder for B must -# contain (A, None). An exception to this strict rule is if C is -# another subclass of A which is not mentioned at all in the typeorder; -# in this case C is considered to be equivalent to A. - - -class FailedToImplement(Exception): - def __new__(cls, *args): - if cls is FailedToImplement: - assert not args, "use FailedToImplementArgs!" - return Exception.__new__(cls, *args) - - def get_w_value(self, space): - return None - - def get_w_type(self, space): - return None - - def __str__(self): - return '<FailedToImplement(None, None)>' - -class FailedToImplementArgs(FailedToImplement): - def __init__(self, w_type=None, w_value=None): - self.w_type = w_type - self.w_value = w_value - - def get_w_value(self, space): - # convenience: same semantics as with OperationError - return self.w_value - - def get_w_type(self, space): - return self.w_type - - def __str__(self): - return '<FailedToImplement(%s, %s)>' % (self.w_type, self.w_value) - - - -def raiseFailedToImplement(): - raise FailedToImplement - - -class MultiMethodTable: - - def __init__(self, arity, root_class, argnames_before=[], argnames_after=[]): - """NOT_RPYTHON: cannot create new multimethods dynamically. - MultiMethod-maker dispatching on exactly 'arity' arguments. - """ - if arity < 1: - raise ValueError, "multimethods cannot dispatch on nothing" - self.arity = arity - self.root_class = root_class - self.dispatch_tree = {} - self.argnames_before = list(argnames_before) - self.argnames_after = list(argnames_after) - - def register(self, function, *types, **kwds): - assert len(types) == self.arity - assert kwds.keys() == [] or kwds.keys() == ['order'] - order = kwds.get('order', 0) - node = self.dispatch_tree - for type in types[:-1]: - node = node.setdefault(type, {}) - lst = node.setdefault(types[-1], []) - if order >= len(lst): - lst += [None] * (order+1 - len(lst)) - assert lst[order] is None, "duplicate function for %r@%d" % ( - types, order) - lst[order] = function - - def install(self, prefix, list_of_typeorders, baked_perform_call=True, - base_typeorder=None, installercls=None): - "NOT_RPYTHON: initialization-time only" - assert len(list_of_typeorders) == self.arity - installercls = installercls or Installer - installer = installercls(self, prefix, list_of_typeorders, - baked_perform_call=baked_perform_call, - base_typeorder=base_typeorder) - return installer.install() - - def install_if_not_empty(self, prefix, list_of_typeorders, - base_typeorder=None, installercls=None): - "NOT_RPYTHON: initialization-time only" - assert len(list_of_typeorders) == self.arity - installercls = installercls or Installer - installer = installercls(self, prefix, list_of_typeorders, - base_typeorder=base_typeorder) - if installer.is_empty(): - return None - else: - return installer.install() - - - - # ____________________________________________________________ - # limited dict-like interface to the dispatch table - - def getfunctions(self, types): - assert len(types) == self.arity - node = self.dispatch_tree - for type in types: - node = node[type] - return [fn for fn in node if fn is not None] - - def has_signature(self, types): - try: - self.getfunctions(types) - except KeyError: - return False - else: - return True - - def signatures(self): - """NOT_RPYTHON""" - result = [] - def enum_keys(types_so_far, node): - for type, subnode in node.items(): - next_types = types_so_far+(type,) - if isinstance(subnode, dict): - enum_keys(next_types, subnode) - else: - assert len(next_types) == self.arity - result.append(next_types) - enum_keys((), self.dispatch_tree) - return result - -# ____________________________________________________________ -# Installer version 1 - -class InstallerVersion1: - """NOT_RPYTHON""" - - instance_counter = 0 - - mmfunccache = {} - - prefix_memo = {} - - def __init__(self, multimethod, prefix, list_of_typeorders, - baked_perform_call=True, base_typeorder=None): - self.__class__.instance_counter += 1 - self.multimethod = multimethod - # avoid prefix clashes, user code should supply different prefixes - # itself for nice names in tracebacks - base_prefix = prefix - n = 1 - while prefix in self.prefix_memo: - n += 1 - prefix = "%s%d" % (base_prefix, n) - self.prefix = prefix - self.prefix_memo[prefix] = 1 - self.list_of_typeorders = list_of_typeorders - self.check_typeorders() - self.subtree_cache = {} - self.to_install = [] - self.non_empty = self.build_tree([], multimethod.dispatch_tree) - - self.baked_perform_call = baked_perform_call - - if self.non_empty: - perform = [(None, prefix, 0)] - else: - perform = [] - - self.perform_call = self.build_function(None, prefix+'_perform_call', - None, perform) - - def check_typeorders(self): - # xxx we use a '__'-separated list of the '__name__' of the types - # in build_single_method(), so types with the same __name__ or - # with '__' in them would obscurely break this logic - for typeorder in self.list_of_typeorders: - for type in typeorder: - assert '__' not in type.__name__, ( - "avoid '__' in the name of %r" % (type,)) - names = dict.fromkeys([type.__name__ for type in typeorder]) - assert len(names) == len(typeorder), ( - "duplicate type.__name__ in %r" % (typeorder,)) - - def is_empty(self): - return not self.non_empty - - def install(self): - #f = open('LOGFILE', 'a') - #print >> f, '_'*60 - #import pprint - #pprint.pprint(self.list_of_typeorders, f) - - def class_key(cls): - "Returns an object such that class_key(subcls) > class_key(cls)." - return len(cls.__mro__) - - # Sort 'to_install' so that base classes come first, which is - # necessary for the 'parentfunc' logic in the loop below to work. - # Moreover, 'to_install' can contain two functions with the same - # name for the root class: the default fallback one and the real - # one. So we have to sort the real one just after the default one - # so that the default one gets overridden. - def key(target, funcname, func, source, fallback): - if target is None: - return () - return (class_key(target), not fallback) - self.to_install.sort(lambda a, b: cmp(key(*a), key(*b))) - - for target, funcname, func, source, fallback in self.to_install: - if target is not None: - # If the parent class provides a method of the same - # name which is actually the same 'func', we don't need - # to install it again. Useful with fallback functions. - parentfunc = getattr(target, funcname, None) - parentfunc = getattr(parentfunc, 'im_func', None) - if parentfunc is func: - continue - #print >> f, target.__name__, funcname - #if source: - # print >> f, source - #else: - # print >> f, '*\n' - setattr(target, funcname, func) - #f.close() - return self.perform_call - - def build_tree(self, types_so_far, dispatch_node): - key = tuple(types_so_far) - if key in self.subtree_cache: - return self.subtree_cache[key] - non_empty = False - typeorder = self.list_of_typeorders[len(types_so_far)] - for next_type in typeorder: - if self.build_single_method(typeorder, types_so_far, next_type, - dispatch_node): - non_empty = True - self.subtree_cache[key] = non_empty - return non_empty - - def build_single_method(self, typeorder, types_so_far, next_type, - dispatch_node): - funcname = '__'.join([self.prefix] + [t.__name__ for t in types_so_far]) - - order = typeorder[next_type] - #order = [(next_type, None)] + order - - things_to_call = [] - for type, conversion in order: - if type not in dispatch_node: - # there is no possible completion of types_so_far+[type] - # that could lead to a registered function. - continue - match = dispatch_node[type] - if isinstance(match, dict): - if self.build_tree(types_so_far+[type], match): - call = funcname + '__' + type.__name__ - call_selfarg_index = len(types_so_far) + 1 - things_to_call.append((conversion, call, - call_selfarg_index)) - else: - for func in match: # list of functions - if func is not None: - things_to_call.append((conversion, func, None)) - - funcname = intern(funcname) - self.build_function(next_type, funcname, len(types_so_far), - things_to_call) - return bool(things_to_call) - - def build_function(self, target, funcname, func_selfarg_index, - things_to_call): - # support for inventing names for the entries in things_to_call - # which are real function objects instead of strings - miniglobals = {'FailedToImplement': FailedToImplement, '__name__': __name__} - def invent_name(obj): - if isinstance(obj, str): - return obj - name = obj.__name__ - n = 1 - while name in miniglobals: - n += 1 - name = '%s%d' % (obj.__name__, n) - miniglobals[name] = obj - return name - - funcargs = ['arg%d' % i for i in range(self.multimethod.arity)] - - bodylines = [] - for conversion, call, call_selfarg_index in things_to_call: - callargs = funcargs[:] - if conversion is not None: - to_convert = func_selfarg_index - convert_callargs = (self.multimethod.argnames_before + - [callargs[to_convert]]) - callargs[to_convert] = '%s(%s)' % ( - invent_name(conversion), ', '.join(convert_callargs)) - callname = invent_name(call) - if call_selfarg_index is not None: - # fallback on root_class - self.build_function(self.multimethod.root_class, - callname, call_selfarg_index, []) - callname = '%s.%s' % (callargs.pop(call_selfarg_index), callname) - callargs = (self.multimethod.argnames_before + - callargs + self.multimethod.argnames_after) - bodylines.append('return %s(%s)' % (callname, ', '.join(callargs))) - - fallback = False - if not bodylines: - miniglobals['raiseFailedToImplement'] = raiseFailedToImplement - bodylines = ['return raiseFailedToImplement()'] - fallback = True - # NB. make sure that there is only one fallback function object, - # i.e. the key used in the mmfunccache below is always the same - # for all functions with the same name and an empty bodylines. - - # protect all lines apart from the last one by a try:except: - for i in range(len(bodylines)-2, -1, -1): - bodylines[i:i+1] = ['try:', - ' ' + bodylines[i], - 'except FailedToImplement:', - ' pass'] - - if func_selfarg_index is not None: - selfargs = [funcargs.pop(func_selfarg_index)] - else: - selfargs = [] - funcargs = (selfargs + self.multimethod.argnames_before + - funcargs + self.multimethod.argnames_after) - - if target is None and not self.baked_perform_call: - return funcargs, bodylines[0][len('return '):], miniglobals, fallback - - # indent mode - bodylines = [' ' + line for line in bodylines] - - bodylines.insert(0, 'def %s(%s):' % (funcname, ', '.join(funcargs))) - bodylines.append('') - source = '\n'.join(bodylines) - - # XXX find a better place (or way) to avoid duplicate functions - l = miniglobals.items() - l.sort() - l = tuple(l) - key = (source, l) - try: - func = self.mmfunccache[key] - except KeyError: - exec compile2(source) in miniglobals - func = miniglobals[funcname] - self.mmfunccache[key] = func - #else: - # print "avoided duplicate function", func - self.to_install.append((target, funcname, func, source, fallback)) - return func - -# ____________________________________________________________ -# Installer version 2 - -class MMDispatcher(object): - """NOT_RPYTHON - Explicit dispatcher class. The __call__ and dispatch() methods - are only present for documentation purposes. The InstallerVersion2 - uses the expressions() method to precompute fast RPython-friendly - dispatch tables. - """ - _revcache = None - - def __init__(self, multimethod, list_of_typeorders): - self.multimethod = multimethod - self.list_of_typeorders = list_of_typeorders - - def __call__(self, *args): - # for testing only: this is slow - i = len(self.multimethod.argnames_before) - j = i + self.multimethod.arity - k = j + len(self.multimethod.argnames_after) - assert len(args) == k - prefixargs = args[:i] - dispatchargs = args[i:j] - suffixargs = args[j:] - return self.dispatch([x.__class__ for x in dispatchargs], - prefixargs, - dispatchargs, - suffixargs) - - def dispatch(self, argtypes, prefixargs, args, suffixargs): - # for testing only: this is slow - def expr(v): - if isinstance(v, Call): - return v.function(*[expr(w) for w in v.arguments]) - else: - return v - # XXX this is incomplete: for each type in argtypes but not - # in the typeorder, we should look for the first base class - # that is in the typeorder. - e = None - for v in self.expressions(argtypes, prefixargs, args, suffixargs): - try: - return expr(v) - except FailedToImplement, e: - pass - else: - raise e or FailedToImplement() - - def expressions(self, argtypes, prefixargs, args, suffixargs): - """Lists the possible expressions that call the appropriate - function for the given argument types. Each expression is a Call - object. The intent is that at run-time the first Call that doesn't - cause FailedToImplement to be raised is the good one. - """ - prefixargs = tuple(prefixargs) - suffixargs = tuple(suffixargs) - - def walktree(node, args_so_far): - if isinstance(node, list): - for func in node: - if func is not None: - result.append(Call(func, prefixargs + - args_so_far + - suffixargs)) - else: - index = len(args_so_far) - typeorder = self.list_of_typeorders[index] - next_type = argtypes[index] - for target_type, converter in typeorder[next_type]: - if target_type not in node: - continue - next_arg = args[index] - if converter: - next_arg = Call(converter, prefixargs + (next_arg,)) - walktree(node[target_type], args_so_far + (next_arg,)) - - result = [] - walktree(self.multimethod.dispatch_tree, ()) - return result - - def anychance(self, typesprefix): - # is there any chance that a list of types starting with typesprefix - # could lead to a successful dispatch? - # (START-UP TIME OPTIMIZATION ONLY) - if self._revcache is None: - - def build_tree(types_so_far, dispatch_node): - non_empty = False - typeorder = self.list_of_typeorders[len(types_so_far)] - for next_type in typeorder: - if build_single_method(typeorder, types_so_far, next_type, - dispatch_node): - non_empty = True - if non_empty: - self._revcache[types_so_far] = True - return non_empty - - def build_single_method(typeorder, types_so_far, next_type, - dispatch_node): - order = typeorder[next_type] - things_to_call = False - for type, conversion in order: - if type not in dispatch_node: - # there is no possible completion of - # types_so_far+[type] that could lead to a - # registered function. - continue - match = dispatch_node[type] - if isinstance(match, dict): - if build_tree(types_so_far+(next_type,), match): - things_to_call = True - elif match: - things_to_call = True - return things_to_call - - self._revcache = {} - build_tree((), self.multimethod.dispatch_tree) - return tuple(typesprefix) in self._revcache - - -class Call(object): - """ Represents a call expression. - The arguments may themselves be Call objects. - """ - def __init__(self, function, arguments): - self.function = function - self.arguments = arguments - - -class CompressedArray(object): - def __init__(self, null_value): - self.null_value = null_value - self.items = [null_value] - - def ensure_length(self, newlen): - if newlen > len(self.items): - self.items.extend([self.null_value] * (newlen - len(self.items))) - - def insert_subarray(self, array): - # insert the given array of numbers into the indexlist, - # allowing null values to become non-null - if array.count(self.null_value) == len(array): - return 0 - test = 1 - while True: - self.ensure_length(test+len(array)) - for i in xrange(len(array)): - if not (array[i] == self.items[test+i] or - array[i] == self.null_value or - self.items[test+i] == self.null_value): - break - else: - # success - for i in range(len(array)): - if array[i] != self.null_value: - self.items[test+i] = array[i] - return test - test += 1 - - def _freeze_(self): - return True - - -class MRDTable(object): - # Multi-Method Dispatch Using Multiple Row Displacement, - # Candy Pang, Wade Holst, Yuri Leontiev, and Duane Szafron - # University of Alberta, Edmonton AB T6G 2H1 Canada - # can be found on http://web.cs.ualberta.ca/~yuri/publ.htm - - Counter = 0 - - def __init__(self, list_of_types): - self.id = MRDTable.Counter - MRDTable.Counter += 1 - self.list_of_types = list_of_types - self.typenum = dict(zip(list_of_types, range(len(list_of_types)))) - self.attrname = '__mrd%d_typenum' % self.id - for t1, num in self.typenum.items(): - setattr(t1, self.attrname, num) - self.indexarray = CompressedArray(0) - - def get_typenum(self, cls): - return self.typenum[cls] - - def is_anti_range(self, typenums): - # NB. typenums should be sorted. Returns (a, b) if typenums contains - # at least half of all typenums and its complement is range(a, b). - # Returns (None, None) otherwise. Returns (0, 0) if typenums contains - # everything. - n = len(self.list_of_types) - if len(typenums) <= n // 2: - return (None, None) - typenums = dict.fromkeys(typenums) - complement = [typenum for typenum in range(n) - if typenum not in typenums] - if not complement: - return (0, 0) - a = min(complement) - b = max(complement) + 1 - if complement == range(a, b): - return (a, b) - else: - return (None, None) - - def normalize_length(self, next_array): - # make sure that the indexarray is not smaller than any funcarray - self.indexarray.ensure_length(len(next_array.items)) - - -def invent_name(miniglobals, obj): - if isinstance(obj, str): - return obj - name = obj.__name__ - n = 1 - while name in miniglobals: - n += 1 - name = '%s%d' % (obj.__name__, n) - miniglobals[name] = obj - return name - - -class FuncEntry(object): - - def __init__(self, bodylines, miniglobals, fallback): - self.body = '\n '.join(bodylines) - self.miniglobals = miniglobals - self.fallback = fallback - self.possiblenames = [] - self.typetree = {} - self._function = None - - def key(self): - lst = self.miniglobals.items() - lst.sort() - return self.body, tuple(lst) - - def get_function_name(self): - # pick a name consistently based on self.possiblenames - length = min([len(parts) for parts in self.possiblenames]) - result = [] - for i in range(length): - choices = {} - for parts in self.possiblenames: - choices[parts[i]] = True - parts = choices.keys() - res = str(len(parts)) - for part in parts: - if type(part) is str: # there is a string at this pos - if '0_fail' in choices: - res = '0_fail' - elif len(parts) == 1: - res = part - break - else: - # only types at this location, try to find a common base - basecls = parts[0] - for cls in parts[1:]: - if issubclass(basecls, cls): - basecls = cls - for cls in parts[1:]: - if not issubclass(cls, basecls): - break # no common base - else: - res = basecls.__name__ - result.append(res) - return '_'.join(result) - - def make_function(self, fnargs, nbargs_before, mrdtable): - if self._function is not None: - return self._function - name = self.get_function_name() - self.compress_typechecks(mrdtable) - checklines = self.generate_typechecks(mrdtable, fnargs[nbargs_before:]) - if not checklines: - body = self.body - else: - checklines.append(self.body) - body = '\n '.join(checklines) - source = 'def %s(%s):\n %s\n' % (name, ', '.join(fnargs), body) - self.debug_dump(source) - exec compile2(source) in self.miniglobals - self._function = self.miniglobals[name] - return self._function - - def debug_dump(self, source): - if 0: # for debugging the generated mm sources - name = self.get_function_name() - f = open('/tmp/mm-source/%s' % name, 'a') - for possiblename in self.possiblenames: - print >> f, '#', - for part in possiblename: - print >> f, getattr(part, '__name__', part), - print >> f - print >> f - print >> f, source - f.close() - - def register_valid_types(self, types): - node = self.typetree - for t1 in types[:-1]: - if node is True: - return - node = node.setdefault(t1, {}) - if node is True: - return - node[types[-1]] = True - - def no_typecheck(self): - self.typetree = True - - def compress_typechecks(self, mrdtable): - def full(node): - if node is True: - return 1 - fulls = 0 - for key, subnode in node.items(): - if full(subnode): - node[key] = True - fulls += 1 - if fulls == types_total: - return 1 - return 0 - - types_total = len(mrdtable.list_of_types) - if full(self.typetree): - self.typetree = True - - def generate_typechecks(self, mrdtable, args): - attrname = mrdtable.attrname - possibletypes = [{} for _ in args] - any_type_is_ok = [False for _ in args] - - def generate(node, level=0): - # this generates type-checking code like the following: - # - # _argtypenum = arg1.__typenum - # if _argtypenum == 5: - # ... - # elif _argtypenum == 6 or _argtypenum == 8: - # ... - # else: - # _failedtoimplement = True - # - # or, in the common particular case of an "anti-range", we optimize it to: - # - # _argtypenum = arg1.__typenum - # if _argtypenum < 5 or _argtypenum >= 10: - # ... - # else: - # _failedtoimplement = True - # - result = [] - indent = ' '*level - if node is True: - for i in range(level, len(args)): - any_type_is_ok[i] = True - result.append('%s_failedtoimplement = False' % (indent,)) - return result - if not node: - result.append('%s_failedtoimplement = True' % (indent,)) - return result - result.append('%s_argtypenum = %s.%s' % (indent, args[level], - attrname)) - cases = {} - for key, subnode in node.items(): - possibletypes[level][key] = True - casebody = tuple(generate(subnode, level+1)) - typenum = mrdtable.get_typenum(key) - cases.setdefault(casebody, []).append(typenum) - for casebody, typenums in cases.items(): - typenums.sort() - cases = [(typenums, casebody) - for (casebody, typenums) in cases.items()] - cases.sort() - if len(cases) == 1: - typenums, casebody = cases[0] - a, b = mrdtable.is_anti_range(typenums) - else: - a, b = None, None - keyword = 'if' - for typenums, casebody in cases: - if a is not None: - if b - a == 1: - condition = '_argtypenum != %d' % a - elif b == a: - condition = 'True' - else: - condition = '_argtypenum < %d or _argtypenum >= %d' % ( - a, b) - else: - conditions = ['_argtypenum == %d' % typenum - for typenum in typenums] - condition = ' or '.join(conditions) - result.append('%s%s %s:' % (indent, keyword, condition)) - result.extend(casebody) - keyword = 'elif' - result.append('%selse:' % (indent,)) - result.append('%s _failedtoimplement = True' % (indent,)) - return result - - result = [] - if self.typetree is not True: - result.extend(generate(self.typetree)) - result.append('if _failedtoimplement:') - result.append(' raise FailedToImplement') - for level in range(len(args)): - if not any_type_is_ok[level]: - cls = commonbase(possibletypes[level].keys()) - clsname = invent_name(self.miniglobals, cls) - result.append('assert isinstance(%s, %s)' % (args[level], - clsname)) - return result - - -def commonbase(classlist): - def baseclasses(cls): - result = set([cls]) - for base in cls.__bases__: - if '_mixin_' not in base.__dict__: - result |= baseclasses(base) - return result - - bag = baseclasses(classlist[0]) - for cls in classlist[1:]: - bag &= baseclasses(cls) - _, candidate = max([(len(cls.__mro__), cls) for cls in bag]) - for cls in bag: - assert issubclass(candidate, cls) - return candidate - - -class InstallerVersion2(object): - """NOT_RPYTHON""" - - instance_counter = 0 - mrdtables = {} - - def __init__(self, multimethod, prefix, list_of_typeorders, - baked_perform_call=True, base_typeorder=None): - #print 'InstallerVersion2:', prefix - self.__class__.instance_counter += 1 - self.multimethod = multimethod - self.prefix = prefix - self.list_of_typeorders = list_of_typeorders - self.baked_perform_call = baked_perform_call - self.mmfunccache = {} - args = ['arg%d' % i for i in range(multimethod.arity)] - self.fnargs = (multimethod.argnames_before + args + - multimethod.argnames_after) - - # compute the complete table - base_typeorder = base_typeorder or list_of_typeorders[0] - for typeorder in list_of_typeorders: - for t1 in typeorder: - assert t1 in base_typeorder - - lst = list(base_typeorder) - def clskey(cls): - return cls.__mro__[::-1] - lst.sort(lambda cls1, cls2: cmp(clskey(cls1), clskey(cls2))) - key = tuple(lst) - try: - self.mrdtable = self.mrdtables[key] - except KeyError: - self.mrdtable = self.mrdtables[key] = MRDTable(key) - - dispatcher = MMDispatcher(multimethod, list_of_typeorders) - self.table = {} - def buildtable(prefixtypes): - if len(prefixtypes) == multimethod.arity: - calllist = dispatcher.expressions(prefixtypes, - multimethod.argnames_before, - args, - multimethod.argnames_after) - if calllist: - self.table[prefixtypes] = calllist - elif dispatcher.anychance(prefixtypes): - typeorder = list_of_typeorders[len(prefixtypes)] - for t1 in typeorder: - buildtable(prefixtypes + (t1,)) - buildtable(()) - self.dispatcher = dispatcher - - def is_empty(self): - return len(self.table) == 0 - - def install(self): - nskip = len(self.multimethod.argnames_before) - null_entry = self.build_funcentry([self.prefix, '0_fail'], []) - null_entry.no_typecheck() - if self.is_empty(): - return self.answer(null_entry) - - entryarray = CompressedArray(null_entry) - indexarray = self.mrdtable.indexarray - lst = self.mrdtable.list_of_types - - def compress(typesprefix, typesnum): - if len(typesprefix) == self.multimethod.arity: - calllist = self.table.get(typesprefix, []) - funcname = [self.prefix] - funcname.extend(typesprefix) - entry = self.build_funcentry(funcname, calllist) - entry.register_valid_types(typesprefix) - return entry - elif self.dispatcher.anychance(typesprefix): - flatline = [] - for num1, t1 in enumerate(lst): - item = compress(typesprefix + (t1,), typesnum + (num1,)) - flatline.append(item) - if len(typesprefix) == self.multimethod.arity - 1: - array = entryarray - else: - array = indexarray - return array.insert_subarray(flatline) - else: - return 0 - - master_index = compress((), ()) - - null_func = null_entry.make_function(self.fnargs, nskip, self.mrdtable) - funcarray = CompressedArray(null_func) - # round up the length to a power of 2 - N = 1 - while N < len(entryarray.items): - N *= 2 - funcarray.ensure_length(N) - for i, entry in enumerate(entryarray.items): - func = entry.make_function(self.fnargs, nskip, self.mrdtable) - funcarray.items[i] = func - self.mrdtable.normalize_length(funcarray) - - #print master_index - #print indexarray.items - #print funcarray.items _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit