Skip to content

Commit 7174c82

Browse files
authored
Merge pull request #11 from bcdev/forman/log_config
Changed signature and behavior of `serve()` function
2 parents 88b7570 + 259cecc commit 7174c82

4 files changed

Lines changed: 58 additions & 6 deletions

File tree

remotestate-py/CHANGES.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
## Version 0.1.1 (in development)
22

3+
- Added `__version__` attribute to main package.
4+
5+
- Changed signature and behavior of `serve()` function:
6+
- Renamed `iframe_heigh` argument into `height`, and added `width`.
7+
- It is no longer using FastAPI/Uvicorn default
8+
logging. Instead, all server logs are written to `server.log`
9+
and logging to stdout/stderr is suppressed.
310

411
## Version 0.1.0
512

remotestate-py/src/remotestate/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
"""Public package exports for building and serving ``remotestate`` apps."""
22

3+
from importlib.metadata import version
4+
35
from .service import Service, action, query
46
from .serve import serve
57
from .store import Store
68

9+
__version__ = version("remotestate")
10+
711
__all__ = [
812
"Store",
913
"Service",

remotestate-py/src/remotestate/serve.py

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
DEFAULT_HOST = "localhost"
3030
DEFAULT_PORT = 9753
31+
DEFAULT_IFRAME_WIDTH = "100%"
3132
DEFAULT_IFRAME_HEIGHT = 400
3233

3334
_servers: dict[str, uvicorn.Server] = {}
@@ -41,7 +42,8 @@ def serve(
4142
app: FastAPI | None = None,
4243
open_browser: bool | None = None,
4344
open_iframe: bool | None = None,
44-
iframe_height: int = DEFAULT_IFRAME_HEIGHT,
45+
width: int | str = DEFAULT_IFRAME_WIDTH,
46+
height: int | str = DEFAULT_IFRAME_HEIGHT,
4547
# --- Uvicorn
4648
host: str = DEFAULT_HOST,
4749
port: int = DEFAULT_PORT,
@@ -59,12 +61,13 @@ def serve(
5961
`fastapi.staticfiles.StaticFiles` object or a directory path.
6062
app: A FastAPI instance to use. If not provided,
6163
a new instance is created and passed to `Service.init_app(app)`
62-
so that it can by initialized by the user.
64+
so that it can be initialized by the user.
6365
open_browser: Open the UI in the default browser after starting.
6466
Defaults to True when not running in Jupyter.
6567
open_iframe: Render the UI as an IFrame in the Jupyter notebook.
6668
Defaults to True when running in Jupyter.
67-
iframe_height: Height of the IFrame in pixels.
69+
width: Width of the IFrame.
70+
height: Height of the IFrame.
6871
host: Host to bind the server to.
6972
port: Port to bind the server to.
7073
uvicorn_settings: Additional [uvicorn settings]((https://uvicorn.dev/settings/)
@@ -93,10 +96,14 @@ def serve(
9396
else:
9497
mounts_["/"] = StaticFiles(directory=ui_dist, html=True)
9598

96-
remotestate_server = Server(service=service, mounts=mounts_, app=app)
99+
rs_server = Server(service=service, mounts=mounts_, app=app)
97100

98101
uvicorn_settings.update(host=host, port=port)
99-
uvicorn_config = uvicorn.Config(remotestate_server.app, **uvicorn_settings)
102+
if "log_config" not in uvicorn_settings:
103+
# disable uvicorn's default logging setup
104+
uvicorn_settings.update(log_config=_get_log_config())
105+
106+
uvicorn_config = uvicorn.Config(rs_server.app, **uvicorn_settings)
100107
uvicorn_server = uvicorn.Server(uvicorn_config)
101108
_servers[registry_key] = uvicorn_server
102109

@@ -119,7 +126,7 @@ def serve(
119126
if should_open_iframe:
120127
from IPython.display import IFrame, display
121128

122-
display(IFrame(src=ui_dist_url, width="100%", height=iframe_height))
129+
display(IFrame(src=ui_dist_url, width=width, height=height))
123130
elif should_open_browser:
124131
webbrowser.open(ui_dist_url)
125132

@@ -158,6 +165,7 @@ def _add_ui_url_params(ui_dist_url: str, *, host: str, port: int) -> str:
158165
("ws", f"ws://{host}:{port}/ws"),
159166
]
160167
)
168+
# noinspection PyTypeChecker
161169
return urlunsplit(url_parts._replace(query=urlencode(query)))
162170

163171

@@ -179,3 +187,35 @@ def _wait_for_port_free(host: str, port: int, timeout: float = 5.0) -> None:
179187
pass # port still in use
180188
time.sleep(0.05)
181189
raise TimeoutError(f"Port {port} did not become free within {timeout}s")
190+
191+
192+
def _get_log_config(log_file: str | PathLike = "server.log") -> dict[str, Any]:
193+
return {
194+
"version": 1,
195+
"disable_existing_loggers": False,
196+
"handlers": {
197+
"file": {
198+
"class": "logging.FileHandler",
199+
"filename": str(log_file),
200+
"formatter": "default",
201+
}
202+
},
203+
"formatters": {
204+
"default": {
205+
"format": "%(asctime)s %(levelname)s %(name)s: %(message)s",
206+
}
207+
},
208+
"loggers": {
209+
"uvicorn": {"handlers": ["file"], "level": "INFO", "propagate": False},
210+
"uvicorn.error": {
211+
"handlers": ["file"],
212+
"level": "INFO",
213+
"propagate": False,
214+
},
215+
"uvicorn.access": {
216+
"handlers": ["file"],
217+
"level": "INFO",
218+
"propagate": False,
219+
},
220+
},
221+
}

remotestate-py/tests/test_serve.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import pytest
99

10+
# noinspection PyProtectedMember
1011
from remotestate.serve import (
1112
_add_ui_url_params,
1213
_get_cell_id,

0 commit comments

Comments
 (0)