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

Allow force run app on cloud if loading locally errors #15019

Merged
merged 28 commits into from Oct 31, 2022
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1441355
Allow force run app on cloud if loading locally errors
manskx Oct 6, 2022
c4a98c8
typo
manskx Oct 6, 2022
763d358
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 6, 2022
6e72a37
update the message
manskx Oct 7, 2022
153d31a
merge conflicts
manskx Oct 7, 2022
7d1c48b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 7, 2022
f648685
add tests
manskx Oct 7, 2022
bc8c1dd
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 7, 2022
85a32e8
resolve conflicts
manskx Oct 26, 2022
079ecb7
update test
manskx Oct 26, 2022
986113a
Merge branch 'master' of github.com:Lightning-AI/lightning into mansk…
manskx Oct 26, 2022
d09b61c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 26, 2022
5805a15
remove confirmation
manskx Oct 26, 2022
f5f34ee
Merge branch 'manskx/app/allow-run-without-loading-app' of github.com…
manskx Oct 26, 2022
2af4a57
unused import
manskx Oct 26, 2022
2bcb998
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 26, 2022
8c7e0be
Merge branch 'master' into manskx/app/allow-run-without-loading-app
manskx Oct 26, 2022
ce384f8
Merge branch 'master' of github.com:Lightning-AI/lightning into mansk…
manskx Oct 27, 2022
1aefba7
Merge branch 'master' into manskx/app/allow-run-without-loading-app
manskx Oct 27, 2022
3885c6d
Merge branch 'manskx/app/allow-run-without-loading-app' of github.com…
manskx Oct 27, 2022
f3b8383
Add changelog
manskx Oct 27, 2022
b497d4e
merge conflicts
manskx Oct 31, 2022
06d6205
fix notfound errors
manskx Oct 31, 2022
b3d1319
update docstrings
manskx Oct 31, 2022
c728138
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 31, 2022
72656c8
fix flake8 error
awaelchli Oct 31, 2022
8a7263d
Apply suggestions from code review
Borda Oct 31, 2022
a2f1109
Merge branch 'master' into manskx/app/allow-run-without-loading-app
Borda Oct 31, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/lightning_app/CHANGELOG.md
Expand Up @@ -26,7 +26,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
- Add a `JustPyFrontend` to ease UI creation with `https://github.com/justpy-org/justpy` ([#15002](https://github.com/Lightning-AI/lightning/pull/15002))
- Added a layout endpoint to the Rest API and enable to disable pulling or pushing to the state ([#15367](https://github.com/Lightning-AI/lightning/pull/15367)
- Added support for functions for `configure_api` and `configure_commands` to be executed in the Rest API process ([#15098](https://github.com/Lightning-AI/lightning/pull/15098)

- Added support to start lightning app on cloud without needing to install dependencies locally ([#15019](https://github.com/Lightning-AI/lightning/pull/15019)

### Changed

Expand Down
26 changes: 26 additions & 0 deletions src/lightning_app/runners/cloud.py
Expand Up @@ -4,6 +4,7 @@
import string
import sys
import time
import traceback
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Callable, List, Optional, Union
Expand Down Expand Up @@ -60,6 +61,7 @@
from lightning_app.utilities.app_helpers import Logger
from lightning_app.utilities.cloud import _get_project
from lightning_app.utilities.dependency_caching import get_hash
from lightning_app.utilities.load_app import _prettifiy_exception, load_app_from_file
from lightning_app.utilities.packaging.app_config import AppConfig, find_config_file
from lightning_app.utilities.packaging.lightning_utils import _prepare_lightning_wheels_and_requirements
from lightning_app.utilities.secrets import _names_to_ids
Expand Down Expand Up @@ -463,6 +465,30 @@ def _project_has_sufficient_credits(self, project: V1Membership, app: Optional[L

return balance >= 1

@classmethod
def load_app_from_file(cls, filepath: str) -> "LightningApp":
"""This is meant to use only locally for cloud runtime."""
try:
app = load_app_from_file(filepath, raise_exception=True)
except ModuleNotFoundError:
# this is very generic exception.
logger.info("Could not load the app() locally. Starting the app directly on the cloud.")
Borda marked this conversation as resolved.
Show resolved Hide resolved
# we want to format the exception as if no frame was on top.
exp, val, tb = sys.exc_info()
listing = traceback.format_exception(exp, val, tb)
# remove the entry for the first frame
del listing[1]
from lightning_app.testing.helpers import EmptyFlow

# Create a mocking app.
app = LightningApp(EmptyFlow())
manskx marked this conversation as resolved.
Show resolved Hide resolved

except FileNotFoundError as e:
raise e
except Exception:
_prettifiy_exception(filepath)
return app


def _create_mount_drive_spec(work_name: str, mount: Mount) -> V1LightningworkDrives:
if mount.protocol == "s3://":
Expand Down
7 changes: 6 additions & 1 deletion src/lightning_app/runners/runtime.py
Expand Up @@ -56,7 +56,7 @@ def dispatch(

runtime_type = RuntimeType(runtime_type)
runtime_cls: Type[Runtime] = runtime_type.get_runtime()
app = load_app_from_file(str(entrypoint_file))
app = runtime_cls.load_app_from_file(str(entrypoint_file))

env_vars = {} if env_vars is None else env_vars
secrets = {} if secrets is None else secrets
Expand Down Expand Up @@ -151,3 +151,8 @@ def _add_stopped_status_to_work(self, work: "lightning_app.LightningWork") -> No
latest_call_hash = work._calls[CacheCallsKeys.LATEST_CALL_HASH]
if latest_call_hash in work._calls:
work._calls[latest_call_hash]["statuses"].append(make_status(WorkStageStatus.STOPPED))

@classmethod
def load_app_from_file(cls, filepath: str) -> "LightningApp":

return load_app_from_file(filepath)
awaelchli marked this conversation as resolved.
Show resolved Hide resolved
38 changes: 26 additions & 12 deletions src/lightning_app/utilities/load_app.py
Expand Up @@ -16,7 +16,28 @@
logger = Logger(__name__)


def load_app_from_file(filepath: str) -> "LightningApp":
def _prettifiy_exception(filepath: str):
"""Pretty print the exception that occurred when loading the app."""
# we want to format the exception as if no frame was on top.
exp, val, tb = sys.exc_info()
listing = traceback.format_exception(exp, val, tb)
# remove the entry for the first frame
del listing[1]
listing = [
f"Found an exception when loading your application from {filepath}. Please, resolve it to run your app.\n\n"
] + listing
logger.error("".join(listing))
sys.exit(1)


def load_app_from_file(filepath: str, raise_exception: bool = False) -> "LightningApp":
"""Load a LightningApp from a file.

Arguments:
filepath: The path to the file containing the LightningApp.
raise_exception: If True, raise an exception if the app cannot be loaded.
"""

# Taken from StreamLit: https://github.com/streamlit/streamlit/blob/develop/lib/streamlit/script_runner.py#L313

from lightning_app.core.app import LightningApp
Expand All @@ -30,17 +51,10 @@ def load_app_from_file(filepath: str) -> "LightningApp":
try:
with _patch_sys_argv():
exec(code, module.__dict__)
except Exception:
# we want to format the exception as if no frame was on top.
exp, val, tb = sys.exc_info()
listing = traceback.format_exception(exp, val, tb)
# remove the entry for the first frame
del listing[1]
listing = [
f"Found an exception when loading your application from {filepath}. Please, resolve it to run your app.\n\n"
] + listing
logger.error("".join(listing))
sys.exit(1)
except Exception as e:
if raise_exception:
raise e
_prettifiy_exception(filepath)

apps = [v for v in module.__dict__.values() if isinstance(v, LightningApp)]
if len(apps) > 1:
Expand Down
16 changes: 14 additions & 2 deletions tests/tests_app/runners/test_cloud.py
@@ -1,4 +1,5 @@
import logging
import os
from copy import copy
from pathlib import Path
from unittest import mock
Expand Down Expand Up @@ -36,9 +37,10 @@
V1Work,
)

from lightning_app import LightningApp, LightningWork
from lightning_app.runners import backends, cloud
from lightning_app import _PROJECT_ROOT, LightningApp, LightningWork
from lightning_app.runners import backends, cloud, CloudRuntime
from lightning_app.storage import Drive, Mount
from lightning_app.testing.helpers import EmptyFlow
from lightning_app.utilities.cloud import _get_project
from lightning_app.utilities.dependency_caching import get_hash
from lightning_app.utilities.packaging.cloud_compute import CloudCompute
Expand Down Expand Up @@ -1082,3 +1084,13 @@ def test_project_has_sufficient_credits():
for balance, result in credits_and_test_value:
project = V1Membership(name="test-project1", project_id="test-project-id1", balance=balance)
assert cloud_runtime._project_has_sufficient_credits(project) is result


@mock.patch(
"lightning_app.runners.cloud.load_app_from_file",
MagicMock(side_effect=ModuleNotFoundError("Module X not found")),
)
def test_load_app_from_file_module_error():
empty_app = CloudRuntime.load_app_from_file(os.path.join(_PROJECT_ROOT, "examples", "app_v0", "app.py"))
assert isinstance(empty_app, LightningApp)
assert isinstance(empty_app.root, EmptyFlow)