juniper-data-client 0.3.0__tar.gz → 0.4.0__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.
Files changed (22) hide show
  1. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/PKG-INFO +28 -10
  2. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/README.md +25 -6
  3. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/juniper_data_client/__init__.py +3 -10
  4. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/juniper_data_client/client.py +122 -11
  5. juniper_data_client-0.4.0/juniper_data_client/testing/__init__.py +45 -0
  6. juniper_data_client-0.4.0/juniper_data_client/testing/fake_client.py +715 -0
  7. juniper_data_client-0.4.0/juniper_data_client/testing/generators.py +284 -0
  8. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/juniper_data_client.egg-info/PKG-INFO +28 -10
  9. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/juniper_data_client.egg-info/SOURCES.txt +8 -1
  10. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/pyproject.toml +30 -8
  11. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/tests/test_client.py +200 -9
  12. juniper_data_client-0.4.0/tests/test_fake_client.py +568 -0
  13. juniper_data_client-0.4.0/tests/test_fake_client_batch.py +312 -0
  14. juniper_data_client-0.4.0/tests/test_performance.py +402 -0
  15. juniper_data_client-0.4.0/tests/test_versioning.py +215 -0
  16. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/LICENSE +0 -0
  17. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/juniper_data_client/exceptions.py +0 -0
  18. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/juniper_data_client/py.typed +0 -0
  19. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/juniper_data_client.egg-info/dependency_links.txt +0 -0
  20. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/juniper_data_client.egg-info/requires.txt +0 -0
  21. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/juniper_data_client.egg-info/top_level.txt +0 -0
  22. {juniper_data_client-0.3.0 → juniper_data_client-0.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: juniper-data-client
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: HTTP client for the JuniperData dataset generation service
5
5
  Author: Paul Calnon
6
6
  License-Expression: MIT
@@ -8,20 +8,19 @@ Project-URL: Homepage, https://github.com/pcalnon/juniper-data-client
8
8
  Project-URL: Repository, https://github.com/pcalnon/juniper-data-client
9
9
  Project-URL: Documentation, https://github.com/pcalnon/juniper-data-client#readme
10
10
  Project-URL: Issues, https://github.com/pcalnon/juniper-data-client/issues
11
- Keywords: juniper,dataset,machine-learning,api-client
11
+ Keywords: juniper,juniper-ml,juniper-ai,artificial-intelligence,dataset,machine-learning,api-client
12
12
  Classifier: Development Status :: 4 - Beta
13
13
  Classifier: Intended Audience :: Developers
14
14
  Classifier: Intended Audience :: Science/Research
15
15
  Classifier: Operating System :: OS Independent
16
16
  Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.11
18
17
  Classifier: Programming Language :: Python :: 3.12
19
18
  Classifier: Programming Language :: Python :: 3.13
20
19
  Classifier: Programming Language :: Python :: 3.14
21
20
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
23
22
  Classifier: Typing :: Typed
24
- Requires-Python: >=3.11
23
+ Requires-Python: >=3.12
25
24
  Description-Content-Type: text/markdown
26
25
  License-File: LICENSE
27
26
  Requires-Dist: numpy>=1.24.0
@@ -47,7 +46,16 @@ Python client library for the JuniperData REST API.
47
46
 
48
47
  ## Overview
49
48
 
50
- `juniper-data-client` provides a simple, robust client for interacting with the JuniperData dataset generation service. It is the official client library used by both JuniperCascor (neural network backend) and JuniperCanopy (web dashboard).
49
+ `juniper-data-client` provides a simple, robust client for interacting with the JuniperData dataset generation service. It is the official client library used by both JuniperCascor (neural network backend) and juniper-canopy (web dashboard).
50
+
51
+ ## Ecosystem Compatibility
52
+
53
+ This package is part of the [Juniper](https://github.com/pcalnon/juniper-ml) ecosystem.
54
+ Compatible with:
55
+
56
+ | juniper-data | juniper-cascor | juniper-canopy |
57
+ |---|---|---|
58
+ | 0.4.x | 0.3.x | 0.2.x |
51
59
 
52
60
  ## Installation
53
61
 
@@ -198,6 +206,12 @@ y_train = torch.from_numpy(arrays["y_train"]) # torch.float32
198
206
  | `download_artifact_bytes(id)` | Download raw NPZ bytes |
199
207
  | `get_preview(id, n)` | Get JSON preview of samples |
200
208
  | `delete_dataset(id)` | Delete a dataset |
209
+ | `list_versions(name)` | List version history for a dataset |
210
+ | `get_latest(name)` | Get latest version by name |
211
+ | `batch_delete(dataset_ids)` | Delete multiple datasets |
212
+ | `batch_create(datasets)` | Create multiple datasets |
213
+ | `batch_update_tags(dataset_ids, add_tags, remove_tags)` | Update tags on multiple datasets |
214
+ | `batch_export(dataset_ids)` | Export multiple datasets as ZIP |
201
215
  | `close()` | Close the client session |
202
216
 
203
217
  ### Exceptions
@@ -234,7 +248,7 @@ Downloaded artifacts contain the following numpy arrays (all `float32`):
234
248
 
235
249
  ## Requirements
236
250
 
237
- - Python >=3.11
251
+ - Python >=3.12
238
252
  - numpy >=1.24.0
239
253
  - requests >=2.28.0
240
254
  - urllib3 >=2.0.0
@@ -243,8 +257,12 @@ Downloaded artifacts contain the following numpy arrays (all `float32`):
243
257
 
244
258
  MIT License - Copyright (c) 2024-2026 Paul Calnon
245
259
 
246
- ## See Also
260
+ ## Juniper Ecosystem
261
+
262
+ This package is part of the Juniper Cascade Correlation Neural Network Research Platform.
247
263
 
248
- - [JuniperData](https://github.com/pcalnon/Juniper/tree/main/JuniperData)
249
- - [JuniperCascor](https://github.com/pcalnon/Juniper/tree/main/JuniperCascor)
250
- - [JuniperCanopy](https://github.com/pcalnon/Juniper/tree/main/JuniperCanopy)
264
+ | Package | Description | Install |
265
+ |---------|-------------|---------|
266
+ | [juniper-data-client](https://github.com/pcalnon/juniper-data-client) | Dataset service client (this package) | `pip install juniper-data-client` |
267
+ | [juniper-cascor-client](https://github.com/pcalnon/juniper-cascor-client) | Neural network service client | `pip install juniper-cascor-client` |
268
+ | [juniper-cascor-worker](https://github.com/pcalnon/juniper-cascor-worker) | Distributed training worker | `pip install juniper-cascor-worker` |
@@ -4,7 +4,16 @@ Python client library for the JuniperData REST API.
4
4
 
5
5
  ## Overview
6
6
 
7
- `juniper-data-client` provides a simple, robust client for interacting with the JuniperData dataset generation service. It is the official client library used by both JuniperCascor (neural network backend) and JuniperCanopy (web dashboard).
7
+ `juniper-data-client` provides a simple, robust client for interacting with the JuniperData dataset generation service. It is the official client library used by both JuniperCascor (neural network backend) and juniper-canopy (web dashboard).
8
+
9
+ ## Ecosystem Compatibility
10
+
11
+ This package is part of the [Juniper](https://github.com/pcalnon/juniper-ml) ecosystem.
12
+ Compatible with:
13
+
14
+ | juniper-data | juniper-cascor | juniper-canopy |
15
+ |---|---|---|
16
+ | 0.4.x | 0.3.x | 0.2.x |
8
17
 
9
18
  ## Installation
10
19
 
@@ -155,6 +164,12 @@ y_train = torch.from_numpy(arrays["y_train"]) # torch.float32
155
164
  | `download_artifact_bytes(id)` | Download raw NPZ bytes |
156
165
  | `get_preview(id, n)` | Get JSON preview of samples |
157
166
  | `delete_dataset(id)` | Delete a dataset |
167
+ | `list_versions(name)` | List version history for a dataset |
168
+ | `get_latest(name)` | Get latest version by name |
169
+ | `batch_delete(dataset_ids)` | Delete multiple datasets |
170
+ | `batch_create(datasets)` | Create multiple datasets |
171
+ | `batch_update_tags(dataset_ids, add_tags, remove_tags)` | Update tags on multiple datasets |
172
+ | `batch_export(dataset_ids)` | Export multiple datasets as ZIP |
158
173
  | `close()` | Close the client session |
159
174
 
160
175
  ### Exceptions
@@ -191,7 +206,7 @@ Downloaded artifacts contain the following numpy arrays (all `float32`):
191
206
 
192
207
  ## Requirements
193
208
 
194
- - Python >=3.11
209
+ - Python >=3.12
195
210
  - numpy >=1.24.0
196
211
  - requests >=2.28.0
197
212
  - urllib3 >=2.0.0
@@ -200,8 +215,12 @@ Downloaded artifacts contain the following numpy arrays (all `float32`):
200
215
 
201
216
  MIT License - Copyright (c) 2024-2026 Paul Calnon
202
217
 
203
- ## See Also
218
+ ## Juniper Ecosystem
219
+
220
+ This package is part of the Juniper Cascade Correlation Neural Network Research Platform.
204
221
 
205
- - [JuniperData](https://github.com/pcalnon/Juniper/tree/main/JuniperData)
206
- - [JuniperCascor](https://github.com/pcalnon/Juniper/tree/main/JuniperCascor)
207
- - [JuniperCanopy](https://github.com/pcalnon/Juniper/tree/main/JuniperCanopy)
222
+ | Package | Description | Install |
223
+ |---------|-------------|---------|
224
+ | [juniper-data-client](https://github.com/pcalnon/juniper-data-client) | Dataset service client (this package) | `pip install juniper-data-client` |
225
+ | [juniper-cascor-client](https://github.com/pcalnon/juniper-cascor-client) | Neural network service client | `pip install juniper-cascor-client` |
226
+ | [juniper-cascor-worker](https://github.com/pcalnon/juniper-cascor-worker) | Distributed training worker | `pip install juniper-cascor-worker` |
@@ -1,20 +1,13 @@
1
1
  """JuniperData Client - Python client library for the JuniperData REST API.
2
2
 
3
3
  This package provides a simple, robust client for interacting with the JuniperData
4
- dataset generation service, used by both JuniperCascor and JuniperCanopy.
4
+ dataset generation service, used by both JuniperCascor and juniper-canopy.
5
5
  """
6
6
 
7
7
  from juniper_data_client.client import JuniperDataClient
8
- from juniper_data_client.exceptions import (
9
- JuniperDataClientError,
10
- JuniperDataConfigurationError,
11
- JuniperDataConnectionError,
12
- JuniperDataNotFoundError,
13
- JuniperDataTimeoutError,
14
- JuniperDataValidationError,
15
- )
8
+ from juniper_data_client.exceptions import JuniperDataClientError, JuniperDataConfigurationError, JuniperDataConnectionError, JuniperDataNotFoundError, JuniperDataTimeoutError, JuniperDataValidationError
16
9
 
17
- __version__ = "0.3.0"
10
+ __version__ = "0.4.0"
18
11
 
19
12
  __all__ = [
20
13
  "JuniperDataClient",
@@ -1,7 +1,7 @@
1
1
  """REST API client for JuniperData service integration.
2
2
 
3
3
  Provides dataset creation, artifact download, and preview functionality
4
- for JuniperCascor and JuniperCanopy applications.
4
+ for JuniperCascor and juniper-canopy applications.
5
5
  """
6
6
 
7
7
  import io
@@ -15,13 +15,7 @@ import requests
15
15
  from requests.adapters import HTTPAdapter
16
16
  from urllib3.util.retry import Retry
17
17
 
18
- from juniper_data_client.exceptions import (
19
- JuniperDataClientError,
20
- JuniperDataConnectionError,
21
- JuniperDataNotFoundError,
22
- JuniperDataTimeoutError,
23
- JuniperDataValidationError,
24
- )
18
+ from juniper_data_client.exceptions import JuniperDataClientError, JuniperDataConnectionError, JuniperDataNotFoundError, JuniperDataTimeoutError, JuniperDataValidationError
25
19
 
26
20
 
27
21
  class JuniperDataClient:
@@ -104,7 +98,7 @@ class JuniperDataClient:
104
98
  total=self.retries,
105
99
  backoff_factor=self.backoff_factor,
106
100
  status_forcelist=[429, 500, 502, 503, 504],
107
- allowed_methods=["HEAD", "GET", "POST", "DELETE"],
101
+ allowed_methods=["HEAD", "GET", "POST", "PATCH", "DELETE"],
108
102
  )
109
103
 
110
104
  adapter = HTTPAdapter(
@@ -233,7 +227,16 @@ class JuniperDataClient:
233
227
  response = self._request("GET", f"/v1/generators/{name}/schema")
234
228
  return response.json()
235
229
 
236
- def create_dataset(self, generator: str, params: Dict[str, Any], persist: bool = True) -> Dict[str, Any]:
230
+ def create_dataset(
231
+ self,
232
+ generator: str,
233
+ params: Dict[str, Any],
234
+ persist: bool = True,
235
+ name: Optional[str] = None,
236
+ description: Optional[str] = None,
237
+ created_by: Optional[str] = None,
238
+ parent_dataset_id: Optional[str] = None,
239
+ ) -> Dict[str, Any]:
237
240
  """Create a new dataset via the JuniperData API.
238
241
 
239
242
  If a dataset with the same parameters already exists, the existing
@@ -243,6 +246,11 @@ class JuniperDataClient:
243
246
  generator: Name of the dataset generator to use (e.g., "spiral")
244
247
  params: Parameters to pass to the generator
245
248
  persist: Whether to persist the dataset (default: True)
249
+ name: Optional dataset name for versioning. When provided, the service
250
+ automatically assigns an incrementing version number.
251
+ description: Optional human-readable description of the dataset.
252
+ created_by: Optional identifier for the creator (user or system).
253
+ parent_dataset_id: Optional ID of the parent dataset this was derived from.
246
254
 
247
255
  Returns:
248
256
  Parsed JSON response containing dataset_id, generator, meta, and artifact_url
@@ -251,15 +259,50 @@ class JuniperDataClient:
251
259
  JuniperDataValidationError: If parameters are invalid
252
260
  JuniperDataNotFoundError: If generator not found
253
261
  """
254
- payload = {
262
+ payload: Dict[str, Any] = {
255
263
  "generator": generator,
256
264
  "params": params,
257
265
  "persist": persist,
258
266
  }
267
+ if name is not None:
268
+ payload["name"] = name
269
+ if description is not None:
270
+ payload["description"] = description
271
+ if created_by is not None:
272
+ payload["created_by"] = created_by
273
+ if parent_dataset_id is not None:
274
+ payload["parent_dataset_id"] = parent_dataset_id
259
275
 
260
276
  response = self._request("POST", "/v1/datasets", json=payload)
261
277
  return response.json()
262
278
 
279
+ def list_versions(self, name: str) -> Dict[str, Any]:
280
+ """List all versions of a named dataset.
281
+
282
+ Args:
283
+ name: Dataset name to list versions for.
284
+
285
+ Returns:
286
+ Dict with dataset_name, versions list, total count, and latest_version.
287
+ """
288
+ response = self._request("GET", "/v1/datasets/versions", params={"name": name})
289
+ return response.json()
290
+
291
+ def get_latest(self, name: str) -> Dict[str, Any]:
292
+ """Get the latest version of a named dataset.
293
+
294
+ Args:
295
+ name: Dataset name to get latest version of.
296
+
297
+ Returns:
298
+ Dataset metadata for the latest version.
299
+
300
+ Raises:
301
+ JuniperDataNotFoundError: If no versions exist for the given name.
302
+ """
303
+ response = self._request("GET", "/v1/datasets/latest", params={"name": name})
304
+ return response.json()
305
+
263
306
  def list_datasets(self, limit: int = 100, offset: int = 0) -> List[str]:
264
307
  """List dataset IDs.
265
308
 
@@ -394,6 +437,74 @@ class JuniperDataClient:
394
437
 
395
438
  return self.create_dataset("spiral", params)
396
439
 
440
+ # ─── Batch Operations ────────────────────────────────────────────────
441
+
442
+ def batch_delete(self, dataset_ids: List[str]) -> Dict[str, Any]:
443
+ """Delete multiple datasets in a single request.
444
+
445
+ Args:
446
+ dataset_ids: List of dataset IDs to delete (1-100).
447
+
448
+ Returns:
449
+ Dictionary with deleted, not_found, and total_deleted.
450
+ """
451
+ response = self._request("POST", "/v1/datasets/batch-delete", json={"dataset_ids": dataset_ids})
452
+ return response.json()
453
+
454
+ def batch_create(self, datasets: List[Dict[str, Any]]) -> Dict[str, Any]:
455
+ """Create multiple datasets in a single request.
456
+
457
+ Each item should have 'generator' and 'params' keys, and optionally
458
+ 'persist', 'tags', and 'ttl_seconds'.
459
+
460
+ Args:
461
+ datasets: List of dataset specifications (1-50).
462
+
463
+ Returns:
464
+ Dictionary with results, total_created, and total_failed.
465
+ """
466
+ response = self._request("POST", "/v1/datasets/batch-create", json={"datasets": datasets})
467
+ return response.json()
468
+
469
+ def batch_update_tags(
470
+ self,
471
+ dataset_ids: List[str],
472
+ add_tags: Optional[List[str]] = None,
473
+ remove_tags: Optional[List[str]] = None,
474
+ ) -> Dict[str, Any]:
475
+ """Add or remove tags from multiple datasets.
476
+
477
+ Args:
478
+ dataset_ids: List of dataset IDs to update (1-100).
479
+ add_tags: Tags to add to all specified datasets.
480
+ remove_tags: Tags to remove from all specified datasets.
481
+
482
+ Returns:
483
+ Dictionary with updated, not_found, and total_updated.
484
+ """
485
+ payload: Dict[str, Any] = {"dataset_ids": dataset_ids}
486
+ if add_tags:
487
+ payload["add_tags"] = add_tags
488
+ if remove_tags:
489
+ payload["remove_tags"] = remove_tags
490
+ response = self._request("PATCH", "/v1/datasets/batch-tags", json=payload)
491
+ return response.json()
492
+
493
+ def batch_export(self, dataset_ids: List[str]) -> bytes:
494
+ """Export multiple datasets as a ZIP archive of NPZ files.
495
+
496
+ Args:
497
+ dataset_ids: List of dataset IDs to export (1-50).
498
+
499
+ Returns:
500
+ Raw bytes of the ZIP archive.
501
+
502
+ Raises:
503
+ JuniperDataNotFoundError: If none of the datasets exist.
504
+ """
505
+ response = self._request("POST", "/v1/datasets/batch-export", json={"dataset_ids": dataset_ids})
506
+ return response.content
507
+
397
508
  def close(self) -> None:
398
509
  """Close the client session and release resources."""
399
510
  self.session.close()
@@ -0,0 +1,45 @@
1
+ #####################################################################################################################################################################################################
2
+ # Project: Juniper
3
+ # Sub-Project: juniper-data-client
4
+ # Application: juniper_data_client
5
+ # File Name: __init__.py
6
+ # Author: Paul Calnon
7
+ # Version: 0.4.0
8
+ #
9
+ # Date Created: 2026-02-27
10
+ # Last Modified: 2026-02-27
11
+ #
12
+ # License: MIT License
13
+ # Copyright: Copyright (c) 2024-2026 Paul Calnon
14
+ #
15
+ # Description:
16
+ # Testing submodule for juniper-data-client. Provides FakeDataClient,
17
+ # a drop-in replacement for JuniperDataClient that generates synthetic
18
+ # datasets in-memory without requiring a running JuniperData service.
19
+ #####################################################################################################################################################################################################
20
+
21
+ """Testing utilities for juniper-data-client.
22
+
23
+ Provides :class:`FakeDataClient` for use in unit tests of consumers
24
+ like JuniperCascor and juniper-canopy, as well as standalone synthetic
25
+ dataset generators.
26
+
27
+ Usage::
28
+
29
+ from juniper_data_client.testing import FakeDataClient
30
+
31
+ with FakeDataClient() as client:
32
+ result = client.create_spiral_dataset(n_spirals=2, seed=42)
33
+ arrays = client.download_artifact_npz(result["dataset_id"])
34
+ """
35
+
36
+ from juniper_data_client.testing.fake_client import FakeDataClient
37
+ from juniper_data_client.testing.generators import generate_circle, generate_moon, generate_spiral, generate_xor
38
+
39
+ __all__ = [
40
+ "FakeDataClient",
41
+ "generate_circle",
42
+ "generate_moon",
43
+ "generate_spiral",
44
+ "generate_xor",
45
+ ]