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

extend Document.__getitem__ type annotation to reflect that the method also accepts slices #3706

Closed
Sorontik opened this issue Jul 19, 2024 · 1 comment

Comments

@Sorontik
Copy link

Problem
The type annotation of Document.__getitem__ is wrong:

def __getitem__(self, i: int =0):
        if isinstance(i, slice):

The type annotation of i requires it to be an int, but the implementation is obviously meant to also accept a slice (and works fine with slices)

Solution
extend the signature to a Union and add overloads to tell typecheckers which return values correspond to which argument types:

    @typing.overload
    def __getitem__(self, i: int) -> Page: ...
    
    @typing.overload
    def __getitem__(self, i: slice) -> list[Page]: ...

    def __getitem__(self, i: typing.Union[int, slice] =0) -> typing.Union[Page, list[Page]]:
        if isinstance(i, slice):
            return [self[j] for j in range(*i.indices(len(self)))]
        assert isinstance(i, int) or (isinstance(i, tuple) and len(i) == 2 and all(isinstance(x, int) for x in i)), \
                f'Invalid item number: {i=}.'
        if i not in self:
            raise IndexError(f"page {i} not in document")
        return self.load_page(i)

that way it reflects the reality of which argument types the methods actually accepts and which return value types they produce.
Note that the actual implementation remains unchanged besides the extension of the type annotation

Thanks to the overload for slices, typecheckers also correctly recognize, that the result of indexing with a slice is iterable
(Which is not the case if you just add the Unions to the signature, then the typechecker would complain that a Page is not iterable)

Alternatives
Ignore the problem until it is solved by the complete rework of type hints as mentioned by julian-smith-artifex-com:

We are looking at possible architectural changes that will allow better type hints in future. But it's quite an involved process so there are no timescales at the moment.
#3361 (comment)

but since there is no ETA for the overhaul and since this fix is so quick and straightforward, i believe it would still be worth it to implement it now

compatibility concerns

  • typing.Union is already used repeatedly in the module
  • typing.overload has not been used previously, but was introduced alongside typing.Union in python 3.5
  • overloading should be fully transparent at runtime
  • no functional changes are made to the actual implementation

therefore i don't see any compatibility concerns

julian-smith-artifex-com added a commit that referenced this issue Aug 5, 2024
Improve typing using @typing.overload - we accept `page`, `(chapter, page)` or
`slice`.

This addresses #3706.
julian-smith-artifex-com added a commit that referenced this issue Aug 5, 2024
Improve typing using @typing.overload - we accept `page`, `(chapter, page)` or
`slice`.

This addresses #3706.
julian-smith-artifex-com added a commit that referenced this issue Aug 5, 2024
Improve typing using @typing.overload - we accept `page`, `(chapter, page)` or
`slice`.

This addresses #3706.
@julian-smith-artifex-com
Copy link
Collaborator

Fixed in 1.24.10.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants