Skip to content

Change str(x) to return only the qualname for some types #57433

Closed

Description

@merwok
BPO 13224
Nosy @gvanrossum, @ncoghlan, @pitrou, @vstinner, @rbtcollins, @ezio-melotti, @merwok, @ericsnowcurrently, @vadmium, @serhiy-storchaka, @vedgar
PRs
  • [WIP] bpo-13224 Change str for classes, functions and modules #3608
  • Dependencies
  • bpo-13448: PEP 3155 implementation
  • Files
  • change-some-str.diff
  • test-str-repr.py
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/merwok'
    closed_at = <Date 2017-09-19.16:08:41.864>
    created_at = <Date 2011-10-19.19:44:54.672>
    labels = ['interpreter-core', 'type-feature', '3.7']
    title = 'Change str(x) to return only the qualname for some types'
    updated_at = <Date 2017-09-19.16:27:20.990>
    user = 'https://github.com/merwok'

    bugs.python.org fields:

    activity = <Date 2017-09-19.16:27:20.990>
    actor = 'gvanrossum'
    assignee = 'eric.araujo'
    closed = True
    closed_date = <Date 2017-09-19.16:08:41.864>
    closer = 'eric.araujo'
    components = ['Interpreter Core']
    creation = <Date 2011-10-19.19:44:54.672>
    creator = 'eric.araujo'
    dependencies = ['13448']
    files = ['23771', '23772']
    hgrepos = []
    issue_num = 13224
    keywords = ['patch', 'needs review']
    message_count = 34.0
    messages = ['145946', '145965', '146080', '146216', '146530', '146566', '146844', '146854', '146858', '146938', '147227', '148122', '148129', '148266', '154309', '154360', '154444', '244997', '245011', '245031', '248069', '248088', '248629', '302311', '302316', '302495', '302496', '302499', '302502', '302506', '302510', '302534', '302536', '302539']
    nosy_count = 11.0
    nosy_names = ['gvanrossum', 'ncoghlan', 'pitrou', 'vstinner', 'rbcollins', 'ezio.melotti', 'eric.araujo', 'eric.snow', 'martin.panter', 'serhiy.storchaka', 'veky']
    pr_nums = ['3608']
    priority = 'normal'
    resolution = 'rejected'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue13224'
    versions = ['Python 3.7']

    Activity

    1. self-assigned this
      on Oct 19, 2011
    2. added
      interpreter-core(Objects, Python, Grammar, and Parser dirs)
      type-featureA feature request or enhancement
      on Oct 19, 2011
    3. merwok commented on Oct 19, 2011

      @merwok
      MemberAuthor

      Suggestion by Guido on bpo-868845:

      I sometimes wish that the str() of a class would return the class name
      rather than its repr(); that way "print(str)" would print "str"
      instead of <class 'str'>. (Use case: printing an exception and its
      message: I wish I could print("%s: %s" % (err.__class__, err)) instead
      of having to use err.__class__.__name__.)

      I wrote a simple patch for that. I just copied the definition of type_repr to a new type_str function, edited the format strings and updated the member mapping (I checked in another file than casting to reprfunc is okay for a str func). It compiles and runs just fine, but I’m still learning C, so there may be things I’ve missed: I don’t know if I have to declare the new function or something like that. The test suite passes with very few edits.

      Guido added this:

      One could even claim that the repr() of a class could be the same
      I for one think of repr first as “string form for debugging”, so I like the angle brackets. My patch leaves __repr__ alone.

      If this get approved, I’ll update my patch with doc changes. If there is no feedback I’ll go to python-ideas.

    4. vstinner commented on Oct 19, 2011

      @vstinner
      Member

      It looks like your change breaks backward compatibility (e.g. tests written using doctests). I don't know if it's a problem or not.

    5. merwok commented on Oct 21, 2011

      @merwok
      MemberAuthor

      I would argue that the previous behavior of str(class) was undefined, or an implementation detail; as a new feature, my patch can break some code that relied on the previous behavior, but we may judge think it’s worth the cost.

      BTW, doctest is inherently fragile and broken by many changes, so I’m not too concerned about it.

    6. merwok commented on Oct 23, 2011

      @merwok
      MemberAuthor
    7. gvanrossum commented on Oct 28, 2011

      @gvanrossum
      Member

      What's holding this up?

    8. merwok commented on Oct 28, 2011

      @merwok
      MemberAuthor

      [Guido]

      What's holding this up?

      • I haven’t updated the patch for function and module objects yet
      • I need to catch up with the python-ideas discussion
      • There is at least one strong argument against the idea (I’ll point it out on the ML)
    9. merwok commented on Nov 2, 2011

      @merwok
      MemberAuthor

      I’ve updated my patch to handle modules and functions too, but there is a decision to make. The functions of built-in modules are implemented by methodobject.c, not functionobject.c (that’s for Python functions), so if we want str(sys.exc_info) to be equal to 'exc_info', then we’ll have str(dict.update) == 'update'. Is this okay?

      The patch needs a review.

      • I tried using PyUnicode_FromString(name) instead of PyUnicode_FromFormat("%U", name), just like in Python I would use str(x) instead of '%s' % x, but this caused segfaults. Is there a simpler function to use?

      • I’ve used copy-paste-tweak and checked the results; I’m still learning C and know very little about Python’s types and refcounting internals, so review mercilessly! I forgot to run the tests in findleaks mode, so I’m doing it right now.

    10. gvanrossum commented on Nov 2, 2011

      @gvanrossum
      Member

      On Wed, Nov 2, 2011 at 9:31 AM, Éric Araujo <[email protected]> wrote:

      Éric Araujo <[email protected]> added the comment:

      I’ve updated my patch to handle modules and functions too, but there is a decision to make.  The functions of built-in modules are implemented by methodobject.c, not functionobject.c (that’s for Python functions), so if we want str(sys.exc_info) to be equal to 'exc_info', then we’ll have str(dict.update) == 'update'.  Is this okay?

      Hm, that doesn't seem right. Currently, str(sys.exc_info) says
      <built-in function exc_info> but str(dict.update) is <built-in
      function exc_info>. So the latter definitely knows that it is an
      unbound method! And {}.update is <built-in method update of dict
      object at 0x10040c050> so that knows it is a bound method.

      I'd be okay if str(sys.exc_info) and str({}.update) were unchanged
      from today and if str(dict.update) returned 'dict.update', but if
      that's too complicated I'd also be okay with all three being
      unchanged, thus limiting this to Python functions (and classes and
      modules).

    11. pitrou commented on Nov 2, 2011

      @pitrou
      Member
    12. merwok commented on Nov 3, 2011

      @merwok
      MemberAuthor

      Please also see the proposed PEP-3155

      I’ve seen it and like it. I assume you’re telling that we should consider str(cls/mod/func) to return the qname instead of the name? I think it could be good. My patch can wait for your PEP, or go in first and be updated later if your PEP is accepted.

    13. merwok commented on Nov 7, 2011

      @merwok
      MemberAuthor

      I misreported: dict.update is actually okay, but collections.Counter.update (a Python method) is a not an unbound method but a function (py3k-style).

    14. merwok commented on Nov 22, 2011

      @merwok
      MemberAuthor

      PEP-3155 is accepted and makes str(cls) and str(function) as well as repr(cls) and repr(function) return the qualified name, which obsoletes part of this request. I haven’t checked if it has the same problem with Python methods. str(module) is not changed by the PEP.

    15. 15 remaining items

    16. merwok commented on Sep 16, 2017

      @merwok
      MemberAuthor

      I converted my patch to a PR.

      One use case mentioned on python-ideas was for reprs of PEP-384 types; need to hunt down where typing could benefit from the new str(class).

      The quote in my first message mentions printing err.__class__ rather than err.__class__.__name__; see if tutorial or logging or other docs could be made short and sweet with this change.

      Finally, I don’t remember why I changed functions and modules when the two original use cases were only for classes.

    17. self-assigned this
      on Sep 16, 2017
    18. vedgar commented on Sep 16, 2017

      vedgarmannequin
      Mannequin

      I'm very glad this is moving forward. Yes, types (classes) are most important, functions and modules not so much. The biggest use case for me is easier construction of sane error messages. In many cases, something like f'Got a {type(argument)}' should be enough.

    19. merwok commented on Sep 19, 2017

      @merwok
      MemberAuthor

      Martin Panter

      I’m a bit confused. In the current patch, the new type_str() function
      appears to use the “qualname” (which I support), but some of the test
      changes seem to contradict this, e.g.:

      >>> C.foo(1)
      -classmethod <class 'test.test_descrtut.C'> 1
      +classmethod C 1

      C is a qualname, as would be C.NestedD or something.<locals>.NestedD
      The qualname PEP explains why the module is not part of the qualname.

      Me

      Finally, I don’t remember why I changed functions and modules when the two
      original use cases were only for classes.

      The reason is a message by Nick on python-ideas.

    20. merwok commented on Sep 19, 2017

      @merwok
      MemberAuthor

      (Nick and Guido! «My proposal is to make str(x) return x.__name__ for exactly these three types of objects: modules, classes, and functions.»)

    21. gvanrossum commented on Sep 19, 2017

      @gvanrossum
      Member

      For modules, __name__ is the fully-qualified name, and that's fine.

      But for classes and functions __name__ is just the "given name" from the syntax (whatever came after 'def' or 'class') and that's not fine -- for anything except builtins where we do this I would like the str() to produce the fully-qualified name.

      I presume by "functions" you mean only things defined with 'def' and excluding bound methods?

    22. ncoghlan commented on Sep 19, 2017

      @ncoghlan
      Contributor

      Specifically this message, where the unwritten rationale is to offer behavioural consistency across the builtin types that know their own name and include it in their current repr() output: https://mail.python.org/pipermail/python-ideas/2011-October/012464.html

      As per Guido's comment and the discussion of PEP-3155 above, the idea now is that for classes/functions/methods, we make it so that:

      repr(x) -> gives both the type and the qualified name (as now)
      str(x) -> gives just the qualified name

      Importantly, this *won't* change the result of printing full namespaces, since the dict repr calls repr() on values, not str().

    23. ncoghlan commented on Sep 19, 2017

      @ncoghlan
      Contributor

      That said, bringing over my overall comment from the PR review:

      I think the number of additional changes needed in the test suite and the standard library highlights the compatibility restoration busy-work risks of actually making this change:

      • while test_structures and test_xmlrpc show the kinds of error messages that the change is aimed at improving (and I think it does indeed improve them), it still requires the kind of test case change that is going to be awkward from projects spanning multiple distinct versions of CPython

      • most of the other cases were for code that actually wanted the current formatting, and needed to be updated to explicitly request repr() (either by calling it directly, or by switching to !r or %r in a formatting string)

      Perhaps a less disruptive way of tackling the problem with the verbosity of name access would be to add a new getname() builtin that understood how to get the names of various things (e.g. via __class__), and preferred qualname to name when both were available? Then Guido's original motivating example could be written as:

      print(f"{getname(err)}: {err}") # f-string
      print("{}: {}".format(getname(err), err) # str.format
      print("%s: %s" % (getname(err), err)) # printf-style
      

      As a new builtin, it could be trivially added to cross-version compatibility libraries like six and python-future, whereas directly changing the behaviour of __str__ on the affected builtin types isn't really something that can be readily emulated on old versions.

    24. vedgar commented on Sep 19, 2017

      vedgarmannequin
      Mannequin

      getname sounds good, but is in fact very confusing. In most cases, what you'd actually want, and end up writing, is getname(type(x)), but we can't make it the default since for classes we would actually like x.name directly. It seems you say the same with "get the names of various things (e.g. via __class__)", but the interface is very complicated if we try to guess what people meant, and clumsy if we force people to write type almost all the time.

      The point is, we want to be explicit about whether we are speaking about x or type(x). Everything else is simply presentation details. type does it perfectly well, only the presentation details are canonically handled via str and repr. And it's perfectly natural, BTW. Imagine if str(5) gave you '<integer 5>'. :-o

    25. gvanrossum commented on Sep 19, 2017

      @gvanrossum
      Member

      Given Nick's feedback (and imagining what this would do to some codebases I
      know) I think this idea is dead, sadly. A helper function just doesn't give
      enough value, so let's not pursue that (it's easy to write the helper you
      want, it's hard to agree on what helper everybody should want).

    26. merwok commented on Sep 19, 2017

      @merwok
      MemberAuthor

      Yeah, it was interesting to explore but there are significant drawbacks and not enough benefit. Thanks for the guidance all!

    27. gvanrossum commented on Sep 19, 2017

      @gvanrossum
      Member

      And thanks for working it through! It was a valuable exercise.

    28. transferred this issue fromon Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    Labels

    3.7 (EOL)end of lifeinterpreter-core(Objects, Python, Grammar, and Parser dirs)type-featureA feature request or enhancement

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions