From a62da1daafc63075c08088f45750152a4974e7b8 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 12 Jan 2008 19:39:10 +0000 Subject: [PATCH] Merged revisions 59921-59932 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r59923 | raymond.hettinger | 2008-01-11 19:04:55 +0100 (Fri, 11 Jan 2008) | 1 line Speed-up and simplify code urlparse's result objects. ........ r59924 | andrew.kuchling | 2008-01-11 20:33:24 +0100 (Fri, 11 Jan 2008) | 1 line Bug #1790: update link; remove outdated paragraph ........ r59925 | thomas.heller | 2008-01-11 20:34:06 +0100 (Fri, 11 Jan 2008) | 5 lines Raise an error instead of crashing with a segfault when a NULL function pointer is called. Will backport to release25-maint. ........ r59927 | thomas.heller | 2008-01-11 21:29:19 +0100 (Fri, 11 Jan 2008) | 4 lines Fix a potential 'SystemError: NULL result without error'. NULL may be a valid return value from PyLong_AsVoidPtr. Will backport to release25-maint. ........ r59928 | raymond.hettinger | 2008-01-12 00:25:18 +0100 (Sat, 12 Jan 2008) | 1 line Update the opcode docs for STORE_MAP and BUILD_MAP ........ r59929 | mark.dickinson | 2008-01-12 02:56:00 +0100 (Sat, 12 Jan 2008) | 4 lines Issue 1780: Allow leading and trailing whitespace in Decimal constructor, when constructing from a string. Disallow trailing newlines in Context.create_decimal. ........ r59930 | georg.brandl | 2008-01-12 11:53:29 +0100 (Sat, 12 Jan 2008) | 3 lines Move OSError docs to exceptions doc, remove obsolete descriptions from os docs, rework posix docs. ........ r59931 | georg.brandl | 2008-01-12 14:47:57 +0100 (Sat, 12 Jan 2008) | 3 lines Patch #1700288: Method cache optimization, by Armin Rigo, ported to 2.6 by Kevin Jacobs. ........ r59932 | georg.brandl | 2008-01-12 17:11:09 +0100 (Sat, 12 Jan 2008) | 2 lines Fix editing glitch. ........ --- Doc/library/decimal.rst | 14 ++- Doc/library/dis.rst | 10 +- Doc/library/exceptions.rst | 16 ++- Doc/library/os.rst | 59 +++------ Doc/library/posix.rst | 29 ++--- Doc/library/xmlrpclib.rst | 12 +- Include/object.h | 8 ++ Lib/ctypes/test/test_funcptr.py | 6 + Lib/decimal.py | 17 ++- Lib/test/test_decimal.py | 8 ++ Lib/urlparse.py | 58 +-------- Modules/_ctypes/_ctypes.c | 7 +- Objects/object.c | 4 + Objects/typeobject.c | 210 +++++++++++++++++++++++++++++++- 14 files changed, 317 insertions(+), 141 deletions(-) diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index fbd6f4349cf..4f8c12753d1 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 41cbe7f2a29..78a7d5b2781 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 e7721fa208e..9c2e3a69f25 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 ee0cf48bc60..c4f6e646d69 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 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. -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 +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 a845e353e99..c33d9e59ada 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 - -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 176c9290ac6..82ce1da7370 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 `_ - 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 `_ - Extensions for various open-source libraries to support introspection and - multicall. + `XML-RPC Introspection `_ + 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 `_ page. - .. _boolean-objects: diff --git a/Include/object.h b/Include/object.h index d008c508a85..90a3354f25a 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 56099cc89a9..7ddf3199108 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 434930ad32e..5e1b16bbf0b 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\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 0dd7f003252..83fb337268f 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 1f435b99c34..0c05dfe42b8 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 5da7de0e7bc..c5e3be41919 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 d60ccc02c9a..587e8063377 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 682c029126a..3342cb43886 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -6,6 +6,171 @@ #include + +/* 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++) {