code-loader 1.0.187.dev0__tar.gz → 1.0.188__tar.gz

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.
Files changed (37) hide show
  1. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/PKG-INFO +1 -1
  2. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/inner_leap_binder/leapbinder.py +12 -16
  3. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/inner_leap_binder/leapbinder_decorators.py +65 -1
  4. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/pyproject.toml +1 -1
  5. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/LICENSE +0 -0
  6. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/README.md +0 -0
  7. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/__init__.py +0 -0
  8. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/contract/__init__.py +0 -0
  9. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/contract/datasetclasses.py +0 -0
  10. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/contract/enums.py +0 -0
  11. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/contract/exceptions.py +0 -0
  12. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/contract/mapping.py +0 -0
  13. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/contract/responsedataclasses.py +0 -0
  14. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/contract/sim_config.py +0 -0
  15. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/contract/visualizer_classes.py +0 -0
  16. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/default_losses.py +0 -0
  17. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/default_metrics.py +0 -0
  18. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/experiment_api/__init__.py +0 -0
  19. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/experiment_api/api.py +0 -0
  20. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/experiment_api/cli_config_utils.py +0 -0
  21. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/experiment_api/client.py +0 -0
  22. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/experiment_api/epoch.py +0 -0
  23. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/experiment_api/experiment.py +0 -0
  24. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/experiment_api/experiment_context.py +0 -0
  25. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/experiment_api/types.py +0 -0
  26. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/experiment_api/utils.py +0 -0
  27. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/experiment_api/workingspace_config_utils.py +0 -0
  28. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/inner_leap_binder/__init__.py +0 -0
  29. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/leaploader.py +0 -0
  30. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/leaploaderbase.py +0 -0
  31. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/mixpanel_tracker.py +0 -0
  32. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/plot_functions/__init__.py +0 -0
  33. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/plot_functions/plot_functions.py +0 -0
  34. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/plot_functions/visualize.py +0 -0
  35. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/utils.py +0 -0
  36. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/visualizers/__init__.py +0 -0
  37. {code_loader-1.0.187.dev0 → code_loader-1.0.188}/code_loader/visualizers/default_visualizers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: code-loader
3
- Version: 1.0.187.dev0
3
+ Version: 1.0.188
4
4
  Summary:
5
5
  License: MIT
6
6
  Author: dorhar
@@ -1,6 +1,6 @@
1
1
 
2
2
  import inspect
3
- from typing import Callable, List, Optional, Dict, Any, Type, Union
3
+ from typing import Callable, List, Optional, Dict, Any, Type, Union, get_args
4
4
 
5
5
  import numpy as np
6
6
  import numpy.typing as npt
@@ -32,21 +32,17 @@ mapping_runtime_mode_env_var_mame = '__MAPPING_RUNTIME_MODE__'
32
32
 
33
33
 
34
34
  def _stringized_annotation_type_name(annotation: Any) -> Optional[str]:
35
- """Return the bare type name when ``annotation`` is a *stringized* annotation
36
- (e.g. produced by ``from __future__ import annotations`` or a quoted hint),
37
- otherwise ``None``. code_loader inspects raw annotations expecting real classes,
38
- so a stringized annotation silently breaks type detection."""
35
+ """Bare type name if ``annotation`` is a stringized annotation (from ``from __future__
36
+ import annotations`` or a quoted hint), else ``None``. code_loader inspects raw
37
+ annotations expecting real classes, so stringization silently breaks type detection."""
39
38
  if isinstance(annotation, str):
40
39
  return annotation.split("[")[0].strip().rsplit(".", 1)[-1]
41
40
  return None
42
41
 
43
42
 
44
- def _reject_stringized_sample_preprocess_response(function: Callable, arg_name: str, annotation: Any) -> None:
45
- """Fail fast when a ``SamplePreprocessResponse`` argument was stringized. Otherwise
46
- ``arg_type == SamplePreprocessResponse`` silently evaluates to ``False`` and the
47
- argument is mis-wired as a regular input — a bug that only surfaces on the platform.
48
- Fires only on the actual damage (a stringized SamplePreprocessResponse), not merely
49
- because the file uses future annotations."""
43
+ def _reject_stringized_sample_preprocess_response(function: Callable[..., Any], arg_name: str, annotation: Any) -> None:
44
+ """Fail fast on a stringized SamplePreprocessResponse arg (otherwise it silently
45
+ mis-wires as a regular input, surfacing only on the platform)."""
50
46
  if _stringized_annotation_type_name(annotation) == SamplePreprocessResponse.__name__:
51
47
  raise Exception(
52
48
  f"Argument '{arg_name}' of function '{function.__name__}' is annotated with a string "
@@ -170,8 +166,8 @@ class LeapBinder:
170
166
 
171
167
  if visualizer_type.value not in map_leap_data_type_to_visualizer_class:
172
168
  raise Exception(
173
- f'The visualizer_type is invalid. current visualizer_type: {visualizer_type}, ' # type: ignore[attr-defined]
174
- f'should be one of : {", ".join([arg.__name__ for arg in LeapData.__args__])}')
169
+ f'The visualizer_type is invalid. current visualizer_type: {visualizer_type}, '
170
+ f'should be one of : {", ".join([arg.__name__ for arg in get_args(LeapData)])}')
175
171
 
176
172
  func_annotations = function.__annotations__
177
173
  if "return" not in func_annotations:
@@ -188,10 +184,10 @@ class LeapBinder:
188
184
  f"'from __future__ import annotations' (or a quoted return hint), which stringizes "
189
185
  f"annotations and prevents Tensorleap from detecting the visualizer type. Remove "
190
186
  f"that import (or the quotes) so the return type is a real class.")
191
- if return_type not in LeapData.__args__: # type: ignore[attr-defined]
187
+ if return_type not in get_args(LeapData):
192
188
  raise Exception(
193
- f'The return type of function {function.__name__} is invalid. current return type: {return_type}, ' # type: ignore[attr-defined]
194
- f'should be one of : {", ".join([arg.__name__ for arg in LeapData.__args__])}')
189
+ f'The return type of function {function.__name__} is invalid. current return type: {return_type}, '
190
+ f'should be one of : {", ".join([arg.__name__ for arg in get_args(LeapData)])}')
195
191
 
196
192
  expected_return_type = map_leap_data_type_to_visualizer_class[visualizer_type.value]
197
193
  if not issubclass(return_type, expected_return_type):
@@ -223,6 +223,59 @@ def _add_mapping_connections(connects_to, arg_names, node_mapping_type, name):
223
223
  _add_mapping_connection(user_unique_name, connection_destinations, arg_names, name, node_mapping_type)
224
224
 
225
225
 
226
+ def _require_sample_preprocess_response_supplied(user_function: Callable, args: tuple, kwargs: dict) -> None:
227
+ """A SamplePreprocessResponse argument is auto-injected by the platform / check_dataset
228
+ but NOT inside integration_test, where the author calls the function directly. Fail fast
229
+ with an actionable message instead of a raw 'missing argument' TypeError."""
230
+ spr_arg_name = None
231
+ for arg_name, arg_type in inspect.getfullargspec(user_function).annotations.items():
232
+ if arg_type == SamplePreprocessResponse:
233
+ spr_arg_name = arg_name
234
+ break
235
+ if spr_arg_name is None:
236
+ return
237
+ signature = inspect.signature(user_function)
238
+ param = signature.parameters.get(spr_arg_name)
239
+ # Only enforce a REQUIRED SamplePreprocessResponse arg. If it has a default it is
240
+ # optional and the function may legitimately be called with or without it.
241
+ if param is None or param.default is not inspect.Parameter.empty:
242
+ return
243
+ try:
244
+ supplied = signature.bind_partial(*args, **kwargs).arguments
245
+ except TypeError:
246
+ return
247
+ if spr_arg_name not in supplied:
248
+ raise Exception(
249
+ f"'{user_function.__name__}' takes a SamplePreprocessResponse argument "
250
+ f"'{spr_arg_name}', but it was not provided. Inside integration_test pass it "
251
+ f"explicitly, e.g. {user_function.__name__}(..., "
252
+ f"SamplePreprocessResponse(sample_id, preprocess)).")
253
+
254
+
255
+ def _warn_loss_inputs_not_wired_to_model() -> None:
256
+ """Warn when a custom loss consumes an input encoder the model is not given.
257
+
258
+ Such an input stays a plain NodeMappingType.Input (model-fed inputs get re-tagged to
259
+ Input0/Input1/...) and crashes the platform model build. Losses only — the same pattern
260
+ in a metric or visualizer builds fine."""
261
+ loss_types = {NodeMappingType.Loss, NodeMappingType.CustomLoss}
262
+ for connection in leap_binder.mapping_connections:
263
+ if connection.node is None or connection.node.type not in loss_types:
264
+ continue
265
+ for arg_name, source in (connection.node_inputs or {}).items():
266
+ if source is not None and source.type == NodeMappingType.Input:
267
+ input_name = source.name or arg_name
268
+ store_general_warning(
269
+ key=("loss_input_not_wired_to_model", connection.node.name, input_name),
270
+ message=(
271
+ f"Custom loss '{connection.node.name}' consumes input '{input_name}', "
272
+ f"which is not fed to the model. This is not supported: the platform builds "
273
+ f"losses into the model graph, so an input the model does not take has no "
274
+ f"source there and the model build fails. Instead, carry this data in the "
275
+ f"ground truth and derive it inside the loss (not as a separate input "
276
+ f"encoder)."))
277
+
278
+
226
279
  def tensorleap_integration_test():
