policyengine-observability 0.2.0__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.
Files changed (29) hide show
  1. policyengine_observability-0.2.0/.github/bump_version.py +134 -0
  2. policyengine_observability-0.2.0/.github/check-changelog.sh +6 -0
  3. policyengine_observability-0.2.0/.github/fetch_version.py +23 -0
  4. policyengine_observability-0.2.0/.github/get-changelog-diff.sh +9 -0
  5. policyengine_observability-0.2.0/.github/publish-git-tag.sh +12 -0
  6. policyengine_observability-0.2.0/.github/workflows/pr.yml +69 -0
  7. policyengine_observability-0.2.0/.github/workflows/push.yml +158 -0
  8. policyengine_observability-0.2.0/.gitignore +8 -0
  9. policyengine_observability-0.2.0/CHANGELOG.md +9 -0
  10. policyengine_observability-0.2.0/PKG-INFO +52 -0
  11. policyengine_observability-0.2.0/README.md +13 -0
  12. policyengine_observability-0.2.0/policyengine_observability/__init__.py +166 -0
  13. policyengine_observability-0.2.0/policyengine_observability/adapters/__init__.py +1 -0
  14. policyengine_observability-0.2.0/policyengine_observability/adapters/fastapi.py +286 -0
  15. policyengine_observability-0.2.0/policyengine_observability/adapters/flask.py +132 -0
  16. policyengine_observability-0.2.0/policyengine_observability/config.py +164 -0
  17. policyengine_observability-0.2.0/policyengine_observability/context.py +238 -0
  18. policyengine_observability-0.2.0/policyengine_observability/integrations/__init__.py +1 -0
  19. policyengine_observability-0.2.0/policyengine_observability/integrations/httpx.py +8 -0
  20. policyengine_observability-0.2.0/policyengine_observability/logging.py +17 -0
  21. policyengine_observability-0.2.0/policyengine_observability/runtime.py +1784 -0
  22. policyengine_observability-0.2.0/policyengine_observability/segments.py +35 -0
  23. policyengine_observability-0.2.0/pyproject.toml +122 -0
  24. policyengine_observability-0.2.0/tests/test_fastapi_adapter.py +334 -0
  25. policyengine_observability-0.2.0/tests/test_flask_adapter.py +170 -0
  26. policyengine_observability-0.2.0/tests/test_public_api.py +101 -0
  27. policyengine_observability-0.2.0/tests/test_release_scripts.py +76 -0
  28. policyengine_observability-0.2.0/tests/test_runtime.py +1415 -0
  29. policyengine_observability-0.2.0/uv.lock +1042 -0
