datalab-api 0.2.7__tar.gz → 0.2.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {datalab_api-0.2.7/src/datalab_api.egg-info → datalab_api-0.2.9}/PKG-INFO +12 -12
- {datalab_api-0.2.7 → datalab_api-0.2.9}/README.md +5 -5
- {datalab_api-0.2.7 → datalab_api-0.2.9}/pyproject.toml +18 -20
- {datalab_api-0.2.7 → datalab_api-0.2.9}/src/datalab_api/__init__.py +27 -4
- {datalab_api-0.2.7 → datalab_api-0.2.9}/src/datalab_api/_base.py +33 -22
- {datalab_api-0.2.7 → datalab_api-0.2.9/src/datalab_api.egg-info}/PKG-INFO +12 -12
- {datalab_api-0.2.7 → datalab_api-0.2.9}/src/datalab_api.egg-info/requires.txt +6 -3
- datalab_api-0.2.9/tests/test_client.py +51 -0
- datalab_api-0.2.7/tests/test_client.py +0 -22
- {datalab_api-0.2.7 → datalab_api-0.2.9}/LICENSE +0 -0
- {datalab_api-0.2.7 → datalab_api-0.2.9}/setup.cfg +0 -0
- {datalab_api-0.2.7 → datalab_api-0.2.9}/src/datalab_api/cli.py +0 -0
- {datalab_api-0.2.7 → datalab_api-0.2.9}/src/datalab_api/helpers.py +0 -0
- {datalab_api-0.2.7 → datalab_api-0.2.9}/src/datalab_api.egg-info/SOURCES.txt +0 -0
- {datalab_api-0.2.7 → datalab_api-0.2.9}/src/datalab_api.egg-info/dependency_links.txt +0 -0
- {datalab_api-0.2.7 → datalab_api-0.2.9}/src/datalab_api.egg-info/entry_points.txt +0 -0
- {datalab_api-0.2.7 → datalab_api-0.2.9}/src/datalab_api.egg-info/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: datalab-api
|
|
3
|
-
Version: 0.2.
|
|
4
|
-
Summary: A Python API for instances of the datalab data management platform (https://github.com/
|
|
3
|
+
Version: 0.2.9
|
|
4
|
+
Summary: A Python API for instances of the datalab data management platform (https://github.com/datalab-org/datalab).
|
|
5
5
|
Author-email: Matthew Evans <matthew@ml-evs.science>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: homepage, https://github.com/datalab-org/datalab-api
|
|
@@ -10,7 +10,6 @@ Project-URL: documentation, https://datalab-python-api.readthedocs.io
|
|
|
10
10
|
Project-URL: changelog, https://github.com/datalab-org/datalab-api/releases
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -22,12 +21,13 @@ Requires-Python: >=3.9
|
|
|
22
21
|
Description-Content-Type: text/markdown
|
|
23
22
|
License-File: LICENSE
|
|
24
23
|
Requires-Dist: httpx~=0.27
|
|
25
|
-
Requires-Dist: bokeh~=2.4
|
|
24
|
+
Requires-Dist: bokeh~=2.4; platform_system != "Emscripten"
|
|
26
25
|
Requires-Dist: rich~=13.0
|
|
26
|
+
Requires-Dist: numpy<2
|
|
27
27
|
Provides-Extra: dev
|
|
28
|
-
Requires-Dist: pre-commit
|
|
28
|
+
Requires-Dist: pre-commit<5,>=3; extra == "dev"
|
|
29
29
|
Requires-Dist: pytest~=8.0; extra == "dev"
|
|
30
|
-
Requires-Dist: pytest-cov
|
|
30
|
+
Requires-Dist: pytest-cov<7,>=5; extra == "dev"
|
|
31
31
|
Requires-Dist: respx~=0.21; extra == "dev"
|
|
32
32
|
Provides-Extra: docs
|
|
33
33
|
Requires-Dist: mkdocs; extra == "docs"
|
|
@@ -56,11 +56,11 @@ Requires-Dist: datalab-api[cli,dev,docs]; extra == "all"
|
|
|
56
56
|
<a href="https://pypi.org/datalab-api"><img src="https://img.shields.io/pypi/pyversions/datalab-api?logo=python&label=Python%20versions" /></a>
|
|
57
57
|
</div>
|
|
58
58
|
|
|
59
|
-
A simple Python API that can interact with [*datalab*](https://github.com/
|
|
59
|
+
A simple Python API that can interact with [*datalab*](https://github.com/datalab-org/datalab) instances.
|
|
60
60
|
|
|
61
61
|
The idea here is to provide a set of utility functions and models for manipulating samples, cells, inventory, files, users and metadata associated with *datalab* entries in an automated way.
|
|
62
62
|
|
|
63
|
-
This API may not expose all the functionality available in a given *datalab* instance, and the idea would be that this package can support multiple versions of the underlying [*datalab* REST API](https://
|
|
63
|
+
This API may not expose all the functionality available in a given *datalab* instance, and the idea would be that this package can support multiple versions of the underlying [*datalab* REST API](https://docs.datalab-org.io/en/stable/rest_api/).
|
|
64
64
|
This means that the API is primarily *functional* in nature, as opposed to object-oriented, since *datalab* instances are free to use their own custom data models.
|
|
65
65
|
The available schemas are reported as instance metadata and in the future object-oriented models may be able to be genereated directly in the client (so e.g., the returned data would be Python objects like `Sample` rather than JSON data).
|
|
66
66
|
|
|
@@ -88,9 +88,9 @@ pip install .
|
|
|
88
88
|
## Usage
|
|
89
89
|
|
|
90
90
|
Example usage as a Jupyter notebook can be found in the `examples` directory or
|
|
91
|
-
in the [online documentation](https://datalab-
|
|
91
|
+
in the [online documentation](https://api-docs.datalab-org.io), as
|
|
92
92
|
well as the full [API
|
|
93
|
-
documentation](https://datalab-
|
|
93
|
+
documentation](https://api-docs.datalab-org.io/en/stable/reference/).
|
|
94
94
|
|
|
95
95
|
### Authentication
|
|
96
96
|
|
|
@@ -98,7 +98,7 @@ Currently the only supported authentication method is via an API key.
|
|
|
98
98
|
You can generate one for your account for a given *datalab* instance by visiting the `/get-api-key` endpoint of your chosen instance, or, if using a recent version of *datalab*, by visiting your account settings in the browser.
|
|
99
99
|
|
|
100
100
|
This API key can be set via the environment variable `DATALAB_API_KEY`.
|
|
101
|
-
To suport the use case of needing to interact with multiple datalab instances, the client will also check prefixed environment variables that use the [`IDENTIFIER_PREFIX` of the chosen datalab instance](https://
|
|
101
|
+
To suport the use case of needing to interact with multiple datalab instances, the client will also check prefixed environment variables that use the [`IDENTIFIER_PREFIX` of the chosen datalab instance](https://docs.datalab-org.io/en/stable/config/#mandatory-settings), e.g., `GREY_DATALAB_API_KEY` or `PUBLIC_DATALAB_API_KEY`.
|
|
102
102
|
Only keys that match will be read (e.g., other environment variables starting with `PUBLIC_` will be ignored, when connecting to the [public demo datalab](https://demo.datalab-org.io)).
|
|
103
103
|
|
|
104
104
|
### Python API
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
<a href="https://pypi.org/datalab-api"><img src="https://img.shields.io/pypi/pyversions/datalab-api?logo=python&label=Python%20versions" /></a>
|
|
11
11
|
</div>
|
|
12
12
|
|
|
13
|
-
A simple Python API that can interact with [*datalab*](https://github.com/
|
|
13
|
+
A simple Python API that can interact with [*datalab*](https://github.com/datalab-org/datalab) instances.
|
|
14
14
|
|
|
15
15
|
The idea here is to provide a set of utility functions and models for manipulating samples, cells, inventory, files, users and metadata associated with *datalab* entries in an automated way.
|
|
16
16
|
|
|
17
|
-
This API may not expose all the functionality available in a given *datalab* instance, and the idea would be that this package can support multiple versions of the underlying [*datalab* REST API](https://
|
|
17
|
+
This API may not expose all the functionality available in a given *datalab* instance, and the idea would be that this package can support multiple versions of the underlying [*datalab* REST API](https://docs.datalab-org.io/en/stable/rest_api/).
|
|
18
18
|
This means that the API is primarily *functional* in nature, as opposed to object-oriented, since *datalab* instances are free to use their own custom data models.
|
|
19
19
|
The available schemas are reported as instance metadata and in the future object-oriented models may be able to be genereated directly in the client (so e.g., the returned data would be Python objects like `Sample` rather than JSON data).
|
|
20
20
|
|
|
@@ -42,9 +42,9 @@ pip install .
|
|
|
42
42
|
## Usage
|
|
43
43
|
|
|
44
44
|
Example usage as a Jupyter notebook can be found in the `examples` directory or
|
|
45
|
-
in the [online documentation](https://datalab-
|
|
45
|
+
in the [online documentation](https://api-docs.datalab-org.io), as
|
|
46
46
|
well as the full [API
|
|
47
|
-
documentation](https://datalab-
|
|
47
|
+
documentation](https://api-docs.datalab-org.io/en/stable/reference/).
|
|
48
48
|
|
|
49
49
|
### Authentication
|
|
50
50
|
|
|
@@ -52,7 +52,7 @@ Currently the only supported authentication method is via an API key.
|
|
|
52
52
|
You can generate one for your account for a given *datalab* instance by visiting the `/get-api-key` endpoint of your chosen instance, or, if using a recent version of *datalab*, by visiting your account settings in the browser.
|
|
53
53
|
|
|
54
54
|
This API key can be set via the environment variable `DATALAB_API_KEY`.
|
|
55
|
-
To suport the use case of needing to interact with multiple datalab instances, the client will also check prefixed environment variables that use the [`IDENTIFIER_PREFIX` of the chosen datalab instance](https://
|
|
55
|
+
To suport the use case of needing to interact with multiple datalab instances, the client will also check prefixed environment variables that use the [`IDENTIFIER_PREFIX` of the chosen datalab instance](https://docs.datalab-org.io/en/stable/config/#mandatory-settings), e.g., `GREY_DATALAB_API_KEY` or `PUBLIC_DATALAB_API_KEY`.
|
|
56
56
|
Only keys that match will be read (e.g., other environment variables starting with `PUBLIC_` will be ignored, when connecting to the [public demo datalab](https://demo.datalab-org.io)).
|
|
57
57
|
|
|
58
58
|
### Python API
|
|
@@ -5,14 +5,15 @@ build-backend = "setuptools.build_meta"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "datalab-api"
|
|
7
7
|
readme = "README.md"
|
|
8
|
-
description = "A Python API for instances of the datalab data management platform (https://github.com/
|
|
8
|
+
description = "A Python API for instances of the datalab data management platform (https://github.com/datalab-org/datalab)."
|
|
9
9
|
keywords = []
|
|
10
10
|
license = { text = "MIT" }
|
|
11
11
|
authors = [{ name = "Matthew Evans", email = "matthew@ml-evs.science" }]
|
|
12
12
|
dynamic = ["version"]
|
|
13
13
|
classifiers = [
|
|
14
|
-
"Programming Language :: Python :: 3",
|
|
15
|
-
"Programming Language :: Python :: 3
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Programming Language :: Python :: 3.9",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
16
17
|
"Programming Language :: Python :: 3.11",
|
|
17
18
|
"Programming Language :: Python :: 3.12",
|
|
18
19
|
"Intended Audience :: Science/Research",
|
|
@@ -22,18 +23,19 @@ classifiers = [
|
|
|
22
23
|
]
|
|
23
24
|
|
|
24
25
|
requires-python = ">=3.9"
|
|
25
|
-
dependencies =[
|
|
26
|
-
"httpx ~= 0.27",
|
|
27
|
-
|
|
28
|
-
"rich ~= 13.0",
|
|
26
|
+
dependencies = [
|
|
27
|
+
"httpx ~= 0.27", # better HTTP requests
|
|
28
|
+
'bokeh ~= 2.4; platform_system != "Emscripten"', # interactive plots from datalab directly
|
|
29
|
+
"rich ~= 13.0", # nicer terminal output
|
|
30
|
+
"numpy < 2", # upper pin for numpy due to bad pins in bokeh
|
|
29
31
|
]
|
|
30
32
|
|
|
31
33
|
|
|
32
34
|
[project.optional-dependencies]
|
|
33
35
|
dev = [
|
|
34
|
-
"pre-commit
|
|
36
|
+
"pre-commit >= 3,< 5",
|
|
35
37
|
"pytest ~= 8.0",
|
|
36
|
-
"pytest-cov
|
|
38
|
+
"pytest-cov >= 5,< 7",
|
|
37
39
|
"respx ~= 0.21",
|
|
38
40
|
]
|
|
39
41
|
|
|
@@ -46,18 +48,13 @@ docs = [
|
|
|
46
48
|
]
|
|
47
49
|
|
|
48
50
|
cli = [
|
|
49
|
-
"typer ~= 0.9",
|
|
51
|
+
"typer ~= 0.9", # command line interface
|
|
50
52
|
"click-shell ~= 2.1", # REPL-like interface
|
|
51
53
|
]
|
|
52
54
|
|
|
53
|
-
cheminventory-helper = [
|
|
54
|
-
"pandas ~= 2.2",
|
|
55
|
-
"openpyxl ~= 3.0"
|
|
56
|
-
]
|
|
55
|
+
cheminventory-helper = ["pandas ~= 2.2", "openpyxl ~= 3.0"]
|
|
57
56
|
|
|
58
|
-
all = [
|
|
59
|
-
"datalab-api[dev,docs,cli]"
|
|
60
|
-
]
|
|
57
|
+
all = ["datalab-api[dev,docs,cli]"]
|
|
61
58
|
|
|
62
59
|
[project.scripts]
|
|
63
60
|
datalab = "datalab_api.cli:app"
|
|
@@ -68,11 +65,13 @@ repository = "https://github.com/datalab-org/datalab-api"
|
|
|
68
65
|
documentation = "https://datalab-python-api.readthedocs.io"
|
|
69
66
|
changelog = "https://github.com/datalab-org/datalab-api/releases"
|
|
70
67
|
|
|
68
|
+
[tool.uv]
|
|
69
|
+
cache-keys = [{ git = true }]
|
|
70
|
+
|
|
71
71
|
[tool.versioningit.vcs]
|
|
72
72
|
method = "git"
|
|
73
73
|
default-tag = "0.0.1"
|
|
74
74
|
|
|
75
|
-
|
|
76
75
|
[tool.mypy]
|
|
77
76
|
# plugins = "pydantic.mypy"
|
|
78
77
|
ignore_missing_imports = true
|
|
@@ -127,7 +126,6 @@ ignore = [
|
|
|
127
126
|
"PLW0603",
|
|
128
127
|
"PLW2901",
|
|
129
128
|
"PT003",
|
|
130
|
-
"PT004", # pytest-missing-fixture-name-underscore
|
|
131
129
|
"PT006", # pytest-parametrize-names-wrong-type
|
|
132
130
|
"PT013", # pytest-incorrect-pytest-import
|
|
133
131
|
"PTH", # prefer Pathlib to os.path
|
|
@@ -162,7 +160,7 @@ ignore = [
|
|
|
162
160
|
pydocstyle.convention = "numpy"
|
|
163
161
|
isort.known-first-party = ["jobflow_remote"]
|
|
164
162
|
isort.split-on-trailing-comma = false
|
|
165
|
-
fixable = ["A", "B", "C", "D", "E", "F", "I"]
|
|
163
|
+
fixable = ["A", "B", "C", "D", "E", "F", "I", "PT", "RUF"]
|
|
166
164
|
unfixable = []
|
|
167
165
|
# Allow unused variables when underscore-prefixed.
|
|
168
166
|
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
@@ -7,7 +7,7 @@ from typing import Any, Optional, Union
|
|
|
7
7
|
|
|
8
8
|
from ._base import BaseDatalabClient, __version__
|
|
9
9
|
|
|
10
|
-
__all__ = ("
|
|
10
|
+
__all__ = ("DatalabClient", "__version__")
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class DuplicateItemError(ValueError):
|
|
@@ -30,13 +30,17 @@ class DatalabClient(BaseDatalabClient):
|
|
|
30
30
|
def get_info(self) -> dict[str, Any]:
|
|
31
31
|
"""Fetch metadata associated with this datalab instance.
|
|
32
32
|
|
|
33
|
+
The server information is stored on the client and can be accessed
|
|
34
|
+
by the `info` attribute.
|
|
35
|
+
|
|
33
36
|
Returns:
|
|
34
37
|
dict: The JSON response from the `/info` endpoint of the Datalab API.
|
|
35
38
|
|
|
36
39
|
"""
|
|
37
40
|
info_url = f"{self.datalab_api_url}/info"
|
|
38
41
|
info_resp = self.session.get(info_url, follow_redirects=True)
|
|
39
|
-
|
|
42
|
+
self.info = info_resp.json()["data"]
|
|
43
|
+
return self.info
|
|
40
44
|
|
|
41
45
|
def authenticate(self):
|
|
42
46
|
"""Tests authentication of the client with the Datalab API."""
|
|
@@ -48,6 +52,26 @@ class DatalabClient(BaseDatalabClient):
|
|
|
48
52
|
)
|
|
49
53
|
return user_resp.json()
|
|
50
54
|
|
|
55
|
+
def get_block_info(self) -> list[dict[str, Any]]:
|
|
56
|
+
"""Return the list of available data blocks types for this instance,
|
|
57
|
+
including some details and descriptions of their usage.
|
|
58
|
+
|
|
59
|
+
The block information is stored on the client and can be accessed
|
|
60
|
+
by the `block_info` attribute.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
A list of dictionary of block types.
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
block_info_url = f"{self.datalab_api_url}/info/blocks"
|
|
67
|
+
block_info_resp = self.session.get(block_info_url, follow_redirects=True)
|
|
68
|
+
if block_info_resp.status_code != 200:
|
|
69
|
+
raise RuntimeError(
|
|
70
|
+
f"Failed to list block information at {block_info_url}: {block_info_resp.status_code=}."
|
|
71
|
+
)
|
|
72
|
+
self.block_info = block_info_resp.json()["data"]
|
|
73
|
+
return self.block_info
|
|
74
|
+
|
|
51
75
|
def get_items(self, item_type: str | None = "samples") -> list[dict[str, Any]]:
|
|
52
76
|
"""List items of the given type available to the authenticated user.
|
|
53
77
|
|
|
@@ -253,8 +277,7 @@ class DatalabClient(BaseDatalabClient):
|
|
|
253
277
|
|
|
254
278
|
item_data = self.get_item(item_id)
|
|
255
279
|
for f in item_data.get("files", []):
|
|
256
|
-
|
|
257
|
-
url = file_location.replace("/app", self.datalab_api_url)
|
|
280
|
+
url = f"{self.datalab_api_url}/files/{f['immutable_id']}/{f['name']}"
|
|
258
281
|
if Path(f["name"]).exists():
|
|
259
282
|
warnings.warn(f"Will not overwrite existing file {f['name']}")
|
|
260
283
|
continue
|
|
@@ -14,7 +14,7 @@ from rich.table import Table
|
|
|
14
14
|
|
|
15
15
|
__version__ = version("datalab-api")
|
|
16
16
|
|
|
17
|
-
__all__ = ("
|
|
17
|
+
__all__ = ("BaseDatalabClient", "__version__")
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
def pretty_displayer(method):
|
|
@@ -43,21 +43,24 @@ def pretty_displayer(method):
|
|
|
43
43
|
if result:
|
|
44
44
|
pprint(result, max_length=None, max_string=100, max_depth=3)
|
|
45
45
|
elif isinstance(result, list):
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
46
|
+
try:
|
|
47
|
+
table = Table(show_lines=True)
|
|
48
|
+
table.add_column("type", overflow="crop", justify="center")
|
|
49
|
+
table.add_column("ID", style="cyan", no_wrap=True)
|
|
50
|
+
table.add_column("refcode", style="magenta", no_wrap=True)
|
|
51
|
+
table.add_column("name", width=30, overflow="ellipsis", no_wrap=True)
|
|
52
|
+
for item in result[:page_limit]:
|
|
53
|
+
table.add_row(
|
|
54
|
+
item["type"][0].upper(),
|
|
55
|
+
item["item_id"],
|
|
56
|
+
item["refcode"],
|
|
57
|
+
item["name"],
|
|
58
|
+
end_section=True,
|
|
59
|
+
)
|
|
60
|
+
console = Console()
|
|
61
|
+
console.print(table)
|
|
62
|
+
except Exception:
|
|
63
|
+
pprint(result, max_length=None, max_string=100, max_depth=3)
|
|
61
64
|
|
|
62
65
|
return result
|
|
63
66
|
|
|
@@ -97,6 +100,12 @@ class BaseDatalabClient(metaclass=AutoPrettyPrint):
|
|
|
97
100
|
_headers: dict[str, str] = {}
|
|
98
101
|
_timeout: httpx.Timeout = httpx.Timeout(10.0, read=60.0)
|
|
99
102
|
|
|
103
|
+
info: dict[str, Any] = {}
|
|
104
|
+
"""The `data` response from the `/info` endpoint of the Datalab API."""
|
|
105
|
+
|
|
106
|
+
block_info: list[dict[str, Any]] = []
|
|
107
|
+
"""The `data` response from the `/info/blocks` endpoint of the Datalab API."""
|
|
108
|
+
|
|
100
109
|
bad_server_versions: Optional[tuple[tuple[int, int, int]]] = ((0, 2, 0),)
|
|
101
110
|
"""Any known server versions that are not supported by this client."""
|
|
102
111
|
|
|
@@ -134,13 +143,12 @@ class BaseDatalabClient(metaclass=AutoPrettyPrint):
|
|
|
134
143
|
|
|
135
144
|
self._detect_api_url()
|
|
136
145
|
|
|
137
|
-
|
|
146
|
+
self.get_info()
|
|
147
|
+
self.get_block_info()
|
|
138
148
|
|
|
139
|
-
self._datalab_api_versions: list[str] =
|
|
140
|
-
|
|
141
|
-
]
|
|
142
|
-
self._datalab_server_version: str = info_json["data"]["attributes"]["server_version"]
|
|
143
|
-
self._datalab_instance_prefix: Optional[str] = info_json["data"]["attributes"].get(
|
|
149
|
+
self._datalab_api_versions: list[str] = self.info["attributes"]["available_api_versions"]
|
|
150
|
+
self._datalab_server_version: str = self.info["attributes"]["server_version"]
|
|
151
|
+
self._datalab_instance_prefix: Optional[str] = self.info["attributes"].get(
|
|
144
152
|
"identifier_prefix"
|
|
145
153
|
)
|
|
146
154
|
|
|
@@ -170,6 +178,9 @@ class BaseDatalabClient(metaclass=AutoPrettyPrint):
|
|
|
170
178
|
def get_info(self) -> dict[str, Any]:
|
|
171
179
|
raise NotImplementedError
|
|
172
180
|
|
|
181
|
+
def get_block_info(self) -> list[dict[str, Any]]:
|
|
182
|
+
raise NotImplementedError
|
|
183
|
+
|
|
173
184
|
@property
|
|
174
185
|
def session(self) -> httpx.Client:
|
|
175
186
|
if self._session is None:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: datalab-api
|
|
3
|
-
Version: 0.2.
|
|
4
|
-
Summary: A Python API for instances of the datalab data management platform (https://github.com/
|
|
3
|
+
Version: 0.2.9
|
|
4
|
+
Summary: A Python API for instances of the datalab data management platform (https://github.com/datalab-org/datalab).
|
|
5
5
|
Author-email: Matthew Evans <matthew@ml-evs.science>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: homepage, https://github.com/datalab-org/datalab-api
|
|
@@ -10,7 +10,6 @@ Project-URL: documentation, https://datalab-python-api.readthedocs.io
|
|
|
10
10
|
Project-URL: changelog, https://github.com/datalab-org/datalab-api/releases
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.9
|
|
13
|
-
Classifier: Programming Language :: Python :: 3
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -22,12 +21,13 @@ Requires-Python: >=3.9
|
|
|
22
21
|
Description-Content-Type: text/markdown
|
|
23
22
|
License-File: LICENSE
|
|
24
23
|
Requires-Dist: httpx~=0.27
|
|
25
|
-
Requires-Dist: bokeh~=2.4
|
|
24
|
+
Requires-Dist: bokeh~=2.4; platform_system != "Emscripten"
|
|
26
25
|
Requires-Dist: rich~=13.0
|
|
26
|
+
Requires-Dist: numpy<2
|
|
27
27
|
Provides-Extra: dev
|
|
28
|
-
Requires-Dist: pre-commit
|
|
28
|
+
Requires-Dist: pre-commit<5,>=3; extra == "dev"
|
|
29
29
|
Requires-Dist: pytest~=8.0; extra == "dev"
|
|
30
|
-
Requires-Dist: pytest-cov
|
|
30
|
+
Requires-Dist: pytest-cov<7,>=5; extra == "dev"
|
|
31
31
|
Requires-Dist: respx~=0.21; extra == "dev"
|
|
32
32
|
Provides-Extra: docs
|
|
33
33
|
Requires-Dist: mkdocs; extra == "docs"
|
|
@@ -56,11 +56,11 @@ Requires-Dist: datalab-api[cli,dev,docs]; extra == "all"
|
|
|
56
56
|
<a href="https://pypi.org/datalab-api"><img src="https://img.shields.io/pypi/pyversions/datalab-api?logo=python&label=Python%20versions" /></a>
|
|
57
57
|
</div>
|
|
58
58
|
|
|
59
|
-
A simple Python API that can interact with [*datalab*](https://github.com/
|
|
59
|
+
A simple Python API that can interact with [*datalab*](https://github.com/datalab-org/datalab) instances.
|
|
60
60
|
|
|
61
61
|
The idea here is to provide a set of utility functions and models for manipulating samples, cells, inventory, files, users and metadata associated with *datalab* entries in an automated way.
|
|
62
62
|
|
|
63
|
-
This API may not expose all the functionality available in a given *datalab* instance, and the idea would be that this package can support multiple versions of the underlying [*datalab* REST API](https://
|
|
63
|
+
This API may not expose all the functionality available in a given *datalab* instance, and the idea would be that this package can support multiple versions of the underlying [*datalab* REST API](https://docs.datalab-org.io/en/stable/rest_api/).
|
|
64
64
|
This means that the API is primarily *functional* in nature, as opposed to object-oriented, since *datalab* instances are free to use their own custom data models.
|
|
65
65
|
The available schemas are reported as instance metadata and in the future object-oriented models may be able to be genereated directly in the client (so e.g., the returned data would be Python objects like `Sample` rather than JSON data).
|
|
66
66
|
|
|
@@ -88,9 +88,9 @@ pip install .
|
|
|
88
88
|
## Usage
|
|
89
89
|
|
|
90
90
|
Example usage as a Jupyter notebook can be found in the `examples` directory or
|
|
91
|
-
in the [online documentation](https://datalab-
|
|
91
|
+
in the [online documentation](https://api-docs.datalab-org.io), as
|
|
92
92
|
well as the full [API
|
|
93
|
-
documentation](https://datalab-
|
|
93
|
+
documentation](https://api-docs.datalab-org.io/en/stable/reference/).
|
|
94
94
|
|
|
95
95
|
### Authentication
|
|
96
96
|
|
|
@@ -98,7 +98,7 @@ Currently the only supported authentication method is via an API key.
|
|
|
98
98
|
You can generate one for your account for a given *datalab* instance by visiting the `/get-api-key` endpoint of your chosen instance, or, if using a recent version of *datalab*, by visiting your account settings in the browser.
|
|
99
99
|
|
|
100
100
|
This API key can be set via the environment variable `DATALAB_API_KEY`.
|
|
101
|
-
To suport the use case of needing to interact with multiple datalab instances, the client will also check prefixed environment variables that use the [`IDENTIFIER_PREFIX` of the chosen datalab instance](https://
|
|
101
|
+
To suport the use case of needing to interact with multiple datalab instances, the client will also check prefixed environment variables that use the [`IDENTIFIER_PREFIX` of the chosen datalab instance](https://docs.datalab-org.io/en/stable/config/#mandatory-settings), e.g., `GREY_DATALAB_API_KEY` or `PUBLIC_DATALAB_API_KEY`.
|
|
102
102
|
Only keys that match will be read (e.g., other environment variables starting with `PUBLIC_` will be ignored, when connecting to the [public demo datalab](https://demo.datalab-org.io)).
|
|
103
103
|
|
|
104
104
|
### Python API
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
httpx~=0.27
|
|
2
|
-
bokeh~=2.4
|
|
3
2
|
rich~=13.0
|
|
3
|
+
numpy<2
|
|
4
|
+
|
|
5
|
+
[:platform_system != "Emscripten"]
|
|
6
|
+
bokeh~=2.4
|
|
4
7
|
|
|
5
8
|
[all]
|
|
6
9
|
datalab-api[cli,dev,docs]
|
|
@@ -14,9 +17,9 @@ typer~=0.9
|
|
|
14
17
|
click-shell~=2.1
|
|
15
18
|
|
|
16
19
|
[dev]
|
|
17
|
-
pre-commit
|
|
20
|
+
pre-commit<5,>=3
|
|
18
21
|
pytest~=8.0
|
|
19
|
-
pytest-cov
|
|
22
|
+
pytest-cov<7,>=5
|
|
20
23
|
respx~=0.21
|
|
21
24
|
|
|
22
25
|
[docs]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import respx
|
|
3
|
+
from httpx import Response
|
|
4
|
+
|
|
5
|
+
from datalab_api import DatalabClient
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@respx.mock
|
|
9
|
+
def test_redirect_url(fake_ui_html, fake_info_json, fake_block_info_json):
|
|
10
|
+
fake_ui = respx.get("https://ui.datalab.industries").mock(
|
|
11
|
+
return_value=Response(200, content=fake_ui_html)
|
|
12
|
+
)
|
|
13
|
+
fake_info_api = respx.get("https://api.datalab.industries/info").mock(
|
|
14
|
+
return_value=Response(200, json=fake_info_json)
|
|
15
|
+
)
|
|
16
|
+
fake_block_info_api = respx.get("https://api.datalab.industries/info/blocks").mock(
|
|
17
|
+
return_value=Response(200, json=fake_block_info_json)
|
|
18
|
+
)
|
|
19
|
+
with pytest.warns(
|
|
20
|
+
UserWarning,
|
|
21
|
+
match="^Found API URL https://api.datalab.industries in HTML meta tag. Creating client with this URL instead.$",
|
|
22
|
+
):
|
|
23
|
+
client = DatalabClient("https://ui.datalab.industries")
|
|
24
|
+
assert fake_ui.called
|
|
25
|
+
assert fake_info_api.called
|
|
26
|
+
assert fake_block_info_api.called
|
|
27
|
+
assert client.datalab_api_url == "https://api.datalab.industries"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@respx.mock
|
|
31
|
+
def test_sample_list(fake_samples_json, fake_info_json, fake_block_info_json):
|
|
32
|
+
fake_api = respx.get("https://api.datalab.industries/").mock(
|
|
33
|
+
return_value=Response(200, content="<!doctype html></html>")
|
|
34
|
+
)
|
|
35
|
+
fake_samples_api = respx.get("https://api.datalab.industries/samples").mock(
|
|
36
|
+
return_value=Response(200, json=fake_samples_json)
|
|
37
|
+
)
|
|
38
|
+
fake_info_api = respx.get("https://api.datalab.industries/info").mock(
|
|
39
|
+
return_value=Response(200, json=fake_info_json)
|
|
40
|
+
)
|
|
41
|
+
fake_block_info_api = respx.get("https://api.datalab.industries/info/blocks").mock(
|
|
42
|
+
return_value=Response(200, json=fake_block_info_json)
|
|
43
|
+
)
|
|
44
|
+
with DatalabClient("https://api.datalab.industries") as client:
|
|
45
|
+
samples = client.get_items(display=True)
|
|
46
|
+
assert fake_samples_api.called
|
|
47
|
+
assert fake_api.called
|
|
48
|
+
assert fake_info_api.called
|
|
49
|
+
assert fake_block_info_api.called
|
|
50
|
+
assert len(samples)
|
|
51
|
+
assert samples[0]["item_id"] == "test"
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
import respx
|
|
3
|
-
from datalab_api import DatalabClient
|
|
4
|
-
from httpx import Response
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@respx.mock
|
|
8
|
-
def test_redirect_url(fake_ui_html, fake_info_json):
|
|
9
|
-
fake_ui = respx.get("https://ui.datalab.industries").mock(
|
|
10
|
-
return_value=Response(200, content=fake_ui_html)
|
|
11
|
-
)
|
|
12
|
-
fake_api = respx.get("https://api.datalab.industries/info").mock(
|
|
13
|
-
return_value=Response(200, json=fake_info_json)
|
|
14
|
-
)
|
|
15
|
-
with pytest.warns(
|
|
16
|
-
UserWarning,
|
|
17
|
-
match="^Found API URL https://api.datalab.industries in HTML meta tag. Creating client with this URL instead.$",
|
|
18
|
-
):
|
|
19
|
-
client = DatalabClient("https://ui.datalab.industries")
|
|
20
|
-
assert fake_ui.called
|
|
21
|
-
assert fake_api.called
|
|
22
|
-
assert client.datalab_api_url == "https://api.datalab.industries"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|