Skip to content

Conversation

@dimitri-yatsenko
Copy link
Member

@dimitri-yatsenko dimitri-yatsenko commented Jan 7, 2026

Summary

DataJoint 2.0 is a major release that modernizes the entire codebase while maintaining backward compatibility for core functionality. This release focuses on extensibility, type safety, and developer experience.

Planning: DataJoint 2.0 Plan | Milestone 2.0

Major Features

Codec System (Extensible Types)

Replaces the adapter system with a modern, composable codec architecture:

  • Base codecs: <blob>, <json>, <attach>, <filepath>, <object>, <hash>, <npy>
  • Chaining: Codecs can wrap other codecs (e.g., <blob> wraps <json> for external storage)
  • Auto-registration: Custom codecs register via __init_subclass__
  • Validation: Optional validate() method for type checking before insert
from datajoint import Codec

class MyCodec(Codec):
    python_type = MyClass
    dj_type = "<blob>"  # Storage format
    
    def encode(self, value): ...
    def decode(self, value): ...

Semantic Matching

Attribute lineage tracking ensures joins only match semantically compatible attributes:

  • Attributes track their origin through foreign key inheritance
  • Joins require matching lineage (not just matching names)
  • Prevents accidental matches on generic names like id or name
  • semantic_check=False for legacy permissive behavior
# These join on subject_id because both inherit from Subject
Session * Recording  # ✓ Works - same lineage

# These fail because 'id' has different origins
TableA * TableB  # ✗ Fails - different lineage for 'id'

Primary Key Rules

Rigorous primary key propagation through all operators:

  • Join: Result PK based on functional dependencies (A→B, B→A, both, neither)
  • Aggregation: Groups by left operand's primary key
  • Projection: Preserves PK attributes, drops secondary
  • Universal set: dj.U('attr') creates ad-hoc grouping entities

AutoPopulate 2.0 (Jobs System)

Per-table job management with enhanced tracking:

  • Hidden metadata: ~~_job_timestamp and ~~_job_duration columns
  • Per-table jobs: Each computed table has its own ~~table_name job table
  • Schema.jobs: List all job tables in a schema
  • Progress tracking: table.progress() returns (remaining, total)
  • Priority scheduling: Jobs ordered by priority, then timestamp

Modern Fetch & Insert API

New fetch methods:

  • to_dicts() - List of dictionaries
  • to_pandas() - DataFrame with PK as index
  • to_arrays(*attrs) - NumPy arrays (structured or individual)
  • keys() - Primary keys only
  • fetch1() - Single row

Insert improvements:

Type Aliases

Core DataJoint types for portability:

Alias MySQL Type
int8, int16, int32, int64 tinyint, smallint, int, bigint
uint8, uint16, uint32, uint64 unsigned variants
float32, float64 float, double
bool tinyint
uuid binary(16)

Object Storage

Content-addressed and object storage types:

  • <hash> - Content-addressed storage with deduplication
  • <object> - Named object storage (Zarr, folders)
  • <npy> - NumPy arrays as .npy files
  • <filepath> - Reference to managed files
  • <attach> - File attachments (uploaded on insert)

