dron 0.1.20241008__tar.gz → 0.2.20251011__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. dron-0.2.20251011/.ci/release-uv +56 -0
  2. {dron-0.1.20241008 → dron-0.2.20251011}/.ci/run +6 -10
  3. dron-0.2.20251011/.github/workflows/main.yml +117 -0
  4. {dron-0.1.20241008/src/dron.egg-info → dron-0.2.20251011}/PKG-INFO +12 -17
  5. {dron-0.1.20241008 → dron-0.2.20251011}/README.org +3 -0
  6. {dron-0.1.20241008 → dron-0.2.20251011}/conftest.py +14 -4
  7. {dron-0.1.20241008 → dron-0.2.20251011}/mypy.ini +6 -3
  8. {dron-0.1.20241008 → dron-0.2.20251011}/pyproject.toml +25 -15
  9. {dron-0.1.20241008 → dron-0.2.20251011}/pytest.ini +7 -0
  10. {dron-0.1.20241008 → dron-0.2.20251011}/ruff.toml +15 -57
  11. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/api.py +6 -5
  12. dron-0.2.20251011/src/dron/cli.py +269 -0
  13. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/common.py +27 -20
  14. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/dron.py +42 -133
  15. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/launchd.py +49 -37
  16. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/launchd_wrapper.py +16 -4
  17. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/monitor.py +2 -4
  18. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/notify/common.py +1 -1
  19. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/notify/email.py +1 -1
  20. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/notify/ntfy_common.py +3 -2
  21. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/notify/telegram.py +1 -1
  22. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/systemd.py +129 -107
  23. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/tests/test_dron.py +81 -10
  24. dron-0.2.20251011/tox.ini +67 -0
  25. dron-0.1.20241008/.github/workflows/main.yml +0 -90
  26. dron-0.1.20241008/PKG-INFO +0 -46
  27. dron-0.1.20241008/setup.cfg +0 -4
  28. dron-0.1.20241008/src/dron/__init__.py +0 -6
  29. dron-0.1.20241008/src/dron/cli.py +0 -382
  30. dron-0.1.20241008/src/dron.egg-info/SOURCES.txt +0 -37
  31. dron-0.1.20241008/src/dron.egg-info/dependency_links.txt +0 -1
  32. dron-0.1.20241008/src/dron.egg-info/entry_points.txt +0 -2
  33. dron-0.1.20241008/src/dron.egg-info/requires.txt +0 -20
  34. dron-0.1.20241008/src/dron.egg-info/top_level.txt +0 -1
  35. dron-0.1.20241008/tox.ini +0 -54
  36. {dron-0.1.20241008 → dron-0.2.20251011}/.ci/release +0 -0
  37. {dron-0.1.20241008 → dron-0.2.20251011}/.gitignore +0 -0
  38. {dron-0.1.20241008 → dron-0.2.20251011}/LICENSE.txt +0 -0
  39. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/__main__.py +0 -0
  40. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/conftest.py +0 -0
  41. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/notify/ntfy_desktop.py +0 -0
  42. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/notify/ntfy_telegram.py +0 -0
  43. {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/py.typed +0 -0
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env python3
2
+ '''
3
+ Deploys Python package onto [[https://pypi.org][PyPi]] or [[https://test.pypi.org][test PyPi]].
4
+
5
+ - running manually
6
+
7
+ You'll need =UV_PUBLISH_TOKEN= env variable
8
+
9
+ - running on Github Actions
10
+
11
+ Instead of env variable, relies on configuring github as Trusted publisher (https://docs.pypi.org/trusted-publishers/) -- both for test and regular pypi
12
+
13
+ It's running as =pypi= job in [[file:.github/workflows/main.yml][Github Actions config]].
14
+ Packages are deployed on:
15
+ - every master commit, onto test pypi
16
+ - every new tag, onto production pypi
17
+ '''
18
+
19
+ UV_PUBLISH_TOKEN = 'UV_PUBLISH_TOKEN'
20
+
21
+ import argparse
22
+ import os
23
+ import shutil
24
+ from pathlib import Path
25
+ from subprocess import check_call
26
+
27
+ is_ci = os.environ.get('CI') is not None
28
+
29
+
30
+ def main() -> None:
31
+ p = argparse.ArgumentParser()
32
+ p.add_argument('--use-test-pypi', action='store_true')
33
+ args = p.parse_args()
34
+
35
+ publish_url = ['--publish-url', 'https://test.pypi.org/legacy/'] if args.use_test_pypi else []
36
+
37
+ root = Path(__file__).absolute().parent.parent
38
+ os.chdir(root) # just in case
39
+
40
+ # TODO ok, for now uv won't remove dist dir if it already exists
41
+ # https://github.com/astral-sh/uv/issues/10293
42
+ dist = root / 'dist'
43
+ if dist.exists():
44
+ shutil.rmtree(dist)
45
+
46
+ check_call(['uv', 'build'])
47
+
48
+ if not is_ci:
49
+ # CI relies on trusted publishers so doesn't need env variable
50
+ assert UV_PUBLISH_TOKEN in os.environ, f'no {UV_PUBLISH_TOKEN} passed'
51
+
52
+ check_call(['uv', 'publish', *publish_url])
53
+
54
+
55
+ if __name__ == '__main__':
56
+ main()
@@ -11,6 +11,8 @@ if ! command -v sudo; then
11
11
  }