@@ -0,0 +1,134 @@
1
+ """Infer semver bump from Towncrier fragment types and update version."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ import subprocess
7
+ import sys
8
+ from pathlib import Path
9
+
10
+ SEMVER_PATTERN = re.compile(r"^(\d+)\.(\d+)\.(\d+)$")
11
+
12
+
13
+ def parse_version(version: str) -> tuple[int, int, int]:
14
+ match = SEMVER_PATTERN.match(version)
15
+ if not match:
16
+ raise ValueError(f"Invalid semver: {version}")
17
+ return tuple(int(part) for part in match.groups())
18
+
19
+
20
+ def get_pyproject_version(pyproject_path: Path) -> str:
21
+ text = pyproject_path.read_text()
22
+ match = re.search(r'^version\s*=\s*"(\d+\.\d+\.\d+)"', text, re.MULTILINE)
23
+ if not match:
24
+ print("Could not find version in pyproject.toml", file=sys.stderr)
25
+ sys.exit(1)
26
+ return match.group(1)
27
+
28
+
29
+ def get_changelog_versions(changelog_path: Path) -> list[str]:
30
+ if not changelog_path.exists():
31
+ return []
32
+ return re.findall(
33
+ r"^## \[(\d+\.\d+\.\d+)\]",
34
+ changelog_path.read_text(),
35
+ re.MULTILINE,
36
+ )
37
+
38
+
39
+ def get_git_tag_versions(repo_root: Path) -> list[str]:
40
+ try:
41
+ result = subprocess.run(
42
+ ["git", "tag"],
43
+ cwd=repo_root,
44
+ capture_output=True,
45
+ text=True,
46
+ check=True,
47
+ )
48
+ except (FileNotFoundError, subprocess.CalledProcessError):
49
+ return []
50
+
51
+ versions = []
52
+ for tag in result.stdout.splitlines():
53
+ normalized = tag.removeprefix("v").strip()
54
+ if SEMVER_PATTERN.match(normalized):
55
+ versions.append(normalized)
56
+ return versions
57
+
58
+
59
+ def get_current_version(
60
+ pyproject_path: Path,
61
+ changelog_path: Path,
62
+ repo_root: Path,
63
+ ) -> str:
64
+ candidates = [get_pyproject_version(pyproject_path)]
65
+ candidates.extend(get_changelog_versions(changelog_path))
66
+ candidates.extend(get_git_tag_versions(repo_root))
67
+ return max(candidates, key=parse_version)
68
+
69
+
70
+ def infer_bump(changelog_dir: Path) -> str:
71
+ fragments = [
72
+ path
73
+ for path in changelog_dir.iterdir()
74
+ if path.is_file() and path.name != ".gitkeep"
75
+ ]
76
+ if not fragments:
77
+ print("No changelog fragments found", file=sys.stderr)
78
+ sys.exit(1)
79
+
80
+ categories = {path.suffix.lstrip(".") for path in fragments}
81
+ for path in fragments:
82
+ parts = path.stem.split(".")
83
+ if len(parts) >= 2:
84
+ categories.add(parts[-1])
85
+
86
+ if "breaking" in categories:
87
+ return "major"
88
+ if "added" in categories or "removed" in categories:
89
+ return "minor"
90
+ return "patch"
91
+
92
+
93
+ def bump_version(version: str, bump: str) -> str:
94
+ major, minor, patch = (int(part) for part in version.split("."))
95
+ if bump == "major":
96
+ return f"{major + 1}.0.0"
97
+ if bump == "minor":
98
+ return f"{major}.{minor + 1}.0"
99
+ return f"{major}.{minor}.{patch + 1}"
100
+
101
+
102
+ def update_file(path: Path, new_version: str) -> None:
103
+ text = path.read_text()
104
+ updated, replacements = re.subn(
105
+ r'(^version\s*=\s*")(\d+\.\d+\.\d+)(")',
106
+ rf"\g<1>{new_version}\g<3>",
107
+ text,
108
+ count=1,
109
+ flags=re.MULTILINE,
110
+ )
111
+ if replacements == 0:
112
+ print(f"Could not update version in {path}", file=sys.stderr)
113
+ sys.exit(1)
114
+ if updated != text:
115
+ path.write_text(updated)
116
+ print(f" Updated {path}")
117
+
118
+
119
+ def main() -> None:
120
+ root = Path(__file__).resolve().parent.parent
121
+ pyproject = root / "pyproject.toml"
122
+ changelog = root / "CHANGELOG.md"
123
+ changelog_dir = root / "changelog.d"
124
+
125
+ current = get_current_version(pyproject, changelog, root)
126
+ bump = infer_bump(changelog_dir)
127
+ new = bump_version(current, bump)
128
+
129
+ print(f"Version: {current} -> {new} ({bump})")
130
+ update_file(pyproject, new)
131
+
132
+
133
+ if __name__ == "__main__":
134
+ main()
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ BASE_REF="${1:-origin/main}"
5
+
6
+ towncrier check --compare-with "$BASE_REF"
@@ -0,0 +1,23 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ import sys
5
+ from pathlib import Path
6
+
7
+
8
+ def fetch_version(pyproject_path: Path) -> str:
9
+ text = pyproject_path.read_text()
10
+ match = re.search(r'^version\s*=\s*"(\d+\.\d+\.\d+)"', text, re.MULTILINE)
11
+ if not match:
12
+ print("Could not find version in pyproject.toml", file=sys.stderr)
13
+ sys.exit(1)
14
+ return match.group(1)
15
+
16
+
17
+ def main() -> None:
18
+ root = Path(__file__).resolve().parent.parent
19
+ print(fetch_version(root / "pyproject.toml"))
20
+
21
+
22
+ if __name__ == "__main__":
23
+ main()
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ if git describe --tags --abbrev=0 --first-parent >/dev/null 2>&1; then
5
+ LAST_TAGGED_COMMIT="$(git describe --tags --abbrev=0 --first-parent)"
6
+ git --no-pager diff "$LAST_TAGGED_COMMIT" -- CHANGELOG.md
7
+ else
8
+ git --no-pager diff HEAD -- CHANGELOG.md
9
+ fi
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ VERSION="$(python .github/fetch_version.py)"
5
+
6
+ if git rev-parse --verify --quiet "refs/tags/${VERSION}" >/dev/null; then
7
+ echo "Tag ${VERSION} already exists locally."
8
+ exit 0
9
+ fi
10
+
11
+ git tag "${VERSION}"
12
+ git push origin "${VERSION}"
@@ -0,0 +1,69 @@
1
+ name: Pull request
2
+
3
+ on:
4
+ pull_request:
5
+ paths:
6
+ - "policyengine_observability/**"
7
+ - "tests/**"
8
+ - ".github/**"
9
+ - "changelog.d/**"
10
+ - "pyproject.toml"
11
+ - "uv.lock"
12
+ workflow_dispatch:
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ check-changelog:
19
+ name: Check changelog fragment
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - name: Checkout repo
23
+ uses: actions/checkout@v6
24
+ with:
25
+ fetch-depth: 0
26
+ - name: Install uv
27
+ uses: astral-sh/setup-uv@v8.1.0
28
+ - name: Set up Python
29
+ uses: actions/setup-python@v6
30
+ with:
31
+ python-version: "3.12"
32
+ - name: Check for changelog fragment
33
+ run: uv run --extra dev bash .github/check-changelog.sh "origin/${{ github.base_ref }}"
34
+
35
+ lint:
36
+ name: Lint
37
+ runs-on: ubuntu-latest
38
+ steps:
39
+ - name: Checkout repo
40
+ uses: actions/checkout@v6
41
+ - name: Install uv
42
+ uses: astral-sh/setup-uv@v8.1.0
43
+ - name: Set up Python
44
+ uses: actions/setup-python@v6
45
+ with:
46
+ python-version: "3.12"
47
+ - name: Run ruff format check
48
+ run: uv run --extra dev ruff format --check .
49
+ - name: Run ruff check
50
+ run: uv run --extra dev ruff check .
51
+
52
+ test:
53
+ name: Test
54
+ runs-on: ubuntu-latest
55
+ steps:
56
+ - name: Checkout repo
57
+ uses: actions/checkout@v6
58
+ - name: Install uv
59
+ uses: astral-sh/setup-uv@v8.1.0
60
+ - name: Set up Python
61
+ uses: actions/setup-python@v6
62
+ with:
63
+ python-version: "3.12"
64
+ - name: Run tests with coverage
65
+ run: uv run --extra dev --extra all coverage run -m pytest
66
+ - name: Enforce coverage
67
+ run: uv run --extra dev --extra all coverage report
68
+ - name: Write coverage XML
69
+ run: uv run --extra dev --extra all coverage xml
@@ -0,0 +1,158 @@
1
+ name: Push
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ concurrency:
13
+ group: observability-release
14
+
15
+ env:
16
+ VERSION_COMMIT_MESSAGE: Update observability package version
17
+
18
+ jobs:
19
+ check-changelog:
20
+ name: Check changelog fragment
21
+ if: github.event.head_commit.message != 'Update observability package version'
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - name: Checkout repo
25
+ uses: actions/checkout@v6
26
+ with:
27
+ fetch-depth: 0
28
+ - name: Install uv
29
+ uses: astral-sh/setup-uv@v8.1.0
30
+ - name: Set up Python
31
+ uses: actions/setup-python@v6
32
+ with:
33
+ python-version: "3.12"
34
+ - name: Check for changelog fragment
35
+ env:
36
+ BASE_REF: ${{ github.event.before }}
37
+ run: |
38
+ BASE_REF="${BASE_REF:-HEAD^}"
39
+ uv run --extra dev bash .github/check-changelog.sh "$BASE_REF"
40
+
41
+ lint:
42
+ name: Lint
43
+ if: github.event.head_commit.message != 'Update observability package version'
44
+ runs-on: ubuntu-latest
45
+ steps:
46
+ - name: Checkout repo
47
+ uses: actions/checkout@v6
48
+ - name: Install uv
49
+ uses: astral-sh/setup-uv@v8.1.0
50
+ - name: Set up Python
51
+ uses: actions/setup-python@v6
52
+ with:
53
+ python-version: "3.12"
54
+ - name: Run ruff format check
55
+ run: uv run --extra dev ruff format --check .
56
+ - name: Run ruff check
57
+ run: uv run --extra dev ruff check .
58
+
59
+ test:
60
+ name: Test
61
+ if: github.event.head_commit.message != 'Update observability package version'
62
+ runs-on: ubuntu-latest
63
+ steps:
64
+ - name: Checkout repo
65
+ uses: actions/checkout@v6
66
+ - name: Install uv
67
+ uses: astral-sh/setup-uv@v8.1.0
68
+ - name: Set up Python
69
+ uses: actions/setup-python@v6
70
+ with:
71
+ python-version: "3.12"
72
+ - name: Run tests with coverage
73
+ run: uv run --extra dev --extra all coverage run -m pytest
74
+ - name: Enforce coverage
75
+ run: uv run --extra dev --extra all coverage report
76
+ - name: Write coverage XML
77
+ run: uv run --extra dev --extra all coverage xml
78
+
79
+ versioning:
80
+ name: Update versioning
81
+ needs:
82
+ - check-changelog
83
+ - lint
84
+ - test
85
+ if: github.event.head_commit.message != 'Update observability package version'
86
+ runs-on: ubuntu-latest
87
+ permissions:
88
+ contents: write
89
+ steps:
90
+ - name: Generate GitHub App token
91
+ id: app-token
92
+ uses: actions/create-github-app-token@v3
93
+ with:
94
+ app-id: ${{ secrets.APP_ID }}
95
+ private-key: ${{ secrets.APP_PRIVATE_KEY }}
96
+ - name: Checkout repo
97
+ uses: actions/checkout@v6
98
+ with:
99
+ token: ${{ steps.app-token.outputs.token }}
100
+ fetch-depth: 0
101
+ - name: Fetch tags
102
+ run: git fetch --tags --force
103
+ - name: Install uv
104
+ uses: astral-sh/setup-uv@v8.1.0
105
+ - name: Set up Python
106
+ uses: actions/setup-python@v6
107
+ with:
108
+ python-version: "3.12"
109
+ - name: Build changelog
110
+ run: |
111
+ uv run --extra dev python .github/bump_version.py
112
+ uv run --extra dev towncrier build --yes --version "$(uv run python .github/fetch_version.py)"
113
+ - name: Preview changelog update
114
+ run: bash .github/get-changelog-diff.sh
115
+ - name: Update changelog and package version
116
+ uses: EndBug/add-and-commit@v10
117
+ with:
118
+ add: "."
119
+ message: ${{ env.VERSION_COMMIT_MESSAGE }}
120
+ github_token: ${{ steps.app-token.outputs.token }}
121
+ fetch: false
122
+
123
+ publish:
124
+ name: Publish
125
+ if: github.event.head_commit.message == 'Update observability package version'
126
+ runs-on: ubuntu-latest
127
+ environment: pypi
128
+ permissions:
129
+ contents: write
130
+ id-token: write
131
+ env:
132
+ GH_TOKEN: ${{ github.token }}
133
+ steps:
134
+ - name: Checkout repo
135
+ uses: actions/checkout@v6
136
+ with:
137
+ fetch-depth: 0
138
+ - name: Install uv
139
+ uses: astral-sh/setup-uv@v8.1.0
140
+ - name: Set up Python
141
+ uses: actions/setup-python@v6
142
+ with:
143
+ python-version: "3.12"
144
+ - name: Publish git tag
145
+ run: bash .github/publish-git-tag.sh
146
+ - name: Build package
147
+ run: uv build
148
+ - name: Publish package to PyPI
149
+ uses: pypa/gh-action-pypi-publish@release/v1
150
+ with:
151
+ skip-existing: true
152
+ - name: Create GitHub release
153
+ run: |
154
+ VERSION="$(uv run python .github/fetch_version.py)"
155
+ gh release create "$VERSION" \
156
+ --title "v$VERSION" \
157
+ --notes "See CHANGELOG.md for details." \
158
+ --latest
@@ -0,0 +1,8 @@
1
+ .venv/
2
+ .pytest_cache/
3
+ .ruff_cache/
4
+ .coverage
5
+ coverage.xml
6
+ __pycache__/
7
+ *.py[cod]
8
+ .DS_Store
@@ -0,0 +1,9 @@
1
+ ## [0.2.0] - 2026-06-22
2
+
3
+ ### Added
4
+
5
+ - Add pull request and push CI/CD workflows with changelog, lint, coverage, versioning, tagging, and PyPI publishing gates.
6
+
7
+
8
+ # Changelog
9
+
@@ -0,0 +1,52 @@
1
+ Metadata-Version: 2.4
2
+ Name: policyengine-observability
3
+ Version: 0.2.0
4
+ Summary: Shared PolicyEngine observability runtime for logs, timings, metrics, and OpenTelemetry.
5
+ Author-email: PolicyEngine <hello@policyengine.org>
6
+ Requires-Python: >=3.12
7
+ Provides-Extra: all
8
+ Requires-Dist: fastapi; extra == 'all'
9
+ Requires-Dist: flask>=2.2; extra == 'all'
10
+ Requires-Dist: httpx; extra == 'all'
11
+ Requires-Dist: opentelemetry-api; extra == 'all'
12
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc; extra == 'all'
13
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http; extra == 'all'
14
+ Requires-Dist: opentelemetry-instrumentation-fastapi; extra == 'all'
15
+ Requires-Dist: opentelemetry-instrumentation-httpx; extra == 'all'
16
+ Requires-Dist: opentelemetry-sdk; extra == 'all'
17
+ Provides-Extra: dev
18
+ Requires-Dist: build; extra == 'dev'
19
+ Requires-Dist: coverage; extra == 'dev'
20
+ Requires-Dist: pytest; extra == 'dev'
21
+ Requires-Dist: ruff>=0.9.0; extra == 'dev'
22
+ Requires-Dist: towncrier>=24.8.0; extra == 'dev'
23
+ Provides-Extra: fastapi
24
+ Requires-Dist: fastapi; extra == 'fastapi'
25
+ Requires-Dist: opentelemetry-instrumentation-fastapi; extra == 'fastapi'
26
+ Provides-Extra: flask
27
+ Requires-Dist: flask>=2.2; extra == 'flask'
28
+ Provides-Extra: httpx
29
+ Requires-Dist: httpx; extra == 'httpx'
30
+ Requires-Dist: opentelemetry-instrumentation-httpx; extra == 'httpx'
31
+ Provides-Extra: otel
32
+ Requires-Dist: opentelemetry-api; extra == 'otel'
33
+ Requires-Dist: opentelemetry-sdk; extra == 'otel'
34
+ Provides-Extra: otlp-grpc
35
+ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc; extra == 'otlp-grpc'
36
+ Provides-Extra: otlp-http
37
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http; extra == 'otlp-http'
38
+ Description-Content-Type: text/markdown
39
+
40
+ # policyengine-observability
41
+
42
+ Shared PolicyEngine observability runtime for fail-open local timings,
43
+ structured logs, OpenTelemetry traces, and OpenTelemetry metrics.
44
+
45
+ The package intentionally keeps framework support in adapters:
46
+
47
+ - `policyengine_observability.adapters.flask`
48
+ - `policyengine_observability.adapters.fastapi`
49
+ - `policyengine_observability.integrations.httpx`
50
+
51
+ OpenTelemetry imports are lazy. Timing and structured logging can run without
52
+ an OTel backend; exporting traces/metrics is opt-in through configuration.
@@ -0,0 +1,13 @@
1
+ # policyengine-observability
2
+
3
+ Shared PolicyEngine observability runtime for fail-open local timings,
4
+ structured logs, OpenTelemetry traces, and OpenTelemetry metrics.
5
+
6
+ The package intentionally keeps framework support in adapters:
7
+
8
+ - `policyengine_observability.adapters.flask`
9
+ - `policyengine_observability.adapters.fastapi`
10
+ - `policyengine_observability.integrations.httpx`
11
+
12
+ OpenTelemetry imports are lazy. Timing and structured logging can run without
13
+ an OTel backend; exporting traces/metrics is opt-in through configuration.
@@ -0,0 +1,166 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+ from .config import ObservabilityConfig
6
+ from .context import OperationObservabilityContext, RequestObservabilityContext
7
+ from .runtime import (
8
+ OBSERVABILITY_INTERNAL_DISPATCH_HEADER,
9
+ REQUEST_ID_HEADER,
10
+ TRACEPARENT_HEADER,
11
+ ObservabilityRuntime,
12
+ observability_runtime,
13
+ set_observability_runtime,
14
+ )
15
+ from .segments import UNKNOWN_SEGMENT, coerce_segment_name
16
+
17
+
18
+ def current_context() -> RequestObservabilityContext | None:
19
+ return observability_runtime().current_context()
20
+
21
+
22
+ def current_operation() -> OperationObservabilityContext | None:
23
+ return observability_runtime().current_operation()
24
+
25
+
26
+ def set_attribute(key: str, value: Any) -> None:
27
+ observability_runtime().set_attribute(key, value)
28
+
29
+
30
+ def record_error(
31
+ exc: BaseException,
32
+ *,
33
+ handled: bool,
34
+ status_code: int | None = None,
35
+ include_stack: bool = True,
36
+ ) -> None:
37
+ observability_runtime().record_error(
38
+ exc,
39
+ handled=handled,
40
+ status_code=status_code,
41
+ include_stack=include_stack,
42
+ )
43
+
44
+
45
+ def record_event(event: str, **fields: Any) -> None:
46
+ observability_runtime().record_event(event, **fields)
47
+
48
+
49
+ def traceparent_header() -> str | None:
50
+ return observability_runtime().traceparent_header()
51
+
52
+
53
+ def capture_context():
54
+ return observability_runtime().capture_context()
55
+
56
+
57
+ def mark(key: str, ms: float) -> None:
58
+ observability_runtime().mark(key, ms)
59
+
60
+
61
+ def mark_ttft(key: str = "ttft_ms") -> None:
62
+ observability_runtime().mark_ttft(key)
63
+
64
+
65
+ def start_scope(
66
+ timings: dict[str, float],
67
+ *,
68
+ name: str = "operation",
69
+ parent_context: Any = None,
70
+ **attrs: Any,
71
+ ):
72
+ return observability_runtime().start_scope(
73
+ timings,
74
+ name=name,
75
+ parent_context=parent_context,
76
+ **attrs,
77
+ )
78
+
79
+
80
+ def annotate(handle=None, **attrs: Any) -> None:
81
+ observability_runtime().annotate(handle, **attrs)
82
+
83
+
84
+ def end_scope(handle, error: BaseException | None = None) -> None:
85
+ observability_runtime().end_scope(handle, error)
86
+
87
+
88
+ def instrument_fastapi(app: Any) -> None:
89
+ observability_runtime().instrument_fastapi(app)
90
+
91
+
92
+ def instrument_httpx() -> None:
93
+ observability_runtime().instrument_httpx()
94
+
95
+
96
+ def shutdown_observability() -> None:
97
+ observability_runtime().shutdown()
98
+
99
+
100
+ def shutdown_tracing() -> None:
101
+ shutdown_observability()
102
+
103
+
104
+ def operation(name: str, *, flavor: str | None = None, **attrs: Any):
105
+ return observability_runtime().operation(name, flavor=flavor, **attrs)
106
+
107
+
108
+ def entrypoint(
109
+ name: str | None = None,
110
+ *,
111
+ flavor: str | None = None,
112
+ **attrs: Any,
113
+ ):
114
+ return observability_runtime().entrypoint(
115
+ name,
116
+ flavor=flavor,
117
+ **attrs,
118
+ )
119
+
120
+
121
+ def segment(name: Any, **attrs: Any):
122
+ return observability_runtime().segment(name, **attrs)
123
+
124
+
125
+ def asegment(name: Any, **attrs: Any):
126
+ return observability_runtime().asegment(name, **attrs)
127
+
128
+
129
+ def collect_timings(name: str = "operation", **attrs: Any):
130
+ return observability_runtime().collect_timings(name, **attrs)
131
+
132
+
133
+ __all__ = [
134
+ "OBSERVABILITY_INTERNAL_DISPATCH_HEADER",
135
+ "REQUEST_ID_HEADER",
136
+ "TRACEPARENT_HEADER",
137
+ "UNKNOWN_SEGMENT",
138
+ "OperationObservabilityContext",
139
+ "ObservabilityConfig",
140
+ "ObservabilityRuntime",
141
+ "RequestObservabilityContext",
142
+ "annotate",
143
+ "asegment",
144
+ "capture_context",
145
+ "coerce_segment_name",
146
+ "collect_timings",
147
+ "current_context",
148
+ "current_operation",
149
+ "end_scope",
150
+ "entrypoint",
151
+ "instrument_fastapi",
152
+ "instrument_httpx",
153
+ "mark",
154
+ "mark_ttft",
155
+ "observability_runtime",
156
+ "operation",
157
+ "record_error",
158
+ "record_event",
159
+ "segment",
160
+ "set_attribute",
161
+ "set_observability_runtime",
162
+ "shutdown_observability",
163
+ "shutdown_tracing",
164
+ "start_scope",
165
+ "traceparent_header",
166
+ ]
@@ -0,0 +1 @@
1
+ """Framework adapters for policyengine-observability."""