polyapi-python 0.2.3.dev5__tar.gz → 0.2.3.dev7__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 (36) hide show
  1. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/PKG-INFO +1 -1
  2. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/cli.py +3 -2
  3. polyapi-python-0.2.3.dev7/polyapi/client.py +25 -0
  4. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/function_cli.py +12 -8
  5. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/generate.py +49 -69
  6. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/typedefs.py +4 -2
  7. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi_python.egg-info/PKG-INFO +1 -1
  8. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi_python.egg-info/SOURCES.txt +1 -0
  9. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/pyproject.toml +1 -1
  10. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/tests/test_function_cli.py +2 -2
  11. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/LICENSE +0 -0
  12. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/README.md +0 -0
  13. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/__init__.py +0 -0
  14. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/__main__.py +0 -0
  15. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/api.py +0 -0
  16. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/auth.py +0 -0
  17. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/config.py +0 -0
  18. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/constants.py +0 -0
  19. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/error_handler.py +0 -0
  20. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/exceptions.py +0 -0
  21. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/execute.py +0 -0
  22. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/py.typed +0 -0
  23. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/schema.py +0 -0
  24. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/server.py +0 -0
  25. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/utils.py +0 -0
  26. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/variables.py +0 -0
  27. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi/webhook.py +0 -0
  28. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi_python.egg-info/dependency_links.txt +0 -0
  29. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi_python.egg-info/requires.txt +0 -0
  30. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/polyapi_python.egg-info/top_level.txt +0 -0
  31. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/setup.cfg +0 -0
  32. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/tests/test_api.py +0 -0
  33. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/tests/test_auth.py +0 -0
  34. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/tests/test_server.py +0 -0
  35. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/tests/test_utils.py +0 -0
  36. {polyapi-python-0.2.3.dev5 → polyapi-python-0.2.3.dev7}/tests/test_variables.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: polyapi-python
3
- Version: 0.2.3.dev5
3
+ Version: 0.2.3.dev7
4
4
  Summary: The PolyAPI Python Client
5
5
  Author-email: Dan Fellin <dan@polyapi.io>
6
6
  License: MIT License
@@ -23,7 +23,8 @@ def execute_from_cli():
23
23
  )
24
24
  parser.add_argument("--context", required=False, default="")
25
25
  parser.add_argument("--description", required=False, default="")
26
- parser.add_argument("--server", action="store_true", help="Pass --server when adding function to add a server function. By default, new functions are client.")
26
+ parser.add_argument("--client", action="store_true", help="Pass --client when adding function to add a client function.")
27
+ parser.add_argument("--server", action="store_true", help="Pass --server when adding function to add a server function.")
27
28
  parser.add_argument("--logs", action="store_true", help="Pass --logs when adding function if you want to store and see the function logs.")
28
29
  parser.add_argument("command", choices=CLI_COMMANDS)
29
30
  parser.add_argument("subcommands", nargs="*")
@@ -47,4 +48,4 @@ def execute_from_cli():
47
48
  print("Clearing the generated library...")
48
49
  clear()
49
50
  elif command == "function":
50
- function_add_or_update(args.context, args.description, args.server, args.logs, args.subcommands)
51
+ function_add_or_update(args.context, args.description, args.client, args.server, args.logs, args.subcommands)
@@ -0,0 +1,25 @@
1
+ from typing import Any, Dict, List, Tuple
2
+
3
+ from polyapi.typedefs import PropertySpecification
4
+ from polyapi.utils import camelCase, add_type_import_path, parse_arguments, get_type_and_def
5
+
6
+ DEFS_TEMPLATE = """
7
+ from typing import List, Dict, Any, TypedDict
8
+ {args_def}
9
+ {return_type_def}
10
+ """
11
+
12
+
13
+ def render_client_function(
14
+ function_name: str,
15
+ code: str,
16
+ arguments: List[PropertySpecification],
17
+ return_type: Dict[str, Any],
18
+ ) -> Tuple[str, str]:
19
+ args, args_def = parse_arguments(function_name, arguments)
20
+ return_type_name, return_type_def = get_type_and_def(return_type) # type: ignore
21
+ func_type_defs = DEFS_TEMPLATE.format(
22
+ args_def=args_def,
23
+ return_type_def=return_type_def,
24
+ )
25
+ return code, func_type_defs
@@ -175,7 +175,7 @@ def _func_already_exists(context: str, function_name: str) -> bool:
175
175
 
