Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: support 3.8 to 3.12 for fal and fal-client #351

Merged
merged 8 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .github/workflows/integration_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,24 @@ jobs:
strategy:
fail-fast: false
matrix:
deps: ["pydantic==1.10.12", "pydantic==2.5.0"]
deps: ["pydantic==1.10.18", "pydantic==2.5.0"]
# TODO: Test Python 3.13
python: ["3.8", "3.12"]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0

- uses: actions/setup-python@v4
with:
python-version: "3.9"
cache: 'pip'
python-version: ${{ matrix.python }}
cache: pip

- name: Install dependencies
run: |
pip install --upgrade pip wheel
pip install -e 'projects/fal[test]' ${{ matrix.deps }}
# TODO: should we include graphlib as a project dependency?
pip install -e 'projects/fal[test]' graphlib ${{ matrix.deps }}

- name: Run integration tests
env:
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/tests-fal-client.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ on:
jobs:
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python: ["3.8", "3.13"]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-python@v4
with:
python-version: "3.12"
python-version: ${{ matrix.python }}
- name: Install dependencies
run: |
pip install --upgrade pip wheel
Expand All @@ -25,4 +29,4 @@ jobs:
env:
FAL_KEY: ${{ secrets.FAL_KEY_PROD }}
run: |
pytest projects/fal_client/tests
pytest projects/fal_client/tests
6 changes: 4 additions & 2 deletions projects/fal/src/fal/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,8 @@ def submit(app_id: str, arguments: dict[str, Any], *, path: str = "") -> Request
app_id = _backwards_compatible_app_id(app_id)
url = _QUEUE_URL_FORMAT.format(app_id=app_id)
if path:
url += "/" + path.removeprefix("/")
_path = path[len("/") :] if path.startswith("/") else path
url += "/" + _path

creds = get_default_credentials()

Expand Down Expand Up @@ -235,7 +236,8 @@ def _connect(app_id: str, *, path: str = "/realtime") -> Iterator[_RealtimeConne
app_id = _backwards_compatible_app_id(app_id)
url = _REALTIME_URL_FORMAT.format(app_id=app_id)
if path:
url += "/" + path.removeprefix("/")
_path = path[len("/") :] if path.startswith("/") else path
url += "/" + _path

creds = get_default_credentials()

Expand Down
14 changes: 10 additions & 4 deletions projects/fal/src/fal/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from argparse import ArgumentParser
from collections import Counter
from dataclasses import dataclass, field
from typing import Any, Iterator, Union, cast
from typing import Any, Dict, Iterator, List, Union, cast

import graphlib
import rich
Expand All @@ -21,8 +21,8 @@
from fal.exceptions import FalServerlessException
from fal.rest_client import REST_CLIENT

JSONType = Union[dict[str, Any], list[Any], str, int, float, bool, None, "Leaf"]
SchemaType = dict[str, Any]
JSONType = Union[Dict[str, Any], List[Any], str, int, float, bool, None, "Leaf"]
SchemaType = Dict[str, Any]

VARIABLE_PREFIX = "$"
INPUT_VARIABLE_NAME = "input"
Expand Down Expand Up @@ -50,7 +50,13 @@ def parse_leaf(raw_leaf: str) -> Leaf:
f"Invalid leaf: {raw_leaf} (must start with a reference)"
)

leaf: Leaf = ReferenceLeaf(reference.removeprefix(VARIABLE_PREFIX))
# remove the $ prefix
_reference = (
reference[len(VARIABLE_PREFIX) :]
if reference.startswith(VARIABLE_PREFIX)
else reference
)
leaf: Leaf = ReferenceLeaf(_reference)
for raw_part in raw_parts:
if raw_part.isdigit():
leaf = IndexLeaf(leaf, int(raw_part))
Expand Down
4 changes: 2 additions & 2 deletions projects/fal/tests/cli/test_deploy.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional
from typing import Optional, Tuple
from unittest.mock import MagicMock, patch

import pytest
Expand Down Expand Up @@ -30,7 +30,7 @@ def mock_parse_pyproject_toml():


def mock_args(
app_ref: tuple[str],
app_ref: Tuple[str, Optional[str]],
app_name: Optional[str] = None,
auth: Optional[str] = None,
strategy: Optional[str] = None,
Expand Down
3 changes: 2 additions & 1 deletion projects/fal/tests/cli/test_run.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from typing import Optional, Tuple
from unittest.mock import MagicMock, patch

import pytest
Expand Down Expand Up @@ -34,7 +35,7 @@ def mocked_fal_serverless_host(host):
return mock


def mock_args(host, func_ref: tuple[str]):
def mock_args(host, func_ref: Tuple[str, Optional[str]]):
args = MagicMock()
args.host = host
args.func_ref = func_ref
Expand Down
15 changes: 8 additions & 7 deletions projects/fal/tests/test_apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import time
from contextlib import contextmanager, suppress
from datetime import datetime
from typing import Generator
from typing import Generator, List, Tuple

import fal
import fal.api as api
Expand Down Expand Up @@ -92,7 +92,7 @@ def container_addition_app(input: Input) -> Output:

@fal.function(
keep_alive=300,
requirements=["fastapi", "uvicorn", "pydantic==1.10.12"],
requirements=["fastapi", "uvicorn", "pydantic==1.10.18"],
machine_type="S",
max_concurrency=1,
max_multiplexing=30,
Expand Down Expand Up @@ -224,7 +224,7 @@ class RTOutput(BaseModel):


class RTOutputs(BaseModel):
texts: list[str]
texts: List[str]


class RealtimeApp(fal.App, keep_alive=300, max_concurrency=1):
Expand Down Expand Up @@ -252,7 +252,7 @@ def generate_rt_batched(self, input: RTInput, *inputs: RTInput) -> RTOutputs:


@pytest.fixture(scope="module")
def aliased_app() -> Generator[tuple[str, str], None, None]:
def aliased_app() -> Generator[Tuple[str, str], None, None]:
# Create a temporary app, register it, and return the ID of it.

import uuid
Expand Down Expand Up @@ -418,6 +418,7 @@ def test_stateful_app_client(test_stateful_app: str):
assert response["result"] == 0


@pytest.mark.flaky(max_runs=3)
def test_app_cancellation(test_app: str, test_cancellable_app: str):
request_handle = apps.submit(
test_cancellable_app, arguments={"lhs": 1, "rhs": 2, "wait_time": 10}
Expand Down Expand Up @@ -516,7 +517,7 @@ def test_404_response(test_app: str, request: pytest.FixtureRequest):
apps.run(test_app, path="/other", arguments={"lhs": 1, "rhs": 2})


def test_app_deploy_scale(aliased_app: tuple[str, str]):
def test_app_deploy_scale(aliased_app: Tuple[str, str]):
import uuid
from dataclasses import replace

Expand Down Expand Up @@ -558,7 +559,7 @@ def test_app_deploy_scale(aliased_app: tuple[str, str]):
assert found.max_multiplexing == 30


def test_app_update_app(aliased_app: tuple[str, str]):
def test_app_update_app(aliased_app: Tuple[str, str]):
app_revision, app_alias = aliased_app

host: api.FalServerlessHost = addition_app.host # type: ignore
Expand Down Expand Up @@ -608,7 +609,7 @@ def test_app_update_app(aliased_app: tuple[str, str]):
assert res.max_multiplexing == new_max_multiplexing


def test_app_set_delete_alias(aliased_app: tuple[str, str]):
def test_app_set_delete_alias(aliased_app: Tuple[str, str]):
app_revision, app_alias = aliased_app

host: api.FalServerlessHost = addition_app.host # type: ignore
Expand Down
4 changes: 2 additions & 2 deletions projects/fal_client/src/fal_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from datetime import datetime, timezone
from dataclasses import dataclass, field
from functools import cached_property
from typing import Any, AsyncIterator, Iterator, TYPE_CHECKING, Optional, Literal
from typing import Any, AsyncIterator, Dict, Iterator, TYPE_CHECKING, Optional, Literal
from urllib.parse import urlencode

import httpx
Expand All @@ -20,7 +20,7 @@
if TYPE_CHECKING:
from PIL import Image

AnyJSON = dict[str, Any]
AnyJSON = Dict[str, Any]
Priority = Literal["normal", "low"]

RUN_URL_FORMAT = f"https://{FAL_RUN_HOST}/"
Expand Down
Loading