Skip to content

Commit 59215b3

Browse files
committed
Merge remote-tracking branch 'refs/remotes/origin/development' into development
2 parents 6c18c62 + a4ad2c6 commit 59215b3

9 files changed

Lines changed: 330 additions & 551 deletions

File tree

.github/dependabot.yml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1-
ignore:
2-
- dependency-name: "*"
3-
update-types: ["version-update:semver-patch", "version-update:semver-minor", "version-update:semver-major"]
1+
version: 2
2+
updates:
3+
- package-ecosystem: "pip"
4+
directory: "/"
5+
schedule:
6+
interval: "monthly"
7+
ignore:
8+
- dependency-name: "*"
9+
- package-ecosystem: "pip"
10+
directory: "/docker"
11+
schedule:
12+
interval: "monthly"
13+
ignore:
14+
- dependency-name: "*"

.github/workflows/publish2.yml

Lines changed: 146 additions & 398 deletions
Large diffs are not rendered by default.

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ classifiers = [
3939
]
4040

4141
dependencies = [
42-
"uv_ffi ; python_version >= '3.8' and python_version <= '3.14'",
42+
"uv_ffi>=0.10.8.post2 ; python_version >= '3.8' and python_version <= '3.14'",
4343
"requests>=2.20",
4444
"psutil>=5.9.0",
4545
"typer>=0.4.0",
@@ -82,7 +82,7 @@ dependencies = [
8282
# Note: Python 3.7-3.8 have known CVEs due to upstream EOL. See SECURITY.md
8383
"aiohttp>=3.8.6,<3.9.0; python_version == '3.7'", # Last Py3.7 version (20 known CVEs)
8484
"aiohttp>=3.10.11,<3.11.0; python_version == '3.8'", # Last Py3.8 version (9 known CVEs)
85-
"aiohttp>=3.13.3; python_version >= '3.9'", # Latest with all patches
85+
"aiohttp>=3.13.5; python_version >= '3.9'", # Latest with all patches
8686
#aiohttp-lts on to-do list for pythons 3.7 and 3.8
8787

8888
# Security scanning

requirements-trace.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#
77
aiohappyeyeballs==2.6.1
88
# via aiohttp
9-
aiohttp==3.13.3 ; python_version >= "3.9"
9+
aiohttp==3.13.5 ; python_version >= "3.9"
1010
# via omnipkg (pyproject.toml)
1111
aiosignal==1.4.0
1212
# via aiohttp
@@ -108,7 +108,7 @@ pygments==2.19.2
108108
# via rich
109109
pyparsing==3.3.2
110110
# via pip-requirements-parser
111-
requests==2.32.5
111+
requests==2.33.1
112112
# via
113113
# cachecontrol
114114
# omnipkg (pyproject.toml)
@@ -138,6 +138,8 @@ urllib3==2.6.3 ; python_version >= "3.10"
138138
# requests
139139
uv==0.10.11 ; python_version >= "3.8"
140140
# via omnipkg (pyproject.toml)
141+
uv-ffi==0.10.8.post2 ; python_version >= "3.8" and python_version <= "3.14"
142+
# via omnipkg (pyproject.toml)
141143
werkzeug==3.1.6 ; python_version >= "3.9"
142144
# via
143145
# flask

requirements.txt

Lines changed: 20 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,149 +1,54 @@
1-
#
2-
# This file is autogenerated by pip-compile with Python 3.11
3-
# by the following command:
4-
#
5-
# pip-compile --output-file=requirements.txt pyproject.toml
6-
#
71
aiohappyeyeballs==2.6.1
8-
# via aiohttp
9-
aiohttp==3.13.3 ; python_version >= "3.9"
10-
# via omnipkg (pyproject.toml)
2+
aiohttp==3.13.5 ; python_version >= "3.9"
113
aiosignal==1.4.0
12-
# via aiohttp
4+
annotated-doc==0.0.4
135
attrs==25.4.0
14-
# via aiohttp
15-
authlib==1.6.6 ; python_version >= "3.9"
16-
# via omnipkg (pyproject.toml)
6+
authlib==1.6.9 ; python_version >= "3.9"
177
blinker==1.9.0
18-
# via flask
198
boolean-py==5.0
20-
# via license-expression
219
cachecontrol[filecache]==0.14.4
22-
# via
23-
# cachecontrol
24-
# pip-audit
25-
certifi==2026.1.4
26-
# via requests
10+
certifi==2026.2.25
2711
cffi==2.0.0
28-
# via cryptography
29-
charset-normalizer==3.4.4
30-
# via requests
12+
charset-normalizer==3.4.6
3113
click==8.3.1
32-
# via
33-
# flask
34-
# typer
3514
cryptography==46.0.5
36-
# via authlib
37-
cyclonedx-python-lib==11.6.0
38-
# via pip-audit
15+
cyclonedx-python-lib==11.7.0
3916
defusedxml==0.7.1
40-
# via py-serializable
41-
filelock==3.24.3 ; python_version >= "3.10"
42-
# via
43-
# cachecontrol
44-
# omnipkg (pyproject.toml)
45-
flask==3.1.3 ; python_version >= "3.8"
46-
# via
47-
# flask-cors
48-
# omnipkg (pyproject.toml)
17+
filelock==3.25.2 ; python_version >= "3.10"
18+
flask==3.1.3 ; python_version >= "3.9"
4919
flask-cors==6.0.2 ; python_version >= "3.8"
50-
# via omnipkg (pyproject.toml)
5120
frozenlist==1.8.0
52-
# via
53-
# aiohttp
54-
# aiosignal
5521
idna==3.11
56-
# via
57-
# requests
58-
# yarl
5922
itsdangerous==2.2.0
60-
# via flask
6123
jinja2==3.1.6
62-
# via flask
6324
license-expression==30.4.4
64-
# via cyclonedx-python-lib
6525
markdown-it-py==4.0.0
66-
# via rich
6726
markupsafe==3.0.3
68-
# via
69-
# flask
70-
# jinja2
71-
# werkzeug
7227
mdurl==0.1.2
73-
# via markdown-it-py
7428
msgpack==1.1.2
75-
# via cachecontrol
76-
multidict==6.7.0
77-
# via
78-
# aiohttp
79-
# yarl
29+
multidict==6.7.1
8030
packageurl-python==0.17.6
81-
# via cyclonedx-python-lib
82-
packaging==25.0 ; python_version >= "3.10"
83-
# via
84-
# omnipkg (pyproject.toml)
85-
# pip-audit
86-
# pip-requirements-parser
31+
packaging==26.0 ; python_version >= "3.10"
8732
pip-api==0.0.34
88-
# via pip-audit
8933
pip-audit==2.10.0
90-
# via omnipkg (pyproject.toml)
9134
pip-requirements-parser==32.0.1
92-
# via pip-audit
93-
platformdirs==4.9.2
94-
# via pip-audit
35+
platformdirs==4.9.4
9536
propcache==0.4.1
96-
# via
97-
# aiohttp
98-
# yarl
99-
psutil==7.2.1
100-
# via omnipkg (pyproject.toml)
37+
psutil==7.2.2
10138
py-serializable==2.1.0
102-
# via cyclonedx-python-lib
103-
pycparser==2.23
104-
# via cffi
39+
pycparser==3.0
10540
pygments==2.19.2
106-
# via rich
10741
pyparsing==3.3.2
108-
# via pip-requirements-parser
109-
requests==2.32.5
110-
# via
111-
# cachecontrol
112-
# omnipkg (pyproject.toml)
113-
# pip-audit
114-
rich==14.2.0
115-
# via
116-
# omnipkg (pyproject.toml)
117-
# pip-audit
118-
# typer
42+
requests==2.33.1
43+
rich==14.3.3
11944
shellingham==1.5.4
120-
# via typer
12145
sortedcontainers==2.4.0
122-
# via cyclonedx-python-lib
12346
tomli==2.4.0
124-
# via pip-audit
12547
tomli-w==1.2.0
126-
# via pip-audit
127-
typer==0.21.1
128-
# via omnipkg (pyproject.toml)
48+
typer==0.24.1
12949
typing-extensions==4.15.0
130-
# via
131-
# aiosignal
132-
# cyclonedx-python-lib
133-
# typer
13450
urllib3==2.6.3 ; python_version >= "3.10"
135-
# via
136-
# omnipkg (pyproject.toml)
137-
# requests
138-
uv==0.9.22 ; python_version >= "3.8"
139-
# via omnipkg (pyproject.toml)
140-
werkzeug==3.1.6 ; python_version >= "3.8"
141-
# via
142-
# flask
143-
# flask-cors
144-
# omnipkg (pyproject.toml)
145-
yarl==1.22.0
146-
# via aiohttp
147-
148-
# The following packages are considered to be unsafe in a requirements file:
149-
# pip
51+
uv==0.10.11 ; python_version >= "3.8"
52+
uv-ffi==0.10.8.post2 ; python_version >= "3.8" and python_version <= "3.14"
53+
werkzeug==3.1.6 ; python_version >= "3.9"
54+
yarl==1.23.0

src/omnipkg/core.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6779,6 +6779,19 @@ def sync_interpreter(py_ver, target_exe):
67796779
easy_pth.unlink()
67806780
except Exception:
67816781
pass
6782+
# === UV_FFI VERSION HEAL ===
6783+
uv_ffi_min = "0.10.8.post2"
6784+
uv_bin = str(Path(target_exe).parent / "uv")
6785+
chk = subprocess.run([uv_bin, "pip", "show", "--python", target_exe, "uv_ffi"], capture_output=True, text=True, timeout=15)
6786+
needs_upgrade = True
6787+
if chk.returncode == 0:
6788+
for _line in chk.stdout.splitlines():
6789+
if _line.startswith("Version:"):
6790+
from packaging.version import Version
6791+
if Version(_line.split(":",1)[1].strip()) >= Version(uv_ffi_min):
6792+
needs_upgrade = False
6793+
if needs_upgrade:
6794+
subprocess.run([uv_bin, "pip", "install", "--python", target_exe, f"uv_ffi>={uv_ffi_min}"], capture_output=True, text=True, timeout=30)
67826795
return (py_ver, result.returncode == 0)
67836796

67846797
with concurrent.futures.ThreadPoolExecutor(max_workers=6) as executor:

src/omnipkg/dispatcher.c

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,14 +1038,21 @@ static void ffi_stamp_path(const char *python_exe, char *out, size_t n) {
10381038
snprintf(out, n, "%s/omnipkg/ffi_ok/%lu.stamp", get_tmp_dir(), h);
10391039
}
10401040

1041-
static int ffi_stamp_exists(const char *python_exe) {
1041+
1042+
1043+
static void ffi_stamp_write(const char *python_exe) {
1044+
char dir1[MAX_PATH], dir2[MAX_PATH];
1045+
snprintf(dir1, sizeof(dir1), "%s/omnipkg", get_tmp_dir());
1046+
snprintf(dir2, sizeof(dir2), "%s/omnipkg/ffi_ok", get_tmp_dir());
1047+
mkdir_compat(dir1, 0755);
1048+
mkdir_compat(dir2, 0755);
10421049
char stamp[MAX_PATH];
10431050
ffi_stamp_path(python_exe, stamp, sizeof(stamp));
1044-
struct stat st;
1045-
return stat(stamp, &st) == 0;
1051+
FILE *f = fopen(stamp, "w");
1052+
if (f) { fputs(python_exe, f); fclose(f); }
10461053
}
10471054

1048-
static void ffi_stamp_write(const char *python_exe) {
1055+
static void ffi_stamp_write_so(const char *python_exe, const char *so_path) {
10491056
char dir1[MAX_PATH], dir2[MAX_PATH];
10501057
snprintf(dir1, sizeof(dir1), "%s/omnipkg", get_tmp_dir());
10511058
snprintf(dir2, sizeof(dir2), "%s/omnipkg/ffi_ok", get_tmp_dir());
@@ -1054,7 +1061,23 @@ static void ffi_stamp_write(const char *python_exe) {
10541061
char stamp[MAX_PATH];
10551062
ffi_stamp_path(python_exe, stamp, sizeof(stamp));
10561063
FILE *f = fopen(stamp, "w");
1057-
if (f) { fputs(python_exe, f); fclose(f); }
1064+
if (f) { fputs(so_path, f); fclose(f); }
1065+
}
1066+
1067+
static int ffi_stamp_exists(const char *python_exe) {
1068+
char stamp[MAX_PATH];
1069+
ffi_stamp_path(python_exe, stamp, sizeof(stamp));
1070+
struct stat st;
1071+
if (stat(stamp, &st) != 0) return 0;
1072+
/* read the .so path recorded in the stamp and verify it still exists */
1073+
FILE *f = fopen(stamp, "r");
1074+
if (!f) return 0;
1075+
char so_path[MAX_PATH] = {0};
1076+
fgets(so_path, sizeof(so_path), f);
1077+
fclose(f);
1078+
so_path[strcspn(so_path, "\n")] = 0;
1079+
if (so_path[0] == '/' && stat(so_path, &st) != 0) return 0;
1080+
return 1;
10581081
}
10591082

10601083
/*
@@ -1127,7 +1150,7 @@ static int ensure_uv_ffi_for_python(
11271150
}
11281151

11291152
if (debug) fprintf(stderr, "[C-DISPATCH] uv_ffi installed for %s\n", target_python);
1130-
ffi_stamp_write(target_python); /* fast path on all future calls */
1153+
/* stamp written by find_or_install_uv_ffi_so with verified .so path */
11311154

11321155
/* Tell the daemon worker to reload its FFI handle for this interpreter.
11331156
* Fire-and-forget via daemon_connect (handles tcp:// on Windows, unix:// on POSIX). */
@@ -1195,6 +1218,7 @@ static int find_or_install_uv_ffi_so(
11951218
so_path_out[so_path_n - 1] = '\0';
11961219
pclose(_pp);
11971220
if (debug) fprintf(stderr, "[C-DISPATCH] Found uv_ffi.so via Python: %s\n", so_path_out);
1221+
ffi_stamp_write_so(target_python, so_path_out);
11981222
return 1;
11991223
}
12001224
}
@@ -1209,21 +1233,32 @@ static int find_or_install_uv_ffi_so(
12091233
#ifdef _WIN32
12101234
if (!home) home = getenv("USERPROFILE");
12111235
#endif
1236+
/* derive target interpreter prefix for ABI-correct .so lookup */
1237+
char _target_prefix[MAX_PATH] = {0};
1238+
{
1239+
char _tp_cmd[MAX_PATH * 2];
1240+
snprintf(_tp_cmd, sizeof(_tp_cmd),
1241+
"\"%s\" -c \"import sys; print(sys.prefix)\"", target_python);
1242+
FILE *_tp_fp = popen(_tp_cmd, "r");
1243+
if (_tp_fp) { fgets(_target_prefix, sizeof(_target_prefix), _tp_fp); pclose(_tp_fp); }
1244+
_target_prefix[strcspn(_target_prefix, "\n")] = 0;
1245+
}
1246+
const char *_search_root = _target_prefix[0] ? _target_prefix : venv_root;
12121247
/* standard installed: uv_ffi/_native/ */
12131248
snprintf(patterns[0], MAX_PATH,
1214-
"%s/lib/python3.*/site-packages/uv_ffi/_native/*.pyd", venv_root);
1249+
"%s/lib/python3.*/site-packages/uv_ffi/_native/*.pyd", _search_root);
12151250
snprintf(patterns[1], MAX_PATH,
1216-
"%s/lib/python3.*/site-packages/uv_ffi/_native/*.so", venv_root);
1251+
"%s/lib/python3.*/site-packages/uv_ffi/_native/*.so", _search_root);
12171252
/* flat install: uv_ffi/*.pyd / *.so */
12181253
snprintf(patterns[2], MAX_PATH,
1219-
"%s/lib/python3.*/site-packages/uv_ffi/*.pyd", venv_root);
1254+
"%s/lib/python3.*/site-packages/uv_ffi/*.pyd", _search_root);
12201255
snprintf(patterns[3], MAX_PATH,
1221-
"%s/lib/python3.*/site-packages/uv_ffi/*.so", venv_root);
1256+
"%s/lib/python3.*/site-packages/uv_ffi/*.so", _search_root);
12221257
/* dev/vendor install: omnipkg/_vendor/uv_ffi/ */
12231258
snprintf(patterns[4], MAX_PATH,
1224-
"%s/lib/python3.*/site-packages/omnipkg/_vendor/uv_ffi/*.pyd", venv_root);
1259+
"%s/lib/python3.*/site-packages/omnipkg/_vendor/uv_ffi/*.pyd", _search_root);
12251260
snprintf(patterns[5], MAX_PATH,
1226-
"%s/lib/python3.*/site-packages/omnipkg/_vendor/uv_ffi/*.so", venv_root);
1261+
"%s/lib/python3.*/site-packages/omnipkg/_vendor/uv_ffi/*.so", _search_root);
12271262
/* user local */
12281263
snprintf(patterns[6], MAX_PATH,
12291264
"%s/.local/lib/python3.*/site-packages/uv_ffi/_native/*.pyd",
@@ -1239,6 +1274,7 @@ static int find_or_install_uv_ffi_so(
12391274
so_path_out[so_path_n - 1] = '\0';
12401275
globfree(&globbuf);
12411276
if (debug) fprintf(stderr, "[C-DISPATCH] Found uv_ffi.so: %s\n", so_path_out);
1277+
ffi_stamp_write_so(target_python, so_path_out);
12421278
return 1;
12431279
}
12441280
globfree(&globbuf);
@@ -1315,6 +1351,7 @@ static int find_or_install_uv_ffi_so(
13151351
so_path_out[so_path_n - 1] = '\0';
13161352
globfree(&globbuf);
13171353
if (debug) fprintf(stderr, "[C-DISPATCH] uv_ffi.so post-install: %s\n", so_path_out);
1354+
ffi_stamp_write_so(target_python, so_path_out);
13181355
return 1;
13191356
}
13201357
globfree(&globbuf);
@@ -1699,6 +1736,10 @@ int main(int argc, char **argv) {
16991736
if (is_foreign) {
17001737
ensure_uv_ffi_for_python(target_python, venv_root,
17011738
daemon_sock, debug);
1739+
/* locate and stamp the .so so future calls skip install check */
1740+
char _stamp_so[MAX_PATH];
1741+
find_or_install_uv_ffi_so(venv_root, target_python,
1742+
_stamp_so, sizeof(_stamp_so));
17021743
}
17031744
}
17041745

0 commit comments

Comments
 (0)