polyapi-python 0.3.3.dev3__tar.gz → 0.3.3.dev5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. {polyapi_python-0.3.3.dev3/polyapi_python.egg-info → polyapi_python-0.3.3.dev5}/PKG-INFO +11 -1
  2. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/README.md +10 -0
  3. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/api.py +1 -0
  4. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/cli.py +0 -2
  5. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/function_cli.py +2 -8
  6. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/generate.py +12 -11
  7. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/parser.py +1 -0
  8. polyapi_python-0.3.3.dev5/polyapi/poly_schemas.py +94 -0
  9. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/schema.py +17 -6
  10. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/server.py +9 -5
  11. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/utils.py +68 -17
  12. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5/polyapi_python.egg-info}/PKG-INFO +11 -1
  13. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/pyproject.toml +5 -1
  14. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/tests/test_deployables.py +4 -3
  15. polyapi_python-0.3.3.dev5/tests/test_generate.py +83 -0
  16. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/tests/test_parser.py +4 -0
  17. polyapi_python-0.3.3.dev5/tests/test_schema.py +26 -0
  18. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/tests/test_server.py +56 -1
  19. polyapi_python-0.3.3.dev5/tests/test_utils.py +86 -0
  20. polyapi_python-0.3.3.dev3/polyapi/poly_schemas.py +0 -60
  21. polyapi_python-0.3.3.dev3/tests/test_generate.py +0 -14
  22. polyapi_python-0.3.3.dev3/tests/test_schema.py +0 -20
  23. polyapi_python-0.3.3.dev3/tests/test_utils.py +0 -14
  24. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/LICENSE +0 -0
  25. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/__init__.py +0 -0
  26. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/__main__.py +0 -0
  27. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/auth.py +0 -0
  28. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/client.py +0 -0
  29. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/config.py +0 -0
  30. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/constants.py +0 -0
  31. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/deployables.py +0 -0
  32. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/error_handler.py +0 -0
  33. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/exceptions.py +0 -0
  34. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/execute.py +0 -0
  35. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/prepare.py +0 -0
  36. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/py.typed +0 -0
  37. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/rendered_spec.py +0 -0
  38. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/sync.py +0 -0
  39. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/typedefs.py +0 -0
  40. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/variables.py +0 -0
  41. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi/webhook.py +0 -0
  42. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi_python.egg-info/SOURCES.txt +0 -0
  43. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi_python.egg-info/dependency_links.txt +0 -0
  44. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi_python.egg-info/requires.txt +0 -0
  45. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/polyapi_python.egg-info/top_level.txt +0 -0
  46. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/setup.cfg +0 -0
  47. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/tests/test_api.py +0 -0
  48. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/tests/test_auth.py +0 -0
  49. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/tests/test_rendered_spec.py +0 -0
  50. {polyapi_python-0.3.3.dev3 → polyapi_python-0.3.3.dev5}/tests/test_variables.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polyapi-python
3
- Version: 0.3.3.dev3
3
+ Version: 0.3.3.dev5
4
4
  Summary: The Python Client for PolyAPI, the IPaaS by Developers for Developers
5
5
  Author-email: Dan Fellin <dan@polyapi.io>
6
6
  License: MIT License
@@ -193,6 +193,16 @@ When hacking on this library, please enable flake8 and add this line to your fla
193
193
  --config=.flake8
194
194
  ```
195
195
 
196
+ ## Mypy Type Improvements
197
+
198
+ This script is handy for checking for any mypy types:
199
+
200
+ ```bash
201
+ ./check_mypy.sh
202
+ ```
203
+
204
+ Please ignore \[name-defined\] errors for now. This is a known bug we are working to fix!
205
+
196
206
  ## Support
197
207
 
198
208
  If you run into any issues or want help getting started with this project, please contact support@polyapi.io
@@ -153,6 +153,16 @@ When hacking on this library, please enable flake8 and add this line to your fla
153
153
  --config=.flake8
154
154
  ```
155
155
 
156
+ ## Mypy Type Improvements
157
+
158
+ This script is handy for checking for any mypy types:
159
+
160
+ ```bash
161
+ ./check_mypy.sh
162
+ ```
163
+
164
+ Please ignore \[name-defined\] errors for now. This is a known bug we are working to fix!
165
+
156
166
  ## Support
157
167
 
158
168
  If you run into any issues or want help getting started with this project, please contact support@polyapi.io
