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 typehints and allow google docstrings #1023

Merged
merged 8 commits into from
May 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
include LICENSE README.rst

# Include
include jira/py.typed

# Exclude what is in these folders
prune tests
prune .github
Expand Down
93 changes: 68 additions & 25 deletions docs/api.rst
Original file line number Diff line number Diff line change
@@ -1,47 +1,90 @@
API Documentation
*****************

.. module:: jira

.. contents:: Contents
:local:

JIRA
====
jira package
============

.. autoclass:: JIRA
jira.client module
------------------

Issue
========
.. automodule:: jira.client
:members:
:undoc-members:
:show-inheritance:

.. autoclass:: Issue
jira.config module
------------------

Priority
========
.. automodule:: jira.config
:members:
:undoc-members:
:show-inheritance:

.. autoclass:: Priority
jira.exceptions module
----------------------

Comment
=======
.. automodule:: jira.exceptions
:members:
:undoc-members:
:show-inheritance:

.. autoclass:: Comment
jira.jirashell module
---------------------

Version
=======
.. automodule:: jira.jirashell
:members:
:undoc-members:
:show-inheritance:

.. autoclass:: jira.resources.Version
jira.resilientsession module
----------------------------

Worklog
=======
.. automodule:: jira.resilientsession
:members:
:undoc-members:
:show-inheritance:

.. autoclass:: Worklog
jira.resources module
---------------------

Watchers
========
.. autodata:: jira.client.ResourceType
:annotation: = alias of TypeVar(‘ResourceType’, contravariant=True, bound=jira.resources.Resource)

.. autoclass:: Watchers
.. automodule:: jira.resources
:members:
:undoc-members:
:show-inheritance:
:private-members:

JIRAError
=========
.. autoclass:: jira.resources.StatusCategory
:members:
:undoc-members:
:show-inheritance:

.. autoclass:: JIRAError
.. autoclass:: jira.resources.GreenHopperResource
:members:
:undoc-members:
:show-inheritance:

.. autoclass:: jira.resources.Sprint
:members:
:undoc-members:
:show-inheritance:

.. autoclass:: jira.resources.Board
:members:
:undoc-members:
:show-inheritance:


jira.utils module
-----------------

.. automodule:: jira.utils
:members:
:undoc-members:
:show-inheritance:
79 changes: 29 additions & 50 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,75 +25,48 @@
# -- General configuration -----------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = "2.2.0"
needs_sphinx = "4.0.0"

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode", "sphinx.ext.intersphinx"]
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.intersphinx",
"sphinx.ext.napoleon",
"sphinx.ext.viewcode",
]

intersphinx_mapping = {
"python": ("https://docs.python.org/3.7", None),
# until https://github.com/psf/requests/issues/5212 is addressed
# "requests": ("http://docs.python-requests.org/en/latest/", None),
"requests": ("https://requests.kennethreitz.org/en/master/", None),
"python": ("https://docs.python.org/3.8", None),
"requests": ("https://requests.readthedocs.io/en/latest/", None),
"requests-oauthlib": ("https://requests-oauthlib.readthedocs.io/en/latest/", None),
"ipython": ("https://ipython.readthedocs.io/en/stable/", None),
"pip": ("https://pip.readthedocs.io/en/stable/", None),
}

autodoc_default_options = {
"member-order": "bysource",
"members": True,
"undoc-members": True,
"show-inheritance": True,
"special-members": "__init__",
"undoc-members": True,
}
autodoc_inherit_docstrings = False


