kosh 3.0__tar.gz → 3.2.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.
Potentially problematic release.
This version of kosh might be problematic. Click here for more details.
- kosh-3.2.0/PKG-INFO +70 -0
- {kosh-3.0 → kosh-3.2.0}/README.md +1 -4
- {kosh-3.0 → kosh-3.2.0}/kosh/__init__.py +4 -4
- {kosh-3.0 → kosh-3.2.0}/kosh/core_sina.py +171 -107
- kosh-3.2.0/kosh/current_version.py +1 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/dataset.py +386 -126
- kosh-3.2.0/kosh/ensemble.py +424 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/exec_graphs/core.py +9 -5
- {kosh-3.0/scripts → kosh-3.2.0/kosh}/kosh_command.py +112 -58
- {kosh-3.0 → kosh-3.2.0}/kosh/loaders/UltraLoader.py +30 -16
- {kosh-3.0 → kosh-3.2.0}/kosh/loaders/__init__.py +2 -1
- {kosh-3.0 → kosh-3.2.0}/kosh/loaders/core.py +28 -14
- {kosh-3.0 → kosh-3.2.0}/kosh/loaders/npy.py +2 -1
- kosh-3.2.0/kosh/loaders/pandas_loader.py +118 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/loaders/pgm.py +1 -1
- {kosh-3.0 → kosh-3.2.0}/kosh/loaders/pil.py +1 -1
- {kosh-3.0 → kosh-3.2.0}/kosh/loaders/sidre.py +1 -1
- kosh-3.2.0/kosh/lock_strategies.py +266 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/operators/__init__.py +1 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/operators/core.py +4 -4
- kosh-3.2.0/kosh/operators/koshClustering.py +756 -0
- kosh-3.2.0/kosh/operators/koshMaths.py +116 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/sampling_methods/cluster_sampling/Clustering.py +731 -217
- kosh-3.2.0/kosh/sampling_methods/cluster_sampling/__init__.py +4 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/schema.py +16 -4
- {kosh-3.0 → kosh-3.2.0}/kosh/store.py +889 -299
- kosh-3.2.0/kosh/threadsafe.py +258 -0
- kosh-3.2.0/kosh/threadsafe_decorators.py +96 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/transformers/core.py +3 -3
- {kosh-3.0 → kosh-3.2.0}/kosh/transformers/npy.py +5 -1
- {kosh-3.0 → kosh-3.2.0}/kosh/transformers/sidre.py +1 -1
- {kosh-3.0 → kosh-3.2.0}/kosh/transformers/skl.py +1 -1
- {kosh-3.0 → kosh-3.2.0}/kosh/transformers/utils.py +1 -1
- {kosh-3.0 → kosh-3.2.0}/kosh/utils.py +173 -32
- kosh-3.2.0/kosh.egg-info/PKG-INFO +70 -0
- kosh-3.2.0/kosh.egg-info/SOURCES.txt +50 -0
- {kosh-3.0 → kosh-3.2.0}/kosh.egg-info/requires.txt +2 -1
- {kosh-3.0 → kosh-3.2.0}/scripts/kosh +112 -58
- {kosh-3.0 → kosh-3.2.0}/scripts/sbang +1 -1
- {kosh-3.0 → kosh-3.2.0}/setup.py +22 -17
- kosh-3.0/PKG-INFO +0 -75
- kosh-3.0/kosh/current_version.py +0 -1
- kosh-3.0/kosh/ensemble.py +0 -264
- kosh-3.0/kosh/operators/koshClustering.py +0 -458
- kosh-3.0/kosh/sampling_methods/cluster_sampling/__init__.py +0 -1
- kosh-3.0/kosh.egg-info/PKG-INFO +0 -75
- kosh-3.0/kosh.egg-info/SOURCES.txt +0 -80
- kosh-3.0/tests/test_kosh_broken_loader.py +0 -73
- kosh-3.0/tests/test_kosh_clustering.py +0 -358
- kosh-3.0/tests/test_kosh_clustering_raw.py +0 -287
- kosh-3.0/tests/test_kosh_command_line.py +0 -282
- kosh-3.0/tests/test_kosh_context_manager.py +0 -53
- kosh-3.0/tests/test_kosh_curves.py +0 -194
- kosh-3.0/tests/test_kosh_datasets.py +0 -556
- kosh-3.0/tests/test_kosh_describe_feature.py +0 -117
- kosh-3.0/tests/test_kosh_ensembles.py +0 -274
- kosh-3.0/tests/test_kosh_fix_fastsha.py +0 -43
- kosh-3.0/tests/test_kosh_flake8.py +0 -43
- kosh-3.0/tests/test_kosh_getitem_back_propagate.py +0 -272
- kosh-3.0/tests/test_kosh_import_export_datasets.py +0 -308
- kosh-3.0/tests/test_kosh_load_sina.py +0 -31
- kosh-3.0/tests/test_kosh_loaders.py +0 -520
- kosh-3.0/tests/test_kosh_loaders_multiple_features_name.py +0 -61
- kosh-3.0/tests/test_kosh_mv.py +0 -401
- kosh-3.0/tests/test_kosh_operators.py +0 -171
- kosh-3.0/tests/test_kosh_operators_decorators.py +0 -110
- kosh-3.0/tests/test_kosh_reassociate.py +0 -235
- kosh-3.0/tests/test_kosh_rm.py +0 -137
- kosh-3.0/tests/test_kosh_schema.py +0 -137
- kosh-3.0/tests/test_kosh_script_wrapper.py +0 -344
- kosh-3.0/tests/test_kosh_search.py +0 -62
- kosh-3.0/tests/test_kosh_search_speed.py +0 -46
- kosh-3.0/tests/test_kosh_sina_curves_as_associated.py +0 -104
- kosh-3.0/tests/test_kosh_sina_files_section_as_associated.py +0 -187
- kosh-3.0/tests/test_kosh_store.py +0 -373
- kosh-3.0/tests/test_kosh_store_custom_loaders.py +0 -70
- kosh-3.0/tests/test_kosh_sync.py +0 -220
- kosh-3.0/tests/test_kosh_transformers.py +0 -241
- kosh-3.0/tests/test_kosh_transformers_decorators.py +0 -93
- kosh-3.0/tests/test_kosh_transformers_parent.py +0 -64
- kosh-3.0/tests/test_kosh_users_and_groups.py +0 -49
- kosh-3.0/tests/test_kosh_verbose_list_features.py +0 -108
- {kosh-3.0 → kosh-3.2.0}/LICENSE +0 -0
- {kosh-3.0 → kosh-3.2.0}/NOTICE +0 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/exec_graphs/__init__.py +0 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/loaders/hdf5.py +0 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/loaders/jsons.py +0 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/sampling_methods/__init__.py +0 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/transformers/__init__.py +0 -0
- {kosh-3.0 → kosh-3.2.0}/kosh/wrapper.py +0 -0
- {kosh-3.0 → kosh-3.2.0}/kosh.egg-info/dependency_links.txt +0 -0
- {kosh-3.0 → kosh-3.2.0}/kosh.egg-info/not-zip-safe +0 -0
- {kosh-3.0 → kosh-3.2.0}/kosh.egg-info/top_level.txt +0 -0
- {kosh-3.0 → kosh-3.2.0}/setup.cfg +0 -0
kosh-3.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: kosh
|
|
3
|
+
Version: 3.2.0
|
|
4
|
+
Summary: Manages Data Store with External Bulk Data
|
|
5
|
+
Home-page: https://github.com/LLNL/Kosh
|
|
6
|
+
Author: Charles Doutriaux, Renee Olson
|
|
7
|
+
Author-email: doutriaux1@llnl.gov
|
|
8
|
+
License: MIT
|
|
9
|
+
Description: 
|
|
10
|
+
# Overview
|
|
11
|
+
|
|
12
|
+
Kosh allows codes to store, query, share data via an easy-to-use Python API. Kosh lies on top of Sina and as a result can use any database backend supported by Sina.
|
|
13
|
+
|
|
14
|
+
In addition Kosh aims to make data access and sharing as simple as possible.
|
|
15
|
+
|
|
16
|
+
Via "loaders", "transformers" and "operators" Kosh can access and process data in a consistent fashion, decoupled from data format and location.
|
|
17
|
+
|
|
18
|
+
Kosh is a Hindi word that means *treasury*, which is derived from *Kosha*, a Sanskrit word that means container in either a direct or metaphorical sense. A fairly good translation would be *repository*.
|
|
19
|
+
|
|
20
|
+
# Getting Started
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
To get the latest public version:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install kosh
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
To get the latest stable, from a cloned repo simply run:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install .
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Alternatively add the path to this repo to your `PYTHONPATH` environment variable, or in your code with:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
import sys
|
|
39
|
+
sys.path.append(path_to_kosh_repo)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
For more details look into the [installation doc](INSTALL.md)
|
|
43
|
+
|
|
44
|
+
# First steps
|
|
45
|
+
|
|
46
|
+
See [this file](docs/source/users/index.md)
|
|
47
|
+
|
|
48
|
+
# Getting Involved
|
|
49
|
+
Kosh is user-oriented, and users' questions, comments, and contributions help guide its evolution. We welcome involvement and feedbacks.
|
|
50
|
+
|
|
51
|
+
# Contact Info
|
|
52
|
+
You can reach our team at kosh-support@llnl.gov.
|
|
53
|
+
Kosh main developer can be reached at: doutriaux1@llnl.gov
|
|
54
|
+
|
|
55
|
+
# Contributing
|
|
56
|
+
Contributions should be submitted as a pull request pointing to the develop branch, and must pass Kosh's CI process; to run the same checks locally follow the instructions in the tests directory.
|
|
57
|
+
|
|
58
|
+
Contributions must be made under the same license as Kosh (see the bottom of this file).
|
|
59
|
+
|
|
60
|
+
# Release and License
|
|
61
|
+
Kosh is distributed under the terms of the MIT license; new contributions must be made under this license.
|
|
62
|
+
|
|
63
|
+
SPDX-License-Identifier: MIT
|
|
64
|
+
|
|
65
|
+
``LLNL-CODE-814755``
|
|
66
|
+
Platform: UNKNOWN
|
|
67
|
+
Classifier: Programming Language :: Python
|
|
68
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
69
|
+
Classifier: Operating System :: OS Independent
|
|
70
|
+
Description-Content-Type: text/markdown
|
|
@@ -45,10 +45,7 @@ You can reach our team at kosh-support@llnl.gov.
|
|
|
45
45
|
Kosh main developer can be reached at: doutriaux1@llnl.gov
|
|
46
46
|
|
|
47
47
|
# Contributing
|
|
48
|
-
Contributions should be submitted as a pull request pointing to the develop branch, and must pass Kosh's CI process; to run the same checks locally
|
|
49
|
-
```
|
|
50
|
-
pytest tests/test_kosh*py
|
|
51
|
-
```
|
|
48
|
+
Contributions should be submitted as a pull request pointing to the develop branch, and must pass Kosh's CI process; to run the same checks locally follow the instructions in the tests directory.
|
|
52
49
|
|
|
53
50
|
Contributions must be made under the same license as Kosh (see the bottom of this file).
|
|
54
51
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import importlib.metadata
|
|
1
2
|
from .current_version import current_version
|
|
2
3
|
import os
|
|
3
4
|
# import .sampling_methods # noqa
|
|
@@ -6,18 +7,17 @@ from .loaders import KoshLoader, KoshSinaLoader # noqa
|
|
|
6
7
|
from .utils import create_new_db, walk_dictionary_keys, version # noqa
|
|
7
8
|
from .schema import KoshSchema # noqa
|
|
8
9
|
from .operators import KoshOperator # noqa
|
|
9
|
-
import pkg_resources # noqa
|
|
10
10
|
from .store import KoshStore, connect # noqa
|
|
11
11
|
from .dataset import KoshDataset # noqa
|
|
12
12
|
from .transformers import typed_transformer, numpy_transformer, typed_transformer_with_format # noqa
|
|
13
13
|
from .transformers import KoshTransformer # noqa
|
|
14
14
|
from .operators import KoshOperator # noqa
|
|
15
15
|
from .operators import typed_operator_with_kwargs, typed_operator, numpy_operator # noqa
|
|
16
|
+
from .kosh_command import KoshCmd # noqa
|
|
16
17
|
|
|
17
18
|
try:
|
|
18
|
-
|
|
19
|
-
__version__ =
|
|
20
|
-
metadata = list(d._get_metadata(d.PKG_INFO))
|
|
19
|
+
metadata = importlib.metadata.metadata("kosh")
|
|
20
|
+
__version__ = metadata["Version"]
|
|
21
21
|
__sha__ = None
|
|
22
22
|
for meta in metadata:
|
|
23
23
|
if "Summary:" in meta:
|
|
@@ -1,25 +1,32 @@
|
|
|
1
1
|
import uuid
|
|
2
2
|
import warnings
|
|
3
3
|
import time
|
|
4
|
+
import reprlib
|
|
4
5
|
from .schema import KoshSchema
|
|
5
|
-
from
|
|
6
|
-
from
|
|
7
|
-
import pickle
|
|
6
|
+
from .utils import KoshPickler
|
|
7
|
+
from . import lock_strategies
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
kosh_pickler = KoshPickler()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def __getattr__(name):
|
|
14
|
+
if name == "sina_version":
|
|
15
|
+
from sina import get_version
|
|
16
|
+
return float(".".join(get_version().split(".")[:2]))
|
|
11
17
|
|
|
12
18
|
|
|
13
19
|
class KoshSinaObject(object):
|
|
14
20
|
"""KoshSinaObject Base class for sina objects
|
|
15
21
|
"""
|
|
16
|
-
|
|
22
|
+
@lock_strategies.lock_method
|
|
17
23
|
def get_record(self):
|
|
18
24
|
return self.__store__.get_record(self.id)
|
|
19
25
|
|
|
20
26
|
def __init__(self, Id, store, kosh_type,
|
|
21
27
|
record_handler, protected=[], metadata={}, schema=None,
|
|
22
28
|
record=None):
|
|
29
|
+
from sina.model import Record
|
|
23
30
|
"""__init__ sina object base class
|
|
24
31
|
|
|
25
32
|
:param Id: id to use for unique identification, if None is passed set for you via uui4()
|
|
@@ -37,56 +44,59 @@ class KoshSinaObject(object):
|
|
|
37
44
|
:param record: sina record to prevent looking it up again and again in sina
|
|
38
45
|
:type record: Record
|
|
39
46
|
"""
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
store.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if record is None:
|
|
61
|
-
try:
|
|
62
|
-
record = self.get_record()
|
|
63
|
-
except BaseException: # record exists nowhere
|
|
64
|
-
record = Record(id=Id, type=kosh_type)
|
|
65
|
-
if store.__sync__:
|
|
66
|
-
store.lock()
|
|
67
|
-
store.__record_handler__.insert(record)
|
|
68
|
-
store.unlock()
|
|
69
|
-
else:
|
|
70
|
-
self.__store__.__sync__dict__[Id] = record
|
|
71
|
-
record["user_defined"]["last_update_from_db"] = time.time()
|
|
47
|
+
with store.lock_strategy:
|
|
48
|
+
self.__dict__["__store__"] = store
|
|
49
|
+
self.__dict__["__schema__"] = schema
|
|
50
|
+
self.__dict__["__record_handler__"] = record_handler
|
|
51
|
+
self.__dict__["__protected__"] = [
|
|
52
|
+
"id", "__type__", "__protected__",
|
|
53
|
+
"__record_handler__", "__store__", "id", "__schema__"] + protected
|
|
54
|
+
self.__dict__["__type__"] = kosh_type
|
|
55
|
+
self.__dict__["lock_strategy"] = store.lock_strategy
|
|
56
|
+
if Id is None:
|
|
57
|
+
Id = uuid.uuid4().hex
|
|
58
|
+
record = Record(id=Id, type=kosh_type, user_defined={'kosh_information': {}})
|
|
59
|
+
if store.__sync__:
|
|
60
|
+
store.lock()
|
|
61
|
+
store.__record_handler__.insert(record)
|
|
62
|
+
store.unlock()
|
|
63
|
+
else:
|
|
64
|
+
record["user_defined"]['kosh_information']["last_update_from_db"] = time.time()
|
|
65
|
+
self.__store__.__sync__dict__[Id] = record
|
|
66
|
+
self.__dict__["id"] = Id
|
|
72
67
|
else:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
68
|
+
self.__dict__["id"] = Id
|
|
69
|
+
if record is None:
|
|
70
|
+
try:
|
|
71
|
+
record = self.get_record()
|
|
72
|
+
except BaseException: # record exists nowhere
|
|
73
|
+
record = Record(id=Id, type=kosh_type, user_defined={'kosh_information': {}})
|
|
74
|
+
if store.__sync__:
|
|
75
|
+
store.lock()
|
|
76
|
+
store.__record_handler__.insert(record)
|
|
77
|
+
store.unlock()
|
|
78
|
+
else:
|
|
79
|
+
self.__store__.__sync__dict__[Id] = record
|
|
80
|
+
record["user_defined"]['kosh_information']["last_update_from_db"] = time.time()
|
|
81
|
+
else:
|
|
82
|
+
deleted_items = False
|
|
83
|
+
for att in self.__dict__["__protected__"]:
|
|
84
|
+
if att in record["data"]:
|
|
85
|
+
del record["data"][att]
|
|
86
|
+
deleted_items = True
|
|
87
|
+
if deleted_items:
|
|
88
|
+
if store.__sync__:
|
|
89
|
+
self._update_record(record)
|
|
90
|
+
else:
|
|
91
|
+
self.__store__.__sync__dict__[Id] = record
|
|
92
|
+
|
|
93
|
+
metadata_copy = metadata.copy()
|
|
94
|
+
for key in metadata:
|
|
95
|
+
if key in self.__dict__["__protected__"]:
|
|
96
|
+
del metadata_copy[key]
|
|
97
|
+
self.update(metadata_copy)
|
|
98
|
+
|
|
99
|
+
@lock_strategies.lock_method
|
|
90
100
|
def __getattr__(self, name):
|
|
91
101
|
"""__getattr__ get an attribute
|
|
92
102
|
|
|
@@ -105,6 +115,12 @@ class KoshSinaObject(object):
|
|
|
105
115
|
rels = self.get_sina_store().relationships.find(
|
|
106
116
|
None, "is a member of ensemble", self.id)
|
|
107
117
|
return [str(x.subject_id) for x in rels]
|
|
118
|
+
if name == "__features__":
|
|
119
|
+
record = self.get_record()
|
|
120
|
+
try:
|
|
121
|
+
return KoshPickler().loads(record["user_defined"]["__features__"])
|
|
122
|
+
except Exception:
|
|
123
|
+
return {None: {}}
|
|
108
124
|
if name == "_associated_data_":
|
|
109
125
|
from kosh.dataset import KoshDataset
|
|
110
126
|
record = self.get_record()
|
|
@@ -138,13 +154,19 @@ class KoshSinaObject(object):
|
|
|
138
154
|
elif name == "schema":
|
|
139
155
|
if self.__dict__[
|
|
140
156
|
"__schema__"] is None and "schema" in record["data"]:
|
|
141
|
-
schema =
|
|
142
|
-
record["data"]["schema"]["value"].encode("latin1"))
|
|
157
|
+
schema = kosh_pickler.loads(record["data"]["schema"]["value"])
|
|
143
158
|
self.__dict__["__schema__"] = schema
|
|
144
159
|
return self.__dict__["__schema__"]
|
|
160
|
+
elif name in ['alias_feature', 'loader_kwargs']:
|
|
161
|
+
if name in record["data"]:
|
|
162
|
+
return kosh_pickler.loads(record["data"][name]["value"])
|
|
163
|
+
else:
|
|
164
|
+
return {}
|
|
145
165
|
if name not in record["data"]:
|
|
146
166
|
if name == "mime_type":
|
|
147
167
|
return record["type"]
|
|
168
|
+
elif name == "uri":
|
|
169
|
+
return ""
|
|
148
170
|
else:
|
|
149
171
|
raise AttributeError(
|
|
150
172
|
"Object {} does not have {} attribute".format(self.id,
|
|
@@ -154,18 +176,20 @@ class KoshSinaObject(object):
|
|
|
154
176
|
# old records have user id let's fix this
|
|
155
177
|
if value in self.__store__.__record_handler__.find_with_type(
|
|
156
178
|
self.__store__._users_type, ids_only=True):
|
|
157
|
-
value = self.__store__.get_record(
|
|
158
|
-
value)["data"]["username"]["value"]
|
|
179
|
+
value = self.__store__.get_record(value)["data"]["username"]["value"]
|
|
159
180
|
return value
|
|
160
181
|
|
|
182
|
+
@lock_strategies.lock_method
|
|
161
183
|
def get_sina_store(self):
|
|
162
184
|
"""Returns the sina store object"""
|
|
163
185
|
return self.__store__.get_sina_store()
|
|
164
186
|
|
|
187
|
+
@lock_strategies.lock_method
|
|
165
188
|
def get_sina_records(self):
|
|
166
189
|
"""Returns sina store's records"""
|
|
167
190
|
return self.__record_handler__
|
|
168
191
|
|
|
192
|
+
@lock_strategies.lock_method
|
|
169
193
|
def update(self, attributes):
|
|
170
194
|
"""update many attributes at once to limit db writes
|
|
171
195
|
:param: attributes: dictionary with attributes to update
|
|
@@ -184,6 +208,7 @@ class KoshSinaObject(object):
|
|
|
184
208
|
update_db = False
|
|
185
209
|
rec = self.___setattr___(name, value, rec, update_db=update_db)
|
|
186
210
|
|
|
211
|
+
@lock_strategies.lock_method
|
|
187
212
|
def __setattr__(self, name, value):
|
|
188
213
|
"""set an attribute
|
|
189
214
|
We are calling the ___setattr___
|
|
@@ -191,6 +216,7 @@ class KoshSinaObject(object):
|
|
|
191
216
|
"""
|
|
192
217
|
self.___setattr___(name, value)
|
|
193
218
|
|
|
219
|
+
@lock_strategies.lock_method
|
|
194
220
|
def ___setattr___(self, name, value, record=None,
|
|
195
221
|
update_db=True, force=False):
|
|
196
222
|
"""__setattr__ set an attribute on an object
|
|
@@ -213,6 +239,14 @@ class KoshSinaObject(object):
|
|
|
213
239
|
if name == "schema":
|
|
214
240
|
assert isinstance(value, KoshSchema)
|
|
215
241
|
value.validate(self)
|
|
242
|
+
elif name == "__features__":
|
|
243
|
+
value = kosh_pickler.dumps(value)
|
|
244
|
+
record["user_defined"]["__features__"] = value
|
|
245
|
+
elif name in ['alias_feature', 'loader_kwargs']:
|
|
246
|
+
if isinstance(value, dict):
|
|
247
|
+
value = kosh_pickler.dumps(value)
|
|
248
|
+
else: # Pre-pickled at dataset level
|
|
249
|
+
value = value
|
|
216
250
|
elif self.schema is not None:
|
|
217
251
|
self.schema.validate_attribute(name, value)
|
|
218
252
|
|
|
@@ -225,36 +259,45 @@ class KoshSinaObject(object):
|
|
|
225
259
|
self.id, self.__store__._ensemble_predicate, None)
|
|
226
260
|
for relationship in relationships:
|
|
227
261
|
ensemble = self.__store__.open(relationship.object_id)
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
262
|
+
ens_tags = self.list_ensemble_tags(ensemble.id, dictionary=True, obscure=False)
|
|
263
|
+
inherit_attributes = ens_tags.get(f"{ensemble.id}_ENSEMBLE_TAG_INHERIT_ATTRIBUTES", True)
|
|
264
|
+
if inherit_attributes:
|
|
265
|
+
if name in ensemble.list_attributes() and name not in ensemble.__dict__["__ok_duplicates__"]:
|
|
266
|
+
if value != getattr(ensemble, name):
|
|
267
|
+
raise KeyError(
|
|
268
|
+
"The attribute {} is controlled by ensemble: {} and cannot be set here".format(
|
|
269
|
+
name, relationship.object_id))
|
|
270
|
+
else:
|
|
271
|
+
warnings.warn(
|
|
272
|
+
"The attribute {} is controlled by ensemble: {}"
|
|
273
|
+
". You should NOT set this attribute at the dataset level"
|
|
274
|
+
". Values match so we will accept it here".format(
|
|
275
|
+
name, relationship.object_id), UserWarning)
|
|
232
276
|
|
|
233
277
|
# For Ensembles we need to set it on all members
|
|
234
278
|
from kosh.ensemble import KoshEnsemble
|
|
235
279
|
if isinstance(self, KoshEnsemble):
|
|
236
280
|
# First we make a pass to collect all other ensembles datasets are
|
|
237
281
|
# part of
|
|
238
|
-
other_ensembles = set()
|
|
239
282
|
for dataset in self.get_members():
|
|
240
|
-
for
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
283
|
+
for ensemble in dataset.get_ensembles():
|
|
284
|
+
ens_tags = dataset.list_ensemble_tags(ensemble.id, dictionary=True, obscure=False)
|
|
285
|
+
inherit_attributes = ens_tags.get(f"{ensemble.id}_ENSEMBLE_TAG_INHERIT_ATTRIBUTES", True)
|
|
286
|
+
if inherit_attributes:
|
|
287
|
+
if ensemble.id != self.id:
|
|
288
|
+
for att in ensemble.list_attributes():
|
|
289
|
+
if att in self.__dict__["__ok_duplicates__"]:
|
|
290
|
+
continue
|
|
291
|
+
if att == name:
|
|
292
|
+
raise NameError("A member of this ensemble belongs to ensemble {} "
|
|
293
|
+
"which already controls attribute {}".format(ensemble.id, att))
|
|
294
|
+
|
|
295
|
+
dataset.___setattr___(
|
|
296
|
+
name=name,
|
|
297
|
+
value=value,
|
|
298
|
+
record=None,
|
|
299
|
+
update_db=update_db,
|
|
300
|
+
force=True)
|
|
258
301
|
|
|
259
302
|
# Did it change on db since we last read it?
|
|
260
303
|
last_modif_att = "{name}_last_modified".format(name=name)
|
|
@@ -265,7 +308,7 @@ class KoshSinaObject(object):
|
|
|
265
308
|
last = time.time()
|
|
266
309
|
try:
|
|
267
310
|
# Time we last read its value
|
|
268
|
-
last_db = record["user_defined"][last_modif_att]
|
|
311
|
+
last_db = record["user_defined"]['kosh_information'][last_modif_att]
|
|
269
312
|
except KeyError:
|
|
270
313
|
last_db = last
|
|
271
314
|
# last time attribute was modified in db
|
|
@@ -281,15 +324,16 @@ class KoshSinaObject(object):
|
|
|
281
324
|
if "{name}_last_modified".format(name=name) not in self.__protected__:
|
|
282
325
|
self.__dict__["__protected__"] += [last_modif_att, ]
|
|
283
326
|
self.__dict__[last_modif_att] = now
|
|
284
|
-
record["user_defined"][last_modif_att] = now
|
|
327
|
+
record["user_defined"]['kosh_information'][last_modif_att] = now
|
|
285
328
|
if name == "schema":
|
|
286
329
|
self.__dict__["__schema__"] = value
|
|
287
|
-
value =
|
|
330
|
+
value = kosh_pickler.dumps(value)
|
|
288
331
|
record["data"][name] = {"value": value}
|
|
289
332
|
if update_db and self.__store__.__sync__:
|
|
290
333
|
self._update_record(record)
|
|
291
334
|
return record
|
|
292
335
|
|
|
336
|
+
@lock_strategies.lock_method
|
|
293
337
|
def _update_record(self, record, store=None):
|
|
294
338
|
"""Updates a record in the sina store
|
|
295
339
|
:param record: The record to update
|
|
@@ -302,23 +346,14 @@ class KoshSinaObject(object):
|
|
|
302
346
|
id_ = record.id
|
|
303
347
|
rels = store.relationships.find(id_, None, None)
|
|
304
348
|
rels += store.relationships.find(None, None, id_)
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
old_record = store.records.get(id_)
|
|
308
|
-
except Exception:
|
|
309
|
-
old_record = None # new record
|
|
310
|
-
store.records.delete(id_)
|
|
311
|
-
try:
|
|
349
|
+
if not self.__store__.__sync__:
|
|
350
|
+
store.records.delete(id_)
|
|
312
351
|
store.records.insert(record)
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if old_record is not None:
|
|
316
|
-
store.records.insert(old_record)
|
|
317
|
-
raise err
|
|
318
|
-
|
|
319
|
-
store.relationships.insert(rels)
|
|
352
|
+
store.relationships.insert(rels)
|
|
353
|
+
store.records.update(record)
|
|
320
354
|
self.__store__.unlock()
|
|
321
355
|
|
|
356
|
+
@lock_strategies.lock_method
|
|
322
357
|
def __delattr__(self, name):
|
|
323
358
|
"""__delattr__ deletes an attribute
|
|
324
359
|
|
|
@@ -330,7 +365,7 @@ class KoshSinaObject(object):
|
|
|
330
365
|
record = self.get_record()
|
|
331
366
|
last_modif_att = "{name}_last_modified".format(name=name)
|
|
332
367
|
now = time.time()
|
|
333
|
-
record["user_defined"][last_modif_att] = now
|
|
368
|
+
record["user_defined"]['kosh_information'][last_modif_att] = now
|
|
334
369
|
# We need to remember we touched it otherwise
|
|
335
370
|
# if we create it again the db will look
|
|
336
371
|
# out of sync.
|
|
@@ -339,36 +374,53 @@ class KoshSinaObject(object):
|
|
|
339
374
|
if self.__store__.__sync__:
|
|
340
375
|
self._update_record(record)
|
|
341
376
|
|
|
377
|
+
@lock_strategies.lock_method
|
|
342
378
|
def sync(self):
|
|
343
379
|
"""sync this object with database"""
|
|
344
380
|
self.__store__.sync([self.id, ])
|
|
345
381
|
|
|
346
|
-
|
|
382
|
+
@lock_strategies.lock_method
|
|
383
|
+
def list_attributes(self, dictionary=False, ensemble_id=None):
|
|
347
384
|
__doc__ = self.listattributes.__doc__.replace("listattributes", "list_attributes") # noqa
|
|
348
|
-
return self.listattributes(dictionary=dictionary)
|
|
385
|
+
return self.listattributes(dictionary=dictionary, ensemble_id=ensemble_id)
|
|
349
386
|
|
|
350
|
-
|
|
387
|
+
@lock_strategies.lock_method
|
|
388
|
+
def listattributes(self, dictionary=False, ensemble_id=None):
|
|
351
389
|
"""listattributes list all non protected attributes
|
|
352
390
|
|
|
353
|
-
:
|
|
391
|
+
:param dictionary: return a dictionary of value/pair rather than just attributes names
|
|
354
392
|
:type dictionary: bool
|
|
393
|
+
:param ensemble_id: Provide ensemble ID(s) to return ensemble tags
|
|
394
|
+
:type ensemble_id: str, lst
|
|
355
395
|
|
|
356
396
|
:return: list of attributes set on object
|
|
357
397
|
:rtype: list
|
|
358
398
|
"""
|
|
359
399
|
record = self.get_record()
|
|
360
400
|
attributes = list(record["data"].keys()) + ['id', ]
|
|
401
|
+
|
|
402
|
+
no_tags = [a for a in attributes if "_ENSEMBLE_TAG_" not in a] # Remove ensemble tags
|
|
403
|
+
ens_tags = []
|
|
404
|
+
if ensemble_id is not None:
|
|
405
|
+
if isinstance(ensemble_id, str):
|
|
406
|
+
ensemble_id = [ensemble_id]
|
|
407
|
+
for ens_id in ensemble_id:
|
|
408
|
+
ens_tags += sorted([a for a in attributes if f"{ens_id}_ENSEMBLE_TAG_" in a])
|
|
409
|
+
attributes = sorted(no_tags)
|
|
410
|
+
|
|
361
411
|
for att in self.__protected__:
|
|
362
412
|
if att in attributes and att != "id":
|
|
363
413
|
attributes.remove(att)
|
|
414
|
+
attributes += ens_tags
|
|
364
415
|
if dictionary:
|
|
365
416
|
out = {}
|
|
366
417
|
for att in attributes:
|
|
367
418
|
out[att] = getattr(self, att)
|
|
368
419
|
return out
|
|
369
420
|
else:
|
|
370
|
-
return
|
|
421
|
+
return attributes
|
|
371
422
|
|
|
423
|
+
@lock_strategies.lock_method
|
|
372
424
|
def __getattributes__(self):
|
|
373
425
|
"""__getattributes__ return dictionary with pairs of attribute/value
|
|
374
426
|
|
|
@@ -378,27 +430,39 @@ class KoshSinaObject(object):
|
|
|
378
430
|
record = self.get_record()
|
|
379
431
|
attributes = {}
|
|
380
432
|
for a in record["data"]:
|
|
433
|
+
if a in ['alias_feature', 'loader_kwargs']:
|
|
434
|
+
continue
|
|
381
435
|
attributes[a] = record["data"][a]["value"]
|
|
382
436
|
if a == "creator":
|
|
383
437
|
# old records have user id let's fix this
|
|
384
438
|
if attributes[a] in self.__store__.__record_handler__.find_with_type(
|
|
385
439
|
self.__store__._users_type, ids_only=True):
|
|
386
|
-
attributes[a] = self.__store__.get_record(
|
|
387
|
-
attributes[a])["data"]["username"]["value"]
|
|
440
|
+
attributes[a] = self.__store__.get_record(attributes[a])["data"]["username"]["value"]
|
|
388
441
|
return attributes
|
|
389
442
|
|
|
443
|
+
@lock_strategies.lock_method
|
|
390
444
|
def __str__(self):
|
|
391
445
|
"""String for printing"""
|
|
446
|
+
if self.__dict__["__store__"].verbose_attributes:
|
|
447
|
+
def reprtool(item):
|
|
448
|
+
return item
|
|
449
|
+
else:
|
|
450
|
+
def reprtool(item):
|
|
451
|
+
if isinstance(item, str):
|
|
452
|
+
return reprlib.repr(item)[1:-1]
|
|
453
|
+
else:
|
|
454
|
+
return reprlib.repr(item)
|
|
392
455
|
st = "Id: {}".format(self.id)
|
|
393
456
|
for att in sorted(self.listattributes()):
|
|
394
457
|
if att != 'id':
|
|
395
|
-
st += "\n\t{}: {}".format(att, getattr(self, att))
|
|
458
|
+
st += "\n\t{}: {}".format(att, reprtool(getattr(self, att)))
|
|
396
459
|
return st
|
|
397
460
|
|
|
398
461
|
|
|
399
462
|
class KoshSinaFile(KoshSinaObject):
|
|
400
463
|
"""KoshSinaFile file representation in Kosh via Sina"""
|
|
401
464
|
|
|
465
|
+
@lock_strategies.lock_method
|
|
402
466
|
def open(self, *args, **kargs):
|
|
403
467
|
"""open opens the file
|
|
404
468
|
:return: handle to file in open mode
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
current_version = "3.2.0"
|