Skip to content

Commit 814ab8b

Browse files
committed
remove loguru and use builtin logging module
1 parent 3e8b9ea commit 814ab8b

File tree

15 files changed

+127
-66
lines changed

15 files changed

+127
-66
lines changed

.github/workflows/test.yml

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ name: Test
22

33
on:
44
push:
5+
branches:
6+
- main
57
pull_request:
68
branches:
79
- main

idom/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
# package is not installed
88
__version__ = "0.0.0"
99

10+
from . import log
1011
from .client.module import Import, Module, install
1112
from .core import hooks
1213
from .core.component import Component, component
@@ -56,4 +57,5 @@
5657
"widgets",
5758
"client",
5859
"install",
60+
"log",
5961
]

idom/__main__.py

-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,4 @@
1-
import sys
2-
3-
from loguru import logger
4-
51
from .cli import main
62

7-
logger.remove(0)
8-
logger.add(sys.stdout, format="{message}", level="INFO")
9-
10-
113
if __name__ == "__main__":
124
main()

idom/_options.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import os
2+
from typing import Any, Callable, Dict, Type
3+
4+
IDOM_DEBUG_MODE = False
5+
6+
7+
def _load_os_environ() -> None:
8+
from_string: Dict[Type[Any], Callable[[Any], Any]] = {
9+
bool: lambda v: bool(int(v)),
10+
}
11+
12+
module = globals()
13+
for name, default in globals().items():
14+
if not name.startswith("_") and name.upper() == name:
15+
value_type = type(default)
16+
value = os.environ.get(name, default)
17+
if value_type in from_string:
18+
value = from_string[value_type](value)
19+
module[name] = value
20+
21+
return None
22+
23+
24+
_load_os_environ()

idom/client/manage.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
import json
22
import shutil
33
import subprocess
4+
from logging import getLogger
45
from pathlib import Path
56
from tempfile import TemporaryDirectory
67
from typing import List, Sequence, Set, Union
78

8-
from loguru import logger
9-
109
from .utils import (
1110
find_js_module_exports_in_source,
1211
get_package_name,
1312
open_modifiable_json,
1413
)
1514

15+
logger = getLogger(__name__)
16+
1617
APP_DIR = Path(__file__).parent / "app"
1718
BUILD_DIR = APP_DIR / "build"
1819
WEB_MODULES_DIR = BUILD_DIR / "web_modules"
@@ -102,7 +103,7 @@ def build(packages_to_install: Sequence[str], clean_build: bool = False) -> None
102103
snowpack_build = snowpack_config.setdefault("buildOptions", {})
103104
snowpack_build["clean"] = clean_build
104105

105-
logger.info(f"Installing {packages_to_install or 'packages'} ...", debug=True)
106+
logger.info(f"Installing {packages_to_install or 'packages'} ...")
106107
_npm_install(packages_to_install, temp_app_dir)
107108
logger.info("Installed successfully ✅")
108109

idom/core/dispatcher.py

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import abc
22
import asyncio
3+
from logging import getLogger
34
from typing import Any, AsyncIterator, Awaitable, Callable, Dict
45

56
from anyio import create_task_group
@@ -8,6 +9,8 @@
89
from .layout import Layout, LayoutEvent, LayoutUpdate
910
from .utils import HasAsyncResources, async_resource
1011

12+
logger = getLogger(__name__)
13+
1114
SendCoroutine = Callable[[Any], Awaitable[None]]
1215
RecvCoroutine = Callable[[], Awaitable[LayoutEvent]]
1316

@@ -49,12 +52,20 @@ async def run(self, send: SendCoroutine, recv: RecvCoroutine, context: Any) -> N
4952
return None
5053

5154
async def _outgoing_loop(self, send: SendCoroutine, context: Any) -> None:
52-
while True:
53-
await send(await self._outgoing(self.layout, context))
55+
try:
56+
while True:
57+
await send(await self._outgoing(self.layout, context))
58+
except Exception:
59+
logger.info("Failed to send outgoing update", exc_info=True)
60+
raise
5461

