modal 1.1.2.dev6__tar.gz → 1.1.2.dev7__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.
Potentially problematic release.
This version of modal might be problematic. Click here for more details.
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/PKG-INFO +1 -1
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/client.pyi +2 -2
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/parallel_map.py +202 -101
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/parallel_map.pyi +63 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal.egg-info/PKG-INFO +1 -1
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_version/__init__.py +1 -1
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/LICENSE +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/README.md +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/__init__.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/__main__.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_clustered_functions.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_clustered_functions.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_container_entrypoint.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_functions.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_ipython.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_location.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_object.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_output.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_partial_function.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_pty.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_resolver.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_resources.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_runtime/__init__.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_runtime/asgi.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_runtime/container_io_manager.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_runtime/container_io_manager.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_runtime/execution_context.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_runtime/execution_context.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_runtime/gpu_memory_snapshot.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_runtime/telemetry.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_runtime/user_code_imports.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_serialization.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_traceback.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_tunnel.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_tunnel.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_type_manager.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/__init__.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/app_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/async_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/auth_token_manager.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/blob_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/bytes_io_segment_payload.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/deprecation.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/docker_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/function_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/git_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/grpc_testing.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/grpc_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/hash_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/http_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/jwt_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/logger.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/mount_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/name_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/package_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/pattern_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/rand_pb_testing.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/shell_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_utils/time_utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_vendor/__init__.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_vendor/a2wsgi_wsgi.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_vendor/cloudpickle.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_vendor/tblib.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/_watcher.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/app.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/app.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/builder/2023.12.312.txt +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/builder/2023.12.txt +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/builder/2024.04.txt +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/builder/2024.10.txt +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/builder/2025.06.txt +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/builder/PREVIEW.txt +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/builder/README.md +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/builder/base-images.json +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/call_graph.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/__init__.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/_download.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/_traceback.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/app.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/cluster.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/config.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/container.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/dict.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/entry_point.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/environment.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/import_refs.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/launch.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/network_file_system.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/profile.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/programs/__init__.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/programs/run_jupyter.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/programs/vscode.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/queues.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/run.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/secret.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/token.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/utils.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cli/volume.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/client.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cloud_bucket_mount.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cloud_bucket_mount.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cls.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/cls.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/config.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/container_process.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/container_process.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/dict.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/dict.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/environments.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/environments.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/exception.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/experimental/__init__.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/experimental/flash.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/experimental/flash.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/experimental/ipython.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/file_io.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/file_io.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/file_pattern_matcher.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/functions.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/functions.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/gpu.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/image.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/image.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/io_streams.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/io_streams.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/mount.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/mount.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/network_file_system.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/network_file_system.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/object.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/object.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/output.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/partial_function.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/partial_function.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/proxy.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/proxy.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/py.typed +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/queue.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/queue.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/retries.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/runner.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/runner.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/running_app.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/sandbox.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/sandbox.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/schedule.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/scheduler_placement.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/secret.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/secret.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/serving.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/serving.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/snapshot.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/snapshot.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/stream_type.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/token_flow.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/token_flow.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/volume.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal/volume.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal.egg-info/SOURCES.txt +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal.egg-info/dependency_links.txt +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal.egg-info/entry_points.txt +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal.egg-info/requires.txt +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal.egg-info/top_level.txt +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_docs/__init__.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_docs/gen_cli_docs.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_docs/gen_reference_docs.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_docs/mdmd/__init__.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_docs/mdmd/mdmd.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_docs/mdmd/signatures.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/__init__.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/api.proto +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/api_grpc.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/api_pb2.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/api_pb2.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/api_pb2_grpc.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/api_pb2_grpc.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/modal_api_grpc.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/modal_options_grpc.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/options.proto +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/options_grpc.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/options_pb2.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/options_pb2.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/options_pb2_grpc.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/options_pb2_grpc.pyi +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_proto/py.typed +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/modal_version/__main__.py +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/pyproject.toml +0 -0
- {modal-1.1.2.dev6 → modal-1.1.2.dev7}/setup.cfg +0 -0
|
@@ -33,7 +33,7 @@ class _Client:
|
|
|
33
33
|
server_url: str,
|
|
34
34
|
client_type: int,
|
|
35
35
|
credentials: typing.Optional[tuple[str, str]],
|
|
36
|
-
version: str = "1.1.2.
|
|
36
|
+
version: str = "1.1.2.dev7",
|
|
37
37
|
):
|
|
38
38
|
"""mdmd:hidden
|
|
39
39
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -164,7 +164,7 @@ class Client:
|
|
|
164
164
|
server_url: str,
|
|
165
165
|
client_type: int,
|
|
166
166
|
credentials: typing.Optional[tuple[str, str]],
|
|
167
|
-
version: str = "1.1.2.
|
|
167
|
+
version: str = "1.1.2.dev7",
|
|
168
168
|
):
|
|
169
169
|
"""mdmd:hidden
|
|
170
170
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -86,6 +86,180 @@ if typing.TYPE_CHECKING:
|
|
|
86
86
|
import modal.functions
|
|
87
87
|
|
|
88
88
|
|
|
89
|
+
class InputPreprocessor:
|
|
90
|
+
"""
|
|
91
|
+
Constructs FunctionPutInputsItem objects from the raw-input queue, and puts them in the processed-input queue.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def __init__(
|
|
95
|
+
self,
|
|
96
|
+
client: "modal.client._Client",
|
|
97
|
+
*,
|
|
98
|
+
raw_input_queue: _SynchronizedQueue,
|
|
99
|
+
processed_input_queue: asyncio.Queue,
|
|
100
|
+
function: "modal.functions._Function",
|
|
101
|
+
created_callback: Callable[[int], None],
|
|
102
|
+
done_callback: Callable[[], None],
|
|
103
|
+
):
|
|
104
|
+
self.client = client
|
|
105
|
+
self.function = function
|
|
106
|
+
self.inputs_created = 0
|
|
107
|
+
self.raw_input_queue = raw_input_queue
|
|
108
|
+
self.processed_input_queue = processed_input_queue
|
|
109
|
+
self.created_callback = created_callback
|
|
110
|
+
self.done_callback = done_callback
|
|
111
|
+
|
|
112
|
+
async def input_iter(self):
|
|
113
|
+
while 1:
|
|
114
|
+
raw_input = await self.raw_input_queue.get()
|
|
115
|
+
if raw_input is None: # end of input sentinel
|
|
116
|
+
break
|
|
117
|
+
yield raw_input # args, kwargs
|
|
118
|
+
|
|
119
|
+
def create_input_factory(self):
|
|
120
|
+
async def create_input(argskwargs):
|
|
121
|
+
idx = self.inputs_created
|
|
122
|
+
self.inputs_created += 1
|
|
123
|
+
self.created_callback(self.inputs_created)
|
|
124
|
+
(args, kwargs) = argskwargs
|
|
125
|
+
return await _create_input(
|
|
126
|
+
args,
|
|
127
|
+
kwargs,
|
|
128
|
+
self.client.stub,
|
|
129
|
+
max_object_size_bytes=self.function._max_object_size_bytes,
|
|
130
|
+
idx=idx,
|
|
131
|
+
method_name=self.function._use_method_name,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return create_input
|
|
135
|
+
|
|
136
|
+
async def drain_input_generator(self):
|
|
137
|
+
# Parallelize uploading blobs
|
|
138
|
+
async with aclosing(
|
|
139
|
+
async_map_ordered(self.input_iter(), self.create_input_factory(), concurrency=BLOB_MAX_PARALLELISM)
|
|
140
|
+
) as streamer:
|
|
141
|
+
async for item in streamer:
|
|
142
|
+
await self.processed_input_queue.put(item)
|
|
143
|
+
|
|
144
|
+
# close queue iterator
|
|
145
|
+
await self.processed_input_queue.put(None)
|
|
146
|
+
self.done_callback()
|
|
147
|
+
yield
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class InputPumper:
|
|
151
|
+
"""
|
|
152
|
+
Reads inputs from a queue of FunctionPutInputsItems, and sends them to the server.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
def __init__(
|
|
156
|
+
self,
|
|
157
|
+
client: "modal.client._Client",
|
|
158
|
+
*,
|
|
159
|
+
input_queue: asyncio.Queue,
|
|
160
|
+
function: "modal.functions._Function",
|
|
161
|
+
function_call_id: str,
|
|
162
|
+
map_items_manager: Optional["_MapItemsManager"] = None,
|
|
163
|
+
):
|
|
164
|
+
self.client = client
|
|
165
|
+
self.function = function
|
|
166
|
+
self.map_items_manager = map_items_manager
|
|
167
|
+
self.input_queue = input_queue
|
|
168
|
+
self.inputs_sent = 0
|
|
169
|
+
self.function_call_id = function_call_id
|
|
170
|
+
|
|
171
|
+
async def pump_inputs(self):
|
|
172
|
+
assert self.client.stub
|
|
173
|
+
async for items in queue_batch_iterator(self.input_queue, max_batch_size=MAP_INVOCATION_CHUNK_SIZE):
|
|
174
|
+
# Add items to the manager. Their state will be SENDING.
|
|
175
|
+
if self.map_items_manager is not None:
|
|
176
|
+
await self.map_items_manager.add_items(items)
|
|
177
|
+
request = api_pb2.FunctionPutInputsRequest(
|
|
178
|
+
function_id=self.function.object_id,
|
|
179
|
+
inputs=items,
|
|
180
|
+
function_call_id=self.function_call_id,
|
|
181
|
+
)
|
|
182
|
+
logger.debug(
|
|
183
|
+
f"Pushing {len(items)} inputs to server. Num queued inputs awaiting"
|
|
184
|
+
f" push is {self.input_queue.qsize()}. "
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
resp = await self._send_inputs(self.client.stub.FunctionPutInputs, request)
|
|
188
|
+
self.inputs_sent += len(items)
|
|
189
|
+
# Change item state to WAITING_FOR_OUTPUT, and set the input_id and input_jwt which are in the response.
|
|
190
|
+
if self.map_items_manager is not None:
|
|
191
|
+
self.map_items_manager.handle_put_inputs_response(resp.inputs)
|
|
192
|
+
logger.debug(
|
|
193
|
+
f"Successfully pushed {len(items)} inputs to server. "
|
|
194
|
+
f"Num queued inputs awaiting push is {self.input_queue.qsize()}."
|
|
195
|
+
)
|
|
196
|
+
yield
|
|
197
|
+
|
|
198
|
+
async def _send_inputs(
|
|
199
|
+
self,
|
|
200
|
+
fn: "modal.client.UnaryUnaryWrapper",
|
|
201
|
+
request: typing.Union[api_pb2.FunctionPutInputsRequest, api_pb2.FunctionRetryInputsRequest],
|
|
202
|
+
) -> typing.Union[api_pb2.FunctionPutInputsResponse, api_pb2.FunctionRetryInputsResponse]:
|
|
203
|
+
# with 8 retries we log the warning below about every 30 seconds which isn't too spammy.
|
|
204
|
+
retry_warning_message = RetryWarningMessage(
|
|
205
|
+
message=f"Warning: map progress for function {self.function._function_name} is limited."
|
|
206
|
+
" Common bottlenecks include slow iteration over results, or function backlogs.",
|
|
207
|
+
warning_interval=8,
|
|
208
|
+
errors_to_warn_for=[Status.RESOURCE_EXHAUSTED],
|
|
209
|
+
)
|
|
210
|
+
return await retry_transient_errors(
|
|
211
|
+
fn,
|
|
212
|
+
request,
|
|
213
|
+
max_retries=None,
|
|
214
|
+
max_delay=PUMP_INPUTS_MAX_RETRY_DELAY,
|
|
215
|
+
additional_status_codes=[Status.RESOURCE_EXHAUSTED],
|
|
216
|
+
retry_warning_message=retry_warning_message,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class SyncInputPumper(InputPumper):
|
|
221
|
+
def __init__(
|
|
222
|
+
self,
|
|
223
|
+
client: "modal.client._Client",
|
|
224
|
+
*,
|
|
225
|
+
input_queue: asyncio.Queue,
|
|
226
|
+
retry_queue: TimestampPriorityQueue,
|
|
227
|
+
function: "modal.functions._Function",
|
|
228
|
+
function_call_jwt: str,
|
|
229
|
+
function_call_id: str,
|
|
230
|
+
map_items_manager: "_MapItemsManager",
|
|
231
|
+
):
|
|
232
|
+
super().__init__(
|
|
233
|
+
client,
|
|
234
|
+
input_queue=input_queue,
|
|
235
|
+
function=function,
|
|
236
|
+
function_call_id=function_call_id,
|
|
237
|
+
map_items_manager=map_items_manager,
|
|
238
|
+
)
|
|
239
|
+
self.retry_queue = retry_queue
|
|
240
|
+
self.inputs_retried = 0
|
|
241
|
+
self.function_call_jwt = function_call_jwt
|
|
242
|
+
|
|
243
|
+
async def retry_inputs(self):
|
|
244
|
+
async for retriable_idxs in queue_batch_iterator(self.retry_queue, max_batch_size=MAP_INVOCATION_CHUNK_SIZE):
|
|
245
|
+
# For each index, use the context in the manager to create a FunctionRetryInputsItem.
|
|
246
|
+
# This will also update the context state to RETRYING.
|
|
247
|
+
inputs: list[api_pb2.FunctionRetryInputsItem] = await self.map_items_manager.prepare_items_for_retry(
|
|
248
|
+
retriable_idxs
|
|
249
|
+
)
|
|
250
|
+
request = api_pb2.FunctionRetryInputsRequest(
|
|
251
|
+
function_call_jwt=self.function_call_jwt,
|
|
252
|
+
inputs=inputs,
|
|
253
|
+
)
|
|
254
|
+
resp = await self._send_inputs(self.client.stub.FunctionRetryInputs, request)
|
|
255
|
+
# Update the state to WAITING_FOR_OUTPUT, and update the input_jwt in the context
|
|
256
|
+
# to the new value in the response.
|
|
257
|
+
self.map_items_manager.handle_retry_response(resp.input_jwts)
|
|
258
|
+
logger.debug(f"Successfully pushed retry for {len(inputs)} to server.")
|
|
259
|
+
self.inputs_retried += len(inputs)
|
|
260
|
+
yield
|
|
261
|
+
|
|
262
|
+
|
|
89
263
|
async def _map_invocation(
|
|
90
264
|
function: "modal.functions._Function",
|
|
91
265
|
raw_input_queue: _SynchronizedQueue,
|
|
@@ -117,8 +291,6 @@ async def _map_invocation(
|
|
|
117
291
|
have_all_inputs = False
|
|
118
292
|
map_done_event = asyncio.Event()
|
|
119
293
|
inputs_created = 0
|
|
120
|
-
inputs_sent = 0
|
|
121
|
-
inputs_retried = 0
|
|
122
294
|
outputs_completed = 0
|
|
123
295
|
outputs_received = 0
|
|
124
296
|
retried_outputs = 0
|
|
@@ -135,25 +307,24 @@ async def _map_invocation(
|
|
|
135
307
|
retry_policy, function_call_invocation_type, retry_queue, sync_client_retries_enabled, max_inputs_outstanding
|
|
136
308
|
)
|
|
137
309
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
max_object_size_bytes=function._max_object_size_bytes,
|
|
147
|
-
idx=idx,
|
|
148
|
-
method_name=function._use_method_name,
|
|
149
|
-
)
|
|
310
|
+
input_preprocessor = InputPreprocessor(
|
|
311
|
+
client=client,
|
|
312
|
+
raw_input_queue=raw_input_queue,
|
|
313
|
+
processed_input_queue=input_queue,
|
|
314
|
+
function=function,
|
|
315
|
+
created_callback=lambda x: update_state(set_inputs_created=x),
|
|
316
|
+
done_callback=lambda: update_state(set_have_all_inputs=True),
|
|
317
|
+
)
|
|
150
318
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
319
|
+
input_pumper = SyncInputPumper(
|
|
320
|
+
client=client,
|
|
321
|
+
input_queue=input_queue,
|
|
322
|
+
retry_queue=retry_queue,
|
|
323
|
+
function=function,
|
|
324
|
+
map_items_manager=map_items_manager,
|
|
325
|
+
function_call_jwt=function_call_jwt,
|
|
326
|
+
function_call_id=function_call_id,
|
|
327
|
+
)
|
|
157
328
|
|
|
158
329
|
def update_state(set_have_all_inputs=None, set_inputs_created=None, set_outputs_completed=None):
|
|
159
330
|
# This should be the only method that needs nonlocal of the following vars
|
|
@@ -175,84 +346,6 @@ async def _map_invocation(
|
|
|
175
346
|
# map is done
|
|
176
347
|
map_done_event.set()
|
|
177
348
|
|
|
178
|
-
async def drain_input_generator():
|
|
179
|
-
# Parallelize uploading blobs
|
|
180
|
-
async with aclosing(
|
|
181
|
-
async_map_ordered(input_iter(), create_input, concurrency=BLOB_MAX_PARALLELISM)
|
|
182
|
-
) as streamer:
|
|
183
|
-
async for item in streamer:
|
|
184
|
-
await input_queue.put(item)
|
|
185
|
-
|
|
186
|
-
# close queue iterator
|
|
187
|
-
await input_queue.put(None)
|
|
188
|
-
update_state(set_have_all_inputs=True)
|
|
189
|
-
yield
|
|
190
|
-
|
|
191
|
-
async def pump_inputs():
|
|
192
|
-
assert client.stub
|
|
193
|
-
nonlocal inputs_sent
|
|
194
|
-
async for items in queue_batch_iterator(input_queue, max_batch_size=MAP_INVOCATION_CHUNK_SIZE):
|
|
195
|
-
# Add items to the manager. Their state will be SENDING.
|
|
196
|
-
await map_items_manager.add_items(items)
|
|
197
|
-
request = api_pb2.FunctionPutInputsRequest(
|
|
198
|
-
function_id=function.object_id,
|
|
199
|
-
inputs=items,
|
|
200
|
-
function_call_id=function_call_id,
|
|
201
|
-
)
|
|
202
|
-
logger.debug(
|
|
203
|
-
f"Pushing {len(items)} inputs to server. Num queued inputs awaiting push is {input_queue.qsize()}."
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
resp = await send_inputs(client.stub.FunctionPutInputs, request)
|
|
207
|
-
inputs_sent += len(items)
|
|
208
|
-
# Change item state to WAITING_FOR_OUTPUT, and set the input_id and input_jwt which are in the response.
|
|
209
|
-
map_items_manager.handle_put_inputs_response(resp.inputs)
|
|
210
|
-
logger.debug(
|
|
211
|
-
f"Successfully pushed {len(items)} inputs to server. "
|
|
212
|
-
f"Num queued inputs awaiting push is {input_queue.qsize()}."
|
|
213
|
-
)
|
|
214
|
-
yield
|
|
215
|
-
|
|
216
|
-
async def retry_inputs():
|
|
217
|
-
nonlocal inputs_retried
|
|
218
|
-
async for retriable_idxs in queue_batch_iterator(retry_queue, max_batch_size=MAP_INVOCATION_CHUNK_SIZE):
|
|
219
|
-
# For each index, use the context in the manager to create a FunctionRetryInputsItem.
|
|
220
|
-
# This will also update the context state to RETRYING.
|
|
221
|
-
inputs: list[api_pb2.FunctionRetryInputsItem] = await map_items_manager.prepare_items_for_retry(
|
|
222
|
-
retriable_idxs
|
|
223
|
-
)
|
|
224
|
-
request = api_pb2.FunctionRetryInputsRequest(
|
|
225
|
-
function_call_jwt=function_call_jwt,
|
|
226
|
-
inputs=inputs,
|
|
227
|
-
)
|
|
228
|
-
resp = await send_inputs(client.stub.FunctionRetryInputs, request)
|
|
229
|
-
# Update the state to WAITING_FOR_OUTPUT, and update the input_jwt in the context
|
|
230
|
-
# to the new value in the response.
|
|
231
|
-
map_items_manager.handle_retry_response(resp.input_jwts)
|
|
232
|
-
logger.debug(f"Successfully pushed retry for {len(inputs)} to server.")
|
|
233
|
-
inputs_retried += len(inputs)
|
|
234
|
-
yield
|
|
235
|
-
|
|
236
|
-
async def send_inputs(
|
|
237
|
-
fn: "modal.client.UnaryUnaryWrapper",
|
|
238
|
-
request: typing.Union[api_pb2.FunctionPutInputsRequest, api_pb2.FunctionRetryInputsRequest],
|
|
239
|
-
) -> typing.Union[api_pb2.FunctionPutInputsResponse, api_pb2.FunctionRetryInputsResponse]:
|
|
240
|
-
# with 8 retries we log the warning below about every 30 seconds which isn't too spammy.
|
|
241
|
-
retry_warning_message = RetryWarningMessage(
|
|
242
|
-
message=f"Warning: map progress for function {function._function_name} is limited."
|
|
243
|
-
" Common bottlenecks include slow iteration over results, or function backlogs.",
|
|
244
|
-
warning_interval=8,
|
|
245
|
-
errors_to_warn_for=[Status.RESOURCE_EXHAUSTED],
|
|
246
|
-
)
|
|
247
|
-
return await retry_transient_errors(
|
|
248
|
-
fn,
|
|
249
|
-
request,
|
|
250
|
-
max_retries=None,
|
|
251
|
-
max_delay=PUMP_INPUTS_MAX_RETRY_DELAY,
|
|
252
|
-
additional_status_codes=[Status.RESOURCE_EXHAUSTED],
|
|
253
|
-
retry_warning_message=retry_warning_message,
|
|
254
|
-
)
|
|
255
|
-
|
|
256
349
|
async def get_all_outputs():
|
|
257
350
|
assert client.stub
|
|
258
351
|
nonlocal \
|
|
@@ -395,8 +488,11 @@ async def _map_invocation(
|
|
|
395
488
|
def log_stats():
|
|
396
489
|
logger.debug(
|
|
397
490
|
f"Map stats: sync_client_retries_enabled={sync_client_retries_enabled} "
|
|
398
|
-
f"have_all_inputs={have_all_inputs}
|
|
399
|
-
f"
|
|
491
|
+
f"have_all_inputs={have_all_inputs} "
|
|
492
|
+
f"inputs_created={inputs_created} "
|
|
493
|
+
f"input_sent={input_pumper.inputs_sent} "
|
|
494
|
+
f"inputs_retried={input_pumper.inputs_retried} "
|
|
495
|
+
f"outputs_received={outputs_received} "
|
|
400
496
|
f"successful_completions={successful_completions} failed_completions={failed_completions} "
|
|
401
497
|
f"no_context_duplicates={no_context_duplicates} old_retry_duplicates={stale_retry_duplicates} "
|
|
402
498
|
f"already_complete_duplicates={already_complete_duplicates} "
|
|
@@ -415,7 +511,12 @@ async def _map_invocation(
|
|
|
415
511
|
|
|
416
512
|
log_debug_stats_task = asyncio.create_task(log_debug_stats())
|
|
417
513
|
async with aclosing(
|
|
418
|
-
async_merge(
|
|
514
|
+
async_merge(
|
|
515
|
+
input_preprocessor.drain_input_generator(),
|
|
516
|
+
input_pumper.pump_inputs(),
|
|
517
|
+
input_pumper.retry_inputs(),
|
|
518
|
+
poll_outputs(),
|
|
519
|
+
)
|
|
419
520
|
) as streamer:
|
|
420
521
|
async for response in streamer:
|
|
421
522
|
if response is not None: # type: ignore[unreachable]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import asyncio.events
|
|
3
|
+
import asyncio.queues
|
|
3
4
|
import collections.abc
|
|
4
5
|
import enum
|
|
5
6
|
import modal._functions
|
|
@@ -60,6 +61,68 @@ class _OutputValue:
|
|
|
60
61
|
"""Return self==value."""
|
|
61
62
|
...
|
|
62
63
|
|
|
64
|
+
class InputPreprocessor:
|
|
65
|
+
"""Constructs FunctionPutInputsItem objects from the raw-input queue, and puts them in the processed-input queue."""
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
client: modal.client._Client,
|
|
69
|
+
*,
|
|
70
|
+
raw_input_queue: _SynchronizedQueue,
|
|
71
|
+
processed_input_queue: asyncio.queues.Queue,
|
|
72
|
+
function: modal._functions._Function,
|
|
73
|
+
created_callback: collections.abc.Callable[[int], None],
|
|
74
|
+
done_callback: collections.abc.Callable[[], None],
|
|
75
|
+
):
|
|
76
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
77
|
+
...
|
|
78
|
+
|
|
79
|
+
def input_iter(self): ...
|
|
80
|
+
def create_input_factory(self): ...
|
|
81
|
+
def drain_input_generator(self): ...
|
|
82
|
+
|
|
83
|
+
class InputPumper:
|
|
84
|
+
"""Reads inputs from a queue of FunctionPutInputsItems, and sends them to the server."""
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
client: modal.client._Client,
|
|
88
|
+
*,
|
|
89
|
+
input_queue: asyncio.queues.Queue,
|
|
90
|
+
function: modal._functions._Function,
|
|
91
|
+
function_call_id: str,
|
|
92
|
+
map_items_manager: typing.Optional[_MapItemsManager] = None,
|
|
93
|
+
):
|
|
94
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
95
|
+
...
|
|
96
|
+
|
|
97
|
+
def pump_inputs(self): ...
|
|
98
|
+
async def _send_inputs(
|
|
99
|
+
self,
|
|
100
|
+
fn: modal.client.UnaryUnaryWrapper,
|
|
101
|
+
request: typing.Union[
|
|
102
|
+
modal_proto.api_pb2.FunctionPutInputsRequest, modal_proto.api_pb2.FunctionRetryInputsRequest
|
|
103
|
+
],
|
|
104
|
+
) -> typing.Union[
|
|
105
|
+
modal_proto.api_pb2.FunctionPutInputsResponse, modal_proto.api_pb2.FunctionRetryInputsResponse
|
|
106
|
+
]: ...
|
|
107
|
+
|
|
108
|
+
class SyncInputPumper(InputPumper):
|
|
109
|
+
"""Reads inputs from a queue of FunctionPutInputsItems, and sends them to the server."""
|
|
110
|
+
def __init__(
|
|
111
|
+
self,
|
|
112
|
+
client: modal.client._Client,
|
|
113
|
+
*,
|
|
114
|
+
input_queue: asyncio.queues.Queue,
|
|
115
|
+
retry_queue: modal._utils.async_utils.TimestampPriorityQueue,
|
|
116
|
+
function: modal._functions._Function,
|
|
117
|
+
function_call_jwt: str,
|
|
118
|
+
function_call_id: str,
|
|
119
|
+
map_items_manager: _MapItemsManager,
|
|
120
|
+
):
|
|
121
|
+
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
122
|
+
...
|
|
123
|
+
|
|
124
|
+
def retry_inputs(self): ...
|
|
125
|
+
|
|
63
126
|
def _map_invocation(
|
|
64
127
|
function: modal._functions._Function,
|
|
65
128
|
raw_input_queue: _SynchronizedQueue,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|