Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 27 additions & 30 deletions extism/extism.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import json
import os
from base64 import b64encode
from cffi import FFI
import typing
from typing import (
get_args,
Expand All @@ -18,7 +17,6 @@
from enum import Enum
from uuid import UUID
from extism_sys import lib as _lib, ffi as _ffi # type: ignore
from annotated_types import Gt
import functools
import pickle

Expand Down Expand Up @@ -242,32 +240,32 @@ class Function:
A host function.
"""

def __init__(self, name: str, args, returns, f, *user_data):
self.pointer = None
args = [a.value for a in args]
returns = [r.value for r in returns]
def __init__(self, namespace: str | None, name: str, args, returns, f, *user_data):
self.namespace = namespace
self.name = name
self.args = [a.value for a in args]
self.returns = [r.value for r in returns]
if len(user_data) > 0:
self.user_data = _ffi.new_handle(user_data)
else:
self.user_data = _ffi.NULL
self.f = f


class _ExtismFunctionMetadata:
def __init__(self, f: Function):
self.pointer = _lib.extism_function_new(
name.encode(),
args,
len(args),
returns,
len(returns),
f,
self.user_data,
f.name.encode(),
f.args,
len(f.args),
f.returns,
len(f.returns),
f.f,
f.user_data,
_ffi.NULL,
)

def set_namespace(self, name: str):
_lib.extism_function_set_namespace(self.pointer, name.encode())

def with_namespace(self, name: str):
self.set_namespace(name)
return self
if f.namespace is not None:
_lib.extism_function_set_namespace(self.pointer, f.namespace.encode())

def __del__(self):
if not hasattr(self, "pointer"):
Expand Down Expand Up @@ -374,12 +372,10 @@ def _map_ret(xs) -> List[Tuple[ValType, Callable[[Any, Any, Any], Any]]]:


class ExplicitFunction(Function):
def __init__(self, name, namespace, args, returns, func, user_data):
def __init__(self, namespace, name, args, returns, func, user_data):
self.func = func

super().__init__(name, args, returns, handle_args, *user_data)
if namespace is not None:
self.set_namespace(namespace)
super().__init__(namespace, name, args, returns, handle_args, *user_data)

functools.update_wrapper(self, func)

Expand All @@ -388,7 +384,7 @@ def __call__(self, *args, **kwargs):


class TypeInferredFunction(ExplicitFunction):
def __init__(self, name, namespace, func, user_data):
def __init__(self, namespace, name, func, user_data):
kwargs: dict[str, Any] = {}
if hasattr(typing, "Annotated"):
kwargs["include_extras"] = True
Expand Down Expand Up @@ -418,8 +414,8 @@ def inner_func(plugin, inputs, outputs, *user_data):
emplace(plugin, slot, result)

super().__init__(
name,
namespace,
name,
[typ for (typ, _) in args],
[typ for (typ, _) in returns],
inner_func,
Expand Down Expand Up @@ -475,11 +471,12 @@ def __init__(
):
wasm = _wasm(plugin)
self.functions = functions
as_extism_functions = [_ExtismFunctionMetadata(f) for f in functions or []]

# Register plugin
errmsg = _ffi.new("char**")
if functions is not None:
function_ptrs = [f.pointer for f in functions]
if as_extism_functions is not None:
function_ptrs = [f.pointer for f in as_extism_functions]
ptr = _ffi.new("ExtismFunction*[]", function_ptrs)
self.plugin = _lib.extism_plugin_new(
wasm, len(wasm), ptr, len(function_ptrs), wasi, errmsg
Expand Down Expand Up @@ -804,10 +801,10 @@ def outer(func):
idx = len(HOST_FN_REGISTRY).to_bytes(length=4, byteorder="big")
user_data.append(idx)
fn = (
TypeInferredFunction(n, namespace, func, user_data)
TypeInferredFunction(namespace, n, func, user_data)
if signature is None
else ExplicitFunction(
n, namespace, signature[0], signature[1], func, user_data
namespace, n, signature[0], signature[1], func, user_data
)
)
HOST_FN_REGISTRY.append(fn)
Expand Down
18 changes: 9 additions & 9 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 19 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
[project]
authors = [{ email = "oss@extism.org", name = "The Extism Authors" }]
name = "extism"
version = "0.0.0+replaced-by-ci"
description = "Extism Host SDK for python"
readme = "README.md"
requires-python = ">=3.11"
dependencies = ["extism-sys>=1.9.0"]

[tool.poetry]
name = "extism"
version = "0.0.0+replaced-by-ci"
Expand All @@ -9,7 +18,7 @@ readme = "README.md"
[tool.poetry.dependencies]
python = "^3.8"
cffi = "^1.10.0"
extism-sys = {version = "^1.0.0rc7", allow-prereleases = true}
extism-sys = { version = ">=1.9.0" }
annotated-types = "^0.6.0"

[tool.poetry.dev-dependencies]
Expand All @@ -25,3 +34,12 @@ mypy = "^1.5.1"
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[dependency-groups]
dev = [
"black>=24.10.0",
"mypy>=1.13.0",
"sphinx-autodoc-typehints>=2.5.0",
"sphinx-rtd-theme>=3.0.2",
"sphinx>=8.1.3",
]
6 changes: 3 additions & 3 deletions tests/test_extism.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def frobbitz(self):

class TestExtism(unittest.TestCase):
def test_call_plugin(self):
plugin = extism.Plugin(self._manifest())
plugin = extism.Plugin(self._manifest(), functions=[])
j = json.loads(plugin.call("count_vowels", "this is a test"))
self.assertEqual(j["count"], 4)
j = json.loads(plugin.call("count_vowels", "this is a test again"))
Expand All @@ -32,7 +32,7 @@ def test_call_plugin(self):
self.assertEqual(j["count"], 3)

def test_function_exists(self):
plugin = extism.Plugin(self._manifest())
plugin = extism.Plugin(self._manifest(), functions=[])
self.assertTrue(plugin.function_exists("count_vowels"))
self.assertFalse(plugin.function_exists("i_dont_exist"))

Expand Down Expand Up @@ -115,7 +115,7 @@ def hello_world(
inp: typing.Annotated[
str, extism.Codec(lambda xs: xs.decode().replace("o", "u"))
],
*user_data
*user_data,
) -> typing.Annotated[
str, extism.Codec(lambda xs: xs.replace("u", "a").encode())
]:
Expand Down