]> granicus.if.org Git - python/commitdiff
Merged revisions 59921-59932 via svnmerge from
authorChristian Heimes <christian@cheimes.de>
Sat, 12 Jan 2008 19:39:10 +0000 (19:39 +0000)
committerChristian Heimes <christian@cheimes.de>
Sat, 12 Jan 2008 19:39:10 +0000 (19:39 +0000)
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.
........

14 files changed:
Doc/library/decimal.rst
Doc/library/dis.rst
Doc/library/exceptions.rst
Doc/library/os.rst
Doc/library/posix.rst
Doc/library/xmlrpclib.rst
Include/object.h
Lib/ctypes/test/test_funcptr.py
Lib/decimal.py
Lib/test/test_decimal.py
Lib/urlparse.py
Modules/_ctypes/_ctypes.c
Objects/object.c
Objects/typeobject.c

index fbd6f4349cfd1bf3357e4dc0ee1770e3e2762deb..4f8c12753d1c039efb850e3378fec4a45474ea3c 100644 (file)
@@ -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()
 
index 41cbe7f2a29e4a6addca3ba2ce6a71c38d2c4861..78a7d5b2781ee5fbdea435941af50ef6d583c839 100644 (file)
@@ -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)
 
index e7721fa208ef63a01885bdd6cae2ca37dda5439c..9c2e3a69f25236a749aa947a4eac68db03e03a10 100644 (file)
@@ -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
index ee0cf48bc600c7abef76c057d6dcf5bdb4d668ec..c4f6e646d69529c8499a97c840269936fd1bcef5 100644 (file)
@@ -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.
 
 
index a845e353e99efd1f7830581c7b3ca16ed2ee6dfc..c33d9e59adacac0ac2d44317e12376ad472adf87 100644 (file)
@@ -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.
-
index 176c9290ac6eefbd75b963399fb4fd158187931b..82ce1da7370c07e456216b5de47fe6854cd2d80d 100644 (file)
@@ -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:
 
index d008c508a850bceba31ea6b1acc465021f760e80..90a3354f25a41bcf5bff50e4f129174875a12b76 100644 (file)
@@ -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)
index 56099cc89a9dc037a77ac0f98665783b7bddda89..7ddf3199108882d09b470495828a38614d0dc003 100644 (file)
@@ -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()
index 434930ad32ea70d0868f554e748eeb45183f93b5..5e1b16bbf0b1fcc8d82a03793ba0c320eab0f9b5 100644 (file)
@@ -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
index 0dd7f003252092304eeacab5f7f7d653435d6db4..83fb337268f9a157b0f8f8b8c243ba25a023913d 100644 (file)
@@ -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) )
index 1f435b99c34bb623247bc93bb01012ba44eff7cf..0c05dfe42b84639f71497a5779341266f27e3374 100644 (file)
@@ -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)
 
index 5da7de0e7bcfb1d307c00965cd66a6cbfd502c37..c5e3be41919b0c0cb9eba643a49ef47c84a13f18 100644 (file)
@@ -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 */
index d60ccc02c9a99a9c3f7d41b917a3ecd55027dc35..587e8063377bbedf1ebaa476e9bd132384f41de8 100644 (file)
@@ -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);
 
index 682c029126a6ecf4ee1f0332e2dddf89422e76ed..3342cb43886622a450686f7809a229a9538324c5 100644 (file)
@@ -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++) {