Skip to content

Commit 7e60eeb

Browse files
committed
chore: update all ci files; switch to hatch; add ty
1 parent 10d8cc8 commit 7e60eeb

13 files changed

Lines changed: 472 additions & 153 deletions

File tree

.ci/run

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,36 @@
1-
#!/bin/bash -eu
1+
#!/bin/bash
2+
set -eu
23

34
cd "$(dirname "$0")"
4-
cd ..
5+
cd .. # git root
56

67
if ! command -v sudo; then
7-
# CI or Docker sometimes don't have it, so useful to have a dummy
8+
# CI or Docker sometimes doesn't have it, so useful to have a dummy
89
function sudo {
910
"$@"
1011
}
1112
fi
1213

13-
if ! [ -z "$CI" ]; then
14+
# --parallel-live to show outputs while it's running
15+
tox_cmd='run-parallel --parallel-live'
16+
if [ -n "${CI-}" ]; then
1417
# install OS specific stuff here
15-
if [[ "$OSTYPE" == "darwin"* ]]; then
18+
case "$OSTYPE" in
19+
darwin*)
1620
# macos
1721
:
18-
else
22+
;;
23+
cygwin* | msys* | win*)
24+
# windows
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'
27+
;;
28+
*)
29+
# must be linux?
1930
:
20-
fi
31+
;;
32+
esac
2133
fi
2234

23-
pip3 install --user tox
24-
tox
35+
# NOTE: expects uv installed
36+
uv tool run --with tox-uv tox $tox_cmd "$@"

.github/workflows/main.yml

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,79 @@ on:
66
branches: '*'
77
tags: 'v[0-9]+.*' # only trigger on 'release' tags for PyPi
88
# Ideally I would put this in the pypi job... but github syntax doesn't allow for regexes there :shrug:
9-
# P.S. fuck made up yaml DSLs.
9+
10+
# Needed to trigger on others' PRs.
1011
# 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".
11-
pull_request: # needed to trigger on others' PRs
12-
workflow_dispatch: # needed to trigger workflows manually
13-
# todo cron?
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
1425

15-
env:
16-
# useful for scripts & sometimes tests to know
17-
CI: true
1826

1927
jobs:
2028
build:
2129
strategy:
30+
fail-fast: false
2231
matrix:
23-
platform: [ubuntu-latest]
24-
python-version: [3.7, 3.8, 3.9]
32+
platform: [ubuntu-latest, macos-latest, windows-latest]
33+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
34+
exclude: [
35+
# windows runners are pretty scarce, so let's only run lowest and highest python version
36+
{platform: windows-latest, python-version: '3.10'},
37+
{platform: windows-latest, python-version: '3.11'},
38+
{platform: windows-latest, python-version: '3.12'},
39+
40+
# same, macos is a bit too slow and ubuntu covers python quirks well
41+
{platform: macos-latest , python-version: '3.10'},
42+
{platform: macos-latest , python-version: '3.11'},
43+
{platform: macos-latest , python-version: '3.12'},
44+
]
2545

2646
runs-on: ${{ matrix.platform }}
2747

48+
# useful for 'optional' pipelines
49+
# continue-on-error: ${{ matrix.platform == 'windows-latest' }}
50+
2851
steps:
2952
# ugh https://github.com/actions/toolkit/blob/main/docs/commands.md#path-manipulation
3053
- run: echo "$HOME/.local/bin" >> $GITHUB_PATH
3154

32-
- uses: actions/setup-python@v1
55+
- uses: actions/checkout@v4
3356
with:
34-
python-version: ${{ matrix.python-version }}
57+
submodules: recursive
58+
fetch-depth: 0 # nicer to have all git history when debugging/for tests
3559

36-
- uses: actions/checkout@v2
60+
- uses: actions/setup-python@v5
3761
with:
38-
submodules: recursive
62+
python-version: ${{ matrix.python-version }}
63+
64+
- uses: astral-sh/setup-uv@v5
65+
with:
66+
enable-cache: false # we don't have lock files, so can't use them as cache key
3967

40-
# uncomment for SSH debugging
41-
# - uses: mxschmitt/action-tmate@v2
68+
- uses: mxschmitt/action-tmate@v3
69+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
4270

43-
- run: .ci/run
71+
# explicit bash command is necessary for Windows CI runner, otherwise it thinks it's cmd...
72+
- run: bash .ci/run
73+
env:
74+
# only compute lxml coverage on ubuntu; it crashes on windows
75+
CI_MYPY_COVERAGE: ${{ matrix.platform == 'ubuntu-latest' && '--cobertura-xml-report .coverage.mypy' || '' }}
4476

45-
- uses: actions/upload-artifact@v2
77+
- if: matrix.platform == 'ubuntu-latest' # no need to compute coverage for other platforms
78+
uses: codecov/codecov-action@v5
4679
with:
47-
name: .coverage.mypy
48-
path: .coverage.mypy/
49-
# restrict to a single python version, otherwise uploading fails
50-
if: ${{ matrix.python-version == '3.8' }}
80+
fail_ci_if_error: true # default false
81+
token: ${{ secrets.CODECOV_TOKEN }}
82+
flags: mypy-${{ matrix.python-version }}
83+
files: .coverage.mypy/cobertura.xml
84+

