Skip to content
Draft
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
1 change: 1 addition & 0 deletions projects/fal/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dependencies = [
"rich_argparse",
"packaging>=21.3",
"pathspec>=0.11.1,<1",
"build>=1,<2",
"pydantic!=2.0.*,!=2.1.*,!=2.2.*,!=2.3.*,!=2.4.*",
# serve=True dependencies
# https://github.com/fastapi/fastapi/issues/14221
Expand Down
36 changes: 29 additions & 7 deletions projects/fal/src/fal/cli/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import copy
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Optional

from fal.api import Options
Expand All @@ -20,6 +21,8 @@
@dataclass(frozen=True)
class AppData:
ref: Optional[str] = None
python_entry_point: Optional[str] = None
project_root: Optional[Path] = None
auth: Optional[AuthModeLiteral] = None
deployment_strategy: Optional[DeploymentStrategyLiteral] = None
reset_scale: bool = False
Expand Down Expand Up @@ -58,14 +61,26 @@ def get_app_data_from_toml(
except KeyError:
raise ValueError(f"App {app_name} not found in pyproject.toml")

try:
app_ref: str = app_data.pop("ref")
except KeyError:
raise ValueError(f"App {app_name} does not have a ref key in pyproject.toml")

# Convert the app_ref to a path relative to the project root
python_entry_point: str | None = app_data.pop("python_entry_point", None)
project_root, _ = find_project_root(None)
app_ref = str(project_root / app_ref)
if python_entry_point is not None:
app_ref: str | None = app_data.pop("ref", None)
if app_ref is not None:
raise ValueError(
f"App {app_name} cannot have both ref "
"and python_entry_point keys in pyproject.toml"
)
else:
try:
app_ref = app_data.pop("ref")
except KeyError:
raise ValueError(
f"App {app_name} does not have a ref key in pyproject.toml"
)
if not isinstance(app_ref, str):
raise ValueError(f"App {app_name} ref must be a string in pyproject.toml")
# Convert the app_ref to a path relative to the project root.
app_ref = str(project_root / app_ref)

app_auth: Optional[AuthModeLiteral] = app_data.pop("auth", None)
app_deployment_strategy: Optional[DeploymentStrategyLiteral] = app_data.pop(
Expand All @@ -77,8 +92,11 @@ def get_app_data_from_toml(
app_name_value = app_name

requirements = app_data.pop("requirements", None)
python_version = app_data.pop("python_version", None)
if requirements is not None:
_validate_requirements(requirements)
if python_version is not None and not isinstance(python_version, str):
raise ValueError("python_version must be a string.")
options = Options()
min_concurrency = app_data.pop("min_concurrency", None)
max_concurrency = app_data.pop("max_concurrency", None)
Expand Down Expand Up @@ -132,6 +150,8 @@ def get_app_data_from_toml(
options.host["app_files_context_dir"] = app_files_context_dir
if requirements is not None:
options.environment["requirements"] = requirements
if python_version is not None:
options.environment["python_version"] = python_version

app_reset_scale: bool
if "no_scale" in app_data:
Expand All @@ -148,6 +168,8 @@ def get_app_data_from_toml(

return AppData(
ref=app_ref,
python_entry_point=python_entry_point,
project_root=project_root,
auth=app_auth,
deployment_strategy=app_deployment_strategy,
reset_scale=app_reset_scale,
Expand Down
13 changes: 10 additions & 3 deletions projects/fal/src/fal/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ def _run(args):
name=app_data.name,
)
team = team or app_data.team
file_path, func_name = RefAction.split_ref(app_data.ref)
if app_data.ref is not None:
file_path, func_name = RefAction.split_ref(app_data.ref)
else:
file_path, func_name = None, None
else:
file_path, func_name = func_ref
# Turn relative path into absolute path for files
Expand All @@ -48,8 +51,10 @@ def _run(args):

no_cache = args.no_cache or args.force_env_build
client = SyncServerlessClient(host=args.host, team=team)
host = client._create_host(local_file_path=file_path, environment_name=args.env)

host = client._create_host(
local_file_path=file_path or "",
environment_name=args.env,
)
loaded = load_function_from(
host,
file_path,
Expand All @@ -59,6 +64,8 @@ def _run(args):
app_name=app_data.name,
app_auth=app_data.auth,
limit_max_requests=args.limit_max_requests,
python_entry_point=app_data.python_entry_point,
project_root=app_data.project_root,
)

isolated_function = loaded.function
Expand Down
Loading
Loading