You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Modernize tooling: uv, ruff, ty, Python 3.13+ and full typing (#31)
* Modernize tooling: uv, ruff, ty and Python 3.13+
Adopt the same project setup as the observ repo:
- Require Python 3.13+ and drop the `tomli` dependency in favour of
stdlib `tomllib`. Unpin `keyring` (~=24.3 -> >=25.6) and `shellingham`.
- Switch the build backend from flit to hatchling and move dev
dependencies from an optional-dependencies extra to PEP 735
dependency groups (dev/ruff/ty), with uv as the package manager.
- Derive `__version__` from package metadata so the version is declared
in one place only.
- Extend the ruff lint selection (isort, flake8-debugger, flake8-print)
and drop the ignores that no longer apply.
- Add ty type checking and a pre-commit config running format, lint,
typecheck and tests. Annotate `error()` as `NoReturn`, which is what
it always was and what ty needs to see to narrow correctly.
- Reorganize CI into separate lint, typecheck, test, build and publish
jobs driven by uv, upgrade all actions to their latest versions and
pin third-party actions by commit SHA. Tests now run on Python 3.13
and 3.14; the matrix stays on Windows because the suite needs an OS
keyring that unlocks unattended.
- Document the development workflow and the 3.13+ requirement in the
README.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011nTpsbs48Pcky9GVthXnWv
* Fully annotate the package and enforce it
Annotate every function, parameter and module-level variable in
`keycmd`, and ship a `py.typed` marker so downstream consumers get the
types too.
Typing the config surface required describing its shape, so the
[keys] and [aliases] tables are now `TypedDict`s. `load_conf` casts to
that shape at the boundary where user authored TOML enters the program,
which is where the promise is actually made.
Enforcement, so this does not decay:
- ruff's `ANN` rules require annotations (the test suite is exempt).
- ty's off-by-default rules are enabled: `missing-type-argument`,
`possibly-missing-attribute`, `possibly-missing-import`,
`possibly-unresolved-reference` and `division-by-zero`. Suppressions
must name a rule and must be needed (`blanket-ignore-comment`,
`unused-ignore-comment`).
- CI and pre-commit run `ty check --error-on-warning`, so warn-level
diagnostics fail rather than scroll by.
Two fixes fell out of making the types honest:
- `shell.exec` passed `env=None` straight to `os.execvpe`, which
requires a mapping and would have raised `TypeError`. It now falls
back to `os.environ`, matching what the subprocess branch already did.
- `creds.get_env` bound both the loop variable and the looked up key
data to the name `key`, so the two had different types under one name.
They are now `key`/`data` and `src`/`alias_src`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011nTpsbs48Pcky9GVthXnWv
* Install runtime dependencies in the typecheck job
`uv sync --only-group ty` installs ty but not the project, so ty could
not resolve the `keyring` and `shellingham` imports and failed with
`unresolved-import`.
The pattern was copied from observ, where it is correct because that
project has no runtime dependencies. keycmd has two, so the typecheck
job needs the project installed as well. It still skips the rest of the
dev group.
The lint job keeps using `--only-group ruff`: ruff is purely syntactic
and needs nothing installed to resolve.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011nTpsbs48Pcky9GVthXnWv
---------
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: README.md
+37-2Lines changed: 37 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,12 +18,20 @@ The most common use case is to load credentials for package managers such as pip
18
18
19
19
## Installation
20
20
21
+
`keycmd` requires Python 3.13 or newer.
22
+
21
23
> **Note**
22
24
> If you're intending to install `keycmd` in a WSL or pyenv environment, you'll have to skip ahead to the specific installation instructions for those environments.
23
25
24
26
### Global installation
25
27
26
-
Install `keycmd` from pypi using `pip install keycmd`, or whatever alternative python package manager you prefer.
28
+
Since `keycmd` is a command line tool, the recommended way to install it is with [uv](https://docs.astral.sh/uv/):
29
+
30
+
```bash
31
+
uv tool install keycmd
32
+
```
33
+
34
+
This installs `keycmd` into its own isolated environment and puts the executable on your `PATH`. Alternatively, install it from pypi using `pip install keycmd`, or whatever alternative python package manager you prefer.
27
35
28
36
Note that the executable `keycmd` has to be installed to a folder that is on your `PATH` environment variable, or the command won't be available globally. Assuming you were able to run `pip` just now, the `keycmd` executable should end up in the exact same location and everything should be fine.
29
37
@@ -42,7 +50,7 @@ Run the following commands one by one to install keycmd into its own standalone
Since keycmd uses keyring as its backend, you're not limited to just working with OS keyrings. 🤯 Any keyring backend will work with keycmd. No special configuration required!
389
397
390
398
See the [third party backends](https://github.com/jaraco/keyring/#third-party-backends) list for all options.
399
+
400
+
## Development
401
+
402
+
This project uses [uv](https://docs.astral.sh/uv/) for dependency management, [ruff](https://docs.astral.sh/ruff/) for linting and formatting, and [ty](https://docs.astral.sh/ty/) for type checking.
403
+
404
+
The `keycmd` package is fully annotated and ships a `py.typed` marker, so the types are available to anything that imports it. Ruff's `ANN` rules keep it that way; the test suite is exempt.
405
+
406
+
```bash
407
+
# create the virtual environment and install all dependencies
408
+
uv sync
409
+
410
+
# install the git hooks that run the checks below on every commit
411
+
uv run pre-commit install
412
+
413
+
# lint, format, typecheck and test
414
+
uv run ruff check --fix
415
+
uv run ruff format
416
+
uv run ty check
417
+
uv run pytest tests
418
+
```
419
+
420
+
Note that the test suite exercises a real OS keyring, so it needs a keyring backend that can be unlocked without user interaction. On Windows that works out of the box, which is why CI runs the tests there. On other platforms you can point keyring at a file-based backend instead:
421
+
422
+
```bash
423
+
uv run --with keyrings.alt pytest tests
424
+
# with PYTHON_KEYRING_BACKEND=keyrings.alt.file.PlaintextKeyring set in your environment
0 commit comments