Skip to content
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
12 changes: 8 additions & 4 deletions reflex/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -1152,11 +1152,12 @@ def _validate_var_dependencies(self, state: type[BaseState] | None = None) -> No
for substate in state.class_subclasses:
self._validate_var_dependencies(substate)

def _compile(self, export: bool = False):
def _compile(self, export: bool = False, dry_run: bool = False):
"""Compile the app and output it to the pages folder.

Args:
export: Whether to compile the app for export.
dry_run: Whether to compile the app without saving it.

Raises:
ReflexRuntimeError: When any page uses state, but no rx.State subclass is defined.
Expand All @@ -1170,7 +1171,7 @@ def get_compilation_time() -> str:

should_compile = self._should_compile()
backend_dir = prerequisites.get_backend_dir()
if not should_compile and backend_dir.exists():
if not dry_run and not should_compile and backend_dir.exists():
stateful_pages_marker = backend_dir / constants.Dirs.STATEFUL_PAGES
if stateful_pages_marker.exists():
with stateful_pages_marker.open("r") as f:
Expand Down Expand Up @@ -1205,7 +1206,7 @@ def get_compilation_time() -> str:
if config.react_strict_mode:
app_wrappers[(200, "StrictMode")] = StrictMode.create()

if not should_compile:
if not should_compile and not dry_run:
with console.timing("Evaluate Pages (Backend)"):
for route in self._unevaluated_pages:
console.debug(f"Evaluating page: {route}")
Expand Down Expand Up @@ -1358,7 +1359,7 @@ def memoized_toast_provider():

# Copy the assets.
assets_src = Path.cwd() / constants.Dirs.APP_ASSETS
if assets_src.is_dir():
if assets_src.is_dir() and not dry_run:
with console.timing("Copy assets"):
path_ops.update_directory_tree(
src=assets_src,
Expand Down Expand Up @@ -1444,6 +1445,9 @@ def _submit_work(fn: Callable[..., tuple[str, str]], *args, **kwargs):
progress.advance(task)
progress.stop()

if dry_run:
return

# Install frontend packages.
with console.timing("Install Frontend Packages"):
self._get_frontend_packages(all_imports)
Expand Down
24 changes: 24 additions & 0 deletions reflex/reflex.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,30 @@ def run(
)


@cli.command()
@loglevel_option
@click.option(
"--dry",
is_flag=True,
default=False,
help="Run the command without making any changes.",
)
def compile(dry: bool):
"""Compile the app in the current directory."""
import time

from reflex.utils import prerequisites

# Check the app.
if prerequisites.needs_reinit():
_init(name=get_config().app_name)
get_config(reload=True)
starting_time = time.monotonic()
prerequisites.compile_app(dry_run=dry)
elapsed_time = time.monotonic() - starting_time
console.success(f"App compiled successfully in {elapsed_time:.3f} seconds.")


@cli.command()
@loglevel_option
@click.option(
Expand Down
14 changes: 10 additions & 4 deletions reflex/utils/prerequisites.py
Original file line number Diff line number Diff line change
Expand Up @@ -432,12 +432,15 @@ def validate_app(reload: bool = False) -> None:
get_and_validate_app(reload=reload)


def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType:
def get_compiled_app(
reload: bool = False, export: bool = False, dry_run: bool = False
) -> ModuleType:
"""Get the app module based on the default config after first compiling it.

Args:
reload: Re-import the app module from disk
export: Compile the app for export
dry_run: If True, do not write the compiled app to disk.

Returns:
The compiled app based on the default config.
Expand All @@ -446,18 +449,21 @@ def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType:
# For py3.9 compatibility when redis is used, we MUST add any decorator pages
# before compiling the app in a thread to avoid event loop error (REF-2172).
app._apply_decorated_pages()
app._compile(export=export)
app._compile(export=export, dry_run=dry_run)
return app_module


def compile_app(reload: bool = False, export: bool = False) -> None:
def compile_app(
reload: bool = False, export: bool = False, dry_run: bool = False
) -> None:
"""Compile the app module based on the default config.

Args:
reload: Re-import the app module from disk
export: Compile the app for export
dry_run: If True, do not write the compiled app to disk.
"""
get_compiled_app(reload=reload, export=export)
get_compiled_app(reload=reload, export=export, dry_run=dry_run)


def _can_colorize() -> bool:
Expand Down
10 changes: 6 additions & 4 deletions reflex/utils/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,7 @@
from typing import get_origin as get_origin_og
from typing import get_type_hints as get_type_hints_og

import sqlalchemy
from pydantic.v1.fields import ModelField
from sqlalchemy.ext.associationproxy import AssociationProxyInstance
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import DeclarativeBase, Mapped, QueryableAttribute, Relationship
from typing_extensions import Self as Self
from typing_extensions import is_typeddict
from typing_extensions import override as override
Expand Down Expand Up @@ -322,6 +318,8 @@ def get_property_hint(attr: Any | None) -> GenericType | None:
Returns:
The type hint of the property, if it is a property, else None.
"""
from sqlalchemy.ext.hybrid import hybrid_property

if not isinstance(attr, (property, hybrid_property)):
return None
hints = get_type_hints(attr.fget)
Expand All @@ -340,6 +338,10 @@ def get_attribute_access_type(cls: GenericType, name: str) -> GenericType | None
Returns:
The type of the attribute, if accessible, or None
"""
import sqlalchemy
from sqlalchemy.ext.associationproxy import AssociationProxyInstance
from sqlalchemy.orm import DeclarativeBase, Mapped, QueryableAttribute, Relationship

from reflex.model import Model

try:
Expand Down
12 changes: 5 additions & 7 deletions reflex/vars/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
)

from rich.markup import escape
from sqlalchemy.orm import DeclarativeBase
from typing_extensions import deprecated, override

from reflex import constants
Expand Down Expand Up @@ -3318,18 +3317,17 @@ def dispatch(
).guess_type()


V = TypeVar("V")

BASE_TYPE = TypeVar("BASE_TYPE", bound=Base | None)
SQLA_TYPE = TypeVar("SQLA_TYPE", bound=DeclarativeBase | None)

if TYPE_CHECKING:
from _typeshed import DataclassInstance
from sqlalchemy.orm import DeclarativeBase

SQLA_TYPE = TypeVar("SQLA_TYPE", bound=DeclarativeBase | None)
BASE_TYPE = TypeVar("BASE_TYPE", bound=Base | None)
DATACLASS_TYPE = TypeVar("DATACLASS_TYPE", bound=DataclassInstance | None)
MAPPING_TYPE = TypeVar("MAPPING_TYPE", bound=Mapping | None)
V = TypeVar("V")

FIELD_TYPE = TypeVar("FIELD_TYPE")
MAPPING_TYPE = TypeVar("MAPPING_TYPE", bound=Mapping | None)


class Field(Generic[FIELD_TYPE]):
Expand Down
Loading