Skip to content

Commit 52cc789

Browse files
committed
Add version() function to vendored FlatBuffers runtime
- Update deps/flatbuffers submodule to track v25.9.23 release tag - Convert vendor-flatbuffers recipe from bash to Python for reliability - Generate _git_version.py with git describe output during vendoring - Patch __init__.py with version() function returning structured tuple - Update hatch_build.py to capture git version during pip install - Provides autobahn.flatbuffers.version() -> (major, minor, patch, commits, hash) This enables zlmdb.check_autobahn_flatbuffers_version_in_sync() to verify version compatibility between zlmdb and autobahn flatbuffers runtimes. Note: This work was completed with AI assistance (Claude Code).
1 parent 063defe commit 52cc789

3 files changed

Lines changed: 220 additions & 14 deletions

File tree

hatch_build.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
This builds the NVX (Native Vector Extensions) for WebSocket frame masking
55
and UTF-8 validation using CFFI.
66
7+
Also captures the git version of deps/flatbuffers submodule at build time.
8+
79
See: https://hatch.pypa.io/latest/plugins/build-hook/custom/
810
"""
911

1012
import os
13+
import subprocess
1114
import sys
1215
import sysconfig
1316
import importlib.util
@@ -28,6 +31,9 @@ def initialize(self, version, build_data):
2831
For wheel builds, compile the CFFI modules.
2932
For sdist builds, just ensure source files are included.
3033
"""
34+
# Always capture flatbuffers git version (for both wheel and sdist)
35+
self._update_flatbuffers_git_version()
36+
3137
if self.target_name != "wheel":
3238
# Only compile for wheel builds, sdist just includes source
3339
return
@@ -128,3 +134,127 @@ def _build_cffi_modules(self, build_data):
128134
traceback.print_exc()
129135

