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

os.PathLike subclasshook causes subclass checks true on abstract implementation #83059

Closed
bharel mannequin opened this issue Nov 21, 2019 · 13 comments
Closed

os.PathLike subclasshook causes subclass checks true on abstract implementation #83059

bharel mannequin opened this issue Nov 21, 2019 · 13 comments
Labels
3.7 (EOL) end of life 3.8 only security fixes 3.9 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@bharel
Copy link
Mannequin

bharel mannequin commented Nov 21, 2019

BPO 38878
Nosy @ilevkivskyi, @bharel, @ammaraskar, @tirkarthi
PRs
  • bpo-38878: Fix os.PathLike __subclasshook__ #17336
  • [3.8] bpo-38878: Fix os.PathLike __subclasshook__ (GH-17336) #17684
  • [3.7] bpo-38878: Fix os.PathLike __subclasshook__ (GH-17336) #17685
  • 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 = <Date 2019-12-23.18:32:01.175>
    created_at = <Date 2019-11-21.15:00:21.484>
    labels = ['3.7', '3.8', 'type-bug', 'library', '3.9']
    title = 'os.PathLike subclasshook causes subclass checks true on abstract implementation'
    updated_at = <Date 2019-12-23.18:32:01.174>
    user = 'https://github.com/bharel'

    bugs.python.org fields:

    activity = <Date 2019-12-23.18:32:01.174>
    actor = 'levkivskyi'
    assignee = 'none'
    closed = True
    closed_date = <Date 2019-12-23.18:32:01.175>
    closer = 'levkivskyi'
    components = ['Library (Lib)']
    creation = <Date 2019-11-21.15:00:21.484>
    creator = 'bar.harel'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 38878
    keywords = ['patch']
    message_count = 13.0
    messages = ['357174', '357205', '357209', '357213', '357326', '357342', '357354', '357356', '357367', '357776', '358787', '358828', '358829']
    nosy_count = 4.0
    nosy_names = ['levkivskyi', 'bar.harel', 'ammar2', 'xtreak']
    pr_nums = ['17336', '17684', '17685']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue38878'
    versions = ['Python 3.7', 'Python 3.8', 'Python 3.9']

    @bharel
    Copy link
    Mannequin Author

    bharel mannequin commented Nov 21, 2019

    Quick and small fix.

    os.PathLike.__subclasshook__ does not check if cls is PathLike as abstract classes should.

    This in turn causes this bug:

        class A(PathLike):
            pass
    
        class B:
            def __fspath__(self):
                pass
    assert issubclass(B, A)
    

    I will fix the bug later today and push a patch over to python/cpython on GitHub.

    @bharel bharel mannequin added 3.7 (EOL) end of life 3.8 only security fixes 3.9 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error labels Nov 21, 2019
    @brettcannon
    Copy link
    Member

    I can't reproduce in Python 3.8.0:

    >>> import os
    >>> class A(os.PathLike): pass
    ...
    >>> class B:
    ...     def __fspath__(self): pass
    ...
    >>> issubclass(B, A)
    True

    Did you check against an older version of Python?

    @bharel
    Copy link
    Mannequin Author

    bharel mannequin commented Nov 21, 2019

    Hey Brett, that's exactly the bug. It's supposed to be False ofc.

    On Thu, Nov 21, 2019, 9:45 PM Brett Cannon <report@bugs.python.org> wrote:

    Brett Cannon <brett@python.org> added the comment:

    I can't reproduce in Python 3.8.0:

    >>> import os
    >>> class A(os.PathLike): pass
    ...
    >>> class B:
    ... def __fspath__(self): pass
    ...
    >>> issubclass(B, A)
    True

    Did you check against an older version of Python?

    ----------
    resolution: -> not a bug
    stage: -> resolved
    status: open -> closed


    Python tracker <report@bugs.python.org>
    <https://bugs.python.org/issue38878\>


    @brettcannon
    Copy link
    Member

    Ah, your assert call threw me since it does succeed so it isn't acting as a test case.

    @bharel
    Copy link
    Mannequin Author

    bharel mannequin commented Nov 22, 2019

    Done.

    On Fri, Nov 22, 2019, 12:23 PM Bar Harel <report@bugs.python.org> wrote:

    Change by Bar Harel <bzvi7919@gmail.com>:

    ----------
    keywords: +patch
    pull_requests: +16820
    stage: resolved -> patch review
    pull_request: #17336


    Python tracker <report@bugs.python.org>
    <https://bugs.python.org/issue38878\>


    @brettcannon
    Copy link
    Member

    I just realized one problem with this is it explicitly requires subclassing the ABC while os.PathLike is supposed to represent a protocol (before typing.Protocol was a thing).

    So why is it bad that in the example class B is considered a "subclass" of os.PathLike by implementing the protocol? Since it implements the expected protocol what exactly is being lost by not checking for an explicit registration or subclass?

    @ilevkivskyi
    Copy link
    Member

    So why is it bad that in the example class B is considered a "subclass" of os.PathLike by implementing the protocol?

    This is not bad, my understanding of the problem is that currently B is considered a subclass of A, while the latter should not be structural.

    To give an analogy with PEP-544 (sorry, this is my favourite one :-)) consider this:

    class P(Protocol):
        def some(self): ...
    
    class C:
        def some(self): ...

    Here C is obviously a "subclass" of P, but:

    class Bad(P):  # <- this is _no_ a protocol, just a nominal class
        pass       # explicitly subclassing P
    
    class Good(P, Protocol):  # <- this is a subprotocol that
        pass                  # happened to be identical to P

    So here C is a "subclass" of Good, but not a "subclass" of Bad.

    @ammaraskar
    Copy link
    Member

    Just for reference/existing behavior:

    >>> class A(collections.abc.Iterable): pass
    ...
    >>> class B:
    ...   def __iter__(): pass
    ...
    >>> issubclass(B, A)
    False
    >>> issubclass(B, collections.abc.Iterable)
    True
    >>> issubclass(A, collections.abc.Iterable)
    True

    @bharel
    Copy link
    Mannequin Author

    bharel mannequin commented Nov 23, 2019

    A better example is this:

        class A(PathLike):
            def foo(self):
                """For all your foo needs"""
    
        class B:
            def __fspath__(self):
                pass
    issubclass(B, A) == True
    A().foo() # Yay, I Foo'd.
    B().foo() # oh barnacles, I should have stayed at home.
    

    @bharel
    Copy link
    Mannequin Author

    bharel mannequin commented Dec 4, 2019

    Ready for merge

    @ilevkivskyi
    Copy link
    Member

    New changeset eae87e3 by Ivan Levkivskyi (Bar Harel) in branch 'master':
    bpo-38878: Fix os.PathLike __subclasshook__ (GH-17336)
    eae87e3

    @ilevkivskyi
    Copy link
    Member

    New changeset 0846e5d by Ivan Levkivskyi (Bar Harel) in branch '3.8':
    [3.8] bpo-38878: Fix os.PathLike __subclasshook__ (GH-17336) (GH-17684)
    0846e5d

    @ilevkivskyi
    Copy link
    Member

    New changeset 59d06b9 by Ivan Levkivskyi (Bar Harel) in branch '3.7':
    [3.7] bpo-38878: Fix os.PathLike __subclasshook__ (GH-17336) (GH-17685)
    59d06b9

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.7 (EOL) end of life 3.8 only security fixes 3.9 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

    3 participants