Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into freider/add-local-pyt…
Browse files Browse the repository at this point in the history
…hon-dir
  • Loading branch information
freider committed Dec 3, 2024
2 parents 4842bc4 + ea5a860 commit 98c7ac9
Show file tree
Hide file tree
Showing 109 changed files with 1,609 additions and 1,199 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ jobs:
run: pytest -v

- name: Run docstring tests
if: github.event.pull_request.head.repo.fork == 'false'
if: github.event.pull_request.head.repo.fork == false
env:
MODAL_ENVIRONMENT: client-doc-tests
MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
Expand Down Expand Up @@ -243,4 +243,4 @@ jobs:
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: twine upload dist/* --non-interactive
run: twine upload dist/* --non-interactive
33 changes: 0 additions & 33 deletions .github/workflows/pr-autoapprove.yml

This file was deleted.

71 changes: 0 additions & 71 deletions .github/workflows/pr_autoapprove.py

This file was deleted.

43 changes: 37 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,48 @@ We appreciate your patience while we speedily work towards a stable release of t

<!-- NEW CONTENT GENERATED BELOW. PLEASE PRESERVE THIS COMMENT. -->

### 0.66.40 (2024-11-23)
### 0.67.12 (2024-12-02)

* Adds `Image.add_local_file(..., copy=False)` and `Image.add_local_dir(..., copy=False)` as a unified replacement for the old `Image.copy_local_*()` and `Mount.add_local_*` methods.
- Fixed a bug that executes the wrong method when a Modal Cls overrides a `@modal.method` inherited from a parent.



### 0.66.30 (2024-11-21)
### 0.67.7 (2024-11-29)

- Removed the `aiostream` package from the modal client library dependencies.
- Fixed a bug where pointing `modal run` at a method on a Modal Cls would fail if the method was inherited from a parent.



### 0.67.0 (2024-11-27)

New minor client version `0.67.x` comes with an internal data model change for how Modal creates functions for Modal classes. There are no breaking or backwards-incompatible changes with this release. All forward lookup scenarios (`.lookup()` of a `0.67` class from a pre `0.67` client) as well as backwards lookup scenarios (`.lookup()` of a pre `0.67` class from a `0.67` client) work, except for a `0.62` client looking up a `0.67` class (this maintains our current restriction of not being able to lookup a `0.63+` class from a `0.62` client).


## 0.66

### 0.66.29 (2024-11-21)

* Adds `Image.add_local_python_packages` which works similarly to `Mount.from_local_python_packages` but for images.

### 0.66.49 (2024-11-26)

- `modal config set-environment` will now raise if the requested environment does not exist.



### 0.66.45 (2024-11-26)

- The `modal launch` CLI now accepts a `--detach` flag to run the App in detached mode, such that it will persist after the local client disconnects.



### 0.66.40 (2024-11-23)

* Adds `Image.add_local_file(..., copy=False)` and `Image.add_local_dir(..., copy=False)` as a unified replacement for the old `Image.copy_local_*()` and `Mount.add_local_*` methods.



### 0.66.30 (2024-11-21)

- Removed the `aiostream` package from the modal client library dependencies.



Expand All @@ -40,6 +67,10 @@ We appreciate your patience while we speedily work towards a stable release of t



## 0.65



### 0.65.55 (2024-11-13)

- Escalates stuck input cancellations to container death. This prevents unresponsive user code from holding up resources.
Expand Down
4 changes: 2 additions & 2 deletions modal/_clustered_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import os
import socket
from dataclasses import dataclass
from typing import List, Optional
from typing import Optional

from modal._utils.async_utils import synchronize_api
from modal._utils.grpc_utils import retry_transient_errors
Expand All @@ -14,7 +14,7 @@
@dataclass
class ClusterInfo:
rank: int
container_ips: List[str]
container_ips: list[str]


cluster_info: Optional[ClusterInfo] = None
Expand Down
9 changes: 5 additions & 4 deletions modal/_container_entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
import sys
import threading
import time
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Sequence
from collections.abc import Sequence
from typing import TYPE_CHECKING, Any, Callable, Optional

from google.protobuf.message import Message

Expand Down Expand Up @@ -175,7 +176,7 @@ def _sigint_handler():
def call_function(
user_code_event_loop: UserCodeEventLoop,
container_io_manager: "modal._runtime.container_io_manager.ContainerIOManager",
finalized_functions: Dict[str, "modal._runtime.user_code_imports.FinalizedFunction"],
finalized_functions: dict[str, "modal._runtime.user_code_imports.FinalizedFunction"],
batch_max_size: int,
batch_wait_ms: int,
):
Expand Down Expand Up @@ -473,7 +474,7 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
# 1. Enable lazy hydration for all objects
# 2. Fully deprecate .new() objects
if service.code_deps is not None: # this is not set for serialized or non-global scope functions
dep_object_ids: List[str] = [dep.object_id for dep in function_def.object_dependencies]
dep_object_ids: list[str] = [dep.object_id for dep in function_def.object_dependencies]
if len(service.code_deps) != len(dep_object_ids):
raise ExecutionError(
f"Function has {len(service.code_deps)} dependencies"
Expand Down Expand Up @@ -595,7 +596,7 @@ def breakpoint_wrapper():
# from shutting down. The sleep(0) here is needed for finished ThreadPoolExecutor resources to
# shut down without triggering this warning (e.g., `@wsgi_app()`).
time.sleep(0)
lingering_threads: List[threading.Thread] = []
lingering_threads: list[threading.Thread] = []
for thread in threading.enumerate():
current_thread = threading.get_ident()
if thread.ident is not None and thread.ident != current_thread and not thread.daemon and thread.is_alive():
Expand Down
57 changes: 29 additions & 28 deletions modal/_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
import re
import socket
import sys
from collections.abc import Generator
from datetime import timedelta
from typing import Callable, ClassVar, Dict, Generator, Optional, Tuple
from typing import Callable, ClassVar

from grpclib.exceptions import GRPCError, StreamTerminatedError
from rich.console import Console, Group, RenderableType
Expand Down Expand Up @@ -121,25 +122,25 @@ def finalize(self):


class OutputManager:
_instance: ClassVar[Optional["OutputManager"]] = None
_instance: ClassVar[OutputManager | None] = None

_console: Console
_task_states: Dict[str, int]
_task_progress_items: Dict[Tuple[str, int], TaskID]
_current_render_group: Optional[Group]
_function_progress: Optional[Progress]
_function_queueing_progress: Optional[Progress]
_snapshot_progress: Optional[Progress]
_line_buffers: Dict[int, LineBufferedOutput]
_task_states: dict[str, int]
_task_progress_items: dict[tuple[str, int], TaskID]
_current_render_group: Group | None
_function_progress: Progress | None
_function_queueing_progress: Progress | None
_snapshot_progress: Progress | None
_line_buffers: dict[int, LineBufferedOutput]
_status_spinner: Spinner
_app_page_url: Optional[str]
_app_page_url: str | None
_show_image_logs: bool
_status_spinner_live: Optional[Live]
_status_spinner_live: Live | None

def __init__(
self,
*,
stdout: Optional[io.TextIOWrapper] = None,
stdout: io.TextIOWrapper | None = None,
status_spinner_text: str = "Running app...",
):
self._stdout = stdout or sys.stdout
Expand All @@ -164,12 +165,12 @@ def disable(cls):
cls._instance = None

@classmethod
def get(cls) -> Optional["OutputManager"]:
def get(cls) -> OutputManager | None:
return cls._instance

@classmethod
@contextlib.contextmanager
def enable_output(cls, show_progress: bool = True) -> Generator[None, None, None]:
def enable_output(cls, show_progress: bool = True) -> Generator[None]:
if show_progress:
cls._instance = OutputManager()
try:
Expand Down Expand Up @@ -252,7 +253,7 @@ def function_queueing_progress(self) -> Progress:
self._current_render_group.renderables.append(self._function_queueing_progress)
return self._function_queueing_progress

def function_progress_callback(self, tag: str, total: Optional[int]) -> Callable[[int, int], None]:
def function_progress_callback(self, tag: str, total: int | None) -> Callable[[int, int], None]:
"""Adds a task to the current function_progress instance, and returns a callback
to update task progress with new completed and total counts."""

Expand Down Expand Up @@ -330,7 +331,7 @@ def update_snapshot_progress(self, image_id: str, task_progress: api_pb2.TaskPro
pass

def update_queueing_progress(
self, *, function_id: str, completed: int, total: Optional[int], description: Optional[str]
self, *, function_id: str, completed: int, total: int | None, description: str | None
) -> None:
"""Handle queueing updates, ignoring completion updates for functions that have no queue progress bar."""
task_key = (function_id, api_pb2.FUNCTION_QUEUED)
Expand Down Expand Up @@ -449,13 +450,13 @@ def _advance_sub_task(self, task_id: TaskID, advance: float):

def progress(
self,
task_id: Optional[TaskID] = None,
advance: Optional[float] = None,
name: Optional[str] = None,
size: Optional[float] = None,
reset: Optional[bool] = False,
complete: Optional[bool] = False,
) -> Optional[TaskID]:
task_id: TaskID | None = None,
advance: float | None = None,
name: str | None = None,
size: float | None = None,
reset: bool | None = False,
complete: bool | None = False,
) -> TaskID | None:
try:
if task_id is not None:
if reset:
Expand Down Expand Up @@ -527,15 +528,15 @@ async def put_pty_content(log: api_pb2.TaskLogs, stdout):
async def get_app_logs_loop(
client: _Client,
output_mgr: OutputManager,
app_id: Optional[str] = None,
task_id: Optional[str] = None,
app_logs_url: Optional[str] = None,
app_id: str | None = None,
task_id: str | None = None,
app_logs_url: str | None = None,
):
last_log_batch_entry_id = ""

pty_shell_stdout = None
pty_shell_finish_event: Optional[asyncio.Event] = None
pty_shell_task_id: Optional[str] = None
pty_shell_finish_event: asyncio.Event | None = None
pty_shell_task_id: str | None = None

async def stop_pty_shell():
nonlocal pty_shell_finish_event
Expand Down
4 changes: 2 additions & 2 deletions modal/_pty.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
import contextlib
import os
import sys
from typing import Optional, Tuple
from typing import Optional

from modal_proto import api_pb2


def get_winsz(fd) -> Tuple[Optional[int], Optional[int]]:
def get_winsz(fd) -> tuple[Optional[int], Optional[int]]:
try:
import fcntl
import struct
Expand Down
Loading

0 comments on commit 98c7ac9

Please sign in to comment.