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

feat: live reloading when running as a module #858

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
37 changes: 19 additions & 18 deletions robyn/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import multiprocess as mp
from nestd import get_all_nested

from robyn import status_codes
from robyn import status_codes, dev_server
VishnuSanal marked this conversation as resolved.
Show resolved Hide resolved
from robyn.argument_parser import Config
from robyn.authentication import AuthenticationHandler
from robyn.dependency_injection import DependencyMap
Expand Down Expand Up @@ -56,8 +56,6 @@ def __init__(
"SERVER IS RUNNING IN VERBOSE/DEBUG MODE. Set --log-level to WARN to run in production mode.",
color=Colors.BLUE,
)
if self.config.dev:
exit("Dev mode is not supported in the python wrapper. Please use the CLI. e.g. python3 -m robyn app.py --dev ")

self.router = Router()
self.middleware_router = MiddlewareRouter()
Expand Down Expand Up @@ -225,21 +223,24 @@ def start(self, host: str = "127.0.0.1", port: int = 8080, _check_port: bool = T

mp.allow_connection_pickling()

run_processes(
host,
port,
self.directories,
self.request_headers,
self.router.get_routes(),
self.middleware_router.get_global_middlewares(),
self.middleware_router.get_route_middlewares(),
self.web_socket_router.get_routes(),
self.event_handlers,
self.config.workers,
self.config.processes,
self.response_headers,
open_browser,
)
if self.config.dev:
dev_server.start_dev_server(self.config, self.config.file_path)
else:
run_processes(
host,
port,
self.directories,
self.request_headers,
self.router.get_routes(),
self.middleware_router.get_global_middlewares(),
self.middleware_router.get_route_middlewares(),
self.web_socket_router.get_routes(),
self.event_handlers,
self.config.workers,
self.config.processes,
self.response_headers,
open_browser,
)

def exception(self, exception_handler: Callable):
self.exception_handler = exception_handler
Expand Down
3 changes: 3 additions & 0 deletions robyn/argument_parser.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import argparse
import sys


class Config:
Expand Down Expand Up @@ -87,6 +88,8 @@ def __init__(self) -> None:
if arg.endswith(".py"):
VishnuSanal marked this conversation as resolved.
Show resolved Hide resolved
self.file_path = arg
break
else:
self.file_path = sys.argv[0]
VishnuSanal marked this conversation as resolved.
Show resolved Hide resolved

if self.dev and (self.processes != 1 or self.workers != 1):
raise Exception("--processes and --workers shouldn't be used with --dev")
Expand Down
19 changes: 4 additions & 15 deletions robyn/cli.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import os
import sys
from typing import Optional
import webbrowser
from InquirerPy.resolver import prompt
from InquirerPy.base.control import Choice

from . import dev_server
VishnuSanal marked this conversation as resolved.
Show resolved Hide resolved
from .argument_parser import Config
from .reloader import create_rust_file, setup_reloader
from .reloader import create_rust_file
VishnuSanal marked this conversation as resolved.
Show resolved Hide resolved
from robyn.robyn import get_version
from pathlib import Path
import shutil
Expand Down Expand Up @@ -76,18 +77,6 @@ def docs():
webbrowser.open("https://robyn.tech")


def start_dev_server(config: Config, file_path: Optional[str] = None):
if file_path is None:
return

absolute_file_path = (Path.cwd() / file_path).resolve()
directory_path = absolute_file_path.parent

if config.dev and not os.environ.get("IS_RELOADER_RUNNING", False):
setup_reloader(str(directory_path), str(absolute_file_path))
return


def start_app_normally(config: Config):
# Parsing the known and unknown arguments
known_arguments, unknown_args = config.parser.parse_known_args()
Expand Down Expand Up @@ -118,6 +107,6 @@ def run():

elif config.dev:
print("Starting dev server...")
start_dev_server(config, config.file_path)
dev_server.start_dev_server(config, config.file_path)
else:
start_app_normally(config)
18 changes: 18 additions & 0 deletions robyn/dev_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import os
from pathlib import Path
from typing import Optional

from robyn import reloader
from robyn.argument_parser import Config


def start_dev_server(config: Config, file_path: Optional[str] = None):
VishnuSanal marked this conversation as resolved.
Show resolved Hide resolved
if file_path is None:
return

absolute_file_path = (Path.cwd() / file_path).resolve()
directory_path = absolute_file_path.parent

if config.dev and not os.environ.get("IS_RELOADER_RUNNING", False):
reloader.setup_reloader(config, str(directory_path), str(absolute_file_path))
return
27 changes: 19 additions & 8 deletions robyn/reloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer

from robyn.argument_parser import Config
from robyn.logger import Colors, logger

dir_path = None
Expand Down Expand Up @@ -58,8 +59,8 @@ def clean_rust_binaries(rust_binaries: List[str]):
os.remove(file)


def setup_reloader(directory_path: str, file_path: str):
event_handler = EventHandler(file_path, directory_path)
def setup_reloader(config: Config, directory_path: str, file_path: str):
event_handler = EventHandler(config, file_path, directory_path)

event_handler.reload()

Expand Down Expand Up @@ -91,7 +92,8 @@ def terminating_signal_handler(_sig, _frame):


class EventHandler(FileSystemEventHandler):
def __init__(self, file_path: str, directory_path: str) -> None:
def __init__(self, config: Config, file_path: str, directory_path: str) -> None:
self.config = config
self.file_path = file_path
self.directory_path = directory_path
self.process = None # Keep track of the subprocess
Expand All @@ -115,11 +117,20 @@ def reload(self):
clean_rust_binaries(self.built_rust_binaries)
self.built_rust_binaries = compile_rust_files(self.directory_path)

self.process = subprocess.Popen(
[sys.executable, *arguments],
env=new_env,
start_new_session=False,
)
if self.config.dev and self.config.file_path is not None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not use self.file_path?

Then we don't need to pass in config.

Copy link
Contributor Author

@VishnuSanal VishnuSanal Jun 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we also need config.dev. should I pass it as a variable?

edit: we also need running_as_module now.

module_name = self.config.file_path.split("/")[-2]
VishnuSanal marked this conversation as resolved.
Show resolved Hide resolved

self.process = subprocess.Popen(
[sys.executable, "-m", module_name, *arguments],
env=new_env,
start_new_session=False,
)
else:
self.process = subprocess.Popen(
[sys.executable, *arguments],
env=new_env,
start_new_session=False,
)

self.last_reload = time.time()

Expand Down
Loading