Skip to content

Commit

Permalink
chore: improve api dev, add working example
Browse files Browse the repository at this point in the history
  • Loading branch information
ajskateboarder committed Jan 9, 2024
1 parent 3318fcc commit 2e3c9a3
Show file tree
Hide file tree
Showing 6 changed files with 36 additions and 49 deletions.
6 changes: 6 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"recommendations": [
"ms-python.python",
"svelte.svelte-vscode"
]
}
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
"--show-column-numbers",
"--disallow-untyped-defs",
"--disallow-untyped-calls"
]
],
}
39 changes: 2 additions & 37 deletions docs/pandas.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Simply add a `DataFrame` return type hint to a backend function, and make sure t
You can reference this type however you want, as long as the underlying type is a `pandas.core.frame.DataFrame`

```py
@app.backend()
@app.f
def get_names() -> pd.DataFrame:
"""Return the first and last names of all employees"""
return pd.read_csv("./data/employees.csv")[["First Name", "Last Name"]]
Expand All @@ -26,39 +26,4 @@ export function get_names() {
const res = fetchSync(`/api/get_names?`);
return DataFrame(res.response);
}
```

## Specifying a shape (TODO)

You can improve wrapper type hinting on the frontend by specifying the exact columns that you return from a DataFrame in a backend function.

You can add this functionality by passing the columns you return in the `@app.backend` decorator.

```py
@app.backend("First Name", "Last Name")
def get_names() -> pd.DataFrame:
"""
Return the first and last names of all employees
"""
return pd.read_csv("./data/employees.csv")[["First Name", "Last Name"]]
```

This way, the JavaScript wrapper can use this information to generate child classes which access those specific columns:

```js
class Employee extends DataFrame {
constructor() {
super();
}
/** @returns {DataFrame} */
firstName() {
return super().select("First Name");
}
/** @returns {DataFrame} */
lastName() {
return super().select("Last Name");
}
}
```

These are also used as return types for functions which specify columns.
```
24 changes: 21 additions & 3 deletions spylt/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ def create_html(linker: str) -> str:
.replace("//# sourceMappingURL=bundle.js.map", "")
.replace("const$", "$")
.replace("function$", "function $")
.replace("let$", "let $")
.replace("var$", "var $")
.replace("instanceof$s", "instanceof $s")
)


Expand Down Expand Up @@ -134,6 +137,7 @@ def _create_api(functions: list[Callable], source_file: str) -> Any:
if defined:
if "return" in line:
return_obj = line.strip().split(" ", 1)[-1]
print(return_obj)
pandas_json = (
'.to_dict(orient="records")'
if "pandas.core.frame.DataFrame"
Expand Down Expand Up @@ -234,14 +238,28 @@ def create_interface(apis: list[Callable], source_file: str) -> tuple[list[str],

def create_api(apis: list[Callable], source_file: str) -> str:
"""Messy API to check imports and function name + args and convert to a Quart app"""
args, source, types, imports, _ = _create_api(apis, source_file)
all_args, source, types, imports, _ = _create_api(apis, source_file)

argmap: Any = [
{k: f"request.args.get('{k}', type={w.__name__})" for k, w in zip(l, t)}
for l, t in zip(args, types)
for l, t in zip(all_args, types)
]
argmap = {k: v for d in argmap for k, v in d.items()}

functions = []
for (name, lines), args in zip(source.items(), all_args):
spaces = " " * lines[0].split(" ").count("")
params = "\n".join([
f"{spaces}{var} = {argmap[var]}"
for var in args
]) + "\n"
functions.append(
f"@app.route(\"/api/{name}\")\n\
async def {name}():\n\
{''.join([params, *lines])}",
)
functions = "\n".join(functions)

api_string = (
f"""{_N.join(imports)}
from quart import Quart, request
Expand All @@ -255,7 +273,7 @@ async def root_():
with open("index.html", encoding="utf-8") as fh:
return fh.read()
{_N.join([replace_some(f"@app.route({_Q}/api/{name}{_Q}){_N}async def {name}():{_N}{''.join(lines)}", argmap) for name, lines in source.items()])}
{functions}
"""[
:-4
]
Expand Down
3 changes: 3 additions & 0 deletions spylt/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ def new(namespace: Namespace) -> None:
os.system(
f"npm install --save-dev {' '.join(REQUIREMENTS)} >/dev/null 2>/dev/null"
)
os.system(
f"pip install json2html >/dev/null 2>/dev/null"
)

shutil.copyfile(
Path(__file__).parent / "rollup.config.js.txt",
Expand Down
11 changes: 3 additions & 8 deletions spylt/module.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Module system to import Svelte"""
from __future__ import annotations

from typing import Callable, Any
from typing import Callable
from collections.abc import MutableMapping

import inspect
Expand Down Expand Up @@ -74,14 +74,9 @@ def create_interface(self) -> tuple[str, bool]:
interface, suggest = builder.create_interface(self._apis, self._file)
return "\n\n".join(interface), suggest

def backend(self) -> Callable:
def __call__(self, *args) -> Callable:
"""Create a function which converts to a Quart API route"""

def wrapper(*args: Callable) -> Any:
ret: Any = self.set_apis(*args)
return ret

return wrapper
return self.set_apis(*args)


def require_svelte(path: str) -> Module:
Expand Down

0 comments on commit 2e3c9a3

Please sign in to comment.