summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/decimal.rst14
-rw-r--r--Doc/library/dis.rst10
-rw-r--r--Doc/library/exceptions.rst16
-rw-r--r--Doc/library/os.rst61
-rw-r--r--Doc/library/posix.rst29
-rw-r--r--Doc/library/xmlrpclib.rst12
-rw-r--r--Include/object.h8
-rw-r--r--Lib/ctypes/test/test_funcptr.py6
-rw-r--r--Lib/decimal.py17
-rw-r--r--Lib/test/test_decimal.py8
-rw-r--r--Lib/urlparse.py58
-rw-r--r--Modules/_ctypes/_ctypes.c7
-rw-r--r--Objects/object.c4
-rw-r--r--Objects/typeobject.c210
14 files changed, 318 insertions, 142 deletions
diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst
index fbd6f43..4f8c127 100644
--- a/Doc/library/decimal.rst
+++ b/Doc/library/decimal.rst
@@ -276,9 +276,10 @@ Decimal objects
Construct a new :class:`Decimal` object based from *value*.
- *value* can be an integer, string, tuple, or another :class:`Decimal` object. If
- no *value* is given, returns ``Decimal("0")``. If *value* is a string, it
- should conform to the decimal numeric string syntax::
+ *value* can be an integer, string, tuple, or another :class:`Decimal`
+ object. If no *value* is given, returns ``Decimal("0")``. If *value* is a
+ string, it should conform to the decimal numeric string syntax after leading
+ and trailing whitespace characters are removed::
sign ::= '+' | '-'
digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
@@ -308,6 +309,10 @@ Decimal objects
Once constructed, :class:`Decimal` objects are immutable.
+ .. versionchanged:: 2.6
+ leading and trailing whitespace characters are permitted when
+ creating a Decimal instance from a string.
+
Decimal floating point objects share many properties with the other built-in
numeric types such as :class:`float` and :class:`int`. All of the usual math
operations and special methods apply. Likewise, decimal objects can be copied,
@@ -925,6 +930,9 @@ method. For example, ``C.exp(x)`` is equivalent to
>>> Decimal("3.4445") + Decimal(0) + Decimal("1.0023")
Decimal("4.44")
+ This method implements the to-number operation of the IBM
+ specification. If the argument is a string, no leading or trailing
+ whitespace is permitted.
.. method:: Context.Etiny()
diff --git a/Doc/library/dis.rst b/Doc/library/dis.rst
index 41cbe7f..78a7d5b 100644
--- a/Doc/library/dis.rst
+++ b/Doc/library/dis.rst
@@ -506,10 +506,10 @@ the more significant byte last.
Works as ``BUILD_TUPLE``, but creates a set.
-.. opcode:: BUILD_MAP (zero)
+.. opcode:: BUILD_MAP (count)
- Pushes a new empty dictionary object onto the stack. The argument is ignored
- and set to zero by the compiler.
+ Pushes a new dictionary object onto the stack. The dictionary is pre-sized
+ to hold *count* entries.
.. opcode:: LOAD_ATTR (namei)
@@ -589,6 +589,10 @@ the more significant byte last.
Pushes a try block from a try-except clause onto the block stack. *delta* points
to the finally block.
+.. opcode:: STORE_MAP ()
+
+ Store a key and value pair in a dictionary. Pops the key and value while leaving
+ the dictionary on the stack.
.. opcode:: LOAD_FAST (var_num)
diff --git a/Doc/library/exceptions.rst b/Doc/library/exceptions.rst
index e7721fa..9c2e3a6 100644
--- a/Doc/library/exceptions.rst
+++ b/Doc/library/exceptions.rst
@@ -207,9 +207,19 @@ The following exceptions are the exceptions that are actually raised.
.. exception:: OSError
- This class is derived from :exc:`EnvironmentError` and is used primarily as the
- :mod:`os` module's ``os.error`` exception. See :exc:`EnvironmentError` above for
- a description of the possible associated values.
+ .. index:: module: errno
+
+ This exception is derived from :exc:`EnvironmentError`. It is raised when a
+ function returns a system-related error (not for illegal argument types or
+ other incidental errors). The :attr:`errno` attribute is a numeric error
+ code from :cdata:`errno`, and the :attr:`strerror` attribute is the
+ corresponding string, as would be printed by the C function :cfunc:`perror`.
+ See the module :mod:`errno`, which contains names for the error codes defined
+ by the underlying operating system.
+
+ For exceptions that involve a file system path (such as :func:`chdir` or
+ :func:`unlink`), the exception instance will contain a third attribute,
+ :attr:`filename`, which is the file name passed to the function.
.. exception:: OverflowError
diff --git a/Doc/library/os.rst b/Doc/library/os.rst
index ee0cf48..c4f6e64 100644
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -1,4 +1,3 @@
-
:mod:`os` --- Miscellaneous operating system interfaces
=======================================================
@@ -6,53 +5,32 @@
:synopsis: Miscellaneous operating system interfaces.
-This module provides a more portable way of using operating system dependent
-functionality than importing an operating system dependent built-in module like
-:mod:`posix` or :mod:`nt`. If you just want to read or write a file see
-:func:`open`, if you want to manipulate paths, see the :mod:`os.path`
-module, and if you want to read all the lines in all the files on the
-command line see the :mod:`fileinput` module. For creating temporary
-files and directories see the :mod:`tempfile` module, and for high-level
-file and directory handling see the :mod:`shutil` module.
-
-This module searches for an operating system dependent built-in module like
-:mod:`mac` or :mod:`posix` and exports the same functions and data as found
-there. The design of all built-in operating system dependent modules of Python
-is such that as long as the same functionality is available, it uses the same
-interface; for example, the function ``os.stat(path)`` returns stat information
-about *path* in the same format (which happens to have originated with the POSIX
+This module provides a portable way of using operating system dependent
+functionality. If you just want to read or write a file see :func:`open`, if
+you want to manipulate paths, see the :mod:`os.path` module, and if you want to
+read all the lines in all the files on the command line see the :mod:`fileinput`
+module. For creating temporary files and directories see the :mod:`tempfile`
+module, and for high-level file and directory handling see the :mod:`shutil`
+module.
+
+The design of all built-in operating system dependent modules of Python is such
+that as long as the same functionality is available, it uses the same interface;
+for example, the function ``os.stat(path)`` returns stat information about
+*path* in the same format (which happens to have originated with the POSIX
interface).
Extensions peculiar to a particular operating system are also available through
the :mod:`os` module, but using them is of course a threat to portability!
-Note that after the first time :mod:`os` is imported, there is *no* performance
-penalty in using functions from :mod:`os` instead of directly from the operating
-system dependent built-in module, so there should be *no* reason not to use
-:mod:`os`!
+.. note::
-The :mod:`os` module contains many functions and data values. The items below
-and in the following sub-sections are all available directly from the :mod:`os`
-module.
+ All functions in this module raise :exc:`OSError` in the case of invalid or
+ inaccessible file names and paths, or other arguments that have the correct
+ type, but are not accepted by the operating system.
.. exception:: error
- .. index:: module: errno
-
- This exception is raised when a function returns a system-related error (not for
- illegal argument types or other incidental errors). This is also known as the
- built-in exception :exc:`OSError`. The accompanying value is a pair containing
- the numeric error code from :cdata:`errno` and the corresponding string, as
- would be printed by the C function :cfunc:`perror`. See the module
- :mod:`errno`, which contains names for the error codes defined by the underlying
- operating system.
-
- When exceptions are classes, this exception carries two attributes,
- :attr:`errno` and :attr:`strerror`. The first holds the value of the C
- :cdata:`errno` variable, and the latter holds the corresponding error message
- from :cfunc:`strerror`. For exceptions that involve a file system path (such as
- :func:`chdir` or :func:`unlink`), the exception instance will contain a third
- attribute, :attr:`filename`, which is the file name passed to the function.
+ An alias for the built-in :exc:`OSError` exception.
.. data:: name
@@ -645,7 +623,6 @@ platforms. For descriptions of their availability and use, consult
Files and Directories
---------------------
-
.. function:: access(path, mode)
Use the real uid/gid to test for access to *path*. Note that most operations
@@ -1760,8 +1737,8 @@ Miscellaneous System Information
.. function:: getloadavg()
- Return the number of processes in the system run queue averaged over the last 1,
- 5, and 15 minutes or raises :exc:`OSError` if the load average was
+ Return the number of processes in the system run queue averaged over the last
+ 1, 5, and 15 minutes or raises :exc:`OSError` if the load average was
unobtainable.
diff --git a/Doc/library/posix.rst b/Doc/library/posix.rst
index a845e35..c33d9e5 100644
--- a/Doc/library/posix.rst
+++ b/Doc/library/posix.rst
@@ -1,4 +1,3 @@
-
:mod:`posix` --- The most common POSIX system calls
===================================================
@@ -22,13 +21,8 @@ available through the :mod:`os` interface. Once :mod:`os` is imported, there is
:mod:`os` provides some additional functionality, such as automatically calling
:func:`putenv` when an entry in ``os.environ`` is changed.
-The descriptions below are very terse; refer to the corresponding Unix manual
-(or POSIX documentation) entry for more information. Arguments called *path*
-refer to a pathname given as a string.
-
Errors are reported as exceptions; the usual exceptions are given for type
-errors, while errors reported by the system calls raise :exc:`error` (a synonym
-for the standard exception :exc:`OSError`), described below.
+errors, while errors reported by the system calls raise :exc:`OSError`.
.. _posix-large-files:
@@ -42,9 +36,8 @@ Large File Support
.. sectionauthor:: Steve Clift <clift@mail.anacapa.net>
-
-Several operating systems (including AIX, HPUX, Irix and Solaris) provide
-support for files that are larger than 2 Gb from a C programming model where
+Several operating systems (including AIX, HP-UX, Irix and Solaris) provide
+support for files that are larger than 2 GB from a C programming model where
:ctype:`int` and :ctype:`long` are 32-bit values. This is typically accomplished
by defining the relevant size and offset types as 64-bit values. Such files are
sometimes referred to as :dfn:`large files`.
@@ -67,16 +60,16 @@ On large-file-capable Linux systems, this might work::
.. _posix-contents:
-Module Contents
----------------
-
-Module :mod:`posix` defines the following data item:
+Notable Module Contents
+-----------------------
+In addition to many functions described in the :mod:`os` module documentation,
+:mod:`posix` defines the following data item:
.. data:: environ
- A dictionary representing the string environment at the time the interpreter was
- started. For example, ``environ['HOME']`` is the pathname of your home
+ A dictionary representing the string environment at the time the interpreter
+ was started. For example, ``environ['HOME']`` is the pathname of your home
directory, equivalent to ``getenv("HOME")`` in C.
Modifying this dictionary does not affect the string environment passed on by
@@ -90,7 +83,3 @@ Module :mod:`posix` defines the following data item:
updates the environment on modification. Note also that updating ``os.environ``
will render this dictionary obsolete. Use of the :mod:`os` module version of
this is recommended over direct access to the :mod:`posix` module.
-
-Additional contents of this module should only be accessed via the :mod:`os`
-module; refer to the documentation for that module for further information.
-
diff --git a/Doc/library/xmlrpclib.rst b/Doc/library/xmlrpclib.rst
index 176c929..82ce1da 100644
--- a/Doc/library/xmlrpclib.rst
+++ b/Doc/library/xmlrpclib.rst
@@ -110,12 +110,11 @@ between conformable Python objects and XML on the wire.
.. seealso::
`XML-RPC HOWTO <http://www.tldp.org/HOWTO/XML-RPC-HOWTO/index.html>`_
- A good description of XML operation and client software in several languages.
+ A good description of XML-RPC operation and client software in several languages.
Contains pretty much everything an XML-RPC client developer needs to know.
- `XML-RPC Hacks page <http://xmlrpc-c.sourceforge.net/hacks.php>`_
- Extensions for various open-source libraries to support introspection and
- multicall.
+ `XML-RPC Introspection <http://xmlrpc-c.sourceforge.net/introspection.html>`_
+ Describes the XML-RPC protocol extension for introspection.
.. _serverproxy-objects:
@@ -167,11 +166,6 @@ grouped under the reserved :attr:`system` member:
no such string is available, an empty string is returned. The documentation
string may contain HTML markup.
-Introspection methods are currently supported by servers written in PHP, C and
-Microsoft .NET. Partial introspection support is included in recent updates to
-UserLand Frontier. Introspection support for Perl, Python and Java is available
-at the `XML-RPC Hacks <http://xmlrpc-c.sourceforge.net/hacks.php>`_ page.
-
.. _boolean-objects:
diff --git a/Include/object.h b/Include/object.h
index d008c50..90a3354 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -374,6 +374,9 @@ typedef struct _typeobject {
PyObject *tp_weaklist;
destructor tp_del;
+ /* Type attribute cache version tag. Added in version 2.6 */
+ unsigned int tp_version_tag;
+
#ifdef COUNT_ALLOCS
/* these must be last and never explicitly initialized */
Py_ssize_t tp_allocs;
@@ -525,6 +528,10 @@ given type object has a specified feature.
#define Py_TPFLAGS_HAVE_STACKLESS_EXTENSION 0
#endif
+/* Objects support type attribute cache */
+#define Py_TPFLAGS_HAVE_VERSION_TAG (1L<<18)
+#define Py_TPFLAGS_VALID_VERSION_TAG (1L<<19)
+
/* These flags are used to determine if a type is a subclass. */
#define Py_TPFLAGS_INT_SUBCLASS (1L<<23)
#define Py_TPFLAGS_LONG_SUBCLASS (1L<<24)
@@ -538,6 +545,7 @@ given type object has a specified feature.
#define Py_TPFLAGS_DEFAULT ( \
Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | \
+ Py_TPFLAGS_HAVE_VERSION_TAG | \
0)
#define PyType_HasFeature(t,f) (((t)->tp_flags & (f)) != 0)
diff --git a/Lib/ctypes/test/test_funcptr.py b/Lib/ctypes/test/test_funcptr.py
index 56099cc..7ddf319 100644
--- a/Lib/ctypes/test/test_funcptr.py
+++ b/Lib/ctypes/test/test_funcptr.py
@@ -123,5 +123,11 @@ class CFuncPtrTestCase(unittest.TestCase):
self.failUnlessEqual(strtok(None, b"\n"), "c")
self.failUnlessEqual(strtok(None, b"\n"), None)
+ def test_NULL_funcptr(self):
+ tp = CFUNCTYPE(c_int)
+ func = tp() # NULL function pointer
+ # raise a ValueError when we try to call it
+ self.assertRaises(ValueError, func)
+
if __name__ == '__main__':
unittest.main()
diff --git a/Lib/decimal.py b/Lib/decimal.py
index 434930a..5e1b16b 100644
--- a/Lib/decimal.py
+++ b/Lib/decimal.py
@@ -524,6 +524,8 @@ class Decimal(_numbers.Real, _numbers.Inexact):
Decimal("314")
>>> Decimal(Decimal(314)) # another decimal instance
Decimal("314")
+ >>> Decimal(' 3.14 \\n') # leading and trailing whitespace okay
+ Decimal("3.14")
"""
# Note that the coefficient, self._int, is actually stored as
@@ -539,7 +541,7 @@ class Decimal(_numbers.Real, _numbers.Inexact):
# From a string
# REs insist on real strings, so we can too.
if isinstance(value, str):
- m = _parser(value)
+ m = _parser(value.strip())
if m is None:
if context is None:
context = getcontext()
@@ -3542,7 +3544,16 @@ class Context(object):
return rounding
def create_decimal(self, num='0'):
- """Creates a new Decimal instance but using self as context."""
+ """Creates a new Decimal instance but using self as context.
+
+ This method implements the to-number operation of the
+ IBM Decimal specification."""
+
+ if isinstance(num, str) and num != num.strip():
+ return self._raise_error(ConversionSyntax,
+ "no trailing or leading whitespace is "
+ "permitted.")
+
d = Decimal(num, context=self)
if d._isnan() and len(d._int) > self.prec - self._clamp:
return self._raise_error(ConversionSyntax,
@@ -5157,7 +5168,7 @@ _parser = re.compile(r""" # A numeric string consists of:
(?P<diag>\d*) # with (possibly empty) diagnostic information.
)
# \s*
- $
+ \Z
""", re.VERBOSE | re.IGNORECASE).match
_all_zeros = re.compile('0*$').match
diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py
index 0dd7f00..83fb337 100644
--- a/Lib/test/test_decimal.py
+++ b/Lib/test/test_decimal.py
@@ -429,6 +429,10 @@ class DecimalExplicitConstructionTest(unittest.TestCase):
#just not a number
self.assertEqual(str(Decimal('ugly')), 'NaN')
+ #leading and trailing whitespace permitted
+ self.assertEqual(str(Decimal('1.3E4 \n')), '1.3E+4')
+ self.assertEqual(str(Decimal(' -7.89')), '-7.89')
+
def test_explicit_from_tuples(self):
#zero
@@ -517,6 +521,10 @@ class DecimalExplicitConstructionTest(unittest.TestCase):
self.assertEqual(str(d), '456789')
d = nc.create_decimal('456789')
self.assertEqual(str(d), '4.57E+5')
+ # leading and trailing whitespace should result in a NaN;
+ # spaces are already checked in Cowlishaw's test-suite, so
+ # here we just check that a trailing newline results in a NaN
+ self.assertEqual(str(nc.create_decimal('3.14\n')), 'NaN')
# from tuples
d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
diff --git a/Lib/urlparse.py b/Lib/urlparse.py
index 1f435b9..0c05dfe 100644
--- a/Lib/urlparse.py
+++ b/Lib/urlparse.py
@@ -37,46 +37,11 @@ _parse_cache = {}
def clear_cache():
"""Clear the parse cache."""
- global _parse_cache
- _parse_cache = {}
+ _parse_cache.clear()
-class BaseResult(tuple):
- """Base class for the parsed result objects.
-
- This provides the attributes shared by the two derived result
- objects as read-only properties. The derived classes are
- responsible for checking the right number of arguments were
- supplied to the constructor.
-
- """
-
- __slots__ = ()
-
- # Attributes that access the basic components of the URL:
-
- @property
- def scheme(self):
- return self[0]
-
- @property
- def netloc(self):
- return self[1]
-
- @property
- def path(self):
- return self[2]
-
- @property
- def query(self):
- return self[-2]
-
- @property
- def fragment(self):
- return self[-1]
-
- # Additional attributes that provide access to parsed-out portions
- # of the netloc:
+class ResultMixin(object):
+ """Shared methods for the parsed result objects."""
@property
def username(self):
@@ -116,31 +81,20 @@ class BaseResult(tuple):
return int(port, 10)
return None
+from collections import namedtuple
-class SplitResult(BaseResult):
+class SplitResult(namedtuple('SplitResult', 'scheme netloc path query fragment'), ResultMixin):
__slots__ = ()
- def __new__(cls, scheme, netloc, path, query, fragment):
- return BaseResult.__new__(
- cls, (scheme, netloc, path, query, fragment))
-
def geturl(self):
return urlunsplit(self)
-class ParseResult(BaseResult):
+class ParseResult(namedtuple('ParseResult', 'scheme netloc path params query fragment'), ResultMixin):
__slots__ = ()
- def __new__(cls, scheme, netloc, path, params, query, fragment):
- return BaseResult.__new__(
- cls, (scheme, netloc, path, params, query, fragment))
-
- @property
- def params(self):
- return self[3]
-
def geturl(self):
return urlunparse(self)
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index 5da7de0..c5e3be4 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -2882,7 +2882,7 @@ CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
&& (PyLong_Check(PyTuple_GET_ITEM(args, 0)))) {
CDataObject *ob;
void *ptr = PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, 0));
- if (ptr == NULL)
+ if (ptr == NULL && PyErr_Occurred())
return NULL;
ob = (CDataObject *)GenericCData_new(type, args, kwds);
if (ob == NULL)
@@ -3291,6 +3291,11 @@ CFuncPtr_call(CFuncPtrObject *self, PyObject *inargs, PyObject *kwds)
pProc = *(void **)self->b_ptr;
+ if (pProc == NULL) {
+ PyErr_SetString(PyExc_ValueError,
+ "attempt to call NULL function pointer");
+ return NULL;
+ }
#ifdef MS_WIN32
if (self->index) {
/* It's a COM method */
diff --git a/Objects/object.c b/Objects/object.c
index d60ccc0..587e806 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -947,6 +947,7 @@ PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
goto done;
}
+#if 0 /* XXX this is not quite _PyType_Lookup anymore */
/* Inline _PyType_Lookup */
{
Py_ssize_t i, n;
@@ -967,6 +968,9 @@ PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
break;
}
}
+#else
+ descr = _PyType_Lookup(tp, name);
+#endif
Py_XINCREF(descr);
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 682c029..3342cb4 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -6,6 +6,171 @@
#include <ctype.h>
+
+/* Support type attribute cache */
+
+/* The cache can keep references to the names alive for longer than
+ they normally would. This is why the maximum size is limited to
+ MCACHE_MAX_ATTR_SIZE, since it might be a problem if very large
+ strings are used as attribute names. */
+#define MCACHE_MAX_ATTR_SIZE 100
+#define MCACHE_SIZE_EXP 10
+#define MCACHE_HASH(version, name_hash) \
+ (((unsigned int)(version) * (unsigned int)(name_hash)) \
+ >> (8*sizeof(unsigned int) - MCACHE_SIZE_EXP))
+#define MCACHE_HASH_METHOD(type, name) \
+ MCACHE_HASH((type)->tp_version_tag, \
+ ((PyStringObject *)(name))->ob_shash)
+#define MCACHE_CACHEABLE_NAME(name) \
+ PyString_CheckExact(name) && \
+ PyString_GET_SIZE(name) <= MCACHE_MAX_ATTR_SIZE
+
+struct method_cache_entry {
+ unsigned int version;
+ PyObject *name; /* reference to exactly a str or None */
+ PyObject *value; /* borrowed */
+};
+
+static struct method_cache_entry method_cache[1 << MCACHE_SIZE_EXP];
+static unsigned int next_version_tag = 0;
+
+static void
+type_modified(PyTypeObject *type)
+{
+ /* Invalidate any cached data for the specified type and all
+ subclasses. This function is called after the base
+ classes, mro, or attributes of the type are altered.
+
+ Invariants:
+
+ - Py_TPFLAGS_VALID_VERSION_TAG is never set if
+ Py_TPFLAGS_HAVE_VERSION_TAG is not set (e.g. on type
+ objects coming from non-recompiled extension modules)
+
+ - before Py_TPFLAGS_VALID_VERSION_TAG can be set on a type,
+ it must first be set on all super types.
+
+ This function clears the Py_TPFLAGS_VALID_VERSION_TAG of a
+ type (so it must first clear it on all subclasses). The
+ tp_version_tag value is meaningless unless this flag is set.
+ We don't assign new version tags eagerly, but only as
+ needed.
+ */
+ PyObject *raw, *ref;
+ Py_ssize_t i, n;
+
+ if(!PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
+ return;
+
+ raw = type->tp_subclasses;
+ if (raw != NULL) {
+ n = PyList_GET_SIZE(raw);
+ for (i = 0; i < n; i++) {
+ ref = PyList_GET_ITEM(raw, i);
+ ref = PyWeakref_GET_OBJECT(ref);
+ if (ref != Py_None) {
+ type_modified((PyTypeObject *)ref);
+ }
+ }
+ }
+ type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
+}
+
+static void
+type_mro_modified(PyTypeObject *type, PyObject *bases) {
+ /*
+ Check that all base classes or elements of the mro of type are
+ able to be cached. This function is called after the base
+ classes or mro of the type are altered.
+
+ Unset HAVE_VERSION_TAG and VALID_VERSION_TAG if the type
+ inherits from an old-style class, either directly or if it
+ appears in the MRO of a new-style class. No support either for
+ custom MROs that include types that are not officially super
+ types.
+
+ Called from mro_internal, which will subsequently be called on
+ each subclass when their mro is recursively updated.
+ */
+ Py_ssize_t i, n;
+ int clear = 0;
+
+ if(!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
+ return;
+
+ n = PyTuple_GET_SIZE(bases);
+ for (i = 0; i < n; i++) {
+ PyObject *b = PyTuple_GET_ITEM(bases, i);
+ PyTypeObject *cls;
+
+ if (!PyType_Check(b) ) {
+ clear = 1;
+ break;
+ }
+
+ cls = (PyTypeObject *)b;
+
+ if (!PyType_HasFeature(cls, Py_TPFLAGS_HAVE_VERSION_TAG) ||
+ !PyType_IsSubtype(type, cls)) {
+ clear = 1;
+ break;
+ }
+ }
+
+ if (clear)
+ type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG|
+ Py_TPFLAGS_VALID_VERSION_TAG);
+}
+
+static int
+assign_version_tag(PyTypeObject *type)
+{
+ /* Ensure that the tp_version_tag is valid and set
+ Py_TPFLAGS_VALID_VERSION_TAG. To respect the invariant, this
+ must first be done on all super classes. Return 0 if this
+ cannot be done, 1 if Py_TPFLAGS_VALID_VERSION_TAG.
+ */
+ Py_ssize_t i, n;
+ PyObject *bases;
+
+ if (PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG))
+ return 1;
+ if (!PyType_HasFeature(type, Py_TPFLAGS_HAVE_VERSION_TAG))
+ return 0;
+ if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
+ return 0;
+
+ type->tp_version_tag = next_version_tag++;
+ /* for stress-testing: next_version_tag &= 0xFF; */
+
+ if (type->tp_version_tag == 0) {
+ /* wrap-around or just starting Python - clear the whole
+ cache by filling names with references to Py_None.
+ Values are also set to NULL for added protection, as they
+ are borrowed reference */
+ for (i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
+ method_cache[i].value = NULL;
+ Py_XDECREF(method_cache[i].name);
+ method_cache[i].name = Py_None;
+ Py_INCREF(Py_None);
+ }
+ /* mark all version tags as invalid */
+ type_modified(&PyBaseObject_Type);
+ return 1;
+ }
+ bases = type->tp_bases;
+ n = PyTuple_GET_SIZE(bases);
+ for (i = 0; i < n; i++) {
+ PyObject *b = PyTuple_GET_ITEM(bases, i);
+ assert(PyType_Check(b));
+ if (!assign_version_tag((PyTypeObject *)b))
+ return 0;
+ }
+ type->tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG;
+ return 1;
+}
+
+
static PyMemberDef type_members[] = {
{"__basicsize__", T_INT, offsetof(PyTypeObject,tp_basicsize),READONLY},
{"__itemsize__", T_INT, offsetof(PyTypeObject, tp_itemsize), READONLY},
@@ -130,6 +295,8 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
return -1;
}
+ type_modified(type);
+
return PyDict_SetItemString(type->tp_dict, "__module__", value);
}
@@ -1299,6 +1466,14 @@ mro_internal(PyTypeObject *type)
}
}
type->tp_mro = tuple;
+
+ type_mro_modified(type, type->tp_mro);
+ /* corner case: the old-style super class might have been hidden
+ from the custom MRO */
+ type_mro_modified(type, type->tp_bases);
+
+ type_modified(type);
+
return 0;
}
@@ -2026,6 +2201,16 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
{
Py_ssize_t i, n;
PyObject *mro, *res, *base, *dict;
+ unsigned int h;
+
+ if (MCACHE_CACHEABLE_NAME(name) &&
+ PyType_HasFeature(type,Py_TPFLAGS_VALID_VERSION_TAG)) {
+ /* fast path */
+ h = MCACHE_HASH_METHOD(type, name);
+ if (method_cache[h].version == type->tp_version_tag &&
+ method_cache[h].name == name)
+ return method_cache[h].value;
+ }
/* Look in tp_dict of types in MRO */
mro = type->tp_mro;
@@ -2036,6 +2221,7 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
if (mro == NULL)
return NULL;
+ res = NULL;
assert(PyTuple_Check(mro));
n = PyTuple_GET_SIZE(mro);
for (i = 0; i < n; i++) {
@@ -2045,9 +2231,18 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
assert(dict && PyDict_Check(dict));
res = PyDict_GetItem(dict, name);
if (res != NULL)
- return res;
+ break;
}
- return NULL;
+
+ if (MCACHE_CACHEABLE_NAME(name) && assign_version_tag(type)) {
+ h = MCACHE_HASH_METHOD(type, name);
+ method_cache[h].version = type->tp_version_tag;
+ method_cache[h].value = res; /* borrowed */
+ Py_INCREF(name);
+ Py_DECREF(method_cache[h].name);
+ method_cache[h].name = name;
+ }
+ return res;
}
/* This is similar to PyObject_GenericGetAttr(),
@@ -2137,10 +2332,6 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
type->tp_name);
return -1;
}
- /* XXX Example of how I expect this to be used...
- if (update_subclasses(type, name, invalidate_cache, NULL) < 0)
- return -1;
- */
if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
return -1;
return update_slot(type, name);
@@ -5421,6 +5612,13 @@ update_slot(PyTypeObject *type, PyObject *name)
slotdef **pp;
int offset;
+ /* Clear the VALID_VERSION flag of 'type' and all its
+ subclasses. This could possibly be unified with the
+ update_subclasses() recursion below, but carefully:
+ they each have their own conditions on which to stop
+ recursing into subclasses. */
+ type_modified(type);
+
init_slotdefs();
pp = ptrs;
for (p = slotdefs; p->name; p++) {