176
176
 
177
177
  def function_add_or_update(
178
- context: str, description: str, server: bool, logs_enabled: bool, subcommands: List
178
+ context: str, description: str, client: bool, server: bool, logs_enabled: bool, subcommands: List
179
179
  ):
180
180
  parser = argparse.ArgumentParser()
181
181
  parser.add_argument("subcommand", choices=["add"])
@@ -184,7 +184,8 @@ def function_add_or_update(
184
184
  args = parser.parse_args(subcommands)
185
185
 
186
186
  verb = "Updating" if _func_already_exists(context, args.function_name) else "Adding"
187
- print(f"{verb} custom server side function...", end="")
187
+ ftype = "server" if server else "client"
188
+ print(f"{verb} custom {ftype} function...", end="")
188
189
 
189
190
  with open(args.filename, "r") as f:
190
191
  code = f.read()
@@ -202,9 +203,6 @@ def function_add_or_update(
202
203
  print(f"Function {args.function_name} not found as top-level function in {args.filename}")
203
204
  sys.exit(1)
204
205
 
205
- if requirements:
206
- print_yellow('\nPlease note that deploying your functions will take a few minutes because it makes use of libraries other than polyapi.')
207
-
208
206
  data = {
209
207
  "context": context,
210
208
  "name": args.function_name,
@@ -213,18 +211,24 @@ def function_add_or_update(
213
211
  "language": "python",
214
212
  "returnType": return_type,
215
213
  "returnTypeSchema": return_type_schema,
216
- "requirements": requirements,
217
214
  "arguments": arguments,
218
215
  "logsEnabled": logs_enabled,
219
216
  }
220
217
 
218
+ if server and requirements:
219
+ print_yellow('\nPlease note that deploying your functions will take a few minutes because it makes use of libraries other than polyapi.')
220
+ data["requirements"] = requirements
221
+
221
222
  api_key, api_url = get_api_key_and_url()
222
223
  assert api_key
223
224
  if server:
224
225
  url = f"{api_url}/functions/server"
226
+ elif client:
227
+ url = f"{api_url}/functions/client"
225
228
  else:
226
- raise NotImplementedError("Client functions not yet implemented.")
227
- # url = f"{base_url}/functions/client"
229
+ print_red("ERROR")
230
+ print("Please specify type of function with --client or --server")
231
+ sys.exit(1)
228
232
 
229
233
  headers = get_auth_headers(api_key)
230
234
  resp = requests.post(url, headers=headers, json=data)
@@ -2,9 +2,10 @@ import json
2
2
  import requests
3
3
  import os
4
4
  import shutil
5
- from typing import Any, Dict, List, Tuple
5
+ from typing import List
6
6
 
7
7
  from polyapi.auth import render_auth_function
8
+ from polyapi.client import render_client_function
8
9
  from polyapi.execute import execute_post
9
10
  from polyapi.webhook import render_webhook_handle
10
11
 
@@ -18,6 +19,7 @@ from .config import get_api_key_and_url, initialize_config
18
19
  SUPPORTED_FUNCTION_TYPES = {
19
20
  "apiFunction",
20
21
  "authFunction",
22
+ "customFunction",
21
23
  "serverFunction",
22
24
  "webhookHandle",
23
25
  }
@@ -38,32 +40,29 @@ def get_specs() -> List:
38
40
 
39
41
 
40
42
  def parse_function_specs(
41
- specs: List, limit_ids: List[str] | None # optional list of ids to limit to
42
- ) -> List[Tuple[str, str, str, str, List[PropertySpecification], Dict[str, Any]]]:
43
+ specs: List[SpecificationDto],
44
+ limit_ids: List[str] | None, # optional list of ids to limit to
45
+ ) -> List[SpecificationDto]:
43
46
  functions = []
44
47
  for spec in specs:
48
+ if not spec or "function" not in spec:
49
+ continue
50
+
51
+ if not spec["function"]:
52
+ continue
53
+
45
54
  if limit_ids and spec["id"] not in limit_ids:
46
55
  continue
47
56
 
48
57
  if spec["type"] not in SUPPORTED_FUNCTION_TYPES:
49
58
  continue
50
59
 
51
- function_type = spec["type"]
52
- function_name = f"poly.{spec['context']}.{spec['name']}"
53
- function_id = spec["id"]
54
- arguments: List[PropertySpecification] = [
55
- arg for arg in spec["function"]["arguments"]
56
- ]
57
- functions.append(
58
- (
59
- function_type,
60
- function_name,
61
- function_id,
62
- spec["description"],
63
- arguments,
64
- spec["function"]["returnType"],
65
- )
66
- )
60
+ if spec["type"] == "customFunction" and spec["language"] != "python":
61
+ # poly libraries only support client functions of same language
62
+ continue
63
+
64
+ functions.append(spec)
65
+
67
66
  return functions
68
67
 
69
68
 
@@ -90,11 +89,10 @@ def read_cached_specs() -> List[SpecificationDto]:
90
89
  return json.loads(f.read())
91
90
 
92
91
 
93
- def get_functions_and_parse(limit_ids: List[str] | None = None):
92
+ def get_functions_and_parse(limit_ids: List[str] | None = None) -> List[SpecificationDto]:
94
93
  specs = get_specs()
95
94
  cache_specs(specs)
96
- functions = parse_function_specs(specs, limit_ids=limit_ids)
97
- return functions
95
+ return parse_function_specs(specs, limit_ids=limit_ids)
98
96
 
99
97
 
100
98
  def get_variables() -> List[VariableSpecDto]:
@@ -162,14 +160,7 @@ def save_rendered_specs() -> None:
162
160
  api_specs = [spec for spec in specs if spec["type"] == "apiFunction"]
163
161
  for spec in api_specs:
164
162
  assert spec["function"]
165
- func_str, type_defs = render_spec(
166
- spec["type"],
167
- spec["name"],
168
- spec["id"],
169
- spec["description"],
170
- spec["function"]["arguments"],
171
- spec["function"]["returnType"],
172
- )
163
+ func_str, type_defs = render_spec(spec)
173
164
  data = {
174
165
  "language": "python",
175
166
  "apiFunctionId": spec["id"],
@@ -181,14 +172,20 @@ def save_rendered_specs() -> None:
181
172
  assert resp.status_code == 201, (resp.text, resp.status_code)
182
173
 
183
174
 
184
- def render_spec(
185
- function_type: str,
186
- function_name: str,
187
- function_id: str,
188
- function_description: str,
189
- arguments: List[PropertySpecification],
190
- return_type: Dict[str, Any],
191
- ):
175
+ def render_spec(spec: SpecificationDto):
176
+ function_type = spec["type"]
177
+ function_description = spec["description"]
178
+ function_name = spec["name"]
179
+ function_id = spec["id"]
180
+
181
+ arguments: List[PropertySpecification] = []
182
+ return_type = {}
183
+ if spec["function"]:
184
+ arguments = [
185
+ arg for arg in spec["function"]["arguments"]
186
+ ]
187
+ return_type = spec["function"]["returnType"]
188
+
192
189
  if function_type == "apiFunction":
193
190
  func_str, func_type_defs = render_api_function(
194
191
  function_type,
@@ -198,6 +195,13 @@ def render_spec(
198
195
  arguments,
199
196
  return_type,
200
197
  )
198
+ elif function_type == "customFunction":
199
+ func_str, func_type_defs = render_client_function(
200
+ function_name,
201
+ spec["code"],
202
+ arguments,
203
+ return_type,
204
+ )
201
205
  elif function_type == "serverFunction":
202
206
  func_str, func_type_defs = render_server_function(
203
207
  function_type,
@@ -229,25 +233,14 @@ def render_spec(
229
233
 
230
234
 
231
235
  def add_function_file(
232
- function_type: str,
233
236
  full_path: str,
234
237
  function_name: str,
235
- function_id: str,
236
- function_description: str,
237
- arguments: List[PropertySpecification],
238
- return_type: Dict[str, Any],
238
+ spec: SpecificationDto,
239
239
  ):
240
240
  # first lets add the import to the __init__
241
241
  init_the_init(full_path)
242
242
 
243
- func_str, func_type_defs = render_spec(
244
- function_type,
245
- function_name,
246
- function_id,
247
- function_description,
248
- arguments,
249
- return_type,
250
- )
243
+ func_str, func_type_defs = render_spec(spec)
251
244
 
252
245
  if func_str:
253
246
  # add function to init
@@ -262,27 +255,17 @@ def add_function_file(
262
255
 
263
256
 
264
257
  def create_function(
265
- function_type: str,
266
- path: str,
267
- function_id: str,
268
- function_description: str,
269
- arguments: List[PropertySpecification],
270
- return_type: Dict[str, Any],
258
+ spec: SpecificationDto
271
259
  ) -> None:
272
260
  full_path = os.path.dirname(os.path.abspath(__file__))
273
-
274
- folders = path.split(".")
261
+ folders = f"poly.{spec['context']}.{spec['name']}".split(".")
275
262
  for idx, folder in enumerate(folders):
276
263
  if idx + 1 == len(folders):
277
264
  # special handling for final level
278
265
  add_function_file(
279
- function_type,
280
266
  full_path,
281
267
  folder,
282
- function_id,
283
- function_description,
284
- arguments,
285
- return_type,
268
+ spec,
286
269
  )
287
270
  else:
288
271
  full_path = os.path.join(full_path, folder)
@@ -296,9 +279,6 @@ def create_function(
296
279
  add_import_to_init(full_path, next)
297
280
 
298
281
 
299
- # TODO create the socket and pass to create_function?
300
-
301
-
302
- def generate_functions(functions: List) -> None:
282
+ def generate_functions(functions: List[SpecificationDto]) -> None:
303
283
  for func in functions:
304
- create_function(*func)
284
+ create_function(func)
@@ -32,9 +32,11 @@ class SpecificationDto(TypedDict):
32
32
  context: str
33
33
  name: str
34
34
  description: str
35
- # function is none if this is actually VariableSpecDto
36
- function: FunctionSpecification | None
35
+ # function is none (or function key not present) if this is actually VariableSpecDto
36
+ function: NotRequired[FunctionSpecification | None]
37
37
  type: Literal['apiFunction', 'customFunction', 'serverFunction', 'authFunction', 'webhookHandle', 'serverVariable']
38
+ code: NotRequired[str]
39
+ language: str
38
40
 
39
41
 
40
42
  class VariableSpecification(TypedDict):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: polyapi-python
3
- Version: 0.2.3.dev5
3
+ Version: 0.2.3.dev7
4
4
  Summary: The PolyAPI Python Client
5
5
  Author-email: Dan Fellin <dan@polyapi.io>
6
6
  License: MIT License
@@ -6,6 +6,7 @@ polyapi/__main__.py
6
6
  polyapi/api.py
7
7
  polyapi/auth.py
8
8
  polyapi/cli.py
9
+ polyapi/client.py
9
10
  polyapi/config.py
10
11
  polyapi/constants.py
11
12
  polyapi/error_handler.py
@@ -3,7 +3,7 @@ requires = ["setuptools>=61.2", "wheel"]
3
3
 
4
4
  [project]
5
5
  name = "polyapi-python"
6
- version = "0.2.3.dev5"
6
+ version = "0.2.3.dev7"
7
7
  description = "The PolyAPI Python Client"
8
8
  authors = [{ name = "Dan Fellin", email = "dan@polyapi.io" }]
9
9
  dependencies = [
@@ -79,12 +79,12 @@ class T(unittest.TestCase):
79
79
  def test_parse_import_basic(self):
80
80
  code = "import flask\n\n\ndef foobar(n: int) -> int:\n return 9\n"
81
81
  _, _, _, additional_requirements = _parse_code(code, "foobar")
82
- self.assertEqual(additional_requirements, ["flask"])
82
+ self.assertEqual(additional_requirements, ["Flask"])
83
83
 
84
84
  def test_parse_import_from(self):
85
85
  code = "from flask import Request, Response\n\n\ndef foobar(n: int) -> int:\n return 9\n"
86
86
  _, _, _, additional_requirements = _parse_code(code, "foobar")
87
- self.assertEqual(additional_requirements, ["flask"])
87
+ self.assertEqual(additional_requirements, ["Flask"])
88
88
 
89
89
  def test_parse_import_base(self):
90
90
  code = "import requests\n\n\ndef foobar(n: int) -> int:\n return 9\n"