Skip to content

Commit

Permalink
fix: Linting, documentation, JSR, CI, etc. (#81)
Browse files Browse the repository at this point in the history
  • Loading branch information
eliassjogreen authored Feb 10, 2025
1 parent 93fa14f commit 99c6d09
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 47 deletions.
21 changes: 7 additions & 14 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Setup latest deno version
uses: denoland/setup-deno@v1
with:
deno-version: v1.x
uses: denoland/setup-deno@v2

- name: Run deno fmt
run: deno fmt --check
Expand All @@ -28,31 +26,26 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Setup latest deno version
uses: denoland/setup-deno@v1
with:
deno-version: v1.x
uses: denoland/setup-deno@v2

- name: Run deno task check
run: deno task check


test:
name: test ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest,]
os: [windows-latest, ubuntu-latest, macos-latest]
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Setup latest deno version
uses: denoland/setup-deno@v1
with:
deno-version: v1.x
uses: denoland/setup-deno@v2

- name: Setup Bun
if: ${{ matrix.os != 'windows-latest' }}
Expand All @@ -62,7 +55,7 @@ jobs:
uses: actions/setup-python@v2
if: ${{ matrix.os == 'windows-latest' }}
with:
python-version: '3.13'
python-version: "3.13"

- name: Install NumPy
if: ${{ matrix.os != 'macos-latest' }}
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Publish

on:
push:
branches:
- main

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- name: Checkout sources
uses: actions/checkout@v4

- name: Setup latest deno version
uses: denoland/setup-deno@v2

- name: Publish to JSR
run: deno publish
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,29 @@ the Python dynamic library, which is like `python310.dll` (Windows),
`libpython310.dylib` (macOS) and `libpython310.so` (Linux) depending on
platform.

## Usage with docker

