diff --git a/Doc/c-api/type.rst b/Doc/c-api/type.rst index 01a94bbed44..d1d4e456231 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 83596748518..3c33f98d6c6 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 5718f7b02f8..5fdff4429c3 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 90a3354f25a..5fc8ccb6aef 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 755d51a6a95..0fdbb900a82 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 da75ab1f267..8de8f230ad7 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[-+]?)(?P\d+)' r'(?:/(?P\d+)|\.(?P\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 790d769c97a..5934e6bb976 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 e6ded8103b0..92c1d00dfcf 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 43ff3728c90..86440b50b2c 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 28cbf0c8738..092be51a71c 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -312,9 +312,16 @@ 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: - return True + 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): @@ -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 859abf00a05..8ef3261c6ad 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 33b2023f0ca..1d6336ce158 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 9b72908df6a..7cee8e4eda6 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 499f3288be2..e536f0aad35 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},