Description: Add filename to classes Add filename from class-defining code object to classes as __filename__ Author: Thomas Viehmann --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -36,6 +36,7 @@ _Py_IDENTIFIER(__builtins__); _Py_IDENTIFIER(__dict__); +_Py_IDENTIFIER(__filename__); _Py_IDENTIFIER(__prepare__); _Py_IDENTIFIER(__round__); _Py_IDENTIFIER(encoding); @@ -58,6 +59,7 @@ PyObject *cls = NULL, *cell = NULL; Py_ssize_t nargs; int isclass = 0; /* initialize to prevent gcc warning */ + int err = 0; assert(args != NULL); if (!PyTuple_Check(args)) { @@ -178,6 +180,19 @@ NULL, 0, NULL, 0, NULL, 0, NULL, PyFunction_GET_CLOSURE(func)); if (cell != NULL) { + if (PyDict_CheckExact(ns)) { + err = _PyDict_SetItemId(ns, &PyId___filename__, ((PyCodeObject*) PyFunction_GET_CODE(func))->co_filename); + } + else { + PyObject *filename_str = _PyUnicode_FromId(&PyId___filename__); + if (filename_str == NULL) { + goto error; + } + err = PyObject_SetItem(ns, filename_str, ((PyCodeObject*) PyFunction_GET_CODE(func))->co_filename); + } + if (err != 0) { + goto error; + } PyObject *margs[3] = {name, bases, ns}; cls = _PyObject_FastCallDict(meta, margs, 3, mkw); if (cls != NULL && PyType_Check(cls) && PyCell_Check(cell)) { --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -4688,8 +4688,8 @@ self.assertNotIsInstance(it, list) keys = list(it) keys.sort() - self.assertEqual(keys, ['__dict__', '__doc__', '__module__', - '__weakref__', 'meth']) + self.assertEqual(keys, ['__dict__', '__doc__', '__filename__', + '__module__', '__weakref__', 'meth']) @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __local__') @@ -4698,7 +4698,7 @@ it = self.C.__dict__.values() self.assertNotIsInstance(it, list) values = list(it) - self.assertEqual(len(values), 5) + self.assertEqual(len(values), 6) @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __local__') @@ -4708,8 +4708,8 @@ self.assertNotIsInstance(it, list) keys = [item[0] for item in it] keys.sort() - self.assertEqual(keys, ['__dict__', '__doc__', '__module__', - '__weakref__', 'meth']) + self.assertEqual(keys, ['__dict__', '__doc__', '__filename__', + '__module__', '__weakref__', 'meth']) def test_dict_type_with_metaclass(self): # Testing type of __dict__ when metaclass set... --- a/Lib/test/test_metaclass.py +++ b/Lib/test/test_metaclass.py @@ -145,7 +145,8 @@ >>> class LoggingDict(dict): ... def __setitem__(self, key, value): - ... print("d[%r] = %r" % (key, value)) + ... if key != '__filename__': + ... print("d[%r] = %r" % (key, value)) ... dict.__setitem__(self, key, value) ... >>> class Meta(type): @@ -169,7 +170,7 @@ >>> def meta(name, bases, namespace, **kwds): ... print("meta:", name, bases) - ... print("ns:", sorted(namespace.items())) + ... print("ns:", sorted([(k, v) for k, v in namespace.items() if k != "__filename__"])) ... print("kw:", sorted(kwds.items())) ... return namespace ... @@ -182,7 +183,7 @@ kw: [] >>> type(C) is dict True - >>> print(sorted(C.items())) + >>> print(sorted([(k, v) for k, v in C.items() if k != "__filename__"])) [('__module__', 'test.test_metaclass'), ('__qualname__', 'C'), ('a', 42), ('b', 24)] >>> --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -647,6 +647,8 @@ return object.__file__ raise TypeError('{!r} is a built-in module'.format(object)) if isclass(object): + if hasattr(object, '__filename__'): + return object.__filename__ if hasattr(object, '__module__'): object = sys.modules.get(object.__module__) if hasattr(object, '__file__'): --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1681,7 +1681,8 @@ class _ProtocolMeta(GenericMeta): attr != '__orig_bases__' and attr != '__extra__' and attr != '__tree_hash__' and - attr != '__module__'): + attr != '__module__' and + attr != '__filename__'): attrs.add(attr) return attrs @@ -2121,7 +2122,7 @@ _prohibited = ('__new__', '__init__', '_ '_fields', '_field_defaults', '_field_types', '_make', '_replace', '_asdict', '_source') -_special = ('__module__', '__name__', '__qualname__', '__annotations__') +_special = ('__filename__', '__module__', '__name__', '__qualname__', '__annotations__') class NamedTupleMeta(type):