modAL 1.2.6.dev2__py3-none-any.whl → 1.2.6.dev4__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.
@@ -403,7 +403,10 @@ class TaskCommandRouterClient:
403
403
  )
404
404
  return
405
405
 
406
+ logger.debug(f"Refreshing JWT for exec with task ID {self._task_id}")
406
407
  resp = await fetch_command_router_access(self._server_client, self._task_id)
408
+ logger.debug(f"Finished refreshing JWT for exec with task ID {self._task_id}")
409
+
407
410
  # Ensure the server URL remains stable for the lifetime of this client.
408
411
  assert resp.url == self._server_url, "Task router URL changed during session"
409
412
  self._jwt = resp.jwt
@@ -434,7 +437,9 @@ class TaskCommandRouterClient:
434
437
  delay_secs = self.stream_stdio_retry_delay_secs
435
438
  delay_factor = self.stream_stdio_retry_delay_factor
436
439
  num_retries_remaining = self.stream_stdio_max_retries
437
- num_auth_retries = 0
440
+ # Flag to prevent infinite auth retries in the event that the JWT
441
+ # refresh yields an invalid JWT somehow or that the JWT is otherwise invalid.
442
+ did_auth_retry = False
438
443
 
439
444
  async def sleep_and_update_delay_and_num_retries_remaining(e: Exception):
440
445
  nonlocal delay_secs, num_retries_remaining
@@ -458,25 +463,28 @@ class TaskCommandRouterClient:
458
463
  file_descriptor=file_descriptor,
459
464
  )
460
465
 
461
- # Scope auth retry strictly to the initial send (where headers/auth are sent).
466
+ # Auth retry is scoped to a single refresh per streaming attempt. While auth metadata is
467
+ # sent on request start, UNAUTHENTICATED may sometimes surface during iteration,
468
+ # so we handle it at both send and receive boundaries.
462
469
  try:
463
470
  await s.send_message(req, end=True)
471
+ async for item in s:
472
+ # We successfully authenticated after a JWT refresh, reset the auth retry flag.
473
+ if did_auth_retry:
474
+ did_auth_retry = False
475
+ # Reset retry backoff after any successful chunk.
476
+ delay_secs = self.stream_stdio_retry_delay_secs
477
+ offset += len(item.data)
478
+ yield item
464
479
  except GRPCError as exc:
465
- if exc.status == Status.UNAUTHENTICATED and num_auth_retries < 1:
480
+ if exc.status == Status.UNAUTHENTICATED and not did_auth_retry:
466
481
  await self._refresh_jwt()
467
- num_auth_retries += 1
482
+ # Mark that we've retried authentication for this streaming attempt, to
483
+ # prevent subsequent retries.
484
+ did_auth_retry = True
468
485
  continue
469
486
  raise
470
487
 
471
- # We successfully authenticated, reset the auth retry count.
472
- num_auth_retries = 0
473
-
474
- async for item in s:
475
- # Reset retry backoff after any successful chunk.
476
- delay_secs = self.stream_stdio_retry_delay_secs
477
- offset += len(item.data)
478
- yield item
479
-
480
488
  # We successfully streamed all output.
481
489
  return
482
490
  except GRPCError as e:
modal/client.pyi CHANGED
@@ -32,7 +32,7 @@ class _Client:
32
32
  server_url: str,
33
33
  client_type: int,
34
34
  credentials: typing.Optional[tuple[str, str]],
35
- version: str = "1.2.6.dev2",
35
+ version: str = "1.2.6.dev4",
36
36
  ):
37
37
  """mdmd:hidden
38
38
  The Modal client object is not intended to be instantiated directly by users.
@@ -163,7 +163,7 @@ class Client:
163
163
  server_url: str,
164
164
  client_type: int,
165
165
  credentials: typing.Optional[tuple[str, str]],
166
- version: str = "1.2.6.dev2",
166
+ version: str = "1.2.6.dev4",
167
167
  ):
168
168
  """mdmd:hidden
169
169
  The Modal client object is not intended to be instantiated directly by users.