Virtual Schema Infrastructure (#1307)

New schema introspection API for exploring existing databases:

  • Schema.get_table(name) - Direct table access with auto tier prefix detection
  • Schema['TableName'] - Bracket notation access
  • for table in schema - Iterate tables in dependency order
  • 'TableName' in schema - Check table existence
  • dj.virtual_schema() - Clean entry point for accessing schemas
  • dj.VirtualModule() - Virtual modules with custom names

CLI Improvements

The dj command-line interface for interactive exploration:

  • dj -s schema:alias - Load schemas as virtual modules
  • --host, --user, --password - Connection options
  • Fixed -h conflict with --help

Settings Modernization

Pydantic-based configuration with validation:

  • Type-safe settings with automatic validation
  • dj.config.override() context manager
  • Secrets directory support (.secrets/)
  • Environment variable overrides (DJ_HOST, etc.)

Migration Utilities

Helper functions for migrating from 0.14.x to 2.0:

  • analyze_blob_columns() - Identify columns needing type markers
  • migrate_blob_columns() - Add :<blob>: prefixes to column comments
  • check_migration_status() - Verify migration readiness
  • add_job_metadata_columns() - Add hidden job tracking columns

License Change

Changed from LGPL to Apache 2.0 license (#1235 (discussion)):

  • More permissive for commercial and academic use
  • Compatible with broader ecosystem of tools
  • Clearer patent grant provisions

Breaking Changes

Removed Support

Removed API Components

  • dj.key - Use table.keys() instead
  • dj.key_hash() - Removed (was for legacy job debugging)
  • dj.schema() - Use dj.Schema() (capitalized)
  • dj.ERD() - Use dj.Diagram()
  • dj.Di() - Use dj.Diagram()

API Changes

  • fetch()to_dicts(), to_pandas(), to_arrays()
  • fetch(format='frame')to_pandas()
  • fetch(as_dict=True)to_dicts()
  • Method parameter safemodeprompt (the config['safemode'] setting remains and controls the default behavior)

Semantic Changes

  • Joins now require lineage compatibility by default
  • Aggregation keeps non-matching rows by default (like LEFT JOIN)

Documentation

Developer Documentation (this repo)

Comprehensive updates in docs/:

  • NumPy-style docstrings for all public APIs
  • Architecture guides for contributors
  • Auto-generated API reference via mkdocstrings

User Documentation (datajoint-docs)

Full documentation site following the Diátaxis framework:

Tutorials (learning-oriented, Jupyter notebooks):

  1. Getting Started - Installation, connection, first schema
  2. Schema Design - Table tiers, definitions, foreign keys
  3. Data Entry - Insert patterns, lookups, manual tables
  4. Queries - Restriction, projection, join, aggregation, fetch
  5. Computation - Computed tables, make(), populate patterns
  6. Object Storage - Blobs, attachments, external storage

How-To Guides (task-oriented):

  • Configure object storage, Design primary keys, Model relationships
  • Handle computation errors, Manage large datasets, Create custom codecs
  • Use the CLI, Migrate from 1.x

Reference (specifications):

  • Table Declaration, Query Algebra, Data Manipulation
  • Primary Keys, Semantic Matching, Type System, Virtual Schemas
  • Codec API, AutoPopulate, Fetch API, Job Metadata

Project Structure

Test Plan

  • 636+ integration tests pass
  • 117+ unit tests pass
  • Pre-commit hooks pass
  • Documentation builds successfully
  • Tutorials execute against test database

Closes

Milestone 2.0 Issues

Bug Fixes

Improvements

Related PRs

Migration Guide

See How to Migrate from 1.x for detailed migration instructions.


🤖 Generated with Claude Code

dimitri-yatsenko and others added 30 commits December 10, 2025 10:29
switch settings to use pydantic-settings
- Remove ConfigWrapper class and backward compatibility layer
- Use direct pydantic BaseSettings with typed nested models
- Change context manager from config(...) to config.override(...)
- Add validate_assignment=True for runtime type checking
- Improve store spec validation with clear error messages
- Update all tests to use new API
- Preserve dict-style access via __getitem__/__setitem__ for convenience
Config file search:
- Search for datajoint.json recursively up from cwd
- Stop at .git/.hg boundaries or filesystem root
- Warn if no config file found (instead of silently using defaults)

Secrets management:
- Add .secrets/ directory support (next to datajoint.json)
- Support /run/secrets/datajoint/ for Docker/Kubernetes
- Use SecretStr for password and aws_secret_access_key
- Secrets masked in repr/logs, excluded from save()
- Dict access automatically unwraps SecretStr for compatibility

Breaking changes:
- Config file renamed from dj_local_conf.json to datajoint.json
- No more ~/.datajoint_config.json (project-only config)
- Secrets should be in env vars or .secrets/ directory
Add convenient type aliases that map to MySQL types:
- float32 -> float
- float64 -> double
- int32 -> int
- uint32 -> int unsigned
- int16 -> smallint
- uint16 -> smallint unsigned
- int8 -> tinyint
- uint8 -> tinyint unsigned

These aliases follow the same pattern as UUID, storing the
original type in the column comment for round-tripping.
- int64 -> bigint
- uint64 -> bigint unsigned
Add comprehensive tests for the new type aliases feature:
- Pattern matching tests for all 10 type aliases
- MySQL type mapping verification
- Table creation with type aliases
- Insert and fetch operations
- Primary key usage with type aliases
- Nullable column support
Credentials should be managed via environment variables or .secrets/
directory, not stored in config files.
Config files should be created/edited manually and version controlled,
not generated programmatically.
- Rewrite settings.md with new config structure and secrets management
- Update credentials.md to remove set_password and save methods
- Update quick-start.md with new config file name and patterns
- Add documentation for .secrets/ directory and environment variables
Document the new type aliases (float32, float64, int8-64, uint8-64)
in the datatypes documentation with a table of mappings and example usage.
- Update pyproject.toml to require Python >=3.10 (was >=3.9)
- Update ruff target-version to py310
- Update pixi Python version constraint
- Modernize type hints to use Python 3.10+ union syntax (X | Y instead
  of Union[X, Y], X | None instead of Optional[X])
- Use built-in dict, list, tuple for generics instead of typing imports
- Update MySQL documentation reference from 5.7 to 8.0
Documentation updates:
- docs/src/client/install.md: Update Python requirement from 3.4+ to 3.10+
- docs/src/quick-start.md: Update Python requirement from 3.8+ to 3.10+
- docs/src/quick-start.md: Update MySQL doc link from 5.7 to 8.0
- docs/src/develop.md: Update Python requirement from 3.9+ to 3.10+
- docs/src/develop.md: Fix version.py path (datajoint/ -> src/datajoint/)

Configuration updates:
- docker-compose.yaml: Update default PY_VER from 3.9 to 3.10
- .github/workflows/test.yaml: Remove Python 3.9 and MySQL 5.7 from test matrix
- .github/workflows/post_draft_release_published.yaml: Update to Python 3.10
- .github/workflows/post_draft_release_published.yaml: Fix version.py path
Draft specification document for the new `file@store` column type that
stores files with JSON metadata. Includes syntax, storage format, insert/fetch
behavior, and comparison with existing attachment types.
- Single storage backend per pipeline (no @store suffix)
- Use fsspec for multi-backend support (local, S3, GCS, Azure)
- Configuration via datajoint.toml at project level
- Configurable partition patterns based on primary key attributes
- Hierarchical project structure with tables/ and objects/ dirs
- Use datajoint.json instead of datajoint.toml
- Add ObjectStorageSettings class spec for settings.py
- Support DJ_OBJECT_STORAGE_* environment variables
- Support .secrets/ directory for credentials
- Partition pattern is per-pipeline (one per settings file)
- No deduplication - each record owns its file
- Random hash suffix for filenames (URL-safe, filename-safe base64)
- Configurable hash_length setting (default: 8, range: 4-16)
- Upload-first transaction strategy with cleanup on failure
- Batch insert atomicity handling
- Orphaned file detection/cleanup utilities (future)
Key changes:
- Support both files and folders
- Immutability contract: insert, read, delete only
- Deterministic bidirectional path mapping from schema/table/field/PK
- Copy-first insert: copy fails → no DB insert attempted
- DB-first delete: file delete is best-effort (stale files acceptable)
- Fetch returns handle (FileRef), no automatic download
- JSON metadata includes is_folder, file_count for folders
- FileRef class with folder operations (listdir, walk)
dimitri-yatsenko and others added 5 commits January 12, 2026 18:21
…orage

Major changes to hash-addressed storage model:
- Rename content_registry.py → hash_registry.py for clarity
- Always store full path in metadata (protects against config changes)
- Use stored path directly for retrieval (no path regeneration)
- Add delete_path() as primary function, deprecate delete_hash()
- Add get_size() as primary function, deprecate get_hash_size()
- Update gc.py to work with paths instead of hashes
- Update builtin_codecs.py HashCodec to use new API

This design enables seamless migration from v0.14:
- Legacy data keeps old paths in metadata
- New data uses new path structure
- GC compares stored paths against filesystem

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove uuid_from_buffer from hash.py (dead code)
- connection.py now uses hashlib.md5().hexdigest() directly
- Update test_hash.py to test key_hash instead

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Remove dead code that was only tested but never used in production:
- hash_exists (gc uses set operations on paths)
- delete_hash (gc uses delete_path directly)
- get_size (gc collects sizes during walk)
- get_hash_size (wrapper for get_size)

Remaining API: compute_hash, build_hash_path, get_store_backend,
get_store_subfolding, put_hash, get_hash, delete_path

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
feat: Add NpyCodec for lazy-loading numpy arrays
"errors",
"migrate",
"DataJointError",
"key",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dimitri-yatsenko The datajoint-docs state that dj.key is removed in 2.0 (see https://github.com/datajoint/datajoint-docs/blob/pre/v2.0/src/reference/specs/fetch-api.md#removed-methods-and-parameters), but' key' is still listed in __all__ in here. Should key be removed from the exports here, or does the documentation need to be updated?

@MilagrosMarin
Copy link
Contributor

The PR description states "Removed safemode parameter; use prompt instead" under Breaking Changes. This could be clearer:

  • config["safemode"] still exists (settings.py:328) and defaults to True
  • Methods renamed their parameter from safemodeprompt (table.py:955, 1041)
  • The prompt parameter uses config["safemode"] as its default value

This behavior is correctly documented in datajoint-docs, but the PR description could be updated to: "Renamed method parameter safemodeprompt; the config['safemode'] setting remains and controls the default behavior."

@MilagrosMarin
Copy link
Contributor

There's a TODO at objectref.py:369 for hash verification that's currently skipped (just pass). Is this planned for a future release or should it be addressed before 2.0?

]
else: # positional
warnings.warn(
"Positional inserts (tuples/lists) are deprecated and will be removed in a future version. "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This deprecation isn't documented in the migration guide. Should it be added, or is this intentionally a soft deprecation for now?


See Also
--------
https://docs.datajoint.com/core/datajoint-python/0.14/client/install/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outdated URL (0.14 docs). Consider updating to https://docs.datajoint.com/how-to/read-diagrams/ or adding Diagram dependencies to the installation guide.

if original_type.startswith("external"):
raise DataJointError(
f"Legacy datatype `{original_type}`. Migrate your external stores to datajoint 0.12: "
"https://docs.datajoint.io/python/admin/5-blob-config.html#migration-between-datajoint-v0-11-and-v0-12"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Outdated URL and message. This error triggers for legacy :external: format, but points to 0.11→0.12 migration on old datajoint.io domain. Should reference Phase 6 of the 2.0 migration guide: https://docs.datajoint.com/how-to/migrate-from-0x/#phase-6-external-storage-database


For single-row fetch, use fetch1() which is unchanged.

See migration guide: https://docs.datajoint.com/migration/fetch-api
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broken URL - /migration/fetch-api doesn't exist. Should be: https://docs.datajoint.com/reference/specs/fetch-api/

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 <noreply@anthropic.com>
@MilagrosMarin
Copy link
Contributor

diagram.py:26,33 — Lines 26 and 33 use bare except: for optional imports. Consider using except ImportError: instead — this avoids accidentally catching unrelated errors (e.g., syntax errors in the imported module, version incompatibilities, or keyboard interrupts during import).

@MilagrosMarin
Copy link
Contributor

Suggestion: blob.py:162,366 — These assert statements validate data integrity but are disabled with Python's -O flag. Consider replacing with explicit exception raises, e.g.:

if len(blob) != blob_size:
    raise DataJointError("Blob size mismatch")

@MilagrosMarin
Copy link
Contributor

There are a few assert False statements in the codebase that could be replaced with explicit raise statements to ensure these checks always run.

dimitri-yatsenko and others added 2 commits January 13, 2026 19:36
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 <noreply@anthropic.com>
- 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 <noreply@anthropic.com>
@dimitri-yatsenko
Copy link
Member Author

@MilagrosMarin Thank you for the thorough review! All feedback has been addressed in PR #1332:

  1. PR description clarified - Updated to: "Method parameter safemodeprompt (the config['safemode'] setting remains and controls the default behavior)"

  2. objectref.py:369 TODO - Implemented hash verification using compute_hash() from hash_registry

  3. diagram.py:26,33 - Changed bare except: to except ImportError:

  4. blob.py:162,367 - Replaced assert statements with explicit DataJointError raises

  5. assert False statements - Replaced with NotImplementedError in expression.py and DataJointError in declare.py

See commit 334da76 for the changes.

@dimitri-yatsenko dimitri-yatsenko marked this pull request as ready for review January 14, 2026 01:49
dimitri-yatsenko and others added 10 commits January 13, 2026 19:57
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 <noreply@anthropic.com>
- 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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- 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 <noreply@anthropic.com>
- 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 <noreply@anthropic.com>
* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* chore: bump version to 2.0.0a19

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The 0.14.6 implementation used:
- Table definitions: blob@store, attach@store, filepath@store
- Column comments: :blob@store:, :attach@store:, :filepath@store:

The migrate.py patterns incorrectly searched for :external-store: format.
Corrected to match actual v0.14.6 column comment format.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes:
- Added prominent migration guide callout near top of README
- Updated Resources section with new docs structure URLs:
  - Tutorials: docs.datajoint.com/tutorials/
  - How-To Guides: docs.datajoint.com/how-to/
  - API Reference: docs.datajoint.com/reference/api/
  - Migration Guide: docs.datajoint.com/how-to/migrate-from-0x/
- Organized Resources section with better formatting

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Issues related to documentation enhancement Indicates new improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants