syft-rds 0.1.1.dev2__tar.gz → 0.1.1.dev3__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.
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/PKG-INFO +4 -4
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/pyproject.toml +9 -9
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/__init__.py +1 -1
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/local_stores/dataset.py +29 -5
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/rds_clients/dataset.py +1 -1
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/models/models.py +20 -5
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/store/store.py +6 -5
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/.gitignore +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/LICENSE +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/README.md +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/adjectives.txt +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/css/fonts.css +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/css/model_repr.css +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/css/style.css +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/css/tabulator_pysyft.min.css +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/data_access_template.py.jinja +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/jinja/model_repr.jinja2 +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/jinja/model_repr_file_section.jinja2 +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/jinja/syft_exception.jinja2 +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/jinja/table.jinja2 +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/js/table.js +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/js/tabulator.min.js +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/nouns.txt +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/svg/arrow.svg +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/svg/clipboard.svg +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/svg/copy.svg +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/svg/folder.svg +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/svg/info.svg +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/svg/request.svg +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/svg/search.svg +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/svg/table.svg +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/cli.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/__init__.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/client_registry.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/connection.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/exceptions.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/local_store.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/local_stores/__init__.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/local_stores/base.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/local_stores/jobs.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/local_stores/runtime.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/local_stores/user_code.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/rds_client.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/rds_clients/__init__.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/rds_clients/base.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/rds_clients/jobs.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/rds_clients/runtime.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/rds_clients/user_code.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/rds_clients/utils.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/rpc.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/setup.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/client/utils.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/jupyter_utils/__init__.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/jupyter_utils/components.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/jupyter_utils/display.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/jupyter_utils/icons.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/jupyter_utils/table_utils.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/jupyter_utils/tabulator.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/jupyter_utils/types.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/models/__init__.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/models/base.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/models/formatter.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/models/html_format.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/orchestra.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/server/__init__.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/server/app.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/server/router.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/server/routers/job_router.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/server/routers/runtime_router.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/server/routers/user_code_router.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/server/user_file_service.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/store/__init__.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/syft_runtime/README.md +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/syft_runtime/__init__.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/syft_runtime/main.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/utils/name_generator.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/utils/paths.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/utils/render_code_template.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/utils/resources.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/utils/sanitize.py +0 -0
- {syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/utils/zip_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: syft-rds
|
|
3
|
-
Version: 0.1.1.
|
|
3
|
+
Version: 0.1.1.dev3
|
|
4
4
|
Summary: Doing remote data science with SyftBox
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.9
|
|
@@ -12,9 +12,9 @@ Requires-Dist: nh3>=0.2.21
|
|
|
12
12
|
Requires-Dist: pydantic[email]>=2.10.4
|
|
13
13
|
Requires-Dist: pyyaml>=6.0.2
|
|
14
14
|
Requires-Dist: rich>=13.9.4
|
|
15
|
-
Requires-Dist: syft-core==0.2.
|
|
16
|
-
Requires-Dist: syft-event==0.2.
|
|
17
|
-
Requires-Dist: syft-rpc==0.2.
|
|
15
|
+
Requires-Dist: syft-core==0.2.7
|
|
16
|
+
Requires-Dist: syft-event==0.2.3
|
|
17
|
+
Requires-Dist: syft-rpc==0.2.2
|
|
18
18
|
Requires-Dist: typer>=0.12.5
|
|
19
19
|
Requires-Dist: typing-extensions>=4.12.2
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "syft-rds"
|
|
3
|
-
version = "0.1.1-dev.
|
|
3
|
+
version = "0.1.1-dev.3"
|
|
4
4
|
description = "Doing remote data science with SyftBox"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.9"
|
|
@@ -8,9 +8,9 @@ dependencies = [
|
|
|
8
8
|
"loguru>=0.7.3",
|
|
9
9
|
"pydantic[email]>=2.10.4",
|
|
10
10
|
"rich>=13.9.4",
|
|
11
|
-
"syft-core==0.2.
|
|
12
|
-
"syft-event==0.2.
|
|
13
|
-
"syft-rpc==0.2.
|
|
11
|
+
"syft-core==0.2.7",
|
|
12
|
+
"syft-event==0.2.3",
|
|
13
|
+
"syft-rpc==0.2.2",
|
|
14
14
|
"typing-extensions>=4.12.2",
|
|
15
15
|
"pyyaml>=6.0.2",
|
|
16
16
|
"typer>=0.12.5",
|
|
@@ -35,15 +35,15 @@ dev-dependencies = [
|
|
|
35
35
|
requires = ["hatchling"]
|
|
36
36
|
build-backend = "hatchling.build"
|
|
37
37
|
|
|
38
|
-
[tool.hatch.build.targets.wheel]
|
|
39
|
-
packages = ["src/syft_rds"]
|
|
40
|
-
only-include = ["src", "pyproject.toml", "README.md"]
|
|
41
|
-
exclude = ["src/**/__pycache__"]
|
|
42
|
-
|
|
43
38
|
[tool.hatch.build.targets.sdist]
|
|
44
39
|
only-include = ["src", "pyproject.toml", "README.md"]
|
|
45
40
|
exclude = ["src/**/__pycache__"]
|
|
46
41
|
|
|
42
|
+
[tool.hatch.build.targets.wheel]
|
|
43
|
+
packages = ["src/syft_rds"]
|
|
44
|
+
only-include = ["src/syft_rds"] # no need for pyproject.toml and README.md for wheel
|
|
45
|
+
exclude = ["src/syft_rds/**/__pycache__"]
|
|
46
|
+
|
|
47
47
|
[project.scripts]
|
|
48
48
|
syft-rds = "syft_rds.cli:app"
|
|
49
49
|
|
|
@@ -265,9 +265,12 @@ class DatasetFilesManager:
|
|
|
265
265
|
return self.copy_directory(path, private_dataset_dir)
|
|
266
266
|
|
|
267
267
|
def copy_description_file_to_public_syftbox_dir(
|
|
268
|
-
self, dataset_name: str, description_path: Union[str, Path]
|
|
269
|
-
) -> Path:
|
|
268
|
+
self, dataset_name: str, description_path: Union[str, Path, None]
|
|
269
|
+
) -> Path | None:
|
|
270
270
|
"""Copy description file to the public SyftBox directory."""
|
|
271
|
+
if not description_path:
|
|
272
|
+
return
|
|
273
|
+
|
|
271
274
|
public_dataset_dir: Path = self._path_manager.get_local_public_dataset_dir(
|
|
272
275
|
dataset_name
|
|
273
276
|
)
|
|
@@ -498,9 +501,30 @@ class DatasetLocalStore(CRUDLocalStore[Dataset, DatasetCreate, DatasetUpdate]):
|
|
|
498
501
|
"""
|
|
499
502
|
return super().get_all(request)
|
|
500
503
|
|
|
501
|
-
def update(self,
|
|
502
|
-
"""
|
|
503
|
-
|
|
504
|
+
def update(self, update_item: DatasetUpdate) -> Dataset:
|
|
505
|
+
"""
|
|
506
|
+
Update an existing dataset.
|
|
507
|
+
|
|
508
|
+
Args:
|
|
509
|
+
update_dataset: The dataset update data
|
|
510
|
+
|
|
511
|
+
Returns:
|
|
512
|
+
The updated dataset
|
|
513
|
+
|
|
514
|
+
Raises:
|
|
515
|
+
RuntimeError: If update fails
|
|
516
|
+
"""
|
|
517
|
+
try:
|
|
518
|
+
existing_dataset = self.store.get_by_uid(update_item.uid)
|
|
519
|
+
if existing_dataset is None:
|
|
520
|
+
raise ValueError(f"Dataset with uid {update_item.uid} not found")
|
|
521
|
+
|
|
522
|
+
updated_dataset = existing_dataset.apply_update(update_item)
|
|
523
|
+
return self.store.update(updated_dataset.uid, updated_dataset)
|
|
524
|
+
except Exception as e:
|
|
525
|
+
raise RuntimeError(
|
|
526
|
+
f"Failed to update dataset with uid {update_item.uid}: {str(e)}"
|
|
527
|
+
)
|
|
504
528
|
|
|
505
529
|
def get(self, request: GetOneRequest) -> Dataset:
|
|
506
530
|
"""
|
|
@@ -331,7 +331,7 @@ class Dataset(ItemBase):
|
|
|
331
331
|
return self.get_private_path()
|
|
332
332
|
|
|
333
333
|
@property
|
|
334
|
-
def readme_path(self) -> Path:
|
|
334
|
+
def readme_path(self) -> Path | None:
|
|
335
335
|
return self.get_readme_path()
|
|
336
336
|
|
|
337
337
|
def get_mock_path(self) -> Path:
|
|
@@ -357,20 +357,24 @@ class Dataset(ItemBase):
|
|
|
357
357
|
)
|
|
358
358
|
return private_path
|
|
359
359
|
|
|
360
|
-
def get_readme_path(self) -> Path:
|
|
360
|
+
def get_readme_path(self) -> Path | None:
|
|
361
361
|
"""
|
|
362
362
|
Will always raise FileNotFoundError for non-admin since the
|
|
363
363
|
private path will never by synced
|
|
364
364
|
"""
|
|
365
|
+
if not self.readme:
|
|
366
|
+
return None
|
|
365
367
|
readme_path: Path = self.readme.to_local_path(
|
|
366
368
|
datasites_path=self._syftbox_client.datasites
|
|
367
369
|
)
|
|
368
370
|
if not readme_path.exists():
|
|
369
|
-
|
|
371
|
+
return None
|
|
370
372
|
return readme_path
|
|
371
373
|
|
|
372
374
|
def get_description(self) -> str:
|
|
373
375
|
# read the description .md file
|
|
376
|
+
if not self.readme:
|
|
377
|
+
return ""
|
|
374
378
|
with open(self.get_readme_path()) as f:
|
|
375
379
|
return f.read()
|
|
376
380
|
|
|
@@ -390,10 +394,17 @@ class Dataset(ItemBase):
|
|
|
390
394
|
except FileNotFoundError:
|
|
391
395
|
pass
|
|
392
396
|
|
|
397
|
+
# Only include display paths that are not None
|
|
398
|
+
display_paths = []
|
|
399
|
+
if self.mock_path is not None:
|
|
400
|
+
display_paths.append("mock_path")
|
|
401
|
+
if self.readme_path is not None:
|
|
402
|
+
display_paths.append("readme_path")
|
|
403
|
+
|
|
393
404
|
description = create_html_repr(
|
|
394
405
|
obj=self,
|
|
395
406
|
fields=field_to_include,
|
|
396
|
-
display_paths=
|
|
407
|
+
display_paths=display_paths,
|
|
397
408
|
)
|
|
398
409
|
|
|
399
410
|
display(HTML(description))
|
|
@@ -425,7 +436,11 @@ class DatasetCreate(ItemBaseCreate[Dataset]):
|
|
|
425
436
|
|
|
426
437
|
|
|
427
438
|
class DatasetUpdate(ItemBaseUpdate[Dataset]):
|
|
428
|
-
|
|
439
|
+
summary: Optional[str] = None
|
|
440
|
+
auto_approval: Optional[list[str]] = Field(
|
|
441
|
+
default_factory=list,
|
|
442
|
+
description="List of datasites whose jobs will be automatically approved.",
|
|
443
|
+
)
|
|
429
444
|
|
|
430
445
|
|
|
431
446
|
class GetOneRequest(BaseModel):
|
|
@@ -9,10 +9,11 @@ from pydantic import TypeAdapter
|
|
|
9
9
|
from syft_rds.models.base import ItemBase
|
|
10
10
|
|
|
11
11
|
PERMS = """
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
rules:
|
|
13
|
+
- pattern: '**'
|
|
14
|
+
access:
|
|
15
|
+
read:
|
|
16
|
+
- '*'
|
|
16
17
|
"""
|
|
17
18
|
|
|
18
19
|
T = TypeVar("T", bound=ItemBase)
|
|
@@ -23,7 +24,7 @@ def ensure_store_exists(func):
|
|
|
23
24
|
def wrapper(self: "YAMLStore", *args, **kwargs):
|
|
24
25
|
if not self.item_type_dir.exists():
|
|
25
26
|
self.item_type_dir.mkdir(parents=True, exist_ok=True)
|
|
26
|
-
perms_file = self.item_type_dir.parent / "
|
|
27
|
+
perms_file = self.item_type_dir.parent / "syft.pub.yaml"
|
|
27
28
|
perms_file.write_text(PERMS) # TODO create more restrictive permissions
|
|
28
29
|
return func(self, *args, **kwargs)
|
|
29
30
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/css/tabulator_pysyft.min.css
RENAMED
|
File without changes
|
{syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/data_access_template.py.jinja
RENAMED
|
File without changes
|
|
File without changes
|
{syft_rds-0.1.1.dev2 → syft_rds-0.1.1.dev3}/src/syft_rds/assets/jinja/model_repr_file_section.jinja2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|