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.
........
This commit is contained in:
Christian Heimes 2008-01-12 19:39:10 +00:00
parent 25bb783c03
commit a62da1daaf
14 changed files with 317 additions and 141 deletions

View File

@ -276,9 +276,10 @@ Decimal objects
Construct a new :class:`Decimal` object based from *value*. Construct a new :class:`Decimal` object based from *value*.
*value* can be an integer, string, tuple, or another :class:`Decimal` object. If *value* can be an integer, string, tuple, or another :class:`Decimal`
no *value* is given, returns ``Decimal("0")``. If *value* is a string, it object. If no *value* is given, returns ``Decimal("0")``. If *value* is a
should conform to the decimal numeric string syntax:: string, it should conform to the decimal numeric string syntax after leading
and trailing whitespace characters are removed::
sign ::= '+' | '-' sign ::= '+' | '-'
digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
@ -308,6 +309,10 @@ Decimal objects
Once constructed, :class:`Decimal` objects are immutable. 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 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 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, 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("3.4445") + Decimal(0) + Decimal("1.0023")
Decimal("4.44") 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() .. method:: Context.Etiny()

View File

@ -506,10 +506,10 @@ the more significant byte last.
Works as ``BUILD_TUPLE``, but creates a set. 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 Pushes a new dictionary object onto the stack. The dictionary is pre-sized
and set to zero by the compiler. to hold *count* entries.
.. opcode:: LOAD_ATTR (namei) .. 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 Pushes a try block from a try-except clause onto the block stack. *delta* points
to the finally block. 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) .. opcode:: LOAD_FAST (var_num)

View File

@ -207,9 +207,19 @@ The following exceptions are the exceptions that are actually raised.
.. exception:: OSError .. exception:: OSError
This class is derived from :exc:`EnvironmentError` and is used primarily as the .. index:: module: errno
:mod:`os` module's ``os.error`` exception. See :exc:`EnvironmentError` above for
a description of the possible associated values. 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 .. exception:: OverflowError

View File

@ -1,4 +1,3 @@
:mod:`os` --- Miscellaneous operating system interfaces :mod:`os` --- Miscellaneous operating system interfaces
======================================================= =======================================================
@ -6,53 +5,32 @@
:synopsis: Miscellaneous operating system interfaces. :synopsis: Miscellaneous operating system interfaces.
This module provides a more portable way of using operating system dependent This module provides a portable way of using operating system dependent
functionality than importing an operating system dependent built-in module like functionality. If you just want to read or write a file see :func:`open`, if
:mod:`posix` or :mod:`nt`. If you just want to read or write a file see you want to manipulate paths, see the :mod:`os.path` module, and if you want to
:func:`open`, if you want to manipulate paths, see the :mod:`os.path` read all the lines in all the files on the command line see the :mod:`fileinput`
module, and if you want to read all the lines in all the files on the module. For creating temporary files and directories see the :mod:`tempfile`
command line see the :mod:`fileinput` module. For creating temporary module, and for high-level file and directory handling see the :mod:`shutil`
files and directories see the :mod:`tempfile` module, and for high-level module.
file and directory handling see the :mod:`shutil` module.
This module searches for an operating system dependent built-in module like The design of all built-in operating system dependent modules of Python is such
:mod:`mac` or :mod:`posix` and exports the same functions and data as found that as long as the same functionality is available, it uses the same interface;
there. The design of all built-in operating system dependent modules of Python for example, the function ``os.stat(path)`` returns stat information about
is such that as long as the same functionality is available, it uses the same *path* in the same format (which happens to have originated with the POSIX
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). interface).
Extensions peculiar to a particular operating system are also available through 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! 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 .. note::
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`!
The :mod:`os` module contains many functions and data values. The items below All functions in this module raise :exc:`OSError` in the case of invalid or
and in the following sub-sections are all available directly from the :mod:`os` inaccessible file names and paths, or other arguments that have the correct
module. type, but are not accepted by the operating system.
.. exception:: error .. exception:: error
.. index:: module: errno An alias for the built-in :exc:`OSError` exception.
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.
.. data:: name .. data:: name
@ -645,7 +623,6 @@ platforms. For descriptions of their availability and use, consult
Files and Directories Files and Directories
--------------------- ---------------------
.. function:: access(path, mode) .. function:: access(path, mode)
Use the real uid/gid to test for access to *path*. Note that most operations Use the real uid/gid to test for access to *path*. Note that most operations
@ -1760,8 +1737,8 @@ Miscellaneous System Information
.. function:: getloadavg() .. function:: getloadavg()
Return the number of processes in the system run queue averaged over the last 1, Return the number of processes in the system run queue averaged over the last
5, and 15 minutes or raises :exc:`OSError` if the load average was 1, 5, and 15 minutes or raises :exc:`OSError` if the load average was
unobtainable. unobtainable.

View File

@ -1,4 +1,3 @@
:mod:`posix` --- The most common POSIX system calls :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 :mod:`os` provides some additional functionality, such as automatically calling
:func:`putenv` when an entry in ``os.environ`` is changed. :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 are reported as exceptions; the usual exceptions are given for type
errors, while errors reported by the system calls raise :exc:`error` (a synonym errors, while errors reported by the system calls raise :exc:`OSError`.
for the standard exception :exc:`OSError`), described below.
.. _posix-large-files: .. _posix-large-files:
@ -42,9 +36,8 @@ Large File Support
.. sectionauthor:: Steve Clift <clift@mail.anacapa.net> .. sectionauthor:: Steve Clift <clift@mail.anacapa.net>
Several operating systems (including AIX, HP-UX, Irix and Solaris) provide
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
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 :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 by defining the relevant size and offset types as 64-bit values. Such files are
sometimes referred to as :dfn:`large files`. sometimes referred to as :dfn:`large files`.
@ -67,16 +60,16 @@ On large-file-capable Linux systems, this might work::
.. _posix-contents: .. _posix-contents:
Module Contents Notable Module Contents
--------------- -----------------------
Module :mod:`posix` defines the following data item:
In addition to many functions described in the :mod:`os` module documentation,
:mod:`posix` defines the following data item:
.. data:: environ .. data:: environ
A dictionary representing the string environment at the time the interpreter was A dictionary representing the string environment at the time the interpreter
started. For example, ``environ['HOME']`` is the pathname of your home was started. For example, ``environ['HOME']`` is the pathname of your home
directory, equivalent to ``getenv("HOME")`` in C. directory, equivalent to ``getenv("HOME")`` in C.
Modifying this dictionary does not affect the string environment passed on by 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`` updates the environment on modification. Note also that updating ``os.environ``
will render this dictionary obsolete. Use of the :mod:`os` module version of will render this dictionary obsolete. Use of the :mod:`os` module version of
this is recommended over direct access to the :mod:`posix` module. 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.

View File

@ -110,12 +110,11 @@ between conformable Python objects and XML on the wire.
.. seealso:: .. seealso::
`XML-RPC HOWTO <http://www.tldp.org/HOWTO/XML-RPC-HOWTO/index.html>`_ `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. Contains pretty much everything an XML-RPC client developer needs to know.
`XML-RPC Hacks page <http://xmlrpc-c.sourceforge.net/hacks.php>`_ `XML-RPC Introspection <http://xmlrpc-c.sourceforge.net/introspection.html>`_
Extensions for various open-source libraries to support introspection and Describes the XML-RPC protocol extension for introspection.
multicall.
.. _serverproxy-objects: .. _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 no such string is available, an empty string is returned. The documentation
string may contain HTML markup. 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: .. _boolean-objects:

View File

@ -374,6 +374,9 @@ typedef struct _typeobject {
PyObject *tp_weaklist; PyObject *tp_weaklist;
destructor tp_del; destructor tp_del;
/* Type attribute cache version tag. Added in version 2.6 */
unsigned int tp_version_tag;
#ifdef COUNT_ALLOCS #ifdef COUNT_ALLOCS
/* these must be last and never explicitly initialized */ /* these must be last and never explicitly initialized */
Py_ssize_t tp_allocs; Py_ssize_t tp_allocs;
@ -525,6 +528,10 @@ given type object has a specified feature.
#define Py_TPFLAGS_HAVE_STACKLESS_EXTENSION 0 #define Py_TPFLAGS_HAVE_STACKLESS_EXTENSION 0
#endif #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. */ /* These flags are used to determine if a type is a subclass. */
#define Py_TPFLAGS_INT_SUBCLASS (1L<<23) #define Py_TPFLAGS_INT_SUBCLASS (1L<<23)
#define Py_TPFLAGS_LONG_SUBCLASS (1L<<24) #define Py_TPFLAGS_LONG_SUBCLASS (1L<<24)
@ -538,6 +545,7 @@ given type object has a specified feature.
#define Py_TPFLAGS_DEFAULT ( \ #define Py_TPFLAGS_DEFAULT ( \
Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | \ Py_TPFLAGS_HAVE_STACKLESS_EXTENSION | \
Py_TPFLAGS_HAVE_VERSION_TAG | \
0) 0)
#define PyType_HasFeature(t,f) (((t)->tp_flags & (f)) != 0) #define PyType_HasFeature(t,f) (((t)->tp_flags & (f)) != 0)

