polyapi-python 0.3.13.dev1__py3-none-any.whl → 0.3.14.dev1__py3-none-any.whl

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.
polyapi/api.py CHANGED
@@ -35,6 +35,25 @@ def {function_name}(
35
35
  return {api_response_type}(resp.json()) # type: ignore
36
36
 
37
37
 
38
+ async def {function_name}_async(
39
+ {args}
40
+ ) -> {api_response_type}:
41
+ \"""{function_description}
42
+
43
+ Function ID: {function_id}
44
+ \"""
45
+ if get_direct_execute_config():
46
+ resp = await direct_execute_async("{function_type}", "{function_id}", {data})
47
+ return {api_response_type}({{
48
+ "status": resp.status_code,
49
+ "headers": dict(resp.headers),
50
+ "data": resp.json()
51
+ }}) # type: ignore
52
+ else:
53
+ resp = await execute_async("{function_type}", "{function_id}", {data})
54
+ return {api_response_type}(resp.json()) # type: ignore
55
+
56
+
38
57
  """
39
58
 
40
59
 
polyapi/auth.py CHANGED
@@ -117,6 +117,16 @@ def introspectToken(token: str) -> AuthFunctionResponse:
117
117
  url = "/auth-providers/{function_id}/introspect"
118
118
  resp = execute_post(url, {{"token": token}})
119
119
  return resp.json()
120
+
121
+
122
+ async def introspectToken_async(token: str) -> AuthFunctionResponse:
123
+ \"""{description}
124
+
125
+ Function ID: {function_id}
126
+ \"""
127
+ url = "/auth-providers/{function_id}/introspect"
128
+ resp = await execute_post_async(url, {{"token": token}})
129
+ return resp.json()
120
130
  """
121
131
 
122
132
  REFRESH_TOKEN_TEMPLATE = """
@@ -128,6 +138,16 @@ def refreshToken(token: str) -> AuthFunctionResponse:
128
138
  url = "/auth-providers/{function_id}/refresh"
129
139
  resp = execute_post(url, {{"token": token}})
130
140
  return resp.json()
141
+
142
+
143
+ async def refreshToken_async(token: str) -> AuthFunctionResponse:
144
+ \"""{description}
145
+
146
+ Function ID: {function_id}
147
+ \"""
148
+ url = "/auth-providers/{function_id}/refresh"
149
+ resp = await execute_post_async(url, {{"token": token}})
150
+ return resp.json()
131
151
  """
132
152
 
133
153
  REVOKE_TOKEN_TEMPLATE = """
@@ -142,6 +162,19 @@ def revokeToken(token: str) -> Optional[AuthFunctionResponse]:
142
162
  return resp.json()
143
163
  except:
144
164
  return None
165
+
166
+
167
+ async def revokeToken_async(token: str) -> Optional[AuthFunctionResponse]:
168
+ \"""{description}
169
+
170
+ Function ID: {function_id}
171
+ \"""
172
+ url = "/auth-providers/{function_id}/revoke"
173
+ resp = await execute_post_async(url, {{"token": token}})
174
+ try:
175
+ return resp.json()
176
+ except:
177
+ return None
145
178
  """
146
179
 
147
180
 
polyapi/execute.py CHANGED
@@ -1,109 +1,224 @@
1
- from typing import Dict, Optional
2
- import requests
1
+ import httpx
3
2
  import os
4
3
  import logging
5
- from requests import Response
6
4
  from polyapi.config import get_api_key_and_url, get_mtls_config
7
5
  from polyapi.exceptions import PolyApiException
6
+ from polyapi import http_client
8
7
 
9
8
  logger = logging.getLogger("poly")
10
9
 
11
- def direct_execute(function_type, function_id, data) -> Response:
12
- """ execute a specific function id/type
13
- """
14
- api_key, api_url = get_api_key_and_url()
15
- headers = {"Authorization": f"Bearer {api_key}"}
16
- url = f"{api_url}/functions/{function_type}/{function_id}/direct-execute"
17
-
18
- endpoint_info = requests.post(url, json=data, headers=headers)
19
- if endpoint_info.status_code < 200 or endpoint_info.status_code >= 300:
20
- error_content = endpoint_info.content.decode("utf-8", errors="ignore")
10
+ def _check_response_error(resp, function_type, function_id, data):
11
+ if resp.status_code < 200 or resp.status_code >= 300:
12
+ error_content = resp.content.decode("utf-8", errors="ignore")
21
13
  if function_type == 'api' and os.getenv("LOGS_ENABLED"):
22
- raise PolyApiException(f"Error executing api function with id: {function_id}. Status code: {endpoint_info.status_code}. Request data: {data}, Response: {error_content}")
14
+ logger.error(f"Error executing api function with id: {function_id}. Status code: {resp.status_code}. Request data: {data}, Response: {error_content}")
23
15
  elif function_type != 'api':
24
- raise PolyApiException(f"{endpoint_info.status_code}: {error_content}")
25
-
26
- endpoint_info_data = endpoint_info.json()
16
+ raise PolyApiException(f"{resp.status_code}: {error_content}")
17
+
18
+
19
+ def _check_endpoint_error(resp, function_type, function_id, data):
20
+ if resp.status_code < 200 or resp.status_code >= 300:
21
+ error_content = resp.content.decode("utf-8", errors="ignore")
22
+ if function_type == 'api' and os.getenv("LOGS_ENABLED"):
23
+ raise PolyApiException(f"Error executing api function with id: {function_id}. Status code: {resp.status_code}. Request data: {data}, Response: {error_content}")
24
+ elif function_type != 'api':
25
+ raise PolyApiException(f"{resp.status_code}: {error_content}")
26
+
27
+
28
+ def _build_direct_execute_params(endpoint_info_data):
27
29
  request_params = endpoint_info_data.copy()
28
30
  request_params.pop("url", None)
29
-
30
31
  if "maxRedirects" in request_params:
31
- request_params["allow_redirects"] = request_params.pop("maxRedirects") > 0
32
-
32
+ request_params["follow_redirects"] = request_params.pop("maxRedirects") > 0
33
+ return request_params
34
+
35
+
36
+ def _sync_direct_execute(function_type, function_id, data) -> httpx.Response:
37
+ api_key, api_url = get_api_key_and_url()
38
+ headers = {"Authorization": f"Bearer {api_key}"}
39
+ url = f"{api_url}/functions/{function_type}/{function_id}/direct-execute"
40
+
41
+ endpoint_info = http_client.post(url, json=data, headers=headers)
42
+ _check_endpoint_error(endpoint_info, function_type, function_id, data)
43
+
44
+ endpoint_info_data = endpoint_info.json()
45
+ request_params = _build_direct_execute_params(endpoint_info_data)
46
+
33
47
  has_mtls, cert_path, key_path, ca_path = get_mtls_config()
34
-
48
+
49
+ # Direct-execute hits URL that may need custom TLS
50
+ # settings (mTLS certs or disabled verification). httpx Client.request()
51
+ # doesn't accept per-request transport kwargs, so use one-off calls.
35
52
  if has_mtls:
36
- resp = requests.request(
53
+ resp = httpx.request(
37
54
  url=endpoint_info_data["url"],
38
55
  cert=(cert_path, key_path),
39
56
  verify=ca_path,
57
+ timeout=None,
40
58
  **request_params
41
59
  )
42
60
  else:
43
- resp = requests.request(
61
+ resp = httpx.request(
44
62
  url=endpoint_info_data["url"],
45
63
  verify=False,
64
+ timeout=None,
46
65
  **request_params
47
66
  )
48
67
 
49
- if (resp.status_code < 200 or resp.status_code >= 300):
50
- error_content = resp.content.decode("utf-8", errors="ignore")
51
- if function_type == 'api' and os.getenv("LOGS_ENABLED"):
52
- logger.error(f"Error executing api function with id: {function_id}. Status code: {resp.status_code}. Request data: {data}, Response: {error_content}")
53
- elif function_type != 'api':
54
- raise PolyApiException(f"{resp.status_code}: {error_content}")
55
-
68
+ _check_response_error(resp, function_type, function_id, data)
69
+ return resp
70
+
71
+
72
+ async def _async_direct_execute(function_type, function_id, data) -> httpx.Response:
73
+ api_key, api_url = get_api_key_and_url()
74
+ headers = {"Authorization": f"Bearer {api_key}"}
75
+ url = f"{api_url}/functions/{function_type}/{function_id}/direct-execute"
76
+
77
+ endpoint_info = await http_client.async_post(url, json=data, headers=headers)
78
+ _check_endpoint_error(endpoint_info, function_type, function_id, data)
79
+
80
+ endpoint_info_data = endpoint_info.json()
81
+ request_params = _build_direct_execute_params(endpoint_info_data)
82
+
83
+ has_mtls, cert_path, key_path, ca_path = get_mtls_config()
84
+
85
+ # One-off async client for custom TLS settings on external URLs.
86
+ if has_mtls:
87
+ async with httpx.AsyncClient(
88
+ cert=(cert_path, key_path), verify=ca_path, timeout=None
89
+ ) as client:
90
+ resp = await client.request(
91
+ url=endpoint_info_data["url"], **request_params
92
+ )
93
+ else:
94
+ async with httpx.AsyncClient(verify=False, timeout=None) as client:
95
+ resp = await client.request(
96
+ url=endpoint_info_data["url"], **request_params
97
+ )
98
+
99
+ _check_response_error(resp, function_type, function_id, data)
56
100
  return resp
57
101
 
58
- def execute(function_type, function_id, data) -> Response:
59
- """ execute a specific function id/type
102
+
103
+ def direct_execute(function_type, function_id, data) -> httpx.Response:
104
+ """ execute a specific function id/type (sync)
60
105
  """
106
+ return _sync_direct_execute(function_type, function_id, data)
107
+
108
+
109
+ async def direct_execute_async(function_type, function_id, data) -> httpx.Response:
110
+ """ execute a specific function id/type (async)
111
+ """
112
+ return await _async_direct_execute(function_type, function_id, data)
113
+
114
+
115
+ def _sync_execute(function_type, function_id, data) -> httpx.Response:
61
116
  api_key, api_url = get_api_key_and_url()
62
117
  headers = {"Authorization": f"Bearer {api_key}"}
118
+ url = f"{api_url}/functions/{function_type}/{function_id}/execute"
63
119
 
120
+ resp = http_client.post(url, json=data, headers=headers)
121
+ _check_response_error(resp, function_type, function_id, data)
122
+ return resp
123
+
124
+
125
+ async def _async_execute(function_type, function_id, data) -> httpx.Response:
126
+ api_key, api_url = get_api_key_and_url()
127
+ headers = {"Authorization": f"Bearer {api_key}"}
64
128
  url = f"{api_url}/functions/{function_type}/{function_id}/execute"
65
-
66
- # Make the request
67
- resp = requests.post(
68
- url,
69
- json=data,
70
- headers=headers,
71
- )
72
-
73
- if (resp.status_code < 200 or resp.status_code >= 300) and os.getenv("LOGS_ENABLED"):
74
- error_content = resp.content.decode("utf-8", errors="ignore")
75
- if function_type == 'api' and os.getenv("LOGS_ENABLED"):
76
- logger.error(f"Error executing api function with id: {function_id}. Status code: {resp.status_code}. Request data: {data}, Response: {error_content}")
77
- elif function_type != 'api':
78
- raise PolyApiException(f"{resp.status_code}: {error_content}")
79
129
 
130
+ resp = await http_client.async_post(url, json=data, headers=headers)
131
+ _check_response_error(resp, function_type, function_id, data)
80
132
  return resp
81
133
 
82
134
 
83
- def execute_post(path, data):
135
+ def execute(function_type, function_id, data) -> httpx.Response:
136
+ """ execute a specific function id/type (sync)
137
+ """
138
+ return _sync_execute(function_type, function_id, data)
139
+
140
+
141
+ async def execute_async(function_type, function_id, data) -> httpx.Response:
142
+ """ execute a specific function id/type (async)
143
+ """
144
+ return await _async_execute(function_type, function_id, data)
145
+
146
+
147
+ def _sync_execute_post(path, data):
148
+ api_key, api_url = get_api_key_and_url()
149
+ headers = {"Authorization": f"Bearer {api_key}"}
150
+ return http_client.post(api_url + path, json=data, headers=headers)
151
+
152
+
153
+ async def _async_execute_post(path, data):
84
154
  api_key, api_url = get_api_key_and_url()
85
155
  headers = {"Authorization": f"Bearer {api_key}"}
86
- resp = requests.post(api_url + path, json=data, headers=headers)
156
+ return await http_client.async_post(api_url + path, json=data, headers=headers)
157
+
158
+
159
+ def execute_post(path, data):
160
+ return _sync_execute_post(path, data)
161
+
162
+
163
+ async def execute_post_async(path, data):
164
+ return await _async_execute_post(path, data)
165
+
166
+
167
+ def _sync_variable_get(variable_id: str) -> httpx.Response:
168
+ api_key, base_url = get_api_key_and_url()
169
+ headers = {"Authorization": f"Bearer {api_key}"}
170
+ url = f"{base_url}/variables/{variable_id}/value"
171
+ resp = http_client.get(url, headers=headers)
172
+ if resp.status_code != 200 and resp.status_code != 201:
173
+ error_content = resp.content.decode("utf-8", errors="ignore")
174
+ raise PolyApiException(f"{resp.status_code}: {error_content}")
87
175
  return resp
88
176
 
89
177
 
90
- def variable_get(variable_id: str) -> Response:
178
+ async def _async_variable_get(variable_id: str) -> httpx.Response:
91
179
  api_key, base_url = get_api_key_and_url()
92
180
  headers = {"Authorization": f"Bearer {api_key}"}
93
181
  url = f"{base_url}/variables/{variable_id}/value"
94
- resp = requests.get(url, headers=headers)
182
+ resp = await http_client.async_get(url, headers=headers)
95
183
  if resp.status_code != 200 and resp.status_code != 201:
96
184
  error_content = resp.content.decode("utf-8", errors="ignore")
97
185
  raise PolyApiException(f"{resp.status_code}: {error_content}")
98
186
  return resp
99
187
 
100
188
 
101
- def variable_update(variable_id: str, value) -> Response:
189
+ def variable_get(variable_id: str) -> httpx.Response:
190
+ return _sync_variable_get(variable_id)
191
+
192
+
193
+ async def variable_get_async(variable_id: str) -> httpx.Response:
194
+ return await _async_variable_get(variable_id)
195
+
196
+
197
+ def _sync_variable_update(variable_id: str, value) -> httpx.Response:
102
198
  api_key, base_url = get_api_key_and_url()
103
199
  headers = {"Authorization": f"Bearer {api_key}"}
104
200
  url = f"{base_url}/variables/{variable_id}"
105
- resp = requests.patch(url, data={"value": value}, headers=headers)
201
+ resp = http_client.patch(url, data={"value": value}, headers=headers)
106
202
  if resp.status_code != 200 and resp.status_code != 201:
107
203
  error_content = resp.content.decode("utf-8", errors="ignore")
108
204
  raise PolyApiException(f"{resp.status_code}: {error_content}")
109
- return resp
205
+ return resp
206
+
207
+
208
+ async def _async_variable_update(variable_id: str, value) -> httpx.Response:
209
+ api_key, base_url = get_api_key_and_url()
210
+ headers = {"Authorization": f"Bearer {api_key}"}
211
+ url = f"{base_url}/variables/{variable_id}"
212
+ resp = await http_client.async_patch(url, data={"value": value}, headers=headers)
213
+ if resp.status_code != 200 and resp.status_code != 201:
214
+ error_content = resp.content.decode("utf-8", errors="ignore")
215
+ raise PolyApiException(f"{resp.status_code}: {error_content}")
216
+ return resp
217
+
218
+
219
+ def variable_update(variable_id: str, value) -> httpx.Response:
220
+ return _sync_variable_update(variable_id, value)
221
+
222
+
223
+ async def variable_update_async(variable_id: str, value) -> httpx.Response:
224
+ return await _async_variable_update(variable_id, value)
polyapi/function_cli.py CHANGED
@@ -1,7 +1,6 @@
1
1
  import sys
2
2
  from typing import Any, List, Optional
3
- import requests
4
-
3
+ from polyapi import http_client
5
4
  from polyapi.config import get_api_key_and_url
6
5
  from polyapi.utils import get_auth_headers, print_green, print_red, print_yellow
7
6
  from polyapi.parser import parse_function_code, get_jsonschema_type
@@ -87,7 +86,7 @@ def function_add_or_update(
87
86
  sys.exit(1)
88
87
 
89
88
  headers = get_auth_headers(api_key)
90
- resp = requests.post(url, headers=headers, json=data)
89
+ resp = http_client.post(url, headers=headers, json=data)
91
90
  if resp.status_code in [200, 201]:
92
91
  print_green("DEPLOYED")
93
92
  function_id = resp.json()["id"]
@@ -126,5 +125,5 @@ def spec_delete(function_type: str, function_id: str):
126
125
  print(f"Unknown function type: {function_type}")
127
126
  sys.exit(1)
128
127
  headers = get_auth_headers(api_key)
129
- resp = requests.delete(url, headers=headers)
128
+ resp = http_client.delete(url, headers=headers)
130
129
  return resp
polyapi/generate.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import json
2
- import requests
3
2
  import os
4
3
  import uuid
5
4
  import shutil
@@ -20,6 +19,7 @@ from .server import render_server_function
20
19
  from .utils import add_import_to_init, get_auth_headers, init_the_init, print_green, to_func_namespace
21
20
  from .variables import generate_variables
22
21
  from .poly_tables import generate_tables
22
+ from . import http_client
23
23
  from .config import get_api_key_and_url, get_direct_execute_config, get_cached_generate_args
24
24
 
25
25
  SUPPORTED_FUNCTION_TYPES = {
@@ -62,7 +62,7 @@ def get_specs(contexts: Optional[List[str]] = None, names: Optional[List[str]] =
62
62
  if get_direct_execute_config():
63
63
  params["apiFunctionDirectExecute"] = "true"
64
64
 
65
- resp = requests.get(url, headers=headers, params=params)
65
+ resp = http_client.get(url, headers=headers, params=params)
66
66
  if resp.status_code == 200:
67
67
  return resp.json()
68
68
  else:
polyapi/http_client.py ADDED
@@ -0,0 +1,73 @@
1
+ import asyncio
2
+ import httpx
3
+
4
+ _sync_client: httpx.Client | None = None
5
+ _async_client: httpx.AsyncClient | None = None
6
+
7
+
8
+ def _get_sync_client() -> httpx.Client:
9
+ global _sync_client
10
+ if _sync_client is None:
11
+ _sync_client = httpx.Client(timeout=None)
12
+ return _sync_client
13
+
14
+
15
+ def _get_async_client() -> httpx.AsyncClient:
16
+ global _async_client
17
+ if _async_client is None:
18
+ _async_client = httpx.AsyncClient(timeout=None)
19
+ return _async_client
20
+
21
+
22
+ def post(url, **kwargs) -> httpx.Response:
23
+ return _get_sync_client().post(url, **kwargs)
24
+
25
+
26
+ async def async_post(url, **kwargs) -> httpx.Response:
27
+ return await _get_async_client().post(url, **kwargs)
28
+
29
+
30
+ def get(url, **kwargs) -> httpx.Response:
31
+ return _get_sync_client().get(url, **kwargs)
32
+
33
+
34
+ async def async_get(url, **kwargs) -> httpx.Response:
35
+ return await _get_async_client().get(url, **kwargs)
36
+
37
+
38
+ def patch(url, **kwargs) -> httpx.Response:
39
+ return _get_sync_client().patch(url, **kwargs)
40
+
41
+
42
+ async def async_patch(url, **kwargs) -> httpx.Response:
43
+ return await _get_async_client().patch(url, **kwargs)
44
+
45
+
46
+ def delete(url, **kwargs) -> httpx.Response:
47
+ return _get_sync_client().delete(url, **kwargs)
48
+
49
+
50
+ async def async_delete(url, **kwargs) -> httpx.Response:
51
+ return await _get_async_client().delete(url, **kwargs)
52
+
53
+
54
+ def request(method, url, **kwargs) -> httpx.Response:
55
+ return _get_sync_client().request(method, url, **kwargs)
56
+
57
+
58
+ async def async_request(method, url, **kwargs) -> httpx.Response:
59
+ return await _get_async_client().request(method, url, **kwargs)
60
+
61
+
62
+ def close():
63
+ global _sync_client
64
+ if _sync_client is not None:
65
+ _sync_client.close()
66
+ _sync_client = None
67
+
68
+ async def close_async():
69
+ global _sync_client, _async_client
70
+ close()
71
+ if _async_client is not None:
72
+ await _async_client.aclose()
73
+ _async_client = None
polyapi/poly_tables.py CHANGED
@@ -1,27 +1,64 @@
1
1
  import os
2
- import requests
2
+ from polyapi import http_client
3
3
  from typing_extensions import NotRequired, TypedDict
4
- from typing import List, Union, Type, Dict, Any, Literal, Tuple, Optional, get_args, get_origin
4
+ from typing import (
5
+ List,
6
+ Union,
7
+ Type,
8
+ Dict,
9
+ Any,
10
+ Literal,
11
+ Tuple,
12
+ Optional,
13
+ get_args,
14
+ get_origin,
15
+ )
5
16
  from polyapi.utils import add_import_to_init, init_the_init
6
17
  from polyapi.typedefs import TableSpecDto
7
18
  from polyapi.constants import JSONSCHEMA_TO_PYTHON_TYPE_MAP
19
+ from polyapi.config import get_api_key_and_url
8
20
 
9
- def scrub(data) -> Dict[str, Any]:
10
- if (not data or not isinstance(data, (Dict, List))): return data
21
+ TABI_MODULE_IMPORTS = "\n".join(
22
+ [
23
+ "from typing_extensions import NotRequired, TypedDict",
24
+ "from typing import Union, List, Dict, Any, Literal, Optional, Required, overload",
25
+ "from polyapi.poly_tables import execute_query, first_result, transform_query, delete_one_response",
26
+ "from polyapi.typedefs import Table, PolyCountResult, PolyDeleteResult, PolyDeleteResults, SortOrder, StringFilter, NullableStringFilter, NumberFilter, NullableNumberFilter, BooleanFilter, NullableBooleanFilter, NullableObjectFilter",
27
+ ]
28
+ )
29
+
30
+
31
+ def scrub(data: Any) -> Any:
32
+ if not data or not isinstance(data, (Dict, List)):
33
+ return data
11
34
  if isinstance(data, List):
12
35
  return [scrub(item) for item in data]
13
36
  else:
14
37
  temp = {}
15
- secrets = ["x_api_key", "x-api-key", "access_token", "access-token", "authorization", "api_key", "api-key", "apikey", "accesstoken", "token", "password", "key"]
38
+ secrets = [
39
+ "x_api_key",
40
+ "x-api-key",
41
+ "access_token",
42
+ "access-token",
43
+ "authorization",
44
+ "api_key",
45
+ "api-key",
46
+ "apikey",
47
+ "accesstoken",
48
+ "token",
49
+ "password",
50
+ "key",
51
+ ]
16
52
  for key, value in data.items():
17
53
  if isinstance(value, (Dict, List)):
18
54
  temp[key] = scrub(data[key])
19
55
  elif key.lower() in secrets:
20
- temp[key] = '********'
56
+ temp[key] = "********"
21
57
  else:
22
58
  temp[key] = data[key]
23
59
  return temp
24
60
 
61
+
25
62
  def scrub_keys(e: Exception) -> Dict[str, Any]:
26
63
  """
27
64
  Scrub the keys of an exception to remove sensitive information.
@@ -31,19 +68,27 @@ def scrub_keys(e: Exception) -> Dict[str, Any]:
31
68
  "error": str(e),
32
69
  "type": type(e).__name__,
33
70
  "message": str(e),
34
- "args": scrub(getattr(e, 'args', None))
71
+ "args": scrub(getattr(e, "args", None)),
35
72
  }
36
73
 
37
74
 
38
75
  def execute_query(table_id, method, query):
39
76
  from polyapi import polyCustom
40
77
  from polyapi.poly.client_id import client_id
78
+
41
79
  try:
42
- url = f"/tables/{table_id}/{method}?clientId={client_id}"
43
- headers = {{
44
- 'x-poly-execution-id': polyCustom.get('executionId')
45
- }}
46
- response = requests.post(url, json=query, headers=headers)
80
+ api_key, base_url = get_api_key_and_url()
81
+ if not base_url:
82
+ raise ValueError(
83
+ "PolyAPI Instance URL is not configured, run `python -m polyapi setup`."
84
+ )
85
+
86
+ auth_key = polyCustom.get("executionApiKey") or api_key
87
+ url = f"{base_url.rstrip('/')}/tables/{table_id}/{method}?clientId={client_id}"
88
+ headers = {"x-poly-execution-id": polyCustom.get("executionId")}
89
+ if auth_key:
90
+ headers["Authorization"] = f"Bearer {auth_key}"
91
+ response = http_client.post(url, json=query, headers=headers)
47
92
  response.raise_for_status()
48
93
  return response.json()
49
94
  except Exception as e:
@@ -51,14 +96,16 @@ def execute_query(table_id, method, query):
51
96
 
52
97
 
53
98
  def first_result(rsp):
54
- if isinstance(rsp, dict) and isinstance(rsp.get('results'), list):
55
- return rsp['results'][0] if rsp['results'] else None
99
+ if isinstance(rsp, dict) and isinstance(rsp.get("results"), list):
100
+ return rsp["results"][0] if rsp["results"] else None
56
101
  return rsp
57
102
 
103
+
58
104
  def delete_one_response(rsp):
59
- if isinstance(rsp, dict) and isinstance(rsp.get('deleted'), int):
60
- return { 'deleted': bool(rsp.get('deleted')) }
61
- return { 'deleted': false }
105
+ if isinstance(rsp, dict) and isinstance(rsp.get("deleted"), int):
106
+ return {"deleted": bool(rsp.get("deleted"))}
107
+ return {"deleted": False}
108
+
62
109
 
63
110
  _key_transform_map = {
64
111
  "not_": "not",
@@ -72,8 +119,7 @@ _key_transform_map = {
72
119
  def _transform_keys(obj: Any) -> Any:
73
120
  if isinstance(obj, dict):
74
121
  return {
75
- _key_transform_map.get(k, k): _transform_keys(v)
76
- for k, v in obj.items()
122
+ _key_transform_map.get(k, k): _transform_keys(v) for k, v in obj.items()
77
123
  }
78
124
 
79
125
  elif isinstance(obj, list):
@@ -88,13 +134,13 @@ def transform_query(query: dict) -> dict:
88
134
  return {
89
135
  **query,
90
136
  "where": _transform_keys(query["where"]) if query["where"] else None,
91
- "orderBy": query["order_by"] if query["order_by"] else None
137
+ "orderBy": query["order_by"] if query["order_by"] else None,
92
138
  }
93
139
 
94
140
  return query
95
141
 
96
142
 
97
- TABI_TABLE_TEMPLATE = '''
143
+ TABI_TABLE_TEMPLATE = """
98
144
  {table_name}Columns = Literal[{table_columns}]
99
145
 
100
146
 
@@ -369,7 +415,7 @@ class {table_name}:{table_description}
369
415
  query["where"]["id"] = kwargs["id"]
370
416
  query.pop("id", None)
371
417
  return delete_one_response(execute_query({table_name}.table_id, "delete", transform_query(query)))
372
- '''
418
+ """
373
419
 
374
420
 
375
421
  def _get_column_type_str(name: str, schema: Dict[str, Any], is_required: bool) -> str:
@@ -377,11 +423,17 @@ def _get_column_type_str(name: str, schema: Dict[str, Any], is_required: bool) -
377
423
 
378
424
  col_type = schema.get("type", "object")
379
425
  if isinstance(col_type, list):
380
- subtypes = [_get_column_type_str(name, { **schema, "type": t }, is_required) for t in col_type]
426
+ subtypes = [
427
+ _get_column_type_str(name, {**schema, "type": t}, is_required)
428
+ for t in col_type
429
+ ]
381
430
  result = f"Union[{', '.join(subtypes)}]"
382
431
  elif col_type == "array":
383
432
  if isinstance(schema["items"], list):
384
- subtypes = [_get_column_type_str(f"{name}{i}", s, True) for i, s in enumerate(schema["items"])]
433
+ subtypes = [
434
+ _get_column_type_str(f"{name}{i}", s, True)
435
+ for i, s in enumerate(schema["items"])
436
+ ]
385
437
  result = f"Tuple[{', '.join(subtypes)}]"
386
438
  elif isinstance(schema["items"], dict):
387
439
  result = f"List[{_get_column_type_str(name, schema['items'], True)}]"
@@ -391,7 +443,10 @@ def _get_column_type_str(name: str, schema: Dict[str, Any], is_required: bool) -
391
443
  if isinstance(schema.get("patternProperties"), dict):
392
444
  # TODO: Handle multiple pattern properties
393
445
  result = f"Dict[str, {_get_column_type_str(f'{name}_', schema['patternProperties'], True)}]"
394
- elif isinstance(schema.get("properties"), dict) and len(schema["properties"].values()) > 0:
446
+ elif (
447
+ isinstance(schema.get("properties"), dict)
448
+ and len(schema["properties"].values()) > 0
449
+ ):
395
450
  # TODO: Handle x-poly-refs
396
451
  result = f'"{name}"'
397
452
  else:
@@ -413,24 +468,32 @@ def _render_table_row_classes(table_name: str, schema: Dict[str, Any]) -> str:
413
468
  return output[1].split("\n", 1)[1].strip()
414
469
 
415
470
 
416
- def _render_table_subset_class(table_name: str, columns: List[Tuple[str, Dict[str, Any]]], required: List[str]) -> str:
471
+ def _render_table_subset_class(
472
+ table_name: str, columns: List[Tuple[str, Dict[str, Any]]], required: List[str]
473
+ ) -> str:
417
474
  # Generate class which can match any subset of a table row
418
475
  lines = [f"class {table_name}Subset(TypedDict):"]
419
476
 
420
477
  for name, schema in columns:
421
- type_str = _get_column_type_str(f"_{table_name}Row{name}", schema, name in required)
478
+ type_str = _get_column_type_str(
479
+ f"_{table_name}Row{name}", schema, name in required
480
+ )
422
481
  lines.append(f" {name}: NotRequired[{type_str}]")
423
482
 
424
483
  return "\n".join(lines)
425
484
 
426
485
 
427
- def _render_table_where_class(table_name: str, columns: List[Tuple[str, Dict[str, Any]]], required: List[str]) -> str:
486
+ def _render_table_where_class(
487
+ table_name: str, columns: List[Tuple[str, Dict[str, Any]]], required: List[str]
488
+ ) -> str:
428
489
  # Generate class for the 'where' part of the query
429
490
  lines = [f"class {table_name}WhereFilter(TypedDict):"]
430
491
 
431
492
  for name, schema in columns:
432
493
  ftype_str = ""
433
- type_str = _get_column_type_str(f"_{table_name}Row{name}", schema, True) # force required to avoid wrapping type in Optional[]
494
+ type_str = _get_column_type_str(
495
+ f"_{table_name}Row{name}", schema, True
496
+ ) # force required to avoid wrapping type in Optional[]
434
497
  is_required = name in required
435
498
  if type_str == "bool":
436
499
  ftype_str = "BooleanFilter" if is_required else "NullableBooleanFilter"
@@ -445,24 +508,34 @@ def _render_table_where_class(table_name: str, columns: List[Tuple[str, Dict[str
445
508
  if ftype_str:
446
509
  lines.append(f" {name}: NotRequired[Union[{type_str}, {ftype_str}]]")
447
510
 
448
- lines.append(f' AND: NotRequired[Union["{table_name}WhereFilter", List["{table_name}WhereFilter"]]]')
511
+ lines.append(
512
+ f' AND: NotRequired[Union["{table_name}WhereFilter", List["{table_name}WhereFilter"]]]'
513
+ )
449
514
  lines.append(f' OR: NotRequired[List["{table_name}WhereFilter"]]')
450
- lines.append(f' NOT: NotRequired[Union["{table_name}WhereFilter", List["{table_name}WhereFilter"]]]')
515
+ lines.append(
516
+ f' NOT: NotRequired[Union["{table_name}WhereFilter", List["{table_name}WhereFilter"]]]'
517
+ )
451
518
 
452
519
  return "\n".join(lines)
453
520
 
454
521
 
455
522
  def _render_table(table: TableSpecDto) -> str:
456
523
  columns = list(table["schema"]["properties"].items())
457
- required_colunms = table["schema"].get("required", [])
524
+ required_columns = table["schema"].get("required", [])
458
525
 
459
- table_columns = ",".join([ f'"{k}"' for k,_ in columns])
526
+ table_columns = ",".join([f'"{k}"' for k, _ in columns])
460
527
  table_row_classes = _render_table_row_classes(table["name"], table["schema"])
461
- table_row_subset_class = _render_table_subset_class(table["name"], columns, required_colunms)
462
- table_where_class = _render_table_where_class(table["name"], columns, required_colunms)
528
+ table_row_subset_class = _render_table_subset_class(
529
+ table["name"], columns, required_columns
530
+ )
531
+ table_where_class = _render_table_where_class(
532
+ table["name"], columns, required_columns
533
+ )
463
534
  if table.get("description", ""):
464
- table_description = '\n """'
465
- table_description += '\n '.join(table["description"].replace('"', "'").split("\n"))
535
+ table_description = '\n """'
536
+ table_description += "\n ".join(
537
+ table["description"].replace('"', "'").split("\n")
538
+ )
466
539
  table_description += '\n """'
467
540
  else:
468
541
  table_description = ""
@@ -502,12 +575,7 @@ def _create_table(table: TableSpecDto) -> None:
502
575
 
503
576
  init_path = os.path.join(full_path, "__init__.py")
504
577
 
505
- imports = "\n".join([
506
- "from typing_extensions import NotRequired, TypedDict",
507
- "from typing import Union, List, Dict, Any, Literal, Optional, Required, overload",
508
- "from polyapi.poly_tables import execute_query, first_result, transform_query",
509
- "from polyapi.typedefs import Table, PolyCountResult, PolyDeleteResults, SortOrder, StringFilter, NullableStringFilter, NumberFilter, NullableNumberFilter, BooleanFilter, NullableBooleanFilter, NullableObjectFilter",
510
- ])
578
+ imports = TABI_MODULE_IMPORTS
511
579
  table_contents = _render_table(table)
512
580
 
513
581
  file_contents = ""
polyapi/prepare.py CHANGED
@@ -2,8 +2,7 @@ import os
2
2
  import sys
3
3
  import subprocess
4
4
  from typing import List, Tuple, Literal
5
- import requests
6
-
5
+ from polyapi import http_client
7
6
  from polyapi.utils import get_auth_headers
8
7
  from polyapi.config import get_api_key_and_url
9
8
  from polyapi.parser import parse_function_code
@@ -32,7 +31,7 @@ def get_server_function_description(description: str, arguments, code: str) -> s
32
31
  api_key, api_url = get_api_key_and_url()
33
32
  headers = get_auth_headers(api_key)
34
33
  data = {"description": description, "arguments": arguments, "code": code}
35
- response = requests.post(f"{api_url}/functions/server/description-generation", headers=headers, json=data)
34
+ response = http_client.post(f"{api_url}/functions/server/description-generation", headers=headers, json=data)
36
35
  return response.json()
37
36
 
38
37
  def get_client_function_description(description: str, arguments, code: str) -> str:
@@ -40,7 +39,7 @@ def get_client_function_description(description: str, arguments, code: str) -> s
40
39
  headers = get_auth_headers(api_key)
41
40
  # Simulated API call to generate client function descriptions
42
41
  data = {"description": description, "arguments": arguments, "code": code}
43
- response = requests.post(f"{api_url}/functions/client/description-generation", headers=headers, json=data)
42
+ response = http_client.post(f"{api_url}/functions/client/description-generation", headers=headers, json=data)
44
43
  return response.json()
45
44
 
46
45
  def fill_in_missing_function_details(deployable: DeployableRecord, code: str) -> DeployableRecord:
polyapi/rendered_spec.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  from typing import Optional
3
3
 
4
- import requests
4
+ from polyapi import http_client
5
5
  from polyapi.config import get_api_key_and_url
6
6
  from polyapi.generate import read_cached_specs, render_spec
7
7
  from polyapi.typedefs import SpecificationDto
@@ -31,7 +31,7 @@ def update_rendered_spec(spec: SpecificationDto):
31
31
 
32
32
  url = f"{base_url}/functions/rendered-specs"
33
33
  headers = {"Authorization": f"Bearer {api_key}"}
34
- resp = requests.post(url, json=data, headers=headers)
34
+ resp = http_client.post(url, json=data, headers=headers)
35
35
  assert resp.status_code == 201, (resp.text, resp.status_code)
36
36
 
37
37
 
@@ -40,7 +40,7 @@ def _get_spec(spec_id: str, no_types: bool = False) -> Optional[SpecificationDto
40
40
  url = f"{base_url}/specs"
41
41
  headers = {"Authorization": f"Bearer {api_key}"}
42
42
  params = {"noTypes": str(no_types).lower()}
43
- resp = requests.get(url, headers=headers, params=params)
43
+ resp = http_client.get(url, headers=headers, params=params)
44
44
  if resp.status_code == 200:
45
45
  specs = resp.json()
46
46
  for spec in specs:
polyapi/server.py CHANGED
@@ -24,6 +24,20 @@ def {function_name}(
24
24
  return resp.text # type: ignore # fallback for debugging
25
25
 
26
26
 
27
+ async def {function_name}_async(
28
+ {args}
29
+ ) -> {return_type_name}:
30
+ \"""{function_description}
31
+
32
+ Function ID: {function_id}
33
+ \"""
34
+ resp = await execute_async("{function_type}", "{function_id}", {data})
35
+ try:
36
+ return {return_action}
37
+ except:
38
+ return resp.text # type: ignore # fallback for debugging
39
+
40
+
27
41
  """
28
42
 
29
43
 
polyapi/sync.py CHANGED
@@ -2,8 +2,7 @@ import os
2
2
  from datetime import datetime
3
3
  from typing import List, Dict
4
4
  from typing_extensions import cast # type: ignore
5
- import requests
6
-
5
+ from polyapi import http_client
7
6
  from polyapi.utils import get_auth_headers
8
7
  from polyapi.config import get_api_key_and_url
9
8
  from polyapi.parser import get_jsonschema_type
@@ -35,10 +34,10 @@ def remove_deployable_function(deployable: SyncDeployment) -> bool:
35
34
  raise Exception("Missing api key!")
36
35
  headers = get_auth_headers(api_key)
37
36
  url = f'{deployable["instance"]}/functions/{deployable["type"].replace("-function", "")}/{deployable["id"]}'
38
- response = requests.get(url, headers=headers)
37
+ response = http_client.get(url, headers=headers)
39
38
  if response.status_code != 200:
40
39
  return False
41
- requests.delete(url, headers=headers)
40
+ http_client.delete(url, headers=headers)
42
41
  return True
43
42
 
44
43
  def remove_deployable(deployable: SyncDeployment) -> bool:
@@ -65,7 +64,7 @@ def sync_function_and_get_id(deployable: SyncDeployment, code: str) -> str:
65
64
  "returnTypeSchema": deployable["types"]["returns"]["typeSchema"],
66
65
  "arguments": [{**p, "key": p["name"], "type": get_jsonschema_type(p["type"]) } for p in deployable["types"]["params"]],
67
66
  }
68
- response = requests.post(url, headers=headers, json=payload)
67
+ response = http_client.post(url, headers=headers, json=payload)
69
68
  response.raise_for_status()
70
69
  return response.json()['id']
71
70
 
polyapi/utils.py CHANGED
@@ -16,7 +16,7 @@ from polyapi.schema import (
16
16
 
17
17
  # this string should be in every __init__ file.
18
18
  # it contains all the imports needed for the function or variable code to run
19
- CODE_IMPORTS = "from typing import List, Dict, Any, Optional, Callable\nfrom typing_extensions import TypedDict, NotRequired\nimport logging\nimport requests\nimport socketio # type: ignore\nfrom polyapi.config import get_api_key_and_url, get_direct_execute_config\nfrom polyapi.execute import execute, execute_post, variable_get, variable_update, direct_execute\n\n"
19
+ CODE_IMPORTS = "from typing import List, Dict, Any, Optional, Callable\nfrom typing_extensions import TypedDict, NotRequired\nimport logging\nimport requests\nimport socketio # type: ignore\nfrom polyapi.config import get_api_key_and_url, get_direct_execute_config\nfrom polyapi.execute import execute, execute_async, execute_post, execute_post_async, variable_get, variable_get_async, variable_update, variable_update_async, direct_execute, direct_execute_async\n\n"
20
20
 
21
21
 
22
22
  def init_the_init(full_path: str, code_imports: Optional[str] = None) -> None:
@@ -73,9 +73,22 @@ def print_red(s: str):
73
73
 
74
74
  def add_type_import_path(function_name: str, arg: str) -> str:
75
75
  """if not basic type, coerce to camelCase and add the import path"""
76
- # for now, just treat Callables as basic types
76
+ # from now, we start qualifying non-basic types :))
77
+ # e.g. Callable[[EmailAddress, Dict, Dict, Dict], None]
78
+ # becomes Callable[[Set_profile_email.EmailAddress, Dict, Dict, Dict], None]
79
+
77
80
  if arg.startswith("Callable"):
78
- return arg
81
+ inner = arg[len("Callable["):-1] # strip outer Callable[...]
82
+ parts = [p.strip() for p in inner.split(",")]
83
+ qualified = []
84
+ for p in parts:
85
+ clean = p.strip("[] ")
86
+ if clean and clean not in BASIC_PYTHON_TYPES:
87
+ replacement = f"{to_func_namespace(function_name)}.{camelCase(clean)}"
88
+ p = p.replace(clean, replacement)
89
+ qualified.append(p)
90
+ return "Callable[" + ",".join(qualified) + "]"
91
+ # return arg
79
92
 
80
93
  if arg in BASIC_PYTHON_TYPES:
81
94
  return arg
polyapi/variables.py CHANGED
@@ -15,6 +15,11 @@ GET_TEMPLATE = """
15
15
  def get() -> {variable_type}:
16
16
  resp = variable_get("{variable_id}")
17
17
  return resp.text
18
+
19
+ @staticmethod
20
+ async def get_async() -> {variable_type}:
21
+ resp = await variable_get_async("{variable_id}")
22
+ return resp.text
18
23
  """
19
24
 
20
25
 
@@ -30,6 +35,11 @@ class {variable_name}:{get_method}
30
35
  resp = variable_update("{variable_id}", value)
31
36
  return resp.json()
32
37
 
38
+ @staticmethod
39
+ async def update_async(value: {variable_type}):
40
+ resp = await variable_update_async("{variable_id}", value)
41
+ return resp.json()
42
+
33
43
  @classmethod
34
44
  async def onUpdate(cls, callback):
35
45
  api_key, base_url = get_api_key_and_url()
polyapi/webhook.py CHANGED
@@ -124,11 +124,7 @@ def render_webhook_handle(
124
124
  ) -> Tuple[str, str]:
125
125
  try:
126
126
  function_args, function_args_def = parse_arguments(function_name, arguments)
127
-
128
- if "WebhookEventType" in function_args:
129
- # let's add the function name import!
130
- function_args = function_args.replace("WebhookEventType", f"{to_func_namespace(function_name)}.WebhookEventType")
131
-
127
+
132
128
  func_str = WEBHOOK_TEMPLATE.format(
133
129
  description=function_description,
134
130
  function_id=function_id,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polyapi-python
3
- Version: 0.3.13.dev1
3
+ Version: 0.3.14.dev1
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
@@ -36,6 +36,7 @@ Requires-Dist: stdlib_list>=0.10.0
36
36
  Requires-Dist: colorama==0.4.4
37
37
  Requires-Dist: python-socketio[asyncio_client]==5.11.1
38
38
  Requires-Dist: truststore>=0.8.0
39
+ Requires-Dist: httpx>=0.28.1
39
40
  Dynamic: license-file
40
41
 
41
42
  # PolyAPI Python Library
@@ -185,6 +186,14 @@ This script is handy for checking for any mypy types:
185
186
 
186
187
  Please ignore \[name-defined\] errors for now. This is a known bug we are working to fix!
187
188
 
189
+ ## Strategies for QA'ing Changes To Generate Or Other Core Functionality
190
+
191
+ Our https://na1.polyapi.io has a large OOB catalog (as does eu1/na2). We also have several big internal PolyAPI projects with Python (message @eupharis if you need a pointer to which ones).
192
+
193
+ Running `python -m polyapi generate` in all these projects and then checking the flake8 and check_mypy steps above is a great way to build confidence that the `generate` changes has no gotchas.
194
+
195
+ Of course all this is in addition to the changes passing through normal unittests and integration tests!
196
+
188
197
  ## Support
189
198
 
190
199
  If you run into any issues or want help getting started with this project, please contact support@polyapi.io
@@ -0,0 +1,33 @@
1
+ polyapi/__init__.py,sha256=hw7x4j9JNJfPdkIOZqV0X9pbYcw3_5AH1iQFdSogH-c,3235
2
+ polyapi/__main__.py,sha256=V4zhAh_YGxno5f_KSrlkELxcuDh9bR3WSd0n-2r-qQQ,93
3
+ polyapi/api.py,sha256=CzqX99GekhQJVqAAWlL03hUenIi_5iTln9cWgQzz6Ko,2784
4
+ polyapi/auth.py,sha256=l0pGAoYvEWqa2TMsrM9GS8imXu3GsWvpn4eFPy1vksI,6195
5
+ polyapi/cli.py,sha256=3M6XpMhpvQSHq4X8tBsq7N93YEwyVIqNFkz1tx_6aSs,11107
6
+ polyapi/client.py,sha256=DW6ljG_xCwAo2yz23A9QfLooE6ZUDvSpdA4e_dCQjiQ,1418
7
+ polyapi/config.py,sha256=dnowfPKEau5sA4zwBzLK_dYAHA-Wi3AOnMtDNsYwUmM,7520
8
+ polyapi/constants.py,sha256=sc-FnS0SngBLvSu1ZWMs0UCf9EYD1u1Yhfr-sZXGLns,607
9
+ polyapi/deployables.py,sha256=njmqTU0W7d3JdDWhjjjr2UOghEv4bTKUN-3ztJvcC7o,12233
10
+ polyapi/error_handler.py,sha256=I_e0iz6VM23FLVQWJljxs2NGcl_OODbi43OcbnqBlp8,2398
11
+ polyapi/exceptions.py,sha256=Zh7i7eCUhDuXEdUYjatkLFTeZkrx1BJ1P5ePgbJ9eIY,89
12
+ polyapi/execute.py,sha256=MeAmc5qsMQfnQPiHJK9Khkn_wrBxMoyMYYqmuFFPsRg,8779
13
+ polyapi/function_cli.py,sha256=kKrecKCDNMmYtcOOCVF2KKOGjFUfWD2TbiVsyYQ84y8,4323
14
+ polyapi/generate.py,sha256=TjMlxxjWN1WnFEmOjSygFcl9RkHCZeCq79R-K_7GYRU,21059
15
+ polyapi/http_client.py,sha256=dfsfFzHlhScQgYMf3zJshJJCwgJGQqM8B5peY9O4NPU,1872
16
+ polyapi/parser.py,sha256=pAkeuwp1Kn6bHXiIoj-DuXQ518PztQThIa8vztgsKLQ,21525
17
+ polyapi/poly_schemas.py,sha256=fZ6AGvHcOKQJtlrzSuzeBNed5DxPMA2dJGdJvuFCHWM,9066
18
+ polyapi/poly_tables.py,sha256=T07j4H-HdcIFP5HwUq0uljpzzYzMtJvdqYvOdC0oZ4I,19728
19
+ polyapi/prepare.py,sha256=DBlrQu_A0PqdaQ3KlMs-C6PkV3qncatkAiBlC7j2hnk,7441
20
+ polyapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ polyapi/rendered_spec.py,sha256=J2Num7MXnW_mKAe2MSVIL1coWopNYZ-6nNNwQRfseV0,2262
22
+ polyapi/schema.py,sha256=-mtRV5iL3CV0X3phXhGYFV8sLz0KouTACOKWyGO9Pwc,5309
23
+ polyapi/server.py,sha256=35l3f9d6jWaa60MfX5n7KF4UCAQR-Xg8WLc0NL9dlfY,2479
24
+ polyapi/sync.py,sha256=t0Khh8B5GylxB7KWRP_xvkrvkuyVarSLCOvtmaz7lcc,6766
25
+ polyapi/typedefs.py,sha256=mfll-KrngOW0wzbvDNiIDaDkFMwbsT-MY5y5hzWj9RE,5642
26
+ polyapi/utils.py,sha256=VN8n_jvphderplI5MDYjSutxVnYbJjHhJznYkwjgstw,13349
27
+ polyapi/variables.py,sha256=hfSDPGQK6YphNCa9MqE0W88WIFfqCQWgpBDWkExMq-A,7582
28
+ polyapi/webhook.py,sha256=0ceLwHNjNd2Yx_8MX5jOIxiQ5Lwhy2pe81ProAuKon0,5108
29
+ polyapi_python-0.3.14.dev1.dist-info/licenses/LICENSE,sha256=6b_I7aPVp8JXhqQwdw7_B84Ca0S4JGjHj0sr_1VOdB4,1068
30
+ polyapi_python-0.3.14.dev1.dist-info/METADATA,sha256=X2RhN7oTKSIFtSQ1wXqKc0Jq9QTWLUBCwyBpgnzmtpw,5913
31
+ polyapi_python-0.3.14.dev1.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
32
+ polyapi_python-0.3.14.dev1.dist-info/top_level.txt,sha256=CEFllOnzowci_50RYJac-M54KD2IdAptFsayVVF_f04,8
33
+ polyapi_python-0.3.14.dev1.dist-info/RECORD,,
@@ -1,32 +0,0 @@
1
- polyapi/__init__.py,sha256=hw7x4j9JNJfPdkIOZqV0X9pbYcw3_5AH1iQFdSogH-c,3235
2
- polyapi/__main__.py,sha256=V4zhAh_YGxno5f_KSrlkELxcuDh9bR3WSd0n-2r-qQQ,93
3
- polyapi/api.py,sha256=2nds6ZdNe9OHvCba4IjOPga0CAYIsib2SbhEyDDCmd8,2188
4
- polyapi/auth.py,sha256=EGstBjTSdAydI5hGAHeRRc1GcmHshogudb3sxCgO6zA,5341
5
- polyapi/cli.py,sha256=3M6XpMhpvQSHq4X8tBsq7N93YEwyVIqNFkz1tx_6aSs,11107
6
- polyapi/client.py,sha256=DW6ljG_xCwAo2yz23A9QfLooE6ZUDvSpdA4e_dCQjiQ,1418
7
- polyapi/config.py,sha256=dnowfPKEau5sA4zwBzLK_dYAHA-Wi3AOnMtDNsYwUmM,7520
8
- polyapi/constants.py,sha256=sc-FnS0SngBLvSu1ZWMs0UCf9EYD1u1Yhfr-sZXGLns,607
9
- polyapi/deployables.py,sha256=njmqTU0W7d3JdDWhjjjr2UOghEv4bTKUN-3ztJvcC7o,12233
10
- polyapi/error_handler.py,sha256=I_e0iz6VM23FLVQWJljxs2NGcl_OODbi43OcbnqBlp8,2398
11
- polyapi/exceptions.py,sha256=Zh7i7eCUhDuXEdUYjatkLFTeZkrx1BJ1P5ePgbJ9eIY,89
12
- polyapi/execute.py,sha256=q4xtV6rYO8f-8hULlFTlVgoTVQSahvlMu3FHkFzYpMs,4423
13
- polyapi/function_cli.py,sha256=W30yL2i152iZCr6zAU1CHKlDp_tt7kB2cGIXIxmWumk,4302
14
- polyapi/generate.py,sha256=0zVDEnKVZnbMNZ6O0SWAuDLHrBrkW01vFkjpf9wrQZo,21046
15
- polyapi/parser.py,sha256=pAkeuwp1Kn6bHXiIoj-DuXQ518PztQThIa8vztgsKLQ,21525
16
- polyapi/poly_schemas.py,sha256=fZ6AGvHcOKQJtlrzSuzeBNed5DxPMA2dJGdJvuFCHWM,9066
17
- polyapi/poly_tables.py,sha256=SrByuusS1aL7L7R8o28TEr4A-8AfMGGHCKLg-9vmnxc,18783
18
- polyapi/prepare.py,sha256=NQzpMIoakNovStvOGOmqSYIpTwiWXaweNSE9se10A2E,7420
19
- polyapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- polyapi/rendered_spec.py,sha256=nJEj2vRgG3N20fU4s-ThRtOIwAuTzXwXuOBIkXljDVc,2240
21
- polyapi/schema.py,sha256=-mtRV5iL3CV0X3phXhGYFV8sLz0KouTACOKWyGO9Pwc,5309
22
- polyapi/server.py,sha256=YXWxhYBx-hluwDQ8Jvfpy2s8ogz0GsNTMcZVNcP5ca8,2147
23
- polyapi/sync.py,sha256=52ODc82jBJpbNYTB9zXlrVZLR39iwDPW3cuIC3P8dbM,6742
24
- polyapi/typedefs.py,sha256=mfll-KrngOW0wzbvDNiIDaDkFMwbsT-MY5y5hzWj9RE,5642
25
- polyapi/utils.py,sha256=Ca189i4PM4TpwvpzwF3T8MfQsOPD45b_falXjjgYCyI,12603
26
- polyapi/variables.py,sha256=SJv106ePpQP5mx7Iiafl_shtFlE8FoaO9Q8lvw-3IRg,7270
27
- polyapi/webhook.py,sha256=I3_uOl4f4L2-2WehzRLMVMRrB-76EiXCPA9Vzoaj30I,5326
28
- polyapi_python-0.3.13.dev1.dist-info/licenses/LICENSE,sha256=6b_I7aPVp8JXhqQwdw7_B84Ca0S4JGjHj0sr_1VOdB4,1068
29
- polyapi_python-0.3.13.dev1.dist-info/METADATA,sha256=3dfqvKcLsYjGJpeIGIu-KoC7RjOtSEddhx68R_qTuNc,5318
30
- polyapi_python-0.3.13.dev1.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
31
- polyapi_python-0.3.13.dev1.dist-info/top_level.txt,sha256=CEFllOnzowci_50RYJac-M54KD2IdAptFsayVVF_f04,8
32
- polyapi_python-0.3.13.dev1.dist-info/RECORD,,