nitpick_ignore = [
("py:class", "Any"),
("py:class", "Attachment"),
("py:class", "Board"),
("py:class", "BufferedReader"),
("py:class", "Component"),
("py:class", "CustomFieldOption"),
("py:class", "Customer"),
("py:class", "Dashboard"),
("py:class", "Dict"),
("py:class", "Filter"),
("py:class", "Issue._IssueFields"),
("py:class", "IssueLinkType"),
("py:class", "IssueType"),
("py:class", "Iterable"),
("py:class", "List"),
("py:class", "NoReturn"),
("py:class", "Optional"),
("py:class", "Project"),
("py:class", "Resolution"),
("py:class", "Resource"),
("py:class", "Response"),
("py:class", "ResultList"),
("py:class", "ServiceDesk"),
("py:class", "Sprint"),
("py:class", "Status"),
("py:class", "StatusCategory"),
("py:class", "Tuple"),
("py:class", "Union"),
("py:class", "User"),
("py:class", "Version"),
("py:class", "Votes"),
("py:class", "diy"),
("py:class", "integer"),
("py:class", "jira.client.ResultList"),
("py:class", "jira.resources.Resource"),
("py:class", "jira.resources.Sprint"),
("py:class", "jira.resources.Watchers"),
("py:class", "kanban"),
("py:class", "project"),
("py:class", "scrum"),
("py:class", "user"),
("py:meth", "Resource.delete"),
("py:meth", "Resource.update"),
("py:class", "JIRA"), # in jira.resources we only import this class if type
("py:obj", "typing.ResourceType"), # only Py36 has a problem with this reference
# From other packages
("py:mod", "filemagic"),
("py:mod", "ipython"),
("py:mod", "pip"),
("py:class", "_io.BufferedReader"),
("py:class", "BufferedReader"),
("py:class", "Request"),
("py:class", "requests.models.Response"),
("py:class", "requests.sessions.Session"),
("py:class", "Response"),
("py:mod", "requests-kerberos"),
("py:mod", "requests-oauthlib"),
]
Expand Down Expand Up @@ -289,6 +262,12 @@
# If true, show URL addresses after external links.
# man_show_urls = False

# -- Options for Napoleon -----------------------------------------------------

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.



# -- Options for Texinfo output ------------------------------------------------

Expand Down
7 changes: 6 additions & 1 deletion examples/basic_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
# username and password over HTTP BASIC authentication.

from collections import Counter
from typing import cast

from jira import JIRA
from jira.client import ResultList
from jira.resources import Issue

# By default, the client will connect to a Jira instance started from the Atlassian Plugin SDK.
# See
Expand All @@ -16,7 +19,9 @@
props = jira.application_properties()

# Find all issues reported by the admin
issues = jira.search_issues("assignee=admin")
# Note: we cast() for mypy's benefit, as search_issues can also return the raw json !
# This is if the following argument is used: `json_result=True`
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?


# Find the top three projects containing issues reported by admin
top_three = Counter([issue.fields.project.key for issue in issues]).most_common(3)
9 changes: 3 additions & 6 deletions examples/basic_use.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@

# By default, the client will connect to a Jira instance started from the Atlassian Plugin SDK
# (see https://developer.atlassian.com/display/DOCS/Installing+the+Atlassian+Plugin+SDK for details).
# Override this with the options parameter.
options = {"server": "https://jira.atlassian.com"}
jira = JIRA(options)
jira = JIRA(server="https://jira.atlassian.com")

# Get all projects viewable by anonymous users.
projects = jira.projects()
Expand All @@ -18,12 +16,11 @@

# Get an issue.
issue = jira.issue("JRA-1330")

# Find all comments made by Atlassians on this issue.
atl_comments = [
comment
for comment in issue.fields.comment.comments
if re.search(r"@atlassian.com$", comment.author.emailAddress)
if re.search(r"@atlassian.com$", comment.author.key)
]

# Add a comment to the issue.
Expand Down Expand Up @@ -51,4 +48,4 @@
# Linking a remote jira issue (needs applinks to be configured to work)
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(issue.id, issue2)
7 changes: 5 additions & 2 deletions examples/cookie_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
# username and password over HTTP BASIC authentication.

from collections import Counter
from typing import cast

from jira import JIRA
from jira.client import ResultList
from jira.resources import Issue

# By default, the client will connect to a Jira instance started from the Atlassian Plugin SDK.
# See
Expand All @@ -16,11 +19,11 @@
props = jira.application_properties()

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

# Find the top three projects containing issues reported by admin
top_three = Counter([issue.fields.project.key for issue in issues]).most_common(3)

# import time; time.sleep(65) # Fake cookie expiration

issues = jira.search_issues("assignee=admin")
issues = cast(ResultList[Issue], jira.search_issues("assignee=admin"))
Loading