GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — PEP 737: gh-111696: Add type.__fully_qualified_name__ attribute by vstinner · Pull Request #112133 · python/cpython

Conversation

@vstinner

@vstinner vstinner commented Nov 15, 2023 •
edited by github-actions Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

Add PyType_GetFullyQualName() function with documentation and tests.

  • Issue: PEP 737: PyUnicode_FromFormat(): Add %T format to format the type name of an object #111696

📚 Documentation preview 📚: https://cpython-previews--112133.org.readthedocs.build/

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vstinner
vstinner requested a review from markshannon as a code owner November 15, 2023 22:42
@bedevere-app bedevere-app Bot added the awaiting core review label Nov 15, 2023
@bedevere-app bedevere-app Bot mentioned this pull request Nov 15, 2023
PEP 737: PyUnicode_FromFormat(): Add %T format to format the type name of an object #111696
Closed
@vstinner
vstinner force-pushed the type_fullyqualname branch from 498a4d6 to f9a8c31 Compare November 15, 2023 22:44
@vstinner

vstinner commented Nov 16, 2023

Copy link
Copy Markdown
Member Author

Existing logic to format a type name as type.__fullyqualname__ does.

I found a lot of code like:

  • name = "%s.%s" % (obj.__module__, obj.__qualname__)
  • __repr__() method: cls = self.__class__; return f"<{cls.__module__}.{cls.__qualname__} at ...}"

traceback: only code that I found which treats the __main__ module differently:

        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

_collections_abc: _type_repr() function

def _type_repr(obj):
    ...
    if isinstance(obj, type):
        if obj.__module__ == 'builtins':
            return obj.__qualname__
        return f'{obj.__module__}.{obj.__qualname__}'
    ...

inspect: omit base_module if specified

def formatannotation(annotation, base_module=None):
    ...
    if isinstance(annotation, type):
        if annotation.__module__ in ('builtins', base_module):
            return annotation.__qualname__
        return annotation.__module__+'.'+annotation.__qualname__
    ...

unittest:

def strclass(cls):
    return "%s.%s" % (cls.__module__, cls.__qualname__)
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
vstinner requested review from 1st1, AlexWaygood, JelleZijlstra, brettcannon, ericsnowcurrently, gvanrossum, iritkatriel, ncoghlan, rhettinger and warsaw as code owners November 16, 2023 02:12
@vstinner

vstinner commented Nov 16, 2023

Copy link
Copy Markdown
Member Author

I added a second commit using the new type.__fullyqualname__ attribute in the stdlib to show how it can be used. Even if the attribute is not used to add new "%T" and "%#T" formats to PyUnicode_FromFormat(), this change is useful.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

gvanrossum
gvanrossum reviewed Nov 16, 2023
View reviewed changes
Comment thread Doc/c-api/type.rst Outdated

.. versionadded:: 3.11

.. c:function:: PyObject* PyType_GetFullyQualName(PyTypeObject *type)

@gvanrossum gvanrossum Nov 16, 2023

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't it just be GetFullyQualifiedName?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@vstinner vstinner Nov 16, 2023

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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__.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@gvanrossum gvanrossum Nov 16, 2023

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that, but it still tickles my grammar bone. :-(

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@vstinner vstinner Nov 16, 2023

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 :-)

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@JelleZijlstra JelleZijlstra Nov 16, 2023

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

AlexWaygood reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

@AlexWaygood AlexWaygood Nov 16, 2023

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I concur with Jelle.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@vstinner vstinner Nov 16, 2023

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
vstinner added 2 commits November 16, 2023 05:34
@vstinner
pythongh-111696: Add type.__fullyqualname__ attribute
d5e8a37
Add PyType_GetFullyQualifiedName() function with documentation and
tests.
@vstinner
Use type.__fullyqualname__ attribute
82dd956
@vstinner
vstinner force-pushed the type_fullyqualname branch from 19c3bf6 to 82dd956 Compare November 16, 2023 04:35
@gvanrossum

gvanrossum commented Nov 16, 2023

