Skip to content

Safer attribute suggestion for classes with custom "bad" __dir__ #139933

Closed

Description

@Locked-chess-official

Feature or enhancement

Proposal:

Now python suggest for attribute possible in this way:

        obj = exc_value.obj
        try:
            try:
                d = dir(obj)
            except TypeError:  # Attributes are unsortable, e.g. int and str
                d = list(obj.__class__.__dict__.keys()) + list(obj.__dict__.keys())
            d = sorted([x for x in d if isinstance(x, str)])
            hide_underscored = (wrong_name[:1] != '_')
            if hide_underscored and tb is not None:
                while tb.tb_next is not None:
                    tb = tb.tb_next
                frame = tb.tb_frame
                if 'self' in frame.f_locals and frame.f_locals['self'] is obj:
                    hide_underscored = False
            if hide_underscored:
                d = [x for x in d if x[:1] != '_']
        except Exception:
            return None

However, the obj may has no attribute "__dict__" if it has the attribute "__slots__". So list(obj.__class__.__dict__.keys()) + list(obj.__dict__.keys()) is not safe. And the class must have the attribute "__name__" that the object of the class cannot get.
In fact, the object.__dir__ is safer, it can exactly find all of the attribute that can be get with object.__getattribute__. So I think that we can change the way by using this way.

Has this already been discussed elsewhere?

No response given

Links to previous discussion of this feature:

No response

Linked PRs

Activity

  1. changed the title [-]Use safer way "object.__dir__"[/-] [+]`traceback.py`: Suggestion for `AttributeError` suggestion mechanism[/+] on Oct 11, 2025
  2. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Oct 11, 2025
  3. StanFromIreland commented on Oct 11, 2025

    @StanFromIreland
    Member

    Please provide context, both in the title and issue body, I had to go grep the lines you provided.

  4. Locked-chess-official commented on Oct 11, 2025

    @Locked-chess-official
    ContributorAuthor

    Please provide context, both in the title and issue body, I had to go grep the lines you provided.

    traceback.py at line 1635-1653

  5. StanFromIreland commented on Oct 11, 2025

    @StanFromIreland
    Member

    Looking at the discussion on the recent PR which added these lines, #131001, the current approach was preferred.

  6. Locked-chess-official commented on Oct 11, 2025

    @Locked-chess-official
    ContributorAuthor

    Looking at the discussion on the recent PR which added these lines, #131001, the current approach was preferred.

    Yes. However, object.__dir__ is unchangable and it won't sort the list, so it cannot raise TypeError:

    >>> class MyClass:
    ...     pass
    ...
    >>> a = MyClass()
    >>> a.__dict__[1] = 1
    >>> object.__dir__(a)
    [1, '__module__', '__firstlineno__', '__static_attributes__', '__dict__', '__weakref__', '__doc__', '__new__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__init__', '__reduce_ex__', '__reduce__', '__getstate__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']
    

    And then d = sorted([x for x in d if isinstance(x, str)]) make sure that the 1 in the list will be ignore.
    This way is more powerful.

  7. changed the title [-]`traceback.py`: Suggestion for `AttributeError` suggestion mechanism[/-] [+]Safer attribute suggestion for a slotted class with custom `__dir__`[/+] on Oct 11, 2025
  8. added
    type-bugAn unexpected behavior, bug, or error
    and removed
    type-featureA feature request or enhancement
    on Oct 11, 2025
  9. self-assigned this
    on Oct 11, 2025
  10. picnixz commented on Oct 11, 2025

    @picnixz
    Member

    There is indeed an "issue":

    class A:
        __slots__ = ()
        def __dir__(self):
            return [1, "abc"]

    If you have:

    >>> a = A()
    >>> a.<TAB>

    then there won't be any completion suggestion because we're catching the AttributeError and returning None directly. I'm considering this as a bug because we're not trying hard enough here to get the attributes. OTOH, I need to check if object.__dir__ is safe to call (that is, if this wouldn't trigger side-effects).

  11. changed the title [-]Safer attribute suggestion for a slotted class with custom `__dir__`[/-] [+]Safer attribute suggestion for classes with custom "bad" `__dir__`[/+] on Oct 11, 2025
  12. Locked-chess-official commented on Oct 11, 2025

    @Locked-chess-official
    ContributorAuthor

    It seems that it is normal with object.__dir__:

    >>> class A:
    ...     __slots__ = ()
    ...     def __dir__(self):
    ...         return [1, "abc"]
    ...
    >>> a = A()
    >>> object.__dir__(a)
    ['__module__', '__firstlineno__', '__slots__', '__dir__', '__static_attributes__', '__doc__', '__new__', '__repr__', '__hash__', '__str__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__init__', '__reduce_ex__', '__reduce__', '__getstate__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__class__']
    
  13. picnixz commented on Oct 11, 2025

    @picnixz
    Member

    It's not just for this specific case. I need to know what happens in C for that. Now, I've confirmed that object.__dir__ is simply the default dir() so nothing fancy should happen. However, what is concerning is that if dir() is failing, then object.__dir__(x) has no meaning because a failing dir() means a custom dir(), which in turn means that the default one is not good.

    Therefore, I think we should just give up on using __dict__ here and only rely on the class attributes.

  14. Locked-chess-official commented on Oct 11, 2025

    @Locked-chess-official
    ContributorAuthor

    And then I will test for this object:

    #include <nanobind/nanobind.h>
    #include <nanobind/stl/string.h>
    #include <nanobind/stl/vector.h>
    #include <nanobind/stl/shared_ptr.h>
    #include <nanobind/stl/map.h>
    #include <nanobind/stl/unordered_set.h>
    
    class NonPyObject {
    };
    
    class TargetObject {
    public:
        TargetObject(int a, int b): a(a), b(b) {
            
        }
        int add() {
            return a+b;
        }
    private:
        int a;
        int b;
    };
    
    namespace nb = nanobind;
    NB_MODULE(targetobject, m) {
        nb::class_<TargetObject>(m, "TargetObject")
            .def(nb::init<int, int>())
            .def("add", &TargetObject::add)
            .def("__dir__", [](const TargetObject &self){
                return NonPyObject();
        });
    };
  15. Locked-chess-official commented on Oct 11, 2025

    @Locked-chess-official
    ContributorAuthor

    It's not just for this specific case. I need to know what happens in C for that. Now, I've confirmed that object.__dir__ is simply the default dir() so nothing fancy should happen. However, what is concerning is that if dir() is failing, then object.__dir__(x) has no meaning because a failing dir() means a custom dir(), which in turn means that the default one is not good.

    Therefore, I think we should just give up on using __dict__ here and only rely on the class attributes.

    The object.__dir__ won't sort the result. This is the biggest difference.

  16. picnixz commented on Oct 11, 2025

    @picnixz
    Member

    It doesn't matter. As you can see, object.__dir__(a) is lacking "abc". In case object.__dir__(a) actually outputs something that was not meant to be returned by the custom __dir__, it will be worse. So relying on the default behavior of __dir__ is not really a better idea IMO. I prefer to have missing suggestions rather than having incorrect ones.

  17. Locked-chess-official commented on Oct 11, 2025

    @Locked-chess-official
    ContributorAuthor

    In fact, a.__dir__() is also unsorted. So, we can try to just call it. What will sort the result is only dir(), unless the code sorted the result before in __dir__

  18. picnixz commented on Oct 11, 2025

    @picnixz
    Member

    Oh, I thought there was some callback for the __dir__ method. Ok, yes, let's just call __dir__. Thanks for that.

  19. added a commit that references this issue on Mar 11, 2026
  20. added 4 commits that reference this issue on Mar 11, 2026
  21. added a commit that references this issue on Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

stdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions