code-loader 1.0.196.dev0__py3-none-any.whl → 1.0.196.dev1__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.
@@ -1,6 +1,9 @@
1
1
 
2
+ import builtins
2
3
  import inspect
3
- from typing import Callable, List, Optional, Dict, Any, Type, Union, get_args, cast
4
+ import os
5
+ from contextlib import contextmanager
6
+ from typing import Callable, List, Optional, Dict, Any, Type, Union, get_args, cast, Iterator, Set
4
7
 
5
8
  import numpy as np
6
9
  import numpy.typing as npt
@@ -16,7 +19,8 @@ from code_loader.contract.datasetclasses import SectionCallableInterface, InputH
16
19
  SimulationHandler, _simulation_context, AutoregressiveStepHandler, AutoregressiveStepCallableInterface, \
17
20
  AUTOREGRESSIVE_LATENT_SPACE_AGGREGATIONS, AUTOREGRESSIVE_IMPLICIT_ARG_NAMES, \
18
21
  AutoregressiveMetricHandler, AutoregressiveLossHandler, AutoregressiveVisualizerHandler
19
- from code_loader.contract.enums import LeapDataType, DataStateEnum, DataStateType, MetricDirection, DatasetMetadataType
22
+ from code_loader.contract.enums import LeapDataType, DataStateEnum, DataStateType, MetricDirection, DatasetMetadataType, \
23
+ TestingSectionEnum
20
24
  from code_loader.contract.mapping import NodeConnection, NodeMapping, NodeMappingType
21
25
  from code_loader.contract.responsedataclasses import DatasetTestResultPayload, LeapAnalysisConfiguration
22
26
  from code_loader.contract.visualizer_classes import map_leap_data_type_to_visualizer_class
@@ -33,6 +37,25 @@ from code_loader.visualizers.default_visualizers import DefaultVisualizer, \
33
37
  mapping_runtime_mode_env_var_mame = '__MAPPING_RUNTIME_MODE__'
34
38
 
35
39
 
40
+ @contextmanager
41
+ def _track_opened_files() -> Iterator[Set[str]]:
42
+ opened: Set[str] = set()
43
+ original_open = builtins.open
44
+
45
+ def tracking_open(file: Any, *args: Any, **kwargs: Any) -> Any:
46
+ mode = args[0] if args else kwargs.get('mode', 'r')
47
+ is_read_mode = isinstance(mode, str) and not any(c in mode for c in ('a', 'w', 'x'))
48
+ if is_read_mode and isinstance(file, (str, os.PathLike)):
49
+ opened.add(os.fspath(file))
50
+ return original_open(file, *args, **kwargs)
51
+
52
+ builtins.open = tracking_open
53
+ try:
54
+ yield opened
55
+ finally:
56
+ builtins.open = original_open
57
+
58
+
36
59
  def _stringized_annotation_type_name(annotation: Any) -> Optional[str]:
37
60
  """Bare type name if ``annotation`` is a stringized annotation (from ``from __future__
38
61
  import annotations`` or a quoted hint), else ``None``. code_loader inspects raw
@@ -744,15 +767,34 @@ class LeapBinder:
744
767
  preprocess_response: PreprocessResponse, test_result: List[DatasetTestResultPayload],
745
768
  dataset_base_handler: Union[DatasetBaseHandler, MetadataHandler], state: DataStateEnum) -> List[DatasetTestResultPayload]:
746
769
  assert preprocess_response.sample_ids is not None
770
+ opened_files: Optional[Set[str]] = None
747
771
  if preprocess_response.is_grouped:
748
772
  # Grouped: probe with the first group, then reduce to a single sample's result so the
749
773
  # recorded shape/type is per-sample (matching the flat case), not the (B, *) batch.
750
774
  # (casts: sample_ids[0] is a group list here, and the grouped result is a per-sample list.)
751
775
  group = preprocess_response.sample_ids[0]
752
- raw_result = cast(Any, dataset_base_handler.function(cast(Any, group), preprocess_response))[0]
776
+ with _track_opened_files() as opened_files:
777
+ raw_result = cast(Any, dataset_base_handler.function(cast(Any, group), preprocess_response))[0]
753
778
  else:
754
779
  raw_result = dataset_base_handler.function(
755
780
  cast(Union[int, str], preprocess_response.sample_ids[0]), preprocess_response)
781
+
782
+ def _warn_if_group_spans_multiple_files(payloads: List[DatasetTestResultPayload]) -> None:
783
+ if opened_files is None or len(opened_files) <= 1:
784
+ return
785
+ shown = sorted(opened_files)[:5]
786
+ suffix = f" (+{len(opened_files) - 5} more)" if len(opened_files) > 5 else ""
787
+ warning = (
788
+ f"Encoding declared group 0 (size {len(cast(List[Any], group))}) for '{dataset_base_handler.name}' opened "
789
+ f"{len(opened_files)} distinct files: {shown}{suffix}. If this group is meant to be one "
790
+ f"physical source (e.g. one Parquet file), it may be grouped incorrectly - check your "
791
+ f"preprocess grouping logic. (Only files opened via Python's open() are tracked, so this "
792
+ f"can miss other I/O paths or be a false positive if grouping isn't meant to reflect file "
793
+ f"locality.)")
794
+ for payload in payloads:
795
+ existing = payload.display.get(TestingSectionEnum.Errors.name, '')
796
+ payload.display[TestingSectionEnum.Errors.name] = (existing + '\n' if existing else '') + warning
797
+
756
798
  handler_type = 'metadata' if isinstance(dataset_base_handler, MetadataHandler) else None
757
799
  if isinstance(dataset_base_handler, MetadataHandler):
758
800
  if isinstance(raw_result, dict):
@@ -792,6 +834,7 @@ class LeapBinder:
792
834
  else:
793
835
  if raw_result is None:
794
836
  if state != DataStateEnum.training:
837
+ _warn_if_group_spans_multiple_files(test_result)
795
838
  return test_result
796
839
 
797
840
  if dataset_base_handler.metadata_type is None:
@@ -813,6 +856,7 @@ class LeapBinder:
813
856
  # setting shape in setup for all encoders
814
857
  if isinstance(dataset_base_handler, (InputHandler, GroundTruthHandler)):
815
858
  dataset_base_handler.shape = result_shape
859
+ _warn_if_group_spans_multiple_files(test_result)
816
860
  return test_result
817
861
 
818
862
  def check_handlers(self, preprocess_result: Dict[DataStateEnum, PreprocessResponse]) -> None:
@@ -2010,9 +2010,10 @@ def tensorleap_autoregressive_step(latent_space_aggregation: str = 'last_step'):
2010
2010
  state returned by the previous call, and return the next step's full input dict together with
2011
2011
  the updated state; returning (None, state) ends the chain — the terminating call still
2012
2012
  returns state, so the final step participates in state aggregation. State is never fed to
2013
- the model; it may be any nest of dicts/lists holding numpy arrays, numbers, strings or bools,
2014
- and it rides the platform queue on every step accumulate reductions, not raw per-step
2015
- tensors. The hook must be stateless and deterministically seeded (seed stochasticity from
2013
+ the model; any picklable nest of builtin/numpy values works (dicts, lists, tuples, sets,
2014
+ numbers, strings, arrays not user-defined classes, lambdas or open resources, since the
2015
+ platform deserializes state without the integration code), and it rides the platform queue
2016
+ on every step — accumulate reductions, not raw per-step tensors. The hook must be stateless and deterministically seeded (seed stochasticity from
2016
2017
  sample_id) — the engine replays steps after crash recovery and relies on identical results.
2017
2018
 
2018
2019
  latent_space_aggregation ('last_step' | 'mean') declares how the chain's latent-space
@@ -3114,10 +3115,12 @@ def tensorleap_input_encoder(name: str, channel_dim=_UNSET, model_input_index=No
3114
3115
  if _called_from_inside_tl_decorator == 0 and _called_from_inside_tl_integration_test_decorator:
3115
3116
  if grouped:
3116
3117
  # A grouped result is a list of per-sample arrays (never stacked, so a
3117
- # B=1-only model can be fed one sample at a time). Add a batch dim to each
3118
- # sample, mirroring the flat path's single-sample expand_dims.
3119
- if isinstance(result, list):
3120
- result = [np.expand_dims(r, axis=0) for r in result]
3118
+ # B=1-only model can be fed one sample at a time). A user may also return a
3119
+ # supported (B, *) array; split it back into per-sample rows first (mirrors
3120
+ # LeapLoader._to_grouped_list) so both forms match the runtime path. Then add
3121
+ # a batch dim to each sample, mirroring the flat path's single-sample expand_dims.
3122
+ rows = result if isinstance(result, list) else [row for row in np.asarray(result)]
3123
+ result = [np.expand_dims(r, axis=0) for r in rows]
3121
3124
  else:
3122
3125
  batch_warning(result, user_function.__name__)
3123
3126
  result = np.expand_dims(result, axis=0)
@@ -3228,10 +3231,12 @@ def tensorleap_gt_encoder(name: str):
3228
3231
  if _called_from_inside_tl_decorator == 0 and _called_from_inside_tl_integration_test_decorator:
3229
3232
  if grouped:
3230
3233
  # A grouped result is a list of per-sample arrays (never stacked, so a
3231
- # B=1-only model can be fed one sample at a time). Add a batch dim to each
3232
- # sample, mirroring the flat path's single-sample expand_dims.
3233
- if isinstance(result, list):
3234
- result = [np.expand_dims(r, axis=0) for r in result]
3234
+ # B=1-only model can be fed one sample at a time). A user may also return a
3235
+ # supported (B, *) array; split it back into per-sample rows first (mirrors
3236
+ # LeapLoader._to_grouped_list) so both forms match the runtime path. Then add
3237
+ # a batch dim to each sample, mirroring the flat path's single-sample expand_dims.
3238
+ rows = result if isinstance(result, list) else [row for row in np.asarray(result)]
3239
+ result = [np.expand_dims(r, axis=0) for r in rows]
3235
3240
  else:
3236
3241
  batch_warning(result, user_function.__name__)
3237
3242
  result = np.expand_dims(result, axis=0)
code_loader/leaploader.py CHANGED
@@ -77,8 +77,6 @@ class LeapLoader(LeapLoaderBase):
77
77
  preprocess_result = global_leap_binder.get_preprocess_result()
78
78
  self._preprocess_result_cached = preprocess_result
79
79
  is_grouped = any(r.is_grouped for r in preprocess_result.values())
80
- except Exception:
81
- is_grouped = False
82
80
  finally:
83
81
  os.environ[mapping_runtime_mode_env_var_mame] = 'TRUE'
84
82
 
code_loader/utils.py CHANGED
@@ -1,4 +1,6 @@
1
+ import io
1
2
  import math
3
+ import pickle
2
4
  import sys
3
5
  from pathlib import Path
4
6
  from types import TracebackType
@@ -15,10 +17,12 @@ from code_loader.contract.enums import DatasetMetadataType
15
17
  def to_numpy_return_wrapper(encoder_function: SectionCallableInterface) -> SectionCallableInterface:
16
18
  def numpy_encoder_function(idx: Union[int, str], samples: PreprocessResponse) -> npt.NDArray[np.float32]:
17
19
  result = encoder_function(idx, samples)
18
- if isinstance(result, list) and len(result) > 0 and all(isinstance(sample, np.ndarray) for sample in result):
19
- # A list of per-sample arrays only comes from a grouped encoder (a flat encoder returns
20
- # a single array). Never stack it into a (B, *) batch grouping is only a storage
21
- # read-unit; batching the group to the model's B is the engine's single responsibility.
20
+ if isinstance(idx, list) and isinstance(result, list) and len(result) > 0 \
21
+ and all(isinstance(sample, np.ndarray) for sample in result):
22
+ # Grouped call (idx is a list of sample ids): keep the per-sample list, never stack
23
+ # it into a (B, *) batch grouping is only a storage read-unit; batching the group to
24
+ # the model's B is the engine's single responsibility. Gate on the call shape (idx is a
25
+ # list), not the result shape, so a flat encoder returning a list of arrays still stacks.
22
26
  return result
23
27
  numpy_result: npt.NDArray[np.float32] = np.array(result)
24
28
  return numpy_result
@@ -109,27 +113,31 @@ def get_metadata_type_from_variable(variable: Union[Optional[str], int, bool, Op
109
113
 
110
114
 
111
115
 
112
- AUTOREGRESSIVE_STATE_LEAF_TYPES = (np.ndarray, np.generic, int, float, str, bool, type(None))
116
+ # The platform queue transports state as pickled payloads which the engine deserializes
117
+ # WITHOUT the integration code loaded, so a class defined in the user's modules round-trips
118
+ # locally but crashes on the engine. Only modules importable on the engine may resolve.
119
+ _STATE_SAFE_PICKLE_MODULES = ('builtins', 'copyreg', 'collections', 'numpy', 'datetime')
120
+
121
+
122
+ class _EngineSafeUnpickler(pickle.Unpickler):
123
+ def find_class(self, module: str, name: str) -> Any:
124
+ if module.split('.')[0] in _STATE_SAFE_PICKLE_MODULES:
125
+ return super().find_class(module, name)
126
+ raise pickle.UnpicklingError(
127
+ f'{module}.{name} is defined in integration code, which is not importable on the '
128
+ f'platform engine')
113
129
 
114
130
 
115
131
  def validate_autoregressive_state_types(value: Any, path: str = 'state') -> None:
116
- if isinstance(value, dict):
117
- for key, item in value.items():
118
- if not isinstance(key, str):
119
- raise AssertionError(
120
- f'autoregressive state validation failed: dict key {key!r} at {path} must be '
121
- f'a string. Got {type(key).__name__}.')
122
- validate_autoregressive_state_types(item, f'{path}[{key!r}]')
123
- return
124
- if isinstance(value, (list, tuple)):
125
- for i, item in enumerate(value):
126
- validate_autoregressive_state_types(item, f'{path}[{i}]')
127
- return
128
- if not isinstance(value, AUTOREGRESSIVE_STATE_LEAF_TYPES):
132
+ try:
133
+ _EngineSafeUnpickler(io.BytesIO(pickle.dumps(value))).load()
134
+ except Exception as e:
129
135
  raise AssertionError(
130
- f'autoregressive state validation failed: {path} holds a {type(value).__name__}. '
131
- f'State must be built only from numpy arrays, numbers, strings, bools and nests of '
132
- f'dicts/lists of thoseit is serialized to the platform queue on every chain step.')
136
+ f'autoregressive state validation failed: {path} is not serializable ({e}). State '
137
+ 'is serialized to the platform queue on every chain step and deserialized without '
138
+ 'the integration codeany picklable nest of builtin/numpy values works (dicts, '
139
+ 'lists, tuples, sets, numbers, strings, arrays), but user-defined classes, lambdas '
140
+ 'and open resources do not.') from e
133
141
 
134
142
 
135
143
  def autoregressive_nests_equal(a: Any, b: Any) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: code-loader
3
- Version: 1.0.196.dev0
3
+ Version: 1.0.196.dev1
4
4
  Summary:
5
5
  Home-page: https://github.com/tensorleap/code-loader
6
6
  License: MIT
@@ -21,18 +21,18 @@ code_loader/experiment_api/types.py,sha256=MY8xFARHwdVA7p4dxyhD60ShmttgTvb4qdp1o
21
21
  code_loader/experiment_api/utils.py,sha256=XZHtxge12TS4H4-8PjV3sKuhp8Ud6ojAiIzTZJEqBqc,3304
22
22
  code_loader/experiment_api/workingspace_config_utils.py,sha256=DLzXQCg4dgTV_YgaSbeTVzq-2ja_SQw4zi7LXwKL9cY,990
23
23
  code_loader/inner_leap_binder/__init__.py,sha256=koOlJyMNYzGbEsoIbXathSmQ-L38N_pEXH_HvL7beXU,99
24
- code_loader/inner_leap_binder/leapbinder.py,sha256=hTkOL-JLtIQSaCqg8EVxnWWaHMbChrnTkgrT1Tj5AxE,54488
25
- code_loader/inner_leap_binder/leapbinder_decorators.py,sha256=1zKGCW-TgC9AYv8oc_jEplIVlnpJEwqM9o2IpGxOwTI,183070
26
- code_loader/leaploader.py,sha256=k1yfKV6SBqxggUp3Yw1sNGfd50xsCaNb8-CXBsgsVY8,83459
24
+ code_loader/inner_leap_binder/leapbinder.py,sha256=yW9EBA4E7YwWoWaiVELWoSP9uWTQjnPl5PfLbmX4cFk,56617
25
+ code_loader/inner_leap_binder/leapbinder_decorators.py,sha256=GyuyffnSmkELj63q_NBAxa6Z0szP6MsaNxDodwCo-pE,183746
26
+ code_loader/leaploader.py,sha256=yWzSc-ynm_ykfup_tk8mwZX3TFq1ZW-fN18fEDXOrfc,83386
27
27
  code_loader/leaploaderbase.py,sha256=PvBU_2OQqBfJLDMsbK1s372954eYr4Y3O4r18y5S7uY,10739
28
28
  code_loader/mixpanel_tracker.py,sha256=rNwRmFifNbdUoqLQvvhhgpKczWpWiEmd8MfyJe27sxw,9131
29
29
  code_loader/plot_functions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
30
  code_loader/plot_functions/plot_functions.py,sha256=2DC-zlVaN13P4VNx5d8csgs80C6SisaeP1-Kq2LW7iM,16075
31
31
  code_loader/plot_functions/visualize.py,sha256=gsBAYYkwMh7jIpJeDMPS8G4CW-pxwx6LznoQIvi4vpo,657
32
- code_loader/utils.py,sha256=mt1V191TL-6goWPnsqjW76wffGFGNdx8xdQ0J4ZT9bU,6779
32
+ code_loader/utils.py,sha256=Grrp8OLagrVAOYjt83DDmE0CPfjE22DuOQftgqDASyU,7215
33
33
  code_loader/visualizers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  code_loader/visualizers/default_visualizers.py,sha256=onRnLE_TXfgLN4o52hQIOOhUcFexGlqJ3xSpQDVLuZM,2604
35
- code_loader-1.0.196.dev0.dist-info/LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
36
- code_loader-1.0.196.dev0.dist-info/METADATA,sha256=okTBjK87MBiyLc7eEgcuXgtl2XjCMCk_FSOtBTAnXQw,1095
37
- code_loader-1.0.196.dev0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
38
- code_loader-1.0.196.dev0.dist-info/RECORD,,
35
+ code_loader-1.0.196.dev1.dist-info/LICENSE,sha256=qIwWjdspQeSMTtnFZBC8MuT-95L02FPvzRUdWFxrwJY,1067
36
+ code_loader-1.0.196.dev1.dist-info/METADATA,sha256=qjJUAz8M-nUFhOQa1jAFKQXk2Qm-pdh4zns9jWNEglY,1095
37
+ code_loader-1.0.196.dev1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
38
+ code_loader-1.0.196.dev1.dist-info/RECORD,,