Skip to content

Commit 4824d96

Browse files
feat(sdk): reusable import module (#970)
<!-- Pull requests are squashed and merged using: - their title as the commit message - their description as the commit body Having a good title and description is important for the users to get readable changelog. --> <!-- 1. Explain WHAT the change is about --> - Closes [MET-819](https://linear.app/metatypedev/issue/MET-819/automated-typegraph-artifact-deps). Example: ```typescript import { Policy, t, typegraph } from "@typegraph/sdk"; import { DenoRuntime, DenoModule } from "@typegraph/sdk/runtimes/deno"; await typegraph({ name: "multi_import" }, (g) => { const deno = new DenoRuntime(); const pub = Policy.public(); const mod = new DenoModule({ path: "ops.ts", deps: ["dep.ts"], exports: ["add", "sub"], // for type hints }); g.expose({ add: deno .import(t.struct({ a: t.float(), b: t.float() }), t.float(), { module: mod.import("add"), }) .withPolicy(pub), sub: deno .import(t.struct({ a: t.float(), b: t.float() }), t.float(), { module: mod.import("sub"), }) .withPolicy(pub), }); }, ); ``` <!-- 3. Explain HOW users should update their code --> #### Migration notes --- - [x] The change comes with new or modified tests - [ ] Hard-to-understand functions have explanatory comments - [x] End-user documentation is updated to reflect the change <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **New Features** - Enhanced module import functionality for Deno and Python runtimes. - Introduced `DenoModule` and `PythonModule` for improved module configuration. - Added new examples demonstrating TypeScript and Python integration using Deno. - **Improvements** - Streamlined import methods with more robust parameter handling. - Added type safety and more precise type definitions for module parameters. - Simplified module import and dependency specification. - **Technical Updates** - Updated runtime import methods with additional parameters. - Introduced new utility functions for module parameter resolution. - Refined type aliases and interfaces for module imports. - Expanded test coverage to include new typegraph files. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent ce6ed96 commit 4824d96

File tree

22 files changed

+504
-157
lines changed

22 files changed

+504
-157
lines changed

docs/metatype.dev/docs/reference/runtimes/deno/index.mdx

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,19 @@ The DenoRuntime allows you to run lightweight and short-lived typescript functio
1414
query={require("./deno.graphql")}
1515
/>
1616

17-
Instead of providing the typescript code inline, we can also point to a file on disk:
18-
19-
```python
20-
# my_typegraph.py
21-
22-
from typegraph import typegraph, Policy, t, Graph
23-
from typegraph.runtimes.deno import DenoRuntime
24-
25-
@typegraph()
26-
def deno(g: Graph):
27-
public = Policy.public()
28-
deno = DenoRuntime()
29-
30-
g.expose(
31-
public,
32-
add=deno.import_(
33-
t.struct({"a": t.number(), "b": t.number()}),
34-
t.number(),
35-
module="main.ts", # path to ts file
36-
name="doAddition", # function export from ts file to use
37-
),
38-
)
39-
```
17+
Instead of providing the typescript code inline, we can also point to a local file:
18+
19+
<TGExample
20+
typegraph="deno"
21+
typescript={require("!!code-loader!../../../../../../examples/typegraphs/deno-import.ts")}
22+
python={require("!!code-loader!../../../../../../examples/typegraphs/deno-import.py")}
23+
disablePlayground={true}
24+
/>
4025

41-
Where main.ts looks like:
26+
Where ops.ts looks like:
4227

4328
```typescript
44-
// main.ts
29+
// ops.ts
4530

4631
interface AddInput {
4732
a: number;

docs/metatype.dev/docs/reference/runtimes/python/index.mdx

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,20 @@
11
# Python
22

3+
import CodeBlock from "@theme/CodeBlock";
4+
import TGExample from "@site/src/components/TGExample";
5+
36
## Python Runtime
47

58
The PythonRuntime allows you to run short-lived code on a Python virtual machine.
69

7-
```python
8-
# my_typegraph.py
9-
10-
from typegraph import typegraph, Policy, t, Graph
11-
from typegraph.runtimes.deno import PythonRuntime
10+
<TGExample
11+
typegraph="deno"
12+
typescript={require("!!code-loader!../../../../../../examples/typegraphs/python.ts")}
13+
python={require("!!code-loader!../../../../../../examples/typegraphs/python.py")}
14+
disablePlayground={true}
15+
/>
1216

13-
@typegraph()
14-
def example_python(g: Graph):
15-
public = Policy.public()
16-
python = PythonRuntime()
17-
18-
g.expose(
19-
public,
20-
add=t.func(
21-
t.struct({"a": t.integer(), "b": t.integer()}),
22-
t.integer(),
23-
# we can provide the code inline using lambdas
24-
python.from_lambda(lambda x: x["a"] + x["b"]),
25-
),
26-
sayHello=python.import_(
27-
t.struct({"name": t.string()}),
28-
t.string(),
29-
# point to pythoin a file on disc
30-
module="hello.py",
31-
name="say_hello"
32-
),
33-
)
34-
```
17+
Where hello.py looks like:
3518

3619
```python
3720
# hello.py

examples/typegraphs/deno-import.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# skip:start
2+
from typegraph import typegraph, Policy, t, Graph
3+
from typegraph.runtimes.deno import DenoRuntime, DenoModule
4+
5+
# skip:end
6+
7+
8+
@typegraph()
9+
def deno_import(g: Graph):
10+
public = Policy.public()
11+
deno = DenoRuntime()
12+
13+
g.expose(
14+
public,
15+
add=deno.import_(
16+
t.struct({"a": t.integer(), "b": t.integer()}),
17+
t.integer(),
18+
module="./scripts/ops.ts", # path to ts file
19+
name="doAddition", # function export from ts file to use
20+
# deps=[], path to dependencies
21+
),
22+
)
23+
24+
# We can also use the following method for reusability
25+
module = DenoModule(
26+
path="./scripts/ops.ts",
27+
deps=["./scripts/deps.ts"],
28+
)
29+
30+
g.expose(
31+
public,
32+
add_alt=deno.import_(
33+
t.struct({"a": t.integer(), "b": t.integer()}),
34+
t.integer(),
35+
module=module.import_("doAddition"), # name of the function to use
36+
),
37+
)

examples/typegraphs/deno-import.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// skip:start
2+
import { Policy, t, typegraph } from "@typegraph/sdk";
3+
import { DenoModule, DenoRuntime } from "@typegraph/sdk/runtimes/deno";
4+
5+
// skip:end
6+
7+
await typegraph(
8+
{
9+
name: "deno-import",
10+
},
11+
(g) => {
12+
const deno = new DenoRuntime();
13+
const pub = Policy.public();
14+
15+
g.expose(
16+
{
17+
add: deno.import(
18+
t.struct({ a: t.integer(), b: t.integer() }),
19+
t.integer(),
20+
{
21+
module: "./scripts/ops.ts", // path to ts file
22+
name: "doAddition", // function export to use
23+
// deps: [], path to dependecies
24+
},
25+
),
26+
},
27+
pub,
28+
);
29+
30+
// We can also use the following method for reusability
31+
const mod = new DenoModule({
32+
path: "./scripts/ops.ts",
33+
deps: ["./scripts/deps.ts"],
34+
});
35+
36+
g.expose(
37+
{
38+
add_alt: deno.import(
39+
t.struct({ a: t.integer(), b: t.integer() }),
40+
t.integer(),
41+
{ module: mod.import("doAddition") }, // name of the function to use
42+
),
43+
},
44+
pub,
45+
);
46+
},
47+
);

examples/typegraphs/python.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from typegraph import typegraph, Policy, t, Graph
2+
from typegraph.runtimes.python import PythonRuntime, PythonModule
3+
4+
5+
@typegraph()
6+
def python(g: Graph):
7+
public = Policy.public()
8+
python = PythonRuntime()
9+
10+
g.expose(
11+
public,
12+
add=python.from_lambda(
13+
t.struct({"a": t.integer(), "b": t.integer()}),
14+
t.integer(),
15+
# we can provide the code inline using lambdas
16+
function=lambda x: x["a"] + x["b"],
17+
),
18+
sayHello=python.import_(
19+
t.struct({"name": t.string()}),
20+
t.string(),
21+
# point to a local python file
22+
module="./scripts/hello.py",
23+
name="say_hello", # name of the function to use
24+
# deps=["deps.py"], path to dependencies
25+
),
26+
)
27+
28+
# We can also use the following method for reusability
29+
module = PythonModule(path="./scripts/hello.py", deps=["./scripts/deps.py"])
30+
31+
g.expose(
32+
public,
33+
sayHelloAlt=python.import_(
34+
t.struct({"name": t.string()}),
35+
t.string(),
36+
module=module.import_("say_hello"), # name of the function to use
37+
),
38+
)

examples/typegraphs/python.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// skip:start
2+
import { Policy, t, typegraph } from "@typegraph/sdk";
3+
import { PythonModule, PythonRuntime } from "@typegraph/sdk/runtimes/python";
4+
5+
// skip:end
6+
7+
await typegraph(
8+
{
9+
name: "python",
10+
},
11+
(g) => {
12+
const python = new PythonRuntime();
13+
const pub = Policy.public();
14+
15+
g.expose(
16+
{
17+
add: python.fromLambda(
18+
t.struct({ a: t.integer(), b: t.integer() }),
19+
t.integer(),
20+
// we can provide the code inline using lambdas
21+
{ code: "lambda x: (x['a'] + x['b'])" },
22+
),
23+
sayHello: python.import(
24+
t.struct({ name: t.string() }),
25+
t.string(),
26+
// point to a local python file
27+
{
28+
module: "./scripts/hello.py",
29+
name: "say_hello", // name of the function to use
30+
// deps: ["deps.py"], path to dependencies
31+
},
32+
),
33+
},
34+
pub,
35+
);
36+
37+
// We can also use the following method for reusability
38+
const mod = new PythonModule({
39+
path: "./scripts/hello.py",
40+
deps: ["./scripts/deps.py"],
41+
});
42+
43+
g.expose(
44+
{
45+
sayHelloAlt: python.import(t.struct({ name: t.string() }), t.string(), {
46+
module: mod.import("say_hello"), // name of the function to use
47+
}),
48+
},
49+
pub,
50+
);
51+
},
52+
);

examples/typegraphs/scripts/deps.py

Whitespace-only changes.

examples/typegraphs/scripts/deps.ts

Whitespace-only changes.

examples/typegraphs/scripts/hello.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def say_hello(x: any):
2+
return f"Hello {x["name"]}"

examples/typegraphs/scripts/ops.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
interface AddInput {
2+
a: number;
3+
b: number;
4+
}
5+
export function doAddition({ a, b }: AddInput) {
6+
return a + b;
7+
}

src/typegraph/deno/src/runtimes/deno.ts

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ import type { Effect } from "../gen/typegraph_core.d.ts";
77
import Policy from "../policy.ts";
88
import { type Materializer, Runtime } from "./mod.ts";
99
import { fx } from "../index.ts";
10+
import {
11+
type ModuleImport,
12+
type ModuleImportPolicy,
13+
resolveModuleParams,
14+
} from "../utils/module.ts";
15+
16+
export { Module as DenoModule } from "../utils/module.ts";
1017

1118
interface FunMat extends Materializer {
1219
code: string;
@@ -32,13 +39,7 @@ export interface DenoFunc {
3239
effect?: Effect;
3340
}
3441

35-
export interface DenoImport {
36-
name: string;
37-
module: string;
38-
deps?: Array<string>;
39-
secrets?: Array<string>;
40-
effect?: Effect;
41-
}
42+
type DenoImport = ModuleImport;
4243

4344
// deno-lint-ignore no-explicit-any
4445
function stringifyFn(code: string | ((...any: []) => any)) {
@@ -85,21 +86,20 @@ export class DenoRuntime extends Runtime {
8586
import<I extends t.Typedef = t.Typedef, O extends t.Typedef = t.Typedef>(
8687
inp: I,
8788
out: O,
88-
{ name, module, deps = [], effect = fx.read(), secrets = [] }: DenoImport,
89+
{ effect = fx.read(), secrets = [], ...params }: DenoImport,
8990
): t.Func<I, O, ImportMat> {
91+
const resolved = resolveModuleParams(params);
9092
const matId = runtimes.importDenoFunction(
9193
{
92-
funcName: name,
93-
module,
94-
deps,
9594
secrets,
95+
...resolved,
9696
},
9797
effect,
9898
);
9999
const mat: ImportMat = {
100100
_id: matId,
101-
name,
102-
module,
101+
name: resolved.funcName,
102+
module: resolved.module,
103103
secrets,
104104
effect,
105105
};
@@ -133,19 +133,18 @@ export class DenoRuntime extends Runtime {
133133
/** Utility for fetching the current request context */
134134
fetchContext<C extends t.Typedef>(outputShape?: C): t.Func {
135135
const returnValue = outputShape ? `context` : "JSON.stringify(context)";
136-
return this.func(
137-
t.struct({}),
138-
outputShape ?? t.json(),
139-
{ code: `(_, { context }) => ${returnValue}` },
140-
);
136+
return this.func(t.struct({}), outputShape ?? t.json(), {
137+
code: `(_, { context }) => ${returnValue}`,
138+
});
141139
}
142140

143141
policy(name: string, _code: string): Policy;
144142
policy(name: string, data: Omit<DenoFunc, "effect">): Policy;
145143
policy(name: string, data: string | Omit<DenoFunc, "effect">): Policy {
146-
const params = typeof data === "string"
147-
? { code: data, secrets: [] }
148-
: { secrets: [], ...data };
144+
const params =
145+
typeof data === "string"
146+
? { code: data, secrets: [] }
147+
: { secrets: [], ...data };
149148

150149
return Policy.create(
151150
name,
@@ -156,17 +155,23 @@ export class DenoRuntime extends Runtime {
156155
);
157156
}
158157

159-
importPolicy(data: Omit<DenoImport, "effect">, name?: string): Policy {
160-
const policyName = name ??
161-
`__imp_${data.module}_${data.name}`.replace(/[^a-zA-Z0-9_]/g, "_");
158+
importPolicy(
159+
{ secrets = [], ...params }: ModuleImportPolicy,
160+
name?: string,
161+
): Policy {
162+
const resolved = resolveModuleParams(params);
163+
const policyName =
164+
name ??
165+
`__imp_${resolved.module}_${resolved.funcName}`.replace(
166+
/[^a-zA-Z0-9_]/g,
167+
"_",
168+
);
162169
return Policy.create(
163170
policyName,
164171
runtimes.importDenoFunction(
165172
{
166-
funcName: data.name,
167-
module: data.module,
168-
secrets: data.secrets ?? [],
169-
deps: data.deps ?? [],
173+
...resolved,
174+
secrets,
170175
},
171176
fx.read(),
172177
),

0 commit comments

Comments
 (0)