From 296848b363c6f82bd96965501e0649173055e7fc Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Tue, 12 Sep 2023 09:43:06 +0530 Subject: [PATCH 01/14] feat: add bun support --- .github/workflows/checks.yml | 6 ++ .gitignore | 2 + README.md | 37 ++++++++--- examples/hello_python.ts | 2 +- mod.ts | 1 + src/bun_compat.js | 97 +++++++++++++++++++++++++++ src/ffi.ts | 2 + src/python.ts | 18 ++++- src/symbols.ts | 125 ----------------------------------- test/asserts.ts | 29 ++++++++ test/bench.ts | 15 +++++ test/bun.test.js | 2 + test/bun_compat.js | 18 +++++ test/deps.ts | 1 - test/package.json | 15 +++++ test/test.ts | 7 +- 16 files changed, 235 insertions(+), 142 deletions(-) create mode 100644 src/bun_compat.js create mode 100644 test/asserts.ts create mode 100644 test/bench.ts create mode 100644 test/bun.test.js create mode 100644 test/bun_compat.js delete mode 100644 test/deps.ts create mode 100644 test/package.json diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index d939b01..a41de1a 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -54,6 +54,9 @@ jobs: with: deno-version: v1.x + - name: Setup Bun + uses: oven-sh/setup-bun@v1 + - name: Setup Python (Windows) uses: actions/setup-python@v2 if: ${{ matrix.os == 'windows-latest' }} @@ -65,3 +68,6 @@ jobs: - name: Run deno test run: deno task test + + - name: Run bun test + run: bun test diff --git a/.gitignore b/.gitignore index a05adb0..532b6ee 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ /*.bat deno.lock plug/ +bun.lockb +node_modules/ diff --git a/README.md b/README.md index 3b63ab8..585acfa 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,19 @@ -# deno_python +# Python Bridge [![Tags](https://img.shields.io/github/release/denosaurs/deno_python)](https://github.com/denosaurs/deno_python/releases) [![deno doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/python/mod.ts) [![checks](https://github.com/denosaurs/deno_python/actions/workflows/checks.yml/badge.svg)](https://github.com/denosaurs/deno_python/actions/workflows/checks.yml) [![License](https://img.shields.io/github/license/denosaurs/deno_python)](https://github.com/denosaurs/deno_python/blob/master/LICENSE) -This module provides a seamless integration between deno and python by -integrating with the [Python/C API](https://docs.python.org/3/c-api/index.html). -It acts as a bridge between the two languages, enabling you to pass data and -execute python code from within your deno applications. This enables access to -the large and wonderful [python ecosystem](https://pypi.org/) while remaining -native (unlike a runtime like the wondeful -[pyodide](https://github.com/pyodide/pyodide) which is compiled to wasm, -sandboxed and may not work with all python packages) and simply using the -existing python installation. +This module provides a seamless integration between JavaScript (Deno/Bun) and +Python by integrating with the +[Python/C API](https://docs.python.org/3/c-api/index.html). It acts as a bridge +between the two languages, enabling you to pass data and execute python code +from within your JS applications. This enables access to the large and wonderful +[python ecosystem](https://pypi.org/) while remaining native (unlike a runtime +like the wondeful [pyodide](https://github.com/pyodide/pyodide) which is +compiled to wasm, sandboxed and may not work with all python packages) and +simply using the existing python installation. ## Example @@ -40,6 +40,23 @@ permissions since enabling FFI effectively escapes the permissions sandbox. deno run -A --unstable ``` +### Usage in Bun + +You can import from the `bunpy` NPM package to use this module in Bun. + +```ts +import { python } from "bunpy"; + +const np = python.import("numpy"); +const plt = python.import("matplotlib.pyplot"); + +const xpoints = np.array([1, 8]); +const ypoints = np.array([3, 10]); + +plt.plot(xpoints, ypoints); +plt.show(); +``` + ### Dependencies Normally deno_python follows the default python way of resolving imports, going diff --git a/examples/hello_python.ts b/examples/hello_python.ts index 868a771..ab20bfd 100644 --- a/examples/hello_python.ts +++ b/examples/hello_python.ts @@ -4,4 +4,4 @@ const { print, str } = python.builtins; const { version } = python.import("sys"); print(str("Hello, World!").lower()); -print(`Python version: ${version}`); +print("Python version:", version); diff --git a/mod.ts b/mod.ts index b27e450..6567ddd 100644 --- a/mod.ts +++ b/mod.ts @@ -1,2 +1,3 @@ +import "./src/bun_compat.js"; export * from "./src/python.ts"; export { python as default } from "./src/python.ts"; diff --git a/src/bun_compat.js b/src/bun_compat.js new file mode 100644 index 0000000..b97a1e0 --- /dev/null +++ b/src/bun_compat.js @@ -0,0 +1,97 @@ +if (!("Deno" in globalThis) && "Bun" in globalThis) { + const { dlopen, FFIType, CString, JSCallback, ptr } = await import("bun:ffi"); + class Deno { + static env = { + get(name) { + return Bun.env[name]; + }, + }; + + static build = { + os: "darwin", + }; + + static transformFFIType(type) { + switch (type) { + case "void": + return FFIType.void; + case "i32": + return FFIType.i64_fast; + case "i64": + return FFIType.i64; + case "f32": + return FFIType.f32; + case "f64": + return FFIType.f64; + case "pointer": + case "buffer": + return FFIType.ptr; + case "u32": + return FFIType.u64_fast; + default: + throw new Error("Type not supported: " + type); + } + } + + static dlopen(path, symbols) { + const bunSymbols = {}; + for (const name in symbols) { + const symbol = symbols[name]; + if ("type" in symbol) { + // throw new Error("Symbol type not supported"); + } else { + bunSymbols[name] = { + args: symbol.parameters.map((type) => this.transformFFIType(type)), + returns: this.transformFFIType(symbol.result), + }; + } + } + const lib = dlopen(path, bunSymbols); + return lib; + } + + static UnsafeCallback = class UnsafeCallback { + constructor(def, fn) { + this.inner = new JSCallback(fn, { + args: def.parameters.map((type) => Deno.transformFFIType(type)), + returns: Deno.transformFFIType(def.result), + }); + this.pointer = this.inner.ptr; + } + + close() { + this.inner.close(); + } + }; + + static UnsafePointerView = class UnsafePointerView { + static getCString(ptr) { + return new CString(ptr); + } + }; + + static UnsafePointer = class UnsafePointer { + static equals(a, b) { + return a === b; + } + + static create(a) { + return Number(a); + } + + static of(buf) { + return ptr(buf); + } + + static value(ptr) { + return ptr; + } + }; + + static test(name, fn) { + globalThis.DenoTestCompat(name, fn); + } + } + + globalThis.Deno = Deno; +} diff --git a/src/ffi.ts b/src/ffi.ts index 64c72b1..7c46aac 100644 --- a/src/ffi.ts +++ b/src/ffi.ts @@ -41,9 +41,11 @@ for (const path of searchPath) { postSetup(path); break; } catch (err) { + console.log(err); if (err instanceof TypeError) { throw new Error( "Cannot load dynamic library because --unstable flag was not set", + { cause: err }, ); } continue; diff --git a/src/python.ts b/src/python.ts index 53881eb..f8dc2ba 100644 --- a/src/python.ts +++ b/src/python.ts @@ -195,7 +195,9 @@ export class PyObject { * Check if the object is NULL (pointer) or None type in Python. */ get isNone() { - return this.handle === null || + // deno-lint-ignore ban-ts-comment + // @ts-expect-error + return this.handle === null || this.handle === 0 || this.handle === python.None[ProxiedPyObject].handle; } @@ -260,6 +262,14 @@ export class PyObject { value: () => this.toString(), }); + Object.defineProperty(object, Symbol.for("nodejs.util.inspect.custom"), { + value: () => this.toString(), + }); + + Object.defineProperty(object, Symbol.toStringTag, { + value: () => this.toString(), + }); + Object.defineProperty(object, Symbol.iterator, { value: () => this[Symbol.iterator](), }); @@ -282,7 +292,7 @@ export class PyObject { return new Proxy(object, { get: (_, name) => { // For the symbols. - if (typeof name === "symbol" && name in object) { + if ((typeof name === "symbol") && name in object) { return (object as any)[name]; } @@ -783,6 +793,10 @@ export class PyObject { [Symbol.for("Deno.customInspect")]() { return this.toString(); } + + [Symbol.for("nodejs.util.inspect.custom")]() { + return this.toString(); + } } /** Python-related error. */ diff --git a/src/symbols.ts b/src/symbols.ts index 176bb0e..8f2fd03 100644 --- a/src/symbols.ts +++ b/src/symbols.ts @@ -1,14 +1,4 @@ export const SYMBOLS = { - Py_DecodeLocale: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - Py_SetProgramName: { - parameters: ["pointer"], - result: "void", - }, - Py_Initialize: { parameters: [], result: "void", @@ -29,11 +19,6 @@ export const SYMBOLS = { result: "pointer", }, - PyEval_GetBuiltins: { - parameters: [], - result: "pointer", - }, - PyRun_SimpleString: { parameters: ["buffer"], result: "i32", @@ -85,11 +70,6 @@ export const SYMBOLS = { result: "pointer", }, - PyObject_CallObject: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - PyObject_GetAttrString: { parameters: ["pointer", "buffer"], result: "pointer", @@ -130,11 +110,6 @@ export const SYMBOLS = { result: "i32", }, - PyDict_Next: { - parameters: ["pointer", "pointer", "pointer", "pointer"], - result: "i32", - }, - PyDict_SetItem: { parameters: ["pointer", "pointer", "pointer"], result: "i32", @@ -205,106 +180,6 @@ export const SYMBOLS = { result: "pointer", }, - PyBytes_FromStringAndSize: { - parameters: ["pointer", "i32"], - result: "pointer", - }, - - PyBytes_AsStringAndSize: { - parameters: ["pointer", "pointer", "pointer"], - result: "i32", - }, - - PyBool_Type: { - parameters: [], - result: "pointer", - }, - - PySlice_Type: { - parameters: [], - result: "pointer", - }, - - PyNumber_Add: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_Subtract: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_Multiply: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_TrueDivide: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_InPlaceAdd: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_InPlaceSubtract: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_InPlaceMultiply: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_InPlaceTrueDivide: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_Negative: { - parameters: ["pointer"], - result: "pointer", - }, - - PyNumber_And: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_Or: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_Xor: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_InPlaceAnd: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_InPlaceOr: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_InPlaceXor: { - parameters: ["pointer", "pointer"], - result: "pointer", - }, - - PyNumber_Invert: { - parameters: ["pointer"], - result: "pointer", - }, - PyList_Size: { parameters: ["pointer"], result: "i32", diff --git a/test/asserts.ts b/test/asserts.ts new file mode 100644 index 0000000..28d0bf0 --- /dev/null +++ b/test/asserts.ts @@ -0,0 +1,29 @@ +// deno-lint-ignore no-explicit-any +export function assert(condition: any) { + if (!condition) { + throw new Error("Assertion failed"); + } +} + +export function assertEquals(actual: T, expected: T) { + if ( + (actual instanceof Map && expected instanceof Map) || + (actual instanceof Set && expected instanceof Set) + ) { + return assertEquals([...actual], [...expected]); + } + const actualS = JSON.stringify(actual); + const expectedS = JSON.stringify(expected); + if (actualS !== expectedS) { + throw new Error(`Expected ${expectedS}, got ${actualS}`); + } +} + +export function assertThrows(fn: () => void) { + try { + fn(); + } catch (_e) { + return; + } + throw new Error("Expected exception"); +} diff --git a/test/bench.ts b/test/bench.ts new file mode 100644 index 0000000..419d894 --- /dev/null +++ b/test/bench.ts @@ -0,0 +1,15 @@ +import { python } from "../mod.ts"; +import { bench, run } from "mitata"; + +const { add } = python.runModule(` +def add(a, b): + return a + b +`); + +bench("noop", () => {}); + +bench("python.add", () => { + add(1, 2); +}); + +await run(); diff --git a/test/bun.test.js b/test/bun.test.js new file mode 100644 index 0000000..0d55eda --- /dev/null +++ b/test/bun.test.js @@ -0,0 +1,2 @@ +import "./bun_compat.js"; +import "./test.ts"; diff --git a/test/bun_compat.js b/test/bun_compat.js new file mode 100644 index 0000000..1822dff --- /dev/null +++ b/test/bun_compat.js @@ -0,0 +1,18 @@ +import { describe, test } from "bun:test"; + +globalThis.DenoTestCompat = function (name, fn) { + const isGroup = (fn + "").includes("(t)"); + if (isGroup) { + describe(name, async () => { + await fn({ + step: async (name, fn) => { + await test(name, fn); + }, + }); + }); + } else { + test(name, async () => { + await fn(); + }); + } +}; diff --git a/test/deps.ts b/test/deps.ts deleted file mode 100644 index 6fc4eab..0000000 --- a/test/deps.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "https://deno.land/std@0.198.0/assert/mod.ts"; diff --git a/test/package.json b/test/package.json new file mode 100644 index 0000000..e920656 --- /dev/null +++ b/test/package.json @@ -0,0 +1,15 @@ +{ + "name": "test", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "mitata": "^0.1.6" + } +} diff --git a/test/test.ts b/test/test.ts index af063a5..57466be 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,4 +1,4 @@ -import { assert, assertEquals, assertThrows } from "./deps.ts"; +import { assert, assertEquals, assertThrows } from "./asserts.ts"; import { kw, NamedArgument, @@ -13,7 +13,7 @@ console.log("Python version:", version); console.log("Executable:", executable); Deno.test("python version", () => { - assert(String(version).match(/^\d+\.\d+\.\d+/)); + assert(version.toString().match(/^\d+\.\d+\.\d+/)); }); Deno.test("types", async (t) => { @@ -192,7 +192,8 @@ Deno.test("slice", async (t) => { assertEquals(list[":2"].valueOf(), [1, 2]); assertEquals(list[":2:"].valueOf(), [1, 2]); assertEquals(list["0:3:2"].valueOf(), [1, 3]); - assertEquals(list["-2:"].valueOf(), [8, 9]); + console.log(list, list["1:"], list["-2:"]); + // assertEquals(list["-2:"].valueOf(), [8, 9]); assertEquals(list["::2"].valueOf(), [1, 3, 5, 7, 9]); }); From f6e638f951cedd6b7bd6be75695eae163c583dc2 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Tue, 12 Sep 2023 09:44:44 +0530 Subject: [PATCH 02/14] x --- src/bun_compat.js | 2 +- src/symbols.ts | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/bun_compat.js b/src/bun_compat.js index b97a1e0..123715a 100644 --- a/src/bun_compat.js +++ b/src/bun_compat.js @@ -38,7 +38,7 @@ if (!("Deno" in globalThis) && "Bun" in globalThis) { for (const name in symbols) { const symbol = symbols[name]; if ("type" in symbol) { - // throw new Error("Symbol type not supported"); + throw new Error("Symbol type not supported"); } else { bunSymbols[name] = { args: symbol.parameters.map((type) => this.transformFFIType(type)), diff --git a/src/symbols.ts b/src/symbols.ts index 8f2fd03..4e84320 100644 --- a/src/symbols.ts +++ b/src/symbols.ts @@ -245,10 +245,6 @@ export const SYMBOLS = { result: "pointer", }, - PyTuple_Pack: { - type: "pointer", - }, - PyCFunction_NewEx: { parameters: ["buffer", "pointer", "pointer"], result: "pointer", From e9a59cbc30566a460c5d5541e6030a4f8112b9f6 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Tue, 12 Sep 2023 09:46:47 +0530 Subject: [PATCH 03/14] fix --- src/ffi.ts | 3 +-- test/test.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/ffi.ts b/src/ffi.ts index 7c46aac..94181ea 100644 --- a/src/ffi.ts +++ b/src/ffi.ts @@ -41,8 +41,7 @@ for (const path of searchPath) { postSetup(path); break; } catch (err) { - console.log(err); - if (err instanceof TypeError) { + if (err instanceof TypeError && !("Bun" in globalThis)) { throw new Error( "Cannot load dynamic library because --unstable flag was not set", { cause: err }, diff --git a/test/test.ts b/test/test.ts index 57466be..6338510 100644 --- a/test/test.ts +++ b/test/test.ts @@ -192,8 +192,7 @@ Deno.test("slice", async (t) => { assertEquals(list[":2"].valueOf(), [1, 2]); assertEquals(list[":2:"].valueOf(), [1, 2]); assertEquals(list["0:3:2"].valueOf(), [1, 3]); - console.log(list, list["1:"], list["-2:"]); - // assertEquals(list["-2:"].valueOf(), [8, 9]); + assertEquals(list["-2:"].valueOf(), [8, 9]); assertEquals(list["::2"].valueOf(), [1, 3, 5, 7, 9]); }); From 0c128284a049e63d91665157000d463b0686249f Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Tue, 12 Sep 2023 10:00:32 +0530 Subject: [PATCH 04/14] fix --- src/bun_compat.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/bun_compat.js b/src/bun_compat.js index 123715a..12b08e1 100644 --- a/src/bun_compat.js +++ b/src/bun_compat.js @@ -1,5 +1,7 @@ +import { type } from "node:os"; + if (!("Deno" in globalThis) && "Bun" in globalThis) { - const { dlopen, FFIType, CString, JSCallback, ptr } = await import("bun:ffi"); + const { dlopen, FFIType, CString, JSCallback, ptr } = Bun.FFI; class Deno { static env = { get(name) { @@ -8,7 +10,7 @@ if (!("Deno" in globalThis) && "Bun" in globalThis) { }; static build = { - os: "darwin", + os: type().toLowerCase(), }; static transformFFIType(type) { From 192bb8dece7c384976aeda36c3e2b2c56d8fd7bf Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Tue, 12 Sep 2023 10:01:49 +0530 Subject: [PATCH 05/14] fix --- src/bun_compat.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bun_compat.js b/src/bun_compat.js index 12b08e1..735741f 100644 --- a/src/bun_compat.js +++ b/src/bun_compat.js @@ -1,7 +1,7 @@ -import { type } from "node:os"; +import {type} from "node:os"; if (!("Deno" in globalThis) && "Bun" in globalThis) { - const { dlopen, FFIType, CString, JSCallback, ptr } = Bun.FFI; + const { dlopen, FFIType, CString, JSCallback, ptr } = await import("bun:ffi"); class Deno { static env = { get(name) { From d470c2be222c1f9f219c68f112441affe34c50e7 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Tue, 12 Sep 2023 10:13:15 +0530 Subject: [PATCH 06/14] x --- .github/workflows/checks.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index a41de1a..dd8a084 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -55,6 +55,7 @@ jobs: deno-version: v1.x - name: Setup Bun + if: ${{ matrix.os != 'windows-latest' }} uses: oven-sh/setup-bun@v1 - name: Setup Python (Windows) @@ -70,4 +71,5 @@ jobs: run: deno task test - name: Run bun test + if: ${{ matrix.os != 'windows-latest' }} run: bun test From 9be0a71e86d03848d6cf20cea3a7eb41fcef93fd Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Tue, 12 Sep 2023 10:13:33 +0530 Subject: [PATCH 07/14] fmt --- src/bun_compat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bun_compat.js b/src/bun_compat.js index 735741f..0685137 100644 --- a/src/bun_compat.js +++ b/src/bun_compat.js @@ -1,4 +1,4 @@ -import {type} from "node:os"; +import { type } from "node:os"; if (!("Deno" in globalThis) && "Bun" in globalThis) { const { dlopen, FFIType, CString, JSCallback, ptr } = await import("bun:ffi"); From 4b68027350bedd929d51675c51649278626dc559 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Tue, 12 Sep 2023 10:28:36 +0530 Subject: [PATCH 08/14] maybe fix --- src/bun_compat.js | 8 ++++++++ src/util.ts | 8 ++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/bun_compat.js b/src/bun_compat.js index 0685137..36788bc 100644 --- a/src/bun_compat.js +++ b/src/bun_compat.js @@ -70,6 +70,14 @@ if (!("Deno" in globalThis) && "Bun" in globalThis) { static getCString(ptr) { return new CString(ptr); } + + constructor(ptr) { + this.ptr = ptr; + } + + getCString() { + return new CString(this.ptr); + } }; static UnsafePointer = class UnsafePointer { diff --git a/src/util.ts b/src/util.ts index a4a6101..99f9b9f 100644 --- a/src/util.ts +++ b/src/util.ts @@ -32,15 +32,11 @@ export function postSetup(lib: string) { libDlDef, ); } else if (Deno.build.os === "darwin") { - libdl = Deno.dlopen(`libc.dylib`, { - dlopen: { - parameters: ["buffer", "i32"], - result: "pointer", - }, - }); + libdl = Deno.dlopen(`libc.dylib`, libDlDef); } else { return; } + console.log("postSetup libdl"); libdl.symbols.dlopen(cstr(lib), 0x00001 | 0x00100); } From 7444426fc64f4763b55b43eb53c792cceda24691 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Tue, 12 Sep 2023 10:34:50 +0530 Subject: [PATCH 09/14] x --- src/ffi.ts | 1 + src/util.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ffi.ts b/src/ffi.ts index 94181ea..f2259e9 100644 --- a/src/ffi.ts +++ b/src/ffi.ts @@ -20,6 +20,7 @@ if (DENO_PYTHON_PATH) { } else if (Deno.build.os === "darwin") { for ( const framework of [ + "/Library/Frameworks/Python.framework/Versions", "/opt/homebrew/Frameworks/Python.framework/Versions", "/usr/local/Frameworks/Python.framework/Versions", ] diff --git a/src/util.ts b/src/util.ts index 99f9b9f..e4566b6 100644 --- a/src/util.ts +++ b/src/util.ts @@ -36,7 +36,6 @@ export function postSetup(lib: string) { } else { return; } - console.log("postSetup libdl"); libdl.symbols.dlopen(cstr(lib), 0x00001 | 0x00100); } From d5426523ae1da4a2213b9bd4f515721b8fddff87 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Tue, 12 Sep 2023 11:18:44 +0530 Subject: [PATCH 10/14] add bun plugin --- .gitignore | 1 + bunfig.toml | 1 + examples/import.js | 7 +++++++ examples/test.py | 2 ++ package.json | 16 ++++++++++++++++ plugin.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 71 insertions(+) create mode 100644 bunfig.toml create mode 100644 examples/import.js create mode 100644 examples/test.py create mode 100644 package.json create mode 100644 plugin.ts diff --git a/.gitignore b/.gitignore index 532b6ee..63efb2e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ deno.lock plug/ bun.lockb node_modules/ +__pycache__/ diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..62b79f8 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1 @@ +preload = ["./plugin.ts"] diff --git a/examples/import.js b/examples/import.js new file mode 100644 index 0000000..c29561f --- /dev/null +++ b/examples/import.js @@ -0,0 +1,7 @@ +import { add } from "./test.py"; +import { print } from "python:builtins"; +import * as np from "python:numpy"; + +console.log(add(1, 2)); +print("Hello, world!"); +console.log(np.array([1, 2, 3])); diff --git a/examples/test.py b/examples/test.py new file mode 100644 index 0000000..2a99cdf --- /dev/null +++ b/examples/test.py @@ -0,0 +1,2 @@ +def add(a, b): + return a + b diff --git a/package.json b/package.json new file mode 100644 index 0000000..4b54d64 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "bunpy", + "version": "0.1.0", + "description": "JavaScript -> Python Bridge for Deno and Bun", + "main": "mod.ts", + "directories": { + "example": "examples", + "test": "test" + }, + "scripts": { + "test": "deno task test && bun test" + }, + "keywords": [], + "author": "DjDeveloperr", + "license": "MIT" +} diff --git a/plugin.ts b/plugin.ts new file mode 100644 index 0000000..c65f38d --- /dev/null +++ b/plugin.ts @@ -0,0 +1,44 @@ +// TODO: Maybe add support for pip: namespace that automatically installs the module if it's not found. + +// deno-lint-ignore-file no-explicit-any +import { plugin } from "bun"; +import { python } from "./mod.ts"; + +const { dir } = python.builtins; +const { SourceFileLoader } = python.import("importlib.machinery"); + +export function exportModule(mod: any) { + const props = dir(mod).valueOf(); + const exports: Record = {}; + for (let prop of props) { + prop = prop.toString(); + exports[prop] = mod[prop]; + } + return exports; +} + +plugin({ + name: "Python Loader", + setup: (build) => { + build.onLoad({ filter: /\.py$/ }, (args) => { + const name = args.path.split("/").pop()!.split(".py")[0]; + const exports = SourceFileLoader(name, args.path).load_module(); + return { + exports: exportModule(exports), + loader: "object", + }; + }); + + build.onResolve({ filter: /.+/, namespace: "python" }, (args) => { + return { path: args.path, namespace: "python" }; + }); + + build.onLoad({ filter: /.+/, namespace: "python" }, (args) => { + const exports = python.import(args.path); + return { + exports: exportModule(exports), + loader: "object", + }; + }); + }, +}); From 4275a8f2982596fdfde2c5d2f4e52fcf12208be2 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Tue, 12 Sep 2023 11:29:47 +0530 Subject: [PATCH 11/14] x --- package.json | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b54d64..c1f883d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bunpy", - "version": "0.1.0", + "version": "0.3.3", "description": "JavaScript -> Python Bridge for Deno and Bun", "main": "mod.ts", "directories": { @@ -10,6 +10,16 @@ "scripts": { "test": "deno task test && bun test" }, + "files": [ + "mod.ts", + "ext/pip.ts", + "src/bun_compat.js", + "src/ffi.ts", + "src/python.ts", + "src/symbols.ts", + "src/util.ts", + "plugin.ts" + ], "keywords": [], "author": "DjDeveloperr", "license": "MIT" From 561571aff1ddc42d61932084dbaef06f22f5df83 Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Thu, 21 Sep 2023 00:12:07 +0530 Subject: [PATCH 12/14] x --- mod.bun.ts | 2 ++ mod.ts | 4 +++- package.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 mod.bun.ts diff --git a/mod.bun.ts b/mod.bun.ts new file mode 100644 index 0000000..9a02e98 --- /dev/null +++ b/mod.bun.ts @@ -0,0 +1,2 @@ +import "./src/bun_compat.js"; +export * from "./mod.ts"; diff --git a/mod.ts b/mod.ts index 6567ddd..92f564e 100644 --- a/mod.ts +++ b/mod.ts @@ -1,3 +1,5 @@ -import "./src/bun_compat.js"; +declare module "python:*"; +declare module "*.py"; + export * from "./src/python.ts"; export { python as default } from "./src/python.ts"; diff --git a/package.json b/package.json index c1f883d..291f790 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "bunpy", "version": "0.3.3", "description": "JavaScript -> Python Bridge for Deno and Bun", - "main": "mod.ts", + "main": "mod.bun.ts", "directories": { "example": "examples", "test": "test" From 22ad40729d58b9aee41092eb0e4b93df510e349c Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Thu, 21 Sep 2023 00:15:13 +0530 Subject: [PATCH 13/14] x --- mod.bun.ts | 3 +++ mod.ts | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mod.bun.ts b/mod.bun.ts index 9a02e98..ad5cf7b 100644 --- a/mod.bun.ts +++ b/mod.bun.ts @@ -1,2 +1,5 @@ +declare module "python:*"; +declare module "*.py"; + import "./src/bun_compat.js"; export * from "./mod.ts"; diff --git a/mod.ts b/mod.ts index 92f564e..b27e450 100644 --- a/mod.ts +++ b/mod.ts @@ -1,5 +1,2 @@ -declare module "python:*"; -declare module "*.py"; - export * from "./src/python.ts"; export { python as default } from "./src/python.ts"; From 356b223b521a69b51dc3acebcb82ed68d0072aaa Mon Sep 17 00:00:00 2001 From: DjDeveloperr Date: Thu, 21 Sep 2023 00:39:08 +0530 Subject: [PATCH 14/14] fix bun test --- ipy.ts | 25 +++++++++++++++++++++++++ test/bun.test.js | 1 + 2 files changed, 26 insertions(+) create mode 100644 ipy.ts diff --git a/ipy.ts b/ipy.ts new file mode 100644 index 0000000..089a16b --- /dev/null +++ b/ipy.ts @@ -0,0 +1,25 @@ +import py, { Python } from "./mod.ts"; +import { Pip, pip } from "./ext/pip.ts"; + +declare global { + const py: Python; + const pip: Pip; +} + +Object.defineProperty(globalThis, "py", { + value: py, + writable: false, + enumerable: false, + configurable: false, +}); + +Object.defineProperty(globalThis, "pip", { + value: pip, + writable: false, + enumerable: false, + configurable: false, +}); + +export * from "./mod.ts"; +export * from "./ext/pip.ts"; +export default py; diff --git a/test/bun.test.js b/test/bun.test.js index 0d55eda..cb81242 100644 --- a/test/bun.test.js +++ b/test/bun.test.js @@ -1,2 +1,3 @@ +import "../src/bun_compat.js"; import "./bun_compat.js"; import "./test.ts";