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

Add type hints #10

Merged
merged 17 commits into from
Feb 7, 2024
Merged

Add type hints #10

merged 17 commits into from
Feb 7, 2024

Conversation

bswck
Copy link
Contributor

@bswck bswck commented Nov 13, 2023

Closes #9.

@bswck bswck changed the title Add type hints to jaraco.classes.meta Add type hints Nov 13, 2023
@bswck bswck marked this pull request as ready for review November 13, 2023 13:28
@bswck
Copy link
Contributor Author

bswck commented Nov 13, 2023

I didn't look into dependencies. Please let me know how to set up the typing_extensions typing-only dependency properly in the project.

As for now, the getter's return type can't be determined. It's better to leave the return type as `Any` so that one could narrow down the getter's return type to their needs.
@bswck
Copy link
Contributor Author

bswck commented Nov 14, 2023

Ad e9788e8, please take a look at jaraco/skeleton#97.

@bswck
Copy link
Contributor Author

bswck commented Nov 14, 2023

Ad 304e455, I thought of making the classproperty class generic, but as I can see in the newest typeshed's stub for built-ins, the property class is non-generic and defines .fget's return type as Any.

@bswck
Copy link
Contributor Author

bswck commented Nov 15, 2023

This PR is part of jaraco/skeleton#98.

@bswck
Copy link
Contributor Author

bswck commented Feb 7, 2024

@jaraco waitin' for review! 🚀
Ready to adopt jaraco/skeleton#97 after.

jaraco/classes/properties.py Outdated Show resolved Hide resolved
@jaraco
Copy link
Owner

jaraco commented Feb 7, 2024

I don't understand why tests are failing with "ruff: file would be formatted". The ruff formatter isn't yet enabled... or is it. Okay, yes, pytest-enabler 3.0.0 does add --ruff-format to the test config. So yes, the formatting needs to be applied.

@jaraco
Copy link
Owner

jaraco commented Feb 7, 2024

Doc builds are failing with:

/home/runner/work/jaraco.classes/jaraco.classes/jaraco/classes/properties.py:docstring of jaraco.classes.properties.classproperty:1: WARNING: py:class reference target not found: _CallableGetter
/home/runner/work/jaraco.classes/jaraco.classes/jaraco/classes/properties.py:docstring of jaraco.classes.properties.classproperty:1: WARNING: py:class reference target not found: _ClassMethodGetter
/home/runner/work/jaraco.classes/jaraco.classes/jaraco/classes/properties.py:docstring of jaraco.classes.properties.classproperty:1: WARNING: py:class reference target not found: _StaticMethodGetter
/home/runner/work/jaraco.classes/jaraco.classes/jaraco/classes/properties.py:docstring of jaraco.classes.properties.classproperty:1: WARNING: py:class reference target not found: _CallableSetter
/home/runner/work/jaraco.classes/jaraco.classes/jaraco/classes/properties.py:docstring of jaraco.classes.properties.classproperty:1: WARNING: py:class reference target not found: _ClassMethodSetter
/home/runner/work/jaraco.classes/jaraco.classes/jaraco/classes/properties.py:docstring of jaraco.classes.properties.classproperty.fget:1: WARNING: py:class reference target not found: _ClassMethodGetter
/home/runner/work/jaraco.classes/jaraco.classes/jaraco/classes/properties.py:docstring of jaraco.classes.properties.classproperty.fget:1: WARNING: py:class reference target not found: _StaticMethodGetter
/home/runner/work/jaraco.classes/jaraco.classes/jaraco/classes/properties.py:docstring of jaraco.classes.properties.classproperty.fset:1: WARNING: py:class reference target not found: _ClassMethodSetter
/home/runner/work/jaraco.classes/jaraco.classes/jaraco/classes/properties.py:docstring of jaraco.classes.properties.classproperty.setter:1: WARNING: py:class reference target not found: _ClassMethodSetter
/home/runner/work/jaraco.classes/jaraco.classes/jaraco/classes/properties.py:docstring of jaraco.classes.properties.classproperty.setter:1: WARNING: py:class reference target not found: _CallableSetter

These errors aren't part of the test suite, but can be invoked with tox -e docs.

These warnings, emitted as errors due to the "nitpicky" setting, are Sphinx's way of trying to indicate that it doesn't have access to the type annotations. This suggests to me that the types should be declared outside the TYPE_CHECKING block. Looks like sphinx-doc/sphinx#11225 may be relevant.

@bswck
Copy link
Contributor Author

bswck commented Feb 7, 2024

Thanks for help with the Ruff error!

I looked at the code again and noticed that I could make it better and more accurate. I'll patch my solution soon. I think I also shouldn't use NoReturn for setters... but that one I already managed to correct. Please wait a moment...

@bswck
Copy link
Contributor Author

bswck commented Feb 7, 2024

These warnings, emitted as errors due to the "nitpicky" setting, are Sphinx's way of trying to indicate that it doesn't have access to the type annotations. This suggests to me that the types should be declared outside the TYPE_CHECKING block. Looks like sphinx-doc/sphinx#11225 may be relevant.

I see. I will try with nitpick_ignore first.

@bswck
Copy link
Contributor Author

