Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix data descriptor detection in inspect.getattr_static #75367

Closed
davidhalter mannequin opened this issue Aug 11, 2017 · 12 comments
Closed

Fix data descriptor detection in inspect.getattr_static #75367

davidhalter mannequin opened this issue Aug 11, 2017 · 12 comments
Labels
3.11 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@davidhalter
Copy link
Mannequin

davidhalter mannequin commented Aug 11, 2017

BPO 31184
Nosy @rhettinger, @serhiy-storchaka
Superseder
  • bpo-26103: Contradiction in definition of "data descriptor" between (dotted lookup behavior/datamodel documentation) and (inspect lib/descriptor how-to)
  • Files
  • 0001-Fix-data-descriptor-detection-in-inspect.getattr_sta.patch: Potential patch that fixes the issue
  • 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 = None
    closed_at = None
    created_at = <Date 2017-08-11.16:22:11.131>
    labels = ['type-bug', 'library', '3.11']
    title = 'Fix data descriptor detection in inspect.getattr_static'
    updated_at = <Date 2021-12-08.04:22:58.232>
    user = 'https://bugs.python.org/davidhalter'

    bugs.python.org fields:

    activity = <Date 2021-12-08.04:22:58.232>
    actor = 'rhettinger'
    assignee = 'none'
    closed = False
    closed_date = None
    closer = None
    components = ['Library (Lib)']
    creation = <Date 2017-08-11.16:22:11.131>
    creator = 'davidhalter'
    dependencies = []
    files = ['47078']
    hgrepos = []
    issue_num = 31184
    keywords = ['patch']
    message_count = 4.0
    messages = ['300170', '300173', '407976', '407985']
    nosy_count = 3.0
    nosy_names = ['rhettinger', 'serhiy.storchaka', 'davidhalter']
    pr_nums = []
    priority = 'normal'
    resolution = None
    stage = 'resolved'
    status = 'open'
    superseder = '26103'
    type = 'behavior'
    url = 'https://bugs.python.org/issue31184'
    versions = ['Python 3.11']

    Linked PRs

    @davidhalter
    Copy link
    Mannequin Author

    davidhalter mannequin commented Aug 11, 2017

    inspect.getattr_static is currently not identifying data descriptors the right way.

    Data descriptors are defined by having a __get__ attribute and at least one of the __set__ and __delete__ attributes.

    Implementation detail: Both __delete__ and __get__ set the same slot called tp_descr_set in CPython.

    I have attached a patch that fixes the issue IMO.

    @davidhalter davidhalter mannequin added the 3.7 (EOL) end of life label Aug 11, 2017
    @serhiy-storchaka
    Copy link
    Member

    See also bpo-26103.

    @davidhalter
    Copy link
    Mannequin Author

    davidhalter mannequin commented Dec 7, 2021

    This is not a duplicate. It is related to https://bugs.python.org/issue26103, because __get__ is not required anymore for an object to be a data descriptor. The current code on master (of inspect.getattr_static) still thinks a descriptor has both __get__ and __set__ set.

    Since issue bpo-26103 has been fixed, it's now clear that my patch is slightly wrong, but I'm happy to fix that if someone is actually going to review it.

    @davidhalter davidhalter mannequin added 3.8 only security fixes 3.9 only security fixes 3.10 only security fixes 3.11 only security fixes labels Dec 7, 2021
    @davidhalter davidhalter mannequin reopened this Dec 7, 2021
    @rhettinger
    Copy link
    Contributor

    Can you give an example of where getattr_static() is not doing what you expect?

    @rhettinger rhettinger added stdlib Python modules in the Lib dir and removed 3.7 (EOL) end of life 3.8 only security fixes 3.9 only security fixes 3.10 only security fixes labels Dec 7, 2021
    @rhettinger rhettinger self-assigned this Dec 7, 2021
    @rhettinger rhettinger added the type-bug An unexpected behavior, bug, or error label Dec 7, 2021
    @rhettinger rhettinger removed their assignment Dec 8, 2021
    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    @iritkatriel
    Copy link
    Member

    Since issue bpo-26103 has been fixed, it's now clear that my patch is slightly wrong, but I'm happy to fix that if someone is actually going to review it.

    @davidhalter Please do update the patch. And please add a unit test that is currently failing so we can see what bug you are fixing.

    @iritkatriel iritkatriel added the pending The issue will be closed if no feedback is provided label Feb 21, 2023
    @davidhalter
    Copy link

    Thanks for bringing it up again. I unfortunately somehow missed @rhettinger's comment on this issue. Will try to give an example as soon as possible.

    @arhadthedev
    Copy link
    Member

    @davidhalter ping

    @davidhalter
    Copy link

    davidhalter commented Mar 5, 2023

    As soon as possible doesn't mean I have time immediately. It will happen and it's still on my radar. Sorry, but I have been quite busy.

    @davidhalter
    Copy link

    Here's my example:

    import inspect
    
    class DescriptorGet:
        def __get__(self, instance, klass):
            return "Foo"
    
    class DescriptorGetDelete:
        def __get__(self, instance, klass):
            return "Foo"
        def __delete__(self, instance, klass):
            pass
    
    class DescriptorGetSet:
        def __get__(self, instance, klass):
            return "Foo"
        def __set__(self, instance, klass, value):
            pass
    
    class Foo:
        get = DescriptorGet()
        get_delete = DescriptorGetDelete()
        get_set = DescriptorGetSet()
    
        def __init__(self):
            self.__dict__['get'] = 42
            self.__dict__['get_delete'] = 42
            self.__dict__['get_set'] = 42
    
    for attr in ['get', 'get_delete', 'get_set']:
        print(f'getattr        {attr}', getattr(Foo(), attr))
        print(f'getattr_static {attr}', inspect.getattr_static(Foo(), attr))
    

    Output:

    getattr        get 42
    getattr_static get 42
    getattr        get_delete Foo
    getattr_static get_delete 42
    getattr        get_set Foo
    getattr_static get_set <__main__.DescriptorGetSet object at 0x7f88b10ab7f0>
    

    The get_delete case is clearly wrong, because if a normal getattr returns the executed descriptor, we should never get the value of the instance's __dict__ as a result in the getattr_static case.

    @iritkatriel @rhettinger Does this make sense? In case you think it's a bug as well, I'm happy to create a pull request with my patch & a unit test of the __delete__ case.

    @iritkatriel iritkatriel removed the pending The issue will be closed if no feedback is provided label Mar 8, 2023
    @AlexWaygood
    Copy link
    Member

    Thanks @davidhalter. I agree that this looks like a bug. Just to give a slightly shorter reproducer:

    >>> class DescriptorGetDelete:
    ...     def __get__(self, instance, klass):
    ...         return 'foo'
    ...     def __delete__(self, instance, klass): pass
    ...
    >>> class Foo:
    ...     get_delete = DescriptorGetDelete()
    ...     def __init__(self):
    ...         self.__dict__['get_delete'] = 42
    ...
    >>> foo = Foo()
    >>> foo.get_delete
    'foo'
    >>> import inspect
    >>> inspect.getattr_static(foo, 'get_delete')
    42

    The result of the last call should probably be <__main__.DescriptorGetDelete object at 0x000001D9BF998DA0> rather than 42.

    If you file a PR for this issue, feel free to ping me on it -- I'd be happy to review it.

    @carljm
    Copy link
    Member

    carljm commented Apr 6, 2023

    The fix here is simply to also check for __delete__ method, not only __set__ method.

    To be clear, despite the resolution of #70291 (which I think was resolved incorrectly), the check for __get__ method here in getattr_static is correct and should remain. In other words, I think @davidhalter 's original patch here remains the correct fix, despite the resolution of #70291.

    furkanonder added a commit to furkanonder/cpython that referenced this issue May 16, 2023
    furkanonder added a commit to furkanonder/cpython that referenced this issue May 16, 2023
    AlexWaygood pushed a commit that referenced this issue May 16, 2023
    miss-islington pushed a commit to miss-islington/cpython that referenced this issue May 16, 2023
    …ic (pythonGH-104517)
    
    (cherry picked from commit 5e9f471)
    
    Co-authored-by: Furkan Onder <furkanonder@protonmail.com>
    Co-authored-by: Carl Meyer <carl@oddbird.net>
    AlexWaygood pushed a commit that referenced this issue May 16, 2023
    …tic (GH-104517) (#104557)
    
    gh-75367: Fix data descriptor detection in inspect.getattr_static (GH-104517)
    (cherry picked from commit 5e9f471)
    
    Co-authored-by: Furkan Onder <furkanonder@protonmail.com>
    Co-authored-by: Carl Meyer <carl@oddbird.net>
    carljm added a commit to carljm/cpython that referenced this issue May 17, 2023
    * main: (26 commits)
      pythonGH-101520: Move tracemalloc functionality into core, leaving interface in Modules. (python#104508)
      typing: Add more tests for TypeVar (python#104571)
      pythongh-104572: Improve error messages for invalid constructs in PEP 695 contexts (python#104573)
      typing: Use PEP 695 syntax in typing.py (python#104553)
      pythongh-102153: Start stripping C0 control and space chars in `urlsplit` (python#102508)
      pythongh-104469: Update README.txt for _testcapi (pythongh-104529)
      pythonGH-103092: isolate `_elementtree` (python#104561)
      pythongh-104050: Add typing to Argument Clinic converters (python#104547)
      pythonGH-103906: Remove immortal refcounting in the interpreter (pythonGH-103909)
      pythongh-87474: Fix file descriptor leaks in subprocess.Popen (python#96351)
      pythonGH-103092: isolate `pyexpat`  (python#104506)
      pythongh-75367: Fix data descriptor detection in inspect.getattr_static (python#104517)
      pythongh-104050: Add more annotations to `Tools/clinic.py` (python#104544)
      pythongh-104555: Fix isinstance() and issubclass() for runtime-checkable protocols that use PEP 695 (python#104556)
      pythongh-103865: add monitoring support to LOAD_SUPER_ATTR (python#103866)
      CODEOWNERS: Assign new PEP 695 files to myself (python#104551)
      pythonGH-104510: Fix refleaks in `_io` base types (python#104516)
      pythongh-104539: Fix indentation error in logging.config.rst (python#104545)
      pythongh-104050: Don't star-import 'types' in Argument Clinic (python#104543)
      pythongh-104050: Add basic typing to CConverter in clinic.py (python#104538)
      ...
    @furkanonder
    Copy link
    Contributor

    @carljm The issue seems to have been resolved. We can close the issue.

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.11 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    8 participants