summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/c-api/type.rst7
-rw-r--r--Doc/conf.py2
-rw-r--r--Doc/library/sys.rst7
-rw-r--r--Include/object.h1
-rwxr-xr-xLib/pydoc.py1
-rwxr-xr-xLib/rational.py56
-rwxr-xr-xLib/test/regrtest.py3
-rw-r--r--Lib/test/test_builtin.py21
-rw-r--r--Lib/test/test_resource.py3
-rw-r--r--Lib/test/test_xmlrpc.py31
-rw-r--r--Objects/floatobject.c159
-rw-r--r--Objects/typeobject.c18
-rw-r--r--Python/pythonrun.c3
-rw-r--r--Python/sysmodule.c13
14 files changed, 254 insertions, 71 deletions
diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst
index 01a94bb..d1d4e45 100644
--- a/Doc/c-api/type.rst
+++ b/Doc/c-api/type.rst
@@ -33,6 +33,13 @@ Type Objects
standard type object. Return false in all other cases.
+.. cfunction:: unsigned int PyType_ClearCache(void)
+
+ Clears the internal lookup cache. Return the current version tag.
+
+ .. versionadded:: 2.6
+
+
.. cfunction:: int PyType_HasFeature(PyObject *o, int feature)
Return true if the type object *o* sets the feature *feature*. Type features
diff --git a/Doc/conf.py b/Doc/conf.py
index 8359674..3c33f98 100644
--- a/Doc/conf.py
+++ b/Doc/conf.py
@@ -13,6 +13,8 @@ sys.path.append('tools/sphinxext')
# General configuration
# ---------------------
+extensions = ['sphinx.addons.refcounting']
+
# General substitutions.
project = 'Python'
copyright = '1990-%s, Python Software Foundation' % time.strftime('%Y')
diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
index 5718f7b..5fdff44 100644
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -54,6 +54,13 @@ always available.
A string containing the copyright pertaining to the Python interpreter.
+.. function:: _cleartypecache()
+
+ Clear the internal type lookup cache.
+
+ .. versionadded:: 2.6
+
+
.. function:: _current_frames()
Return a dictionary mapping each thread's identifier to the topmost stack frame
diff --git a/Include/object.h b/Include/object.h
index 90a3354..5fc8ccb 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -428,6 +428,7 @@ PyAPI_FUNC(PyObject *) PyType_GenericAlloc(PyTypeObject *, Py_ssize_t);
PyAPI_FUNC(PyObject *) PyType_GenericNew(PyTypeObject *,
PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *);
+PyAPI_FUNC(unsigned int) PyType_ClearCache(void);
/* Generic operations on objects */
PyAPI_FUNC(int) PyObject_Print(PyObject *, FILE *, int);
diff --git a/Lib/pydoc.py b/Lib/pydoc.py
index 755d51a..0fdbb90 100755
--- a/Lib/pydoc.py
+++ b/Lib/pydoc.py
@@ -1203,7 +1203,6 @@ class TextDoc(Doc):
else:
tag = "inherited from %s" % classname(thisclass,
object.__module__)
- filter(lambda t: not t[0].startswith('_'), attrs)
# Sort attrs by name.
attrs.sort()
diff --git a/Lib/rational.py b/Lib/rational.py
index da75ab1..8de8f23 100755
--- a/Lib/rational.py
+++ b/Lib/rational.py
@@ -24,60 +24,6 @@ def gcd(a, b):
return a
-def _binary_float_to_ratio(x):
- """x -> (top, bot), a pair of ints s.t. x = top/bot.
-
- The conversion is done exactly, without rounding.
- bot > 0 guaranteed.
- Some form of binary fp is assumed.
- Pass NaNs or infinities at your own risk.
-
- >>> _binary_float_to_ratio(10.0)
- (10, 1)
- >>> _binary_float_to_ratio(0.0)
- (0, 1)
- >>> _binary_float_to_ratio(-.25)
- (-1, 4)
- """
- # XXX Move this to floatobject.c with a name like
- # float.as_integer_ratio()
-
- if x == 0:
- return 0, 1
- f, e = math.frexp(x)
- signbit = 1
- if f < 0:
- f = -f
- signbit = -1
- assert 0.5 <= f < 1.0
- # x = signbit * f * 2**e exactly
-
- # Suck up CHUNK bits at a time; 28 is enough so that we suck
- # up all bits in 2 iterations for all known binary double-
- # precision formats, and small enough to fit in an int.
- CHUNK = 28
- top = 0
- # invariant: x = signbit * (top + f) * 2**e exactly
- while f:
- f = math.ldexp(f, CHUNK)
- digit = trunc(f)
- assert digit >> CHUNK == 0
- top = (top << CHUNK) | digit
- f = f - digit
- assert 0.0 <= f < 1.0
- e = e - CHUNK
- assert top
-
- # Add in the sign bit.
- top = signbit * top
-
- # now x = top * 2**e exactly; fold in 2**e
- if e>0:
- return (top * 2**e, 1)
- else:
- return (top, 2 ** -e)
-
-
_RATIONAL_FORMAT = re.compile(
r'^\s*(?P<sign>[-+]?)(?P<num>\d+)'
r'(?:/(?P<denom>\d+)|\.(?P<decimal>\d+))?\s*$')
@@ -162,7 +108,7 @@ class Rational(RationalAbc):
(cls.__name__, f, type(f).__name__))
if math.isnan(f) or math.isinf(f):
raise TypeError("Cannot convert %r to %s." % (f, cls.__name__))
- return cls(*_binary_float_to_ratio(f))
+ return cls(*f.as_integer_ratio())
@classmethod
def from_decimal(cls, dec):
diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py
index 790d769..5934e6b 100755
--- a/Lib/test/regrtest.py
+++ b/Lib/test/regrtest.py
@@ -752,6 +752,9 @@ def dash_R_cleanup(fs, ps, pic, abcs):
sys.path_importer_cache.clear()
sys.path_importer_cache.update(pic)
+ # clear type cache
+ sys._cleartypecache()
+
# Clear ABC registries, restoring previously saved ABC registries.
for abc in [getattr(_abcoll, a) for a in _abcoll.__all__]:
if not issubclass(abc, _Abstract):
diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py
index e6ded81..92c1d00 100644
--- a/Lib/test/test_builtin.py
+++ b/Lib/test/test_builtin.py
@@ -5,7 +5,7 @@ from test.test_support import fcmp, TESTFN, unlink, run_unittest, \
run_with_locale
from operator import neg
-import sys, warnings, random, UserDict, io
+import sys, warnings, random, UserDict, io, rational
warnings.filterwarnings("ignore", "hex../oct.. of negative int",
FutureWarning, __name__)
warnings.filterwarnings("ignore", "integer argument expected",
@@ -592,6 +592,25 @@ class BuiltinTest(unittest.TestCase):
# make sure we can take a subclass of str as a format spec
self.assertEqual(format(0, C('10')), ' 0')
+ def test_floatasratio(self):
+ R = rational.Rational
+ self.assertEqual(R(0, 1),
+ R(*float(0.0).as_integer_ratio()))
+ self.assertEqual(R(5, 2),
+ R(*float(2.5).as_integer_ratio()))
+ self.assertEqual(R(1, 2),
+ R(*float(0.5).as_integer_ratio()))
+ self.assertEqual(R(4728779608739021, 2251799813685248),
+ R(*float(2.1).as_integer_ratio()))
+ self.assertEqual(R(-4728779608739021, 2251799813685248),
+ R(*float(-2.1).as_integer_ratio()))
+ self.assertEqual(R(-2100, 1),
+ R(*float(-2100.0).as_integer_ratio()))
+
+ self.assertRaises(OverflowError, float('inf').as_integer_ratio)
+ self.assertRaises(OverflowError, float('-inf').as_integer_ratio)
+ self.assertRaises(ValueError, float('nan').as_integer_ratio)
+
def test_getattr(self):
import sys
self.assert_(getattr(sys, 'stdout') is sys.stdout)
diff --git a/Lib/test/test_resource.py b/Lib/test/test_resource.py
index 43ff372..86440b5 100644
--- a/Lib/test/test_resource.py
+++ b/Lib/test/test_resource.py
@@ -56,13 +56,12 @@ class ResourceTest(unittest.TestCase):
f.flush()
# On some systems (e.g., Ubuntu on hppa) the flush()
# doesn't always cause the exception, but the close()
- # does eventually. Try closing several times in
+ # does eventually. Try flushing several times in
# an attempt to ensure the file is really synced and
# the exception raised.
for i in range(5):
time.sleep(.1)
f.flush()
- f.close()
except IOError:
if not limit_set:
raise
diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py
index 28cbf0c..092be51 100644
--- a/Lib/test/test_xmlrpc.py
+++ b/Lib/test/test_xmlrpc.py
@@ -312,10 +312,17 @@ def is_unavailable_exception(e):
given by operations on non-blocking sockets.'''
# sometimes we get a -1 error code and/or empty headers
- if e.errcode == -1 or e.headers is None:
+ try:
+ if e.errcode == -1 or e.headers is None:
+ return True
+ exc_mess = e.headers.get('X-exception')
+ except AttributeError:
+ # Ignore socket.errors here.
+ exc_mess = str(e)
+
+ if exc_mess and 'temporarily unavailable' in exc_mess.lower():
return True
-
class SimpleServerTestCase(unittest.TestCase):
def setUp(self):
# enable traceback reporting
@@ -349,7 +356,7 @@ class SimpleServerTestCase(unittest.TestCase):
# ignore failures due to non-blocking socket 'unavailable' errors
if not is_unavailable_exception(e):
# protocol error; provide additional information in test output
- self.fail("%s\n%s" % (e, e.headers))
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))
# [ch] The test 404 is causing lots of false alarms.
def XXXtest_404(self):
@@ -375,7 +382,7 @@ class SimpleServerTestCase(unittest.TestCase):
# ignore failures due to non-blocking socket 'unavailable' errors
if not is_unavailable_exception(e):
# protocol error; provide additional information in test output
- self.fail("%s\n%s" % (e, e.headers))
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))
def test_introspection2(self):
@@ -388,7 +395,7 @@ class SimpleServerTestCase(unittest.TestCase):
# ignore failures due to non-blocking socket 'unavailable' errors
if not is_unavailable_exception(e):
# protocol error; provide additional information in test output
- self.fail("%s\n%s" % (e, e.headers))
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))
def test_introspection3(self):
try:
@@ -400,7 +407,7 @@ class SimpleServerTestCase(unittest.TestCase):
# ignore failures due to non-blocking socket 'unavailable' errors
if not is_unavailable_exception(e):
# protocol error; provide additional information in test output
- self.fail("%s\n%s" % (e, e.headers))
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))
def test_introspection4(self):
# the SimpleXMLRPCServer doesn't support signatures, but
@@ -413,7 +420,7 @@ class SimpleServerTestCase(unittest.TestCase):
# ignore failures due to non-blocking socket 'unavailable' errors
if not is_unavailable_exception(e):
# protocol error; provide additional information in test output
- self.fail("%s\n%s" % (e, e.headers))
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))
def test_multicall(self):
try:
@@ -430,7 +437,7 @@ class SimpleServerTestCase(unittest.TestCase):
# ignore failures due to non-blocking socket 'unavailable' errors
if not is_unavailable_exception(e):
# protocol error; provide additional information in test output
- self.fail("%s\n%s" % (e, e.headers))
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))
def test_non_existing_multicall(self):
try:
@@ -451,7 +458,7 @@ class SimpleServerTestCase(unittest.TestCase):
# ignore failures due to non-blocking socket 'unavailable' errors
if not is_unavailable_exception(e):
# protocol error; provide additional information in test output
- self.fail("%s\n%s" % (e, e.headers))
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))
# This is a contrived way to make a failure occur on the server side
# in order to test the _send_traceback_header flag on the server
@@ -498,7 +505,7 @@ class FailingServerTestCase(unittest.TestCase):
# ignore failures due to non-blocking socket 'unavailable' errors
if not is_unavailable_exception(e):
# protocol error; provide additional information in test output
- self.fail("%s\n%s" % (e, e.headers))
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))
def test_fail_no_info(self):
# use the broken message class
@@ -509,7 +516,7 @@ class FailingServerTestCase(unittest.TestCase):
p.pow(6,8)
except (xmlrpclib.ProtocolError, socket.error) as e:
# ignore failures due to non-blocking socket 'unavailable' errors
- if not is_unavailable_exception(e):
+ if not is_unavailable_exception(e) and hasattr(e, "headers"):
# The two server-side error headers shouldn't be sent back in this case
self.assertTrue(e.headers.get("X-exception") is None)
self.assertTrue(e.headers.get("X-traceback") is None)
@@ -529,7 +536,7 @@ class FailingServerTestCase(unittest.TestCase):
p.pow(6,8)
except (xmlrpclib.ProtocolError, socket.error) as e:
# ignore failures due to non-blocking socket 'unavailable' errors
- if not is_unavailable_exception(e):
+ if not is_unavailable_exception(e) and hasattr(e, "headers"):
# We should get error info in the response
expected_err = "invalid literal for int() with base 10: 'I am broken'"
self.assertEqual(e.headers.get("x-exception"), expected_err)
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index 859abf0..8ef3261 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -1073,6 +1073,163 @@ float_float(PyObject *v)
return v;
}
+static PyObject *
+float_as_integer_ratio(PyObject *v)
+{
+ double self;
+ double float_part;
+ int exponent;
+ int is_negative;
+ const int chunk_size = 28;
+ PyObject *prev;
+ PyObject *py_chunk = NULL;
+ PyObject *py_exponent = NULL;
+ PyObject *numerator = NULL;
+ PyObject *denominator = NULL;
+ PyObject *result_pair = NULL;
+ PyNumberMethods *long_methods;
+
+#define INPLACE_UPDATE(obj, call) \
+ prev = obj; \
+ obj = call; \
+ Py_DECREF(prev); \
+
+ CONVERT_TO_DOUBLE(v, self);
+
+ if (Py_IS_INFINITY(self)) {
+ PyErr_SetString(PyExc_OverflowError,
+ "Cannot pass infinity to float.as_integer_ratio.");
+ return NULL;
+ }
+#ifdef Py_NAN
+ if (Py_IS_NAN(self)) {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot pass nan to float.as_integer_ratio.");
+ return NULL;
+ }
+#endif
+
+ if (self == 0) {
+ numerator = PyLong_FromLong(0);
+ if (numerator == NULL) goto error;
+ denominator = PyLong_FromLong(1);
+ if (denominator == NULL) goto error;
+ result_pair = PyTuple_Pack(2, numerator, denominator);
+ /* Hand ownership over to the tuple. If the tuple
+ wasn't created successfully, we want to delete the
+ ints anyway. */
+ Py_DECREF(numerator);
+ Py_DECREF(denominator);
+ return result_pair;
+ }
+
+ /* XXX: Could perhaps handle FLT_RADIX!=2 by using ilogb and
+ scalbn, but those may not be in C89. */
+ PyFPE_START_PROTECT("as_integer_ratio", goto error);
+ float_part = frexp(self, &exponent);
+ is_negative = 0;
+ if (float_part < 0) {
+ float_part = -float_part;
+ is_negative = 1;
+ /* 0.5 <= float_part < 1.0 */
+ }
+ PyFPE_END_PROTECT(float_part);
+ /* abs(self) == float_part * 2**exponent exactly */
+
+ /* Suck up chunk_size bits at a time; 28 is enough so that we
+ suck up all bits in 2 iterations for all known binary
+ double-precision formats, and small enough to fit in a
+ long. */
+ numerator = PyLong_FromLong(0);
+ if (numerator == NULL) goto error;
+
+ long_methods = PyLong_Type.tp_as_number;
+
+ py_chunk = PyLong_FromLong(chunk_size);
+ if (py_chunk == NULL) goto error;
+
+ while (float_part != 0) {
+ /* invariant: abs(self) ==
+ (numerator + float_part) * 2**exponent exactly */
+ long digit;
+ PyObject *py_digit;
+
+ PyFPE_START_PROTECT("as_integer_ratio", goto error);
+ /* Pull chunk_size bits out of float_part, into digits. */
+ float_part = ldexp(float_part, chunk_size);
+ digit = (long)float_part;
+ float_part -= digit;
+ /* 0 <= float_part < 1 */
+ exponent -= chunk_size;
+ PyFPE_END_PROTECT(float_part);
+
+ /* Shift digits into numerator. */
+ // numerator <<= chunk_size
+ INPLACE_UPDATE(numerator,
+ long_methods->nb_lshift(numerator, py_chunk));
+ if (numerator == NULL) goto error;
+
+ // numerator |= digit
+ py_digit = PyLong_FromLong(digit);
+ if (py_digit == NULL) goto error;
+ INPLACE_UPDATE(numerator,
+ long_methods->nb_or(numerator, py_digit));
+ Py_DECREF(py_digit);
+ if (numerator == NULL) goto error;
+ }
+
+ /* Add in the sign bit. */
+ if (is_negative) {
+ INPLACE_UPDATE(numerator,
+ long_methods->nb_negative(numerator));
+ if (numerator == NULL) goto error;
+ }
+
+ /* now self = numerator * 2**exponent exactly; fold in 2**exponent */
+ denominator = PyLong_FromLong(1);
+ py_exponent = PyLong_FromLong(labs(exponent));
+ if (py_exponent == NULL) goto error;
+ INPLACE_UPDATE(py_exponent,
+ long_methods->nb_lshift(denominator, py_exponent));
+ if (py_exponent == NULL) goto error;
+ if (exponent > 0) {
+ INPLACE_UPDATE(numerator,
+ long_methods->nb_multiply(numerator,
+ py_exponent));
+ if (numerator == NULL) goto error;
+ }
+ else {
+ Py_DECREF(denominator);
+ denominator = py_exponent;
+ py_exponent = NULL;
+ }
+
+ result_pair = PyTuple_Pack(2, numerator, denominator);
+
+#undef INPLACE_UPDATE
+error:
+ Py_XDECREF(py_exponent);
+ Py_XDECREF(py_chunk);
+ Py_XDECREF(denominator);
+ Py_XDECREF(numerator);
+ return result_pair;
+}
+
+PyDoc_STRVAR(float_as_integer_ratio_doc,
+"float.as_integer_ratio() -> (int, int)\n"
+"\n"
+"Returns a pair of integers, not necessarily in lowest terms, whose\n"
+"ratio is exactly equal to the original float. This method raises an\n"
+"OverflowError on infinities and a ValueError on nans. The resulting\n"
+"denominator will be positive.\n"
+"\n"
+">>> (10.0).as_integer_ratio()\n"
+"(167772160L, 16777216L)\n"
+">>> (0.0).as_integer_ratio()\n"
+"(0, 1)\n"
+">>> (-.25).as_integer_ratio()\n"
+"(-134217728L, 536870912L)");
+
static PyObject *
float_subtype_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
@@ -1281,6 +1438,8 @@ static PyMethodDef float_methods[] = {
{"__round__", (PyCFunction)float_round, METH_VARARGS,
"Returns the Integral closest to x, rounding half toward even.\n"
"When an argument is passed, works like built-in round(x, ndigits)."},
+ {"as_integer_ratio", (PyCFunction)float_as_integer_ratio, METH_NOARGS,
+ float_as_integer_ratio_doc},
{"__getnewargs__", (PyCFunction)float_getnewargs, METH_NOARGS},
{"__getformat__", (PyCFunction)float_getformat,
METH_O|METH_CLASS, float_getformat_doc},
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 33b2023..1d6336c 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -33,6 +33,24 @@ struct method_cache_entry {
static struct method_cache_entry method_cache[1 << MCACHE_SIZE_EXP];
static unsigned int next_version_tag = 0;
+static void type_modified(PyTypeObject *);
+
+unsigned int
+PyType_ClearCache(void)
+{
+ Py_ssize_t i;
+ unsigned int cur_version_tag = next_version_tag - 1;
+
+ for (i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
+ method_cache[i].version = 0;
+ Py_CLEAR(method_cache[i].name);
+ method_cache[i].value = NULL;
+ }
+ next_version_tag = 0;
+ /* mark all version tags as invalid */
+ type_modified(&PyBaseObject_Type);
+ return cur_version_tag;
+}
static void
type_modified(PyTypeObject *type)
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index 9b72908..7cee8e4 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -404,6 +404,9 @@ Py_Finalize(void)
Py_XDECREF(warnings_module);
warnings_module = NULL;
+ /* Clear type lookup cache */
+ PyType_ClearCache();
+
/* Collect garbage. This may call finalizers; it's nice to call these
* before all modules are destroyed.
* XXX If a __del__ or weakref callback is triggered here, and tries to
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index 499f328..e536f0a 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -730,6 +730,17 @@ a 11-tuple where the entries in the tuple are counts of:\n\
10. Number of stack pops performed by call_function()"
);
+static PyObject *
+sys_cleartypecache(PyObject* self, PyObject* args)
+{
+ PyType_ClearCache();
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(cleartypecache_doc,
+"_cleartypecache() -> None\n\
+Clear the internal type lookup cache.");
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -752,6 +763,8 @@ static PyMethodDef sys_methods[] = {
/* Might as well keep this in alphabetic order */
{"callstats", (PyCFunction)PyEval_GetCallStats, METH_NOARGS,
callstats_doc},
+ {"_cleartypecache", sys_cleartypecache, METH_NOARGS,
+ cleartypecache_doc},
{"_current_frames", sys_current_frames, METH_NOARGS,
current_frames_doc},
{"displayhook", sys_displayhook, METH_O, displayhook_doc},