5562
async def _incoming_loop(self, recv: RecvCoroutine, context: Any) -> None:
56-
while True:
57-
await self._incoming(self.layout, context, await recv())
63+
try:
64+
while True:
65+
await self._incoming(self.layout, context, await recv())
66+
except Exception:
67+
logger.info("Failed to receive incoming event", exc_info=True)
68+
raise
5869

5970
@abc.abstractmethod
6071
async def _outgoing(self, layout: Layout, context: Any) -> Any:

idom/core/hooks.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
from logging import getLogger
23
from threading import get_ident as get_thread_id
34
from typing import (
45
Any,
@@ -17,7 +18,6 @@
1718
overload,
1819
)
1920

20-
from loguru import logger
2121
from typing_extensions import Protocol
2222

2323
from idom.utils import Ref
@@ -33,6 +33,7 @@
3333
"use_memo",
3434
]
3535

36+
logger = getLogger(__name__)
3637

3738
_StateType = TypeVar("_StateType")
3839

idom/core/layout.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import abc
22
import asyncio
33
from functools import wraps
4+
from logging import getLogger
45
from typing import (
56
Any,
67
AsyncIterator,
@@ -16,16 +17,17 @@
1617
)
1718

1819
from jsonpatch import apply_patch, make_patch
19-
from loguru import logger
2020

21-
from idom.options import IDOM_DEBUG
21+
from idom._options import IDOM_DEBUG_MODE
2222

2323
from .component import AbstractComponent
2424
from .events import EventHandler, EventTarget
2525
from .hooks import LifeCycleHook
2626
from .utils import CannotAccessResource, HasAsyncResources, async_resource
2727
from .vdom import validate_serialized_vdom
2828

29+
logger = getLogger(__name__)
30+
2931

3032
class LayoutUpdate(NamedTuple):
3133
"""An object describing an update to a :class:`Layout`"""
@@ -89,16 +91,18 @@ async def dispatch(self, event: LayoutEvent) -> None:
8991
handler = self._event_handlers.get(event.target)
9092
if handler is not None:
9193
await handler(event.data)
94+
else:
95+
logger.info(
96+
f"Ignored event - handler {event.target!r} does not exist or its component unmounted"
97+
)
9298

9399
async def render(self) -> LayoutUpdate:
94100
while True:
95101
component = await self._rendering_queue.get()
96102
if self._has_component_state(component):
97103
return self._create_layout_update(component)
98104

99-
if IDOM_DEBUG:
100-
from loguru import logger
101-
105+
if IDOM_DEBUG_MODE:
102106
_debug_render = render
103107

104108
@wraps(_debug_render)

idom/log.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import logging
2+
import sys
3+
from logging.config import dictConfig
4+
5+
from ._options import IDOM_DEBUG_MODE
6+
7+
root_logger = logging.getLogger("idom")
8+
9+
10+
LOGGING_CONFIG_DEFAULTS = {
11+
"version": 1,
12+
"disable_existing_loggers": False,
13+
"loggers": {
14+
"idom": {
15+
"level": "DEBUG" if IDOM_DEBUG_MODE else "INFO",
16+
"handlers": ["console"],
17+
},
18+
},
19+
"handlers": {
20+
"console": {
21+
"class": "logging.StreamHandler",
22+
"formatter": "generic",
23+
"stream": sys.stdout,
24+
}
25+
},
26+
"formatters": {
27+
"generic": {
28+
"format": "%(asctime)s | %(levelname)s | %(name)s | %(message)s",
29+
"datefmt": r"%Y-%m-%dT%H:%M:%S%z",
30+
"class": "logging.Formatter",
31+
}
32+
},
33+
}
34+
35+
36+
dictConfig(LOGGING_CONFIG_DEFAULTS)

idom/options.py

-25
This file was deleted.

idom/testing.py

+15-14
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
)
1616
from urllib.parse import urlencode, urlunparse
1717

18-
from loguru import logger
1918
from selenium.webdriver import Chrome
2019
from selenium.webdriver.remote.webdriver import WebDriver
2120