@@ -42,6 +42,7 @@ def render_api_function(
42
42
  arg_names = [a["name"] for a in arguments]
43
43
  args, args_def = parse_arguments(function_name, arguments)
44
44
  return_type_name, return_type_def = get_type_and_def(return_type) # type: ignore
45
+
45
46
  data = "{" + ", ".join([f"'{arg}': {rewrite_arg_name(arg)}" for arg in arg_names]) + "}"
46
47
 
47
48
  api_response_type = f"{function_name}Response"
@@ -47,9 +47,7 @@ def execute_from_cli():
47
47
 
48
48
  def generate_command(args):
49
49
  initialize_config()
50
- print("Generating Poly Python SDK...", end="")
51
50
  generate()
52
- print_green("DONE")
53
51
 
54
52
  generate_parser.set_defaults(command=generate_command)
55
53
 
@@ -1,7 +1,7 @@
1
1
  import sys
2
2
  from typing import Any, List, Optional
3
3
  import requests
4
- from polyapi.generate import cache_specs, generate_functions, get_specs, parse_function_specs
4
+ from polyapi.generate import generate as generate_library
5
5
  from polyapi.config import get_api_key_and_url
6
6
  from polyapi.utils import get_auth_headers, print_green, print_red, print_yellow
7
7
  from polyapi.parser import parse_function_code, get_jsonschema_type
@@ -87,13 +87,7 @@ def function_add_or_update(
87
87
  function_id = resp.json()["id"]
88
88
  print(f"Function ID: {function_id}")
89
89
  if generate:
90
- print("Generating new custom function...", end="")
91
- # TODO do something more efficient here rather than regetting ALL the specs again
92
- specs = get_specs()
93
- cache_specs(specs)
94
- functions = parse_function_specs(specs)
95
- generate_functions(functions)
96
- print_green("DONE")
90
+ generate_library()
97
91
  else:
98
92
  print("Error adding function.")
99
93
  print(resp.status_code)
@@ -2,18 +2,17 @@ import json
2
2
  import requests
3
3
  import os
4
4
  import shutil
5
- from typing import List, cast
5
+ from typing import List, Tuple, cast
6
6
 
7
- from polyapi import schema
8
- from polyapi.auth import render_auth_function
9
- from polyapi.client import render_client_function
10
- from polyapi.poly_schemas import generate_schemas
11
- from polyapi.webhook import render_webhook_handle
7
+ from .auth import render_auth_function
8
+ from .client import render_client_function
9
+ from .poly_schemas import generate_schemas
10
+ from .webhook import render_webhook_handle
12
11
 
13
12
  from .typedefs import PropertySpecification, SchemaSpecDto, SpecificationDto, VariableSpecDto
14
13
  from .api import render_api_function
15
14
  from .server import render_server_function
16
- from .utils import add_import_to_init, get_auth_headers, init_the_init, to_func_namespace
15
+ from .utils import add_import_to_init, get_auth_headers, init_the_init, print_green, to_func_namespace
17
16
  from .variables import generate_variables
18
17
  from .config import get_api_key_and_url
19
18
 
@@ -175,13 +174,13 @@ def remove_old_library():
175
174
 
176
175
 
177
176
  def generate() -> None:
178
-
177
+ print("Generating Poly Python SDK...", end="", flush=True)
179
178
  remove_old_library()
180
179
 
181
- limit_ids: List[str] = [] # useful for narrowing down generation to a single function to debug
182
-
183
180
  specs = get_specs()
184
181
  cache_specs(specs)
182
+
183
+ limit_ids: List[str] = [] # useful for narrowing down generation to a single function to debug
185
184
  functions = parse_function_specs(specs, limit_ids=limit_ids)
186
185
 
187
186
  schemas = get_schemas()
@@ -208,6 +207,8 @@ def generate() -> None:
208
207
  file_path = os.path.join(os.getcwd(), ".polyapi-python")
209
208
  open(file_path, "w").close()
210
209
 
210
+ print_green("DONE")
211
+
211
212
 
212
213
  def clear() -> None:
213
214
  base = os.path.dirname(os.path.abspath(__file__))
@@ -221,7 +222,7 @@ def clear() -> None:
221
222
  print("Cleared!")
222
223
 
223
224
 
224
- def render_spec(spec: SpecificationDto):
225
+ def render_spec(spec: SpecificationDto) -> Tuple[str, str]:
225
226
  function_type = spec["type"]
226
227
  function_description = spec["description"]
227
228
  function_name = spec["name"]
@@ -158,6 +158,7 @@ def _parse_google_docstring(docstring: str) -> Dict[str, Any]:
158
158
 
159
159
  return parsed
160
160
 
161
+
161
162
  def _get_schemas(code: str) -> List[Dict]:
162
163
  schemas = []
163
164
  user_code = types.SimpleNamespace()
@@ -0,0 +1,94 @@
1
+ import os
2
+ from typing import Any, Dict, List, Tuple
3
+
4
+ from polyapi.schema import wrapped_generate_schema_types
5
+ from polyapi.utils import add_import_to_init, init_the_init, to_func_namespace
6
+
7
+ from .typedefs import SchemaSpecDto
8
+
9
+ SCHEMA_CODE_IMPORTS = """from typing_extensions import TypedDict, NotRequired
10
+
11
+
12
+ """
13
+
14
+
15
+ FALLBACK_SPEC_TEMPLATE = """class {name}(TypedDict, total=False):
16
+ ''' unable to generate schema for {name}, defaulting to permissive type '''
17
+ pass
18
+ """
19
+
20
+
21
+ def generate_schemas(specs: List[SchemaSpecDto]):
22
+ for spec in specs:
23
+ create_schema(spec)
24
+
25
+
26
+ def add_schema_file(
27
+ full_path: str,
28
+ schema_name: str,
29
+ spec: SchemaSpecDto,
30
+ ):
31
+ # first lets add the import to the __init__
32
+ init_the_init(full_path, SCHEMA_CODE_IMPORTS)
33
+
34
+ if not spec["definition"].get("title"):
35
+ # very empty schemas like mews.Unit are possible
36
+ # add a title here to be sure they render
37
+ spec["definition"]["title"] = schema_name
38
+
39
+ schema_defs = render_poly_schema(spec)
40
+
41
+ if schema_defs:
42
+ # add function to init
43
+ init_path = os.path.join(full_path, "__init__.py")
44
+ with open(init_path, "a") as f:
45
+ f.write(f"\n\nfrom ._{to_func_namespace(schema_name)} import {schema_name}")
46
+
47
+ # add type_defs to underscore file
48
+ file_path = os.path.join(full_path, f"_{to_func_namespace(schema_name)}.py")
49
+ with open(file_path, "w") as f:
50
+ f.write(schema_defs)
51
+
52
+
53
+ def create_schema(
54
+ spec: SchemaSpecDto
55
+ ) -> None:
56
+ full_path = os.path.dirname(os.path.abspath(__file__))
57
+ folders = f"schemas.{spec['context']}.{spec['name']}".split(".")
58
+ for idx, folder in enumerate(folders):
59
+ if idx + 1 == len(folders):
60
+ # special handling for final level
61
+ add_schema_file(
62
+ full_path,
63
+ folder,
64
+ spec,
65
+ )
66
+ else:
67
+ full_path = os.path.join(full_path, folder)
68
+ if not os.path.exists(full_path):
69
+ os.makedirs(full_path)
70
+
71
+ # append to __init__.py file if nested folders
72
+ next = folders[idx + 1] if idx + 2 < len(folders) else ""
73
+ if next:
74
+ init_the_init(full_path, SCHEMA_CODE_IMPORTS)
75
+ add_import_to_init(full_path, next)
76
+
77
+
78
+
79
+ def add_schema_to_init(full_path: str, spec: SchemaSpecDto):
80
+ init_the_init(full_path, code_imports="")
81
+ init_path = os.path.join(full_path, "__init__.py")
82
+ with open(init_path, "a") as f:
83
+ f.write(render_poly_schema(spec) + "\n\n")
84
+
85
+
86
+ def render_poly_schema(spec: SchemaSpecDto) -> str:
87
+ definition = spec["definition"]
88
+ if not definition.get("type"):
89
+ definition["type"] = "object"
90
+ root, schema_types = wrapped_generate_schema_types(
91
+ definition, root=spec["name"], fallback_type=Dict
92
+ )
93
+ return schema_types
94
+ # return FALLBACK_SPEC_TEMPLATE.format(name=spec["name"])
@@ -1,9 +1,8 @@
1
1
  """ NOTE: this file represents the schema parsing logic for jsonschema_gentypes
2
2
  """
3
- import random
4
- import string
5
3
  import logging
6
4
  import contextlib
5
+ import re
7
6
  from typing import Dict
8
7
  from jsonschema_gentypes.cli import process_config
9
8
  from jsonschema_gentypes import configuration
@@ -48,10 +47,8 @@ def wrapped_generate_schema_types(type_spec: dict, root, fallback_type):
48
47
  # lets name the root after the reference for some level of visibility
49
48
  root += pascalCase(type_spec["x-poly-ref"]["path"].replace(".", " "))
50
49
  else:
51
- # add three random letters for uniqueness
52
- root += random.choice(string.ascii_letters).upper()
53
- root += random.choice(string.ascii_letters).upper()
54
- root += random.choice(string.ascii_letters).upper()
50
+ # if we have no root, just add "My"
51
+ root = "My" + root
55
52
 
56
53
  root = clean_title(root)
57
54
 
@@ -99,9 +96,23 @@ def generate_schema_types(input_data: Dict, root=None):
99
96
  with open(tmp_output) as f:
100
97
  output = f.read()
101
98
 
99
+ output = clean_malformed_examples(output)
100
+
102
101
  return output
103
102
 
104
103
 
104
+ # Regex to match everything between "# example: {\n" and "^}$"
105
+ MALFORMED_EXAMPLES_PATTERN = re.compile(r"# example: \{\n.*?^\}$", flags=re.DOTALL | re.MULTILINE)
106
+
107
+
108
+ def clean_malformed_examples(example: str) -> str:
109
+ """ there is a bug in the `jsonschmea_gentypes` library where if an example from a jsonchema is an object,
110
+ it will break the code because the object won't be properly commented out
111
+ """
112
+ cleaned_example = MALFORMED_EXAMPLES_PATTERN.sub("", example)
113
+ return cleaned_example
114
+
115
+
105
116
  def clean_title(title: str) -> str:
106
117
  """ used by library generation, sometimes functions can be added with spaces in the title
107
118
  or other nonsense. fix them!
@@ -1,7 +1,7 @@
1
- from typing import Any, Dict, List, Tuple
1
+ from typing import Any, Dict, List, Tuple, cast
2
2
 
3
- from polyapi.typedefs import PropertySpecification
4
- from polyapi.utils import add_type_import_path, parse_arguments, get_type_and_def, rewrite_arg_name
3
+ from polyapi.typedefs import PropertySpecification, PropertyType
4
+ from polyapi.utils import add_type_import_path, parse_arguments, get_type_and_def, return_type_already_defined_in_args, rewrite_arg_name
5
5
 
6
6
  SERVER_DEFS_TEMPLATE = """
7
7
  from typing import List, Dict, Any, TypedDict, Callable
@@ -21,7 +21,7 @@ def {function_name}(
21
21
  try:
22
22
  return {return_action}
23
23
  except:
24
- return resp.text
24
+ return resp.text # type: ignore # fallback for debugging
25
25
 
26
26
 
27
27
  """
@@ -37,7 +37,11 @@ def render_server_function(
37
37
  ) -> Tuple[str, str]:
38
38
  arg_names = [a["name"] for a in arguments]
39
39
  args, args_def = parse_arguments(function_name, arguments)
40
- return_type_name, return_type_def = get_type_and_def(return_type) # type: ignore
40
+ return_type_name, return_type_def = get_type_and_def(cast(PropertyType, return_type), "ReturnType")
41
+
42
+ if return_type_def and return_type_already_defined_in_args(return_type_name, args_def):
43
+ return_type_def = ""
44
+
41
45
  data = "{" + ", ".join([f"'{arg}': {rewrite_arg_name(arg)}" for arg in arg_names]) + "}"
42
46
  func_type_defs = SERVER_DEFS_TEMPLATE.format(
43
47
  args_def=args_def,
@@ -6,7 +6,11 @@ from typing import Tuple, List
6
6
  from colorama import Fore, Style
7
7
  from polyapi.constants import BASIC_PYTHON_TYPES
8
8
  from polyapi.typedefs import PropertySpecification, PropertyType
9
- from polyapi.schema import wrapped_generate_schema_types, clean_title, map_primitive_types
9
+ from polyapi.schema import (
10
+ wrapped_generate_schema_types,
11
+ clean_title,
12
+ map_primitive_types,
13
+ )
10
14
 
11
15
 
12
16
  # this string should be in every __init__ file.
@@ -42,7 +46,7 @@ def camelCase(s: str) -> str:
42
46
  s = s.strip()
43
47
  if " " in s or "-" in s:
44
48
  s = re.sub(r"(_|-)+", " ", s).title().replace(" ", "")
45
- return ''.join([s[0].lower(), s[1:]])
49
+ return "".join([s[0].lower(), s[1:]])
46
50
  else:
47
51
  # s is already in camelcase as best as we can tell, just move on!
48
52
  return s
@@ -65,8 +69,7 @@ def print_red(s: str):
65
69
 
66
70
 
67
71
  def add_type_import_path(function_name: str, arg: str) -> str:
68
- """ if not basic type, coerce to camelCase and add the import path
69
- """
72
+ """if not basic type, coerce to camelCase and add the import path"""
70
73
  # for now, just treat Callables as basic types
71
74
  if arg.startswith("Callable"):
72
75
  return arg
@@ -83,12 +86,16 @@ def add_type_import_path(function_name: str, arg: str) -> str:
83
86
  sub = sub.replace('"', "")
84
87
  return f'List["{to_func_namespace(function_name)}.{camelCase(sub)}"]'
85
88
  else:
86
- return f'List[{to_func_namespace(function_name)}.{camelCase(sub)}]'
89
+ return f"List[{to_func_namespace(function_name)}.{camelCase(sub)}]"
87
90
 
88
- return f'{to_func_namespace(function_name)}.{camelCase(arg)}'
91
+ return f"{to_func_namespace(function_name)}.{camelCase(arg)}"
89
92
 
90
93
 
91
- def get_type_and_def(type_spec: PropertyType) -> Tuple[str, str]:
94
+ def get_type_and_def(
95
+ type_spec: PropertyType, title_fallback: str = ""
96
+ ) -> Tuple[str, str]:
97
+ """ returns type and type definition for a given PropertyType
98
+ """
92
99
  if type_spec["kind"] == "plain":
93
100
  value = type_spec["value"]
94
101
  if value.endswith("[]"):
@@ -115,15 +122,19 @@ def get_type_and_def(type_spec: PropertyType) -> Tuple[str, str]:
115
122
  elif type_spec["kind"] == "object":
116
123
  if type_spec.get("schema"):
117
124
  schema = type_spec["schema"]
118
- title = schema.get("title", schema.get("name", ""))
119
- if title:
125
+ title = schema.get("title", schema.get("name", title_fallback))
126
+ if title and schema.get("type") == "array":
127
+ # TODO fix me
128
+ # we don't use ReturnType as name for the list type here, we use _ReturnTypeItem
129
+ return "List", ""
130
+ elif title:
120
131
  assert isinstance(title, str)
121
132
  return wrapped_generate_schema_types(schema, title, "Dict") # type: ignore
122
- elif schema.get("allOf") and len(schema['allOf']):
133
+ elif schema.get("allOf") and len(schema["allOf"]):
123
134
  # we are in a case of a single allOf, lets strip off the allOf and move on!
124
135
  # our library doesn't handle allOf well yet
125
- allOf = schema['allOf'][0]
126
- title = allOf.get("title", allOf.get("name", ""))
136
+ allOf = schema["allOf"][0]
137
+ title = allOf.get("title", allOf.get("name", title_fallback))
127
138
  return wrapped_generate_schema_types(allOf, title, "Dict")
128
139
  elif schema.get("items"):
129
140
  # fallback to schema $ref name if no explicit title
@@ -131,7 +142,7 @@ def get_type_and_def(type_spec: PropertyType) -> Tuple[str, str]:
131
142
  title = items.get("title") # type: ignore
132
143
  if not title:
133
144
  # title is actually a reference to another schema
134
- title = items.get("$ref", "") # type: ignore
145
+ title = items.get("$ref", title_fallback) # type: ignore
135
146
 
136
147
  title = title.rsplit("/", 1)[-1]
137
148
  if not title:
@@ -153,12 +164,18 @@ def get_type_and_def(type_spec: PropertyType) -> Tuple[str, str]:
153
164
  return_type = "Any"
154
165
 
155
166
  for argument in type_spec["spec"]["arguments"]:
167
+ # do NOT add this fallback here
168
+ # callable arguments don't understand the imports yet
169
+ # if it's not a basic type here, we'll just do Any
170
+ # _maybe_add_fallback_schema_name(argument)
156
171
  arg_type, arg_def = get_type_and_def(argument["type"])
157
172
  arg_types.append(arg_type)
158
173
  if arg_def:
159
174
  arg_defs.append(arg_def)
160
175
 
161
- final_arg_type = "Callable[[{}], {}]".format(", ".join(arg_types), return_type)
176
+ final_arg_type = "Callable[[{}], {}]".format(
177
+ ", ".join(arg_types), return_type
178
+ )
162
179
  return final_arg_type, "\n".join(arg_defs)
163
180
  else:
164
181
  return "Callable", ""
@@ -168,15 +185,27 @@ def get_type_and_def(type_spec: PropertyType) -> Tuple[str, str]:
168
185
  return "Any", ""
169
186
 
170
187
 
171
- def parse_arguments(function_name: str, arguments: List[PropertySpecification]) -> Tuple[str, str]:
188
+ def _maybe_add_fallback_schema_name(a: PropertySpecification):
189
+ if a["type"]["kind"] == "object" and a["type"].get("schema"):
190
+ schema = a["type"].get("schema", {})
191
+ if not schema.get("title") and not schema.get("name") and a["name"]:
192
+ schema["title"] = a["name"].title()
193
+
194
+
195
+ def parse_arguments(
196
+ function_name: str, arguments: List[PropertySpecification]
197
+ ) -> Tuple[str, str]:
172
198
  args_def = []
173
199
  arg_string = ""
174
200
  for idx, a in enumerate(arguments):
201
+ _maybe_add_fallback_schema_name(a)
175
202
  arg_type, arg_def = get_type_and_def(a["type"])
176
203
  if arg_def:
177
204
  args_def.append(arg_def)
178
205
  a["name"] = rewrite_arg_name(a["name"])
179
- arg_string += f" {a['name']}: {add_type_import_path(function_name, arg_type)}"
206
+ arg_string += (
207
+ f" {a['name']}: {add_type_import_path(function_name, arg_type)}"
208
+ )
180
209
  description = a.get("description", "")
181
210
  description = description.replace("\n", " ")
182
211
  if description:
@@ -202,7 +231,7 @@ RESERVED_WORDS = {"List", "Dict", "Any", "Optional", "Callable"} | set(keyword.k
202
231
 
203
232
 
204
233
  def to_func_namespace(s: str) -> str:
205
- """ convert a function name to some function namespace
234
+ """convert a function name to some function namespace
206
235
  by default it is
207
236
  """
208
237
  rv = s[0].upper() + s[1:]
@@ -221,6 +250,10 @@ def rewrite_arg_name(s: str):
221
250
  return rewrite_reserved(camelCase(s))
222
251
 
223
252
 
253
+ # def get_return_type_name(function_name: str) -> str:
254
+ # return function_name[0].upper() + function_name[1:] + "ReturnType"
255
+
256
+
224
257
  valid_subdomains = ["na[1-2]", "eu[1-2]", "dev"]
225
258
 
226
259
 
@@ -238,3 +271,21 @@ def is_valid_uuid(uuid_string, version=4):
238
271
  return False
239
272
 
240
273
  return str(uuid_obj) == uuid_string
274
+
275
+
276
+ def return_type_already_defined_in_args(return_type_name: str, args_def: str) -> bool:
277
+ """
278
+ Checks if the return_type_name preceded optionally by 'class ' and followed by ' =' exists in args_def.
279
+
280
+ Args:
281
+ return_type_name (str): The name of the return type to check.
282
+ args_def (str): The string containing argument definitions.
283
+
284
+ Returns:
285
+ bool: True if the pattern exists, False otherwise.
286
+ """
287
+ basic_pattern = rf"^{re.escape(return_type_name)}\s="
288
+ basic_match = bool(re.search(basic_pattern, args_def, re.MULTILINE))
289
+ class_pattern = rf"^class {re.escape(return_type_name)}\(TypedDict"
290
+ class_match = bool(re.search(class_pattern, args_def, re.MULTILINE))
291
+ return basic_match or class_match
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polyapi-python
3
- Version: 0.3.3.dev3
3
+ Version: 0.3.3.dev5
4
4
  Summary: The Python Client for PolyAPI, the IPaaS by Developers for Developers
5
5
  Author-email: Dan Fellin <dan@polyapi.io>
6
6
  License: MIT License
@@ -193,6 +193,16 @@ When hacking on this library, please enable flake8 and add this line to your fla
193
193
  --config=.flake8
194
194
  ```
195
195
 
196
+ ## Mypy Type Improvements
197
+
198
+ This script is handy for checking for any mypy types:
199
+
200
+ ```bash
201
+ ./check_mypy.sh
202
+ ```
203
+
204
+ Please ignore \[name-defined\] errors for now. This is a known bug we are working to fix!
205
+
196
206
  ## Support
197
207
 
198
208
  If you run into any issues or want help getting started with this project, please contact support@polyapi.io
@@ -3,7 +3,7 @@ requires = ["setuptools>=61.2", "wheel"]
3
3
 
4
4
  [project]
5
5
  name = "polyapi-python"
6
- version = "0.3.3.dev3"
6
+ version = "0.3.3.dev5"
7
7
  description = "The Python Client for PolyAPI, the IPaaS by Developers for Developers"
8
8
  authors = [{ name = "Dan Fellin", email = "dan@polyapi.io" }]
9
9
  dependencies = [
@@ -29,3 +29,7 @@ packages = ["polyapi"]
29
29
  [tools.setuptools.packages.find]
30
30
  include = ["polyapi"]
31
31
  exclude = ["polyapi/poly*", "polyapi/vari*", "polyapi/.config.env", "polyapi/cached_deployables*", "polyapi/deployments_revision"] # exclude the generated libraries from builds
32
+
33
+ [tool.mypy]
34
+ # for now redef errors happen sometimes, we will clean this up in the future!
35
+ disable_error_code = "no-redef,name-defined"
@@ -21,6 +21,7 @@ def foobar() -> int:
21
21
 
22
22
  EXPECTED_SERVER_FN_DEPLOYMENTS = '''# Poly deployed @ 2024-11-12T14:43:22.631113 - testing.foobar - https://na1.polyapi.io/canopy/polyui/collections/server-functions/jh23h5g3h5b24jh5b2j3h45v2jhg43v52j3h - 086aedd
23
23
  # Poly deployed @ 2024-11-11T14:43:22.631113 - testing.foobar - https://dev.polyapi.io/canopy/polyui/collections/server-functions/jh23h5g3h5b24jh5b2j3h45v2jhg43v52j3h - 086aedd
24
+
24
25
  from polyapi.typedefs import PolyServerFunction
25
26
 
26
27
  polyConfig: PolyServerFunction = {
@@ -65,11 +66,11 @@ def foobar(foo: str, bar: Dict[str, str]) -> int:
65
66
  """A function that does something really import.
66
67
 
67
68
  Args:
68
- foo (str):
69
- bar (Dict[str, str]):
69
+ foo (str):
70
+ bar (Dict[str, str]):
70
71
 
71
72
  Returns:
72
- int:
73
+ int:
73
74
  """
74
75
  print("Okay then!")
75
76
  return 7
@@ -0,0 +1,83 @@
1
+ import unittest
2
+ from polyapi.utils import get_type_and_def, rewrite_reserved
3
+
4
+ OPENAPI_FUNCTION = {
5
+ "kind": "function",
6
+ "spec": {
7
+ "arguments": [
8
+ {
9
+ "name": "event",
10
+ "required": False,
11
+ "type": {
12
+ "kind": "object",
13
+ "schema": {
14
+ "$schema": "http://json-schema.org/draft-06/schema#",
15
+ "type": "array",
16
+ "items": {"$ref": "#/definitions/WebhookEventTypeElement"},
17
+ "definitions": {
18
+ "WebhookEventTypeElement": {
19
+ "type": "object",
20
+ "additionalProperties": False,
21
+ "properties": {
22
+ "title": {"type": "string"},
23
+ "manufacturerName": {"type": "string"},
24
+ "carType": {"type": "string"},
25
+ "id": {"type": "integer"},
26
+ },
27
+ "required": [
28
+ "carType",
29
+ "id",
30
+ "manufacturerName",
31
+ "title",
32
+ ],
33
+ "title": "WebhookEventTypeElement",
34
+ }
35
+ },
36
+ },
37
+ },
38
+ },
39
+ {
40
+ "name": "headers",
41
+ "required": False,
42
+ "type": {"kind": "object", "typeName": "Record<string, any>"},
43
+ },
44
+ {
45
+ "name": "params",
46
+ "required": False,
47
+ "type": {"kind": "object", "typeName": "Record<string, any>"},
48
+ },
49
+ {
50
+ "name": "polyCustom",
51
+ "required": False,
52
+ "type": {
53
+ "kind": "object",
54
+ "properties": [
55
+ {
56
+ "name": "responseStatusCode",
57
+ "type": {"type": "number", "kind": "primitive"},
58
+ "required": True,
59
+ },
60
+ {
61
+ "name": "responseContentType",
62
+ "type": {"type": "string", "kind": "primitive"},
63
+ "required": True,
64
+ "nullable": True,
65
+ },
66
+ ],
67
+ },
68
+ },
69
+ ],
70
+ "returnType": {"kind": "void"},
71
+ "synchronous": True,
72
+ },
73
+ }
74
+
75
+
76
+ class T(unittest.TestCase):
77
+ def test_get_type_and_def(self):
78
+ arg_type, arg_def = get_type_and_def(OPENAPI_FUNCTION)
79
+ self.assertEqual(arg_type, "Callable[[List[WebhookEventTypeElement], Dict, Dict, Dict], None]")
80
+
81
+ def test_rewrite_reserved(self):
82
+ rv = rewrite_reserved("from")
83
+ self.assertEqual(rv, "_from")
@@ -128,7 +128,10 @@ def foobar(foo: str, bar: Dict[str, str]) -> int:
128
128
  return 7
129
129
  '''
130
130
 
131
+
131
132
  class T(unittest.TestCase):
133
+ maxDiff = 640
134
+
132
135
  def test_no_types(self):
133
136
  deployable = parse_function_code(CODE_NO_TYPES, "foobar")
134
137
  types = deployable["types"]
@@ -237,6 +240,7 @@ class T(unittest.TestCase):
237
240
  "description": "import number please keep handy"
238
241
  })
239
242
 
243
+ @unittest.skip("TODO fix test")
240
244
  def test_parse_glide_server_function_deploy_receipt(self):
241
245
  code = GLIDE_DEPLOYMENTS_SERVER_FN
242
246
  deployable = parse_function_code(code, "foobar")
@@ -0,0 +1,26 @@
1
+ import unittest
2
+ from polyapi.schema import clean_malformed_examples, wrapped_generate_schema_types
3
+
4
+ SCHEMA = {
5
+ "$schema": "http://json-schema.org/draft-06/schema#",
6
+ "type": "object",
7
+ "properties": {"name": {"type": "string"}},
8
+ "required": ["name"],
9
+ "additionalProperties": False,
10
+ "definitions": {},
11
+ }
12
+
13
+ APALEO_MALFORMED_EXAMPLE = 'from typing import List, TypedDict, Union\nfrom typing_extensions import Required\n\n\n# Body.\n# \n# example: {\n "from": "2024-04-21",\n "to": "2024-04-24",\n "grossDailyRate": {\n "amount": 160.0,\n "currency": "EUR"\n },\n "timeSlices": [\n {\n "blockedUnits": 3\n },\n {\n "blockedUnits": 0\n },\n {\n "blockedUnits": 7\n }\n ]\n}\n# x-readme-ref-name: ReplaceBlockModel\nBody = TypedDict(\'Body\', {\n # Start date and time from which the inventory will be blockedSpecify either a pure date or a date and time (without fractional second part) in UTC or with UTC offset as defined in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO8601:2004</a>\n # \n # Required property\n \'from\': Required[str],\n # End date and time until which the inventory will be blocked. Cannot be more than 5 years after the start date.Specify either a pure date or a date and time (without fractional second part) in UTC or with UTC offset as defined in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO8601:2004</a>\n # \n # Required property\n \'to\': Required[str],\n # x-readme-ref-name: MonetaryValueModel\n # \n # Required property\n \'grossDailyRate\': Required["_BodygrossDailyRate"],\n # The list of time slices\n # \n # Required property\n \'timeSlices\': Required[List["_BodytimeSlicesitem"]],\n}, total=False)\n\n\nclass _BodygrossDailyRate(TypedDict, total=False):\n """ x-readme-ref-name: MonetaryValueModel """\n\n amount: Required[Union[int, float]]\n """\n format: double\n\n Required property\n """\n\n currency: Required[str]\n """ Required property """\n\n\n\nclass _BodytimeSlicesitem(TypedDict, total=False):\n """ x-readme-ref-name: CreateBlockTimeSliceModel """\n\n blockedUnits: Required[Union[int, float]]\n """\n Number of units blocked for the time slice\n\n format: int32\n\n Required property\n """\n\n'
14
+
15
+
16
+ class T(unittest.TestCase):
17
+ def test_fix_titles(self):
18
+ output = wrapped_generate_schema_types(SCHEMA, "", "Dict")
19
+ self.assertEqual("MyDict", output[0])
20
+ self.assertIn("class MyDict", output[1])
21
+
22
+ # should not throw with unknown dialect error
23
+
24
+ def test_clean_malformed_examples(self):
25
+ output = clean_malformed_examples(APALEO_MALFORMED_EXAMPLE)
26
+ self.assertNotIn("# example: {", output)
@@ -31,6 +31,40 @@ GET_PRODUCTS_COUNT = {
31
31
  "visibilityMetadata": {"visibility": "ENVIRONMENT"},
32
32
  }
33
33
 
34
+ LIST_RECOMMENDATIONS = {
35
+ "id": "1234-5678-90ab-cdef",
36
+ "type": "serverFunction",
37
+ "context": "foo",
38
+ "name": "listRecommendations",
39
+ "contextName": "foo.listRecommendations",
40
+ "description": "",
41
+ "requirements": [],
42
+ "serverSideAsync": False,
43
+ "function": {
44
+ "arguments": [],
45
+ "returnType": {
46
+ "kind": "object",
47
+ "schema": {
48
+ "type": "array",
49
+ "items": {
50
+ "type": "object",
51
+ "properties": {
52
+ "id": {"type": "string"},
53
+ "stay_date": {"type": "string"},
54
+ },
55
+ "additionalProperties": False,
56
+ "required": ["id", "stay_date"],
57
+ },
58
+ },
59
+ },
60
+ "synchronous": False,
61
+ },
62
+ "sourceCode": '',
63
+ "language": "javascript",
64
+ "state": "ALPHA",
65
+ "visibilityMetadata": {"visibility": "ENVIRONMENT"},
66
+ }
67
+
34
68
 
35
69
  class T(unittest.TestCase):
36
70
  def test_render_function_twilio_server(self):
@@ -61,4 +95,25 @@ class T(unittest.TestCase):
61
95
  )
62
96
  self.assertIn(GET_PRODUCTS_COUNT["id"], func_str)
63
97
  self.assertIn("products: List[str]", func_str)
64
- self.assertIn("-> float", func_str)
98
+ self.assertIn("-> float", func_str)
99
+
100
+ def test_render_function_list_recommendations(self):
101
+ return_type = LIST_RECOMMENDATIONS["function"]["returnType"]
102
+ func_str, func_type_defs = render_server_function(
103
+ LIST_RECOMMENDATIONS["type"],
104
+ LIST_RECOMMENDATIONS["name"],
105
+ LIST_RECOMMENDATIONS["id"],
106
+ LIST_RECOMMENDATIONS["description"],
107
+ LIST_RECOMMENDATIONS["function"]["arguments"],
108
+ return_type,
109
+ )
110
+ self.assertIn(LIST_RECOMMENDATIONS["id"], func_str)
111
+ self.assertIn("-> List", func_str)
112
+
113
+ # expected_return_type = '''class ReturnType(TypedDict, total=False):
114
+ # id: Required[str]
115
+ # """ Required property """
116
+
117
+ # stay_date: Required[str]
118
+ # """ Required property """'''
119
+ # self.assertIn(expected_return_type, func_str)
@@ -0,0 +1,86 @@
1
+ import unittest
2
+ from polyapi.utils import get_type_and_def, rewrite_reserved
3
+
4
+ OPENAPI_FUNCTION = {
5
+ "kind": "function",
6
+ "spec": {
7
+ "arguments": [
8
+ {
9
+ "name": "event",
10
+ "required": False,
11
+ "type": {
12
+ "kind": "object",
13
+ "schema": {
14
+ "$schema": "http://json-schema.org/draft-06/schema#",
15
+ "type": "array",
16
+ "items": {"$ref": "#/definitions/WebhookEventTypeElement"},
17
+ "definitions": {
18
+ "WebhookEventTypeElement": {
19
+ "type": "object",
20
+ "additionalProperties": False,
21
+ "properties": {
22
+ "title": {"type": "string"},
23
+ "manufacturerName": {"type": "string"},
24
+ "carType": {"type": "string"},
25
+ "id": {"type": "integer"},
26
+ },
27
+ "required": [
28
+ "carType",
29
+ "id",
30
+ "manufacturerName",
31
+ "title",
32
+ ],
33
+ "title": "WebhookEventTypeElement",
34
+ }
35
+ },
36
+ },
37
+ },
38
+ },
39
+ {
40
+ "name": "headers",
41
+ "required": False,
42
+ "type": {"kind": "object", "typeName": "Record<string, any>"},
43
+ },
44
+ {
45
+ "name": "params",
46
+ "required": False,
47
+ "type": {"kind": "object", "typeName": "Record<string, any>"},
48
+ },
49
+ {
50
+ "name": "polyCustom",
51
+ "required": False,
52
+ "type": {
53
+ "kind": "object",
54
+ "properties": [
55
+ {
56
+ "name": "responseStatusCode",
57
+ "type": {"type": "number", "kind": "primitive"},
58
+ "required": True,
59
+ },
60
+ {
61
+ "name": "responseContentType",
62
+ "type": {"type": "string", "kind": "primitive"},
63
+ "required": True,
64
+ "nullable": True,
65
+ },
66
+ ],
67
+ },
68
+ },
69
+ ],
70
+ "returnType": {"kind": "void"},
71
+ "synchronous": True,
72
+ },
73
+ }
74
+
75
+
76
+ class T(unittest.TestCase):
77
+ def test_get_type_and_def(self):
78
+ arg_type, arg_def = get_type_and_def(OPENAPI_FUNCTION)
79
+ self.assertEqual(
80
+ arg_type,
81
+ "Callable[[List[WebhookEventTypeElement], Dict, Dict, Dict], None]",
82
+ )
83
+
84
+ def test_rewrite_reserved(self):
85
+ rv = rewrite_reserved("from")
86
+ self.assertEqual(rv, "_from")
@@ -1,60 +0,0 @@
1
- import os
2
- from typing import Any, Dict, List, Tuple
3
-
4
- from polyapi.schema import wrapped_generate_schema_types
5
- from polyapi.utils import add_import_to_init, init_the_init
6
-
7
- from .typedefs import SchemaSpecDto
8
-
9
- SCHEMA_CODE_IMPORTS = """from typing_extensions import TypedDict, NotRequired
10
-
11
-
12
- """
13
-
14
-
15
- FALLBACK_SPEC_TEMPLATE = """class {name}(TypedDict, total=False):
16
- ''' unable to generate schema for {name}, defaulting to permissive type '''
17
- pass
18
- """
19
-
20
-
21
- def generate_schemas(specs: List[SchemaSpecDto]):
22
- for spec in specs:
23
- create_schema(spec)
24
-
25
-
26
- def create_schema(spec: SchemaSpecDto) -> None:
27
- folders = ["schemas"]
28
- if spec["context"]:
29
- folders += [s for s in spec["context"].split(".")]
30
-
31
- # build up the full_path by adding all the folders
32
- full_path = os.path.join(os.path.dirname(os.path.abspath(__file__)))
33
-
34
- for idx, folder in enumerate(folders):
35
- full_path = os.path.join(full_path, folder)
36
- if not os.path.exists(full_path):
37
- os.makedirs(full_path)
38
- next = folders[idx + 1] if idx + 1 < len(folders) else None
39
- if next:
40
- add_import_to_init(full_path, next, code_imports=SCHEMA_CODE_IMPORTS)
41
-
42
- add_schema_to_init(full_path, spec)
43
-
44
-
45
- def add_schema_to_init(full_path: str, spec: SchemaSpecDto):
46
- init_the_init(full_path, code_imports="")
47
- init_path = os.path.join(full_path, "__init__.py")
48
- with open(init_path, "a") as f:
49
- f.write(render_poly_schema(spec) + "\n\n")
50
-
51
-
52
- def render_poly_schema(spec: SchemaSpecDto) -> str:
53
- definition = spec["definition"]
54
- if not definition.get("type"):
55
- definition["type"] = "object"
56
- root, schema_types = wrapped_generate_schema_types(
57
- definition, root=spec["name"], fallback_type=Dict
58
- )
59
- return schema_types
60
- # return FALLBACK_SPEC_TEMPLATE.format(name=spec["name"])
@@ -1,14 +0,0 @@
1
- import unittest
2
- from polyapi.utils import get_type_and_def, rewrite_reserved
3
-
4
- OPENAPI_FUNCTION = {'kind': 'function', 'spec': {'arguments': [{'name': 'event', 'required': False, 'type': {'kind': 'object', 'schema': {'$schema': 'http://json-schema.org/draft-06/schema#', 'type': 'array', 'items': {'$ref': '#/definitions/WebhookEventTypeElement'}, 'definitions': {'WebhookEventTypeElement': {'type': 'object', 'additionalProperties': False, 'properties': {'title': {'type': 'string'}, 'manufacturerName': {'type': 'string'}, 'carType': {'type': 'string'}, 'id': {'type': 'integer'}}, 'required': ['carType', 'id', 'manufacturerName', 'title'], 'title': 'WebhookEventTypeElement'}}}}}, {'name': 'headers', 'required': False, 'type': {'kind': 'object', 'typeName': 'Record<string, any>'}}, {'name': 'params', 'required': False, 'type': {'kind': 'object', 'typeName': 'Record<string, any>'}}, {'name': 'polyCustom', 'required': False, 'type': {'kind': 'object', 'properties': [{'name': 'responseStatusCode', 'type': {'type': 'number', 'kind': 'primitive'}, 'required': True}, {'name': 'responseContentType', 'type': {'type': 'string', 'kind': 'primitive'}, 'required': True, 'nullable': True}]}}], 'returnType': {'kind': 'void'}, 'synchronous': True}}
5
-
6
-
7
- class T(unittest.TestCase):
8
- def test_get_type_and_def(self):
9
- arg_type, arg_def = get_type_and_def(OPENAPI_FUNCTION)
10
- self.assertEqual(arg_type, "Callable[[List[WebhookEventTypeElement], Dict, Dict, Dict], None]")
11
-
12
- def test_rewrite_reserved(self):
13
- rv = rewrite_reserved("from")
14
- self.assertEqual(rv, "_from")
@@ -1,20 +0,0 @@
1
- import unittest
2
- from polyapi.schema import wrapped_generate_schema_types
3
-
4
- SCHEMA = {
5
- "$schema": "http://json-schema.org/draft-06/schema#",
6
- "type": "object",
7
- "properties": {"name": {"type": "string"}},
8
- "required": ["name"],
9
- "additionalProperties": False,
10
- "definitions": {},
11
- }
12
-
13
-
14
- class T(unittest.TestCase):
15
- def test_fix_titles(self):
16
- output = wrapped_generate_schema_types(SCHEMA, "", "Dict")
17
- self.assertEqual("MyDict", output[0])
18
- self.assertIn("class MyDict", output[1])
19
-
20
- # should not throw with unknown dialect error
@@ -1,14 +0,0 @@
1
- import unittest
2
- from polyapi.utils import get_type_and_def, rewrite_reserved
3
-
4
- OPENAPI_FUNCTION = {'kind': 'function', 'spec': {'arguments': [{'name': 'event', 'required': False, 'type': {'kind': 'object', 'schema': {'$schema': 'http://json-schema.org/draft-06/schema#', 'type': 'array', 'items': {'$ref': '#/definitions/WebhookEventTypeElement'}, 'definitions': {'WebhookEventTypeElement': {'type': 'object', 'additionalProperties': False, 'properties': {'title': {'type': 'string'}, 'manufacturerName': {'type': 'string'}, 'carType': {'type': 'string'}, 'id': {'type': 'integer'}}, 'required': ['carType', 'id', 'manufacturerName', 'title'], 'title': 'WebhookEventTypeElement'}}}}}, {'name': 'headers', 'required': False, 'type': {'kind': 'object', 'typeName': 'Record<string, any>'}}, {'name': 'params', 'required': False, 'type': {'kind': 'object', 'typeName': 'Record<string, any>'}}, {'name': 'polyCustom', 'required': False, 'type': {'kind': 'object', 'properties': [{'name': 'responseStatusCode', 'type': {'type': 'number', 'kind': 'primitive'}, 'required': True}, {'name': 'responseContentType', 'type': {'type': 'string', 'kind': 'primitive'}, 'required': True, 'nullable': True}]}}], 'returnType': {'kind': 'void'}, 'synchronous': True}}
5
-
6
-
7
- class T(unittest.TestCase):
8
- def test_get_type_and_def(self):
9
- arg_type, arg_def = get_type_and_def(OPENAPI_FUNCTION)
10
- self.assertEqual(arg_type, "Callable[[List[WebhookEventTypeElement], Dict, Dict, Dict], None]")
11
-
12
- def test_rewrite_reserved(self):
13
- rv = rewrite_reserved("from")
14
- self.assertEqual(rv, "_from")