From 87b70e588cba6672e17f020650c087f7ed3886e0 Mon Sep 17 00:00:00 2001 From: FITAHIANA Nomeniavo joe <24nomeniavo@gmail.com> Date: Fri, 27 Dec 2024 12:47:36 +0300 Subject: [PATCH] feat: python code validation (#939) Add python code validation on deploy #### Migration notes --- - [x] The change comes with new or modified tests - [ ] Hard-to-understand functions have explanatory comments - [ ] End-user documentation is updated to reflect the change --- .pre-commit-config.yaml | 7 +- Cargo.lock | 185 +++++++++++++++++- Cargo.toml | 19 +- src/typegate/engine/00_runtime.js | 3 + src/typegate/engine/Cargo.toml | 1 + src/typegate/engine/runtime.d.ts | 80 ++++---- src/typegate/engine/src/ext.rs | 2 + src/typegate/engine/src/lib.rs | 1 + src/typegate/engine/src/py_validation.rs | 24 +++ src/typegate/src/runtimes/python/hooks/mod.ts | 68 +++++++ src/typegate/src/typegate/hooks.ts | 36 ++-- src/typegate/src/typegate/mod.ts | 25 ++- tests/runtimes/python/py_fail/dep_fail.py | 5 + tests/runtimes/python/py_fail/hello_fail.py | 8 + tests/runtimes/python/python_test.ts | 16 +- tests/runtimes/python/python_validation.py | 26 +++ tests/runtimes/wasm_reflected/rust/Cargo.lock | 2 +- 17 files changed, 433 insertions(+), 75 deletions(-) create mode 100644 src/typegate/engine/src/py_validation.rs create mode 100644 src/typegate/src/runtimes/python/hooks/mod.ts create mode 100644 tests/runtimes/python/py_fail/dep_fail.py create mode 100644 tests/runtimes/python/py_fail/hello_fail.py create mode 100644 tests/runtimes/python/python_validation.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bc3a39ce3c..87aced3408 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -71,6 +71,7 @@ repos: args: - --maxkb=768 - id: check-ast + exclude: tests/runtimes/python/py_fail/.*\.py - id: check-json exclude: .vscode/.*\.json - id: check-toml @@ -137,8 +138,12 @@ repos: rev: "v0.5.1" hooks: - id: ruff + exclude: tests/runtimes/python/py_fail/.*\.py - id: ruff-format - exclude: "tests/metagen/typegraphs/sample/py/client.py" + exclude: (?x)( + tests/metagen/typegraphs/sample/py/client.py"| + tests/runtimes/python/py_fail/.*\.py + ) - repo: https://github.com/commitizen-tools/commitizen rev: v3.27.0 hooks: diff --git a/Cargo.lock b/Cargo.lock index 658d114911..c0518f22cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5007,6 +5007,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -6143,6 +6152,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -6353,6 +6371,12 @@ dependencies = [ "urlencoding", ] +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" + [[package]] name = "lazy-regex" version = "3.1.0" @@ -6717,6 +6741,64 @@ dependencies = [ "libc", ] +[[package]] +name = "malachite" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5616515d632967cd329b6f6db96be9a03ea0b3a49cdbc45b0016803dad8a77b7" +dependencies = [ + "malachite-base", + "malachite-nz", + "malachite-q", +] + +[[package]] +name = "malachite-base" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46059721011b0458b7bd6d9179be5d0b60294281c23320c207adceaecc54d13b" +dependencies = [ + "hashbrown 0.14.5", + "itertools 0.11.0", + "libm", + "ryu", +] + +[[package]] +name = "malachite-bigint" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17703a19c80bbdd0b7919f0f104f3b0597f7de4fc4e90a477c15366a5ba03faa" +dependencies = [ + "derive_more 0.99.17", + "malachite", + "num-integer", + "num-traits", + "paste", +] + +[[package]] +name = "malachite-nz" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1503b27e825cabd1c3d0ff1e95a39fb2ec9eab6fd3da6cfa41aec7091d273e78" +dependencies = [ + "itertools 0.11.0", + "libm", + "malachite-base", +] + +[[package]] +name = "malachite-q" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a475503a70a3679dbe3b9b230a23622516742528ba614a7b2490f180ea9cb514" +dependencies = [ + "itertools 0.11.0", + "malachite-base", + "malachite-nz", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -6753,7 +6835,7 @@ checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" dependencies = [ "log", "phf 0.10.1", - "phf_codegen", + "phf_codegen 0.10.0", "string_cache", "string_cache_codegen", "tendril", @@ -8396,6 +8478,16 @@ dependencies = [ "phf_shared 0.10.0", ] +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator 0.11.2", + "phf_shared 0.11.2", +] + [[package]] name = "phf_generator" version = "0.10.0" @@ -10067,6 +10159,63 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "rustpython-ast" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cdaf8ee5c1473b993b398c174641d3aa9da847af36e8d5eb8291930b72f31a5" +dependencies = [ + "is-macro", + "malachite-bigint", + "rustpython-parser-core", + "static_assertions", +] + +[[package]] +name = "rustpython-parser" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "868f724daac0caf9bd36d38caf45819905193a901e8f1c983345a68e18fb2abb" +dependencies = [ + "anyhow", + "is-macro", + "itertools 0.11.0", + "lalrpop-util", + "log", + "malachite-bigint", + "num-traits", + "phf 0.11.2", + "phf_codegen 0.11.2", + "rustc-hash 1.1.0", + "rustpython-ast", + "rustpython-parser-core", + "tiny-keccak", + "unic-emoji-char", + "unic-ucd-ident", + "unicode_names2", +] + +[[package]] +name = "rustpython-parser-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b6c12fa273825edc7bccd9a734f0ad5ba4b8a2f4da5ff7efe946f066d0f4ad" +dependencies = [ + "is-macro", + "memchr", + "rustpython-parser-vendored", +] + +[[package]] +name = "rustpython-parser-vendored" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04fcea49a4630a3a5d940f4d514dc4f575ed63c14c3e3ed07146634aed7f67a6" +dependencies = [ + "memchr", + "once_cell", +] + [[package]] name = "rustversion" version = "1.0.17" @@ -12838,6 +12987,7 @@ dependencies = [ "query-engine-metrics", "regex", "request-handlers", + "rustpython-parser", "schema-connector", "schema-core", "serde", @@ -12985,6 +13135,17 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + [[package]] name = "unic-segment" version = "0.9.0" @@ -13103,6 +13264,28 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "unicode_names2" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1673eca9782c84de5f81b82e4109dcfb3611c8ba0d52930ec4a9478f547b2dd" +dependencies = [ + "phf 0.11.2", + "unicode_names2_generator", +] + +[[package]] +name = "unicode_names2_generator" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91e5b84611016120197efd7dc93ef76774f4e084cd73c9fb3ea4a86c570c56e" +dependencies = [ + "getopts", + "log", + "phf_codegen 0.11.2", + "rand 0.8.5", +] + [[package]] name = "unindent" version = "0.2.3" diff --git a/Cargo.toml b/Cargo.toml index 40a4a340b9..05e5299a2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ members = [ "src/metagen-client-rs", "tests/metagen/typegraphs/sample/rs", "tests/metagen/typegraphs/sample/rs_upload", - ] +] exclude = [ "tests/runtimes/wasm_reflected/rust", @@ -72,19 +72,19 @@ sha2 = "0.10.8" seahash = "4.1.0" # patterns -anyhow = "1.0.89" # FIXME: replace anyhow with eyre +anyhow = "1.0.89" # FIXME: replace anyhow with eyre color-eyre = "0.6.3" -eyre = "0.6.12" # NOTE: keep in sync with verison used by color-eyre +eyre = "0.6.12" # NOTE: keep in sync with verison used by color-eyre thiserror = "1.0.64" indoc = "2.0.5" unindent = "0.2.3" itertools = "0.13.0" -lazy_static = "1.5.0" # FIXME: replace with Lazy Cell +lazy_static = "1.5.0" # FIXME: replace with Lazy Cell crossbeam-channel = "0.5.13" enum_dispatch = "0.3.13" tap = "1.0.1" derive_more = { version = "1", features = ["from"] } -cached = "0.53.1" # FIXME: replace usage with a Lazy Cell + dashmap +cached = "0.53.1" # FIXME: replace usage with a Lazy Cell + dashmap garde = "0.20" paste = "1.0.15" @@ -119,11 +119,12 @@ indexmap = { version = "2.6.0", features = ["serde"] } semver = "1.0.23" dashmap = "6.1.0" connection-string = "0.2.0" -chrono = { version = "0.4.38", features = ["serde"] } +chrono = { version = "0.4.38", features = ["serde"] } tera = { version = "1.20", default-features = false } ordered-float = "4.3.0" graphql-parser = "0.4.0" uuid = "1.10.0" +rustpython-parser = "0.4.0" # wasm wasmtime = "25.0.2" @@ -153,7 +154,7 @@ tracing-unwrap = { version = "1.0.1", features = ["log-location"] } tracing-appender = "0.2.3" # async -futures = "=0.3.30" # pinned due to bug with .31 with zeromq (deno) +futures = "=0.3.30" # pinned due to bug with .31 with zeromq (deno) futures-concurrency = "7.6" futures-lite = "2.3" tokio = { version = "1", features = ["parking_lot"] } @@ -167,7 +168,9 @@ temporal-sdk-core-protos = { git = "https://github.com/temporalio/sdk-core", rev # prisma query-core = { git = "https://github.com/metatypedev/prisma-engines", branch = "fix/version-compat" } query-connector = { git = "https://github.com/metatypedev/prisma-engines", branch = "fix/version-compat" } -request-handlers = { git = "https://github.com/metatypedev/prisma-engines", features = ["all"], branch = "fix/version-compat" } +request-handlers = { git = "https://github.com/metatypedev/prisma-engines", features = [ + "all", +], branch = "fix/version-compat" } datamodel-renderer = { git = "https://github.com/metatypedev/prisma-engines", branch = "fix/version-compat" } user-facing-errors = { git = "https://github.com/metatypedev/prisma-engines", branch = "fix/version-compat" } query-engine-metrics = { git = "https://github.com/metatypedev/prisma-engines", branch = "fix/version-compat" } diff --git a/src/typegate/engine/00_runtime.js b/src/typegate/engine/00_runtime.js index caf7bb7e74..80b6d867d4 100644 --- a/src/typegate/engine/00_runtime.js +++ b/src/typegate/engine/00_runtime.js @@ -85,6 +85,9 @@ const Meta = { unregister: getOp("op_grpc_unregister"), callGrpcMethod: getOp("op_call_grpc_method"), }, + py_validation: { + validate: getOp("op_validate"), + }, }; globalThis.Meta = Meta; diff --git a/src/typegate/engine/Cargo.toml b/src/typegate/engine/Cargo.toml index 6567b7cfb1..86d29bfd9d 100644 --- a/src/typegate/engine/Cargo.toml +++ b/src/typegate/engine/Cargo.toml @@ -67,6 +67,7 @@ tonic.workspace = true bytes.workspace = true protobuf.workspace = true protobuf-json-mapping.workspace = true +rustpython-parser.workspace = true [dev-dependencies] env_logger.workspace = true diff --git a/src/typegate/engine/runtime.d.ts b/src/typegate/engine/runtime.d.ts index 9b482ccd1b..4114b4c8ab 100644 --- a/src/typegate/engine/runtime.d.ts +++ b/src/typegate/engine/runtime.d.ts @@ -86,6 +86,10 @@ export type MetaNS = { inp: EnumerateAllChildrenInput, ) => Promise>; }; + + py_validation: { + validate: (inp: string) => void; + }; }; interface WasmInput { @@ -116,16 +120,16 @@ interface PrismaDevInp { } type PrismaApplyOut = | { - ResetRequired: { - reset_reason: string; - }; - } + ResetRequired: { + reset_reason: string; + }; + } | { - Ok: { - applied_migrations: Array; - reset_reason: string | undefined | null; + Ok: { + applied_migrations: Array; + reset_reason: string | undefined | null; + }; }; - }; interface PrismaDeployOut { migration_count: number; applied_migrations: Array; @@ -237,14 +241,14 @@ export type WitWireReq = { export type WitWireHandleError = | { - InstanceNotFound: string; - } + InstanceNotFound: string; + } | { - ModuleErr: string; - } + ModuleErr: string; + } | { - MatErr: string; - }; + MatErr: string; + }; export type WitWireMatInfo = { op_name: string; @@ -261,29 +265,29 @@ export type WitWireInitArgs = { export type WitWireInitResponse = object; export type WitWireInitError = | { - VersionMismatch: string; - } + VersionMismatch: string; + } | { - UnexpectedMat: string; - } + UnexpectedMat: string; + } | { - ModuleErr: string; - } + ModuleErr: string; + } | { - Other: string; - }; + Other: string; + }; export type WitWireHandleResponse = | { - Ok: string; - } + Ok: string; + } | "NoHandler" | { - InJsonErr: string; - } + InJsonErr: string; + } | { - HandlerErr: string; - }; + HandlerErr: string; + }; export type GrpcRegisterInput = { proto_file_content: string; @@ -301,20 +305,20 @@ export type Backend = | { type: "fs" } | { type: "memory" } | { - type: "redis"; - connection_string: string; - }; + type: "redis"; + connection_string: string; + }; export type OperationEvent = | { type: "Sleep"; id: number; start: string; end: string } | { - type: "Save"; - id: number; - value: - | { type: "Retry"; wait_until: string; counter: number } - | { type: "Resolved"; payload: unknown } - | { type: "Failed"; err: unknown }; - } + type: "Save"; + id: number; + value: + | { type: "Retry"; wait_until: string; counter: number } + | { type: "Resolved"; payload: unknown } + | { type: "Failed"; err: unknown }; + } | { type: "Send"; event_name: string; value: unknown } | { type: "Stop"; result: unknown } | { type: "Start"; kwargs: Record } diff --git a/src/typegate/engine/src/ext.rs b/src/typegate/engine/src/ext.rs index 72d4c13686..3d434a4f1c 100644 --- a/src/typegate/engine/src/ext.rs +++ b/src/typegate/engine/src/ext.rs @@ -3,6 +3,7 @@ use crate::interlude::*; use crate::{ + py_validation, runtimes::{grpc, prisma, substantial, temporal, wasm, wit_wire}, typegraph, }; @@ -59,6 +60,7 @@ deno_core::extension!( substantial::op_sub_metadata_write_workflow_link, substantial::op_sub_metadata_write_parent_child_link, substantial::op_sub_metadata_enumerate_all_children, + py_validation::op_validate, // FIXME(yohe): this test broke and has proven difficult to fix // #[cfg(test)] diff --git a/src/typegate/engine/src/lib.rs b/src/typegate/engine/src/lib.rs index a7e83d53ae..bec02096a7 100644 --- a/src/typegate/engine/src/lib.rs +++ b/src/typegate/engine/src/lib.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: MPL-2.0 mod ext; +mod py_validation; mod runtimes; // mod snapshot; mod typegraph; diff --git a/src/typegate/engine/src/py_validation.rs b/src/typegate/engine/src/py_validation.rs new file mode 100644 index 0000000000..107645aebe --- /dev/null +++ b/src/typegate/engine/src/py_validation.rs @@ -0,0 +1,24 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 + +use std::{fs::File, io::Read}; + +use anyhow::Result; +use rustpython_parser::{ast, Parse}; + +#[rustfmt::skip] +use deno_core as deno_core; + +fn read_file(path: &str) -> Result { + let mut output = String::new(); + let mut file = File::open(path)?; + file.read_to_string(&mut output)?; + Ok(output) +} + +#[deno_core::op2(fast)] +pub fn op_validate(#[string] input: String) -> Result<()> { + let python_source = read_file(&input)?; + ast::Suite::parse(&python_source, "")?; + Ok(()) +} diff --git a/src/typegate/src/runtimes/python/hooks/mod.ts b/src/typegate/src/runtimes/python/hooks/mod.ts new file mode 100644 index 0000000000..1a02a48e80 --- /dev/null +++ b/src/typegate/src/runtimes/python/hooks/mod.ts @@ -0,0 +1,68 @@ +// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +// SPDX-License-Identifier: MPL-2.0 + +import { getLogger } from "../../../log.ts"; +import { PushFailure, PushHandler } from "../../../typegate/hooks.ts"; +import { createArtifactMeta } from "../../utils/deno.ts"; + +const logger = getLogger("typegate"); + +export class ValidationFailure extends Error { + failure: PushFailure; + + constructor(message: string) { + super(message); + this.failure = { reason: "ValidationError", message }; + } +} + +export const codeValidations: PushHandler = async ( + typegraph, + _secretManager, + _response, + artifactStore, +) => { + const { title } = typegraph.types[0]; + const { artifacts } = typegraph.meta; + + for (const mat of typegraph.materializers) { + if (mat.name === "import_function") { + const pyModMat = typegraph.materializers[mat.data.mod as number]; + const entryPoint = artifacts[pyModMat.data.entryPoint as string]; + const moduleMeta = createArtifactMeta(title, entryPoint); + const depMetas = (pyModMat.data.deps as string[]).map((dep) => + createArtifactMeta(title, artifacts[dep]) + ); + const entryModulePath = await artifactStore.getLocalPath( + moduleMeta, + depMetas, + ); + + if (entryModulePath.split(".").slice(-1)[0] == "py") { + try { + logger.info( + `Validating Python code at entry point: ${entryPoint.path}`, + ); + Meta.py_validation.validate(entryModulePath); + + for (const dep of depMetas) { + const depPath = await artifactStore.getLocalPath(dep); + logger.info(`Validating Python code for dependency: ${depPath}`); + Meta.py_validation.validate(depPath); + } + + logger.info( + `Successfully validated Python code at entry point: ${entryPoint.path}`, + ); + } catch (err) { + console.error({ err }); + throw new ValidationFailure( + `Python code validation error at entry point '${entryPoint.path}': ${err.message}`, + ); + } + } + } + } + + return typegraph; +}; diff --git a/src/typegate/src/typegate/hooks.ts b/src/typegate/src/typegate/hooks.ts index 1802d61575..3b72a619d2 100644 --- a/src/typegate/src/typegate/hooks.ts +++ b/src/typegate/src/typegate/hooks.ts @@ -13,25 +13,29 @@ const Message = { export type PushFailure = | { - reason: "DatabaseResetRequired"; - message: string; - runtimeName: string; - } + reason: "DatabaseResetRequired"; + message: string; + runtimeName: string; + } | { - reason: "NullConstraintViolation"; - message: string; - runtimeName: string; - column: string; - table: string; - } + reason: "NullConstraintViolation"; + message: string; + runtimeName: string; + column: string; + table: string; + } | { - reason: "DenoImportError"; - message: string; - } + reason: "DenoImportError"; + message: string; + } + | { + reason: "ValidationError"; + message: string; + } | { - reason: "Unknown"; - message: string; - }; + reason: "Unknown"; + message: string; + }; export class PushResponse { tgName?: string; diff --git a/src/typegate/src/typegate/mod.ts b/src/typegate/src/typegate/mod.ts index 05c73ee532..8760da0025 100644 --- a/src/typegate/src/typegate/mod.ts +++ b/src/typegate/src/typegate/mod.ts @@ -20,6 +20,7 @@ import { upgradeTypegraph } from "../typegraph/versions.ts"; import { parseGraphQLTypeGraph } from "../transports/graphql/typegraph.ts"; import * as PrismaHooks from "../runtimes/prisma/hooks/mod.ts"; import * as DenoHooks from "../runtimes/deno/hooks/mod.ts"; +import * as PythonHooks from "../runtimes/python/hooks/mod.ts"; import { type RuntimeResolver, SecretManager, @@ -33,7 +34,10 @@ import { handleGraphQL } from "../services/graphql_service.ts"; import { getLogger } from "../log.ts"; import { MigrationFailure } from "../runtimes/prisma/hooks/run_migrations.ts"; import { DenoFailure } from "../runtimes/deno/hooks/mod.ts"; -import introspectionJson from "../typegraphs/introspection.json" with { type: "json" }; +import { ValidationFailure } from "../runtimes/python/hooks/mod.ts"; +import introspectionJson from "../typegraphs/introspection.json" with { + type: "json", +}; import { ArtifactService } from "../services/artifact_service.ts"; import type { ArtifactStore } from "./artifacts/mod.ts"; // TODO move from tests (MET-497) @@ -171,6 +175,7 @@ export class Typegate implements AsyncDisposable { this.#onPush(PrismaHooks.generateSchema); this.#onPush(PrismaHooks.runMigrations); this.#onPush(DenoHooks.cacheModules); + this.#onPush(PythonHooks.codeValidations); this.#artifactService = new ArtifactService(artifactStore); } @@ -202,6 +207,8 @@ export class Typegate implements AsyncDisposable { response.setFailure(e.errors[0]); } else if (e instanceof DenoFailure) { response.setFailure(e.failure); + } else if (e instanceof ValidationFailure) { + response.setFailure(e.failure); } else { response.setFailure({ reason: "Unknown", @@ -402,14 +409,14 @@ export class Typegate implements AsyncDisposable { const introspection = enableIntrospection ? await TypeGraph.init( - this, - introspectionDef, - new SecretManager(introspectionDef, {}), - { - typegraph: TypeGraphRuntime.init(tgDS, [], {}), - }, - null, - ) + this, + introspectionDef, + new SecretManager(introspectionDef, {}), + { + typegraph: TypeGraphRuntime.init(tgDS, [], {}), + }, + null, + ) : null; const tg = await TypeGraph.init( diff --git a/tests/runtimes/python/py_fail/dep_fail.py b/tests/runtimes/python/py_fail/dep_fail.py new file mode 100644 index 0000000000..d951778807 --- /dev/null +++ b/tests/runtimes/python/py_fail/dep_fail.py @@ -0,0 +1,5 @@ +# Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +# SPDX-License-Identifier: MPL-2.0 + +def hello(name: str: + return f"Hello {name}" diff --git a/tests/runtimes/python/py_fail/hello_fail.py b/tests/runtimes/python/py_fail/hello_fail.py new file mode 100644 index 0000000000..c3fedb1984 --- /dev/null +++ b/tests/runtimes/python/py_fail/hello_fail.py @@ -0,0 +1,8 @@ +# Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +# SPDX-License-Identifier: MPL-2.0 + +from typing import Dict + + +def say_hello(x: Dict): + print(f"hello: {undefind}") diff --git a/tests/runtimes/python/python_test.ts b/tests/runtimes/python/python_test.ts index 93b378dd62..15f2afa9f7 100644 --- a/tests/runtimes/python/python_test.ts +++ b/tests/runtimes/python/python_test.ts @@ -1,7 +1,7 @@ // Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. // SPDX-License-Identifier: MPL-2.0 -import { assert, assertEquals } from "@std/assert"; +import { assert, assertEquals, assertRejects } from "@std/assert"; import { gql, Meta } from "test-utils/mod.ts"; import { WitWireMessenger } from "@metatype/typegate/runtimes/wit_wire/mod.ts"; import { QueryEngine } from "@metatype/typegate/engine/query_engine.ts"; @@ -478,3 +478,17 @@ Meta.test( ); }, ); + +Meta.test( + { + name: "Python code validation on deploy", + }, + async (t) => { + await t.should("fail on validation", async () => { + await assertRejects( + async () => await t.engine("runtimes/python/python_validation.py"), + "ValidationError", + ); + }); + }, +); diff --git a/tests/runtimes/python/python_validation.py b/tests/runtimes/python/python_validation.py new file mode 100644 index 0000000000..41b5705b54 --- /dev/null +++ b/tests/runtimes/python/python_validation.py @@ -0,0 +1,26 @@ +# Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0. +# SPDX-License-Identifier: MPL-2.0 + +from typegraph.graph.typegraph import Graph +from typegraph.policy import Policy +from typegraph.runtimes.python import PythonRuntime + +from typegraph import t, typegraph + + +@typegraph() +def python(g: Graph): + public = Policy.public() + python = PythonRuntime() + + g.expose( + testValidation=( + python.import_( + t.struct({"name": t.string()}), + t.string(), + module="py_fail/hello_fail.py", + deps=["py_fail/dep_fail.py"], + name="sayHello", + ).with_policy(public), + ) + ) diff --git a/tests/runtimes/wasm_reflected/rust/Cargo.lock b/tests/runtimes/wasm_reflected/rust/Cargo.lock index 6160ba1c09..d6b3aeef13 100644 --- a/tests/runtimes/wasm_reflected/rust/Cargo.lock +++ b/tests/runtimes/wasm_reflected/rust/Cargo.lock @@ -114,7 +114,7 @@ dependencies = [ [[package]] name = "rust" -version = "0.5.0-rc.8" +version = "0.5.0-rc.7" dependencies = [ "wit-bindgen", ]