12
12
  fi
13
13
 
14
+ # --parallel-live to show outputs while it's running
15
+ tox_cmd='run-parallel --parallel-live'
14
16
  if [ -n "${CI-}" ]; then
15
17
  # install OS specific stuff here
16
18
  case "$OSTYPE" in
@@ -20,7 +22,8 @@ if [ -n "${CI-}" ]; then
20
22
  ;;
21
23
  cygwin* | msys* | win*)
22
24
  # windows
23
- :
25
+ # ugh. parallel stuff seems super flaky under windows, some random failures, "file used by other process" and crap like that
26
+ tox_cmd='run'
24
27
  ;;
25
28
  *)
26
29
  # must be linux?
@@ -30,12 +33,5 @@ if [ -n "${CI-}" ]; then
30
33
  esac
31
34
  fi
32
35
 
33
-
34
- PY_BIN="python3"
35
- # some systems might have python pointing to python3
36
- if ! command -v python3 &> /dev/null; then
37
- PY_BIN="python"
38
- fi
39
-
40
- "$PY_BIN" -m pip install --user tox
41
- "$PY_BIN" -m tox --parallel --parallel-live "$@"
36
+ # NOTE: expects uv installed
37
+ uv tool run --with tox-uv tox $tox_cmd "$@"
@@ -0,0 +1,117 @@
1
+ # see https://github.com/karlicoss/pymplate for up-to-date reference
2
+
3
+ name: CI
4
+ on:
5
+ push:
6
+ branches: '*'
7
+ tags: 'v[0-9]+.*' # only trigger on 'release' tags for PyPi
8
+ # Ideally I would put this in the pypi job... but github syntax doesn't allow for regexes there :shrug:
9
+
10
+ # Needed to trigger on others' PRs.
11
+ # Note that people who fork it need to go to "Actions" tab on their fork and click "I understand my workflows, go ahead and enable them".
12
+ pull_request:
13
+
14
+ # Needed to trigger workflows manually.
15
+ workflow_dispatch:
16
+ inputs:
17
+ debug_enabled:
18
+ type: boolean
19
+ description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
20
+ required: false
21
+ default: false
22
+
23
+ schedule:
24
+ - cron: '31 18 * * 5' # run every Friday
25
+
26
+
27
+ jobs:
28
+ build:
29
+ strategy:
30
+ fail-fast: false
31
+ matrix:
32
+ platform: [ubuntu-latest, macos-latest]
33
+ python-version: ['3.12', '3.13', '3.14']
34
+ # vvv just an example of excluding stuff from matrix
35
+ # exclude: [{platform: macos-latest, python-version: '3.6'}]
36
+
37
+ runs-on: ${{ matrix.platform }}
38
+
39
+ # useful for 'optional' pipelines
40
+ # continue-on-error: ${{ matrix.platform == 'windows-latest' }}
41
+
42
+ steps:
43
+ # ugh https://github.com/actions/toolkit/blob/main/docs/commands.md#path-manipulation
44
+ - run: echo "$HOME/.local/bin" >> $GITHUB_PATH
45
+
46
+ - uses: actions/checkout@v5
47
+ with:
48
+ submodules: recursive
49
+ fetch-depth: 0 # nicer to have all git history when debugging/for tests
50
+
51
+ - uses: actions/setup-python@v6
52
+ with:
53
+ python-version: ${{ matrix.python-version }}
54
+
55
+ - uses: astral-sh/setup-uv@v7
56
+ with:
57
+ enable-cache: false # we don't have lock files, so can't use them as cache key
58
+
59
+ - uses: mxschmitt/action-tmate@v3
60
+ if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
61
+
62
+ # explicit bash command is necessary for Windows CI runner, otherwise it thinks it's cmd...
63
+ - run: bash .ci/run
64
+ env:
65
+ # only compute lxml coverage on ubuntu; it crashes on windows
66
+ CI_MYPY_COVERAGE: ${{ matrix.platform == 'ubuntu-latest' && '--cobertura-xml-report .coverage.mypy' || '' }}
67
+
68
+ - if: matrix.platform == 'ubuntu-latest' # no need to compute coverage for other platforms
69
+ uses: codecov/codecov-action@v5
70
+ with:
71
+ fail_ci_if_error: true # default false
72
+ token: ${{ secrets.CODECOV_TOKEN }}
73
+ flags: mypy-${{ matrix.python-version }}
74
+ files: .coverage.mypy/cobertura.xml
75
+
76
+
77
+ pypi:
78
+ # Do not run it for PRs/cron schedule etc.
79
+ # NOTE: release tags are guarded by on: push: tags on the top.
80
+ if: github.event_name == 'push' && (startsWith(github.event.ref, 'refs/tags/') || (github.event.ref == format('refs/heads/{0}', github.event.repository.master_branch)))
81
+ # Ugh, I tried using matrix or something to explicitly generate only test pypi or prod pypi pipelines.
82
+ # But github actions is so shit, it's impossible to do any logic at all, e.g. doesn't support conditional matrix, if/else statements for variables etc.
83
+
84
+ needs: [build] # add all other jobs here
85
+
86
+ runs-on: ubuntu-latest
87
+
88
+ permissions:
89
+ # necessary for Trusted Publishing
90
+ id-token: write
91
+
92
+ steps:
93
+ # ugh https://github.com/actions/toolkit/blob/main/docs/commands.md#path-manipulation
94
+ - run: echo "$HOME/.local/bin" >> $GITHUB_PATH
95
+
96
+ - uses: actions/checkout@v5
97
+ with:
98
+ submodules: recursive
99
+ fetch-depth: 0 # pull all commits to correctly infer vcs version
100
+
101
+ - uses: actions/setup-python@v6
102
+ with:
103
+ python-version: '3.12'
104
+
105
+ - uses: astral-sh/setup-uv@v7
106
+ with:
107
+ enable-cache: false # we don't have lock files, so can't use them as cache key
108
+
109
+ - name: 'release to test pypi'
110
+ # always deploy merged master to test pypi
111
+ if: github.event.ref == format('refs/heads/{0}', github.event.repository.master_branch)
112
+ run: .ci/release-uv --use-test-pypi
113
+
114
+ - name: 'release to prod pypi'
115
+ # always deploy tags to release pypi
116
+ if: startsWith(github.event.ref, 'refs/tags/')
117
+ run: .ci/release-uv
@@ -1,6 +1,8 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: dron
3
- Version: 0.1.20241008
3
+ Version: 0.2.20251011
4
+ Summary: What if cron and systemd had a baby?
5
+ Project-URL: Homepage, https://github.com/karlicoss/dron
4
6
  Author-email: "Dima Gerasimov (@karlicoss)" <karlicoss@gmail.com>
5
7
  Maintainer-email: "Dima Gerasimov (@karlicoss)" <karlicoss@gmail.com>
6
8
  License: The MIT License (MIT)
@@ -24,23 +26,16 @@ License: The MIT License (MIT)
24
26
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
27
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
28
  SOFTWARE.
27
-
28
- Project-URL: Homepage, https://github.com/karlicoss/dron
29
- Requires-Python: >=3.9
30
29
  License-File: LICENSE.txt
30
+ Requires-Python: >=3.12
31
31
  Requires-Dist: click
32
- Requires-Dist: prompt_toolkit
33
- Requires-Dist: tzlocal
34
- Requires-Dist: textual
32
+ Requires-Dist: dbus-python; platform_system != 'Darwin'
33
+ Requires-Dist: loguru
34
+ Requires-Dist: mypy
35
+ Requires-Dist: prompt-toolkit
35
36
  Requires-Dist: tabulate
36
37
  Requires-Dist: termcolor
37
- Requires-Dist: mypy
38
- Requires-Dist: loguru
39
- Requires-Dist: dbus-python; platform_system != "Darwin"
40
- Provides-Extra: testing
41
- Requires-Dist: pytest; extra == "testing"
42
- Requires-Dist: ruff; extra == "testing"
43
- Requires-Dist: mypy; extra == "testing"
44
- Requires-Dist: lxml; extra == "testing"
38
+ Requires-Dist: textual
39
+ Requires-Dist: tzlocal
45
40
  Provides-Extra: notify-telegram
46
- Requires-Dist: telegram-send>=0.37; extra == "notify-telegram"
41
+ Requires-Dist: telegram-send>=0.37; extra == 'notify-telegram'
@@ -1,3 +1,6 @@
1
+ NOTE: readme is a little outdated (although mostly correct).
2
+ Best source of truth is =--help=.
3
+
1
4
  #+begin_src python :results drawer :exports results
2
5
  import dron; return dron.make_parser().description
3
6
  #+end_src
@@ -4,7 +4,6 @@
4
4
 
5
5
  import os
6
6
  import pathlib
7
- from typing import Optional
8
7
 
9
8
  import _pytest.main
10
9
  import _pytest.pathlib
@@ -20,7 +19,9 @@ namespace_pkg_dirs = [str(d) for d in root_dir.iterdir() if d.is_dir()]
20
19
  # resolve_package_path is called from _pytest.pathlib.import_path
21
20
  # takes a full abs path to the test file and needs to return the path to the 'root' package on the filesystem
22
21
  resolve_pkg_path_orig = _pytest.pathlib.resolve_package_path
23
- def resolve_package_path(path: pathlib.Path) -> Optional[pathlib.Path]:
22
+
23
+
24
+ def resolve_package_path(path: pathlib.Path) -> pathlib.Path | None:
24
25
  result = path # search from the test file upwards
25
26
  for parent in result.parents:
26
27
  if str(parent) in namespace_pkg_dirs:
@@ -30,7 +31,12 @@ def resolve_package_path(path: pathlib.Path) -> Optional[pathlib.Path]:
30
31
  if path.name == 'conftest.py':
31
32
  return resolve_pkg_path_orig(path)
32
33
  raise RuntimeError("Couldn't determine path for ", path)
33
- _pytest.pathlib.resolve_package_path = resolve_package_path
34
+
35
+
36
+ # NOTE: seems like it's not necessary anymore?
37
+ # keeping it for now just in case
38
+ # after https://github.com/pytest-dev/pytest/pull/13426 we should be able to remove the whole conftest
39
+ # _pytest.pathlib.resolve_package_path = resolve_package_path
34
40
 
35
41
 
36
42
  # without patching, the orig function returns just a package name for some reason
@@ -38,10 +44,14 @@ _pytest.pathlib.resolve_package_path = resolve_package_path
38
44
  # so we need to point it at the absolute path properly
39
45
  # not sure what are the consequences.. maybe it wouldn't be able to run against installed packages? not sure..
40
46
  search_pypath_orig = _pytest.main.search_pypath
47
+
48
+
41
49
  def search_pypath(module_name: str) -> str:
42
50
  mpath = root_dir / module_name.replace('.', os.sep)
43
51
  if not mpath.is_dir():
44
52
  mpath = mpath.with_suffix('.py')
45
53
  assert mpath.exists(), mpath # just in case
46
54
  return str(mpath)
47
- _pytest.main.search_pypath = search_pypath
55
+
56
+
57
+ _pytest.main.search_pypath = search_pypath # ty: ignore[invalid-assignment]
@@ -3,11 +3,14 @@ pretty = True
3
3
  show_error_context = True
4
4
  show_column_numbers = True
5
5
  show_error_end = True
6
- warn_redundant_casts = True
7
- warn_unused_ignores = True
6
+
8
7
  check_untyped_defs = True
8
+
9
+ # see https://mypy.readthedocs.io/en/stable/error_code_list2.html
10
+ warn_redundant_casts = True
9
11
  strict_equality = True
10
- enable_error_code = possibly-undefined
12
+ warn_unused_ignores = True
13
+ enable_error_code = deprecated,redundant-expr,possibly-undefined,truthy-bool,truthy-iterable,ignore-without-code,unused-awaitable
11
14
 
12
15
  # an example of suppressing
13
16
  # [mypy-my.config.repos.pdfannots.pdfannots]
@@ -1,6 +1,6 @@
1
1
  # see https://github.com/karlicoss/pymplate for up-to-date reference
2
2
  [project]
3
- dynamic = ["version"] # version is managed by setuptools_scm
3
+ dynamic = ["version"] # version is managed by build backend
4
4
  name = "dron"
5
5
  dependencies = [
6
6
  "click" , # CLI
@@ -13,10 +13,11 @@ dependencies = [
13
13
  "loguru" , # nicer logging
14
14
  "dbus-python; platform_system != 'Darwin'", # dbus interface to systemd
15
15
  ]
16
- requires-python = ">=3.9"
16
+ requires-python = ">=3.12"
17
17
  # FIXME dbus
18
18
 
19
19
  ## these need to be set if you're planning to upload to pypi
20
+ description = "What if cron and systemd had a baby?"
20
21
  license = {file = "LICENSE.txt"}
21
22
  authors = [
22
23
  {name = "Dima Gerasimov (@karlicoss)", email = "karlicoss@gmail.com"},
@@ -24,33 +25,42 @@ authors = [
24
25
  maintainers = [
25
26
  {name = "Dima Gerasimov (@karlicoss)", email = "karlicoss@gmail.com"},
26
27
  ]
27
-
28
28
  [project.urls]
29
29
  Homepage = "https://github.com/karlicoss/dron"
30
30
  ##
31
31
 
32
- [project.scripts]
33
- dron = "dron.__main__:main"
34
32
 
35
33
  [project.optional-dependencies]
36
- testing = [
37
- "pytest",
38
- "ruff",
39
- "mypy",
40
- "lxml", # for mypy html coverage
41
- ]
42
34
  notify-telegram = [
43
35
  # version before that had a bug that prevented it from working
44
36
  # see https://github.com/rahiel/telegram-send/issues/115#issuecomment-1368728425
45
37
  "telegram-send>=0.37",
46
38
  ]
39
+ [dependency-groups]
40
+ testing = [
41
+ "pytest",
42
+ "ruff",
43
+ "mypy",
44
+ "lxml", # for mypy html coverage
45
+ "types-tabulate",
46
+ "ty>=0.0.1a22",
47
+ ]
48
+
49
+ [project.scripts]
50
+ dron = "dron.__main__:main"
47
51
 
48
52
 
49
53
  [build-system]
50
- requires = ["setuptools", "setuptools-scm"]
51
- build-backend = "setuptools.build_meta"
54
+ requires = ["hatchling", "hatch-vcs"]
55
+ build-backend = "hatchling.build"
56
+
57
+ # unfortunately have to duplicate project name here atm, see https://github.com/pypa/hatch/issues/1894
58
+ [tool.hatch.build.targets.wheel]
59
+ packages = ["src/dron"]
52
60
 
53
- [tool.setuptools_scm]
61
+ [tool.hatch.version]
62
+ source = "vcs"
63
+
64
+ [tool.hatch.version.raw-options]
54
65
  version_scheme = "python-simplified-semver"
55
66
  local_scheme = "dirty-tag"
56
-
@@ -1,7 +1,14 @@
1
1
  [pytest]
2
2
  # discover files that don't follow test_ naming. Useful to keep tests along with the source code
3
3
  python_files = *.py
4
+
5
+ # this setting only impacts package/module naming under pytest, not the discovery
6
+ consider_namespace_packages = true
7
+
4
8
  addopts =
9
+ # prevent pytest cache from being created... it craps into project dir and I never use it anyway
10
+ -p no:cacheprovider
11
+
5
12
  # -rap to print tests summary even when they are successful
6
13
  -rap
7
14
  --verbose
@@ -1,36 +1,7 @@
1
+ line-length = 120 # impacts import sorting
2
+
1
3
  lint.extend-select = [
2
- "F", # flakes rules -- default, but extend just in case
3
- "E", # pycodestyle -- default, but extend just in case
4
- "W", # various warnings
5
-
6
- "B", # 'bugbear' set -- various possible bugs
7
- "C4", # flake8-comprehensions -- unnecessary list/map/dict calls
8
- "COM", # trailing commas
9
- "EXE", # various checks wrt executable files
10
- "I", # sort imports
11
- "ICN", # various import conventions
12
- "FBT", # detect use of boolean arguments
13
- "FURB", # various rules
14
- "PERF", # various potential performance speedups
15
- "PD", # pandas rules
16
- "PIE", # 'misc' lints
17
- "PLC", # pylint convention rules
18
- "PLR", # pylint refactor rules
19
- "PLW", # pylint warnings
20
- "PT", # pytest stuff
21
- "PYI", # various type hinting rules
22
- "RET", # early returns
23
- "RUF", # various ruff-specific rules
24
- "TID", # various imports suggestions
25
- "TRY", # various exception handling rules
26
- "UP", # detect deprecated python stdlib stuff
27
- "FA", # suggest using from __future__ import annotations
28
- "PTH", # pathlib migration
29
- "ARG", # unused argument checks
30
- "A", # builtin shadowing
31
- # "EM", # TODO hmm could be helpful to prevent duplicate err msg in traceback.. but kinda annoying
32
-
33
- # "ALL", # uncomment this to check for new rules!
4
+ "ALL",
34
5
  ]
35
6
 
36
7
  lint.ignore = [
@@ -42,10 +13,10 @@ lint.ignore = [
42
13
  "FIX", # complains about fixmes/todos -- annoying
43
14
  "TD", # complains about todo formatting -- too annoying
44
15
  "ANN", # missing type annotations? seems way to strict though
16
+ "EM" , # suggests assigning all exception messages into a variable first... pretty annoying
45
17
 
46
18
  ### too opinionated style checks
47
19
  "E501", # too long lines
48
- "E702", # Multiple statements on one line (semicolon)
49
20
  "E731", # assigning lambda instead of using def
50
21
  "E741", # Ambiguous variable name: `l`
51
22
  "E742", # Ambiguous class name: `O
@@ -60,15 +31,6 @@ lint.ignore = [
60
31
  ## might be nice .. but later and I don't wanna make it strict
61
32
  "E402", # Module level import not at top of file
62
33
 
63
- ### maybe consider these soon
64
- # sometimes it's useful to give a variable a name even if we don't use it as a documentation
65
- # on the other hand, often is a sign of error
66
- "F841", # Local variable `count` is assigned to but never used
67
- ###
68
-
69
- "RUF100", # unused noqa -- handle later
70
- "RUF012", # mutable class attrs should be annotated with ClassVar... ugh pretty annoying for user configs
71
-
72
34
  ### these are just nitpicky, we usually know better
73
35
  "PLR0911", # too many return statements
74
36
  "PLR0912", # too many branches
@@ -83,10 +45,8 @@ lint.ignore = [
83
45
 
84
46
  "B009", # calling gettattr with constant attribute -- this is useful to convince mypy
85
47
  "B010", # same as above, but setattr
86
- "B011", # complains about assert False
87
48
  "B017", # pytest.raises(Exception)
88
49
  "B023", # seems to result in false positives?
89
- "B028", # suggest using explicit stacklevel? TODO double check later, but not sure it's useful
90
50
 
91
51
  # complains about useless pass, but has sort of a false positive if the function has a docstring?
92
52
  # this is common for click entrypoints (e.g. in __main__), so disable
@@ -106,28 +66,19 @@ lint.ignore = [
106
66
  "PLW0603", # global variable update.. we usually know why we are doing this
107
67
  "PLW2901", # for loop variable overwritten, usually this is intentional
108
68
 
109
- "PT004", # deprecated rule, will be removed later
110
- "PT011", # pytest raises should is too broad
111
- "PT012", # pytest raises should contain a single statement
69
+ "PT011", # pytest raises is too broad
112
70
 
113
71
  "COM812", # trailing comma missing -- mostly just being annoying with long multiline strings
114
72
 
115
- "PD901", # generic variable name df
116
-
117
73
  "TRY003", # suggests defining exception messages in exception class -- kinda annoying
118
- "TRY004", # prefer TypeError -- don't see the point
119
74
  "TRY201", # raise without specifying exception name -- sometimes hurts readability
120
- "TRY400", # TODO double check this, might be useful
75
+ "TRY400", # a bit dumb, and results in false positives (see https://github.com/astral-sh/ruff/issues/18070)
121
76
  "TRY401", # redundant exception in logging.exception call? TODO double check, might result in excessive logging
122
77
 
123
- "PGH", # TODO force error code in mypy instead
124
-
125
78
  "TID252", # Prefer absolute imports over relative imports from parent modules
126
79
 
127
- "UP038", # suggests using | (union) in isisntance checks.. but it results in slower code
128
-
129
80
  ## too annoying
130
- "T20", # just complains about prints and pprints
81
+ "T20", # just complains about prints and pprints (TODO maybe consider later?)
131
82
  "Q", # flake quotes, too annoying
132
83
  "C90", # some complexity checking
133
84
  "G004", # logging statement uses f string
@@ -135,7 +86,14 @@ lint.ignore = [
135
86
  "SLF001", # private member accessed
136
87
  "BLE001", # do not catch 'blind' Exception
137
88
  "INP001", # complains about implicit namespace packages
138
- "SIM", # some if statements crap
89
+ "SIM102", # if statements collapsing, often hurts readability
90
+ "SIM103", # multiple conditions collapsing, often hurts readability
91
+ "SIM105", # suggests using contextlib.suppress instad of try/except -- this wouldn't be mypy friendly
92
+ "SIM108", # suggests using ternary operation instead of if -- hurts readability
93
+ "SIM110", # suggests using any(...) instead of for look/return -- hurts readability
94
+ "SIM117", # suggests using single with statement instead of nested -- doesn't work in tests
139
95
  "RSE102", # complains about missing parens in exceptions
140
96
  ##
97
+
98
+ "PLC0415", # "imports should be at the top level" -- not realistic
141
99
  ]
@@ -4,8 +4,8 @@ import getpass
4
4
  import inspect
5
5
  import re
6
6
  import sys
7
+ from collections.abc import Sequence
7
8
  from dataclasses import dataclass
8
- from typing import Sequence
9
9
 
10
10
  from .common import (
11
11
  IS_SYSTEMD,
@@ -19,6 +19,7 @@ OnFailureAction = str
19
19
 
20
20
  UnitName = str
21
21
 
22
+
22
23
  @dataclass
23
24
  class Job:
24
25
  when: When | None
@@ -95,12 +96,12 @@ def job(
95
96
 
96
97
 
97
98
  __all__ = (
98
- 'When',
99
+ 'Command',
100
+ 'Job', # todo maybe don't expose it?
99
101
  'OnCalendar',
100
102
  'OnFailureAction',
101
- 'Command',
102
- 'wrap',
103
+ 'When',
103
104
  'job',
104
105
  'notify',
105
- 'Job', # todo maybe don't expose it?
106
+ 'wrap',
106
107
  )