You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@burr.apache.org by je...@apache.org on 2026/07/26 18:52:51 UTC
(burr) 01/01: Populate dependecies in redirect from main pyproject.toml
This is an automated email from the ASF dual-hosted git repository.
jernejfrank pushed a commit to branch fix/add-langfuse-to-redirect
in repository https://gitbox.apache.org/repos/asf/burr.git
commit 016dd79957fc439cc0721fa463be66c02aca7236
Author: jernejfrank <je...@gmail.com>
AuthorDate: Sun Jul 26 19:52:38 2026 +0100
Populate dependecies in redirect from main pyproject.toml
---
burr-redirect/build.sh | 12 ++-
burr-redirect/generate_pyproject.py | 144 ++++++++++++++++++++++++++++++++++
burr-redirect/pyproject.toml.template | 40 +++-------
pyproject.toml | 1 +
4 files changed, 166 insertions(+), 31 deletions(-)
diff --git a/burr-redirect/build.sh b/burr-redirect/build.sh
index 8f078dbb..d805d61a 100755
--- a/burr-redirect/build.sh
+++ b/burr-redirect/build.sh
@@ -33,10 +33,18 @@ fi
VERSION="$1"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+# Requires an activated virtualenv with the release tooling installed:
+# pip install -e ".[developer]" (provides build and twine)
+if [ -z "${VIRTUAL_ENV:-}" ]; then
+ echo "ERROR: no active Python virtualenv detected. Activate the release venv first." >&2
+ exit 1
+fi
+
echo "Building burr redirect package for version ${VERSION}..."
-# Stamp version into pyproject.toml from template
-sed "s/VERSION/${VERSION}/g" "${SCRIPT_DIR}/pyproject.toml.template" > "${SCRIPT_DIR}/pyproject.toml"
+# Generate pyproject.toml from the template, stamping in the version and the
+# extras declared in the repository root pyproject.toml
+python "${SCRIPT_DIR}/generate_pyproject.py" "${VERSION}"
# Clean previous build
rm -rf "${SCRIPT_DIR}/dist" "${SCRIPT_DIR}/build" "${SCRIPT_DIR}"/*.egg-info
diff --git a/burr-redirect/generate_pyproject.py b/burr-redirect/generate_pyproject.py
new file mode 100644
index 00000000..23ac6023
--- /dev/null
+++ b/burr-redirect/generate_pyproject.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python3
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+"""
+Generate burr-redirect/pyproject.toml from the template and the root pyproject.
+
+The redirect package (PyPI name ``burr``) exists only to point users at
+``apache-burr``. Every extra that ``apache-burr`` declares must also exist on
+the redirect package, otherwise ``pip install burr[langfuse]`` warns about an
+unknown extra and silently installs nothing. Hand-mirroring the extras drifted
+in the past, so the ``[project.optional-dependencies]`` section is generated
+here from the extras declared in the repository root ``pyproject.toml``.
+
+Usage:
+ python generate_pyproject.py <version>
+ python generate_pyproject.py 0.42.0
+"""
+
+import sys
+from pathlib import Path
+
+try:
+ import tomllib
+except ImportError: # Python < 3.11
+ import tomli as tomllib
+
+PLACEHOLDER = "#OPTIONAL_DEPENDENCIES#"
+
+SCRIPT_DIR = Path(__file__).resolve().parent
+REPO_ROOT = SCRIPT_DIR.parent
+ROOT_PYPROJECT = REPO_ROOT / "pyproject.toml"
+TEMPLATE = SCRIPT_DIR / "pyproject.toml.template"
+OUTPUT = SCRIPT_DIR / "pyproject.toml"
+
+
+def fail(message: str) -> None:
+ """Print an error and exit nonzero."""
+ print(f"ERROR: {message}", file=sys.stderr)
+ sys.exit(1)
+
+
+def load_toml(path: Path) -> dict:
+ """Parse a TOML file, failing with a clear message if it is unreadable."""
+ try:
+ with open(path, "rb") as f:
+ return tomllib.load(f)
+ except FileNotFoundError:
+ fail(f"{path} not found")
+ except tomllib.TOMLDecodeError as e:
+ fail(f"{path} is not valid TOML: {e}")
+
+
+def read_root_extras() -> list:
+ """Return the alphabetically sorted extra names declared by apache-burr."""
+ extras = load_toml(ROOT_PYPROJECT).get("project", {}).get("optional-dependencies")
+ if not extras:
+ fail(f"no [project.optional-dependencies] found in {ROOT_PYPROJECT}")
+ return sorted(extras)
+
+
+def render_extras(extras: list, version: str) -> str:
+ """Render the [project.optional-dependencies] section for the redirect package."""
+ lines = ["[project.optional-dependencies]"]
+ lines += [f'{name} = ["apache-burr[{name}]=={version}"]' for name in extras]
+ return "\n".join(lines)
+
+
+def render_template(extras: list, version: str) -> str:
+ """Substitute the extras section and the version into the template text."""
+ template = TEMPLATE.read_text()
+ matches = [line for line in template.splitlines() if PLACEHOLDER in line]
+ if not matches:
+ fail(
+ f"placeholder {PLACEHOLDER} not found in {TEMPLATE}. "
+ "The template must contain that line so the generated "
+ "[project.optional-dependencies] section has somewhere to go."
+ )
+ rendered = "\n".join(
+ render_extras(extras, version) if PLACEHOLDER in line else line
+ for line in template.splitlines()
+ )
+ if not rendered.endswith("\n"):
+ rendered += "\n"
+ return rendered.replace("VERSION", version)
+
+
+def verify(extras: list, version: str) -> None:
+ """Re-parse the written file and assert it says exactly what we intended."""
+ project = load_toml(OUTPUT).get("project", {})
+
+ written_extras = project.get("optional-dependencies", {})
+ if sorted(written_extras) != extras:
+ missing = sorted(set(extras) - set(written_extras))
+ unexpected = sorted(set(written_extras) - set(extras))
+ fail(
+ f"{OUTPUT} extras do not match {ROOT_PYPROJECT}: "
+ f"missing={missing} unexpected={unexpected}"
+ )
+
+ for name in extras:
+ expected = [f"apache-burr[{name}]=={version}"]
+ if written_extras[name] != expected:
+ fail(f"{OUTPUT} extra {name!r} is {written_extras[name]!r}, expected {expected!r}")
+
+ expected_deps = [f"apache-burr=={version}"]
+ if project.get("dependencies") != expected_deps:
+ fail(
+ f"{OUTPUT} dependencies are {project.get('dependencies')!r}, expected {expected_deps!r}"
+ )
+
+ if project.get("version") != version:
+ fail(f"{OUTPUT} version is {project.get('version')!r}, expected {version!r}")
+
+
+def main(argv: list) -> None:
+ if len(argv) != 2:
+ print(f"Usage: {Path(argv[0]).name} <version>", file=sys.stderr)
+ print(f"Example: {Path(argv[0]).name} 0.42.0", file=sys.stderr)
+ sys.exit(1)
+
+ version = argv[1]
+ extras = read_root_extras()
+ OUTPUT.write_text(render_template(extras, version))
+ verify(extras, version)
+ print(f"Wrote {OUTPUT} for version {version} with {len(extras)} extras.")
+
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/burr-redirect/pyproject.toml.template b/burr-redirect/pyproject.toml.template
index cb52080b..6c101a78 100644
--- a/burr-redirect/pyproject.toml.template
+++ b/burr-redirect/pyproject.toml.template
@@ -19,6 +19,13 @@
requires = ["setuptools>=64"]
build-backend = "setuptools.build_meta"
+# This package ships no code at all -- it only declares a dependency on
+# apache-burr. Without this, setuptools flat-layout auto-discovery would pick up
+# generate_pyproject.py sitting next to this file and install it as a top-level
+# module into every user's site-packages.
+[tool.setuptools]
+py-modules = []
+
[project]
name = "burr"
version = "VERSION"
@@ -28,35 +35,10 @@ requires-python = ">=3.9"
license = "Apache-2.0"
dependencies = ["apache-burr==VERSION"]
-[project.optional-dependencies]
-aiosqlite = ["apache-burr[aiosqlite]==VERSION"]
-asyncpg = ["apache-burr[asyncpg]==VERSION"]
-bedrock = ["apache-burr[bedrock]==VERSION"]
-cli = ["apache-burr[cli]==VERSION"]
-developer = ["apache-burr[developer]==VERSION"]
-documentation = ["apache-burr[documentation]==VERSION"]
-examples = ["apache-burr[examples]==VERSION"]
-graphviz = ["apache-burr[graphviz]==VERSION"]
-hamilton = ["apache-burr[hamilton]==VERSION"]
-haystack = ["apache-burr[haystack]==VERSION"]
-inappexamples = ["apache-burr[inappexamples]==VERSION"]
-learn = ["apache-burr[learn]==VERSION"]
-opentelemetry = ["apache-burr[opentelemetry]==VERSION"]
-postgresql = ["apache-burr[postgresql]==VERSION"]
-psycopg2 = ["apache-burr[psycopg2]==VERSION"]
-pydantic = ["apache-burr[pydantic]==VERSION"]
-pymongo = ["apache-burr[pymongo]==VERSION"]
-ray = ["apache-burr[ray]==VERSION"]
-redis = ["apache-burr[redis]==VERSION"]
-release = ["apache-burr[release]==VERSION"]
-start = ["apache-burr[start]==VERSION"]
-streamlit = ["apache-burr[streamlit]==VERSION"]
-tests = ["apache-burr[tests]==VERSION"]
-tracking = ["apache-burr[tracking]==VERSION"]
-tracking-client = ["apache-burr[tracking-client]==VERSION"]
-tracking-client-s3 = ["apache-burr[tracking-client-s3]==VERSION"]
-tracking-server = ["apache-burr[tracking-server]==VERSION"]
-tracking-server-s3 = ["apache-burr[tracking-server-s3]==VERSION"]
+# The optional-dependencies section is generated by generate_pyproject.py from
+# the extras declared in the repository root pyproject.toml, so the redirect
+# package never drifts out of sync with apache-burr. Do not list extras here.
+#OPTIONAL_DEPENDENCIES#
[project.urls]
Homepage = "https://burr.apache.org/"
diff --git a/pyproject.toml b/pyproject.toml
index 6a17ee98..87d1087f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -216,6 +216,7 @@ developer = [
"build",
"twine",
"pre-commit",
+ "tomli; python_version < '3.11'",
]
opentelemetry = [