Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ When hacking on this library, please enable flake8 and add this line to your fla
--config=.flake8
```

## Mypy Type Improvements

This script is handy for checking for any mypy types:

```bash
./check_mypy.sh
```

Please ignore \[name-defined\] errors for now. This is a known bug we are working to fix!

## Support

If you run into any issues or want help getting started with this project, please contact support@polyapi.io
5 changes: 5 additions & 0 deletions check_mypy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

mypy polyapi/poly
mypy polyapi/vari
mypy polyapi/schemas
1 change: 1 addition & 0 deletions polyapi/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def render_api_function(
arg_names = [a["name"] for a in arguments]
args, args_def = parse_arguments(function_name, arguments)
return_type_name, return_type_def = get_type_and_def(return_type) # type: ignore

data = "{" + ", ".join([f"'{arg}': {rewrite_arg_name(arg)}" for arg in arg_names]) + "}"

api_response_type = f"{function_name}Response"
Expand Down
8 changes: 4 additions & 4 deletions polyapi/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import requests
import os
import shutil
from typing import List, cast
from typing import List, Tuple, cast

from .auth import render_auth_function
from .client import render_client_function
Expand Down Expand Up @@ -177,10 +177,10 @@ def generate() -> None:
print("Generating Poly Python SDK...", end="", flush=True)
remove_old_library()

limit_ids: List[str] = [] # useful for narrowing down generation to a single function to debug

specs = get_specs()
cache_specs(specs)

limit_ids: List[str] = [] # useful for narrowing down generation to a single function to debug
functions = parse_function_specs(specs, limit_ids=limit_ids)

schemas = get_schemas()
Expand Down Expand Up @@ -222,7 +222,7 @@ def clear() -> None:
print("Cleared!")


def render_spec(spec: SpecificationDto):
def render_spec(spec: SpecificationDto) -> Tuple[str, str]:
function_type = spec["type"]
function_description = spec["description"]
function_name = spec["name"]
Expand Down
1 change: 1 addition & 0 deletions polyapi/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ def _parse_google_docstring(docstring: str) -> Dict[str, Any]:

return parsed


def _get_schemas(code: str) -> List[Dict]:
schemas = []
user_code = types.SimpleNamespace()
Expand Down
64 changes: 49 additions & 15 deletions polyapi/poly_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Any, Dict, List, Tuple

from polyapi.schema import wrapped_generate_schema_types
from polyapi.utils import add_import_to_init, init_the_init
from polyapi.utils import add_import_to_init, init_the_init, to_func_namespace

from .typedefs import SchemaSpecDto

Expand All @@ -23,23 +23,57 @@ def generate_schemas(specs: List[SchemaSpecDto]):
create_schema(spec)


def create_schema(spec: SchemaSpecDto) -> None:
folders = ["schemas"]
if spec["context"]:
folders += [s for s in spec["context"].split(".")]
def add_schema_file(
full_path: str,
schema_name: str,
spec: SchemaSpecDto,
):
# first lets add the import to the __init__
init_the_init(full_path, SCHEMA_CODE_IMPORTS)

# build up the full_path by adding all the folders
full_path = os.path.join(os.path.dirname(os.path.abspath(__file__)))
if not spec["definition"].get("title"):
# very empty schemas like mews.Unit are possible
# add a title here to be sure they render
spec["definition"]["title"] = schema_name

schema_defs = render_poly_schema(spec)

if schema_defs:
# add function to init
init_path = os.path.join(full_path, "__init__.py")
with open(init_path, "a") as f:
f.write(f"\n\nfrom ._{to_func_namespace(schema_name)} import {schema_name}")

# add type_defs to underscore file
file_path = os.path.join(full_path, f"_{to_func_namespace(schema_name)}.py")
with open(file_path, "w") as f:
f.write(schema_defs)


def create_schema(
spec: SchemaSpecDto
) -> None:
full_path = os.path.dirname(os.path.abspath(__file__))
folders = f"schemas.{spec['context']}.{spec['name']}".split(".")
for idx, folder in enumerate(folders):
full_path = os.path.join(full_path, folder)
if not os.path.exists(full_path):
os.makedirs(full_path)
next = folders[idx + 1] if idx + 1 < len(folders) else None
if next:
add_import_to_init(full_path, next, code_imports=SCHEMA_CODE_IMPORTS)

add_schema_to_init(full_path, spec)
if idx + 1 == len(folders):
# special handling for final level
add_schema_file(
full_path,
folder,
spec,
)
else:
full_path = os.path.join(full_path, folder)
if not os.path.exists(full_path):
os.makedirs(full_path)

# append to __init__.py file if nested folders
next = folders[idx + 1] if idx + 2 < len(folders) else ""
if next:
init_the_init(full_path, SCHEMA_CODE_IMPORTS)
add_import_to_init(full_path, next)



def add_schema_to_init(full_path: str, spec: SchemaSpecDto):
Expand Down
23 changes: 17 additions & 6 deletions polyapi/schema.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
""" NOTE: this file represents the schema parsing logic for jsonschema_gentypes
"""
import random
import string
import logging
import contextlib
import re
from typing import Dict
from jsonschema_gentypes.cli import process_config
from jsonschema_gentypes import configuration
Expand Down Expand Up @@ -48,10 +47,8 @@ def wrapped_generate_schema_types(type_spec: dict, root, fallback_type):
# lets name the root after the reference for some level of visibility
root += pascalCase(type_spec["x-poly-ref"]["path"].replace(".", " "))
else:
# add three random letters for uniqueness
root += random.choice(string.ascii_letters).upper()
root += random.choice(string.ascii_letters).upper()
root += random.choice(string.ascii_letters).upper()
# if we have no root, just add "My"
root = "My" + root

root = clean_title(root)

Expand Down Expand Up @@ -99,9 +96,23 @@ def generate_schema_types(input_data: Dict, root=None):
with open(tmp_output) as f:
output = f.read()

output = clean_malformed_examples(output)

return output


# Regex to match everything between "# example: {\n" and "^}$"
MALFORMED_EXAMPLES_PATTERN = re.compile(r"# example: \{\n.*?^\}$", flags=re.DOTALL | re.MULTILINE)


def clean_malformed_examples(example: str) -> str:
""" there is a bug in the `jsonschmea_gentypes` library where if an example from a jsonchema is an object,
it will break the code because the object won't be properly commented out
"""
cleaned_example = MALFORMED_EXAMPLES_PATTERN.sub("", example)
return cleaned_example


def clean_title(title: str) -> str:
""" used by library generation, sometimes functions can be added with spaces in the title
or other nonsense. fix them!
Expand Down
14 changes: 9 additions & 5 deletions polyapi/server.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any, Dict, List, Tuple
from typing import Any, Dict, List, Tuple, cast

from polyapi.typedefs import PropertySpecification
from polyapi.utils import add_type_import_path, parse_arguments, get_type_and_def, rewrite_arg_name
from polyapi.typedefs import PropertySpecification, PropertyType
from polyapi.utils import add_type_import_path, parse_arguments, get_type_and_def, return_type_already_defined_in_args, rewrite_arg_name

SERVER_DEFS_TEMPLATE = """
from typing import List, Dict, Any, TypedDict, Callable
Expand All @@ -21,7 +21,7 @@ def {function_name}(
try:
return {return_action}
except:
return resp.text
return resp.text # type: ignore # fallback for debugging


"""
Expand All @@ -37,7 +37,11 @@ def render_server_function(
) -> Tuple[str, str]:
arg_names = [a["name"] for a in arguments]
args, args_def = parse_arguments(function_name, arguments)
return_type_name, return_type_def = get_type_and_def(return_type) # type: ignore
return_type_name, return_type_def = get_type_and_def(cast(PropertyType, return_type), "ReturnType")

if return_type_def and return_type_already_defined_in_args(return_type_name, args_def):
return_type_def = ""

data = "{" + ", ".join([f"'{arg}': {rewrite_arg_name(arg)}" for arg in arg_names]) + "}"
func_type_defs = SERVER_DEFS_TEMPLATE.format(
args_def=args_def,
Expand Down
Loading