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 — str(weakref) throws error `KeyError: '__name__'` if the original obj override __getattr__ · Issue #99184 · python/cpython

str(weakref) throws error KeyError: '__name__' if the original obj override __getattr__ #99184

New issue
Copy link
New issue
Copy link
Closed
Closed
str(weakref) throws error KeyError: '__name__' if the original obj override __getattr__#99184
Copy link
Labels
type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@yanboliang
yanboliang
opened on Nov 7, 2022
Issue body actions

Bug report

A clear and concise description of what the bug is.
Include a minimal, reproducible example (https://stackoverflow.com/help/minimal-reproducible-example), if possible.

Repo:

class MyConfig(dict):
    def __getattr__(self, x):
        return self[x]

obj = MyConfig(offset=5)
obj_weakref = weakref.ref(obj)
str(obj_weakref)  # raise error: KeyError: '__name__'

Error:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __getattr__
KeyError: '__name__'

Your environment

  • CPython versions tested on: python 3.8 & 3.9
  • Operating system and architecture: Ubuntu Linux and MacOS
  • PR: gh-99184: Bypass instance attribute access in repr of weakref.ref #99244
  • PR: [3.11] gh-99184: Bypass instance attribute access in repr of weakref.ref (GH-99244) #103789

Activity

  1. yanboliang
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Nov 7, 2022
  2. sobolevn commented on Nov 7, 2022

    @sobolevn
    sobolevn
    on Nov 7, 2022
    Member
    More actions

    This does not look like a bug to me.
    What do you expect to happen?

    Right now weakrefobject.c has these lines:

    static PyObject *
    weakref_repr(PyWeakReference *self)
    {
        PyObject *name, *repr;
        PyObject* obj = PyWeakref_GET_OBJECT(self);
    
        if (obj == Py_None) {
            return PyUnicode_FromFormat("<weakref at %p; dead>", self);
        }
    
        Py_INCREF(obj);
        if (_PyObject_LookupAttr(obj, &_Py_ID(__name__), &name) < 0) {
            Py_DECREF(obj);
            return NULL;
        }
        // ...

    It tries to get __name__ attribute from the passed object. Since __getattr__ tries to get __name__ from self - it fails with a KeyError. It works exactly as it is written.

    To fix this you can do something like:

    class MyConfig(dict):
        def __getattr__(self, x):
            if x in {'__name__', '__class__', 'whatever_else'}:
                 return getattr(type(self), x)
            return self[x]
  3. sobolevn
    added
    pendingThe issue will be closed if no feedback is providedThe issue will be closed if no feedback is provided
    on Nov 7, 2022
  4. yanboliang commented on Nov 7, 2022

    @yanboliang
    yanboliang
    on Nov 7, 2022
    Author
    More actions

    @sobolevn Thanks for your quick response! I do agree this works well in functionality's perspective, but I'd like to say if this is a good user experience. If users are just trying to override __getattr__ to implement their own functionality(like what I'm doing in the example), it means they should handle a lot of other python internal stuff at well. I think this would sort of increase the complexity, and make users can't focus on their own functionality.

  5. sobolevn commented on Nov 7, 2022

    @sobolevn
    sobolevn
    on Nov 7, 2022
    Member
    More actions

    So, probably you are asking from some kind of fallback for cases like this. Please, correct me if I am wrong :)

    I think that repr() of weakref.ref would have a default value if _PyObject_LookupAttr(obj, &_Py_ID(__name__), &name) fails.

    Example: <weakref at 0x1085b5800; to __some_default__ at 0x7ffd1071c080 (...)>

    However, I don't think this is a good user experience either.
    It is better to see an explicit exception early on than very strange and mostly unreadable repr somewhere in logs.

    Considering your class, it looks problematic to me. It does not respect basic python's API, like: __dict__, __name__, __qualname__, __class__, __module__, etc.

    They are quite basic, many other parts of CPython (and 3rd party projects) do use it.
    And you will have a lot of other failure when these fundamentals cannot be accessed 🤔

    Do you agree with me?

  6. yanboliang commented on Nov 7, 2022

    @yanboliang
    yanboliang
    on Nov 7, 2022
    Author
    More actions

    I can understand your point, if other parts of CPython do the same way, it's fine. But if you take a look at this example:

    >>> class MyConfig(dict):
    ...         def __getattr__(self, x):
    ...             return self[x]
    ... 
    >>> obj = MyConfig(offset=5)
    >>> str(obj)
    "{'offset': 5}"
    

    If users don't user weakref, it works well even they don't handle __dict__, __name__, __qualname__, __class__, __module__, etc. Do you think the inconsistency will confuse users?

  7. MojoVampire commented on Nov 8, 2022

    @MojoVampire
    MojoVampire
    on Nov 8, 2022
    Contributor
    More actions

    If the goal is to look up __name__ on the class of the instance, shouldn't the weakref code be using _PyObject_LookupSpecial (or _PyObject_LookupSpecialId, though I guess those are the same thing now?), not _PyObject_LookupAttr? The only advantage to the latter is if we wanted to allow instances to change their names by overwriting __name__. Using the LookupSpecial API would bypass the instance and avoid this issue (as well as being faster and likely more correct).

    Reacted by Yanbo Liang
  8. sobolevn
    added a commit that references this issue on Nov 8, 2022

    pythongh-99184: Bypass instance attribute access in `repr` of `weakre…

    d0aad14
  9. sobolevn commented on Nov 8, 2022

    @sobolevn
    sobolevn
    on Nov 8, 2022
    Member
    More actions

    @MojoVampire yes, this is a good idea. Thank you! I didn't think about it :)

    I've submitted #99244 with your proposal.

    Reacted by Josh {*()} Rosenberg and Yanbo Liang
  10. sobolevn
    removed
    pendingThe issue will be closed if no feedback is providedThe issue will be closed if no feedback is provided
    on Nov 9, 2022
  11. carljm
    added a commit that references this issue on Apr 24, 2023

    gh-99184: Bypass instance attribute access in `repr` of `weakref.ref` (…

    58b6be3
  12. miss-islington
    added a commit that references this issue on Apr 24, 2023

    pythongh-99184: Bypass instance attribute access in `repr` of `weakre…

    29adef0
  13. carljm
    added a commit that references this issue on Apr 24, 2023

    [3.11] gh-99184: Bypass instance attribute access in `repr` of `weakr…

    c8de883
  14. carljm
    closed this as completedon Apr 24, 2023
  15. carljm
    added 2 commits that reference this issue on Apr 24, 2023

    Merge branch 'main' into superopt

    19b8025

    Merge branch 'superopt' into superopt_spec

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      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.