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.
- dron-0.2.20251011/.ci/release-uv +56 -0
- {dron-0.1.20241008 → dron-0.2.20251011}/.ci/run +6 -10
- dron-0.2.20251011/.github/workflows/main.yml +117 -0
- {dron-0.1.20241008/src/dron.egg-info → dron-0.2.20251011}/PKG-INFO +12 -17
- {dron-0.1.20241008 → dron-0.2.20251011}/README.org +3 -0
- {dron-0.1.20241008 → dron-0.2.20251011}/conftest.py +14 -4
- {dron-0.1.20241008 → dron-0.2.20251011}/mypy.ini +6 -3
- {dron-0.1.20241008 → dron-0.2.20251011}/pyproject.toml +25 -15
- {dron-0.1.20241008 → dron-0.2.20251011}/pytest.ini +7 -0
- {dron-0.1.20241008 → dron-0.2.20251011}/ruff.toml +15 -57
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/api.py +6 -5
- dron-0.2.20251011/src/dron/cli.py +269 -0
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/common.py +27 -20
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/dron.py +42 -133
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/launchd.py +49 -37
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/launchd_wrapper.py +16 -4
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/monitor.py +2 -4
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/notify/common.py +1 -1
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/notify/email.py +1 -1
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/notify/ntfy_common.py +3 -2
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/notify/telegram.py +1 -1
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/systemd.py +129 -107
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/tests/test_dron.py +81 -10
- dron-0.2.20251011/tox.ini +67 -0
- dron-0.1.20241008/.github/workflows/main.yml +0 -90
- dron-0.1.20241008/PKG-INFO +0 -46
- dron-0.1.20241008/setup.cfg +0 -4
- dron-0.1.20241008/src/dron/__init__.py +0 -6
- dron-0.1.20241008/src/dron/cli.py +0 -382
- dron-0.1.20241008/src/dron.egg-info/SOURCES.txt +0 -37
- dron-0.1.20241008/src/dron.egg-info/dependency_links.txt +0 -1
- dron-0.1.20241008/src/dron.egg-info/entry_points.txt +0 -2
- dron-0.1.20241008/src/dron.egg-info/requires.txt +0 -20
- dron-0.1.20241008/src/dron.egg-info/top_level.txt +0 -1
- dron-0.1.20241008/tox.ini +0 -54
- {dron-0.1.20241008 → dron-0.2.20251011}/.ci/release +0 -0
- {dron-0.1.20241008 → dron-0.2.20251011}/.gitignore +0 -0
- {dron-0.1.20241008 → dron-0.2.20251011}/LICENSE.txt +0 -0
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/__main__.py +0 -0
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/conftest.py +0 -0
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/notify/ntfy_desktop.py +0 -0
- {dron-0.1.20241008 → dron-0.2.20251011}/src/dron/notify/ntfy_telegram.py +0 -0
- {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
|
-
|
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
|
+
Metadata-Version: 2.4
|
2
2
|
Name: dron
|
3
|
-
Version: 0.
|
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:
|
33
|
-
Requires-Dist:
|
34
|
-
Requires-Dist:
|
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:
|
38
|
-
Requires-Dist:
|
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 ==
|
41
|
+
Requires-Dist: telegram-send>=0.37; extra == 'notify-telegram'
|
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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.
|
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 = ["
|
51
|
-
build-backend = "
|
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.
|
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
|
-
"
|
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
|
-
"
|
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", #
|
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
|
-
"
|
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
|
-
'
|
99
|
+
'Command',
|
100
|
+
'Job', # todo maybe don't expose it?
|
99
101
|
'OnCalendar',
|
100
102
|
'OnFailureAction',
|
101
|
-
'
|
102
|
-
'wrap',
|
103
|
+
'When',
|
103
104
|
'job',
|
104
105
|
'notify',
|
105
|
-
'
|
106
|
+
'wrap',
|
106
107
|
)
|