@@ -61,7 +60,6 @@ class ServerMountPoint(Generic[_Mount, _Server]):
6160
server: _Server
6261

6362
_log_handler: "_LogRecordCaptor"
64-
_loguru_handler_id: int
6563

6664
def __init__(
6765
self,
@@ -113,20 +111,28 @@ def assert_logged_exception(
113111
if clear_after:
114112
self.log_records.clear()
115113

116-
def raise_first_logged_exception(
114+
def raise_if_logged_exception(
117115
self,
116+
log_level: int = logging.ERROR,
118117
exclude_exc_types: Union[Type[Exception], Tuple[Type[Exception], ...]] = (),
118+
clear_after: bool = True,
119119
) -> None:
120120
"""Raise the first logged exception (if any)
121121
122122
Args:
123+
log_level: The level of log to check
123124
exclude_exc_types: Any exception types to ignore
125+
clear_after: Whether to clear logs after check
124126
"""
125-
for record in self._log_handler.records:
126-
if record.exc_info is not None:
127-
error = record.exc_info[1]
128-
if error is not None and not isinstance(error, exclude_exc_types):
129-
raise error
127+
try:
128+
for record in self._log_handler.records:
129+
if record.levelno >= log_level and record.exc_info is not None:
130+
error = record.exc_info[1]
131+
if error is not None and not isinstance(error, exclude_exc_types):
132+
raise error
133+
finally:
134+
if clear_after:
135+
self.log_records.clear()
130136

131137
def url(self, path: str = "", query: Optional[Any] = None) -> str:
132138
return urlunparse(
@@ -143,7 +149,6 @@ def url(self, path: str = "", query: Optional[Any] = None) -> str:
143149
def __enter__(self: _Self) -> _Self:
144150
self._log_handler = _LogRecordCaptor()
145151
logging.getLogger().addHandler(self._log_handler)
146-
self._loguru_handler_id = logger.add(self._log_handler, format="{message}")
147152
self.mount, self.server = self._mount_and_server_constructor()
148153
return self
149154

@@ -154,12 +159,8 @@ def __exit__(
154159
traceback: Optional[TracebackType],
155160
) -> None:
156161
self.server.stop()
157-
158162
logging.getLogger().removeHandler(self._log_handler)
159-
logger.remove(self._loguru_handler_id)
160-
161-
self.raise_first_logged_exception()
162-
163+
self.raise_if_logged_exception()
163164
return None
164165

165166

noxfile.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def test(session: Session) -> None:
8585
@nox.session
8686
def test_python(session: Session) -> None:
8787
"""Run the Python-based test suite"""
88-
session.env["IDOM_DEBUG"] = "1"
88+
session.env["IDOM_DEBUG_MODE"] = "1"
8989
session.install("-r", "requirements/test-env.txt")
9090
session.install(".[all]")
9191
args = ["pytest", "tests"] + get_posargs("pytest", session)

requirements/pkg-deps.txt

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
loguru >=0.3.2
21
typing-extensions >=3.7.4
32
mypy-extensions >=0.4.3
43
anyio >=2.0

tests/conftest.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from _pytest.config.argparsing import Parser
1212
from _pytest.logging import LogCaptureFixture
1313
from _pytest.logging import caplog as _caplog # noqa
14-
from loguru import logger
1514
from selenium.webdriver import Chrome, ChromeOptions
1615
from selenium.webdriver.support.ui import WebDriverWait
1716

@@ -110,9 +109,9 @@ def driver_is_headless(pytestconfig: Config):
110109

111110
@pytest.fixture(autouse=True)
112111
def caplog(_caplog: LogCaptureFixture) -> Iterator[LogCaptureFixture]:
113-
handler_id = logger.add(_PropogateHandler(), format="{message}")
112+
_caplog.set_level(logging.DEBUG)
114113
yield _caplog
115-
logger.remove(handler_id)
114+
# check that there are no ERROR level log messages
116115
for record in _caplog.records:
117116
if record.exc_info:
118117
raise record.exc_info[1]

0 commit comments

Comments
 (0)