datajoint 0.14.2__py3-none-any.whl → 0.14.4__py3-none-any.whl
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 datajoint might be problematic. Click here for more details.
- datajoint/__init__.py +16 -14
- datajoint/admin.py +4 -2
- datajoint/attribute_adapter.py +1 -0
- datajoint/autopopulate.py +62 -20
- datajoint/blob.py +6 -5
- datajoint/cli.py +78 -0
- datajoint/condition.py +38 -5
- datajoint/connection.py +17 -10
- datajoint/declare.py +25 -6
- datajoint/dependencies.py +67 -33
- datajoint/diagram.py +58 -48
- datajoint/expression.py +92 -42
- datajoint/external.py +17 -10
- datajoint/fetch.py +18 -42
- datajoint/hash.py +1 -1
- datajoint/heading.py +14 -11
- datajoint/jobs.py +4 -3
- datajoint/plugin.py +5 -3
- datajoint/s3.py +6 -4
- datajoint/schemas.py +18 -19
- datajoint/settings.py +25 -11
- datajoint/table.py +27 -22
- datajoint/user_tables.py +30 -2
- datajoint/utils.py +2 -1
- datajoint/version.py +4 -1
- datajoint-0.14.4.dist-info/METADATA +703 -0
- datajoint-0.14.4.dist-info/RECORD +34 -0
- {datajoint-0.14.2.dist-info → datajoint-0.14.4.dist-info}/WHEEL +1 -1
- datajoint-0.14.4.dist-info/entry_points.txt +3 -0
- datajoint-0.14.2.dist-info/METADATA +0 -26
- datajoint-0.14.2.dist-info/RECORD +0 -33
- datajoint-0.14.2.dist-info/datajoint.pub +0 -6
- {datajoint-0.14.2.dist-info → datajoint-0.14.4.dist-info/licenses}/LICENSE.txt +0 -0
- {datajoint-0.14.2.dist-info → datajoint-0.14.4.dist-info}/top_level.txt +0 -0
datajoint/s3.py
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
AWS S3 operations
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import logging
|
|
6
|
+
import uuid
|
|
5
7
|
from io import BytesIO
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
6
10
|
import minio # https://docs.minio.io/docs/python-client-api-reference
|
|
7
11
|
import urllib3
|
|
8
|
-
|
|
9
|
-
import logging
|
|
10
|
-
from pathlib import Path
|
|
12
|
+
|
|
11
13
|
from . import errors
|
|
12
14
|
|
|
13
15
|
logger = logging.getLogger(__name__.split(".")[0])
|
|
@@ -27,7 +29,7 @@ class Folder:
|
|
|
27
29
|
*,
|
|
28
30
|
secure=False,
|
|
29
31
|
proxy_server=None,
|
|
30
|
-
**_
|
|
32
|
+
**_,
|
|
31
33
|
):
|
|
32
34
|
# from https://docs.min.io/docs/python-client-api-reference
|
|
33
35
|
self.client = minio.Minio(
|
datajoint/schemas.py
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import logging
|
|
1
|
+
import collections
|
|
3
2
|
import inspect
|
|
4
|
-
import re
|
|
5
3
|
import itertools
|
|
6
|
-
import
|
|
4
|
+
import logging
|
|
5
|
+
import re
|
|
6
|
+
import types
|
|
7
|
+
import warnings
|
|
8
|
+
|
|
7
9
|
from .connection import conn
|
|
8
|
-
from .
|
|
9
|
-
from .settings import config
|
|
10
|
-
from .errors import DataJointError, AccessError
|
|
11
|
-
from .jobs import JobTable
|
|
10
|
+
from .errors import AccessError, DataJointError
|
|
12
11
|
from .external import ExternalMapping
|
|
13
12
|
from .heading import Heading
|
|
14
|
-
from .
|
|
15
|
-
from .
|
|
16
|
-
from .table import
|
|
17
|
-
import
|
|
13
|
+
from .jobs import JobTable
|
|
14
|
+
from .settings import config
|
|
15
|
+
from .table import FreeTable, Log, lookup_class_name
|
|
16
|
+
from .user_tables import Computed, Imported, Lookup, Manual, Part, _get_tier
|
|
17
|
+
from .utils import to_camel_case, user_choice
|
|
18
18
|
|
|
19
19
|
logger = logging.getLogger(__name__.split(".")[0])
|
|
20
20
|
|
|
@@ -413,6 +413,7 @@ class Schema:
|
|
|
413
413
|
|
|
414
414
|
:return: a string containing the body of a complete Python module defining this schema.
|
|
415
415
|
"""
|
|
416
|
+
self.connection.dependencies.load()
|
|
416
417
|
self._assert_exists()
|
|
417
418
|
module_count = itertools.count()
|
|
418
419
|
# add virtual modules for referenced modules with names vmod0, vmod1, ...
|
|
@@ -451,10 +452,8 @@ class Schema:
|
|
|
451
452
|
).replace("\n", "\n " + indent),
|
|
452
453
|
)
|
|
453
454
|
|
|
454
|
-
|
|
455
|
-
body = "\n\n".join(
|
|
456
|
-
make_class_definition(table) for table in diagram.topological_sort()
|
|
457
|
-
)
|
|
455
|
+
tables = self.connection.dependencies.topo_sort()
|
|
456
|
+
body = "\n\n".join(make_class_definition(table) for table in tables)
|
|
458
457
|
python_code = "\n\n".join(
|
|
459
458
|
(
|
|
460
459
|
'"""This module was auto-generated by datajoint from an existing schema"""',
|
|
@@ -480,11 +479,12 @@ class Schema:
|
|
|
480
479
|
|
|
481
480
|
:return: A list of table names from the database schema.
|
|
482
481
|
"""
|
|
482
|
+
self.connection.dependencies.load()
|
|
483
483
|
return [
|
|
484
484
|
t
|
|
485
485
|
for d, t in (
|
|
486
|
-
|
|
487
|
-
for
|
|
486
|
+
table_name.replace("`", "").split(".")
|
|
487
|
+
for table_name in self.connection.dependencies.topo_sort()
|
|
488
488
|
)
|
|
489
489
|
if d == self.database
|
|
490
490
|
]
|
|
@@ -533,7 +533,6 @@ class VirtualModule(types.ModuleType):
|
|
|
533
533
|
|
|
534
534
|
def list_schemas(connection=None):
|
|
535
535
|
"""
|
|
536
|
-
|
|
537
536
|
:param connection: a dj.Connection object
|
|
538
537
|
:return: list of all accessible schemas on the server
|
|
539
538
|
"""
|
datajoint/settings.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Settings for DataJoint
|
|
2
|
+
Settings for DataJoint
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import collections
|
|
6
6
|
import json
|
|
7
|
+
import logging
|
|
7
8
|
import os
|
|
8
9
|
import pprint
|
|
9
|
-
import
|
|
10
|
-
import collections
|
|
10
|
+
from contextlib import contextmanager
|
|
11
11
|
from enum import Enum
|
|
12
|
+
|
|
12
13
|
from .errors import DataJointError
|
|
13
14
|
|
|
14
15
|
LOCALCONFIG = "dj_local_conf.json"
|
|
@@ -47,7 +48,9 @@ default = dict(
|
|
|
47
48
|
"display.show_tuple_count": True,
|
|
48
49
|
"database.use_tls": None,
|
|
49
50
|
"enable_python_native_blobs": True, # python-native/dj0 encoding support
|
|
50
|
-
"
|
|
51
|
+
"add_hidden_timestamp": False,
|
|
52
|
+
# file size limit for when to disable checksums
|
|
53
|
+
"filepath_checksum_size_limit": None,
|
|
51
54
|
}
|
|
52
55
|
)
|
|
53
56
|
|
|
@@ -116,6 +119,7 @@ class Config(collections.abc.MutableMapping):
|
|
|
116
119
|
if filename is None:
|
|
117
120
|
filename = LOCALCONFIG
|
|
118
121
|
with open(filename, "r") as fid:
|
|
122
|
+
logger.info(f"DataJoint is configured from {os.path.abspath(filename)}")
|
|
119
123
|
self._conf.update(json.load(fid))
|
|
120
124
|
|
|
121
125
|
def save_local(self, verbose=False):
|
|
@@ -235,7 +239,8 @@ class Config(collections.abc.MutableMapping):
|
|
|
235
239
|
|
|
236
240
|
def __init__(self, *args, **kwargs):
|
|
237
241
|
self._conf = dict(default)
|
|
238
|
-
|
|
242
|
+
# use the free update to set keys
|
|
243
|
+
self._conf.update(dict(*args, **kwargs))
|
|
239
244
|
|
|
240
245
|
def __getitem__(self, key):
|
|
241
246
|
return self._conf[key]
|
|
@@ -246,6 +251,13 @@ class Config(collections.abc.MutableMapping):
|
|
|
246
251
|
self._conf[key] = value
|
|
247
252
|
else:
|
|
248
253
|
raise DataJointError("Validator for {0:s} did not pass".format(key))
|
|
254
|
+
valid_logging_levels = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
|
|
255
|
+
if key == "loglevel":
|
|
256
|
+
if value not in valid_logging_levels:
|
|
257
|
+
raise ValueError(
|
|
258
|
+
f"'{value}' is not a valid logging value {tuple(valid_logging_levels)}"
|
|
259
|
+
)
|
|
260
|
+
logger.setLevel(value)
|
|
249
261
|
|
|
250
262
|
|
|
251
263
|
# Load configuration from file
|
|
@@ -254,11 +266,9 @@ config_files = (
|
|
|
254
266
|
os.path.expanduser(n) for n in (LOCALCONFIG, os.path.join("~", GLOBALCONFIG))
|
|
255
267
|
)
|
|
256
268
|
try:
|
|
257
|
-
|
|
269
|
+
config.load(next(n for n in config_files if os.path.exists(n)))
|
|
258
270
|
except StopIteration:
|
|
259
|
-
|
|
260
|
-
else:
|
|
261
|
-
config.load(config_file)
|
|
271
|
+
logger.info("No config file was found.")
|
|
262
272
|
|
|
263
273
|
# override login credentials with environment variables
|
|
264
274
|
mapping = {
|
|
@@ -270,6 +280,7 @@ mapping = {
|
|
|
270
280
|
"database.password",
|
|
271
281
|
"external.aws_access_key_id",
|
|
272
282
|
"external.aws_secret_access_key",
|
|
283
|
+
"loglevel",
|
|
273
284
|
),
|
|
274
285
|
map(
|
|
275
286
|
os.getenv,
|
|
@@ -279,11 +290,14 @@ mapping = {
|
|
|
279
290
|
"DJ_PASS",
|
|
280
291
|
"DJ_AWS_ACCESS_KEY_ID",
|
|
281
292
|
"DJ_AWS_SECRET_ACCESS_KEY",
|
|
293
|
+
"DJ_LOG_LEVEL",
|
|
282
294
|
),
|
|
283
295
|
),
|
|
284
296
|
)
|
|
285
297
|
if v is not None
|
|
286
298
|
}
|
|
287
|
-
|
|
299
|
+
if mapping:
|
|
300
|
+
logger.info(f"Overloaded settings {tuple(mapping)} from environment variables.")
|
|
301
|
+
config.update(mapping)
|
|
288
302
|
|
|
289
303
|
logger.setLevel(log_levels[config["loglevel"]])
|
datajoint/table.py
CHANGED
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
import collections
|
|
2
|
-
import
|
|
2
|
+
import csv
|
|
3
3
|
import inspect
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import pandas
|
|
4
|
+
import itertools
|
|
5
|
+
import json
|
|
7
6
|
import logging
|
|
8
|
-
import
|
|
9
|
-
import csv
|
|
7
|
+
import platform
|
|
10
8
|
import re
|
|
11
|
-
import
|
|
9
|
+
import uuid
|
|
12
10
|
from pathlib import Path
|
|
13
|
-
from
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
from typing import Union
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
import pandas
|
|
15
|
+
|
|
17
16
|
from . import blob
|
|
18
|
-
from .
|
|
19
|
-
from .
|
|
17
|
+
from .condition import make_condition
|
|
18
|
+
from .declare import alter, declare
|
|
20
19
|
from .errors import (
|
|
21
|
-
DuplicateError,
|
|
22
20
|
AccessError,
|
|
23
21
|
DataJointError,
|
|
24
|
-
|
|
22
|
+
DuplicateError,
|
|
25
23
|
IntegrityError,
|
|
24
|
+
UnknownAttributeError,
|
|
26
25
|
)
|
|
27
|
-
from
|
|
26
|
+
from .expression import QueryExpression
|
|
27
|
+
from .heading import Heading
|
|
28
|
+
from .settings import config
|
|
29
|
+
from .utils import get_master, is_camel_case, user_choice
|
|
28
30
|
from .version import __version__ as version
|
|
29
31
|
|
|
30
32
|
logger = logging.getLogger(__name__.split(".")[0])
|
|
@@ -196,7 +198,6 @@ class Table(QueryExpression):
|
|
|
196
198
|
|
|
197
199
|
def children(self, primary=None, as_objects=False, foreign_key_info=False):
|
|
198
200
|
"""
|
|
199
|
-
|
|
200
201
|
:param primary: if None, then all children are returned. If True, then only foreign keys composed of
|
|
201
202
|
primary key attributes are considered. If False, return foreign keys including at least one
|
|
202
203
|
secondary attribute.
|
|
@@ -218,7 +219,6 @@ class Table(QueryExpression):
|
|
|
218
219
|
|
|
219
220
|
def descendants(self, as_objects=False):
|
|
220
221
|
"""
|
|
221
|
-
|
|
222
222
|
:param as_objects: False - a list of table names; True - a list of table objects.
|
|
223
223
|
:return: list of tables descendants in topological order.
|
|
224
224
|
"""
|
|
@@ -230,7 +230,6 @@ class Table(QueryExpression):
|
|
|
230
230
|
|
|
231
231
|
def ancestors(self, as_objects=False):
|
|
232
232
|
"""
|
|
233
|
-
|
|
234
233
|
:param as_objects: False - a list of table names; True - a list of table objects.
|
|
235
234
|
:return: list of tables ancestors in topological order.
|
|
236
235
|
"""
|
|
@@ -246,6 +245,7 @@ class Table(QueryExpression):
|
|
|
246
245
|
|
|
247
246
|
:param as_objects: if False (default), the output is a dict describing the foreign keys. If True, return table objects.
|
|
248
247
|
"""
|
|
248
|
+
self.connection.dependencies.load(force=False)
|
|
249
249
|
nodes = [
|
|
250
250
|
node
|
|
251
251
|
for node in self.connection.dependencies.nodes
|
|
@@ -427,7 +427,8 @@ class Table(QueryExpression):
|
|
|
427
427
|
self.connection.query(query)
|
|
428
428
|
return
|
|
429
429
|
|
|
430
|
-
|
|
430
|
+
# collects the field list from first row (passed by reference)
|
|
431
|
+
field_list = []
|
|
431
432
|
rows = list(
|
|
432
433
|
self.__make_row_to_insert(row, field_list, ignore_extra_fields)
|
|
433
434
|
for row in rows
|
|
@@ -520,7 +521,8 @@ class Table(QueryExpression):
|
|
|
520
521
|
delete_count = table.delete_quick(get_count=True)
|
|
521
522
|
except IntegrityError as error:
|
|
522
523
|
match = foreign_key_error_regexp.match(error.args[0]).groupdict()
|
|
523
|
-
|
|
524
|
+
# if schema name missing, use table
|
|
525
|
+
if "`.`" not in match["child"]:
|
|
524
526
|
match["child"] = "{}.{}".format(
|
|
525
527
|
table.full_table_name.split(".")[0], match["child"]
|
|
526
528
|
)
|
|
@@ -644,6 +646,8 @@ class Table(QueryExpression):
|
|
|
644
646
|
logger.warn("Nothing to delete.")
|
|
645
647
|
if transaction:
|
|
646
648
|
self.connection.cancel_transaction()
|
|
649
|
+
elif not transaction:
|
|
650
|
+
logger.info("Delete completed")
|
|
647
651
|
else:
|
|
648
652
|
if not safemode or user_choice("Commit deletes?", default="no") == "yes":
|
|
649
653
|
if transaction:
|
|
@@ -962,7 +966,8 @@ def lookup_class_name(name, context, depth=3):
|
|
|
962
966
|
while nodes:
|
|
963
967
|
node = nodes.pop(0)
|
|
964
968
|
for member_name, member in node["context"].items():
|
|
965
|
-
|
|
969
|
+
# skip IPython's implicit variables
|
|
970
|
+
if not member_name.startswith("_"):
|
|
966
971
|
if inspect.isclass(member) and issubclass(member, Table):
|
|
967
972
|
if member.full_table_name == name: # found it!
|
|
968
973
|
return ".".join([node["context_name"], member_name]).lstrip(".")
|
datajoint/user_tables.py
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
Hosts the table tiers, user tables should be derived from.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
import re
|
|
6
|
+
|
|
6
7
|
from .autopopulate import AutoPopulate
|
|
7
|
-
from .utils import from_camel_case, ClassProperty
|
|
8
8
|
from .errors import DataJointError
|
|
9
|
+
from .table import Table
|
|
10
|
+
from .utils import ClassProperty, from_camel_case
|
|
9
11
|
|
|
10
12
|
_base_regexp = r"[a-z][a-z0-9]*(_[a-z][a-z0-9]*)*"
|
|
11
13
|
|
|
@@ -242,3 +244,29 @@ class Part(UserTable):
|
|
|
242
244
|
def alter(self, prompt=True, context=None):
|
|
243
245
|
# without context, use declaration context which maps master keyword to master table
|
|
244
246
|
super().alter(prompt=prompt, context=context or self.declaration_context)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
user_table_classes = (Manual, Lookup, Computed, Imported, Part)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class _AliasNode:
|
|
253
|
+
"""
|
|
254
|
+
special class to indicate aliased foreign keys
|
|
255
|
+
"""
|
|
256
|
+
|
|
257
|
+
pass
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _get_tier(table_name):
|
|
261
|
+
"""given the table name, return the user table class."""
|
|
262
|
+
if not table_name.startswith("`"):
|
|
263
|
+
return _AliasNode
|
|
264
|
+
else:
|
|
265
|
+
try:
|
|
266
|
+
return next(
|
|
267
|
+
tier
|
|
268
|
+
for tier in user_table_classes
|
|
269
|
+
if re.fullmatch(tier.tier_regexp, table_name.split("`")[-2])
|
|
270
|
+
)
|
|
271
|
+
except StopIteration:
|
|
272
|
+
return None
|
datajoint/utils.py
CHANGED
datajoint/version.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
# version bump auto managed by Github Actions:
|
|
2
|
+
# label_prs.yaml(prep), release.yaml(bump), post_release.yaml(edit)
|
|
3
|
+
# manually set this version will be eventually overwritten by the above actions
|
|
4
|
+
__version__ = "0.14.4"
|
|
2
5
|
|
|
3
6
|
assert len(__version__) <= 10 # The log table limits version to the 10 characters
|