xoutil.eight
– Extensions for writing code that runs on Python 2 and 3¶
Xoutil extensions for writing code that runs on Python 2 and 3
The name comes from (Manu’s idea’) “2 raised to the power of 3”.
There is an existing library written by “Benjamin Peterson” named six, both (xoutil.eight and six) can be used together since this module don’t claim to be a replacement of six, just some extra extensions. Nevertheless, there are some simple definitions that even when are in six also are defined also here.
This package also fixes some issues from PyPy interpreter.
-
xoutil.eight.
intern
(string) → string[source]¶ ``Intern’’ the given string. This enters the string in the (global) table of interned strings whose purpose is to speed up dictionary lookups. Return the string itself or the previously interned string object with the same value.
Tools¶
Implements the metaclass() function using the Py3k syntax.
-
xoutil.eight.meta.
metaclass
(meta, **kwargs)[source]¶ Define the metaclass of a class.
New in version 1.7.0: It’s available as
xoutil.objects.metaclass()
since 1.4.1. That alias is now deprecated and will be removed in 1.8.This function allows to define the metaclass of a class equally in Python 2 and 3.
Usage:
>>> class Meta(type): ... pass >>> class Foobar(metaclass(Meta)): ... pass >>> class Spam(metaclass(Meta), dict): ... pass >>> type(Spam) is Meta True >>> Spam.__bases__ == (dict, ) True
New in version 1.5.5: The kwargs keywords arguments with support for
__prepare__
.Metaclasses are allowed to have a
__prepare__
classmethod to return the namespace into which the body of the class should be evaluated. See PEP 3115.Warning
The PEP 3115 is not possible to implement in Python 2.7.
Despite our best efforts to have a truly compatible way of creating meta classes in both Python 2.7 and 3.0+, there is an inescapable difference in Python 2.7. The PEP 3115 states that
__prepare__
should be called before evaluating the body of the class. This is not possible in Python 2.7, since__new__
already receives the attributes collected in the body of the class. So it’s always too late to call__prepare__
at this point and the Python 2.7 interpreter does not call it.Our approach for Python 2.7 is calling it inside the
__new__
of a “side” metaclass that is used for the base class returned. This means that__prepare__
is called only for classes that use themetaclass()
directly. In the following hierarchy:class Meta(type): @classmethod def __prepare__(cls, name, bases, **kwargs): from xoutil.collections import OrderedDict return OrderedDict() class Foo(metaclass(Meta)): pass class Bar(Foo): pass
when creating the class
Bar
the__prepare__()
class method is not called in Python 2.7!See also
Warning
You should always place your metaclass declaration first in the list of bases. Doing otherwise triggers twice the metaclass’ constructors in Python 3.1 or less.
If your metaclass has some non-idempotent side-effect (such as registration of classes), then this would lead to unwanted double registration of the class:
>>> class BaseMeta(type): ... classes = [] ... def __new__(cls, name, bases, attrs): ... res = super(BaseMeta, cls).__new__(cls, name, bases, attrs) ... cls.classes.append(res) # <-- side effect ... return res >>> class Base(metaclass(BaseMeta)): ... pass >>> class SubType(BaseMeta): ... pass >>> class Egg(metaclass(SubType), Base): # <-- metaclass first ... pass >>> Egg.__base__ is Base # <-- but the base is Base True >>> len(BaseMeta.classes) == 2 True >>> class Spam(Base, metaclass(SubType)): ... 'Like "Egg" but it will be registered twice in Python 2.x.'
In this case the registration of Spam ocurred twice:
>>> BaseMeta.classes [<class Base>, <class Egg>, <class Spam>, <class Spam>]
Bases, however, are just fine:
>>> Spam.__bases__ == (Base, ) True
New in version 1.7.1: Now are accepted atypical meta-classes, for example functions or any callable with the same arguments as those that type accepts (class name, tuple of base classes, attributes mapping).
All functions implemented in module types
in Python 3 but not in
Python 2.
-
xoutil.eight.types.
MemberDescriptorType
¶ alias of
member_descriptor
-
class
xoutil.eight.types.
SimpleNamespace
(**kwargs)[source]¶ A simple attribute-based namespace.
SimpleNamespace(**kwargs)
-
class
xoutil.eight.types.
DynamicClassAttribute
(fget=None, fset=None, fdel=None, doc=None)[source]¶ Route attribute access on a class to
__getattr__()
.This is a descriptor, used to define attributes that act differently when accessed through an instance and through a class. Instance access remains normal, but access to an attribute through a class will be routed to the class’s
__getattr__()
method; this is done by raising AttributeError.This allows one to have properties active on an instance, and have virtual attributes on the class with the same name (see
Enum
for an example).New in version 1.5.5.
Note
The class Enum mentioned has not yet been backported.
Note
In Python 3.4+ this is an alias to
types.DynamicClassAttribute
.
-
xoutil.eight.types.
new_class
(name, bases=(), kwds=None, exec_body=None)[source]¶ Create a class object dynamically using the appropriate metaclass.
-
xoutil.eight.types.
prepare_class
(name, bases=(), kwds=None)[source]¶ Call the __prepare__ method of the appropriate metaclass.
Returns (metaclass, namespace, kwds) as a 3-tuple
metaclass is the appropriate metaclass namespace is the prepared class namespace kwds is an updated copy of the passed in kwds argument with any ‘metaclass’ entry removed. If no kwds argument is passed in, this will be an empty dict.
Extensions to Python’s io
module.
You may use it as drop-in replacement of io
. Although we don’t document
all items here. Refer to io
documentation.
In Python 2, buil-int open()
is different from io.open()
; in
Python 3 are the same function.
So, generated files with the built-in funtion in Python 2, can not be processed using abc types, for example:
f = open('test.rst')
assert isinstance(f, io.IOBase)
will fail in Python 2 and not in Python 3.
Another incompatibilities:
- file type doesn’t exists in Python 3.
- Python 2 instances created with io.StringIO:class`, or with
io.open()
using text mode, don’t accept str values, so it will be better to use any of the standards classes (StringIO.StringIO
,cStringIO.StringIO
oropen()
built-in).
New in version 1.7.0.
Compatibility exceptions between Python 2 and 3.
Python 2 defines an module named exceptions but Python 3 doesn’t. Also, there are some exception classes defined in Python 2 but not in Python 3.
-
xoutil.eight.exceptions.
reraise
(tp, value, tb=None)[source]¶ Raise an exception with a traceback.
In Python 2.7 this is the same as:
raise T, V, tb
In Python 3+ this is:
raise T(V).with_traceback(tb)
“”“Abstract Base Classes (ABCs) according to PEP 3119.”“”
Compatibility module between Python 2 and 3.
This module defines one symbol that is defined in Python 3 as a class:
- class ABC(metaclass=ABCMeta):
- “”“Helper class that provides a standard way to create an ABC using inheritance. “”” pass
In our case it’s defined as ABC = metaclass(ABCMeta)
, that is a little
tricky (see xoutil.eight.meta.metaclass:func`).
abstractclassmethod is deprecated. Use classmethod with abstractmethod instead.
abstractstaticmethod is deprecated. Use staticmethod with abstractmethod instead.