Skip to content

Commit 8a6b2a9

Browse files
committed
general: cleanup logging and warnings
- handle 'from promnesia import ...' warnings properly; previously they'd always be shown - more consistent logging in server (and slightly less) - suppress spammy warnings in tests (via conftest)
1 parent 122d5d3 commit 8a6b2a9

8 files changed

Lines changed: 91 additions & 74 deletions

File tree

src/promnesia/__init__.py

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
1-
# add deprecation warning so eventually this may converted to a namespace package?
2-
import warnings
1+
def __getattr__(name: str):
2+
# for backward compatibility
3+
deprecated_imports = [
4+
'Context',
5+
'DbVisit',
6+
'Loc',
7+
'PathIsh',
8+
'Res',
9+
'Results',
10+
'Source',
11+
'Visit',
12+
'last',
13+
]
14+
if name in deprecated_imports:
15+
import warnings
316

4-
from .common import ( # noqa: F401
5-
Context,
6-
DbVisit,
7-
Loc,
8-
PathIsh,
9-
Res,
10-
Results,
11-
Source,
12-
Visit,
13-
last,
14-
)
17+
warnings.warn(
18+
"DEPRECATED! Import directly from 'promnesia.common', e.g. 'from promnesia.common import Visit, Source, Results'",
19+
DeprecationWarning,
20+
)
1521

16-
# TODO think again about it -- what are the pros and cons?
17-
warnings.warn(
18-
"DEPRECATED! Please import directly from 'promnesia.common', e.g. 'from promnesia.common import Visit, Source, Results'",
19-
DeprecationWarning,
20-
)
22+
from . import common
23+
24+
return getattr(common, name)
25+
26+
# need to raise so other imports can proceed as usual
27+
raise AttributeError

src/promnesia/common.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import platformdirs
2424
from more_itertools import intersperse
25+
from typing_extensions import deprecated
2526

2627
from .cannon import canonify
2728

@@ -338,8 +339,8 @@ def __init__(self, ff: PreSource, *args, src: SourceName = '', name: SourceName
338339
self.args = args
339340
self.kwargs = kwargs
340341
self.extractor: Extractor = lambda: self.ff(*self.args, **self.kwargs)
341-
if src is not None:
342-
warnings.warn("'src' argument is deprecated, please use 'name' instead", DeprecationWarning)
342+
if src != '':
343+
warnings.warn("'src' argument is deprecated, use 'name' instead", DeprecationWarning)
343344
if name != '':
344345
self.name = name
345346
elif src != '':
@@ -357,8 +358,8 @@ def description(self) -> str:
357358
return f'{getattr(self.ff, "__module__", None)}:{getattr(self.ff, "__name__", None)} {self.args} {self.kwargs}'
358359

359360
@property
361+
@deprecated("'src' property is deprecated, use 'name' instead")
360362
def src(self) -> str:
361-
# TODO deprecated!
362363
return self.name
363364

364365

@@ -609,7 +610,8 @@ def measure(tag: str = '', *, logger: logging.Logger, unit: str = 'ms'):
609610
secs = after - before
610611
mult = {'s': 1, 'ms': 10**3, 'us': 10**6}[unit]
611612
xx = secs * mult
612-
logger.debug(f'[{tag}]: {xx:.1f}{unit} elapsed')
613+
if secs > 1:
614+
logger.warning(f'[{tag}]: {xx:.1f}{unit} elapsed')
613615

614616

615617
def is_sqlite_db(x: Path) -> bool:

src/promnesia/server.py

Lines changed: 34 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,13 @@ def get_db_path(*, check: bool = True) -> Path:
130130
return db
131131

132132

133+
# NOTE:
134+
# - this logging might appear multiple times in the logs for the same db/mtime because server uses multiple threads
135+
# - lru cache isn't ideal perhaps because there is no cleanup happening? but hasn't caused any issues so far
133136
@lru_cache(1)
134137
# PathWithMtime aids lru_cache in reloading the sqlalchemy binder
135138
def _get_stuff(db_path: PathWithMtime) -> DbStuff:
136-
get_logger().debug('Reloading DB: %s', db_path)
139+
get_logger().debug(f'reloading db: {db_path}')
137140
return get_db_stuff(db_path=db_path.path)
138141

139142

@@ -169,12 +172,11 @@ def search_common(url: str, where: Where) -> VisitsResponse:
169172
logger = get_logger()
170173
config = EnvConfig.get()
171174

172-
logger.info('url: %s', url)
173175
original_url = url and url.strip()
174176
url = canonify(original_url)
175177
if not url: # Don't eliminate a "#tag" query.
176178
url = original_url
177-
logger.info('normalised url: %s', url)
179+
logger.debug(f'normalised url {original_url!r} to {url!r}')
178180

179181
engine, table = get_stuff()
180182

@@ -192,7 +194,7 @@ def search_common(url: str, where: Where) -> VisitsResponse:
192194
# return result
193195
raise
194196

195-
logger.debug('got %d visits from db', len(visits))
197+
logger.debug(f'got {len(visits)} visits from db, responding')
196198

197199
vlist: list[DbVisit] = []
198200
for vis in visits:
@@ -202,7 +204,6 @@ def search_common(url: str, where: Where) -> VisitsResponse:
202204
vis = vis._replace(dt=dt)
203205
vlist.append(vis)
204206

205-
logger.debug('responding with %d visits', len(vlist))
206207
# TODO respond with normalised result, then frontent could choose how to present children/siblings/whatever?
207208
return VisitsResponse(
208209
original_url=original_url,
@@ -215,32 +216,34 @@ def search_common(url: str, where: Where) -> VisitsResponse:
215216
# perhasp should switch to get for most endpoint
216217
@app.get ('/status', response_model=Json) # fmt: skip
217218
@app.post('/status', response_model=Json) # fmt: skip
218-
def status() -> Json:
219+
def status(fastapi_request: fastapi.Request) -> Json:
219220
'''
220221
Ideally, status will always respond, regardless the internal state of the backend?
221222
'''
222-
logger = get_logger()
223+
get_logger().debug(f'{fastapi_request.url.path}')
223224

224225
db = get_db_path(check=False)
225-
try:
226-
assert db.exists(), db
226+
db_exists: bool = db.exists()
227+
if db_exists:
227228
db_path = str(db)
228-
except Exception as e:
229-
logger.exception(e)
230-
db_path = f'ERROR: db not found/unreadable (expected path {db}). You probably forgot to run indexer first. See https://github.com/karlicoss/promnesia/blob/master/doc/TROUBLESHOOTING.org'
229+
else:
230+
db_path = f'ERROR: db not found/unreadable (expected path {db}). You likely forgot to run indexer first. See https://github.com/karlicoss/promnesia/blob/master/doc/TROUBLESHOOTING.org.'
231231

232232
stats: Json
233-
try:
234-
stats = db_stats(db)
235-
except Exception as e:
236-
logger.exception(e)
237-
stats = {'ERROR': str(e)}
233+
if db_exists:
234+
try:
235+
stats = db_stats(db)
236+
except Exception as e:
237+
stats = {'ERROR': str(e)}
238+
else:
239+
stats = {'ERROR': 'db file does not exist'}
238240

239241
version: str | None
240242
try:
241243
version = get_version()
242244
except Exception as e:
243-
logger.exception(e)
245+
# this shouldn't really fail at all, so fine to log exception always
246+
get_logger().exception(e)
244247
version = None
245248

246249
return {
@@ -257,11 +260,10 @@ class VisitsRequest:
257260

258261
@app.get ('/visits', response_model=VisitsResponse) # fmt: skip
259262
@app.post('/visits', response_model=VisitsResponse) # fmt: skip
260-
def visits(request: VisitsRequest) -> VisitsResponse:
261-
url = request.url
262-
get_logger().info('/visited %s', url)
263+
def visits(request: VisitsRequest, fastapi_request: fastapi.Request) -> VisitsResponse:
264+
get_logger().debug(f'{fastapi_request.url.path} {request}')
263265
return search_common(
264-
url=url,
266+
url=request.url,
265267
# odd, doesn't work just with: x or (y and z)
266268
where=lambda table, url: or_(
267269
# exact match
@@ -279,21 +281,18 @@ class SearchRequest:
279281

280282
@app.get ('/search', response_model=VisitsResponse) # fmt: skip
281283
@app.post('/search', response_model=VisitsResponse) # fmt: skip
282-
def search(request: SearchRequest) -> VisitsResponse:
283-
url = request.url
284-
get_logger().info('/search %s', url)
285-
# fmt: off
284+
def search(request: SearchRequest, fastapi_request: fastapi.Request) -> VisitsResponse:
285+
get_logger().debug(f'{fastapi_request.url.path} {request}')
286286
return search_common(
287-
url=url,
287+
url=request.url,
288288
where=lambda table, url: or_(
289289
# todo hmm. think about it, not sure if I need proper indexer for fuzzy search etc?
290290
table.c.norm_url .contains(url, autoescape=True),
291291
table.c.orig_url .contains(url, autoescape=True),
292292
table.c.context .contains(url, autoescape=True),
293293
table.c.locator_title.contains(url, autoescape=True),
294294
),
295-
)
296-
# fmt: on
295+
) # fmt: skip
297296

298297

299298
@dataclass
@@ -303,10 +302,9 @@ class SearchAroundRequest:
303302

304303
@app.get ('/search_around', response_model=VisitsResponse) # fmt: skip
305304
@app.post('/search_around', response_model=VisitsResponse) # fmt: skip
306-
def search_around(request: SearchAroundRequest) -> VisitsResponse:
307-
timestamp = request.timestamp
308-
get_logger().info('/search_around %s', timestamp)
309-
utc_timestamp = timestamp # old 'timestamp' name is legacy
305+
def search_around(request: SearchAroundRequest, fastapi_request: fastapi.Request) -> VisitsResponse:
306+
get_logger().debug(f'{fastapi_request.url.path} {request}')
307+
utc_timestamp = request.timestamp # old 'timestamp' name is legacy
310308

311309
# TODO meh. use count/pagination instead?
312310
delta_back = timedelta(hours=3).total_seconds()
@@ -366,14 +364,12 @@ class VisitedRequest:
366364

367365
@app.get ('/visited', response_model=VisitedResponse) # fmt: skip
368366
@app.post('/visited', response_model=VisitedResponse) # fmt: skip
369-
def visited(request: VisitedRequest) -> VisitedResponse:
370-
# TODO instead switch logging to fastapi
367+
def visited(request: VisitedRequest, fastapi_request: fastapi.Request) -> VisitedResponse:
368+
get_logger().debug(f'{fastapi_request.url.path} {request}')
369+
371370
urls = request.urls
372371
client_version = request.client_version
373372

374-
logger = get_logger()
375-
logger.info('/visited %s %s', urls, client_version)
376-
377373
_version = as_version(client_version) # todo use it?
378374

379375
nurls = [canonify(u) for u in urls]

src/promnesia/sources/browser_legacy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from pathlib import Path
66
from urllib.parse import unquote
77

8-
from promnesia import config
8+
import promnesia.config as config
99
from promnesia.common import Loc, PathIsh, Results, Second, Visit, is_sqlite_db, logger
1010

1111
try:

src/promnesia/sources/takeout_legacy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def index() -> Results:
3535

3636
from more_itertools import unique_everseen
3737

38-
from promnesia import config
38+
import promnesia.config as config
3939

4040
try:
4141
from cachew import cachew

tests/addon.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from __future__ import annotations
66

7+
import functools
78
import json
89
from collections.abc import Iterator, Sequence
910
from contextlib import contextmanager
@@ -27,12 +28,8 @@
2728
from .common import logger
2829
from .webdriver_utils import frame_context, is_visible, wait_for_alert
2930

30-
31-
@contextmanager
32-
def measure(*args, **kwargs):
33-
kwargs['logger'] = logger
34-
with measure_orig(*args, **kwargs) as m:
35-
yield m
31+
# logger is a required arg there, so need to pass it..
32+
measure = functools.partial(measure_orig, logger=logger) # type: ignore[arg-type]
3633

3734

3835
@pytest.fixture
@@ -174,8 +171,7 @@ def set_checkbox(cid: str, value: bool) -> None: # noqa: FBT001
174171
if selected != value:
175172
cb.click()
176173

177-
# TODO log properly
178-
print(f"Setting: port {port}, show_dots {show_dots}")
174+
logger.debug(f"setting: {port=}, {show_dots=}")
179175

180176
self.open()
181177

tests/conftest.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import warnings
2+
3+
import pytest
4+
5+
6+
@pytest.fixture(autouse=True)
7+
def ignore_warnings():
8+
## This is coming from HPI since we use tmp_config stuff
9+
# Hmm looking at it, the whole thing is pretty annoying...
10+
# I think maybe instead what should happen is
11+
# - if my.config is already present _and_ it's not the stub config, just use it
12+
# - only otherwise try messing with it
13+
warnings.filterwarnings("ignore", message="'my.config' package isn't found!")
14+
##
15+
## These are coming from hypothesis data access layer, doesn't matter for tests
16+
warnings.filterwarnings("ignore", message="You might want to 'pip install colorlog'")
17+
warnings.filterwarnings("ignore", message="recommended to 'pip install ijson'")
18+
warnings.filterwarnings("ignore", message="recommended to 'pip install orjson'")
19+
##

tests/demos.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@
1010
from .common import uses_x
1111
from .record import CURSOR_SCRIPT, SELECT_SCRIPT, hotkeys, record
1212
from .test_end2end import ( # type: ignore[attr-defined]
13-
CHROME,
14-
FIREFOX,
1513
_test_helper, # ty: ignore[unresolved-import]
16-
browsers,
1714
configure_extension, # ty: ignore[unresolved-import]
1815
confirm,
1916
)
20-
from .webdriver_utils import get_webdriver
17+
from .webdriver_utils import CHROME, FIREFOX, browsers, get_webdriver
2118

2219

2320
def real_db():

0 commit comments

Comments
 (0)