X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/4ad6191d53207a8b2d4c0c8a30b18119daaa5fbc..936ed3a6a7484917fc10636b3dc2c5fdd9578643:/sdk/python/discovery2pydoc.py diff --git a/sdk/python/discovery2pydoc.py b/sdk/python/discovery2pydoc.py index 6ca3aafeb6..70a51371ac 100755 --- a/sdk/python/discovery2pydoc.py +++ b/sdk/python/discovery2pydoc.py @@ -77,12 +77,19 @@ If you work with this raw object, the keys of the dictionary are documented below, along with their types. The `items` key maps to a list of matching `{cls_name}` objects. ''' -_MODULE_PYDOC = '''Arvados API client documentation skeleton - -This module documents the methods and return types provided by the Arvados API -client. Start with `ArvadosAPIClient`, which documents the methods available -from the API client objects constructed by `arvados.api`. The implementation is -generated dynamically at runtime when the client object is built. +_MODULE_PYDOC = '''Arvados API client reference documentation + +This module provides reference documentation for the interface of the +Arvados API client, including method signatures and type information for +returned objects. However, the functions in `arvados.api` will return +different classes at runtime that are generated dynamically from the Arvados +API discovery document. The classes in this module do not have any +implementation, and you should not instantiate them in your code. + +If you're just starting out, `ArvadosAPIClient` documents the methods +available from the client object. From there, you can follow the trail into +resource methods, request objects, and finally the data dictionaries returned +by the API server. ''' _SCHEMA_PYDOC = ''' @@ -95,25 +102,62 @@ to list the specific keys you need. Refer to the API documentation for details. ''' _MODULE_PRELUDE = ''' +import googleapiclient.discovery +import googleapiclient.http +import httplib2 import sys +from typing import Any, Dict, Generic, List, Optional, TypeVar if sys.version_info < (3, 8): - from typing import Any from typing_extensions import TypedDict else: - from typing import Any, TypedDict + from typing import TypedDict + +# ST represents an API response type +ST = TypeVar('ST', bound=TypedDict) ''' +_REQUEST_CLASS = ''' +class ArvadosAPIRequest(googleapiclient.http.HttpRequest, Generic[ST]): + """Generic API request object + + When you call an API method in the Arvados Python SDK, it returns a + request object. You usually call `execute()` on this object to submit the + request to your Arvados API server and retrieve the response. `execute()` + will return the type of object annotated in the subscript of + `ArvadosAPIRequest`. + """ + + def execute(self, http: Optional[httplib2.Http]=None, num_retries: int=0) -> ST: + """Execute this request and return the response + + Arguments: + + * http: httplib2.Http | None --- The HTTP client object to use to + execute the request. If not specified, uses the HTTP client object + created with the API client object. + + * num_retries: int --- The maximum number of times to retry this + request if the server returns a retryable failure. The API client + object also has a maximum number of retries specified when it is + instantiated (see `arvados.api.api_client`). This request is run + with the larger of that number and this argument. Default 0. + """ -_TYPE_MAP = { +''' + +# Annotation represents a valid Python type annotation. Future development +# could expand this to include other valid types like `type`. +Annotation = str +_TYPE_MAP: Mapping[str, Annotation] = { # Map the API's JavaScript-based type names to Python annotations. # Some of these may disappear after Arvados issue #19795 is fixed. - 'Array': 'list', - 'array': 'list', + 'Array': 'List', + 'array': 'List', 'boolean': 'bool', # datetime fields are strings in ISO 8601 format. 'datetime': 'str', - 'Hash': 'dict[str, Any]', + 'Hash': 'Dict[str, Any]', 'integer': 'int', - 'object': 'dict[str, Any]', + 'object': 'Dict[str, Any]', 'string': 'str', 'text': 'str', } @@ -197,9 +241,15 @@ class Parameter(inspect.Parameter): class Method: - def __init__(self, name: str, spec: Mapping[str, Any]) -> None: + def __init__( + self, + name: str, + spec: Mapping[str, Any], + annotate: Callable[[Annotation], Annotation]=str, + ) -> None: self.name = name self._spec = spec + self._annotate = annotate self._required_params = [] self._optional_params = [] for param_name, param_spec in spec['parameters'].items(): @@ -221,7 +271,8 @@ class Method: try: returns = get_type_annotation(self._spec['response']['$ref']) except KeyError: - returns = 'dict[str, Any]' + returns = 'Dict[str, Any]' + returns = self._annotate(returns) return inspect.Signature(parameters, return_annotation=returns) def doc(self, doc_slice: slice=slice(None)) -> str: @@ -285,7 +336,7 @@ def document_resource(name: str, spec: Mapping[str, Any]) -> str: if class_name in _DEPRECATED_RESOURCES: docstring += _DEPRECATED_NOTICE methods = [ - Method(key, meth_spec) + Method(key, meth_spec, 'ArvadosAPIRequest[{}]'.format) for key, meth_spec in spec['methods'].items() if key not in _ALIASED_METHODS ] @@ -354,7 +405,11 @@ def main(arglist: Optional[Sequence[str]]=None) -> int: for name, resource_spec in resources: print(document_resource(name, resource_spec), file=args.out_file) - print('''class ArvadosAPIClient:''', file=args.out_file) + print( + _REQUEST_CLASS, + '''class ArvadosAPIClient(googleapiclient.discovery.Resource):''', + sep='\n', file=args.out_file, + ) for name, _ in resources: class_name = classify_name(name) docstring = f"Return an instance of `{class_name}` to call methods via this client"