I suppose that your main concern is using the colon as separator. I recorded your suggestion in the Use colon separator in fully qualified name section. What do you mean by repeating here? Do you mean that the section doesn’t summarize well your arguments? Or that you disagree that dot separator should be the recommended format?
The PEP is about unifying existing code formatting type names. Extract of this section:
In the standard library, no code formats a type fully qualified name this way.
So right, pkgutil.resolve_name()
and python -m inspect
expect a type fully qualified name using a colon separator. I understand that it’s more convenient to split the “type module” part from the “type qualified name” part in an unambigious way, and it avoids to import modules and to get attributes.
But that format is unique to inspect+pkgutil, everything else in the stdlib uses the dot separator, no?
Also, it’s already possible to split a fully qualified name at the dot separator, and then try to import one part, or use getattr()
, to get a type. Example:
import importlib
def resolve(fully_qualified_name):
parent = None
obj = None
use_import = True
for name in fully_qualified_name.split('.'):
if use_import:
if parent:
module_name = f'{parent}.{name}'
else:
module_name = name
try:
obj = importlib.import_module(module_name)
continue
except ImportError:
pass
use_import = False
obj = getattr(obj, name)
return obj
print(resolve('datetime.timedelta'))
(Is there already a function doing that in the stdlib?)
I would also prefer type.__fully_qualified_name__
to be close to repr(type)
, even if there is now a difference: type.__fully_qualified_name__
omits the "__main__."
prefix for the __main__
module.
repr(type)
output can be copied/pasted in Python REPL, and you get the type (if you already imported the right module). I mean, copy the string between quotes of repr(type)
output. It’s somehow a “standard” for repr() in Python (more or less respected) that the output can be used directly in regular code to get/create the same object. With type.__fully_qualified_name__
, it’s even simpler: you can just copy/paste type.__fully_qualified_name__
value in the REPL.
Example:
>>> import datetime
# using repr()
>>> datetime.timedelta
<class 'datetime.timedelta'>
# copy/paste
>>> datetime.timedelta
<class 'datetime.timedelta'>
# using __fully_qualified_name__
>>> datetime.timedelta.__fully_qualified_name__
'datetime.timedelta'
# copy/paste (well,it's the same string)
>>> datetime.timedelta
<class 'datetime.timedelta'>
It’s not only about the REPL, you can also paste datetime.timedelta
in your source code and “it just works” (again, if you imported the expected module).
Programming languages such as C++ and PHP use namespace::name
syntax, whereas Python uses module.name
syntax.
By the way, I was always confused by the thin difference between a package sub-module and a module attribute. For example, import os; os.listdir
gets an attribute of a module. But from os import listdir
works as well. And it’s just the same syntax to get a sub-module: from os import path
. Wait, is os.path
a module attribute or a sub-module? Well, does it really matter?
import os; os.path
just works which makes things even more confusing.
Right, the PEP intent is to use a the same (or at least similar) format for type names in the stdlib.
You can already write f"{type.__module__}:{type.__qualname__}"
. You’re right that not providing a built-in method or attribute discourage using this format.
Copy of merwok’s message (highlight in mine):
The need to resolve a dotted name to a Python object is spreading in the stdlib: pydoc has locate and resolve, packaging has util.resolve_name, unittest has something else, etc. For the benefit of stdlib maintainers as well as the community, I think such functionality should be exposed publicly by the inspect module.
If the inspect
module uses this format, why not adding a function to the inspect
module to format a type name in the format that it expects, instead of adding an attribute or a method to type? Example, add this function to inspect
:
def type_name(cls):
return f"{type.__module__}:{type.__qualname__}"
The unittest
module uses the dot separator. It has the unittest.util
function:
def strclass(cls):
return "%s.%s" % (cls.__module__, cls.__qualname__)
The typing
module also uses the dot separator. It has a private _type_repr()
function.
PEP 737 scope is limited to types: coroutines, generators, functions and methods are not covered by the PEP on purpose. Entry points can be functions, and maybe other types. Maybe an utility to format all accepted types to the expected format is needed? Or maybe the hypothetical inspect
function discussed above can also cover this use case as well?