130136
return built_any
137+
138+
def _update_flatbuffers_git_version(self):
139+
"""
140+
Capture the git describe version of deps/flatbuffers submodule.
141+
142+
This writes the version to flatbuffers/_git_version.py and patches
143+
__init__.py with the version() function so that
144+
autobahn.flatbuffers.version() returns the exact git version at runtime.
145+
"""
146+
print("=" * 70)
147+
print("Capturing FlatBuffers git version from deps/flatbuffers")
148+
print("=" * 70)
149+
150+
flatbuffers_dir = Path(self.root) / "deps" / "flatbuffers"
151+
flatbuffers_dst = Path(self.root) / "src" / "autobahn" / "flatbuffers"
152+
git_version_file = flatbuffers_dst / "_git_version.py"
153+
init_file = flatbuffers_dst / "__init__.py"
154+
155+
# Check if flatbuffers directory exists (vendored)
156+
if not flatbuffers_dst.exists():
157+
print(" -> src/autobahn/flatbuffers not found, skipping")
158+
print(" -> Run 'just vendor-flatbuffers' first")
159+
return
160+
161+
# Default version if git is not available or submodule not initialized
162+
git_version = "unknown"
163+
164+
if flatbuffers_dir.exists() and (flatbuffers_dir / ".git").exists():
165+
try:
166+
result = subprocess.run(
167+
["git", "describe", "--tags", "--always"],
168+
cwd=flatbuffers_dir,
169+
capture_output=True,
170+
text=True,
171+
timeout=10,
172+
)
173+
if result.returncode == 0:
174+
git_version = result.stdout.strip()
175+
print(f" -> Git version: {git_version}")
176+
else:
177+
print(f" -> git describe failed: {result.stderr}")
178+
except FileNotFoundError:
179+
print(" -> git command not found, using existing version")
180+
return
181+
except subprocess.TimeoutExpired:
182+
print(" -> git describe timed out, using existing version")
183+
return
184+
except Exception as e:
185+
print(f" -> Error getting git version: {e}")
186+
return
187+
else:
188+
print(" -> deps/flatbuffers not found or not a git repo")
189+
if git_version_file.exists():
190+
print(f" -> Using existing version in {git_version_file.name}")
191+
return
192+
193+
# Write the _git_version.py file
194+
git_version_content = '''\
195+
# Copyright 2014 Google Inc. All rights reserved.
196+
#
197+
# Licensed under the Apache License, Version 2.0 (the "License");
198+
# you may not use this file except in compliance with the License.
199+
# You may obtain a copy of the License at
200+
#
201+
# http://www.apache.org/licenses/LICENSE-2.0
202+
#
203+
# Unless required by applicable law or agreed to in writing, software
204+
# distributed under the License is distributed on an "AS IS" BASIS,
205+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
206+
# See the License for the specific language governing permissions and
207+
# limitations under the License.
208+
209+
# Git version from deps/flatbuffers submodule.
210+
# This file is regenerated at build time by hatch_build.py.
211+
# The version is captured via `git describe --tags` in the submodule.
212+
#
213+
# Format: "v25.9.23" (tagged release) or "v25.9.23-2-g95053e6a" (post-tag)
214+
#
215+
# If building from sdist without git, this will retain the version
216+
# from when the sdist was created.
217+
218+
__git_version__ = "{version}"
219+
'''.format(version=git_version)
220+
221+
git_version_file.write_text(git_version_content)
222+
print(f" -> Updated {git_version_file.name}")
223+
224+
# Check if __init__.py already has version() function
225+
init_content = init_file.read_text()
226+
if "def version()" not in init_content:
227+
# Patch __init__.py to add version() function
228+
version_func = '''
229+
import re
230+
231+
from ._git_version import __git_version__
232+
233+
234+
def version() -> tuple[int, int, int, int | None, str | None]:
235+
"""
236+
Return the exact git version of the vendored FlatBuffers runtime.
237+
238+
Handles:
239+
240+
1. "v25.9.23" -> (25, 9, 23, None, None) # Release (Named Tag, CalVer Year.Month.Day)
241+
2. "v25.9.23-71" -> (25, 9, 23, 71, None) # 71 commits ahead of the Release v25.9.23
242+
3. "v25.9.23-71-g19b2300f" -> (25, 9, 23, 71, "19b2300f") # dito, with Git commit hash
243+
"""
244+
245+
pattern = r"^v(\\d+)\\.(\\d+)\\.(\\d+)(?:-(\\d+))?(?:-g([0-9a-f]+))?$"
246+
247+
match = re.match(pattern, __git_version__)
248+
249+
if match:
250+
major, minor, patch, commits, commit_hash = match.groups()
251+
commits_int = int(commits) if commits else None
252+
return (int(major), int(minor), int(patch), commits_int, commit_hash)
253+
254+
# Fallback if regex fails entirely
255+
return (0, 0, 0, None, None)
256+
'''
257+
init_file.write_text(init_content + version_func)
258+
print(f" -> Patched {init_file.name} with version() function")
259+
else:
260+
print(f" -> {init_file.name} already has version() function")

justfile

