uv-kernel-manager 0.1.1a1__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.
- uv_kernel_manager-0.1.1a1/.github/workflows/ci.yml +32 -0
- uv_kernel_manager-0.1.1a1/.github/workflows/release.yaml +69 -0
- uv_kernel_manager-0.1.1a1/.gitignore +11 -0
- uv_kernel_manager-0.1.1a1/LICENSE +21 -0
- uv_kernel_manager-0.1.1a1/PKG-INFO +171 -0
- uv_kernel_manager-0.1.1a1/README.md +155 -0
- uv_kernel_manager-0.1.1a1/pyproject.toml +37 -0
- uv_kernel_manager-0.1.1a1/src/uv_kernel/__init__.py +7 -0
- uv_kernel_manager-0.1.1a1/src/uv_kernel/cli.py +146 -0
- uv_kernel_manager-0.1.1a1/src/uv_kernel/core.py +171 -0
- uv_kernel_manager-0.1.1a1/tests/test_cli.py +73 -0
- uv_kernel_manager-0.1.1a1/tests/test_core.py +40 -0
- uv_kernel_manager-0.1.1a1/uv.lock +965 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, develop]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
test-and-build:
|
|
15
|
+
name: Test and Build Validation
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: astral-sh/setup-uv@v6
|
|
20
|
+
- name: Set up the project environment
|
|
21
|
+
run: uv sync --locked --group dev
|
|
22
|
+
- name: Run tests
|
|
23
|
+
run: uv run --group dev pytest
|
|
24
|
+
- name: Build distributions
|
|
25
|
+
run: uv build --no-sources --clear
|
|
26
|
+
- name: Validate distribution metadata
|
|
27
|
+
run: uv run --group dev twine check dist/*
|
|
28
|
+
- name: Upload distributions
|
|
29
|
+
uses: actions/upload-artifact@v4
|
|
30
|
+
with:
|
|
31
|
+
name: dist
|
|
32
|
+
path: dist/
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
name: Release to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
# Trusted Publishing exchanges this short-lived GitHub OIDC identity for a
|
|
8
|
+
# PyPI upload credential. No repository secret or write-capable token is used.
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: pypi-publish
|
|
15
|
+
cancel-in-progress: false
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
publish:
|
|
19
|
+
name: Validate, build, and publish
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
environment:
|
|
22
|
+
name: pypi
|
|
23
|
+
url: https://pypi.org/p/uv-kernel-manager
|
|
24
|
+
steps:
|
|
25
|
+
- name: Check out the released tag
|
|
26
|
+
uses: actions/checkout@v4
|
|
27
|
+
with:
|
|
28
|
+
ref: ${{ github.event.release.tag_name }}
|
|
29
|
+
persist-credentials: false
|
|
30
|
+
|
|
31
|
+
- name: Install uv
|
|
32
|
+
uses: astral-sh/setup-uv@v6
|
|
33
|
+
|
|
34
|
+
- name: Validate the release tag and package version
|
|
35
|
+
env:
|
|
36
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
37
|
+
run: |
|
|
38
|
+
set -euo pipefail
|
|
39
|
+
if [[ ! "$RELEASE_TAG" =~ ^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)((a|b|rc)(0|[1-9][0-9]*))?$ ]]; then
|
|
40
|
+
echo "Release tag must be vMAJOR.MINOR.PATCH or a PyPI pre-release such as v0.1.0rc1; got: $RELEASE_TAG" >&2
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
package_version="$(python - <<'PY'
|
|
44
|
+
import tomllib
|
|
45
|
+
from pathlib import Path
|
|
46
|
+
|
|
47
|
+
manifest = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))
|
|
48
|
+
print(manifest["project"]["version"])
|
|
49
|
+
PY
|
|
50
|
+
)"
|
|
51
|
+
if [[ "v$package_version" != "$RELEASE_TAG" ]]; then
|
|
52
|
+
echo "Release tag $RELEASE_TAG does not match pyproject.toml version $package_version" >&2
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
- name: Install locked development dependencies
|
|
57
|
+
run: uv sync --locked --group dev
|
|
58
|
+
|
|
59
|
+
- name: Run tests
|
|
60
|
+
run: uv run --group dev pytest
|
|
61
|
+
|
|
62
|
+
- name: Build distributions
|
|
63
|
+
run: uv build --no-sources --clear
|
|
64
|
+
|
|
65
|
+
- name: Validate distribution metadata
|
|
66
|
+
run: uv run --group dev twine check dist/*
|
|
67
|
+
|
|
68
|
+
- name: Publish to PyPI with Trusted Publishing
|
|
69
|
+
run: uv publish --trusted-publishing always dist/*
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 nOtFoRgEeKs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: uv-kernel-manager
|
|
3
|
+
Version: 0.1.1a1
|
|
4
|
+
Summary: A lightweight CLI to seamlessly register and manage uv-managed Python environments as Jupyter kernels.
|
|
5
|
+
Project-URL: Homepage, https://github.com/nOtFoRgEeKs/uv-kernel-manager
|
|
6
|
+
Author: nOtFoRgEeKs
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Requires-Dist: jupyter-client>=8.0
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# uv-kernel-manager
|
|
18
|
+
|
|
19
|
+
`uv-kernel-manager` registers the Python environments created by
|
|
20
|
+
[`uv`](https://docs.astral.sh/uv/) projects as Jupyter kernels. It is designed
|
|
21
|
+
for a single JupyterLab installation shared across many projects, each with
|
|
22
|
+
its own project-local `.venv`.
|
|
23
|
+
|
|
24
|
+
The PyPI distribution is `uv-kernel-manager`; the command is `uv-kernel`; the
|
|
25
|
+
Python package is `uv_kernel`.
|
|
26
|
+
|
|
27
|
+
## How it works
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
JupyterLab installation
|
|
31
|
+
│
|
|
32
|
+
├── Python 3.12 (analytics) ──> analytics/.venv/bin/python
|
|
33
|
+
└── Python 3.13 (forecasting) ─> forecasting/.venv/bin/python
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Run `uv-kernel register` from a uv project to create a standard, user-level
|
|
37
|
+
Jupyter kernelspec pointing at that project's interpreter. The command suggests
|
|
38
|
+
a label in the form `Python <major.minor> (<project-name>)`, such as
|
|
39
|
+
`Python 3.13 (forecasting)`, and lets you accept it or provide a label that is
|
|
40
|
+
more meaningful to you.
|
|
41
|
+
|
|
42
|
+
The tool manages kernelspecs only. It does not install, start, configure, or
|
|
43
|
+
upgrade JupyterLab; it also does not create or otherwise manage your uv
|
|
44
|
+
projects or their virtual environments.
|
|
45
|
+
|
|
46
|
+
## Prerequisites
|
|
47
|
+
|
|
48
|
+
- [uv](https://docs.astral.sh/uv/) on your `PATH`
|
|
49
|
+
- A uv project with a `pyproject.toml` and a synchronized `.venv` (`uv sync`)
|
|
50
|
+
- JupyterLab or another Jupyter frontend installed wherever you normally run it
|
|
51
|
+
|
|
52
|
+
`register` adds `ipykernel` to the current project's development dependencies
|
|
53
|
+
by default. Use `--no-add-ipykernel` only when it is already installed or you
|
|
54
|
+
do not want the command to change project dependencies.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
Install the tool permanently with uv:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
uv tool install uv-kernel-manager
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Then run the `uv-kernel` command from a project directory:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
cd path/to/forecasting
|
|
68
|
+
uv sync
|
|
69
|
+
uv-kernel register
|
|
70
|
+
jupyter lab
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
During registration, the tool shows the suggested Jupyter label:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
Register kernel as: Python 3.13 (forecasting)
|
|
77
|
+
Press Enter to continue, or enter a custom name:
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Press Enter to use it, or type a custom label. For scripts and other
|
|
81
|
+
non-interactive usage, accept the suggestion with `--yes` or set it explicitly
|
|
82
|
+
with `--name`:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv-kernel register --yes
|
|
86
|
+
uv-kernel register --name "Forecasting — production"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
To run without installing it, use `uvx`:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
uvx uv-kernel-manager register
|
|
93
|
+
uvx uv-kernel-manager status
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Commands
|
|
97
|
+
|
|
98
|
+
| Command | Purpose |
|
|
99
|
+
| --- | --- |
|
|
100
|
+
| `uv-kernel register` | Prompt for a label and add the current project's `.venv` as a Jupyter kernel. |
|
|
101
|
+
| `uv-kernel register --yes` | Register with the suggested label without prompting. |
|
|
102
|
+
| `uv-kernel register --name "NAME"` | Register with an explicit Jupyter label. |
|
|
103
|
+
| `uv-kernel status` | Show whether the current project is registered. |
|
|
104
|
+
| `uv-kernel refresh` | Recreate the current project's kernel with its current interpreter and settings. |
|
|
105
|
+
| `uv-kernel unregister` | Remove the current project's managed kernel. |
|
|
106
|
+
| `uv-kernel list` | List every kernel managed by this tool. |
|
|
107
|
+
| `uv-kernel clean` | Report managed kernels whose project or interpreter no longer exists. |
|
|
108
|
+
| `uv-kernel clean --yes` | Remove the stale managed kernels reported by `clean`. |
|
|
109
|
+
| `uv-kernel doctor` | Show diagnostic information about uv, the project, and Jupyter kernel locations. |
|
|
110
|
+
|
|
111
|
+
Use `--json` with reporting commands when scripting, for example:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
uv-kernel --json list
|
|
115
|
+
uv-kernel --json status
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
For a non-interactive JSON registration, include either `--yes` or `--name`:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
uv-kernel --json register --yes
|
|
122
|
+
uv-kernel --json register --name "Forecasting — production"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Safety and ownership
|
|
126
|
+
|
|
127
|
+
Every kernel created by this tool includes a structured `metadata.uv-kernel`
|
|
128
|
+
ownership record. `unregister`, `refresh`, and `clean` act only on kernels
|
|
129
|
+
with that record; manually installed kernels and kernels managed by other
|
|
130
|
+
tools are left untouched. Kernel directory names also begin with `uv-kernel-`,
|
|
131
|
+
but the metadata marker—not the directory name—is the ownership check.
|
|
132
|
+
|
|
133
|
+
`clean` is intentionally non-destructive unless you explicitly pass `--yes`.
|
|
134
|
+
|
|
135
|
+
## Build from source
|
|
136
|
+
|
|
137
|
+
To build the package locally, clone the repository and install its development
|
|
138
|
+
dependencies:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
uv sync --dev
|
|
142
|
+
uv build
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The wheel and source distribution are written to `dist/`. To install the local
|
|
146
|
+
checkout as a tool, run:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
uv tool install .
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Development
|
|
153
|
+
|
|
154
|
+
Run the test suite and validate built package metadata before contributing:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
uv run pytest
|
|
158
|
+
uv run twine check dist/*
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
You can also run the command directly from the checkout:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
uv run uv-kernel --help
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Contributing
|
|
168
|
+
|
|
169
|
+
Contributions are welcome. Please open an issue or pull request with a focused
|
|
170
|
+
change, include tests when behavior changes, and run the checks above before
|
|
171
|
+
submitting.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# uv-kernel-manager
|
|
2
|
+
|
|
3
|
+
`uv-kernel-manager` registers the Python environments created by
|
|
4
|
+
[`uv`](https://docs.astral.sh/uv/) projects as Jupyter kernels. It is designed
|
|
5
|
+
for a single JupyterLab installation shared across many projects, each with
|
|
6
|
+
its own project-local `.venv`.
|
|
7
|
+
|
|
8
|
+
The PyPI distribution is `uv-kernel-manager`; the command is `uv-kernel`; the
|
|
9
|
+
Python package is `uv_kernel`.
|
|
10
|
+
|
|
11
|
+
## How it works
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
JupyterLab installation
|
|
15
|
+
│
|
|
16
|
+
├── Python 3.12 (analytics) ──> analytics/.venv/bin/python
|
|
17
|
+
└── Python 3.13 (forecasting) ─> forecasting/.venv/bin/python
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Run `uv-kernel register` from a uv project to create a standard, user-level
|
|
21
|
+
Jupyter kernelspec pointing at that project's interpreter. The command suggests
|
|
22
|
+
a label in the form `Python <major.minor> (<project-name>)`, such as
|
|
23
|
+
`Python 3.13 (forecasting)`, and lets you accept it or provide a label that is
|
|
24
|
+
more meaningful to you.
|
|
25
|
+
|
|
26
|
+
The tool manages kernelspecs only. It does not install, start, configure, or
|
|
27
|
+
upgrade JupyterLab; it also does not create or otherwise manage your uv
|
|
28
|
+
projects or their virtual environments.
|
|
29
|
+
|
|
30
|
+
## Prerequisites
|
|
31
|
+
|
|
32
|
+
- [uv](https://docs.astral.sh/uv/) on your `PATH`
|
|
33
|
+
- A uv project with a `pyproject.toml` and a synchronized `.venv` (`uv sync`)
|
|
34
|
+
- JupyterLab or another Jupyter frontend installed wherever you normally run it
|
|
35
|
+
|
|
36
|
+
`register` adds `ipykernel` to the current project's development dependencies
|
|
37
|
+
by default. Use `--no-add-ipykernel` only when it is already installed or you
|
|
38
|
+
do not want the command to change project dependencies.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
Install the tool permanently with uv:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
uv tool install uv-kernel-manager
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Then run the `uv-kernel` command from a project directory:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cd path/to/forecasting
|
|
52
|
+
uv sync
|
|
53
|
+
uv-kernel register
|
|
54
|
+
jupyter lab
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
During registration, the tool shows the suggested Jupyter label:
|
|
58
|
+
|
|
59
|
+
```text
|
|
60
|
+
Register kernel as: Python 3.13 (forecasting)
|
|
61
|
+
Press Enter to continue, or enter a custom name:
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Press Enter to use it, or type a custom label. For scripts and other
|
|
65
|
+
non-interactive usage, accept the suggestion with `--yes` or set it explicitly
|
|
66
|
+
with `--name`:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
uv-kernel register --yes
|
|
70
|
+
uv-kernel register --name "Forecasting — production"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
To run without installing it, use `uvx`:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
uvx uv-kernel-manager register
|
|
77
|
+
uvx uv-kernel-manager status
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Commands
|
|
81
|
+
|
|
82
|
+
| Command | Purpose |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| `uv-kernel register` | Prompt for a label and add the current project's `.venv` as a Jupyter kernel. |
|
|
85
|
+
| `uv-kernel register --yes` | Register with the suggested label without prompting. |
|
|
86
|
+
| `uv-kernel register --name "NAME"` | Register with an explicit Jupyter label. |
|
|
87
|
+
| `uv-kernel status` | Show whether the current project is registered. |
|
|
88
|
+
| `uv-kernel refresh` | Recreate the current project's kernel with its current interpreter and settings. |
|
|
89
|
+
| `uv-kernel unregister` | Remove the current project's managed kernel. |
|
|
90
|
+
| `uv-kernel list` | List every kernel managed by this tool. |
|
|
91
|
+
| `uv-kernel clean` | Report managed kernels whose project or interpreter no longer exists. |
|
|
92
|
+
| `uv-kernel clean --yes` | Remove the stale managed kernels reported by `clean`. |
|
|
93
|
+
| `uv-kernel doctor` | Show diagnostic information about uv, the project, and Jupyter kernel locations. |
|
|
94
|
+
|
|
95
|
+
Use `--json` with reporting commands when scripting, for example:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
uv-kernel --json list
|
|
99
|
+
uv-kernel --json status
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
For a non-interactive JSON registration, include either `--yes` or `--name`:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
uv-kernel --json register --yes
|
|
106
|
+
uv-kernel --json register --name "Forecasting — production"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Safety and ownership
|
|
110
|
+
|
|
111
|
+
Every kernel created by this tool includes a structured `metadata.uv-kernel`
|
|
112
|
+
ownership record. `unregister`, `refresh`, and `clean` act only on kernels
|
|
113
|
+
with that record; manually installed kernels and kernels managed by other
|
|
114
|
+
tools are left untouched. Kernel directory names also begin with `uv-kernel-`,
|
|
115
|
+
but the metadata marker—not the directory name—is the ownership check.
|
|
116
|
+
|
|
117
|
+
`clean` is intentionally non-destructive unless you explicitly pass `--yes`.
|
|
118
|
+
|
|
119
|
+
## Build from source
|
|
120
|
+
|
|
121
|
+
To build the package locally, clone the repository and install its development
|
|
122
|
+
dependencies:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
uv sync --dev
|
|
126
|
+
uv build
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The wheel and source distribution are written to `dist/`. To install the local
|
|
130
|
+
checkout as a tool, run:
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
uv tool install .
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Development
|
|
137
|
+
|
|
138
|
+
Run the test suite and validate built package metadata before contributing:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
uv run pytest
|
|
142
|
+
uv run twine check dist/*
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
You can also run the command directly from the checkout:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
uv run uv-kernel --help
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Contributing
|
|
152
|
+
|
|
153
|
+
Contributions are welcome. Please open an issue or pull request with a focused
|
|
154
|
+
change, include tests when behavior changes, and run the checks above before
|
|
155
|
+
submitting.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "uv-kernel-manager"
|
|
7
|
+
version = "0.1.1a1"
|
|
8
|
+
|
|
9
|
+
description = "A lightweight CLI to seamlessly register and manage uv-managed Python environments as Jupyter kernels."
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
license = "MIT"
|
|
13
|
+
license-files = ["LICENSE"]
|
|
14
|
+
authors = [{ name = "nOtFoRgEeKs" }]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Topic :: Software Development :: Build Tools",
|
|
20
|
+
]
|
|
21
|
+
dependencies = ["jupyter_client>=8.0"]
|
|
22
|
+
|
|
23
|
+
[project.scripts]
|
|
24
|
+
uv-kernel = "uv_kernel.cli:main"
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Homepage = "https://github.com/nOtFoRgEeKs/uv-kernel-manager"
|
|
28
|
+
|
|
29
|
+
[tool.hatch.build.targets.wheel]
|
|
30
|
+
packages = ["src/uv_kernel"]
|
|
31
|
+
|
|
32
|
+
[tool.pytest.ini_options]
|
|
33
|
+
testpaths = ["tests"]
|
|
34
|
+
addopts = "-q"
|
|
35
|
+
|
|
36
|
+
[dependency-groups]
|
|
37
|
+
dev = ["pytest>=8", "twine>=6"]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Manage Jupyter kernels for uv-managed projects."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import version
|
|
4
|
+
|
|
5
|
+
# The installed distribution metadata is generated from [project].version in
|
|
6
|
+
# pyproject.toml, keeping that file as the only version authority.
|
|
7
|
+
__version__ = version("uv-kernel-manager")
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Command line interface for uv-kernel."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import json
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from . import __version__
|
|
12
|
+
from .core import (UVKernelError, display_name, doctor, ensure_ipykernel,
|
|
13
|
+
find_project, install, matching, owned_kernels, remove, stale)
|
|
14
|
+
|
|
15
|
+
DESCRIPTION = """
|
|
16
|
+
Manage Jupyter kernels for uv-managed Python environments.
|
|
17
|
+
|
|
18
|
+
Register a project's uv `.venv` with the Jupyter installation you already use.
|
|
19
|
+
Only kernels carrying uv-kernel's ownership metadata are ever changed or removed.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def parser() -> argparse.ArgumentParser:
|
|
24
|
+
p = argparse.ArgumentParser(prog="uv-kernel", description=DESCRIPTION,
|
|
25
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
26
|
+
epilog="Examples:\n uv-kernel register\n uv-kernel --json list\n uv-kernel clean --yes")
|
|
27
|
+
p.add_argument("--version", action="version", version=f"%(prog)s {__version__}")
|
|
28
|
+
p.add_argument("--json", action="store_true", dest="json_output", help="Output machine-readable JSON.")
|
|
29
|
+
sub = p.add_subparsers(dest="command", required=True, metavar="COMMAND")
|
|
30
|
+
register = sub.add_parser("register", help="Register the current uv project as a Jupyter kernel.",
|
|
31
|
+
description="Adds ipykernel as a development dependency by default and writes a standard user kernelspec.")
|
|
32
|
+
register.add_argument("--no-add-ipykernel", action="store_true", help="Do not run `uv add --dev ipykernel` first.")
|
|
33
|
+
naming = register.add_mutually_exclusive_group()
|
|
34
|
+
naming.add_argument("--name", metavar="NAME", help="Use NAME as the kernel label instead of the suggested label.")
|
|
35
|
+
naming.add_argument("--yes", action="store_true", help="Accept the suggested kernel label without prompting.")
|
|
36
|
+
sub.add_parser("unregister", help="Remove this project's uv-kernel-owned kernel.",
|
|
37
|
+
description="Removes only kernels marked as owned by uv-kernel for the current project.")
|
|
38
|
+
sub.add_parser("list", help="List every kernel owned by uv-kernel.")
|
|
39
|
+
sub.add_parser("status", help="Show this project's uv-kernel registration status.")
|
|
40
|
+
sub.add_parser("refresh", help="Recreate this project's owned kernel with current settings.",
|
|
41
|
+
description="Updates the spec for the current .venv; it never touches other kernels.")
|
|
42
|
+
clean = sub.add_parser("clean", help="Find stale uv-kernel-owned kernels safely.",
|
|
43
|
+
description="Reports kernels whose project or .venv interpreter is gone. Nothing is deleted without --yes.")
|
|
44
|
+
clean.add_argument("--yes", action="store_true", help="Remove the stale kernels reported by clean.")
|
|
45
|
+
sub.add_parser("doctor", help="Diagnose uv, the current project, and Jupyter kernel paths.")
|
|
46
|
+
return p
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def emit(value: Any, as_json: bool) -> None:
|
|
50
|
+
if as_json:
|
|
51
|
+
print(json.dumps(value, indent=2, sort_keys=True))
|
|
52
|
+
return
|
|
53
|
+
if isinstance(value, list):
|
|
54
|
+
if not value:
|
|
55
|
+
print("No uv-kernel-owned kernels found.")
|
|
56
|
+
for item in value:
|
|
57
|
+
print(f"{item['name']}: {item.get('display_name', '')} — {item.get('project_path', '')}")
|
|
58
|
+
elif isinstance(value, dict):
|
|
59
|
+
for key, item in value.items(): print(f"{key}: {item}")
|
|
60
|
+
else: print(value)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def choose_kernel_name(default: str) -> str:
|
|
64
|
+
"""Prompt for a kernel label, accepting the suggested label on Enter."""
|
|
65
|
+
try:
|
|
66
|
+
response = input(
|
|
67
|
+
f"Register kernel as: {default}\n"
|
|
68
|
+
"Press Enter to continue, or enter a custom name: "
|
|
69
|
+
).strip()
|
|
70
|
+
except EOFError as exc:
|
|
71
|
+
raise UVKernelError("No kernel name was provided. Use --yes or --name for non-interactive use.") from exc
|
|
72
|
+
return response or default
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def main(argv: list[str] | None = None) -> int:
|
|
76
|
+
args = parser().parse_args(argv)
|
|
77
|
+
try:
|
|
78
|
+
if args.command == "list":
|
|
79
|
+
emit(owned_kernels(), args.json_output)
|
|
80
|
+
return 0
|
|
81
|
+
|
|
82
|
+
if args.command == "clean":
|
|
83
|
+
items = stale(owned_kernels())
|
|
84
|
+
if args.yes:
|
|
85
|
+
removed = remove(items)
|
|
86
|
+
emit({"removed": removed, "count": len(removed)}, args.json_output)
|
|
87
|
+
else:
|
|
88
|
+
emit(items, args.json_output)
|
|
89
|
+
if not args.json_output and items:
|
|
90
|
+
print("Run `uv-kernel clean --yes` to remove these kernels.")
|
|
91
|
+
return 0
|
|
92
|
+
|
|
93
|
+
if args.command == "doctor":
|
|
94
|
+
try:
|
|
95
|
+
project = find_project()
|
|
96
|
+
except UVKernelError:
|
|
97
|
+
project = None
|
|
98
|
+
emit(doctor(project), args.json_output)
|
|
99
|
+
return 0
|
|
100
|
+
|
|
101
|
+
project = find_project()
|
|
102
|
+
if args.command == "register":
|
|
103
|
+
suggested_name = display_name(project)
|
|
104
|
+
if args.name:
|
|
105
|
+
kernel_display_name = args.name
|
|
106
|
+
elif args.yes:
|
|
107
|
+
kernel_display_name = suggested_name
|
|
108
|
+
elif args.json_output:
|
|
109
|
+
raise UVKernelError("Use --yes or --name with --json register.")
|
|
110
|
+
else:
|
|
111
|
+
kernel_display_name = choose_kernel_name(suggested_name)
|
|
112
|
+
if not args.no_add_ipykernel:
|
|
113
|
+
ensure_ipykernel(project)
|
|
114
|
+
result = install(project, kernel_display_name=kernel_display_name)
|
|
115
|
+
emit({"registered": result}, args.json_output)
|
|
116
|
+
return 0
|
|
117
|
+
|
|
118
|
+
if args.command == "unregister":
|
|
119
|
+
removed = remove(matching(project))
|
|
120
|
+
emit({"removed": removed, "count": len(removed)}, args.json_output)
|
|
121
|
+
return 0
|
|
122
|
+
|
|
123
|
+
if args.command == "status":
|
|
124
|
+
items = matching(project)
|
|
125
|
+
result = {"project": str(project.root), "registered": bool(items), "kernels": items}
|
|
126
|
+
emit(result, args.json_output)
|
|
127
|
+
return 0 if items else 1
|
|
128
|
+
|
|
129
|
+
if args.command == "refresh":
|
|
130
|
+
remove(matching(project))
|
|
131
|
+
result = install(project)
|
|
132
|
+
emit({"refreshed": result}, args.json_output)
|
|
133
|
+
return 0
|
|
134
|
+
|
|
135
|
+
except UVKernelError as exc:
|
|
136
|
+
if args.json_output:
|
|
137
|
+
print(json.dumps({"error": str(exc)}))
|
|
138
|
+
else:
|
|
139
|
+
print(f"uv-kernel: error: {exc}", file=sys.stderr)
|
|
140
|
+
return 2
|
|
141
|
+
|
|
142
|
+
return 0
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
if __name__ == "__main__":
|
|
146
|
+
raise SystemExit(main())
|