modal/functions.pyi CHANGED
@@ -409,7 +409,7 @@ class Function(
409
409
 
410
410
  _call_generator: ___call_generator_spec[typing_extensions.Self]
411
411
 
412
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
412
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
413
413
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
414
414
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
415
415
  ...
@@ -418,7 +418,7 @@ class Function(
418
418
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
419
419
  ...
420
420
 
421
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
421
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
422
422
 
423
423
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
424
424
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -445,7 +445,7 @@ class Function(
445
445
  """
446
446
  ...
447
447
 
448
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
448
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
449
449
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
450
450
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
451
451
 
@@ -469,7 +469,7 @@ class Function(
469
469
  ...
470
470
 
471
471
  _experimental_spawn: ___experimental_spawn_spec[
472
- modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
472
+ modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
473
473
  ]
474
474
 
475
475
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -478,7 +478,7 @@ class Function(
478
478
 
479
479
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
480
480
 
481
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
481
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
482
482
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
483
483
  """Calls the function with the given arguments, without waiting for the results.
484
484
 
@@ -499,7 +499,7 @@ class Function(
499
499
  """
500
500
  ...
501
501
 
502
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
502
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
503
503
 
504
504
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
505
505
  """Return the inner Python object wrapped by this Modal Function."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.6.dev2
3
+ Version: 1.2.6.dev4
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -26,7 +26,7 @@ modal/app.pyi,sha256=nI_Tibj-rI5I7dJFJnczbHvcmVzCvvCih0tMzoQlr_Y,50835
26
26
  modal/billing.py,sha256=zmQ3bcCJlwa4KD1IA_QgdWpm1pn13c-7qfy79iEauYI,195
27
27
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
28
28
  modal/client.py,sha256=k1CqQ_0t4-vHylkqYZgYCnClg-FT0QrTPAKivL01A_k,14354
29
- modal/client.pyi,sha256=Tc3JwKQ9lm4komj2_-aiTLXjyHFIJmKfwhmqz7C7DT0,13146
29
+ modal/client.pyi,sha256=5Jwlh9gkgM1TCBe1LuIA4tAQio2D7I8v2TMg09H5kKo,13146
30
30
  modal/cloud_bucket_mount.py,sha256=cvVj5Rt4nDHoQbTneoJPTaAcnl_Ab_mrsjnnzd0v9OI,6013
31
31
  modal/cloud_bucket_mount.pyi,sha256=vXusGMOUk9HjCs4lhxteuCK4xLQclPfugGWKo7LECEA,7539
32
32
  modal/cls.py,sha256=4T__mtYlQUnkatif1Wnr8Ld9DN_ySydHj2UwhRg89ok,41984
@@ -43,7 +43,7 @@ modal/file_io.py,sha256=Whs3QSl3pQbSoLzSRIIWfK4XyP-kwgyZmWgx3bhJ9u0,20933
43
43
  modal/file_io.pyi,sha256=xtO6Glf_BFwDE7QiQQo24QqcMf_Vv-iz7WojcGVlLBU,15932
44
44
  modal/file_pattern_matcher.py,sha256=A_Kdkej6q7YQyhM_2-BvpFmPqJ0oHb54B6yf9VqvPVE,8116
45
45
  modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
46
- modal/functions.pyi,sha256=iaFyXiAZEdzd2e7vwB5sLzoLB1kSWCQrIiqmTaczFgQ,38111
46
+ modal/functions.pyi,sha256=Gyb8fhedU4BlEXIlgSBdDpQKNL-QIYvRxuf50WrN04k,38111
47
47
  modal/gpu.py,sha256=Fe5ORvVPDIstSq1xjmM6OoNgLYFWvogP9r5BgmD3hYg,6769
48
48
  modal/image.py,sha256=Pi1tpi41rklU-7PvXyJxPBYKdG3FcW-H_2pWjjeLYzs,108165
49
49
  modal/image.pyi,sha256=grkqK9BfyKDXmJYNme-lGa0O3u7pOU5GifyLOAhG3sc,78202
@@ -116,7 +116,7 @@ modal/_utils/package_utils.py,sha256=LcL2olGN4xaUzu2Tbv-C-Ft9Qp6bsLxEfETOAVd-mjU
116
116
  modal/_utils/pattern_utils.py,sha256=ZUffaECfe2iYBhH6cvCB-0-UWhmEBTZEl_TwG_So3ag,6714
117
117
  modal/_utils/rand_pb_testing.py,sha256=mmVPk1rZldHwHZx0DnHTuHQlRLAiiAYdxjwEJpxvT9c,3900
118
118
  modal/_utils/shell_utils.py,sha256=hWHzv730Br2Xyj6cGPiMZ-198Z3RZuOu3pDXhFSZ22c,2157
119
- modal/_utils/task_command_router_client.py,sha256=siFolGus_vehtY2D6MywXZrV5IJbgz5zhbU7xYKmMf0,22220
119
+ modal/_utils/task_command_router_client.py,sha256=d50gv9vovjxeyoC9pSr5f_2qcaQZrrJ83drbWIKyZ68,23004
120
120
  modal/_utils/time_utils.py,sha256=43tpFVwT7ykOjlETIFLVt9auMsRZqYYRYBEKxGCrRSA,1212
121
121
  modal/_vendor/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
122
122
  modal/_vendor/a2wsgi_wsgi.py,sha256=Q1AsjpV_Q_vzQsz_cSqmP9jWzsGsB-ARFU6vpQYml8k,21878
@@ -160,7 +160,7 @@ modal/experimental/__init__.py,sha256=GOuo3t4-Le0ZVddiHIOw0MQxkdTJge-bVbhDvgnblm
160
160
  modal/experimental/flash.py,sha256=GJZwSr-4OHlP6gCIZdcbsSb8D8WX9xJwMphjwcfsRoU,31262
161
161
  modal/experimental/flash.pyi,sha256=q4a3mdgL0jzATPL2tS7LpuyulYzF1vUSE453S9xc8n8,16965
162
162
  modal/experimental/ipython.py,sha256=TrCfmol9LGsRZMeDoeMPx3Hv3BFqQhYnmD_iH0pqdhk,2904
163
- modal-1.2.6.dev2.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
163
+ modal-1.2.6.dev4.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
164
164
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
165
165
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
166
166
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -182,10 +182,10 @@ modal_proto/task_command_router_pb2.py,sha256=2Y8wRvnq4_7RiQ-5ZAPIgXaxrHv_MXzgS5
182
182
  modal_proto/task_command_router_pb2.pyi,sha256=yI4tFteErLlonFA2HyaWtONfhY_VGSe7KCWFIspvmkE,16323
183
183
  modal_proto/task_command_router_pb2_grpc.py,sha256=q2VWfK2-4uDcPaPLhAqPZkgxhuQRYnWmwUcnEbPbGuk,13895
184
184
  modal_proto/task_command_router_pb2_grpc.pyi,sha256=e1MM10VGUgDkm041v53QeTglGQPzTpA0FUsFtezjp74,4536
185
- modal_version/__init__.py,sha256=0QNcTD7KqDn8wXhqDtaFw0mLr08Tk8-kvfenGuUrn_I,120
185
+ modal_version/__init__.py,sha256=mFsXOevjOHo5RGkFcC7zBVIU5LVwRggi9DX1Qy9VUy4,120
186
186
  modal_version/__main__.py,sha256=nLUbubxKx7GXNzJIkTB7M7_u8080LyLM0IS0rzFkC_c,119
187
- modal-1.2.6.dev2.dist-info/METADATA,sha256=PH9hhq8dY3eqmjXwQQ6B0UtxjlMIKIt7ZoeJhYZtbw0,2489
188
- modal-1.2.6.dev2.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
189
- modal-1.2.6.dev2.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
190
- modal-1.2.6.dev2.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
191
- modal-1.2.6.dev2.dist-info/RECORD,,
187
+ modal-1.2.6.dev4.dist-info/METADATA,sha256=Usp7oYLxYLWkv6L6hfIeRibBWYaRFUv8XXh_UOmKYng,2489
188
+ modal-1.2.6.dev4.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
189
+ modal-1.2.6.dev4.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
190
+ modal-1.2.6.dev4.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
191
+ modal-1.2.6.dev4.dist-info/RECORD,,
modal_version/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
  """Supplies the current version of the modal client library."""
3
3
 
4
- __version__ = "1.2.6.dev2"
4
+ __version__ = "1.2.6.dev4"