Skip to content

Commit 365d023

Browse files
authored
feat: also allow passing an imported module to Tesseract.from_tesseract_api (#130)
#### Relevant issue or PR n/a #### Description of changes Also allow passing an imported module to `Tesseract.from_tesseract_api`. For example, this allows users to monkeypatch or construct modules at runtime without having to write them to a file. #### Testing done new test on CI #### License - [x] By submitting this pull request, I confirm that my contribution is made under the terms of the [Apache 2.0 license](https://pasteurlabs.github.io/tesseract/LICENSE). - [x] I sign the Developer Certificate of Origin below by adding my name and email address to the `Signed-off-by` line. <details> <summary><b>Developer Certificate of Origin</b></summary> ```text Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` </details> Signed-off-by: Dion Häfner <dion.haefner@simulation.science>
1 parent b587f4d commit 365d023

File tree

2 files changed

+46
-19
lines changed

2 files changed

+46
-19
lines changed

tesseract_core/sdk/tesseract.py

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from dataclasses import dataclass
1010
from functools import cached_property, wraps
1111
from pathlib import Path
12+
from types import ModuleType
1213
from typing import Any, Callable
1314
from urllib.parse import urlparse, urlunparse
1415

@@ -127,26 +128,43 @@ def from_image(
127128
@classmethod
128129
def from_tesseract_api(
129130
cls,
130-
tesseract_api_path: str | Path,
131+
tesseract_api: str | Path | ModuleType,
131132
) -> Tesseract:
132-
"""Create a Tesseract instance from a Tesseract API path.
133+
"""Create a Tesseract instance from a Tesseract API module.
133134
134135
Warning: This does not use a containerized Tesseract, but rather
135136
imports the Tesseract API directly. This is useful for debugging,
136137
but requires a matching runtime environment + all dependencies to be
137138
installed locally.
138139
139140
Args:
140-
tesseract_api_path: Path to the `tesseract_api.py` file.
141+
tesseract_api: Path to the `tesseract_api.py` file, or an
142+
already imported Tesseract API module.
141143
142144
Returns:
143145
A Tesseract instance.
144146
"""
147+
if isinstance(tesseract_api, str | Path):
148+
from tesseract_core.runtime.core import load_module_from_path
149+
150+
tesseract_api_path = Path(tesseract_api).resolve(strict=True)
151+
if not tesseract_api_path.is_file():
152+
raise RuntimeError(
153+
f"Tesseract API path {tesseract_api_path} is not a file."
154+
)
155+
156+
try:
157+
tesseract_api = load_module_from_path(tesseract_api_path)
158+
except ImportError as ex:
159+
raise RuntimeError(
160+
f"Cannot load Tesseract API from {tesseract_api_path}"
161+
) from ex
162+
145163
obj = cls.__new__(cls)
146164
obj._spawn_config = None
147165
obj._serve_context = None
148166
obj._lastlog = None
149-
obj._client = LocalClient(tesseract_api_path)
167+
obj._client = LocalClient(tesseract_api)
150168
return obj
151169

152170
def __enter__(self) -> Tesseract:
@@ -584,23 +602,10 @@ def run_tesseract(self, endpoint: str, payload: dict | None = None) -> dict:
584602
class LocalClient:
585603
"""Local Client for Tesseracts."""
586604

587-
def __init__(self, tesseract_api_path: str | Path) -> None:
588-
from tesseract_core.runtime.core import create_endpoints, load_module_from_path
605+
def __init__(self, tesseract_api: ModuleType) -> None:
606+
from tesseract_core.runtime.core import create_endpoints
589607
from tesseract_core.runtime.serve import create_rest_api
590608

591-
tesseract_api_path = Path(tesseract_api_path).resolve(strict=True)
592-
if not tesseract_api_path.is_file():
593-
raise RuntimeError(
594-
f"Tesseract API path {tesseract_api_path} is not a file."
595-
)
596-
597-
try:
598-
tesseract_api = load_module_from_path(tesseract_api_path)
599-
except ImportError as ex:
600-
raise RuntimeError(
601-
f"Cannot load Tesseract API from {tesseract_api_path}"
602-
) from ex
603-
604609
self._endpoints = {
605610
func.__name__: func for func in create_endpoints(tesseract_api)
606611
}

tests/sdk_tests/test_tesseract.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,28 @@ def test_Tesseract_init():
4545
pass
4646

4747

48+
def test_Tesseract_from_tesseract_api(dummy_tesseract_location, dummy_tesseract_module):
49+
all_endpoints = [
50+
"apply",
51+
"jacobian",
52+
"jacobian_vector_product",
53+
"vector_jacobian_product",
54+
"health",
55+
"input_schema",
56+
"output_schema",
57+
"abstract_eval",
58+
]
59+
60+
t = Tesseract.from_tesseract_api(dummy_tesseract_location / "tesseract_api.py")
61+
endpoints = t.available_endpoints
62+
assert endpoints == all_endpoints
63+
64+
# should also work when importing the module
65+
t = Tesseract.from_tesseract_api(dummy_tesseract_module)
66+
endpoints = t.available_endpoints
67+
assert endpoints == all_endpoints
68+
69+
4870
def test_Tesseract_from_image(mock_serving, mock_clients):
4971
# Object is built and has the correct attributes set
5072
t = Tesseract.from_image("sometesseract:0.2.3", volumes=["/my/files"], gpus=["all"])

0 commit comments

Comments
 (0)