arraybridge 0.3.4__py3-none-any.whl → 0.3.6__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.
arraybridge/__init__.py CHANGED
@@ -10,7 +10,7 @@ attribute access instead of at import time. Declaration-only consumers
10
10
  NumPy/numcodecs import cost.
11
11
  """
12
12
 
13
- __version__ = "0.3.4"
13
+ __version__ = "0.3.6"
14
14
 
15
15
  _LAZY_EXPORTS: dict[str, str] = {
16
16
  "MemoryType": ".types",
arraybridge/decorators.py CHANGED
@@ -82,11 +82,13 @@ class DtypeConversionConfig(RuntimeParameterDeclarationABC):
82
82
  return DtypeConversionConfig
83
83
 
84
84
  @classmethod
85
- def parameter(cls) -> inspect.Parameter:
85
+ def parameter(
86
+ cls, *, default_value: "DtypeConversionConfig | None" = None
87
+ ) -> inspect.Parameter:
86
88
  return inspect.Parameter(
87
89
  cls.require_parameter_name(),
88
90
  inspect.Parameter.KEYWORD_ONLY,
89
- default=cls.default_value(),
91
+ default=cls.default_value() if default_value is None else default_value,
90
92
  annotation=cls.annotation_type(),
91
93
  )
92
94
 
@@ -264,12 +266,30 @@ class KeywordOnlySignatureExtension:
264
266
  return len(parameters)
265
267
 
266
268
 
267
- # Thread-local storage for GPU streams and contexts
268
- _thread_gpu_contexts = threading.local()
269
+ class ThreadGPUContextStorage(threading.local):
270
+ """Declare the existing native thread-local context slot and its type."""
271
+
272
+ context: "ThreadGPUContext | None" = None
269
273
 
270
274
 
271
275
  class ThreadGPUContext:
272
- """Thread-local streams keyed by framework-local device identity."""
276
+ """Runtime-owned thread-local streams keyed by framework/device identity.
277
+
278
+ Keep the runtime handle on its importable owner, not in decorator function
279
+ globals. A retained, unpublished decorated callable is serialized by value;
280
+ its durable closure must not pull a ``threading.local`` into history.
281
+ """
282
+
283
+ _contexts: ClassVar[ThreadGPUContextStorage] = ThreadGPUContextStorage()
284
+
285
+ @classmethod
286
+ def current(cls) -> "ThreadGPUContext":
287
+ """Return this thread's runtime context without serializing its handle."""
288
+ context = cls._contexts.context
289
+ if context is None:
290
+ context = cls()
291
+ cls._contexts.context = context
292
+ return context
273
293
 
274
294
  def __init__(self):
275
295
  self._streams: dict[tuple[MemoryType, int], Any] = {}
@@ -301,13 +321,6 @@ class ThreadGPUContext:
301
321
  return device_id, self._streams[key]
302
322
 
303
323
 