View File

@ -123,5 +123,11 @@ class CFuncPtrTestCase(unittest.TestCase):
self.failUnlessEqual(strtok(None, b"\n"), "c") self.failUnlessEqual(strtok(None, b"\n"), "c")
self.failUnlessEqual(strtok(None, b"\n"), None) 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__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -524,6 +524,8 @@ class Decimal(_numbers.Real, _numbers.Inexact):
Decimal("314") Decimal("314")
>>> Decimal(Decimal(314)) # another decimal instance >>> Decimal(Decimal(314)) # another decimal instance
Decimal("314") Decimal("314")
>>> Decimal(' 3.14 \\n') # leading and trailing whitespace okay
Decimal("3.14")
""" """
# Note that the coefficient, self._int, is actually stored as # Note that the coefficient, self._int, is actually stored as
@ -539,7 +541,7 @@ class Decimal(_numbers.Real, _numbers.Inexact):
# From a string # From a string
# REs insist on real strings, so we can too. # REs insist on real strings, so we can too.
if isinstance(value, str): if isinstance(value, str):
m = _parser(value) m = _parser(value.strip())
if m is None: if m is None:
if context is None: if context is None:
context = getcontext() context = getcontext()
@ -3542,7 +3544,16 @@ class Context(object):
return rounding return rounding
def create_decimal(self, num='0'): 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) d = Decimal(num, context=self)
if d._isnan() and len(d._int) > self.prec - self._clamp: if d._isnan() and len(d._int) > self.prec - self._clamp:
return self._raise_error(ConversionSyntax, 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. (?P<diag>\d*) # with (possibly empty) diagnostic information.
) )
# \s* # \s*
$ \Z
""", re.VERBOSE | re.IGNORECASE).match """, re.VERBOSE | re.IGNORECASE).match
_all_zeros = re.compile('0*$').match _all_zeros = re.compile('0*$').match

View File

@ -429,6 +429,10 @@ class DecimalExplicitConstructionTest(unittest.TestCase):
#just not a number #just not a number
self.assertEqual(str(Decimal('ugly')), 'NaN') 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): def test_explicit_from_tuples(self):
#zero #zero
@ -517,6 +521,10 @@ class DecimalExplicitConstructionTest(unittest.TestCase):
self.assertEqual(str(d), '456789') self.assertEqual(str(d), '456789')
d = nc.create_decimal('456789') d = nc.create_decimal('456789')
self.assertEqual(str(d), '4.57E+5') 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 # from tuples
d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) ) d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )

View File

@ -37,46 +37,11 @@ _parse_cache = {}
def clear_cache(): def clear_cache():
"""Clear the parse cache.""" """Clear the parse cache."""
global _parse_cache _parse_cache.clear()
_parse_cache = {}
class BaseResult(tuple): class ResultMixin(object):
"""Base class for the parsed result objects. """Shared methods 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:
@property @property
def username(self): def username(self):
@ -116,31 +81,20 @@ class BaseResult(tuple):
return int(port, 10) return int(port, 10)
return None return None
from collections import namedtuple
class SplitResult(BaseResult): class SplitResult(namedtuple('SplitResult', 'scheme netloc path query fragment'), ResultMixin):
__slots__ = () __slots__ = ()
def __new__(cls, scheme, netloc, path, query, fragment):
return BaseResult.__new__(
cls, (scheme, netloc, path, query, fragment))
def geturl(self): def geturl(self):
return urlunsplit(self) return urlunsplit(self)
class ParseResult(BaseResult): class ParseResult(namedtuple('ParseResult', 'scheme netloc path params query fragment'), ResultMixin):
__slots__ = () __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): def geturl(self):
return urlunparse(self) return urlunparse(self)

