Skip to content

Commit

Permalink
use context manager as a more explicit way to configurate the logger
Browse files Browse the repository at this point in the history
  • Loading branch information
glemaitre committed Dec 20, 2024
1 parent 801795c commit 376099c
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 131 deletions.
3 changes: 0 additions & 3 deletions skore/src/skore/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,6 @@ def cli(args: list[str]):
parser.add_argument(
"--version", action="version", version=f"%(prog)s {version('skore')}"
)
parser.add_argument(
"-v", "--verbose", action="store_true", help="Increase logging verbosity"
)

subparsers = parser.add_subparsers(dest="subcommand")

Expand Down
42 changes: 21 additions & 21 deletions skore/src/skore/cli/launch_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
from skore.cli import logger
from skore.project import load
from skore.ui.app import create_app
from skore.utils._logger import with_logging
from skore.utils._logger import logger_context


@with_logging(logger)
def __launch(
project_name: Union[str, Path], port: int, open_browser: bool, verbose: bool = False
):
Expand All @@ -33,22 +32,23 @@ def __launch(
"""
from skore import console # avoid circular import

project = load(project_name)

@asynccontextmanager
async def lifespan(app: FastAPI):
if open_browser:
webbrowser.open(f"http://localhost:{port}")
yield

app = create_app(project=project, lifespan=lifespan)

try:
# TODO: check port is free
console.rule("[bold cyan]skore-UI[/bold cyan]")
console.print(
f"Running skore UI from '{project_name}' at URL http://localhost:{port}"
)
uvicorn.run(app, port=port, log_level="error")
except KeyboardInterrupt:
console.print("Closing skore UI")
with logger_context(logger, verbose):
project = load(project_name)

@asynccontextmanager
async def lifespan(app: FastAPI):
if open_browser:
webbrowser.open(f"http://localhost:{port}")
yield

app = create_app(project=project, lifespan=lifespan)

try:
# TODO: check port is free
console.rule("[bold cyan]skore-UI[/bold cyan]")
console.print(
f"Running skore UI from '{project_name}' at URL http://localhost:{port}"
)
uvicorn.run(app, port=port, log_level="error")
except KeyboardInterrupt:
console.print("Closing skore UI")
34 changes: 17 additions & 17 deletions skore/src/skore/cli/quickstart_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
from skore.cli.launch_dashboard import __launch
from skore.exceptions import ProjectAlreadyExistsError
from skore.project import create
from skore.utils._logger import with_logging
from skore.utils._logger import logger_context


@with_logging(logger)
def __quickstart(
project_name: Union[str, Path],
working_dir: Optional[Path],
Expand Down Expand Up @@ -42,21 +41,22 @@ def __quickstart(
verbose : bool
Whether to increase logging verbosity.
"""
try:
create(
with logger_context(logger, verbose):
try:
create(
project_name=project_name,
working_dir=working_dir,
overwrite=overwrite,
verbose=verbose,
)
except ProjectAlreadyExistsError:
logger.info(
f"Project file '{project_name}' already exists. Skipping creation step."
)

__launch(
project_name=project_name,
working_dir=working_dir,
overwrite=overwrite,
port=port,
open_browser=open_browser,
verbose=verbose,
)
except ProjectAlreadyExistsError:
logger.info(
f"Project file '{project_name}' already exists. Skipping creation step."
)

__launch(
project_name=project_name,
port=port,
open_browser=open_browser,
verbose=verbose,
)
142 changes: 73 additions & 69 deletions skore/src/skore/project/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
)
from skore.project.load import load
from skore.project.project import Project, logger
from skore.utils._logger import with_logging
from skore.utils._logger import logger_context
from skore.view.view import View


Expand Down Expand Up @@ -58,7 +58,6 @@ def _validate_project_name(project_name: str) -> tuple[bool, Optional[Exception]
return True, None


@with_logging(logger)
def create(
project_name: Union[str, Path],
working_dir: Optional[Path] = None,
Expand Down Expand Up @@ -88,70 +87,75 @@ def create(
"""
from skore import console # avoid circular import

project_path = Path(project_name)

# Remove trailing ".skore" if it exists to check the name is valid
checked_project_name: str = project_path.name.split(".skore")[0]

validation_passed, validation_error = _validate_project_name(checked_project_name)
if not validation_passed:
raise ProjectCreationError(
f"Unable to create project file '{project_path}'."
) from validation_error

# The file must end with the ".skore" extension.
# If not provided, it will be automatically appended.
# If project name is an absolute path, we keep that path

# NOTE: `working_dir` has no effect if `checked_project_name` is absolute
if working_dir is None:
working_dir = Path.cwd()
project_directory = working_dir / (
project_path.with_name(checked_project_name + ".skore")
)

if project_directory.exists():
if not overwrite:
raise ProjectAlreadyExistsError(
f"Unable to create project file '{project_directory}' because a file "
"with that name already exists. Please choose a different name or "
"use the --overwrite flag with the CLI or overwrite=True with the API."
)
shutil.rmtree(project_directory)

try:
project_directory.mkdir(parents=True)
except PermissionError as e:
raise ProjectPermissionError(
f"Unable to create project file '{project_directory}'. "
"Please check your permissions for the current directory."
) from e
except Exception as e:
raise ProjectCreationError(
f"Unable to create project file '{project_directory}'."
) from e

# Once the main project directory has been created, created the nested directories

items_dir = project_directory / "items"
try:
items_dir.mkdir()
except Exception as e:
raise ProjectCreationError(
f"Unable to create project file '{items_dir}'."
) from e

views_dir = project_directory / "views"
try:
views_dir.mkdir()
except Exception as e:
raise ProjectCreationError(
f"Unable to create project file '{views_dir}'."
) from e

p = load(project_directory)
p.put_view("default", View(layout=[]))

console.rule("[bold cyan]skore[/bold cyan]")
console.print(f"Project file '{project_directory}' was successfully created.")
return p
with logger_context(logger, verbose):
project_path = Path(project_name)

# Remove trailing ".skore" if it exists to check the name is valid
checked_project_name: str = project_path.name.split(".skore")[0]

validation_passed, validation_error = _validate_project_name(
checked_project_name
)
if not validation_passed:
raise ProjectCreationError(
f"Unable to create project file '{project_path}'."
) from validation_error

# The file must end with the ".skore" extension.
# If not provided, it will be automatically appended.
# If project name is an absolute path, we keep that path

# NOTE: `working_dir` has no effect if `checked_project_name` is absolute
if working_dir is None:
working_dir = Path.cwd()
project_directory = working_dir / (
project_path.with_name(checked_project_name + ".skore")
)

if project_directory.exists():
if not overwrite:
raise ProjectAlreadyExistsError(
f"Unable to create project file '{project_directory}' because a "
"file with that name already exists. Please choose a different "
"name or use the --overwrite flag with the CLI or overwrite=True "
"with the API."
)
shutil.rmtree(project_directory)

try:
project_directory.mkdir(parents=True)
except PermissionError as e:
raise ProjectPermissionError(
f"Unable to create project file '{project_directory}'. "
"Please check your permissions for the current directory."
) from e
except Exception as e:
raise ProjectCreationError(
f"Unable to create project file '{project_directory}'."
) from e

# Once the main project directory has been created, created the nested
# directories

items_dir = project_directory / "items"
try:
items_dir.mkdir()
except Exception as e:
raise ProjectCreationError(
f"Unable to create project file '{items_dir}'."
) from e

views_dir = project_directory / "views"
try:
views_dir.mkdir()
except Exception as e:
raise ProjectCreationError(
f"Unable to create project file '{views_dir}'."
) from e

p = load(project_directory)
p.put_view("default", View(layout=[]))

console.rule("[bold cyan]skore[/bold cyan]")
console.print(f"Project file '{project_directory}' was successfully created.")
return p
36 changes: 15 additions & 21 deletions skore/src/skore/utils/_logger.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Module to handle the verbosity of a given logger."""

import logging
from functools import wraps
from contextlib import contextmanager

from rich.logging import LogRecord, RichHandler, Text

Expand All @@ -21,23 +21,17 @@ def get_level_text(self, record: LogRecord) -> Text:
)


def with_logging(logger):
def decorator(func):
@wraps(func)
def wrapper(*args, verbose=False, **kwargs):
if verbose:
formatter = logging.Formatter("%(message)s")
console_handler = Handler(markup=True)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)

try:
result = func(*args, **kwargs)
return result
finally:
if verbose:
logger.handlers.pop()

return wrapper

return decorator
@contextmanager
def logger_context(logger, verbose=False):
"""Context manager for temporarily adding a Rich handler to a logger."""
handler = None
try:
if verbose:
formatter = logging.Formatter("%(message)s")
handler = Handler(markup=True)
handler.setFormatter(formatter)
logger.addHandler(handler)
yield
finally:
if verbose and handler:
logger.removeHandler(handler)

0 comments on commit 376099c

Please sign in to comment.