From 2f431afbd74bc63938ff063eb210f16dd3037959 Mon Sep 17 00:00:00 2001 From: Dima Gerasimov Date: Mon, 26 Aug 2024 03:34:45 +0100 Subject: [PATCH] tests: move remaining tests from tests/ to my.tests, cleanup corresponding modules --- my/body/weight.py | 35 ++++++++---- my/orgmode.py | 36 ++++++++---- my/pdfs.py | 8 +-- {tests => my/tests}/bluemaestro.py | 12 ++-- my/tests/body/weight.py | 57 +++++++++++++++++++ {tests => my/tests}/pdfs.py | 3 +- my/tests/reddit.py | 9 +-- tests/config.py | 89 ------------------------------ tox.ini | 5 -- 9 files changed, 120 insertions(+), 134 deletions(-) rename {tests => my/tests}/bluemaestro.py (86%) create mode 100644 my/tests/body/weight.py rename {tests => my/tests}/pdfs.py (99%) delete mode 100644 tests/config.py diff --git a/my/body/weight.py b/my/body/weight.py index def3e877..277b4d15 100644 --- a/my/body/weight.py +++ b/my/body/weight.py @@ -2,21 +2,29 @@ Weight data (manually logged) ''' +from dataclasses import dataclass from datetime import datetime -from typing import NamedTuple, Iterator +from typing import Any, Iterator -from ..core import LazyLogger -from ..core.error import Res, set_error_datetime, extract_error_datetime +from my.core import make_logger +from my.core.error import Res, extract_error_datetime, set_error_datetime -from .. import orgmode +from my import orgmode -from my.config import weight as config # type: ignore[attr-defined] +config = Any -log = LazyLogger('my.body.weight') +def make_config() -> config: + from my.config import weight as user_config # type: ignore[attr-defined] + return user_config() -class Entry(NamedTuple): + +log = make_logger(__name__) + + +@dataclass +class Entry: dt: datetime value: float # TODO comment?? @@ -26,6 +34,8 @@ class Entry(NamedTuple): def from_orgmode() -> Iterator[Result]: + cfg = make_config() + orgs = orgmode.query() for o in orgmode.query().all(): if 'weight' not in o.tags: @@ -46,8 +56,8 @@ def from_orgmode() -> Iterator[Result]: yield e continue # FIXME use timezone provider - created = config.default_timezone.localize(created) - assert created is not None #??? somehow mypy wasn't happy? + created = cfg.default_timezone.localize(created) + assert created is not None # ??? somehow mypy wasn't happy? yield Entry( dt=created, value=w, @@ -57,19 +67,21 @@ def from_orgmode() -> Iterator[Result]: def make_dataframe(data: Iterator[Result]): import pandas as pd + def it(): for e in data: if isinstance(e, Exception): dt = extract_error_datetime(e) yield { - 'dt' : dt, + 'dt': dt, 'error': str(e), } else: yield { - 'dt' : e.dt, + 'dt': e.dt, 'weight': e.value, } + df = pd.DataFrame(it()) df.set_index('dt', inplace=True) # TODO not sure about UTC?? @@ -81,6 +93,7 @@ def dataframe(): entries = from_orgmode() return make_dataframe(entries) + # TODO move to a submodule? e.g. my.body.weight.orgmode? # so there could be more sources # not sure about my.body thing though diff --git a/my/orgmode.py b/my/orgmode.py index c27f5a70..cf14e432 100644 --- a/my/orgmode.py +++ b/my/orgmode.py @@ -6,18 +6,28 @@ 'orgparse', ] +import re from datetime import datetime from pathlib import Path -import re -from typing import List, Sequence, Iterable, NamedTuple, Optional, Tuple +from typing import Iterable, List, NamedTuple, Optional, Sequence, Tuple + +import orgparse -from my.core import get_files +from my.core import Paths, Stats, get_files, stat from my.core.cachew import cache_dir, mcachew from my.core.orgmode import collect -from my.config import orgmode as user_config -import orgparse +class config: + paths: Paths + + +def make_config() -> config: + from my.config import orgmode as user_config + + class combined_config(user_config, config): ... + + return combined_config() # temporary? hack to cache org-mode notes @@ -28,10 +38,13 @@ class OrgNote(NamedTuple): def inputs() -> Sequence[Path]: - return get_files(user_config.paths) + cfg = make_config() + return get_files(cfg.paths) _rgx = re.compile(orgparse.date.gene_timestamp_regex(brtype='inactive'), re.VERBOSE) + + def _created(n: orgparse.OrgNode) -> Tuple[Optional[datetime], str]: heading = n.heading # meh.. support in orgparse? @@ -41,7 +54,7 @@ def _created(n: orgparse.OrgNode) -> Tuple[Optional[datetime], str]: # try to guess from heading m = _rgx.search(heading) if m is not None: - createds = m.group(0) # could be None + createds = m.group(0) # could be None if createds is None: return (None, heading) assert isinstance(createds, str) @@ -67,7 +80,7 @@ def to_note(x: orgparse.OrgNode) -> OrgNote: created = None return OrgNote( created=created, - heading=heading, # todo include the body? + heading=heading, # todo include the body? tags=list(x.tags), ) @@ -84,14 +97,15 @@ def _cachew_cache_path(_self, f: Path) -> Path: def _cachew_depends_on(_self, f: Path): return (f, f.stat().st_mtime) - + class Query: def __init__(self, files: Sequence[Path]) -> None: self.files = files # TODO yield errors? @mcachew( - cache_path=_cachew_cache_path, force_file=True, + cache_path=_cachew_cache_path, + force_file=True, depends_on=_cachew_depends_on, ) def _iterate(self, f: Path) -> Iterable[OrgNote]: @@ -114,8 +128,8 @@ def query() -> Query: return Query(files=inputs()) -from my.core import Stats, stat def stats() -> Stats: def outlines(): return query().all() + return stat(outlines) diff --git a/my/pdfs.py b/my/pdfs.py index 524c68b1..1cedfd50 100644 --- a/my/pdfs.py +++ b/my/pdfs.py @@ -10,7 +10,7 @@ import time from datetime import datetime from pathlib import Path -from typing import Iterator, List, NamedTuple, Optional, Protocol, Sequence +from typing import Iterator, List, NamedTuple, Optional, Protocol, Sequence, TYPE_CHECKING import pdfannots from more_itertools import bucket @@ -185,8 +185,6 @@ def stats() -> Stats: ### legacy/misc stuff -iter_annotations = annotations # for backwards compatibility +if not TYPE_CHECKING: + iter_annotations = annotations ### - -# can use 'hpi query my.pdfs.annotations -o pprint' to test -# diff --git a/tests/bluemaestro.py b/my/tests/bluemaestro.py similarity index 86% rename from tests/bluemaestro.py rename to my/tests/bluemaestro.py index 63ce589d..2d7c81e6 100644 --- a/tests/bluemaestro.py +++ b/my/tests/bluemaestro.py @@ -1,12 +1,12 @@ -from pathlib import Path from typing import Iterator -from more_itertools import one - import pytest +from more_itertools import one +from my.bluemaestro import Measurement, measurements +from my.core.cfg import tmp_config -from my.bluemaestro import measurements, Measurement +from .common import testdata def ok_measurements() -> Iterator[Measurement]: @@ -26,7 +26,7 @@ def test() -> None: # check that timezone is set properly assert dts == '20200824 22' - assert len(tp) == 1 # should be unique + assert len(tp) == 1 # should be unique # 2.5 K + 4 K datapoints, somewhat overlapping assert len(res2020) < 6000 @@ -46,14 +46,12 @@ def test_old_db() -> None: @pytest.fixture(autouse=True) def prepare(): - from my.tests.common import testdata bmdata = testdata() / 'hpi-testdata' / 'bluemaestro' assert bmdata.exists(), bmdata class bluemaestro: export_path = bmdata - from my.core.cfg import tmp_config with tmp_config() as config: config.bluemaestro = bluemaestro yield diff --git a/my/tests/body/weight.py b/my/tests/body/weight.py new file mode 100644 index 00000000..069e9409 --- /dev/null +++ b/my/tests/body/weight.py @@ -0,0 +1,57 @@ +from pathlib import Path +import pytz +from my.core.cfg import tmp_config +import pytest +from my.body.weight import from_orgmode + + +def test_body_weight() -> None: + weights = [0.0 if isinstance(x, Exception) else x.value for x in from_orgmode()] + + assert weights == [ + 0.0, + 62.0, + 0.0, + 61.0, + 62.0, + 0.0, + ] + + +@pytest.fixture(autouse=True) +def prepare(tmp_path: Path): + ndir = tmp_path / 'notes' + ndir.mkdir() + logs = ndir / 'logs.org' + logs.write_text( + ''' +#+TITLE: Stuff I'm logging + +* Weight (org-capture) :weight: +** [2020-05-01 Fri 09:00] 62 +** 63 + this should be ignored, got no timestamp +** [2020-05-03 Sun 08:00] 61 +** [2020-05-04 Mon 10:00] 62 +''' + ) + misc = ndir / 'misc.org' + misc.write_text( + ''' +Some misc stuff + +* unrelated note :weight:whatever: +''' + ) + + class orgmode: + paths = [ndir] + + class weight: + # TODO ugh. this belongs to tz provider or global config or something + default_timezone = pytz.timezone('Europe/London') + + with tmp_config() as cfg: + cfg.orgmode = orgmode + cfg.weight = weight + yield diff --git a/tests/pdfs.py b/my/tests/pdfs.py similarity index 99% rename from tests/pdfs.py rename to my/tests/pdfs.py index 6db669f2..bd1e93a0 100644 --- a/tests/pdfs.py +++ b/my/tests/pdfs.py @@ -5,9 +5,8 @@ from more_itertools import ilen from my.core.cfg import tmp_config -from my.tests.common import testdata - from my.pdfs import annotated_pdfs, annotations, get_annots +from my.tests.common import testdata def test_module(with_config) -> None: diff --git a/my/tests/reddit.py b/my/tests/reddit.py index fb8d6d2f..4f1ec516 100644 --- a/my/tests/reddit.py +++ b/my/tests/reddit.py @@ -1,15 +1,16 @@ +import pytest +from more_itertools import consume + from my.core.cfg import tmp_config from my.core.utils.itertools import ensure_unique -# todo ugh, it's discovered as a test??? from .common import testdata -from more_itertools import consume -import pytest # deliberately use mixed style imports on the top level and inside the methods to test tmp_config stuff -import my.reddit.rexport as my_reddit_rexport +# todo won't really be necessary once we migrate to lazy user config import my.reddit.all as my_reddit_all +import my.reddit.rexport as my_reddit_rexport def test_basic_1() -> None: diff --git a/tests/config.py b/tests/config.py deleted file mode 100644 index acfe1f1d..00000000 --- a/tests/config.py +++ /dev/null @@ -1,89 +0,0 @@ -from pathlib import Path - - -# TODO move this somewhere else -- there are more specific tests covering this now -def test_dynamic_configuration(notes: Path) -> None: - import pytz - from types import SimpleNamespace as NS - - from my.core.cfg import tmp_config - with tmp_config() as C: - C.orgmode = NS(paths=[notes]) - # TODO ugh. this belongs to tz provider or global config or something - C.weight = NS(default_timezone=pytz.timezone('Europe/London')) - - from my.body.weight import from_orgmode - weights = [0.0 if isinstance(x, Exception) else x.value for x in from_orgmode()] - - assert weights == [ - 0.0, - 62.0, - 0.0, - 61.0, - 62.0, - 0.0, - ] - -import pytest - - - -from dataclasses import dataclass - - -# TODO this test should probs be deprecated? it's more of a documentation? -def test_user_config() -> None: - from my.core.common import classproperty - class user_config: - param1 = 'abacaba' - # TODO fuck. properties don't work here??? - @classproperty - def param2(cls) -> int: - return 456 - - extra = 'extra!' - - @dataclass - class test_config(user_config): - param1: str - param2: int # type: ignore[assignment] # TODO need to figure out how to trick mypy for @classproperty - param3: str = 'default' - - assert test_config.param1 == 'abacaba' - assert test_config.param2 == 456 - assert test_config.param3 == 'default' - assert test_config.extra == 'extra!' - - from my.core.cfg import make_config - c = make_config(test_config) - assert c.param1 == 'abacaba' - assert c.param2 == 456 - assert c.param3 == 'default' - assert c.extra == 'extra!' - - -@pytest.fixture -def notes(tmp_path: Path): - ndir = tmp_path / 'notes' - ndir.mkdir() - logs = ndir / 'logs.org' - logs.write_text(''' -#+TITLE: Stuff I'm logging - -* Weight (org-capture) :weight: -** [2020-05-01 Fri 09:00] 62 -** 63 - this should be ignored, got no timestamp -** [2020-05-03 Sun 08:00] 61 -** [2020-05-04 Mon 10:00] 62 - ''') - misc = ndir / 'misc.org' - misc.write_text(''' -Some misc stuff - -* unrelated note :weight:whatever: - ''') - try: - yield ndir - finally: - pass diff --git a/tox.ini b/tox.ini index 20f730b3..6b95088d 100644 --- a/tox.ini +++ b/tox.ini @@ -86,11 +86,6 @@ commands = --pyargs {[testenv]package_name}.core {[testenv]package_name}.tests \ {posargs} - {envpython} -m pytest tests \ - # ignore some tests which might take a while to run on ci.. - --ignore tests/extra/polar.py - {posargs} - [testenv:demo] commands =