-
- Notifications
You must be signed in to change notification settings - Fork 895
add typehints and allow google docstrings #1023
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
Conversation
9bbdd81 to 3e7e3b7 Compare a2fc82b to 824aa1a Compare
tuukkamustonen left a comment
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.
Took a glance at this, then realized it's a massive diff (as would be expected). As I'm not familiar with the internals of the library, I stopped :)
I see there are 0 checks for PRs, which is a bit scary, but if it passes mypy (and other linters) checks, it's a good indication.
The most vital thing (imo) is to have the public API annotations in place (and detailed enough). Internals aid in development, but are normally not needed for users of the library. But this PR covering everything is a nice thing, for sure.
What should be added, is py.typed file, so that mypy can actually read the types from the package (I don't know why they require that, IDEs don't need it). Can you add that? See #1025.
| | ||
| napoleon_google_docstring = True | ||
| napoleon_numpy_docstring = False # Explicitly prefer Google style docstring | ||
| napoleon_use_param = True # for type hint support |
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.
napoleon_google_docstring and napoleon_use_param already default to True in plugin. Can be removed?
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.
see there are 0 checks for PRs, which is a bit scary, but if it passes mypy (and other linters) checks, it's a good indication.
Regarding the checks, this branch will definitely wait for the CI to pass before merging.
The background is currently the CI test suite is only run on PR's directed at the main branch, as currently this branch targetting an unmerged branch (the isort branch, which this branch is based off of) they aren't being run.
I have run the tests locally so have some confidence it will pass.
What should be added, is py.typed file, so that mypy can actually read the types from the package
I did not know this, I will take a look. Thanks for raising.
napoleon_google_docstring and napoleon_use_param already default to True in plugin. Can be removed?
This is true, it is just a personal preference to have these written explicitly so they are not accidentally changed in the future. Happy to remove if there is a preference either way.
Took a glance at this, then realized it's a massive diff (as would be expected). As I'm not familiar with the internals of the library, I stopped :)
Luckily 90% of the diff (probably ) is just docstrings hahaha. But yeah, I do regret creating such a monster of a PR, as the files and functions depend on one another it was hard to pick a stopping point, as mypy would be throwing warnings left and right.
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.
it was hard to pick a stopping point, as mypy would be throwing warnings left and right.
Completely understandable.
It's good to get the type hints! Even if the hints were invalid, the actual code should not break. So, if a few issues slip in on first go (despite passing 100% of checks), it's not the end of the world 💣.
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.
py.typed file added, branch also rebased on to master confirming tests pass.
824aa1a to 7937b84 Compare 7937b84 to ba44342 Compare ba44342 to 0d58e50 Compare | | ||
| def options(self, url, **kwargs): | ||
| return self.__verb("OPTIONS", url, **kwargs) | ||
| def options(self, url: Union[str, bytes], **kwargs) -> Response: |
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.
I think you could use AnyStr for Union[str, bytes]. Just slightly shorter.
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.
Unfortunately we can't use AnyStr in this case. This is because this class is a subclass of Session, so the typehints need to be consistent with the parent class. Technically we could add a # type: ignore[override], but I felt it unnecessary.
Example of mypy failure
| def options(self, url: Union[str, bytes], **kwargs) -> Response: | |
| def options(self, url: AnyStr, **kwargs) -> Response: |
jira\resilientsession.py:221: error: Signature of "options" incompatible with supertype "Session" Found 1 error in 1 file (checked 19 source files) | i see you're changing the way the documentation is generated maybe check this PR #1039 |
jira/client.py Outdated
| :param ignore_epics: ignore any issues listed in ``issue_keys`` that are epics. (Default: True) | ||
| :type ignore_epics: bool | ||
| Args: | ||
| epic_id (int): The ID for the epic where issues should be added. |
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.
#968 mentions a mistake here maybe take it along
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.
close the issue if you take it along
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.
I've added it as a linked issue, I'll close after this PR is merged
| | ||
| # Find all issues reported by the admin | ||
| issues = jira.search_issues("assignee=admin") | ||
| issues = cast(ResultList[Issue], jira.search_issues("assignee=admin")) |
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.
maybe explain this a little bit more => why cast?
examples/basic_use.py Outdated
| | ||
| # Or modify the List of existing labels. The new label is unicode with no | ||
| # spaces | ||
| assert issue.fields.labels # help mypy |
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.
just for my interest what do you mean with help mypy?
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.
So mypy thinks that the issue.fields.labels can be None, but it has certain ways to infer the type of a variable. This can be via cast(), isinstance() or assert statements - refer: https://mypy.readthedocs.io/en/stable/casts.html
examples/basic_use.py Outdated
| issue = jira.issue("JRA-1330") | ||
| issue2 = jira.issue("XX-23") # could also be another instance | ||
| jira.add_remote_link(issue, issue2) | ||
| jira.add_remote_link(str(issue.id), issue2) |
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.
why cast to str? issue.id is a string already..
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.
Yep you are completely right. The original typehint was an Optional[int] which was incorrect, it is always present, and is a str. I verified this both from the API documentation and by checking examples in jira.atlassian.com
It also helped me remove the assertion in this file
jira/resources.py Outdated
| self.key = None | ||
| """ :type: str """ | ||
| self.fields: Optional[Issue._IssueFields] = None | ||
| self.id: Optional[int] = None |
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.
Referring to both:
https://docs.atlassian.com/software/jira/docs/api/REST/8.13.6/#api/2/issue-getIssue
and
This Issue id field is a str
1da4a70 to 998b869 Compare | Replase resolve conflicts. |
| @ssbarnea fixed conflicts, happy to address the Codacy failure in a separate PR, seems like we should add pylint and mccabe to our lint step ? Looks like a few of them are already picked up by mypy and I've added the |
We already have pylint as part of pre-commit but maybe we have too many includes. Codacy can be ignored for the moment as what matters regards to linting is to pass pre-commit. Obviously that any change that goes towards making codacy happier is appreciated. |
* chore: allow google docstring * bugfix: respect logging argument * bugfix: correct usage of JIRAError, add typehints * feature: add typehints and coax mypy * example, add _Comment helper * feature: add py.typed for PEP-561 compliance * bugfix: fix epic_id typehint, closes pycontribs#968
Summary of Changes
typing.cast,isinstance()and as a last resortassertJIRAErrorargument order to reflect how the baseExceptionclass is used, i.e. first argument is a string, prevent misuse ofstatus_codefor a string of an error messagebasic_use.pyno longer worked, since GDPR theemailAddressproperty has been removed, changing the field used in the example to.keyat least makes it run fine.Removed logging parameter and switched back to standard module logging #329 review comments were checked, I don't feel the logging changes made here are particularly contentious - they didn't intend to be. I think a further PR should be raised if anything more needs to be done - as the changes here we mainly for the mypy fixes.
The implementation chosen imports
loggingas_logging, this should further prevent a developer accidentally using the raw logging module. Exposing the logger underself.logmay also allow user to use the logger if they monkey patch or base class of the JIRA class.This branch is currently based of the isort branchnow rebasedTodo:
Document logging bug fixed on the client.py side, may need changes to the other classes, e.g. resilient sessionshould be a separate PR if anything further is required than what was added to fix mypyConvertnot required as inferred by mypy, could maybe check they are added to the docstrings, but might take that up in another PR, a bit burnt out as I severely under estimated the work required here hahastr = NonetoOptional[str] = Noneif the function has an actual none check