uvk 0.0.1__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.
- uvk-0.0.1/PKG-INFO +49 -0
- uvk-0.0.1/README.md +26 -0
- uvk-0.0.1/pyproject.toml +38 -0
- uvk-0.0.1/src/uvk/__init__.py +47 -0
- uvk-0.0.1/src/uvk/__main__.py +108 -0
uvk-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: uvk
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Fully isolated IPython kernels based on uv
|
|
5
|
+
Keywords: jupyter,kernel,ipython,ipykernel,uv
|
|
6
|
+
Author: Benoit Hamelin
|
|
7
|
+
Author-email: Benoit Hamelin <benoit.hamelin@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Requires-Dist: ipykernel>=7.0.0
|
|
16
|
+
Requires-Dist: jupyter-core>=5.9.1
|
|
17
|
+
Requires-Dist: uv>=0.10.2
|
|
18
|
+
Maintainer: Benoit Hamelin
|
|
19
|
+
Maintainer-email: Benoit Hamelin <benoit.hamelin@gmail.com>
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Project-URL: Homepage, https://github.com/hamelin/uvk
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# uvk -- Fully isolated IPython kernels based on uv
|
|
25
|
+
|
|
26
|
+
Proper documentation TBD... In the meantime:
|
|
27
|
+
|
|
28
|
+
1. Install `uvk` in an environment from which you run Jupyter Lab / Notebook.
|
|
29
|
+
1. Invoke `uvk` from this environment to install the UVK kernel attached to the Python executable of your environment.
|
|
30
|
+
- Distinct icon TBD
|
|
31
|
+
1. Use kernel `UVK (Python 3.xx)` in your notebooks
|
|
32
|
+
|
|
33
|
+
and 🎉 your Python kernel runs out of a temporary, isolated environment.
|
|
34
|
+
Check it out by running a notebook with a code cell going
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
import sys
|
|
38
|
+
sys.prefix
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
and see how every time you restart the kernel, the prefix directory changes.
|
|
42
|
+
|
|
43
|
+
Feature wishlist:
|
|
44
|
+
|
|
45
|
+
- Python version validation against a constraint with a magic
|
|
46
|
+
- Magic for adding dependencies on the fly
|
|
47
|
+
- Magic for including [inline script metadata](https://packaging.python.org/en/latest/specifications/inline-script-metadata/#inline-script-metadata)
|
|
48
|
+
- Integration with uv project workflows
|
|
49
|
+
- Python version and executable management through duly-named alternate kernels
|
uvk-0.0.1/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# uvk -- Fully isolated IPython kernels based on uv
|
|
2
|
+
|
|
3
|
+
Proper documentation TBD... In the meantime:
|
|
4
|
+
|
|
5
|
+
1. Install `uvk` in an environment from which you run Jupyter Lab / Notebook.
|
|
6
|
+
1. Invoke `uvk` from this environment to install the UVK kernel attached to the Python executable of your environment.
|
|
7
|
+
- Distinct icon TBD
|
|
8
|
+
1. Use kernel `UVK (Python 3.xx)` in your notebooks
|
|
9
|
+
|
|
10
|
+
and 🎉 your Python kernel runs out of a temporary, isolated environment.
|
|
11
|
+
Check it out by running a notebook with a code cell going
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
import sys
|
|
15
|
+
sys.prefix
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
and see how every time you restart the kernel, the prefix directory changes.
|
|
19
|
+
|
|
20
|
+
Feature wishlist:
|
|
21
|
+
|
|
22
|
+
- Python version validation against a constraint with a magic
|
|
23
|
+
- Magic for adding dependencies on the fly
|
|
24
|
+
- Magic for including [inline script metadata](https://packaging.python.org/en/latest/specifications/inline-script-metadata/#inline-script-metadata)
|
|
25
|
+
- Integration with uv project workflows
|
|
26
|
+
- Python version and executable management through duly-named alternate kernels
|
uvk-0.0.1/pyproject.toml
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build>=0.10.2"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "uvk"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
authors = [{name = "Benoit Hamelin", email = "benoit.hamelin@gmail.com"}]
|
|
9
|
+
maintainers = [{name = "Benoit Hamelin", email = "benoit.hamelin@gmail.com"}]
|
|
10
|
+
description = "Fully isolated IPython kernels based on uv"
|
|
11
|
+
readme = "README.md"
|
|
12
|
+
keywords = ["jupyter", "kernel", "ipython", "ipykernel", "uv"]
|
|
13
|
+
license = "MIT"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3.10",
|
|
16
|
+
"Programming Language :: Python :: 3.11",
|
|
17
|
+
"Programming Language :: Python :: 3.12",
|
|
18
|
+
"Programming Language :: Python :: 3.13",
|
|
19
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
]
|
|
22
|
+
requires-python = ">=3.10"
|
|
23
|
+
dependencies = [
|
|
24
|
+
"ipykernel>=7.0.0",
|
|
25
|
+
"jupyter-core>=5.9.1",
|
|
26
|
+
"uv>=0.10.2",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/hamelin/uvk"
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
uvk = "uvk.__main__:main"
|
|
34
|
+
|
|
35
|
+
[tool.setuptools]
|
|
36
|
+
zip-safe = true
|
|
37
|
+
include-package-data = true
|
|
38
|
+
packages = ["uvk"]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The uvk IPython extension provides magicks to manage the dependencies
|
|
3
|
+
of an isolated kernel. It also ensures that the uv executable is visible
|
|
4
|
+
by the IPython process, as shelling out to uv is how most of the kernel's
|
|
5
|
+
specific functionalities are delivered.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
from IPython.core.magic import cell_magic, line_magic, line_cell_magic
|
|
10
|
+
import subprocess as sp
|
|
11
|
+
import warnings
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CannotFindUV(Exception):
|
|
15
|
+
|
|
16
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
17
|
+
super().__init__(
|
|
18
|
+
(
|
|
19
|
+
"uv executable cannot be found; "
|
|
20
|
+
"it is critical for the features provided by "
|
|
21
|
+
f"extension {__name__}."
|
|
22
|
+
),
|
|
23
|
+
*args,
|
|
24
|
+
**kwargs,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def load_ipython_extension(ipython) -> None:
|
|
29
|
+
try:
|
|
30
|
+
sp.run(["uv"], stdout=sp.DEVNULL, stderr=sp.DEVNULL)
|
|
31
|
+
except OSError:
|
|
32
|
+
raise CannotFindUV()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@cell_magic
|
|
36
|
+
def script_metadata(cell: str) -> None:
|
|
37
|
+
print("TBD")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@line_magic
|
|
41
|
+
def python_version(line: str) -> None:
|
|
42
|
+
print("TBD")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@line_cell_magic
|
|
46
|
+
def dependencies(line: str) -> None:
|
|
47
|
+
print("TBD")
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from argparse import ArgumentParser
|
|
2
|
+
import json
|
|
3
|
+
import logging as lg
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess as sp
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
LOG = lg.getLogger("uvk")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main():
|
|
14
|
+
lg.basicConfig(level=lg.DEBUG, format="%(levelname)-10s %(message)s")
|
|
15
|
+
|
|
16
|
+
parser = ArgumentParser(
|
|
17
|
+
description="Deploys the UVK kernel",
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
"--name",
|
|
21
|
+
help="Name of the kernelspec. Default is `uvk`.",
|
|
22
|
+
default="uvk",
|
|
23
|
+
)
|
|
24
|
+
displayname_default = f"UVK (Python {sys.version_info.major}.{sys.version_info.minor})"
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"--display-name",
|
|
27
|
+
help=(
|
|
28
|
+
"Pretty name for the kernelspec that will show in Jupyter "
|
|
29
|
+
f"interface. Default is `{displayname_default}`."
|
|
30
|
+
),
|
|
31
|
+
default=displayname_default,
|
|
32
|
+
)
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--tmp",
|
|
35
|
+
help="Location of the temporary directory. Default is the system's.",
|
|
36
|
+
default="",
|
|
37
|
+
)
|
|
38
|
+
args = parser.parse_args()
|
|
39
|
+
|
|
40
|
+
path_uv = shutil.which("uv")
|
|
41
|
+
if not path_uv:
|
|
42
|
+
LOG.error("Cannot find jupyter executable. Abort")
|
|
43
|
+
sys.exit(cp.returncode)
|
|
44
|
+
|
|
45
|
+
cp = sp.run(["jupyter", "--paths", "--json"], stdout=sp.PIPE)
|
|
46
|
+
paths_data = list(json.loads(cp.stdout).get("data") or [])
|
|
47
|
+
if not paths_data:
|
|
48
|
+
LOG.error("Cannot interpret the output of jupyter --paths. Abort")
|
|
49
|
+
sys.exit(1)
|
|
50
|
+
for p in paths_data:
|
|
51
|
+
dir = Path(p)
|
|
52
|
+
if dir.is_relative_to(Path.home() / ".local"):
|
|
53
|
+
dir_kernelspecs_user = dir / "kernels"
|
|
54
|
+
break
|
|
55
|
+
else:
|
|
56
|
+
LOG.error(
|
|
57
|
+
"Cannot find the user data path where Jupyter would find "
|
|
58
|
+
"user-specific kernelspecs. Abort"
|
|
59
|
+
)
|
|
60
|
+
sys.exit(2)
|
|
61
|
+
|
|
62
|
+
path_kernelspec_resources = Path(sys.prefix) / "share" / "jupyter" / "kernels" / "python3"
|
|
63
|
+
if not path_kernelspec_resources.is_dir():
|
|
64
|
+
LOG.error(
|
|
65
|
+
"The directory we expected to find kernelspec resources in does"
|
|
66
|
+
"not exist. Abort"
|
|
67
|
+
)
|
|
68
|
+
sys.exit(3)
|
|
69
|
+
dir_kernelspec_new = dir_kernelspecs_user / args.name
|
|
70
|
+
if dir_kernelspec_new.is_dir():
|
|
71
|
+
LOG.warn(
|
|
72
|
+
f"Kernel directory {dir_kernelspec_new} already exists. "
|
|
73
|
+
"Since carrying on would overwrite it, "
|
|
74
|
+
"we err on the side of caution and abort. "
|
|
75
|
+
"Delete it yourself and retry if you would rather overwrite."
|
|
76
|
+
)
|
|
77
|
+
sys.exit(4)
|
|
78
|
+
dir_kernelspec_new.mkdir(parents=True, exist_ok=False)
|
|
79
|
+
for p in path_kernelspec_resources.iterdir():
|
|
80
|
+
shutil.copy(p, dir_kernelspec_new / p.name, follow_symlinks=True)
|
|
81
|
+
|
|
82
|
+
path_kernel_json = dir_kernelspec_new / "kernel.json"
|
|
83
|
+
if not path_kernel_json.is_file():
|
|
84
|
+
LOG.error("Copying kernelspec resources failed somehow. Abort")
|
|
85
|
+
sys.exit(5)
|
|
86
|
+
kernel_json = json.loads(path_kernel_json.read_text(encoding="utf-8"))
|
|
87
|
+
kernel_json["argv"] = [
|
|
88
|
+
str(path_uv),
|
|
89
|
+
"run",
|
|
90
|
+
"--with",
|
|
91
|
+
"uvk",
|
|
92
|
+
"--isolated",
|
|
93
|
+
"--no-cache",
|
|
94
|
+
"python",
|
|
95
|
+
"-m",
|
|
96
|
+
"ipykernel_launcher",
|
|
97
|
+
"-f",
|
|
98
|
+
"{connection_file}",
|
|
99
|
+
]
|
|
100
|
+
kernel_json["display_name"] = args.display_name
|
|
101
|
+
if args.tmp:
|
|
102
|
+
kernel_json["env"] = {"TMPDIR": args.tmp}
|
|
103
|
+
path_kernel_json.write_text(json.dumps(kernel_json, indent=2), encoding="utf-8")
|
|
104
|
+
LOG.info(f"UVK kernelspec added at {dir_kernelspec_new}")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
main()
|