[?1034hdiff -r fa032af2b5a7 Doc/library/inspect.rst --- a/Doc/library/inspect.rst Wed Jun 11 09:08:52 2014 +0200 +++ b/Doc/library/inspect.rst Thu Jun 12 14:05:37 2014 +0200 @@ -159,6 +159,16 @@ attributes: | | | arguments and local | | | | variables | +-----------+-----------------+---------------------------+ +| generator | __name__ | name | ++-----------+-----------------+---------------------------+ +| | __qualname__ | qualified name | ++-----------+-----------------+---------------------------+ +| | gi_frame | frame | ++-----------+-----------------+---------------------------+ +| | gi_running | is the generator running? | ++-----------+-----------------+---------------------------+ +| | gi_code | code | ++-----------+-----------------+---------------------------+ | builtin | __doc__ | documentation string | +-----------+-----------------+---------------------------+ | | __name__ | original name of this | @@ -169,6 +179,10 @@ attributes: | | | ``None`` | +-----------+-----------------+---------------------------+ +.. versionchanged:: 3.5 + + Add ``__qualname__`` attribute to generators. + .. function:: getmembers(object[, predicate]) diff -r fa032af2b5a7 Include/genobject.h --- a/Include/genobject.h Wed Jun 11 09:08:52 2014 +0200 +++ b/Include/genobject.h Thu Jun 12 14:05:37 2014 +0200 @@ -25,6 +25,12 @@ typedef struct { /* List of weak reference. */ PyObject *gi_weakreflist; + + /* Name of the generator. */ + PyObject *gi_name; + + /* Qualified name of the generator. */ + PyObject *gi_qualname; } PyGenObject; PyAPI_DATA(PyTypeObject) PyGen_Type; @@ -33,6 +39,8 @@ PyAPI_DATA(PyTypeObject) PyGen_Type; #define PyGen_CheckExact(op) (Py_TYPE(op) == &PyGen_Type) PyAPI_FUNC(PyObject *) PyGen_New(struct _frame *); +PyAPI_FUNC(PyObject *) PyGen_NewWithQualName(struct _frame *, + PyObject *name, PyObject *qualname); PyAPI_FUNC(int) PyGen_NeedsFinalizing(PyGenObject *); PyAPI_FUNC(int) _PyGen_FetchStopIterationValue(PyObject **); PyObject *_PyGen_Send(PyGenObject *, PyObject *); diff -r fa032af2b5a7 Lib/test/regrtest.py --- a/Lib/test/regrtest.py Wed Jun 11 09:08:52 2014 +0200 +++ b/Lib/test/regrtest.py Thu Jun 12 14:05:37 2014 +0200 @@ -125,6 +125,7 @@ import importlib import argparse import builtins import faulthandler +import gc import io import json import locale @@ -152,6 +153,10 @@ try: import _multiprocessing, multiprocessing.process except ImportError: multiprocessing = None +try: + import tracemalloc +except ImportError: + tracemalloc = None # Some times __path__ and __file__ are not absolute (e.g. while running from @@ -442,7 +447,8 @@ def run_test_in_subprocess(testname, ns) use_resources=ns.use_resources, output_on_failure=ns.verbose3, timeout=ns.timeout, failfast=ns.failfast, - match_tests=ns.match_tests)) + match_tests=ns.match_tests, + findleaks=ns.findleaks)) # Running the child from the same working directory as regrtest's original # invocation ensures that TEMPDIR for the child is the same when # sysconfig.is_python_build() is true. See issue 15300. @@ -760,7 +766,8 @@ def main(tests=None, **kwargs): ns.huntrleaks, output_on_failure=ns.verbose3, timeout=ns.timeout, failfast=ns.failfast, - match_tests=ns.match_tests) + match_tests=ns.match_tests, + findleaks=ns.findleaks) accumulate_result(test, result) except KeyboardInterrupt: interrupted = True @@ -818,7 +825,8 @@ def main(tests=None, **kwargs): try: ns.verbose = True ok = runtest(test, True, ns.quiet, ns.huntrleaks, - timeout=ns.timeout) + timeout=ns.timeout, + findleaks=ns.findleaks) except KeyboardInterrupt: # print a newline separate from the ^C print() @@ -912,7 +920,7 @@ def replace_stdout(): def runtest(test, verbose, quiet, huntrleaks=False, use_resources=None, output_on_failure=False, failfast=False, match_tests=None, - timeout=None): + timeout=None, findleaks=False): """Run a single test. test -- the name of the test @@ -964,7 +972,8 @@ def runtest(test, verbose, quiet, sys.stdout = stream sys.stderr = stream result = runtest_inner(test, verbose, quiet, huntrleaks, - display_failure=False) + display_failure=False, + findleaks=findleaks) if result[0] == FAILED: output = stream.getvalue() orig_stderr.write(output) @@ -975,7 +984,8 @@ def runtest(test, verbose, quiet, else: support.verbose = verbose # Tell tests to be moderately quiet result = runtest_inner(test, verbose, quiet, huntrleaks, - display_failure=not verbose) + display_failure=not verbose, + findleaks=findleaks) return result finally: if use_timeout: @@ -1255,7 +1265,7 @@ class saved_test_environment: def runtest_inner(test, verbose, quiet, - huntrleaks=False, display_failure=True): + huntrleaks=False, display_failure=True, findleaks=False): support.unload(test) test_time = 0.0 @@ -1266,6 +1276,7 @@ def runtest_inner(test, verbose, quiet, else: # Always import it from the test package abstest = 'test.' + test + with saved_test_environment(test, verbose, quiet) as environment: start_time = time.time() the_module = importlib.import_module(abstest) @@ -1275,10 +1286,17 @@ def runtest_inner(test, verbose, quiet, if test_runner is None: tests = unittest.TestLoader().loadTestsFromModule(the_module) test_runner = lambda: support.run_unittest(tests) + test_runner() if huntrleaks: refleak = dash_R(the_module, test, test_runner, huntrleaks) test_time = time.time() - start_time + + test_runner = None + tests = None + + env_changed = environment.changed + environment = None except support.ResourceDenied as msg: if not quiet: print(test, "skipped --", msg) @@ -1306,7 +1324,7 @@ def runtest_inner(test, verbose, quiet, else: if refleak: return FAILED, test_time - if environment.changed: + if env_changed: return ENV_CHANGED, test_time return PASSED, test_time @@ -1366,6 +1384,14 @@ def dash_R(the_module, test, indirect_te raise Exception("Tracking reference leaks requires a debug build " "of Python") + use_tracemalloc = (tracemalloc is not None) + if use_tracemalloc: + tracemalloc.start() + tracemalloc_filters = [ + tracemalloc.Filter(False, '', all_frames=True), + tracemalloc.Filter(False, tracemalloc.__file__, all_frames=True), + ] + # Save current values for dash_R_cleanup() to restore. fs = warnings.filters[:] ps = copyreg.dispatch_table.copy() @@ -1392,14 +1418,35 @@ def dash_R(the_module, test, indirect_te print("beginning", repcount, "repetitions", file=sys.stderr) print(("1234567890"*(repcount//10 + 1))[:repcount], file=sys.stderr) sys.stderr.flush() + previous_snapshot = snapshot = None for i in range(repcount): + trace_leaks = (i >= nwarmup) indirect_test() alloc_after, rc_after = dash_R_cleanup(fs, ps, pic, zdc, abcs) sys.stderr.write('.') sys.stderr.flush() - if i >= nwarmup: + if trace_leaks: rc_deltas[i] = rc_after - rc_before alloc_deltas[i] = alloc_after - alloc_before + print(i) + if use_tracemalloc and (i >= (nwarmup-1)): + print("take snapshot") + previous_snapshot = snapshot + snapshot = tracemalloc.take_snapshot() + snapshot = snapshot.filter_traces(tracemalloc_filters) + if previous_snapshot is not None: + top = snapshot.compare_to(previous_snapshot, 'traceback') + print("[ Top 10 memory differences, run #%s ]" % (1+i)) + for index, stat in enumerate(top[:10], 1): + print("#%s: %.1f KiB (%+.1f KiB), %s memory blocks" + % (index, stat.size / 1024, stat.size_diff / 1024, stat.count)) + for line in stat.traceback.format(): + print(line) + mem_before = sum(stat.size for stat in previous_snapshot.traces) + mem_after = sum(stat.size for stat in snapshot.traces) + print("Python memory before: %.1f kB" % (mem_before / 1024)) + print("Python memory after: %.1f kB (%+.1f kB)" % (mem_after / 1024, (mem_after - mem_before) / 1024)) + print() alloc_before, rc_before = alloc_after, rc_after print(file=sys.stderr) # These checkers return False on success, True on failure diff -r fa032af2b5a7 Lib/test/support/__init__.py --- a/Lib/test/support/__init__.py Wed Jun 11 09:08:52 2014 +0200 +++ b/Lib/test/support/__init__.py Thu Jun 12 14:05:37 2014 +0200 @@ -1722,6 +1722,7 @@ def _run_suite(suite): runner = BasicTestRunner() result = runner.run(suite) + suite = None if not result.wasSuccessful(): if len(result.errors) == 1 and not result.failures: err = result.errors[0][1] @@ -1754,8 +1755,12 @@ def run_unittest(*classes): if fnmatch.fnmatchcase(name, match_tests): return True return False - _filter_suite(suite, case_pred) - _run_suite(suite) + try: + _filter_suite(suite, case_pred) + _run_suite(suite) + finally: + suite._tests.clear() + suite = None #======================================================================= # Check for the presence of docstrings. diff -r fa032af2b5a7 Lib/test/test_generators.py --- a/Lib/test/test_generators.py Wed Jun 11 09:08:52 2014 +0200 +++ b/Lib/test/test_generators.py Thu Jun 12 14:05:37 2014 +0200 @@ -50,6 +50,32 @@ class FinalizationTest(unittest.TestCase self.assertEqual(gc.garbage, old_garbage) +class GeneratorTest(unittest.TestCase): + + def test_name(self): + def func(): + yield 1 + + gen = func() + self.assertEqual(gen.__name__, "func") + self.assertEqual(gen.__qualname__, + "GeneratorTest.test_name..func") + gen.__qualname__ = "123" + self.assertEqual(gen.__qualname__, "123") + + func.__qualname__ = "func_qualname" + func.__name__ = "func_name" + gen = func() + self.assertEqual(gen.__name__, "func_name") + self.assertEqual(gen.__qualname__, "func_qualname") + + gen = (x for x in range(10)) + self.assertEqual(gen.__name__, + "") + self.assertEqual(gen.__qualname__, + "GeneratorTest.test_name..") + + tutorial_tests = """ Let's try a simple generator: diff -r fa032af2b5a7 Lib/test/test_os.py --- a/Lib/test/test_os.py Wed Jun 11 09:08:52 2014 +0200 +++ b/Lib/test/test_os.py Thu Jun 12 14:05:37 2014 +0200 @@ -2038,6 +2038,7 @@ class TestSendfile(unittest.TestCase): self.client.close() if self.server.running: self.server.stop() + self.server = None def sendfile_wrapper(self, sock, file, offset, nbytes, headers=[], trailers=[]): """A higher level wrapper representing how an application is diff -r fa032af2b5a7 Objects/genobject.c --- a/Objects/genobject.c Wed Jun 11 09:08:52 2014 +0200 +++ b/Objects/genobject.c Thu Jun 12 14:05:37 2014 +0200 @@ -58,6 +58,8 @@ gen_dealloc(PyGenObject *gen) _PyObject_GC_UNTRACK(self); Py_CLEAR(gen->gi_frame); Py_CLEAR(gen->gi_code); + Py_CLEAR(gen->gi_name); + Py_CLEAR(gen->gi_qualname); PyObject_GC_Del(gen); } @@ -418,33 +420,18 @@ static PyObject * gen_repr(PyGenObject *gen) { return PyUnicode_FromFormat("", - ((PyCodeObject *)gen->gi_code)->co_name, - gen); + gen->gi_qualname, gen); } -static PyObject * -gen_get_name(PyGenObject *gen) -{ - PyObject *name = ((PyCodeObject *)gen->gi_code)->co_name; - Py_INCREF(name); - return name; -} - - -PyDoc_STRVAR(gen__name__doc__, -"Return the name of the generator's associated code object."); - -static PyGetSetDef gen_getsetlist[] = { - {"__name__", (getter)gen_get_name, NULL, gen__name__doc__}, - {NULL} -}; - - static PyMemberDef gen_memberlist[] = { - {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY}, - {"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY}, - {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY}, + {"gi_frame", T_OBJECT, offsetof(PyGenObject, gi_frame), READONLY}, + {"gi_running", T_BOOL, offsetof(PyGenObject, gi_running), READONLY}, + {"gi_code", T_OBJECT, offsetof(PyGenObject, gi_code), READONLY}, + {"__name__", T_OBJECT, offsetof(PyGenObject, gi_name), RESTRICTED, + PyDoc_STR("name of the generator")}, + {"__qualname__", T_OBJECT, offsetof(PyGenObject, gi_qualname), RESTRICTED, + PyDoc_STR("qualified name of the generator")}, {NULL} /* Sentinel */ }; @@ -487,7 +474,7 @@ PyTypeObject PyGen_Type = { (iternextfunc)gen_iternext, /* tp_iternext */ gen_methods, /* tp_methods */ gen_memberlist, /* tp_members */ - gen_getsetlist, /* tp_getset */ + 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ @@ -510,7 +497,7 @@ PyTypeObject PyGen_Type = { }; PyObject * -PyGen_New(PyFrameObject *f) +PyGen_NewWithQualName(PyFrameObject *f, PyObject *name, PyObject *qualname) { PyGenObject *gen = PyObject_GC_New(PyGenObject, &PyGen_Type); if (gen == NULL) { @@ -523,10 +510,26 @@ PyGen_New(PyFrameObject *f) gen->gi_code = (PyObject *)(f->f_code); gen->gi_running = 0; gen->gi_weakreflist = NULL; + if (name != NULL) + gen->gi_name = name; + else + gen->gi_name = ((PyCodeObject *)gen->gi_code)->co_name; + Py_INCREF(gen->gi_name); + if (qualname != NULL) + gen->gi_qualname = qualname; + else + gen->gi_qualname = gen->gi_name; + Py_INCREF(gen->gi_qualname); _PyObject_GC_TRACK(gen); return (PyObject *)gen; } +PyObject * +PyGen_New(PyFrameObject *f) +{ + return PyGen_NewWithQualName(f, NULL, NULL); +} + int PyGen_NeedsFinalizing(PyGenObject *gen) { diff -r fa032af2b5a7 Python/ceval.c --- a/Python/ceval.c Wed Jun 11 09:08:52 2014 +0200 +++ b/Python/ceval.c Thu Jun 12 14:05:37 2014 +0200 @@ -3402,9 +3402,10 @@ too_many_positional(PyCodeObject *co, in the test in the if statements in Misc/gdbinit (pystack and pystackv). */ PyObject * -PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, +PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals, PyObject **args, int argcount, PyObject **kws, int kwcount, - PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) + PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure, + PyObject *name, PyObject *qualname) { PyCodeObject* co = (PyCodeObject*)_co; PyFrameObject *f; @@ -3596,7 +3597,7 @@ PyEval_EvalCodeEx(PyObject *_co, PyObjec /* Create a new generator that owns the ready to run frame * and return that as the value. */ - return PyGen_New(f); + return PyGen_NewWithQualName(f, name, qualname); } retval = PyEval_EvalFrameEx(f,0); @@ -3615,6 +3616,16 @@ fail: /* Jump here from prelude on failu return retval; } +PyObject * +PyEval_EvalCodeEx(PyObject *_co, PyObject *globals, PyObject *locals, + PyObject **args, int argcount, PyObject **kws, int kwcount, + PyObject **defs, int defcount, PyObject *kwdefs, PyObject *closure) +{ + return PyEval_EvalCodeWithName(_co, globals, locals, + args, argcount, kws, kwcount, + defs, defcount, kwdefs, closure, + NULL, NULL); +} static PyObject * special_lookup(PyObject *o, _Py_Identifier *id) @@ -4313,6 +4324,8 @@ fast_function(PyObject *func, PyObject * PyObject *globals = PyFunction_GET_GLOBALS(func); PyObject *argdefs = PyFunction_GET_DEFAULTS(func); PyObject *kwdefs = PyFunction_GET_KW_DEFAULTS(func); + PyObject *name = ((PyFunctionObject *)func) -> func_name; + PyObject *qualname = ((PyFunctionObject *)func) -> func_qualname; PyObject **d = NULL; int nd = 0; @@ -4355,10 +4368,11 @@ fast_function(PyObject *func, PyObject * d = &PyTuple_GET_ITEM(argdefs, 0); nd = Py_SIZE(argdefs); } - return PyEval_EvalCodeEx((PyObject*)co, globals, - (PyObject *)NULL, (*pp_stack)-n, na, - (*pp_stack)-2*nk, nk, d, nd, kwdefs, - PyFunction_GET_CLOSURE(func)); + return PyEval_EvalCodeWithName((PyObject*)co, globals, + (PyObject *)NULL, (*pp_stack)-n, na, + (*pp_stack)-2*nk, nk, d, nd, kwdefs, + PyFunction_GET_CLOSURE(func), + name, qualname); } static PyObject *