diff --git a/.ci/run b/.ci/run index 7fa3c69..7fc809f 100755 --- a/.ci/run +++ b/.ci/run @@ -11,6 +11,8 @@ if ! command -v sudo; then } fi +# --parallel-live to show outputs while it's running +tox_cmd='run-parallel --parallel-live' if [ -n "${CI-}" ]; then # install OS specific stuff here case "$OSTYPE" in @@ -21,7 +23,8 @@ if [ -n "${CI-}" ]; then ;; cygwin* | msys* | win*) # windows - : + # ugh. parallel stuff seems super flaky under windows, some random failures, "file used by other process" and crap like that + tox_cmd='run' ;; *) # must be linux? @@ -40,5 +43,9 @@ if ! command -v python3 &> /dev/null; then PY_BIN="python" fi -"$PY_BIN" -m pip install --user tox -"$PY_BIN" -m tox --parallel --parallel-live "$@" + +# TODO hmm for some reason installing uv with pip and then running +# "$PY_BIN" -m uv tool fails with missing setuptools error?? +# just uvx directly works, but it's not present in PATH... +"$PY_BIN" -m pip install --user pipx +"$PY_BIN" -m pipx run uv tool run --with=tox-uv tox $tox_cmd "$@" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2724186..0d8b716 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -25,7 +25,7 @@ jobs: fail-fast: false matrix: platform: [ubuntu-latest, macos-latest] # todo enable windows-latest later -- need to fix a couple of tests first - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] # vvv just an example of excluding stuff from matrix # exclude: [{platform: macos-latest, python-version: '3.6'}] @@ -56,8 +56,15 @@ jobs: - if: matrix.platform == 'ubuntu-latest' # no need to compute coverage for other platforms uses: actions/upload-artifact@v4 with: - name: .coverage.mypy_${{ matrix.platform }}_${{ matrix.python-version }} - path: .coverage.mypy/ + include-hidden-files: true + name: .coverage.mypy-core_${{ matrix.platform }}_${{ matrix.python-version }} + path: .coverage.mypy-core/ + - if: matrix.platform == 'ubuntu-latest' # no need to compute coverage for other platforms + uses: actions/upload-artifact@v4 + with: + include-hidden-files: true + name: .coverage.mypy-misc_${{ matrix.platform }}_${{ matrix.python-version }} + path: .coverage.mypy-misc/ pypi: @@ -70,7 +77,7 @@ jobs: - uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.10' - uses: actions/checkout@v4 with: diff --git a/mypy.ini b/mypy.ini index 5a21a85..9ab1c91 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,14 +1,13 @@ [mypy] -namespace_packages = True pretty = True show_error_context = True -show_error_codes = True show_column_numbers = True show_error_end = True +warn_redundant_casts = True warn_unused_ignores = True check_untyped_defs = True -enable_error_code = possibly-undefined strict_equality = True +enable_error_code = possibly-undefined # an example of suppressing # [mypy-my.config.repos.pdfannots.pdfannots] diff --git a/pyproject.toml b/pyproject.toml index c38ea50..16c00e9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,6 +6,7 @@ dependencies = [ "appdirs" , # to keep state files "atomicwrites", # to safely append data to a file ] +requires-python = ">= 3.9" ## these need to be set if you're planning to upload to pypi description = "Converts data into org-mode" diff --git a/ruff.toml b/ruff.toml index 35bda57..632f356 100644 --- a/ruff.toml +++ b/ruff.toml @@ -1,4 +1,4 @@ -target-version = "py38" # NOTE: inferred from pyproject.toml if present +target-version = "py39" # NOTE: inferred from pyproject.toml if present lint.extend-select = [ "F", # flakes rules -- default, but extend just in case @@ -9,7 +9,7 @@ lint.extend-select = [ "C4", # flake8-comprehensions -- unnecessary list/map/dict calls "COM", # trailing commas "EXE", # various checks wrt executable files - # "I", # sort imports + "I", # sort imports "ICN", # various import conventions "FBT", # detect use of boolean arguments "FURB", # various rules @@ -30,6 +30,7 @@ lint.extend-select = [ "PTH", # pathlib migration "ARG", # unused argument checks "A", # builtin shadowing + "G", # logging stuff # "EM", # TODO hmm could be helpful to prevent duplicate err msg in traceback.. but kinda annoying # "ALL", # uncomment this to check for new rules! @@ -63,10 +64,9 @@ lint.ignore = [ "E402", # Module level import not at top of file ### maybe consider these soon -# sometimes it's useful to give a variable a name even if we don't use it as a documentation -# on the other hand, often is a sign of error + # sometimes it's useful to give a variable a name even if we don't use it as a documentation + # on the other hand, often is a sign of error "F841", # Local variable `count` is assigned to but never used - "F401", # imported but unused ### "RUF100", # unused noqa -- handle later @@ -127,6 +127,8 @@ lint.ignore = [ "TID252", # Prefer absolute imports over relative imports from parent modules + "UP038", # suggests using | (union) in isisntance checks.. but it results in slower code + ## too annoying "T20", # just complains about prints and pprints "Q", # flake quotes, too annoying diff --git a/src/orger/__init__.py b/src/orger/__init__.py index 7912219..a320275 100644 --- a/src/orger/__init__.py +++ b/src/orger/__init__.py @@ -1,7 +1,15 @@ from typing import TYPE_CHECKING -from .org_view import Mirror, OrgWithKey, Queue, StaticView +from .org_view import Mirror, OrgWithKey, Queue + +__all__ = [ + 'Mirror', + 'OrgWithKey', + 'Queue', +] + if not TYPE_CHECKING: # TODO deprecate properly? InteractiveView = Queue + StaticView = Mirror diff --git a/src/orger/atomic_append.py b/src/orger/atomic_append.py index 5f774f8..aaa76f7 100644 --- a/src/orger/atomic_append.py +++ b/src/orger/atomic_append.py @@ -1,13 +1,13 @@ +from __future__ import annotations + import logging from os.path import lexists from pathlib import Path -from typing import Union -PathIsh = Union[str, Path] def atomic_append_raw( - path: PathIsh, - data: str, + path: Path | str, + data: str, ) -> None: path = Path(path) # https://stackoverflow.com/a/13232181 @@ -25,13 +25,13 @@ def assert_not_edited(path: Path) -> None: emacs = '.#' + path.name for x in [vim, emacs]: lf = path.parent / x - if lexists(lf): # lexist is necessary because emacs uses symlink for lock file + if lexists(lf): # lexist is necessary because emacs uses symlink for lock file raise RuntimeError(f'File is being edited: {lf}') def atomic_append_check( - path: PathIsh, - data: str, + path: Path | str, + data: str, ) -> None: """ This is editor (emacs/vim)-aware and checks for existence of swap file first. @@ -56,8 +56,9 @@ def test_atomic_append_check(tmp_path: Path) -> None: of.touch() from contextlib import contextmanager - from subprocess import PIPE, Popen, check_call + from subprocess import PIPE, Popen from time import sleep + @contextmanager def tmp_popen(*args, **kwargs): with Popen(*args, **kwargs) as p: @@ -70,7 +71,7 @@ def tmp_popen(*args, **kwargs): atomic_append_check(of, 'data2') assert of.read_text() == 'data1data2' - with tmp_popen(['vi', '-c', 'startinsert', str(of)], stdin=PIPE, stdout=PIPE, stderr=PIPE) as p: # enter insert mode + with tmp_popen(['vi', '-c', 'startinsert', str(of)], stdin=PIPE, stdout=PIPE, stderr=PIPE) as p: # enter insert mode for _attempt in range(10): # ugh, needs long pause for some reason sleep(1) diff --git a/src/orger/common.py b/src/orger/common.py index 281340e..a0f07c2 100644 --- a/src/orger/common.py +++ b/src/orger/common.py @@ -16,6 +16,8 @@ class settings: _timezones = set() # type: ignore + + def dt_heading(dt: datetime | None, heading: str) -> str: """ Helper to inline datetime in heading @@ -27,7 +29,9 @@ def dt_heading(dt: datetime | None, heading: str) -> str: tz = dt.tzinfo # todo come up with a better way of reporting this.. if tz not in _timezones and len(_timezones) > 0: - warnings.warn(f"Seems that a mixture of timezones is used. Org-mode doesn't support timezones, so this might end up confusing: {_timezones} {tz} {heading}") + warnings.warn( + f"Seems that a mixture of timezones is used. Org-mode doesn't support timezones, so this might end up confusing: {_timezones} {tz} {heading}" + ) _timezones.add(tz) return timestamp_with_style(dt=dt, style=settings.DEFAULT_TIMESTAMP_STYLE) + ' ' + heading @@ -57,9 +61,10 @@ def todo(dt: datetime, **kwargs): def orger_user_dir() -> Path: import appdirs # type: ignore[import-untyped] + return Path(appdirs.user_config_dir('orger')) if not TYPE_CHECKING: # legacy imports for bwd compatibility - from .logging_helper import LazyLogger, setup_logger + from .logging_helper import LazyLogger, setup_logger # noqa: F401 diff --git a/src/orger/inorganic.py b/src/orger/inorganic.py index 288fb1d..597f1ca 100644 --- a/src/orger/inorganic.py +++ b/src/orger/inorganic.py @@ -1,21 +1,16 @@ from __future__ import annotations -import logging import os import re import textwrap import warnings -from collections import OrderedDict +from collections.abc import Mapping, Sequence from dataclasses import dataclass from datetime import date, datetime from enum import Enum from pathlib import Path from typing import ( - TYPE_CHECKING, - Any, Callable, - Mapping, - Sequence, TypeVar, Union, ) diff --git a/src/orger/modules/auto.py b/src/orger/modules/auto.py index 8cafe4f..c1d0ad7 100755 --- a/src/orger/modules/auto.py +++ b/src/orger/modules/auto.py @@ -1,19 +1,19 @@ #!/usr/bin/env python3 from __future__ import annotations -from orger import Mirror -from orger.inorganic import node, link, Quoted -from orger.common import dt_heading, error - +import string +from collections.abc import Iterator from datetime import datetime -from typing import Iterator, Any from pprint import pformat -import string +from typing import Any from more_itertools import bucket - from my.core.types import asdict +from orger import Mirror +from orger.common import dt_heading, error +from orger.inorganic import Quoted, node + def pp_item(i, **kwargs) -> str: # annoying, pprint doesn't have dataclass support till 3.10 https://bugs.python.org/issue43080 diff --git a/src/orger/modules/github.py b/src/orger/modules/github.py index b800602..2920fdc 100755 --- a/src/orger/modules/github.py +++ b/src/orger/modules/github.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 -from orger import Mirror -from orger.inorganic import node, link +import my.github.all as github + +from orger import Mirror, pandoc from orger.common import dt_heading, error -from orger import pandoc +from orger.inorganic import link, node -import my.github.all as github # todo use later: import my.github.ghexport as gh. also careful about using events() -- need to sort? # I guess makes sense to generally expose get_ methods? diff --git a/src/orger/modules/hyp2org.py b/src/orger/modules/hyp2org.py index fe0638d..16bb905 100755 --- a/src/orger/modules/hyp2org.py +++ b/src/orger/modules/hyp2org.py @@ -12,11 +12,11 @@ so I can un/reschedule them if they don't require immediate attention. """ +from my.hypothesis import Highlight, highlights + from orger import Queue -from orger.inorganic import node, link from orger.common import todo - -from my.hypothesis import highlights, Highlight +from orger.inorganic import link def is_todo(e: Highlight) -> bool: diff --git a/src/orger/modules/hypothesis.py b/src/orger/modules/hypothesis.py index 3ea1512..f82a268 100755 --- a/src/orger/modules/hypothesis.py +++ b/src/orger/modules/hypothesis.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 +import my.hypothesis as hypothesis + from orger import Mirror -from orger.inorganic import node, link from orger.common import dt_heading, error - -import my.hypothesis as hypothesis +from orger.inorganic import link, node class HypView(Mirror): diff --git a/src/orger/modules/instapaper.py b/src/orger/modules/instapaper.py index d44ce6a..ab0c000 100755 --- a/src/orger/modules/instapaper.py +++ b/src/orger/modules/instapaper.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 +from my.instapaper import pages + from orger import Mirror -from orger.inorganic import node, link, Quoted from orger.common import dt_heading - -from my.instapaper import pages +from orger.inorganic import Quoted, link, node class Instapaper(Mirror): diff --git a/src/orger/modules/ip2org.py b/src/orger/modules/ip2org.py index 62209ee..63b5a2b 100755 --- a/src/orger/modules/ip2org.py +++ b/src/orger/modules/ip2org.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 +from my.instapaper import is_todo, pages + from orger import Queue -from orger.inorganic import node, link from orger.common import todo - -from my.instapaper import pages, is_todo +from orger.inorganic import link class IpTodos(Queue): diff --git a/src/orger/modules/kobo.py b/src/orger/modules/kobo.py index 0f96339..a2550b5 100755 --- a/src/orger/modules/kobo.py +++ b/src/orger/modules/kobo.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 +from my.kobo import Highlight, get_books_with_highlights + from orger import Mirror -from orger.inorganic import node, link, OrgNode from orger.common import dt_heading - -from my.kobo import get_books_with_highlights, Highlight +from orger.inorganic import OrgNode, node class KoboView(Mirror): diff --git a/src/orger/modules/kobo2org.py b/src/orger/modules/kobo2org.py index 307232c..375d448 100755 --- a/src/orger/modules/kobo2org.py +++ b/src/orger/modules/kobo2org.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 +from my.kobo import get_todos + from orger import Queue -from orger.inorganic import node, link from orger.common import todo -from my.books.kobo import get_todos, Highlight - class KoboTodos(Queue): def get_items(self) -> Queue.Results: diff --git a/src/orger/modules/krill.py b/src/orger/modules/krill.py index 88d65c6..91372b9 100755 --- a/src/orger/modules/krill.py +++ b/src/orger/modules/krill.py @@ -7,12 +7,11 @@ The name stands for K[oboD]rill. """ +from my.kobo import Highlight, get_highlights + from orger import Queue -from orger.inorganic import node, link from orger.common import todo -from my.books.kobo import get_highlights, Highlight - def is_drill(i: Highlight) -> bool: if i.kind == 'bookmark': diff --git a/src/orger/modules/materialistic.py b/src/orger/modules/materialistic.py index eb4c11f..5280c0c 100755 --- a/src/orger/modules/materialistic.py +++ b/src/orger/modules/materialistic.py @@ -4,11 +4,12 @@ https://play.google.com/store/apps/details?id=io.github.hidroh.materialistic """ +from my.hackernews.materialistic import saves + from orger import Queue -from orger.inorganic import node, link from orger.common import dt_heading +from orger.inorganic import link, node -from my.materialistic import saves class Materialistic(Queue): def get_items(self): diff --git a/src/orger/modules/movies.py b/src/orger/modules/movies.py index 57908c5..bc14442 100755 --- a/src/orger/modules/movies.py +++ b/src/orger/modules/movies.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 +from my.media.movies import get_movies + from orger import StaticView -from orger.inorganic import node, link from orger.common import dt_heading +from orger.inorganic import node -from my.media.movies import get_movies - class Movies(StaticView): def get_items(self): movies = get_movies() diff --git a/src/orger/modules/pdfs.py b/src/orger/modules/pdfs.py index c2781d5..90c8cd5 100755 --- a/src/orger/modules/pdfs.py +++ b/src/orger/modules/pdfs.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 +from datetime import datetime + from orger import Mirror -from orger.inorganic import node, link, docview_link, literal from orger.common import dt_heading, error - -from datetime import datetime +from orger.inorganic import docview_link, literal, node class PdfView(Mirror): diff --git a/src/orger/modules/pinboard.py b/src/orger/modules/pinboard.py index b83fbe2..6ed90ff 100755 --- a/src/orger/modules/pinboard.py +++ b/src/orger/modules/pinboard.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 from __future__ import annotations +import my.pinboard as pinboard + from orger import Mirror from orger.common import dt_heading, error -from orger.inorganic import node, link - -import my.pinboard as pinboard +from orger.inorganic import link, node class PinboardView(Mirror): diff --git a/src/orger/modules/pocket.py b/src/orger/modules/pocket.py index 5ccc34b..d87acae 100755 --- a/src/orger/modules/pocket.py +++ b/src/orger/modules/pocket.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 -from orger import Mirror -from orger.inorganic import node, link -from orger.common import dt_heading - # TODO add tags? from my.pocket import articles +from orger import Mirror +from orger.common import dt_heading +from orger.inorganic import link, node + class PocketMirror(Mirror): def get_items(self) -> Mirror.Results: diff --git a/src/orger/modules/pocket_demo.py b/src/orger/modules/pocket_demo.py index e1ff73f..dbf36e9 100755 --- a/src/orger/modules/pocket_demo.py +++ b/src/orger/modules/pocket_demo.py @@ -14,9 +14,11 @@ """ +from collections.abc import Sequence from datetime import datetime from pathlib import Path -from typing import NamedTuple, Sequence, Any +from typing import Any, NamedTuple + class Highlight(NamedTuple): """ @@ -75,11 +77,12 @@ def get_articles(json_path: Path) -> Sequence[Article]: Ok, now we can get to implementing the adapter. """ from orger import Mirror + """ Mirror means it's meant to be read-only view onto data (as opposed to Queue). """ -from orger.inorganic import node, link from orger.common import dt_heading +from orger.inorganic import link, node class Pocket(Mirror): diff --git a/src/orger/modules/polar.py b/src/orger/modules/polar.py index 4f686bc..ec32934 100755 --- a/src/orger/modules/polar.py +++ b/src/orger/modules/polar.py @@ -15,10 +15,9 @@ """ -from orger import Mirror -from orger.inorganic import node, link, docview_link, OrgNode, literal +from orger import Mirror, pandoc from orger.common import dt_heading, error -from orger import pandoc +from orger.inorganic import OrgNode, docview_link, literal, node class PolarView(Mirror): diff --git a/src/orger/modules/reddit2org.py b/src/orger/modules/reddit2org.py index 2a0aa4d..8ac289f 100755 --- a/src/orger/modules/reddit2org.py +++ b/src/orger/modules/reddit2org.py @@ -2,11 +2,11 @@ """ Better interface for reading saved reddit posts/comments """ +from my.reddit.all import saved + from orger import Queue -from orger.inorganic import node, link, Quoted from orger.common import dt_heading - -from my.reddit import saved +from orger.inorganic import Quoted, link, node class RedditView(Queue): @@ -28,7 +28,7 @@ def is_dead_url(self, url: str) -> bool: # TODO this is probably easier to control via env variables, more malleable if not self.cmdline_args.mark_dead: return False - from kython.knetwork import is_alive # type: ignore + from kython.knetwork import is_alive # type: ignore return is_alive(url) # todo should somehow track handle DELETED comments... # sometimes it's also [removed] diff --git a/src/orger/modules/reddit_all.py b/src/orger/modules/reddit_all.py index 3e6abf2..0dc9b5d 100755 --- a/src/orger/modules/reddit_all.py +++ b/src/orger/modules/reddit_all.py @@ -2,12 +2,11 @@ """ Read-only reddit mirror of comments, submissions and upvoted posts; everything except saved """ +from my.reddit.all import comments, submissions, upvoted + from orger import Mirror -from orger.inorganic import node, link, Quoted from orger.common import dt_heading - - -from my.reddit import upvoted, submissions, comments +from orger.inorganic import Quoted, link, node class RedditAllView(Mirror): diff --git a/src/orger/modules/roamresearch.py b/src/orger/modules/roamresearch.py index f0b1c93..10be7f0 100755 --- a/src/orger/modules/roamresearch.py +++ b/src/orger/modules/roamresearch.py @@ -1,14 +1,13 @@ #!/usr/bin/env python3 +from collections.abc import Iterable from itertools import chain -from typing import Iterable - -from orger import Mirror -from orger.inorganic import node, link, OrgNode -from orger.common import dt_heading -from orger import pandoc import my.roamresearch as roamresearch +from orger import Mirror, pandoc +from orger.common import dt_heading +from orger.inorganic import OrgNode, link + # todo ^^ ^^ things are highlight? def roam_text_to_org(text: str) -> str: diff --git a/src/orger/modules/rss.py b/src/orger/modules/rss.py index 96adeb0..26983f3 100755 --- a/src/orger/modules/rss.py +++ b/src/orger/modules/rss.py @@ -1,10 +1,9 @@ #!/usr/bin/env python3 -from orger import StaticView -from orger.inorganic import node, link -from orger.common import dt_heading - import my.rss.all as rss +from orger import StaticView +from orger.inorganic import link, node + class RssSubscriptions(StaticView): def get_items(self): diff --git a/src/orger/modules/rtm2org.py b/src/orger/modules/rtm2org.py index 166682b..bd56990 100755 --- a/src/orger/modules/rtm2org.py +++ b/src/orger/modules/rtm2org.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 +from my.rtm import active_tasks + from orger import StaticView -from orger.inorganic import node, link from orger.common import dt_heading - -from my.rtm import active_tasks +from orger.inorganic import node class RtmView(StaticView): diff --git a/src/orger/modules/stackexchange.py b/src/orger/modules/stackexchange.py index 89ff971..60738b2 100755 --- a/src/orger/modules/stackexchange.py +++ b/src/orger/modules/stackexchange.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 +import my.stackexchange.stexport as se + from orger import Mirror -from orger.inorganic import node, link, Quoted from orger.common import dt_heading - -import my.stackexchange.stexport as se +from orger.inorganic import Quoted, link, node class Stackexchange(Mirror): diff --git a/src/orger/modules/tinder.py b/src/orger/modules/tinder.py index 949c5a7..f191b08 100755 --- a/src/orger/modules/tinder.py +++ b/src/orger/modules/tinder.py @@ -1,13 +1,13 @@ #!/usr/bin/env python3 -from orger import Mirror -from orger.inorganic import node -from orger.common import dt_heading, error - -import my.config # todo later, use .all? import my.tinder.android as tinder +from orger import Mirror +from orger.common import dt_heading, error +from orger.inorganic import node + + class TinderView(Mirror): def get_items(self) -> Mirror.Results: res = None diff --git a/src/orger/modules/twitter.py b/src/orger/modules/twitter.py index 8401c26..883e554 100755 --- a/src/orger/modules/twitter.py +++ b/src/orger/modules/twitter.py @@ -1,17 +1,15 @@ #!/usr/bin/env python3 -from orger import Mirror -from orger.inorganic import node, link, timestamp, OrgNode -from orger.common import dt_heading, error - import datetime -from typing import Iterable, Any +from collections.abc import Iterable # shit... this is annoying, need to 'nudge' the config so it picks up all.py override? -import my.config import my.twitter.all as twi - from my.core import Res +from orger import Mirror +from orger.common import dt_heading, error +from orger.inorganic import OrgNode, link, node, timestamp + today = datetime.datetime.now() Tweet = twi.Tweet diff --git a/src/orger/modules/twitter_likes.py b/src/orger/modules/twitter_likes.py index 4c367f8..4aa2e6f 100755 --- a/src/orger/modules/twitter_likes.py +++ b/src/orger/modules/twitter_likes.py @@ -1,11 +1,10 @@ #!/usr/bin/env python3 -from orger import Mirror -from orger.inorganic import node, link -from orger.common import dt_heading, error - -import my.config import my.twitter.all as twi +from orger import Mirror +from orger.common import error +from orger.inorganic import link, node + class TwitterLikesView(Mirror): def get_items(self) -> Mirror.Results: diff --git a/src/orger/modules/vkfavs.py b/src/orger/modules/vkfavs.py index bf3533d..4ee4e9c 100755 --- a/src/orger/modules/vkfavs.py +++ b/src/orger/modules/vkfavs.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 +from my.vk.favorites import Favorite, favorites + from orger import Mirror -from orger.inorganic import node, link, Quoted from orger.common import dt_heading, error +from orger.inorganic import Quoted, link, node -from my.vk.favorites import favorites, Favorite class VkFavs(Mirror): def get_items(self) -> Mirror.Results: diff --git a/src/orger/modules/zotero.py b/src/orger/modules/zotero.py index 61404d7..9aa65e8 100755 --- a/src/orger/modules/zotero.py +++ b/src/orger/modules/zotero.py @@ -1,14 +1,14 @@ #!/usr/bin/env python3 from __future__ import annotations -from orger import Mirror -from orger.inorganic import node, link, docview_link, literal -from orger.common import dt_heading, error - from textwrap import indent, wrap from more_itertools import bucket +from orger import Mirror +from orger.common import dt_heading, error +from orger.inorganic import docview_link, literal, node + class config: MAX_LINE_WIDTH: int = 120 diff --git a/src/orger/modules/zulip.py b/src/orger/modules/zulip.py index 5d83900..b30b9a5 100755 --- a/src/orger/modules/zulip.py +++ b/src/orger/modules/zulip.py @@ -1,14 +1,12 @@ #!/usr/bin/env python3 -from orger import Mirror -from orger.inorganic import node, link, Quoted -from orger.common import dt_heading, error -from orger import pandoc - from more_itertools import bucket - from my.zulip.organization import messages +from orger import Mirror, pandoc +from orger.common import dt_heading, error +from orger.inorganic import link, node + class Zulip(Mirror): def get_items(self) -> Mirror.Results: diff --git a/src/orger/org_view.py b/src/orger/org_view.py index f9c32f9..db3aa71 100644 --- a/src/orger/org_view.py +++ b/src/orger/org_view.py @@ -6,9 +6,10 @@ import sys from argparse import ArgumentParser, Namespace from collections import Counter +from collections.abc import Iterable from pathlib import Path from subprocess import check_call -from typing import Any, Callable, Iterable, Tuple, Union +from typing import Any, Callable, Union from .atomic_append import assert_not_edited, atomic_append_check from .common import orger_user_dir @@ -20,7 +21,7 @@ # think of some generic thing to test that? Key = str -OrgWithKey = Tuple[Key, OrgNode] +OrgWithKey = tuple[Key, OrgNode] _style_map: dict[str, TimestampStyle] = { diff --git a/src/orger/pandoc.py b/src/orger/pandoc.py index aa6cc02..6fc64a5 100644 --- a/src/orger/pandoc.py +++ b/src/orger/pandoc.py @@ -1,11 +1,11 @@ """ Helper for converting stuff to pandoc """ + import logging import shutil from functools import lru_cache from subprocess import PIPE, run -from typing import Optional from .common import settings @@ -20,6 +20,7 @@ def should_use_pandoc() -> bool: return True import warnings + warnings.warn("Install 'pandoc' to convert HTML to org-mode. See https://pandoc.org/installing.html") return False @@ -42,7 +43,7 @@ def to_org(data: str, *, from_: str, logger=logging) -> str: ) except Exception as e: logger.exception(e) - return data # fallback + return data # fallback res = r.stdout.decode('utf8') return res diff --git a/src/orger/state.py b/src/orger/state.py index fa5ffec..357befb 100644 --- a/src/orger/state.py +++ b/src/orger/state.py @@ -2,16 +2,12 @@ import json import logging -import os -import sys -import warnings from pathlib import Path -from typing import Any, Callable, Dict, List, Union +from typing import Any, Callable from atomicwrites import atomic_write # type: ignore[import-untyped] -PathIsh = Union[str, Path] -State = Dict[str, Any] +State = dict[str, Any] # TODO hmm. state should be ordered ideally? so it's easy to add/remove items? @@ -19,7 +15,7 @@ class JsonState: def __init__( self, - path: PathIsh, + path: Path | str, *, dry_run: bool = False, default: State | None = None, diff --git a/tox.ini b/tox.ini index ce9dd52..7d4ce0f 100644 --- a/tox.ini +++ b/tox.ini @@ -17,20 +17,23 @@ passenv = PYTHONPYCACHEPREFIX MYPY_CACHE_DIR RUFF_CACHE_DIR +usedevelop = true # for some reason tox seems to ignore "-e ." in deps section?? +setenv = + HPI_MODULE_INSTALL_USE_UV=true +uv_seed = true # seems necessary so uv creates separate venvs per tox env? [testenv:ruff] +deps = + -e .[testing] commands = - {envpython} -m pip install --use-pep517 -e .[testing] {envpython} -m ruff check src/ -# note: --use-pep517 here is necessary for tox --parallel flag to work properly -# otherwise it seems that it tries to modify .eggs dir in parallel and it fails [testenv:tests] +deps = + -e .[testing] commands = - {envpython} -m pip install --use-pep517 -e .[testing] - # posargs allow test filtering, e.g. tox ... -- -k test_name {envpython} -m pytest \ --pyargs {[testenv]package_name} \ --ignore-glob='src/orger/modules/*' \ @@ -38,32 +41,35 @@ commands = [testenv:mypy-core] +deps = + -e .[testing,optional] commands = - {envpython} -m pip install --use-pep517 -e .[testing,optional] {envpython} -m mypy --install-types --non-interactive \ + # note: modules are tested separately, below -p {[testenv]package_name} \ --exclude 'orger.modules' \ # txt report is a bit more convenient to view on CI - --txt-report .coverage.mypy \ - --html-report .coverage.mypy \ + --txt-report .coverage.mypy-core \ + --html-report .coverage.mypy-core \ {posargs} - [testenv:mypy-misc] +deps = + -e .[testing,optional] + HPI + uv # for hpi module install commands = - {envpython} -m pip install --use-pep517 -e .[testing,optional] - {envpython} -m pip install HPI {envpython} -m my.core module install \ - my.pocket \ - my.reddit \ - my.instapaper \ - my.pinboard \ + my.pocket \ + my.reddit.rexport \ + my.instapaper \ + my.pinboard \ my.kobo {envpython} -m mypy --install-types --non-interactive \ -p {[testenv]package_name}.modules \ # txt report is a bit more convenient to view on CI - --txt-report .coverage.mypy \ - --html-report .coverage.mypy \ + --txt-report .coverage.mypy-misc \ + --html-report .coverage.mypy-misc \ {posargs}