21211: Clean arvados.api imports
[arvados.git] / sdk / python / discovery2pydoc.py
index 889ee93293a5edd75b3858e9747a1cb18cd00e3a..6ca3aafeb64bc319147db1b52dcdf38986792af3 100755 (executable)
@@ -49,8 +49,8 @@ _ALIASED_METHODS = frozenset([
 ])
 _DEPRECATED_NOTICE = '''
 
-!!! deprecated
-    This resource is deprecated in the Arvados API.
+.. WARNING:: Deprecated
+   This resource is deprecated in the Arvados API.
 '''
 _DEPRECATED_RESOURCES = frozenset([
     'Humans',
@@ -86,11 +86,21 @@ generated dynamically at runtime when the client object is built.
 '''
 _SCHEMA_PYDOC = '''
 
-This is the dictionary object that represents a single {cls_name} in Arvados.
+This is the dictionary object that represents a single {cls_name} in Arvados
+and is returned by most `{cls_name}s` methods.
 The keys of the dictionary are documented below, along with their types.
 Not every key may appear in every dictionary returned by an API call.
-Refer to the API documentation for details about how to retrieve specific keys
-if you need them.
+When a method doesn't return all the data, you can use its `select` parameter
+to list the specific keys you need. Refer to the API documentation for details.
+'''
+
+_MODULE_PRELUDE = '''
+import sys
+if sys.version_info < (3, 8):
+    from typing import Any
+    from typing_extensions import TypedDict
+else:
+    from typing import Any, TypedDict
 '''
 
 _TYPE_MAP = {
@@ -172,13 +182,17 @@ class Parameter(inspect.Parameter):
         if default_value is None:
             default_doc = ''
         else:
-            default_doc = f" Default {default_value!r}."
-        # If there is no description, use a zero-width space to help Markdown
-        # parsers retain the definition list structure.
-        description = self._spec['description'] or '\u200b'
+            default_doc = f"Default {default_value!r}."
+        description = self._spec['description']
+        doc_parts = [f'{self.api_name}: {self.annotation}']
+        if description or default_doc:
+            doc_parts.append('---')
+            if description:
+                doc_parts.append(description)
+            if default_doc:
+                doc_parts.append(default_doc)
         return f'''
-{self.api_name}: {self.annotation}
-: {description}{default_doc}
+* {' '.join(doc_parts)}
 '''
 
 
@@ -200,7 +214,7 @@ class Method:
 
     def signature(self) -> inspect.Signature:
         parameters = [
-            inspect.Parameter('self', inspect.Parameter.POSITIONAL_ONLY),
+            inspect.Parameter('self', inspect.Parameter.POSITIONAL_OR_KEYWORD),
             *self._required_params,
             *self._optional_params,
         ]
@@ -308,16 +322,18 @@ If not provided, retrieved dynamically from Arvados client configuration.
         parts = urllib.parse.urlsplit(args.discovery_url)
         if not (parts.scheme or parts.netloc):
             args.discovery_url = pathlib.Path(args.discovery_url).resolve().as_uri()
+    # Our output is Python source, so it should be UTF-8 regardless of locale.
     if args.output_file == STDSTREAM_PATH:
-        args.out_file = sys.stdout
+        args.out_file = open(sys.stdout.fileno(), 'w', encoding='utf-8', closefd=False)
     else:
-        args.out_file = args.output_file.open('w')
+        args.out_file = args.output_file.open('w', encoding='utf-8')
     return args
 
 def main(arglist: Optional[Sequence[str]]=None) -> int:
     args = parse_arguments(arglist)
     with urllib.request.urlopen(args.discovery_url) as discovery_file:
-        if not (discovery_file.status is None or 200 <= discovery_file.status < 300):
+        status = discovery_file.getcode()
+        if not (status is None or 200 <= status < 300):
             print(
                 f"error getting {args.discovery_url}: server returned {discovery_file.status}",
                 file=sys.stderr,
@@ -326,8 +342,8 @@ def main(arglist: Optional[Sequence[str]]=None) -> int:
         discovery_document = json.load(discovery_file)
     print(
         to_docstring(_MODULE_PYDOC, indent=0),
-        '''from typing import Any, TypedDict''',
-        sep='\n\n', end='\n\n', file=args.out_file,
+        _MODULE_PRELUDE,
+        sep='\n', file=args.out_file,
     )
 
     schemas = sorted(discovery_document['schemas'].items())
@@ -353,6 +369,7 @@ def main(arglist: Optional[Sequence[str]]=None) -> int:
         }
         print(Method(name, method_spec).doc(), file=args.out_file)
 
+    args.out_file.close()
     return os.EX_OK
 
 if __name__ == '__main__':