Skip to content

Conversation

@adehad
Copy link
Contributor

@adehad adehad commented May 3, 2021

Summary of Changes

  1. Add typehints, coaxing mypy where required - i.e. typing.cast ,isinstance() and as a last resort assert
  2. Re-order JIRAError argument order to reflect how the base Exception class is used, i.e. first argument is a string, prevent misuse of status_code for a string of an error message
  3. Allow "Google style" docstrings, allowing for more legible docstrings
  4. The basic_use.py no longer worked, since GDPR the emailAddress property has been removed, changing the field used in the example to .key at least makes it run fine.
  5. Partially (maybe fully?) address Disabling logging is not working #1009 in order to satisfy mypy, refer to commit: "bugfix: respect logging argument" (cc504a1).
    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 logging as _logging, this should further prevent a developer accidentally using the raw logging module. Exposing the logger under self.log may 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 branch now rebased

Todo:

  • Document logging bug fixed on the client.py side, may need changes to the other classes, e.g. resilient session should be a separate PR if anything further is required than what was added to fix mypy
  • Document fix of JIRAError class, argument order causing misuse
  • Convert str = None to Optional[str] = None if the function has an actual none check not 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 haha
  • Check the actual docs build looks ok
  • do some more google style docstrings if you aren't tired of them yet, resources.py has a few left
  • where possible, convert assertions to properly typehinted variables
  • cleanup the commit history, no point separating by files as they all need to work together for mypy to be happy
@adehad adehad changed the base branch from master to feature/isort May 3, 2021 13:30
@adehad adehad force-pushed the feature/typehints_and_google_docstrings branch 2 times, most recently from 9bbdd81 to 3e7e3b7 Compare May 9, 2021 12:32
@adehad adehad marked this pull request as ready for review May 9, 2021 12:34
@adehad adehad force-pushed the feature/typehints_and_google_docstrings branch 2 times, most recently from a2fc82b to 824aa1a Compare May 9, 2021 17:47
Copy link
Contributor

@tuukkamustonen tuukkamustonen left a 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
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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 💣.

Copy link
Contributor Author

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.

@delete-merged-branch delete-merged-branch bot deleted the branch master May 12, 2021 12:46
@adehad adehad force-pushed the feature/typehints_and_google_docstrings branch from 824aa1a to 7937b84 Compare May 12, 2021 17:05
@adehad adehad changed the base branch from feature/isort to master May 12, 2021 17:05
@adehad adehad force-pushed the feature/typehints_and_google_docstrings branch from 7937b84 to ba44342 Compare May 12, 2021 17:19
@adehad adehad force-pushed the feature/typehints_and_google_docstrings branch from ba44342 to 0d58e50 Compare May 12, 2021 17:44

def options(self, url, **kwargs):
return self.__verb("OPTIONS", url, **kwargs)
def options(self, url: Union[str, bytes], **kwargs) -> Response:
Copy link
Contributor

@tuukkamustonen tuukkamustonen May 13, 2021

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.

Copy link
Contributor Author

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

Suggested change
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) 
@studioj
Copy link
Collaborator

studioj commented May 13, 2021

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.
Copy link
Collaborator

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

Copy link
Collaborator

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

Copy link
Contributor Author

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

@adehad adehad linked an issue May 13, 2021 that may be closed by this pull request

# Find all issues reported by the admin
issues = jira.search_issues("assignee=admin")
issues = cast(ResultList[Issue], jira.search_issues("assignee=admin"))
Copy link
Collaborator

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?


# Or modify the List of existing labels. The new label is unicode with no
# spaces
assert issue.fields.labels # help mypy
Copy link
Collaborator

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?

Copy link
Contributor Author

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

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)
Copy link
Collaborator

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..

Copy link
Contributor Author

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

@adehad adehad linked an issue May 14, 2021 that may be closed by this pull request
self.key = None
""" :type: str """
self.fields: Optional[Issue._IssueFields] = None
self.id: Optional[int] = None
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@adehad adehad force-pushed the feature/typehints_and_google_docstrings branch from 1da4a70 to 998b869 Compare May 14, 2021 18:27
@adehad adehad linked an issue May 14, 2021 that may be closed by this pull request
@adehad adehad mentioned this pull request May 14, 2021
@ssbarnea
Copy link
Member

Replase resolve conflicts.

@adehad
Copy link
Contributor Author

adehad commented May 15, 2021

@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 ignore[override] to keep mypy happy.

@ssbarnea
Copy link
Member

@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 ignore[override] to keep mypy happy.

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.

@ssbarnea ssbarnea merged commit 6e51395 into master May 15, 2021
@delete-merged-branch delete-merged-branch bot deleted the feature/typehints_and_google_docstrings branch May 15, 2021 12:45
svermeulen pushed a commit to svermeulen/jira that referenced this pull request Oct 31, 2021
* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment