Skip to content

Commit 3218cd1

Browse files
feat: Add migration utilities and fix flaky job tests (#1332)
* feat: add Phase 2 migration functions for column type and blob markers Add migrate_columns() and supporting functions for Phase 2 of the 0.14.6 → 2.0 migration: - analyze_columns(): Identify columns needing type labels - migrate_columns(): Add core type markers to column comments - NATIVE_TO_CORE_TYPE mapping for type conversion - Support for bool/datetime special cases - Dry-run mode for previewing changes Also adds placeholder stubs for Phase 3-4 migration functions: - migrate_external(): For external storage migration - migrate_filepath(): For filepath attribute migration - finalize_migration(): For Phase 4 finalization These functions implement the migration guide documented in datajoint-docs/src/how-to/migrate-from-0x.md. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: resolve flaky tests by using delay=-1 for immediate job scheduling The tests test_sigint, test_sigterm, test_suppress_dj_errors, and test_populate_exclude_error_and_ignore_jobs were flaky due to a race condition: jobs created with scheduled_time=NOW(3) might not pass the scheduled_time <= NOW(3) check if checked in the same millisecond. Fix by using delay=-1 in auto-refresh during populate(), ensuring jobs are scheduled 1 second in the past and immediately schedulable. Also update test_populate_exclude_error_and_ignore_jobs to use delay=-1 in its explicit refresh() call. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: address PR #1311 review comments - Replace bare except: with except ImportError: in diagram.py - Replace assert statements with explicit raises in blob.py - Replace assert False with explicit raises in expression.py and declare.py - Implement hash verification in objectref.py Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: remove deprecated dj.key from __all__ dj.key was removed in 2.0 but was still listed in __all__ without being imported or defined. Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: remove deprecated dj.schema and dj.Di aliases - Remove `schema` alias for `Schema` (use `dj.Schema` instead of `dj.schema`) - Remove `Di` alias for `Diagram` (use `dj.Diagram` or `dj.ERD`) - Update all examples, tests, and docstrings Co-Authored-By: Claude Opus 4.5 <[email protected]> * docs: fix broken documentation links Update outdated docs.datajoint.com URLs to new paths: - diagram.py: /how-to/installation/ - heading.py: /how-to/migrate-from-0x/ - expression.py: /how-to/migrate-from-0x/ Co-Authored-By: Claude Opus 4.5 <[email protected]> * chore: bump version to 2.0.0a19 Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: remove deprecated dj.key_hash and dj.Di - Remove key_hash function (legacy job table debugging) - Remove hash.py module - Fix test_erd.py to use dj.Diagram instead of dj.Di - Bump version to 2.0.0a20 Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: remove deprecated dj.ERD alias - Remove ERD alias (use dj.Diagram) - Rename test_erd_algebra to test_diagram_algebra - Remove test_diagram_aliases test - Bump version to 2.0.0a21 Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent 471b8a9 commit 3218cd1

19 files changed

+672
-85
lines changed

src/datajoint/__init__.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
"conn",
2525
"Connection",
2626
"Schema",
27-
"schema",
2827
"VirtualModule",
2928
"virtual_schema",
3029
"list_schemas",
@@ -40,8 +39,6 @@
4039
"Top",
4140
"U",
4241
"Diagram",
43-
"Di",
44-
"ERD",
4542
"kill",
4643
"MatCell",
4744
"MatStruct",
@@ -56,8 +53,6 @@
5653
"errors",
5754
"migrate",
5855
"DataJointError",
59-
"key",
60-
"key_hash",
6156
"logger",
6257
"cli",
6358
"ValidationResult",
@@ -81,7 +76,6 @@
8176
from .connection import Connection, conn
8277
from .errors import DataJointError
8378
from .expression import AndList, Not, Top, U
84-
from .hash import key_hash
8579
from .logging import logger
8680
from .objectref import ObjectRef
8781
from .schemas import Schema, VirtualModule, list_schemas, virtual_schema
@@ -90,8 +84,6 @@
9084
from .user_tables import Computed, Imported, Lookup, Manual, Part
9185
from .version import __version__
9286

93-
schema = Schema # Alias for Schema
94-
9587
# =============================================================================
9688
# Lazy imports — heavy dependencies loaded on first access
9789
# =============================================================================
@@ -101,8 +93,6 @@
10193
_lazy_modules = {
10294
# Diagram imports networkx and matplotlib
10395
"Diagram": (".diagram", "Diagram"),
104-
"Di": (".diagram", "Diagram"),
105-
"ERD": (".diagram", "Diagram"),
10696
"diagram": (".diagram", None), # Return the module itself
10797
# kill imports pymysql via connection
10898
"kill": (".admin", "kill"),

src/datajoint/autopopulate.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,9 @@ def handler(signum, frame):
485485
if refresh is None:
486486
refresh = config.jobs.auto_refresh
487487
if refresh:
488-
self.jobs.refresh(*restrictions, priority=priority)
488+
# Use delay=-1 to ensure jobs are immediately schedulable
489+
# (avoids race condition with scheduled_time <= NOW(3) check)
490+
self.jobs.refresh(*restrictions, priority=priority, delay=-1)
489491

490492
# Fetch pending jobs ordered by priority (use NOW(3) to match CURRENT_TIMESTAMP(3) precision)
491493
pending_query = self.jobs.pending & "scheduled_time <= NOW(3)"

src/datajoint/blob.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ def unpack(self, blob):
159159
self._pos += len(prefix)
160160
blob_size = self.read_value()
161161
blob = compression[prefix](self._blob[self._pos :])
162-
assert len(blob) == blob_size
162+
if len(blob) != blob_size:
163+
raise DataJointError(f"Blob size mismatch: expected {blob_size}, got {len(blob)}")
163164
self._blob = blob
164165
self._pos = 0
165166
blob_format = self.read_zero_terminated_string()
@@ -363,7 +364,8 @@ def read_int(self):
363364
@staticmethod
364365
def pack_int(v):
365366
n_bytes = v.bit_length() // 8 + 1
366-
assert 0 < n_bytes <= 0xFFFF, "Integers are limited to 65535 bytes"
367+
if not (0 < n_bytes <= 0xFFFF):
368+
raise DataJointError("Integers are limited to 65535 bytes")
367369
return b"\x0a" + np.uint16(n_bytes).tobytes() + v.to_bytes(n_bytes, byteorder="little", signed=True)
368370

369371
def read_bool(self):

src/datajoint/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ def __enter__(self) -> "Connection":
299299
Examples
300300
--------
301301
>>> with dj.Connection(host, user, password) as conn:
302-
... schema = dj.schema('my_schema', connection=conn)
302+
... schema = dj.Schema('my_schema', connection=conn)
303303
... # perform operations
304304
... # connection automatically closed
305305
"""

src/datajoint/declare.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,7 @@ def substitute_special_type(match: dict, category: str, foreign_key_sql: list[st
673673
match["type"] = sql_type
674674
# else: type passes through as-is (json, date, datetime, char, varchar, enum)
675675
else:
676-
assert False, f"Unknown special type: {category}"
676+
raise DataJointError(f"Unknown special type: {category}")
677677

678678

679679
def compile_attribute(line: str, in_key: bool, foreign_key_sql: list[str], context: dict) -> tuple[str, str, str | None]:

src/datajoint/diagram.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@
2323
from matplotlib import pyplot as plt
2424

2525
plot_active = True
26-
except:
26+
except ImportError:
2727
plot_active = False
2828

2929
try:
3030
from networkx.drawing.nx_pydot import pydot_layout
3131

3232
diagram_active = True
33-
except:
33+
except ImportError:
3434
diagram_active = False
3535

3636

@@ -48,7 +48,7 @@ class Diagram:
4848
4949
See Also
5050
--------
51-
https://docs.datajoint.com/core/datajoint-python/0.14/client/install/
51+
https://docs.datajoint.com/how-to/installation/
5252
"""
5353

5454
def __init__(self, *args, **kwargs) -> None:

src/datajoint/expression.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ def fetch(self):
596596
597597
For single-row fetch, use fetch1() which is unchanged.
598598
599-
See migration guide: https://docs.datajoint.com/migration/fetch-api
599+
See migration guide: https://docs.datajoint.com/how-to/migrate-from-0x/
600600
"""
601601
raise AttributeError(
602602
"fetch() has been removed in DataJoint 2.0. "
@@ -1085,12 +1085,12 @@ def make_sql(self):
10851085
return "({sql1}) UNION ({sql2})".format(sql1=sql1, sql2=sql2)
10861086

10871087
def from_clause(self):
1088-
"""The union does not use a FROM clause"""
1089-
assert False
1088+
"""The union does not use a FROM clause."""
1089+
raise NotImplementedError("Union does not use a FROM clause")
10901090

10911091
def where_clause(self):
1092-
"""The union does not use a WHERE clause"""
1093-
assert False
1092+
"""The union does not use a WHERE clause."""
1093+
raise NotImplementedError("Union does not use a WHERE clause")
10941094

10951095
def __len__(self):
10961096
return self.connection.query(

src/datajoint/hash.py

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/datajoint/heading.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -466,8 +466,8 @@ def _init_from_database(self) -> None:
466466
except StopIteration:
467467
if original_type.startswith("external"):
468468
raise DataJointError(
469-
f"Legacy datatype `{original_type}`. Migrate your external stores to datajoint 0.12: "
470-
"https://docs.datajoint.io/python/admin/5-blob-config.html#migration-between-datajoint-v0-11-and-v0-12"
469+
f"Legacy datatype `{original_type}`. See migration guide: "
470+
"https://docs.datajoint.com/how-to/migrate-from-0x/"
471471
)
472472
# Not a special type - that's fine, could be native passthrough
473473
category = None

0 commit comments

Comments
 (0)