Copy link
Copy Markdown
Member

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou

encukou commented Nov 16, 2023

Copy link
Copy Markdown
Member

IMO, this should use something more customizable than an attribute. A format() directive for example.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

malemburg
malemburg reviewed Nov 16, 2023
View reviewed changes
Comment thread Modules/_testcapimodule.c
{
PyObject *tp_name = PyType_GetName(&PyLong_Type);
assert(strcmp(PyUnicode_AsUTF8(tp_name), "int") == 0);
assert(PyUnicode_EqualToUTF8(tp_name, "int"));

@malemburg malemburg Nov 16, 2023

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though it's still useful, this has nothing to do with the PR title and should go into a separate PR.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@vstinner vstinner Nov 16, 2023

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
malemburg
malemburg reviewed Nov 16, 2023
View reviewed changes
Comment thread Lib/typing.py Outdated
if obj.__module__ == 'builtins':
return obj.__qualname__
return f'{obj.__module__}.{obj.__qualname__}'
return obj.__fullyqualname__

@malemburg malemburg Nov 16, 2023

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are changing the contract for the method here. This may result in breaking code relying on previous behavior.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@vstinner vstinner Nov 16, 2023

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that you misunderstood the API of this attribute. Please check its documentation in Doc/library/stdtypes.rst, or just try my PR.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
malemburg
malemburg reviewed Nov 16, 2023
View reviewed changes

@malemburg malemburg left a comment

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Functional changes should not go into this PR.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread Objects/typeobject.c
rtn = PyUnicode_FromFormat("<class '%s'>", type->tp_name);

Py_XDECREF(mod);
PyObject *result = PyUnicode_FromFormat("<class '%U'>", name);

@malemburg malemburg Nov 16, 2023

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above: you are readding "builtins." to builtin types.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@vstinner vstinner Nov 16, 2023 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@malemburg malemburg Nov 16, 2023

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you can see from the code, the "builtins." part was deliberately omitted for builtin types.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@vstinner vstinner Nov 16, 2023

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vstinner
Rename the attribute to __fully_qualified_name__
76dcfd5
@vstinner

vstinner commented Nov 16, 2023

Copy link
Copy Markdown
Member Author

@gvanrossum:

Naming is hard. Taking care of myself is harder.

He he, I totally get it and it's perfectly fine. Take care.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Nov 16, 2023

Copy link
Copy Markdown
Member Author

@encukou:

IMO, this should use something more customizable than an attribute. A format() directive for example.

This change is not exclusive with extending type.__format__() to get new formats. Moreover, if we add a format to get the fully qualified name of a type, I would prefer to have a more "regular" way to get it instead of having to go through formatting.

unittest example from this PR:

def strclass(cls): return "%s.%s" % (cls.__module__, cls.__qualname__)

is replaced with:

def strclass(cls): return cls.__fully_qualified_name__

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:

  • https://discuss.python.org/t/enhance-type-name-formatting-when-raising-an-exception-add-t-format-in-c-and-add-type-fullyqualname/38129
  • PEP 737: PyUnicode_FromFormat(): Add %T format to format the type name of an object #111696
  • https://mail.python.org/archives/list/[email protected]/thread/HKYUMTVHNBVB5LJNRMZ7TPUQKGKAERCJ/#3UAMHYG6UF4MPLXBZORHO4JVKUBRUZ53
  • PyUnicode_FromFormat(): add %T format for an object type name #78776

Problems:

  • I'm not convinced that there is a strong need for formats other than type.__name__ and type.__fully_qualified_name__. I prefer to get an attribute easy to read and understand without having to check the documentation, rather than using a single letter format such as {type|T}
  • So far, I didn't see a clear consensus on an API for such formatting.

So my plan is now to add type.__fully_qualified_name__ attribute, add %T (type.__name__) and %#T (type.__fully_qualified_name__) to PyUnicode_FromFormat() in C, use these new APIs, and then see if more APIs should be added.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou

encukou commented Nov 16, 2023

Copy link
Copy Markdown
Member

Adding new formats to format a type name in Python has been discussed in length.

Yes. Did any of those discussions reach consensus on adding __fully_qualified_name__ with this implementation?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner vstinner changed the title gh-111696: Add type.__fullyqualname__ attribute gh-111696: Add type.__fully_qualified_name__ attribute Nov 16, 2023
JelleZijlstra
JelleZijlstra reviewed Nov 17, 2023
View reviewed changes
Comment thread Doc/library/stdtypes.rst Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Lib/test/test_builtin.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Modules/_testcapimodule.c Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread Objects/typeobject.c Outdated
PyObject *
PyType_GetFullyQualifiedName(PyTypeObject *type)
{
return type_get_fullyqualname(type, NULL);

@JelleZijlstra JelleZijlstra Nov 17, 2023

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return type_get_fullyqualname(type, NULL);
return type_fullyqualname(type, 0);

No need to call the intermediate function

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@vstinner vstinner Nov 17, 2023

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vstinner
Doc: Add "fully qualified name" term to the glossary
36cf2a9
@vstinner
vstinner requested review from a team and vsajip as code owners November 17, 2023 21:35
vstinner added 3 commits November 17, 2023 22:44
@vstinner
Add tests on more types: built-in and types
266a8e5
@vstinner
Fix PyType_FromSpec() error handling
e63c937
@vstinner
type.__fully_qualified_name__ getter calls PyType_GetFullyQualifiedNa…
c79c519
…me()
@vstinner

vstinner commented Nov 17, 2023

Copy link
Copy Markdown
Member Author

@JelleZijlstra: Please review the updated PR. I tried to address all comments of your latest review.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner
Fix typo in the doc
46fb6df
@rhettinger
rhettinger removed their request for review November 23, 2023 19:44
@vstinner

vstinner commented Nov 29, 2023

Copy link
Copy Markdown
Member Author

I wrote PEP 737 – Unify type name formatting for these changes: see the PEP discussion.

AlexWaygood reacted with heart emoji
All reactions
  • ❤️ 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@AlexWaygood

AlexWaygood commented Nov 29, 2023

Copy link
Copy Markdown
Member

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 :-)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner vstinner changed the title gh-111696: Add type.__fully_qualified_name__ attribute PEP 737: gh-111696: Add type.__fully_qualified_name__ attribute Dec 1, 2023
@AlexWaygood
AlexWaygood removed their request for review December 13, 2023 12:52
@vstinner

vstinner commented Dec 20, 2023

Copy link
Copy Markdown
Member Author

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.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner vstinner closed this Dec 20, 2023
@vstinner
vstinner deleted the type_fullyqualname branch December 20, 2023 10:56
@vstinner

vstinner commented Mar 14, 2024

Copy link
Copy Markdown
Member Author

The Steering Council rejected the type.__fully_qualified_name__ attribute: see https://peps.python.org/pep-0737/#id1 for details. Thomas wrote: "I think the SC could be persuaded given some concrete use-cases."

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@JelleZijlstra JelleZijlstra JelleZijlstra left review comments
@gvanrossum gvanrossum gvanrossum left review comments
@malemburg malemburg malemburg left review comments
@AlexWaygood AlexWaygood AlexWaygood left review comments
@markshannon markshannon Awaiting requested review from markshannon markshannon is a code owner
@brettcannon brettcannon Awaiting requested review from brettcannon brettcannon is a code owner
@ericsnowcurrently ericsnowcurrently Awaiting requested review from ericsnowcurrently ericsnowcurrently is a code owner
@ncoghlan ncoghlan Awaiting requested review from ncoghlan ncoghlan is a code owner
@warsaw warsaw Awaiting requested review from warsaw warsaw is a code owner
@iritkatriel iritkatriel Awaiting requested review from iritkatriel
@1st1 1st1 Awaiting requested review from 1st1
@vsajip vsajip Awaiting requested review from vsajip vsajip is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

awaiting core review

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

6 participants

@vstinner @gvanrossum @encukou @AlexWaygood @JelleZijlstra @malemburg
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.