PEP 737: gh-111696: Add type.__fully_qualified_name__ attribute#112133
PEP 737: gh-111696: Add type.__fully_qualified_name__ attribute#112133vstinner wants to merge 8 commits intopython:mainfrom
Conversation
498a4d6 to
f9a8c31
Compare
|
Existing logic to format a type name as I found a lot of code like:
stype = self.exc_type.__qualname__
smod = self.exc_type.__module__
if smod not in ("__main__", "builtins"):
if not isinstance(smod, str):
smod = "<unknown>"
stype = smod + '.' + stype
def _type_repr(obj):
...
if isinstance(obj, type):
if obj.__module__ == 'builtins':
return obj.__qualname__
return f'{obj.__module__}.{obj.__qualname__}'
...
def strclass(cls):
return "%s.%s" % (cls.__module__, cls.__qualname__) |
|
I added a second commit using the new |
Doc/c-api/type.rst
Outdated
|
|
||
| .. versionadded:: 3.11 | ||
|
|
||
| .. c:function:: PyObject* PyType_GetFullyQualName(PyTypeObject *type) |
There was a problem hiding this comment.
Why can't it just be GetFullyQualifiedName?
There was a problem hiding this comment.
We already have a PyType_GetQualName() function which returns type.__qualname__: https://docs.python.org/dev/c-api/type.html#c.PyType_GetQualName
I followed the PyType_GetQualName() name: PyType_GetFullyQualName() returns type.__fullyqualname__.
There was a problem hiding this comment.
I know that, but it still tickles my grammar bone. :-(
There was a problem hiding this comment.
Ok, I renamed the function to PyType_GetFullyQualifiedName().
I prefer to keep __fullyqualname__ for the attribute name. We already have __qualname__. And since it's commonly used, I prefer to make it short.
An alternative is to use FQN acronym :-D
type.__fqn__PyType_GetFQN()
But I prefer "longer" names, so I don't need to check the doc to know what they mean ;-)
On the Internet, Domain Name Service (DNS) use FQDN: Fully Qualified Domain Name :-)
There was a problem hiding this comment.
I share Guido's unease with "fullyqualname": it sounds wrong to have the abbreviated "qual" between two unabbreviated words.
For comparison:
- Several dunders, including some of the most recently added ones, include an underscore in the word:
__class_getitem__,__release_buffer__,__type_params__,__init_subclass__,__text_signature__ - Other dunders include multiple words, often abbreviated, without underscores:
__getnewargs__,__kwdefaults__,__isabstractmethod__
I would prefer __fully_qualified_name__. It's on the long side, but it matches the current name for the concept (mentioned in https://docs.python.org/3.12/glossary.html#term-qualified-name). __fqn__ seems too cryptic.
There was a problem hiding this comment.
Ok, I renamed the attribute to __fully_qualified_name__. Does it look better to you?
It's on the long side, but it matches the current name for the concept (mentioned in https://docs.python.org/3.12/glossary.html#term-qualified-name). fqn seems too cryptic.
I agree with you.
Add PyType_GetFullyQualifiedName() function with documentation and tests.
19c3bf6 to
82dd956
Compare
|
Naming is hard. Taking care of myself is harder. I have a lot going on at the moment and I really would prefer not to be responsible for this particular naming decision. Hopefully one or more of the other reviewers can help instead. |
|
IMO, this should use something more customizable than an attribute. A |
| { | ||
| PyObject *tp_name = PyType_GetName(&PyLong_Type); | ||
| assert(strcmp(PyUnicode_AsUTF8(tp_name), "int") == 0); | ||
| assert(PyUnicode_EqualToUTF8(tp_name, "int")); |
There was a problem hiding this comment.
Even though it's still useful, this has nothing to do with the PR title and should go into a separate PR.
There was a problem hiding this comment.
It's related, I changed the 3 tests which check the 3 C functions to get a "type name". I prefer to make the code consistent to make it easier to maintain.
This change also fix a bug: caller must check if PyUnicode_AsUTF8() is NULL. PyUnicode_EqualToUTF8() doesn't have this problem.
Lib/typing.py
Outdated
| if obj.__module__ == 'builtins': | ||
| return obj.__qualname__ | ||
| return f'{obj.__module__}.{obj.__qualname__}' | ||
| return obj.__fullyqualname__ |
There was a problem hiding this comment.
You are changing the contract for the method here. This may result in breaking code relying on previous behavior.
There was a problem hiding this comment.
I think that you misunderstood the API of this attribute. Please check its documentation in Doc/library/stdtypes.rst, or just try my PR.
malemburg
left a comment
There was a problem hiding this comment.
Functional changes should not go into this PR.
| rtn = PyUnicode_FromFormat("<class '%s'>", type->tp_name); | ||
|
|
||
| Py_XDECREF(mod); | ||
| PyObject *result = PyUnicode_FromFormat("<class '%U'>", name); |
There was a problem hiding this comment.
Same comment as above: you are readding "builtins." to builtin types.
There was a problem hiding this comment.
The fully qualified name omits the module if the module is "builtins". There is no behavior change. This code has multiple tests. I'm not sure that I get your comment correctly.
There was a problem hiding this comment.
As you can see from the code, the "builtins." part was deliberately omitted for builtin types.
There was a problem hiding this comment.
I'm not sure of what you are talking about. You are commenting type_repr() function. I didn't change the function output, it's the same:
>>> import builtins
>>> print(repr(builtins.str)) # <=== HERE "builtins." is omitted
<class 'str'>
>>> builtins.str.__module__
'builtins'
>>> builtins.str.__qualname__
'str'
repr() omits the module if it's "builtins". But it's added otherwise:
>>> import collections
>>> print(repr(collections.OrderedDict))
<class 'collections.OrderedDict'>
>>> collections.OrderedDict.__module__
'collections'
>>> collections.OrderedDict.__qualname__
'OrderedDict'
It's the same behavior than before.
He he, I totally get it and it's perfectly fine. Take care. |
This change is not exclusive with extending unittest example from this PR:
is replaced with:
I like the ability to get an attribute rather than having to build a f-string for that. Adding new formats to format a type name in Python has been discussed in length. Most recent to oldest discussions:
Problems:
So my plan is now to add |
Yes. Did any of those discussions reach consensus on adding |
Objects/typeobject.c
Outdated
| PyObject * | ||
| PyType_GetFullyQualifiedName(PyTypeObject *type) | ||
| { | ||
| return type_get_fullyqualname(type, NULL); |
There was a problem hiding this comment.
| return type_get_fullyqualname(type, NULL); | |
| return type_fullyqualname(type, 0); |
No need to call the intermediate function
There was a problem hiding this comment.
I reorganized the calls. The special function with int is_repr parameter now gets a _impl suffix. The getter losts get_ in its name to look alike type_name() and type_qualname() getters. It now calls PyType_GetFullyQualifiedName(). Does it look better to you?
|
@JelleZijlstra: Please review the updated PR. I tried to address all comments of your latest review. |
|
I wrote PEP 737 – Unify type name formatting for these changes: see the PEP discussion. |
Thanks for taking the time to write a PEP. I think it's important that changes to builtin classes like this go through the PEP process :-) |
|
PEP 737 changed the API since this PR was created. I close this PR for now and will create a new one (or maybe reopen this PR) since PEP 737 will be approved. |
|
The Steering Council rejected the |
Add PyType_GetFullyQualName() function with documentation and tests.
📚 Documentation preview 📚: https://cpython-previews--112133.org.readthedocs.build/