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 = [