227
280
  def decorating_function(integration_test_function: Callable):
228
281
  leap_binder.integration_test_func = integration_test_function
@@ -279,6 +332,8 @@ def tensorleap_integration_test():
279
332
  finally:
280
333
  _called_from_inside_tl_integration_test_decorator = False
281
334
 
335
+ _warn_loss_inputs_not_wired_to_model()
336
+
282
337
  leap_binder.check()
283
338
 
284
339
  return inner
@@ -822,6 +877,7 @@ def tensorleap_custom_metric(name: str,
822
877
  _called_from_inside_tl_decorator += 1
823
878
 
824
879
  try:
880
+ _require_sample_preprocess_response_supplied(user_function, args, kwargs)
825
881
  result = user_function(*args, **kwargs)
826
882
  finally:
827
883
  _called_from_inside_tl_decorator -= 1
@@ -1031,6 +1087,7 @@ def tensorleap_custom_instances_metric(name: str,
1031
1087
  _called_from_inside_tl_decorator += 1
1032
1088
 
1033
1089
  try:
1090
+ _require_sample_preprocess_response_supplied(user_function, args, kwargs)
1034
1091
  result = user_function(*args, **kwargs)
1035
1092
  finally:
1036
1093
  _called_from_inside_tl_decorator -= 1
@@ -1383,6 +1440,7 @@ def tensorleap_custom_visualizer(name: str, visualizer_type: LeapDataType,
1383
1440
  _called_from_inside_tl_decorator += 1
1384
1441
 
1385
1442
  try:
1443
+ _require_sample_preprocess_response_supplied(user_function, args, kwargs)
1386
1444
  result = user_function(*args, **kwargs)
1387
1445
  finally:
1388
1446
  _called_from_inside_tl_decorator -= 1
@@ -1929,8 +1987,13 @@ def tensorleap_input_encoder(name: str, channel_dim=_UNSET, model_input_index=No
1929
1987
 
1930
1988
  )
1931
1989
 
1990
+ # channel_dim is the channel axis in the BATCHED tensor (batch = axis 0):
1991
+ # 1 for channel-first (NCHW), -1 for channel-last (NHWC); 0 is invalid.
1932
1992
  if channel_dim <= 0 and channel_dim != -1:
1933
- raise Exception(f"Channel dim for input {name} is expected to be either -1 or positive")
1993
+ raise Exception(
1994
+ f"Invalid channel_dim ({channel_dim}) for input '{name}'. channel_dim is the "
1995
+ f"channel axis in the batched tensor (batch = axis 0), so 0 is invalid. Use 1 "
1996
+ f"for channel-first (NCHW) and -1 for channel-last (NHWC).")
1934
1997
 
1935
1998
  def _validate_input_args(sample_id: Union[int, str], preprocess_response: PreprocessResponse):
1936
1999
  assert type(sample_id) == preprocess_response.sample_id_type, \
@@ -2149,6 +2212,7 @@ def tensorleap_custom_loss(name: str, connects_to=None):
2149
2212
  _called_from_inside_tl_decorator += 1
2150
2213
 
2151
2214
  try:
2215
+ _require_sample_preprocess_response_supplied(user_function, args, kwargs)
2152
2216
  result = user_function(*args, **kwargs)
2153
2217
  finally:
2154
2218
  _called_from_inside_tl_decorator -= 1
@@ -1,7 +1,7 @@
1
1
  [tool.poetry]
2
2
  name = "code-loader"
3
3
 
4
- version = "1.0.187.dev0"
4
+ version = "1.0.188"
5
5
  description = ""
6
6
  authors = ["dorhar <doron.harnoy@tensorleap.ai>"]
7
7
  license = "MIT"