Skip to content

Commit e0bf525

Browse files
authored
Extract and re-inject base client sphinx param doc into client classes, for better sphinx doc (#1131)
* Extract and re-inject base client sphinx param doc Add three helpers to `utils` in order to improve the runtime docstrings for client classes. 1. `read_sphinx_params` -- a simple `:param:` parser 2. `inject_sphinx_params` -- a `__doc__` rewriter which takes parsed params and inserts them in place of a special marker comment 3. `inject_sphinx_params_of` -- a decorator version of these which chains together reading the docstring of one object and modifying the docstring of another Then apply these to write ```python @utils.inject_sphinx_params_of(client.BaseClient) class FlowsClient(client.BaseClient): ... ``` and so forth. This is applied to all client classes except for the Auth login clients, which do not inherit all of the parameters of `BaseClient` -- in particular, we do not allow construction of a login client with an `app`. `read_sphinx_params` gets a functools cache, but the rest of the work is still done dynamically, so there is a little bit of repetition. This results in improved docstrings for sphinx and for runtime usages like `help(globus_sdk.TransferClient)`. Static analyzers (e.g., IDEs) will pick up on the original source string, which will show the marker comment used to drive this behavior: ``` .. globus-sdk-inject-doc-params ``` * Convert copy-params to custom sphinx directive - Remove utility functions for adding parsed `:param:` doc to a runtime docstring - Add a new `CopyParams` sphinx directive registered under the name `sdk-sphinx-copy-params`, and use this to implement param copy/injection functionality as a documentation-time customization. There are some particulars about how this looks in the Sphinx/docutils context which drive the implementation to look a little different from discussed ideas: 1. The source for doc has to be explicit. `:inherit:` or similar ideas about picking up the current class and class hierarchy aren't easily implemented in a sphinx plugin. The context of directive execution isn't populated with easy values for inspection, and finding the "current object being documented" is much more work than it is worth. So each usage specifies `BaseClient` as the source. (required param) 2. We support added parameters as directive contents. Having a custom directive adjacent to a `:param:` list causes docutils errors which kill the entire build. Having blank lines between `:param:` lists causes multiple `PARAMETERS` blocks to get rendered. So we need adjacency but we can't have _literal_ adjacency. If we put params into the content area, we can re-emit them from the directive. 2a. Supporting added parameters before and/or after the copied params with "<YIELD>". Although not strictly necessary, I've tested to ensure we can add params before or after the generated/copied params by having a special line in the content of "<YIELD>". The first time the directive sees this line, it will stop emitting content, emit the copied params, and then resume. * Minor variable rename + comment per review
1 parent 63b49b1 commit e0bf525

File tree

12 files changed

+156
-2
lines changed

12 files changed

+156
-2
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Added
2+
~~~~~
3+
4+
- Most client classes now have their ``__doc__`` attribute modified at runtime
5+
to provide better ``help()`` and sphinx documentation. (:pr:`NUMBER`)

src/globus_sdk/_sphinxext.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,38 @@ def gen_rst(self):
335335
yield ":ref:`how to make paginated calls <making_paginated_calls>`."
336336

337337

338+
class CopyParams(AddContentDirective):
339+
has_content = True
340+
required_arguments = 1
341+
optional_arguments = 0
342+
343+
def gen_rst(self):
344+
import globus_sdk
345+
346+
source_object_name: str = self.arguments[0]
347+
path = source_object_name.split(".")
348+
349+
# traverse `globus_sdk` element by element to find the target
350+
source_object = globus_sdk
351+
for element in path:
352+
source_object = getattr(source_object, element)
353+
354+
if self.content:
355+
content = iter(self.content)
356+
else:
357+
content = ()
358+
359+
for line in content:
360+
if line.strip() == "<YIELD>":
361+
break
362+
yield line
363+
364+
yield from globus_sdk.utils.read_sphinx_params(source_object.__doc__)
365+
366+
for line in content:
367+
yield line
368+
369+
338370
def after_autodoc_signature_replace_MISSING_repr( # pylint: disable=missing-param-doc,missing-type-doc # noqa: E501
339371
app, # pylint: disable=unused-argument
340372
what, # pylint: disable=unused-argument
@@ -365,6 +397,7 @@ def setup(app):
365397
app.add_directive("expandtestfixture", ExpandTestingFixture)
366398
app.add_directive("extdoclink", ExternalDocLink)
367399
app.add_directive("paginatedusage", PaginatedUsage)
400+
app.add_directive("sdk-sphinx-copy-params", CopyParams)
368401

369402
app.add_role("extdoclink", extdoclink_role)
370403

src/globus_sdk/services/auth/client/service_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ class AuthClient(client.BaseClient):
7676
A client for using the
7777
`Globus Auth API <https://docs.globus.org/api/auth/>`_
7878
79+
.. sdk-sphinx-copy-params:: BaseClient
80+
7981
This class provides helper methods for most common resources in the
8082
Auth API, and the common low-level interface from
8183
:class:`BaseClient <globus_sdk.client.BaseClient>` of ``get``, ``put``,

src/globus_sdk/services/compute/client.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class ComputeClientV2(client.BaseClient):
1616
r"""
1717
Client for the Globus Compute API, version 2.
1818
19+
.. sdk-sphinx-copy-params:: BaseClient
20+
1921
.. automethodlist:: globus_sdk.ComputeClientV2
2022
"""
2123

@@ -353,5 +355,7 @@ class ComputeClient(ComputeClientV2):
353355
Canonical client for the Globus Compute API, with support exclusively for
354356
API version 2.
355357
358+
.. sdk-sphinx-copy-params:: BaseClient
359+
356360
.. automethodlist:: globus_sdk.ComputeClient
357361
"""

src/globus_sdk/services/flows/client.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class FlowsClient(client.BaseClient):
2424
r"""
2525
Client for the Globus Flows API.
2626
27+
.. sdk-sphinx-copy-params:: BaseClient
28+
2729
.. automethodlist:: globus_sdk.FlowsClient
2830
"""
2931

@@ -833,7 +835,9 @@ class SpecificFlowClient(client.BaseClient):
833835
Unlike other client types, this must be provided with a specific flow id. All other
834836
arguments are the same as those for :class:`~globus_sdk.BaseClient`.
835837
836-
:param flow_id: The generated UUID associated with a flow
838+
.. sdk-sphinx-copy-params:: BaseClient
839+
840+
:param flow_id: The generated UUID associated with a flow
837841
838842
.. automethodlist:: globus_sdk.SpecificFlowClient
839843
"""

src/globus_sdk/services/gcs/client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ class GCSClient(client.BaseClient):
3434
Manager. All other arguments are the same as those for
3535
:class:`~globus_sdk.BaseClient`.
3636
37-
:param gcs_address: The FQDN (DNS name) or HTTPS URL for the GCS Manager API.
37+
.. sdk-sphinx-copy-params:: BaseClient
38+
39+
:param gcs_address: The FQDN (DNS name) or HTTPS URL for the GCS Manager API.
3840
3941
.. automethodlist:: globus_sdk.GCSClient
4042
"""

src/globus_sdk/services/groups/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ class GroupsClient(client.BaseClient):
1515
Client for the
1616
`Globus Groups API <https://docs.globus.org/api/groups/>`_.
1717
18+
.. sdk-sphinx-copy-params:: BaseClient
19+
1820
This provides a relatively low level client to public groups API endpoints.
1921
You may also consider looking at the GroupsManager as a simpler interface
2022
to more common actions.

src/globus_sdk/services/search/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ class SearchClient(client.BaseClient):
1818
r"""
1919
Client for the Globus Search API
2020
21+
.. sdk-sphinx-copy-params:: BaseClient
22+
2123
This class provides helper methods for most common resources in the
2224
API, and basic ``get``, ``put``, ``post``, and ``delete`` methods
2325
from the base client that can be used to access any API resource.

src/globus_sdk/services/timers/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ class TimersClient(client.BaseClient):
2323
r"""
2424
Client for the Globus Timer API.
2525
26+
.. sdk-sphinx-copy-params:: BaseClient
27+
2628
.. automethodlist:: globus_sdk.TimersClient
2729
"""
2830

src/globus_sdk/services/transfer/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ class TransferClient(client.BaseClient):
4646
Client for the
4747
`Globus Transfer API <https://docs.globus.org/api/transfer/>`_.
4848
49+
.. sdk-sphinx-copy-params:: BaseClient
50+
4951
This class provides helper methods for most common resources in the
5052
REST API, and basic ``get``, ``put``, ``post``, and ``delete`` methods
5153
from the base rest client that can be used to access any REST resource.

0 commit comments

Comments
 (0)