Lines changed: 89 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,21 +1271,97 @@ docs-spelling venv="": (install-docs venv)
12711271
# This avoids conflicts with the standalone 'flatbuffers' PyPI package.
12721272
# The vendored copy is gitignored and must be regenerated before build.
12731273
vendor-flatbuffers:
1274-
#!/usr/bin/env bash
1275-
set -e
1276-
SRC_DIR="deps/flatbuffers/python/flatbuffers"
1277-
DST_DIR="src/autobahn/flatbuffers"
1274+
#!/usr/bin/env python3
1275+
import os
1276+
import shutil
1277+
import subprocess
1278+
from pathlib import Path
1279+
1280+
src_dir = Path("deps/flatbuffers/python/flatbuffers")
1281+
dst_dir = Path("src/autobahn/flatbuffers")
1282+
1283+
if not src_dir.exists():
1284+
print("ERROR: Flatbuffers submodule not found at", src_dir)
1285+
print("Run: git submodule update --init --recursive")
1286+
raise SystemExit(1)
1287+
1288+
print(f"==> Vendoring flatbuffers from {src_dir} to {dst_dir}...")
1289+
if dst_dir.exists():
1290+
shutil.rmtree(dst_dir)
1291+
shutil.copytree(src_dir, dst_dir)
1292+
1293+
# Capture git version from submodule
1294+
try:
1295+
result = subprocess.run(
1296+
["git", "describe", "--tags", "--always"],
1297+
cwd="deps/flatbuffers",
1298+
capture_output=True,
1299+
text=True,
1300+
timeout=10
1301+
)
1302+
git_version = result.stdout.strip() if result.returncode == 0 else "unknown"
1303+
except Exception:
1304+
git_version = "unknown"
1305+
1306+
print(f"==> FlatBuffers git version: {git_version}")
1307+
1308+
# Generate _git_version.py
1309+
git_version_content = f'''\
1310+
# Copyright 2014 Google Inc. All rights reserved.
1311+
#
1312+
# Licensed under the Apache License, Version 2.0 (the "License");
1313+
# you may not use this file except in compliance with the License.
1314+
# You may obtain a copy of the License at
1315+
#
1316+
# http://www.apache.org/licenses/LICENSE-2.0
1317+
#
1318+
# Unless required by applicable law or agreed to in writing, software
1319+
# distributed under the License is distributed on an "AS IS" BASIS,
1320+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1321+
# See the License for the specific language governing permissions and
1322+
# limitations under the License.
1323+
1324+
# Git version from deps/flatbuffers submodule.
1325+
# This file is generated at build time by justfile vendor-flatbuffers recipe.
1326+
# The version is captured via `git describe --tags` in the submodule.
1327+
#
1328+
# Format: "v25.9.23" (tagged release) or "v25.9.23-2-g95053e6a" (post-tag)
12781329
1279-
if [ ! -d "${SRC_DIR}" ]; then
1280-
echo "ERROR: Flatbuffers submodule not found at ${SRC_DIR}"
1281-
echo "Run: git submodule update --init --recursive"
1282-
exit 1
1283-
fi
1330+
__git_version__ = "{git_version}"
1331+
'''
1332+
(dst_dir / "_git_version.py").write_text(git_version_content)
1333+
1334+
# Append version() function to __init__.py
1335+
init_file = dst_dir / "__init__.py"
1336+
init_content = init_file.read_text()
1337+
1338+
version_func = '''
1339+
import re
1340+
1341+
from ._git_version import __git_version__
1342+
1343+
1344+
def version() -> tuple[int, int, int, int | None, str | None]:
1345+
"""
1346+
Return the exact git version of the vendored FlatBuffers runtime.
1347+
1348+
Handles:
1349+
1350+
1. "v25.9.23" -> (25, 9, 23, None, None) # Release
1351+
2. "v25.9.23-71" -> (25, 9, 23, 71, None) # 71 commits ahead
1352+
3. "v25.9.23-71-g19b2300f" -> (25, 9, 23, 71, "19b2300f") # with hash
1353+
"""
1354+
pattern = r"^v(\\d+)\\.(\\d+)\\.(\\d+)(?:-(\\d+))?(?:-g([0-9a-f]+))?$"
1355+
match = re.match(pattern, __git_version__)
1356+
if match:
1357+
major, minor, patch, commits, commit_hash = match.groups()
1358+
commits_int = int(commits) if commits else None
1359+
return (int(major), int(minor), int(patch), commits_int, commit_hash)
1360+
return (0, 0, 0, None, None)
1361+
'''
1362+
init_file.write_text(init_content + version_func)
12841363

1285-
echo "==> Vendoring flatbuffers from ${SRC_DIR} to ${DST_DIR}..."
1286-
rm -rf "${DST_DIR}"
1287-
cp -r "${SRC_DIR}" "${DST_DIR}"
1288-
echo "==> Flatbuffers vendored successfully."
1364+
print("==> Flatbuffers vendored successfully with version() function.")
12891365

12901366
# Build wheel only (usage: `just build cpy314`)
12911367
build venv="": vendor-flatbuffers (install-build-tools venv)

0 commit comments

Comments
 (0)