View File

@ -2882,7 +2882,7 @@ CFuncPtr_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
&& (PyLong_Check(PyTuple_GET_ITEM(args, 0)))) { && (PyLong_Check(PyTuple_GET_ITEM(args, 0)))) {
CDataObject *ob; CDataObject *ob;
void *ptr = PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, 0)); void *ptr = PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, 0));
if (ptr == NULL) if (ptr == NULL && PyErr_Occurred())
return NULL; return NULL;
ob = (CDataObject *)GenericCData_new(type, args, kwds); ob = (CDataObject *)GenericCData_new(type, args, kwds);
if (ob == NULL) if (ob == NULL)
@ -3291,6 +3291,11 @@ CFuncPtr_call(CFuncPtrObject *self, PyObject *inargs, PyObject *kwds)
pProc = *(void **)self->b_ptr; pProc = *(void **)self->b_ptr;
if (pProc == NULL) {
PyErr_SetString(PyExc_ValueError,
"attempt to call NULL function pointer");
return NULL;
}
#ifdef MS_WIN32 #ifdef MS_WIN32
if (self->index) { if (self->index) {
/* It's a COM method */ /* It's a COM method */

View File

@ -947,6 +947,7 @@ PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
goto done; goto done;
} }
#if 0 /* XXX this is not quite _PyType_Lookup anymore */
/* Inline _PyType_Lookup */ /* Inline _PyType_Lookup */
{ {
Py_ssize_t i, n; Py_ssize_t i, n;
@ -967,6 +968,9 @@ PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
break; break;
} }
} }
#else
descr = _PyType_Lookup(tp, name);
#endif
Py_XINCREF(descr); Py_XINCREF(descr);

View File

@ -6,6 +6,171 @@
#include <ctype.h> #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[] = { static PyMemberDef type_members[] = {
{"__basicsize__", T_INT, offsetof(PyTypeObject,tp_basicsize),READONLY}, {"__basicsize__", T_INT, offsetof(PyTypeObject,tp_basicsize),READONLY},
{"__itemsize__", T_INT, offsetof(PyTypeObject, tp_itemsize), READONLY}, {"__itemsize__", T_INT, offsetof(PyTypeObject, tp_itemsize), READONLY},
@ -130,6 +295,8 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
return -1; return -1;
} }
type_modified(type);
return PyDict_SetItemString(type->tp_dict, "__module__", value); return PyDict_SetItemString(type->tp_dict, "__module__", value);
} }
@ -1299,6 +1466,14 @@ mro_internal(PyTypeObject *type)
} }
} }
type->tp_mro = tuple; 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; return 0;
} }
@ -2026,6 +2201,16 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
{ {
Py_ssize_t i, n; Py_ssize_t i, n;
PyObject *mro, *res, *base, *dict; 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 */ /* Look in tp_dict of types in MRO */
mro = type->tp_mro; mro = type->tp_mro;
@ -2036,6 +2221,7 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
if (mro == NULL) if (mro == NULL)
return NULL; return NULL;
res = NULL;
assert(PyTuple_Check(mro)); assert(PyTuple_Check(mro));
n = PyTuple_GET_SIZE(mro); n = PyTuple_GET_SIZE(mro);
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
@ -2045,9 +2231,18 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
assert(dict && PyDict_Check(dict)); assert(dict && PyDict_Check(dict));
res = PyDict_GetItem(dict, name); res = PyDict_GetItem(dict, name);
if (res != NULL) 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(), /* This is similar to PyObject_GenericGetAttr(),
@ -2137,10 +2332,6 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
type->tp_name); type->tp_name);
return -1; 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) if (PyObject_GenericSetAttr((PyObject *)type, name, value) < 0)
return -1; return -1;
return update_slot(type, name); return update_slot(type, name);
@ -5421,6 +5612,13 @@ update_slot(PyTypeObject *type, PyObject *name)
slotdef **pp; slotdef **pp;
int offset; 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(); init_slotdefs();
pp = ptrs; pp = ptrs;
for (p = slotdefs; p->name; p++) { for (p = slotdefs; p->name; p++) {