Usage with docker is easiest done using the
[`denoland/deno:bin` image](https://github.com/denoland/deno_docker?tab=readme-ov-file#using-your-own-base-image)
along with the [official `python` image](https://hub.docker.com/_/python/).

```Dockerfile
ARG DENO_VERSION=1.38.2
ARG PYTHON_VERSION=3.12

FROM denoland/deno:bin-$DENO_VERSION AS deno
FROM python:$PYTHON_VERSION

# Copy and configure deno
COPY --from=deno /deno /usr/local/bin/deno
ENTRYPOINT ["/usr/local/bin/deno"]

# Copy your project source
COPY . .

RUN ["run", "-A", "--unstable", "https://deno.land/x/[email protected]/examples/hello_python.ts"]
```

## Maintainers

- DjDeveloper ([@DjDeveloperr](https://github.com/DjDeveloperr))
Expand Down
6 changes: 6 additions & 0 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"name": "@denosaurs/python",
"version": "0.4.4",
"exports": {
".": "./mod.ts",
"./ext/pip": "./ext/pip.ts"
},
"tasks": {
"check": "deno task check:mod && deno task check:ext && deno task check:examples",
"check:mod": "deno check --unstable-ffi mod.ts",
Expand Down
5 changes: 3 additions & 2 deletions ext/pip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@ export class Pip {
*
* ```
*/
async import(module: string, entrypoint?: string) {
// deno-lint-ignore no-explicit-any
async import(module: string, entrypoint?: string): Promise<any> {
const { name } = getModuleNameAndVersion(module);

await this.install(module);
Expand Down Expand Up @@ -166,5 +167,5 @@ export class Pip {
}
}

export const pip = new Pip();
export const pip: Pip = new Pip();
export default pip;
4 changes: 2 additions & 2 deletions ipy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import py, { Python } from "./mod.ts";
import { Pip, pip } from "./ext/pip.ts";
import py, { type Python } from "./mod.ts";
import { type Pip, pip } from "./ext/pip.ts";

declare global {
const py: Python;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bunpy",
"version": "0.3.3",
"version": "0.4.4",
"description": "JavaScript -> Python Bridge for Deno and Bun",
"main": "mod.bun.ts",
"directories": {
Expand Down
65 changes: 38 additions & 27 deletions src/python.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ export function kw(
* ```
*/
export class Callback {
unsafe;
unsafe: Deno.UnsafeCallback<{
parameters: ["pointer", "pointer", "pointer"];
result: "pointer";
}>;

constructor(public callback: PythonJSCallback) {
this.unsafe = new Deno.UnsafeCallback(
Expand Down Expand Up @@ -200,7 +203,7 @@ export class PyObject {
/**
* Check if the object is NULL (pointer) or None type in Python.
*/
get isNone() {
get isNone(): boolean {
// deno-lint-ignore ban-ts-comment
// @ts-expect-error
return this.handle === null || this.handle === 0 ||
Expand Down Expand Up @@ -403,9 +406,17 @@ export class PyObject {
/**
* Performs an equals operation on the Python object.
*/
equals(rhs: PythonConvertible) {
equals(rhs: PythonConvertible): boolean {
const rhsObject = PyObject.from(rhs);
return py.PyObject_RichCompareBool(this.handle, rhsObject.handle, 3);
const comparison = py.PyObject_RichCompareBool(
this.handle,
rhsObject.handle,
3,
);
if (comparison === -1) {
maybeThrowError();
}
return comparison === 1;
}

/**
Expand Down Expand Up @@ -590,7 +601,7 @@ export class PyObject {
/**
* Tries to set the attribute, throws an error otherwise.
*/
setAttr(name: string, v: PythonConvertible) {
setAttr(name: string, v: PythonConvertible): void {
if (
py.PyObject_SetAttrString(
this.handle,
Expand All @@ -603,43 +614,43 @@ export class PyObject {
}

/** Checks if Python object has an attribute of given name. */
hasAttr(attr: string) {
hasAttr(attr: string): boolean {
return py.PyObject_HasAttrString(this.handle, cstr(attr)) !== 0;
}

/**
* Casts a Bool Python object as JS Boolean value.
*/
asBoolean() {
asBoolean(): boolean {
return py.PyLong_AsLong(this.handle) === 1;
}

/**
* Casts a Int Python object as JS Number value.
*/
asLong() {
asLong(): number {
return py.PyLong_AsLong(this.handle) as number;
}

/**
* Casts a Float (Double) Python object as JS Number value.
*/
asDouble() {
asDouble(): number {
return py.PyFloat_AsDouble(this.handle) as number;
}

/**
* Casts a String Python object as JS String value.
*/
asString() {
asString(): string | null {
const str = py.PyUnicode_AsUTF8(this.handle);
return str !== null ? Deno.UnsafePointerView.getCString(str) : null;
}

/**
* Casts a List Python object as JS Array value.
*/
asArray() {
asArray(): PythonConvertible[] {
const array: PythonConvertible[] = [];
for (const i of this) {
array.push(i.valueOf());
Expand All @@ -653,7 +664,7 @@ export class PyObject {
* Note: `from` supports converting both Map and Object to Python Dict.
* But this only supports returning a Map.
*/
asDict() {
asDict(): Map<PythonConvertible, PythonConvertible> {
const dict = new Map<PythonConvertible, PythonConvertible>();
const keys = py.PyDict_Keys(this.handle);
const length = py.PyList_Size(keys) as number;
Expand All @@ -669,7 +680,7 @@ export class PyObject {
return dict;
}

*[Symbol.iterator]() {
*[Symbol.iterator](): Generator<PyObject> {
const iter = py.PyObject_GetIter(this.handle);
let item = py.PyIter_Next(iter);
while (item !== null) {
Expand All @@ -682,8 +693,8 @@ export class PyObject {
/**
* Casts a Set Python object as JS Set object.
*/
asSet() {
const set = new Set();
asSet(): Set<PythonConvertible> {
const set = new Set<PythonConvertible>();
for (const i of this) {
set.add(i.valueOf());
}
Expand All @@ -693,7 +704,7 @@ export class PyObject {
/**
* Casts a Tuple Python object as JS Array value.
*/
asTuple() {
asTuple(): PythonConvertible[] {
const tuple = new Array<PythonConvertible>();
const length = py.PyTuple_Size(this.handle) as number;
for (let i = 0; i < length; i++) {
Expand All @@ -711,7 +722,7 @@ export class PyObject {
* Only primitives are casted as JS value type, otherwise returns
* a proxy to Python object.
*/
valueOf() {
valueOf(): any {
const type = py.PyObject_Type(this.handle);

if (Deno.UnsafePointer.equals(type, python.None[ProxiedPyObject].handle)) {
Expand Down Expand Up @@ -759,7 +770,7 @@ export class PyObject {
call(
positional: (PythonConvertible | NamedArgument)[] = [],
named: Record<string, PythonConvertible> = {},
) {
): PyObject {
// count named arguments
const namedCount = positional.filter(
(arg) => arg instanceof NamedArgument,
Expand Down Expand Up @@ -808,16 +819,16 @@ export class PyObject {
/**
* Returns `str` representation of the Python object.
*/
toString() {
toString(): string {
return new PyObject(py.PyObject_Str(this.handle))
.asString();
.asString()!;
}

[Symbol.for("Deno.customInspect")]() {
[Symbol.for("Deno.customInspect")](): string {
return this.toString();
}

[Symbol.for("nodejs.util.inspect.custom")]() {
[Symbol.for("nodejs.util.inspect.custom")](): string {
return this.toString();
}
}
Expand Down Expand Up @@ -928,7 +939,7 @@ export class Python {
/**
* Runs Python script from the given string.
*/
run(code: string) {
run(code: string): void {
if (py.PyRun_SimpleString(cstr(code)) !== 0) {
throw new EvalError("Failed to run python code");
}
Expand All @@ -938,7 +949,7 @@ export class Python {
* Runs Python script as a module and returns its module object,
* for using its attributes, functions, classes, etc. from JavaScript.
*/
runModule(code: string, name?: string) {
runModule(code: string, name?: string): any {
const module = py.PyImport_ExecCodeModule(
cstr(name ?? "__main__"),
PyObject.from(
Expand All @@ -956,7 +967,7 @@ export class Python {
/**
* Import a module as PyObject.
*/
importObject(name: string) {
importObject(name: string): PyObject {
const mod = py.PyImport_ImportModule(cstr(name));
if (mod === null) {
maybeThrowError();
Expand All @@ -968,7 +979,7 @@ export class Python {
/**
* Import a Python module as a proxy object.
*/
import(name: string) {
import(name: string): any {
return this.importObject(name).proxy;
}

Expand Down Expand Up @@ -1013,7 +1024,7 @@ export class Python {
* and also make use of some common built-ins attached to
* this object, such as `str`, `int`, `tuple`, etc.
*/
export const python = new Python();
export const python: Python = new Python();

/**
* Returns true if the value can be converted into a Python slice or
Expand Down
2 changes: 1 addition & 1 deletion test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ProxiedPyObject,
PyObject,
python,
PythonProxy,
type PythonProxy,
} from "../mod.ts";

const { version, executable } = python.import("sys");
Expand Down

0 comments on commit 99c6d09

Please sign in to comment.