44
55from __future__ import annotations
66
7+ import warnings
8+
79from ..codecs import Codec
810from ..errors import DataJointError
911
@@ -24,14 +26,27 @@ class SchemaCodec(Codec, register=False):
2426 - ``validate()``: Validate input values
2527
2628 Helper Methods:
27- - ``_extract_context()``: Parse key dict into schema/table/field/pk
29+ - ``_extract_context()``: Parse key/context into schema/table/field/pk
30+ - ``_codec_config()``: Read the calling connection's config
2831 - ``_build_path()``: Construct storage path from context
2932 - ``_get_backend()``: Get storage backend by name
3033
31- Both helpers take a ``config`` and fall back to the global ``dj.config``
32- without one. Read it off ``key["_config"]`` and pass it through, as below: it
33- is the calling connection's config, and in a process holding connections for
34- several users the global one belongs to none of them.
34+ ``_build_path`` and ``_get_backend`` take a ``config`` and fall back to the
35+ global ``dj.config`` without one. Always pass the calling connection's
36+ config: in a process holding connections for several users, the global one
37+ belongs to none of them, and the fallback resolves a different store
38+ silently rather than raising.
39+
40+ Since 2.3.4 that config arrives in an explicit ``context`` argument rather
41+ than hidden among the primary key values. **Declare ``context=None`` in
42+ ``encode`` and ``decode`` and pass it to the helpers**, as the example below
43+ does.
44+
45+ The old underscore keys in ``key`` still work and are still populated, so a
46+ codec written before 2.3.4 keeps running. They are removed in 2.4.
47+ ``_extract_context`` warns when it falls back to them; ``_codec_config`` and
48+ a direct ``key["_config"]`` do not, so the warning is a prompt rather than a
49+ complete inventory of what still reads them.
3550
3651 Comparison with Hash-addressed:
3752 - **Schema-addressed** (this): Path from schema structure, no dedup
@@ -42,9 +57,9 @@ class SchemaCodec(Codec, register=False):
4257 class MyCodec(SchemaCodec):
4358 name = "my"
4459
45- def encode(self, value, *, key=None, store_name=None):
46- schema, table, field, pk = self._extract_context(key)
47- config = (key or {}).get("_config" )
60+ def encode(self, value, *, key=None, context=None, store_name=None):
61+ schema, table, field, pk = self._extract_context(key, context )
62+ config = self._codec_config (key, context )
4863 path, _ = self._build_path(
4964 schema, table, field, pk, ext=".dat",
5065 store_name=store_name, config=config,
@@ -53,8 +68,8 @@ def encode(self, value, *, key=None, store_name=None):
5368 backend.put_buffer(serialize(value), path)
5469 return {"path": path, "store": store_name, ...}
5570
56- def decode(self, stored, *, key=None):
57- config = (key or {}).get("_config" )
71+ def decode(self, stored, *, key=None, context=None ):
72+ config = self._codec_config (key, context )
5873 backend = self._get_backend(stored.get("store"), config=config)
5974 return MyRef(stored, backend)
6075
@@ -88,25 +103,41 @@ def get_dtype(self, is_store: bool) -> str:
88103 raise DataJointError (f"<{ self .name } > requires @ (store only)" )
89104 return "json"
90105
91- def _extract_context (self , key : dict | None ) -> tuple [str , str , str , dict ]:
106+ def _extract_context (self , key : dict | None , context : dict | None = None ) -> tuple [str , str , str , dict ]:
92107 """
93- Extract schema, table, field, and primary key from context dict .
108+ Extract schema, table, field, and primary key.
94109
95110 Parameters
96111 ----------
97112 key : dict or None
98- Context dict with ``_schema``, ``_table``, ``_field``,
99- and primary key values.
113+ Primary key values. Before 2.3.4 this also carried connection
114+ context under ``_schema``, ``_table``, ``_field`` and ``_config``;
115+ those keys are still populated and still read, with a
116+ ``DeprecationWarning``, when ``context`` is not supplied.
117+ context : dict or None
118+ Connection context with ``schema``, ``table``, ``field`` and
119+ ``config``. Pass the ``context`` argument your ``encode``/``decode``
120+ received.
100121
101122 Returns
102123 -------
103124 tuple[str, str, str, dict]
104125 ``(schema, table, field, primary_key)``
105126 """
106127 key = dict (key ) if key else {}
107- schema = key .pop ("_schema" , "unknown" )
108- table = key .pop ("_table" , "unknown" )
109- field = key .pop ("_field" , "data" )
128+ if context is None and any (k .startswith ("_" ) for k in key ):
129+ warnings .warn (
130+ "Reading connection context from the `key` dict is deprecated and will "
131+ "be removed in DataJoint 2.4. Accept a `context` argument in encode()/decode() "
132+ "and pass it to _extract_context(key, context). See "
133+ "https://github.com/datajoint/datajoint-python/issues/1550" ,
134+ DeprecationWarning ,
135+ stacklevel = 2 ,
136+ )
137+ context = context or {}
138+ schema = context .get ("schema" , key .pop ("_schema" , "unknown" ))
139+ table = context .get ("table" , key .pop ("_table" , "unknown" ))
140+ field = context .get ("field" , key .pop ("_field" , "data" ))
110141 primary_key = {k : v for k , v in key .items () if not k .startswith ("_" )}
111142 return schema , table , field , primary_key
112143
0 commit comments