conftest.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# this is a hack to monkey patch pytest so it handles tests inside namespace packages without __init__.py properly
2+
# without it, pytest can't discover the package root for some reason
3+
# also see https://github.com/karlicoss/pytest_namespace_pkgs for more
4+
5+
import os
6+
import pathlib
7+
from typing import Optional
8+
9+
import _pytest.main
10+
import _pytest.pathlib
11+
12+
# we consider all dirs in repo/ to be namespace packages
13+
root_dir = pathlib.Path(__file__).absolute().parent.resolve() / 'src'
14+
assert root_dir.exists(), root_dir
15+
16+
# TODO assert it contains package name?? maybe get it via setuptools..
17+
18+
namespace_pkg_dirs = [str(d) for d in root_dir.iterdir() if d.is_dir()]
19+
20+
# resolve_package_path is called from _pytest.pathlib.import_path
21+
# takes a full abs path to the test file and needs to return the path to the 'root' package on the filesystem
22+
resolve_pkg_path_orig = _pytest.pathlib.resolve_package_path
23+
24+
25+
def resolve_package_path(path: pathlib.Path) -> Optional[pathlib.Path]:
26+
result = path # search from the test file upwards
27+
for parent in result.parents:
28+
if str(parent) in namespace_pkg_dirs:
29+
return parent
30+
if os.name == 'nt':
31+
# ??? for some reason on windows it is trying to call this against conftest? but not on linux/osx
32+
if path.name == 'conftest.py':
33+
return resolve_pkg_path_orig(path)
34+
raise RuntimeError("Couldn't determine path for ", path)
35+
36+
37+
# NOTE: seems like it's not necessary anymore?
38+
# keeping it for now just in case
39+
# after https://github.com/pytest-dev/pytest/pull/13426 we should be able to remove the whole conftest
40+
# _pytest.pathlib.resolve_package_path = resolve_package_path
41+
42+
43+
# without patching, the orig function returns just a package name for some reason
44+
# (I think it's used as a sort of fallback)
45+
# so we need to point it at the absolute path properly
46+
# not sure what are the consequences.. maybe it wouldn't be able to run against installed packages? not sure..
47+
search_pypath_orig = _pytest.main.search_pypath
48+
49+
50+
def search_pypath(module_name: str) -> str:
51+
mpath = root_dir / module_name.replace('.', os.sep)
52+
if not mpath.is_dir():
53+
mpath = mpath.with_suffix('.py')
54+
assert mpath.exists(), mpath # just in case
55+
return str(mpath)
56+
57+
58+
_pytest.main.search_pypath = search_pypath # ty: ignore[invalid-assignment]

mypy.ini

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
[mypy]
22
pretty = True
33
show_error_context = True
4-
show_error_codes = True
4+
show_column_numbers = True
5+
show_error_end = True
6+
57
check_untyped_defs = True
6-
namespace_packages = True
8+
9+
# see https://mypy.readthedocs.io/en/stable/error_code_list2.html
10+
warn_redundant_casts = True
11+
strict_equality = True
12+
warn_unused_ignores = True
13+
enable_error_code = deprecated,redundant-expr,possibly-undefined,truthy-bool,truthy-iterable,ignore-without-code,unused-awaitable
14+
715

816
# an example of suppressing
917
# [mypy-my.config.repos.pdfannots.pdfannots]

pyproject.toml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# see https://github.com/karlicoss/pymplate for up-to-date reference
2+
[project]
3+
dynamic = ["version"] # version is managed by build backend
4+
name = "goodrexport"
5+
dependencies = [
6+
"goodrexport[dal]", # TODO backwards compatibility -- remove later?
7+
]
8+
requires-python = ">=3.9"
9+
10+
## these need to be set if you're planning to upload to pypi
11+
# description = "TODO"
12+
# license = {file = "LICENSE"}
13+
# authors = [
14+
# {name = "Dima Gerasimov (@karlicoss)", email = "karlicoss@gmail.com"},
15+
# ]
16+
# maintainers = [
17+
# {name = "Dima Gerasimov (@karlicoss)", email = "karlicoss@gmail.com"},
18+
# ]
19+
#
20+
# [project.urls]
21+
# Homepage = "https://github.com/karlicoss/pymplate"
22+
##
23+
24+
25+
[project.optional-dependencies]
26+
dal = ["lxml"]
27+
export = []
28+
optional = [
29+
"orjson", # faster json processing
30+
"colorlog",
31+
"ijson", # faster iterative json processing
32+
]
33+
[dependency-groups]
34+
# TODO: not sure, on the one hand could just use 'standard' dev dependency group
35+
# On the other hand, it's a bit annoying that it's always included by default?
36+
# To make sure it's not included, need to use `uv run --exact --no-default-groups ...`
37+
testing = [
38+
"pytest",
39+
"ruff",
40+
"mypy",
41+
"lxml", # for mypy html coverage
42+
"ty>=0.0.1a15",
43+
44+
"lxml-stubs",
45+
]
46+
47+
48+
[build-system]
49+
requires = ["hatchling", "hatch-vcs"]
50+
build-backend = "hatchling.build"
51+
52+
# unfortunately have to duplicate project name here atm, see https://github.com/pypa/hatch/issues/1894
53+
[tool.hatch.build.targets.wheel]
54+
packages = ["src/goodrexport"]
55+
56+
[tool.hatch.version]
57+
source = "vcs"
58+
59+
[tool.hatch.version.raw-options]
60+
version_scheme = "python-simplified-semver"
61+
local_scheme = "dirty-tag"
62+

pytest.ini

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,20 @@
11
[pytest]
22
# discover files that don't follow test_ naming. Useful to keep tests along with the source code
33
python_files = *.py
4+
5+
# this setting only impacts package/module naming under pytest, not the discovery
6+
consider_namespace_packages = true
7+
48
addopts =
9+
# prevent pytest cache from being created... it craps into project dir and I never use it anyway
10+
-p no:cacheprovider
11+
12+
# -rap to print tests summary even when they are successful
13+
-rap
514
--verbose
615

716
# otherwise it won't discover doctests
817
--doctest-modules
18+
19+
# show all test durations (unless they are too short)
20+
--durations=0

0 commit comments

Comments
 (0)