bswck commented Feb 7, 2024

These warnings, emitted as errors due to the "nitpicky" setting, are Sphinx's way of trying to indicate that it doesn't have access to the type annotations. This suggests to me that the types should be declared outside the TYPE_CHECKING block. Looks like sphinx-doc/sphinx#11225 may be relevant.

I see. I will try with nitpick_ignore first.

I found a successful work-around using nitpick_ignore:

nitpicky = True
nitpick_ignore = [
    ('py:class', 'Self'),
    ('py:class', '_T'),
    ('py:obj', 'jaraco.classes.properties._T'),
    ('py:class', '_ClassPropertyAttribute'),
    ('py:class', '_GetterCallable'),
    ('py:class', '_GetterClassMethod'),
    ('py:class', '_GetterStaticMethod'),
    ('py:class', '_SetterCallable'),
    ('py:class', '_SetterClassMethod'),
]

This is the preview I deployed from https://github.com/bswck/jaraco.classes/tree/type-hints-nitpick-ignore: https://smokeshow.helpmanual.io/6k37174k0d170s5q0h65/

I personally don't like these interfaces being unlinked, so I guess I will indeed move them out of the TYPE_CHECKING block.
@jaraco I am willing to hear your feedback on the preview, though.

@bswck
Copy link
Contributor Author

bswck commented Feb 7, 2024

Important

There is a known typing issue when trying to set values of class properties: mypy will often warn that you cannot assign to a method. The current work-around is to # type: ignore[assignment].

Should we note that somewhere? I think a relevant issue in mypy ought to be opened too, unless there is one already. Descriptors defining __set__ should be strong enough to make that error impossible.

@bswck
Copy link
Contributor Author

bswck commented Feb 7, 2024

Ah, yes, we need a requirement typing_extensions; python_version < "3.11" now, because typing_extensions gets imported at runtime.

@bswck
Copy link
Contributor Author

bswck commented Feb 7, 2024

Hmmm, that's weird. I completely removed the TYPE_CHECKING block and docs still fail with the same nitpicky-check errors.

@bswck
Copy link
Contributor Author

bswck commented Feb 7, 2024

Hmmm, that's weird. I completely removed the TYPE_CHECKING block and docs still fail with the same nitpicky-check errors.

It might be because I quoted type aliases to defer it from running at runtime... Well, let's try with the deprecated type aliases from typing then. Maybe related: sphinx-doc/sphinx#9813 (comment)

@bswck
Copy link
Contributor Author

bswck commented Feb 7, 2024

Ok, I'm honestly disappointed. Getting this to work with Sphinx will:

  • introduce a new dependency,
  • create an overhead for importing things that should be typing-only,
  • create a version-dependent, non-covered code branch.

@jaraco would you be OK with merging code that builds these docs in the end? It's bswck/jaraco.classes@type-hints-nitpick-ignore.

@bswck
Copy link
Contributor Author

bswck commented Feb 7, 2024

Rebased onto https://github.com/bswck/jaraco.classes/tree/type-hints-nitpick-ignore. Ready for re-review.
The new version preserves the return types of decorated staticmethods and classmethods, i.e.

class TestClassProperty(metaclass=classproperty.Meta):
    @classproperty
    @classmethod
    def foo(cls) -> str:
        return "Test"


reveal_type(TestClassProperty.foo)

reveals TestClassProperty.foo as builtins.str.

@bswck
Copy link
Contributor Author

bswck commented Feb 7, 2024

Hmmm, well... I let myself un-make the second arg to __get__ optional, as it always is passed a value according to docs.
But my change is now incompatible with PEP 252. Rolling that back.

No need for an extra type for staticmethods, it evaluates to `Callable`
@jaraco
Copy link
Owner

jaraco commented Feb 7, 2024

Important

There is a known typing issue when trying to set values of class properties: mypy will often warn that you cannot assign to a method. The current work-around is to # type: ignore[assignment].

Should we note that somewhere? I think a relevant issue in mypy ought to be opened too, unless there is one already. Descriptors defining __set__ should be strong enough to make that error impossible.

🤷 Ideally, there'd be an upstream issue, but I'll leave it to you to decide if that's worth the investment.

Copy link
Owner

@jaraco jaraco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for working through all the issues.

jaraco/classes/properties.py Show resolved Hide resolved
@bswck
Copy link
Contributor Author

bswck commented Feb 7, 2024

Important
There is a known typing issue when trying to set values of class properties: mypy will often warn that you cannot assign to a method. The current work-around is to # type: ignore[assignment].
Should we note that somewhere? I think a relevant issue in mypy ought to be opened too, unless there is one already. Descriptors defining __set__ should be strong enough to make that error impossible.

🤷 Ideally, there'd be an upstream issue, but I'll leave it to you to decide if that's worth the investment.

The error is easy to silence with # type: ignore[assignment] and I will maybe open an issue in the future. As for now, what we have seems to work out just fine.

@bswck
Copy link
Contributor Author

bswck commented Feb 7, 2024

One more pedantic improvement 😅
I think we're ready to merge?

@jaraco jaraco merged commit bf6dac1 into jaraco:main Feb 7, 2024
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add type hints
2 participants