easybind 0.2.5__tar.gz → 0.2.7__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.
- {easybind-0.2.5 → easybind-0.2.7}/PKG-INFO +5 -2
- {easybind-0.2.5 → easybind-0.2.7}/README.md +4 -1
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/devtools/__init__.py +2 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/devtools/pin_pyproject.py +103 -6
- {easybind-0.2.5 → easybind-0.2.7}/.clangd +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/.github/workflows/publish.yml +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/.gitignore +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/.vscode/tasks.json +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/CMakeLists.txt +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/LICENSE +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/NOTICE +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/RELEASING.md +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/cmake/easybind_dependencies.cmake +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/cmake/easybind_pip.cmake +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/pyproject.toml +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/pyrightconfig.json +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/scripts/clangd-update.sh +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/scripts/release_tag.py +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/scripts/wait_pypi_release.py +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/__init__.cpp +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/bind.hpp +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/devtools/release_helpers.py +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/devtools/release_tag.py +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/devtools/wait_pypi.py +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/export.hpp +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/module/__init__.cpp +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/module/node.cpp +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/module/node.hpp +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/module/node__init__.cpp +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/module/ns_module.hpp +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/py.typed +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/sample/__init__.cpp +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/sample/sample.cpp +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/sample/sample.hpp +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/sample/test1.py +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/src/easybind/sample/test2/__init__.py +0 -0
- {easybind-0.2.5 → easybind-0.2.7}/tests/test_sample.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: easybind
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: Self-registering nanobind helpers (import as easybind)
|
|
5
5
|
Keywords: easybind,nanobind,bindings,pymergetic
|
|
6
6
|
Author-Email: PymergeticOS Maintainers <raudzus@pymergetic.com>
|
|
@@ -90,17 +90,20 @@ Use **`easybind.devtools`** or the **`easybind-pin-pyproject`** CLI to rewrite e
|
|
|
90
90
|
- **cppdantic** pinning **easybind** in several tables.
|
|
91
91
|
- A future project pinning **cppdantic** the same way: pass **`--distribution cppdantic`**.
|
|
92
92
|
|
|
93
|
-
**CLI** (defaults: **`distribution=easybind`**, version = latest on PyPI
|
|
93
|
+
**CLI** (defaults: **`distribution=easybind`**, version = latest on PyPI; run from the tree that contains **`pyproject.toml`**):
|
|
94
94
|
|
|
95
95
|
```bash
|
|
96
96
|
easybind-pin-pyproject --dry-run
|
|
97
97
|
easybind-pin-pyproject
|
|
98
|
+
easybind-pin-pyproject --from-github pymergetic/easybind # latest v* tag via GitHub API (PyPI may lag)
|
|
98
99
|
easybind-pin-pyproject --installed
|
|
99
100
|
easybind-pin-pyproject --version 0.2.3
|
|
100
101
|
easybind-pin-pyproject --distribution cppdantic
|
|
101
102
|
easybind-pin-pyproject --pyproject /path/to/pyproject.toml
|
|
102
103
|
```
|
|
103
104
|
|
|
105
|
+
Use **`GITHUB_TOKEN`** for private GitHub repos or higher API rate limits.
|
|
106
|
+
|
|
104
107
|
**Other devtools CLIs** (after **`pip install`** / **`uv pip install -e .`**, or run **`scripts/…`** shims from a git checkout):
|
|
105
108
|
|
|
106
109
|
```bash
|
|
@@ -61,17 +61,20 @@ Use **`easybind.devtools`** or the **`easybind-pin-pyproject`** CLI to rewrite e
|
|
|
61
61
|
- **cppdantic** pinning **easybind** in several tables.
|
|
62
62
|
- A future project pinning **cppdantic** the same way: pass **`--distribution cppdantic`**.
|
|
63
63
|
|
|
64
|
-
**CLI** (defaults: **`distribution=easybind`**, version = latest on PyPI
|
|
64
|
+
**CLI** (defaults: **`distribution=easybind`**, version = latest on PyPI; run from the tree that contains **`pyproject.toml`**):
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
67
|
easybind-pin-pyproject --dry-run
|
|
68
68
|
easybind-pin-pyproject
|
|
69
|
+
easybind-pin-pyproject --from-github pymergetic/easybind # latest v* tag via GitHub API (PyPI may lag)
|
|
69
70
|
easybind-pin-pyproject --installed
|
|
70
71
|
easybind-pin-pyproject --version 0.2.3
|
|
71
72
|
easybind-pin-pyproject --distribution cppdantic
|
|
72
73
|
easybind-pin-pyproject --pyproject /path/to/pyproject.toml
|
|
73
74
|
```
|
|
74
75
|
|
|
76
|
+
Use **`GITHUB_TOKEN`** for private GitHub repos or higher API rate limits.
|
|
77
|
+
|
|
75
78
|
**Other devtools CLIs** (after **`pip install`** / **`uv pip install -e .`**, or run **`scripts/…`** shims from a git checkout):
|
|
76
79
|
|
|
77
80
|
```bash
|
|
@@ -8,6 +8,7 @@ from easybind.devtools.pin_pyproject import (
|
|
|
8
8
|
compatible_pin_versions,
|
|
9
9
|
fetch_pypi_version,
|
|
10
10
|
installed_distribution_version,
|
|
11
|
+
latest_release_version_from_github,
|
|
11
12
|
pypi_release_exists,
|
|
12
13
|
single_compatible_pin_version,
|
|
13
14
|
wait_pypi_for_compatible_pin,
|
|
@@ -28,6 +29,7 @@ __all__ = [
|
|
|
28
29
|
"ensure_clean_worktree",
|
|
29
30
|
"fetch_pypi_version",
|
|
30
31
|
"installed_distribution_version",
|
|
32
|
+
"latest_release_version_from_github",
|
|
31
33
|
"latest_v_tag",
|
|
32
34
|
"next_v_tag",
|
|
33
35
|
"pypi_release_exists",
|
|
@@ -4,16 +4,20 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
6
|
import json
|
|
7
|
+
import os
|
|
7
8
|
import re
|
|
8
9
|
import sys
|
|
9
10
|
import time
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from urllib.error import HTTPError
|
|
12
|
-
from urllib.request import urlopen
|
|
13
|
+
from urllib.request import Request, urlopen
|
|
13
14
|
|
|
14
15
|
# PEP 440 version suitable for ``~=`` RHS in typical pyproject pins (numeric release).
|
|
15
16
|
_VERSION_RE = re.compile(r"^[0-9]+(?:\.[0-9]+)*$")
|
|
16
17
|
|
|
18
|
+
# Release tags: ``vMAJOR.MINOR.PATCH`` (GitHub / git tag style).
|
|
19
|
+
_V_SEMVER_TAG = re.compile(r"^v(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$")
|
|
20
|
+
|
|
17
21
|
# Reject pathological / mistaken distribution strings (full re.escape covers the rest).
|
|
18
22
|
_DIST_OK = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9._-]*$")
|
|
19
23
|
|
|
@@ -121,6 +125,76 @@ def installed_distribution_version(package: str = "easybind") -> str:
|
|
|
121
125
|
return version(package)
|
|
122
126
|
|
|
123
127
|
|
|
128
|
+
def _github_request_json(url: str, *, token: str | None, timeout_s: float) -> object:
|
|
129
|
+
req = Request(url, headers={"Accept": "application/vnd.github+json"})
|
|
130
|
+
if token:
|
|
131
|
+
req.add_header("Authorization", f"Bearer {token}")
|
|
132
|
+
with urlopen(req, timeout=timeout_s) as r:
|
|
133
|
+
return json.load(r)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def latest_release_version_from_github(
|
|
137
|
+
owner_repo: str,
|
|
138
|
+
*,
|
|
139
|
+
token: str | None = None,
|
|
140
|
+
timeout_s: float = 30.0,
|
|
141
|
+
) -> str:
|
|
142
|
+
"""Return the highest ``vMAJOR.MINOR.PATCH`` tag on GitHub as ``X.Y.Z`` (no local git clone).
|
|
143
|
+
|
|
144
|
+
Uses ``GET /repos/{owner}/{repo}/tags`` (paginated). Works when the tag exists on GitHub but
|
|
145
|
+
PyPI has not published the wheel yet. *owner_repo* is ``OWNER/REPO`` (e.g. ``pymergetic/easybind``).
|
|
146
|
+
|
|
147
|
+
Set ``GITHUB_TOKEN`` or ``GH_TOKEN`` (or pass *token*) for private repos or higher rate limits.
|
|
148
|
+
"""
|
|
149
|
+
s = owner_repo.strip().strip("/")
|
|
150
|
+
parts = s.split("/")
|
|
151
|
+
if len(parts) != 2 or not parts[0] or not parts[1]:
|
|
152
|
+
raise ValueError(f"expected OWNER/REPO, got {owner_repo!r}")
|
|
153
|
+
|
|
154
|
+
owner, repo = parts[0], parts[1]
|
|
155
|
+
tok = token if token is not None else os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN")
|
|
156
|
+
|
|
157
|
+
best: tuple[int, int, int] | None = None
|
|
158
|
+
best_ver: str | None = None
|
|
159
|
+
page = 1
|
|
160
|
+
per_page = 100
|
|
161
|
+
while page <= 100:
|
|
162
|
+
url = f"https://api.github.com/repos/{owner}/{repo}/tags?per_page={per_page}&page={page}"
|
|
163
|
+
try:
|
|
164
|
+
data = _github_request_json(url, token=tok, timeout_s=timeout_s)
|
|
165
|
+
except HTTPError as e:
|
|
166
|
+
detail = ""
|
|
167
|
+
try:
|
|
168
|
+
detail = e.read().decode("utf-8", errors="replace")[:500]
|
|
169
|
+
except Exception:
|
|
170
|
+
pass
|
|
171
|
+
raise ValueError(
|
|
172
|
+
f"GitHub API HTTP {e.code} for {owner}/{repo} (set GITHUB_TOKEN for private repos): {detail}"
|
|
173
|
+
) from e
|
|
174
|
+
if not isinstance(data, list) or len(data) == 0:
|
|
175
|
+
break
|
|
176
|
+
for item in data:
|
|
177
|
+
if not isinstance(item, dict):
|
|
178
|
+
continue
|
|
179
|
+
name = item.get("name")
|
|
180
|
+
if not isinstance(name, str):
|
|
181
|
+
continue
|
|
182
|
+
m = _V_SEMVER_TAG.fullmatch(name.strip())
|
|
183
|
+
if not m:
|
|
184
|
+
continue
|
|
185
|
+
t = (int(m.group(1)), int(m.group(2)), int(m.group(3)))
|
|
186
|
+
if best is None or t > best:
|
|
187
|
+
best = t
|
|
188
|
+
best_ver = f"{m.group(1)}.{m.group(2)}.{m.group(3)}"
|
|
189
|
+
if len(data) < per_page:
|
|
190
|
+
break
|
|
191
|
+
page += 1
|
|
192
|
+
|
|
193
|
+
if best_ver is None:
|
|
194
|
+
raise ValueError(f"no vMAJOR.MINOR.PATCH tags found for github.com/{owner}/{repo}")
|
|
195
|
+
return best_ver
|
|
196
|
+
|
|
197
|
+
|
|
124
198
|
def bump_compatible_pins(pyproject_toml: str, distribution: str, version: str) -> tuple[str, int]:
|
|
125
199
|
"""Replace each ``{distribution}~=X.Y.Z`` with ``{distribution}~={version}``.
|
|
126
200
|
|
|
@@ -181,7 +255,10 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
181
255
|
ap = argparse.ArgumentParser(
|
|
182
256
|
description=(
|
|
183
257
|
"Set every {distribution}~= pin in pyproject.toml "
|
|
184
|
-
"(default distribution: easybind
|
|
258
|
+
"(default distribution: easybind). "
|
|
259
|
+
"Default version is latest on PyPI (can lag CI). "
|
|
260
|
+
"Use --from-github OWNER/REPO for the latest vX.Y.Z tag on GitHub (no local git); "
|
|
261
|
+
"use --version when you know the number; --installed uses the env's installed wheel."
|
|
185
262
|
)
|
|
186
263
|
)
|
|
187
264
|
ap.add_argument(
|
|
@@ -201,18 +278,32 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
201
278
|
"--version",
|
|
202
279
|
metavar="X.Y.Z",
|
|
203
280
|
default=None,
|
|
204
|
-
help="pin to this
|
|
281
|
+
help="pin to this exact release (use when PyPI does not list it yet, e.g. CI still publishing)",
|
|
205
282
|
)
|
|
206
283
|
ap.add_argument(
|
|
207
284
|
"--installed",
|
|
208
285
|
action="store_true",
|
|
209
|
-
help="use
|
|
286
|
+
help="use importlib.metadata.version for --distribution in this env",
|
|
287
|
+
)
|
|
288
|
+
ap.add_argument(
|
|
289
|
+
"--from-github",
|
|
290
|
+
metavar="OWNER/REPO",
|
|
291
|
+
default=None,
|
|
292
|
+
help=(
|
|
293
|
+
"pin to highest vMAJOR.MINOR.PATCH tag from GitHub API (e.g. pymergetic/easybind). "
|
|
294
|
+
"Use when the tag exists but PyPI does not yet. Set GITHUB_TOKEN for private repos."
|
|
295
|
+
),
|
|
210
296
|
)
|
|
211
297
|
ap.add_argument("--dry-run", action="store_true", help="do not write the file")
|
|
212
298
|
ns = ap.parse_args(argv)
|
|
213
299
|
|
|
214
|
-
|
|
215
|
-
|
|
300
|
+
nsrc = sum(
|
|
301
|
+
1
|
|
302
|
+
for x in (ns.version is not None, ns.installed, ns.from_github is not None)
|
|
303
|
+
if x
|
|
304
|
+
)
|
|
305
|
+
if nsrc > 1:
|
|
306
|
+
print("error: use at most one of --version, --installed, or --from-github", file=sys.stderr)
|
|
216
307
|
return 2
|
|
217
308
|
|
|
218
309
|
pyproject = ns.pyproject if ns.pyproject is not None else Path.cwd() / "pyproject.toml"
|
|
@@ -227,6 +318,12 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
227
318
|
|
|
228
319
|
if ns.installed:
|
|
229
320
|
ver = installed_distribution_version(dist)
|
|
321
|
+
elif ns.from_github is not None:
|
|
322
|
+
try:
|
|
323
|
+
ver = latest_release_version_from_github(ns.from_github.strip())
|
|
324
|
+
except ValueError as e:
|
|
325
|
+
print(f"error: {e}", file=sys.stderr)
|
|
326
|
+
return 1
|
|
230
327
|
elif ns.version:
|
|
231
328
|
ver = ns.version.strip()
|
|
232
329
|
else:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|