304
- def _get_thread_gpu_context():
305
- """Get or create thread-local GPU context."""
306
- if not hasattr(_thread_gpu_contexts, "context"):
307
- _thread_gpu_contexts.context = ThreadGPUContext()
308
- return _thread_gpu_contexts.context
309
-
310
-
311
324
  def memory_types(
312
325
  input_type: str | MemoryType,
313
326
  output_type: str | MemoryType,
@@ -352,6 +365,7 @@ def wrap_dtype_preserving_callable(
352
365
  mem_type: MemoryType,
353
366
  *,
354
367
  slice_by_slice_default: bool = False,
368
+ dtype_config_default: DtypeConversionConfig | None = None,
355
369
  ):
356
370
  """
357
371
  Return a callable with ArrayBridge dtype and slice controls.
@@ -363,18 +377,25 @@ def wrap_dtype_preserving_callable(
363
377
  input_memory_type = MemoryType(MemoryContractAttribute.INPUT.read(func, mem_type.value))
364
378
  output_memory_type = MemoryType(MemoryContractAttribute.OUTPUT.read(func, mem_type.value))
365
379
  scale_func = output_memory_type.scale_dtype
380
+ default_dtype_config = (
381
+ DtypeConversionConfig.default_value()
382
+ if dtype_config_default is None
383
+ else dtype_config_default
384
+ )
385
+ if not isinstance(default_dtype_config, DtypeConversionConfig):
386
+ raise TypeError("Callable dtype default must be a DtypeConversionConfig.")
366
387
 
367
388
  @functools.wraps(func)
368
389
  def dtype_wrapper(image, *args, **kwargs):
369
390
  # Pipeline runtimes may inject dtype_config; direct calls use the same
370
- # preserve-input default explicitly.
391
+ # callable-owned default explicitly (preserve-input when undeclared).
371
392
  slice_by_slice = kwargs.pop(
372
393
  SliceBySliceRuntimeParameter.require_parameter_name(),
373
394
  slice_by_slice_default,
374
395
  )
375
396
  dtype_config: DtypeConversionConfig = kwargs.pop(
376
397
  DtypeConversionConfig.require_parameter_name(),
377
- DtypeConversionConfig.default_value(),
398
+ default_dtype_config,
378
399
  )
379
400
  dtype_conversion = dtype_config.default_dtype_conversion
380
401
 
@@ -424,7 +445,7 @@ def wrap_dtype_preserving_callable(
424
445
  )
425
446
  )
426
447
  dtype_signature = KeywordOnlySignatureExtension(dtype_signature).with_parameter(
427
- DtypeConversionConfig.parameter()
448
+ DtypeConversionConfig.parameter(default_value=default_dtype_config)
428
449
  )
429
450
  setattr(dtype_wrapper, "__signature__", dtype_signature)
430
451
 
@@ -461,7 +482,7 @@ def _create_gpu_wrapper(func, mem_type: MemoryType, oom_recovery: bool):
461
482
  # Check if GPU is available for this framework
462
483
  if framework is not None and mem_type.available_device_ids(framework):
463
484
  # Get thread-local context
464
- ctx = _get_thread_gpu_context()
485
+ ctx = ThreadGPUContext.current()
465
486
 
466
487
  device_id, stream = ctx.stream_for(mem_type, framework)
467
488
 
@@ -510,6 +531,7 @@ def _create_memory_decorator(mem_type: MemoryType):
510
531
  oom_recovery=True,
511
532
  contract=None,
512
533
  slice_by_slice_default=False,
534
+ dtype_config_default: DtypeConversionConfig | None = None,
513
535
  ):
514
536
  """
515
537
  Decorator for {mem_type} memory type functions.
@@ -521,6 +543,8 @@ def _create_memory_decorator(mem_type: MemoryType):
521
543
  oom_recovery: Enable automatic OOM recovery (default: True)
522
544
  contract: Optional validation function for outputs
523
545
  slice_by_slice_default: Default for the decorator-owned slice control
546
+ dtype_config_default: Callable-owned dtype policy for direct calls;
547
+ explicit runtime dtype_config still overrides this default.
524
548
 
525
549
  Returns:
526
550
  Decorated function with memory type metadata and dtype preservation
@@ -538,6 +562,7 @@ def _create_memory_decorator(mem_type: MemoryType):
538
562
  func,
539
563
  mem_type,
540
564
  slice_by_slice_default=slice_by_slice_default,
565
+ dtype_config_default=dtype_config_default,
541
566
  )
542
567
 
543
568
  # Apply GPU wrapper if this is a GPU memory type
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arraybridge
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: Unified API for NumPy, CuPy, PyTorch, TensorFlow, JAX, and pyclesperanto with automatic memory type conversion
5
5
  Project-URL: Homepage, https://github.com/OpenHCSDev/arraybridge
6
6
  Project-URL: Documentation, https://arraybridge.readthedocs.io
@@ -36,6 +36,7 @@ Requires-Dist: cupy>=10.0; extra == 'cupy'
36
36
  Provides-Extra: dev
37
37
  Requires-Dist: black>=23.0; extra == 'dev'
38
38
  Requires-Dist: build>=1.0; extra == 'dev'
