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鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
馃悜 Add seperate typing hints for humanize #150
Conversation
Adds a collection of `.pyi` files that describe the expected type signatures of the publicly available `humanize` functions. A `MANIFEST.in` file has been added to include the new `.pyi` files and the `py.typed` file to indicate to mypy that this package supports typing. mypy has been added to the pre-commit configuration to check the stubs. Some of the signatures are slightly optimistic, since depending on error conditions, different types are returned.
Codecov Report
@@ Coverage Diff @@
## master #150 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 9 9
Lines 554 554
=========================================
Hits 554 554
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
In addition, I originally started these stub files thinking you couldn't use inline typing for Python 3.5, but it turns out that was incorrect. It might be better in that case to make the typing inline, rather than a stub; depends on what people want. |
I haven't used typing hints much in Python yet, so thanks for the PR! Support for Python 3.5 (EOL in two weeks) has now been dropped from I think inline typing makes more sense than stubs, that way it sits right next to the actual code. What do you think?
This project uses setuptools_scm which means all files in source control are automatically included in the sdist, so a MANIFEST.in isn't needed (unless we wanted to include generated files, or exclude something). |
Ah, this one dropped off my radar; apologies. I'll move the stubs into the function definitions themselves. |
It is better to include typing hints directly in the functions that they're describing; which allows mypy to actually check the functions, both in their execution, and that they are actually returning the appropriate values. The major change here is that previously a number of the functions, if they encountered a conversion error, would return the value unchanged. However, that would require just declaring the return type as Any, which although correct, isn't particularly useful. So instead, any failed, unconvertable, inconsistent values will just have `str` called on them, and the output will returned. The function will always return a string, although sometimes it won't be in the format you expect. In addition, some internal variables inside the function have been rearranged and moved around, to avoid changing the type of an already defined variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this! I see it already found and you fixed some bugs it found!
Just a few small suggestions.
@@ -98,30 +105,29 @@ def naturaldelta(value, months=True, minimum_unit="seconds"): | |||
tmp = Unit[minimum_unit.upper()] | |||
if tmp not in (Unit.SECONDS, Unit.MILLISECONDS, Unit.MICROSECONDS): | |||
raise ValueError(f"Minimum unit '{minimum_unit}' not supported") | |||
minimum_unit = tmp | |||
min_u = tmp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep the original name here, it's more descriptive (or at least min_unit
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is, the minimum_unit
variable is defined in the function signature as a str
, and mypy does not approve of reassigning variables to different types. I didn't want to change the name of the argument, because that could break other people calling the functions using keywords.
So the renaming is a bit awkward, but I'm not sure how else to do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see.
Can we rename it to min_unit
then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure.
And of course the CI is failing:
|
@@ -98,30 +105,29 @@ def naturaldelta(value, months=True, minimum_unit="seconds"): | |||
tmp = Unit[minimum_unit.upper()] | |||
if tmp not in (Unit.SECONDS, Unit.MILLISECONDS, Unit.MICROSECONDS): | |||
raise ValueError(f"Minimum unit '{minimum_unit}' not supported") | |||
minimum_unit = tmp | |||
min_u = tmp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see.
Can we rename it to min_unit
then?
@hugovk Would I be able to add |
Originally only included this file to include the `.pyi` files in the package, but it turns out it was redundant, and we don't have `.pyi` files anymore.
Instead of using `typing.Literal`, which was only added in Python 3.7, make the type annotations strings, preventing their execution in the interpreter, and use `typing_extensions` to provide Literal.
In order to make the typing accurate, the failure case was changed in multiple functions from "return the object unchanged", to "return the string representation of the object". As such, a number of tests all needed to be updated.
@hugovk Okay, we're passing CI with everything, except what appears to be a bit of an unrelated failure on windows pypy3. Is it going to be okay changing the expected behavior of the functions from "when failing, return the original object", to "when failing, return the string representation of the object"? If we didn't want to make the API change, we could revert the behavior, and declare the functions in question to return |
Sure, but Python 3.5 has been dropped, 3.6 is the minimum now. Is it still needed? Fine to add it if you need backports from 3.7+. |
|
Let's see how the next push goes 馃
I'm a bit cautious. On the one hand, for success cases, we return a string anyway. On the other, this could be considered a breaking change, and I wonder if people are relying on this behaviour. Breaking changes are allowed with a major bump. But we just had 3.0 last week (Python 3.5 dropped), 2.0 in March (2.7 dropped) and 1.0 in February (first release in 5 years, other old versions dropped). So (if this is a breaking change) we could release 4.0 after some time with deprecation warnings. But then on the third hand 馃檭, we may end up returning odd looking strings with stuff like this:
And I think Django's humanize has similar behaviour. So I'm leaning towards retaining the API. |
I mean, why not both? We could change the return type to Even a small amount of typing hints will be better than no typing hints. |
Let's leave the API as it is for now, and use |
Any chance of getting this merged? I've got |
馃殌 Development has moved to https://github.com/python-humanize/humanize 馃殌 Please open new PRs at https://github.com/python-humanize/humanize/pulls |
@coiax I've redone this PR at python-humanize/humanize#15, please could you have a look? Thanks! |
Adds a collection of
.pyi
files that describe the expected typesignatures of the publicly available
humanize
functions.A
MANIFEST.in
file has been added to include the new.pyi
files andthe
py.typed
file to indicate to mypy that this package supportstyping.
mypy has been added to the pre-commit configuration to check the stubs.
Some of the signatures are slightly optimistic, since depending on error
conditions, different types are returned.