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/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
- import uuid
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 warnings
2
- import logging
1
+ import collections
3
2
  import inspect
4
- import re
5
3
  import itertools
6
- import collections
4
+ import logging
5
+ import re
6
+ import types
7
+ import warnings
8
+
7
9
  from .connection import conn
8
- from .diagram import Diagram, _get_tier
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 .utils import user_choice, to_camel_case
15
- from .user_tables import Part, Computed, Imported, Manual, Lookup
16
- from .table import lookup_class_name, Log, FreeTable
17
- import types
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
- diagram = Diagram(self)
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
- full_t.replace("`", "").split(".")
487
- for full_t in Diagram(self).topological_sort()
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
- from contextlib import contextmanager
5
+ import collections
6
6
  import json
7
+ import logging
7
8
  import os
8
9
  import pprint
9
- import logging
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
- "filepath_checksum_size_limit": None, # file size limit for when to disable checksums
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
- self._conf.update(dict(*args, **kwargs)) # use the free update to set keys
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
- config_file = next(n for n in config_files if os.path.exists(n))
269
+ config.load(next(n for n in config_files if os.path.exists(n)))
258
270
  except StopIteration:
259
- pass
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
- config.update(mapping)
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 itertools
2
+ import csv
3
3
  import inspect
4
- import platform
5
- import numpy as np
6
- import pandas
4
+ import itertools
5
+ import json
7
6
  import logging
8
- import uuid
9
- import csv
7
+ import platform
10
8
  import re
11
- import json
9
+ import uuid
12
10
  from pathlib import Path
13
- from .settings import config
14
- from .declare import declare, alter
15
- from .condition import make_condition
16
- from .expression import QueryExpression
11
+ from typing import Union
12
+
13
+ import numpy as np
14
+ import pandas
15
+
17
16
  from . import blob
18
- from .utils import user_choice, get_master, is_camel_case
19
- from .heading import Heading
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
- UnknownAttributeError,
22
+ DuplicateError,
25
23
  IntegrityError,
24
+ UnknownAttributeError,
26
25
  )
27
- from typing import Union
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
- field_list = [] # collects the field list from first row (passed by reference)
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
- if "`.`" not in match["child"]: # if schema name missing, use table
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
- if not member_name.startswith("_"): # skip IPython's implicit variables
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
- from .table import Table
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
@@ -1,8 +1,9 @@
1
1
  """General-purpose utilities"""
2
2
 
3
3
  import re
4
- from pathlib import Path
5
4
  import shutil
5
+ from pathlib import Path
6
+
6
7
  from .errors import DataJointError
7
8
 
8
9
 
datajoint/version.py CHANGED
@@ -1,3 +1,6 @@
1
- __version__ = "0.14.2"
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