39
+ Requires-Dist: dill>=0.3.8; extra == 'dev'
39
40
  Requires-Dist: mypy>=1.0; extra == 'dev'
40
41
  Requires-Dist: packaging>=23.0; extra == 'dev'
41
42
  Requires-Dist: pytest-cov>=4.0; extra == 'dev'
@@ -1,10 +1,10 @@
1
- arraybridge/__init__.py,sha256=a27ES1ItSBsT77vP3f12ILNu0MzrvQfcpCBVDXwi-Sg,3184
1
+ arraybridge/__init__.py,sha256=rhst2HuRsZE1bp4f23SjK6cTHNpjQUAsSnqgh9R6QUA,3184
2
2
  arraybridge/array_geometry.py,sha256=CopfGmFVKwV5l-lOTImhm1YVh18weUng0ReEF58qlPI,1689
3
3
  arraybridge/array_operations.py,sha256=fp69o9BYcCzfhmLcWrH68SVGSfPL3nb6Cty-Amd_oQM,13082
4
4
  arraybridge/array_payload.py,sha256=6_jJwPRya0MHrUNSA9kXPlTISpLa0G74rqDValGXKx4,1079
5
5
  arraybridge/converters.py,sha256=q44E6Pg1rJz8yq6WQIPLOZPv1lqqcUcEG4Gh4D5M1_o,2649
6
6
  arraybridge/converters_registry.py,sha256=_hHtbd-3SMIKUw8fvNu3twPWJY7qHyNyQ_NhVp3eSHQ,5647
7
- arraybridge/decorators.py,sha256=mYIxBbCuWQVN8dhAE2M7i2Yo0KbcMyY4MZmsfGXGm-0,19975
7
+ arraybridge/decorators.py,sha256=KPm_R3C9TMHvNARf3vKAZQY-3gKSlvYrimxBQMyI5A4,21271
8
8
  arraybridge/dtype_scaling.py,sha256=KDgYyCvxgsQXLJJl_bs5nH46zQimsA7c1Olv6xJ2z1o,831
9
9
  arraybridge/exceptions.py,sha256=fhej5ZS39QcBFWzNeNYjoqeLVnOgwwp-cWIme25jyuo,752
10
10
  arraybridge/framework_config.py,sha256=5yTTt9NWcwzKDMXbc-fqCdiFl-piKOt3SUnxmRXXCQk,744
@@ -15,7 +15,7 @@ arraybridge/slice_processing.py,sha256=5R10OmHW8BSbJFwT1URd2Sz3P15o5EhaI2VnVUUIv
15
15
  arraybridge/stack_utils.py,sha256=FVikN0XG-Xryw-1p9MaSRUTZet7kLcy0gTEERSjc9r4,6915
16
16
  arraybridge/types.py,sha256=e5R2DVflQAjXrfTk033KRdiiwoip3IQAvgDCYUjPdiQ,35807
17
17
  arraybridge/utils.py,sha256=RqThtYScPEOEQG491dA8b_VvUTjgcFhwLbh0erQu5Ns,4833
18
- arraybridge-0.3.4.dist-info/METADATA,sha256=jq9-ULcNwbZo7cMtZlhhqj4F2QlV-8PrpdM1Tv0tLCE,5275
19
- arraybridge-0.3.4.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
20
- arraybridge-0.3.4.dist-info/licenses/LICENSE,sha256=xagEoeTAj1WT64RmyR3E6HH-eTGdgXN6gqPMUUt7L_Y,1070
21
- arraybridge-0.3.4.dist-info/RECORD,,
18
+ arraybridge-0.3.6.dist-info/METADATA,sha256=uOSPAN8b0foxSeEJslCFyv0VhJR7suC1TWL9RBCrLTo,5318
19
+ arraybridge-0.3.6.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
20
+ arraybridge-0.3.6.dist-info/licenses/LICENSE,sha256=xagEoeTAj1WT64RmyR3E6HH-eTGdgXN6gqPMUUt7L_Y,1070
21
+ arraybridge-0.3.6.dist-info/RECORD,,