src-py-lib 0.1.1__tar.gz → 0.1.2__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 (35) hide show
  1. src_py_lib-0.1.2/.github/workflows/ci.yml +16 -0
  2. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/.github/workflows/release.yml +59 -28
  3. src_py_lib-0.1.1/.github/workflows/ci.yml → src_py_lib-0.1.2/.github/workflows/validate.yml +47 -16
  4. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/AGENTS.md +11 -6
  5. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/PKG-INFO +8 -1
  6. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/README.md +7 -0
  7. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/pyproject.toml +2 -2
  8. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/__init__.py +32 -1
  9. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/clients/graphql.py +20 -3
  10. src_py_lib-0.1.2/src/src_py_lib/clients/sourcegraph.py +472 -0
  11. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/utils/http.py +69 -3
  12. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/utils/logging.py +121 -12
  13. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/tests/test_logging_http_clients.py +127 -4
  14. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/uv.lock +1 -1
  15. src_py_lib-0.1.1/src/src_py_lib/clients/sourcegraph.py +0 -127
  16. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/.gitignore +0 -0
  17. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/.markdownlint-cli2.yaml +0 -0
  18. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/.python-version +0 -0
  19. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/LICENSE +0 -0
  20. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/SECURITY.md +0 -0
  21. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/renovate.json +0 -0
  22. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/clients/__init__.py +0 -0
  23. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/clients/github.py +0 -0
  24. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/clients/google_sheets.py +0 -0
  25. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/clients/linear.py +0 -0
  26. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/clients/one_password.py +0 -0
  27. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/clients/slack.py +0 -0
  28. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/py.typed +0 -0
  29. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/utils/__init__.py +0 -0
  30. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/utils/config.py +0 -0
  31. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/utils/json_cache.py +0 -0
  32. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/utils/json_types.py +0 -0
  33. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/src/src_py_lib/utils/tsv.py +0 -0
  34. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/tests/test_import.py +0 -0
  35. {src_py_lib-0.1.1 → src_py_lib-0.1.2}/tests/test_tsv.py +0 -0
@@ -0,0 +1,16 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+
6
+ permissions:
7
+ contents: read
8
+
9
+ concurrency:
10
+ group: ci-${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ validate:
15
+ name: Validate
16
+ uses: ./.github/workflows/validate.yml
@@ -23,8 +23,15 @@ defaults:
23
23
  shell: bash
24
24
 
25
25
  jobs:
26
+ validate:
27
+ name: Validate
28
+ uses: ./.github/workflows/validate.yml
29
+ with:
30
+ ref: ${{ github.event.inputs.tag || github.ref }}
31
+
26
32
  wheel:
27
33
  name: Build wheel
34
+ needs: validate
28
35
  runs-on: ubuntu-24.04
29
36
  env:
30
37
  IMPORT_NAME: src_py_lib
@@ -45,6 +52,14 @@ jobs:
45
52
  python-version: ${{ env.PYTHON_VERSION }}
46
53
  cache: pip
47
54
 
55
+ - name: Cache uv
56
+ uses: actions/cache@v4
57
+ with:
58
+ path: ~/.cache/uv
59
+ key: uv-${{ runner.os }}-py${{ env.PYTHON_VERSION }}-${{ hashFiles('uv.lock') }}
60
+ restore-keys: |
61
+ uv-${{ runner.os }}-py${{ env.PYTHON_VERSION }}-
62
+
48
63
  - name: Install build tools
49
64
  run: |
50
65
  python -m pip install --upgrade pip
@@ -62,6 +77,14 @@ jobs:
62
77
  echo "::error title=Missing tag::Tag '${release_tag}' was not fetched. Create and push it before running this workflow."
63
78
  exit 1
64
79
  fi
80
+ tag_revision="$(git rev-list -n 1 "${release_tag}")"
81
+ git fetch --no-tags origin main
82
+ main_revision="$(git rev-parse origin/main)"
83
+ if ! git merge-base --is-ancestor "${tag_revision}" "${main_revision}"; then
84
+ echo "::error title=Tag is not on main::Tag '${release_tag}' points at ${tag_revision}, which is not reachable from origin/main."
85
+ echo "::error::Merge the release PR first, then tag the main commit."
86
+ exit 1
87
+ fi
65
88
 
66
89
  project_version=$(uv run --frozen python - <<'PY'
67
90
  import tomllib
@@ -77,22 +100,6 @@ jobs:
77
100
 
78
101
  echo "tag=${release_tag}" >> "${GITHUB_OUTPUT}"
79
102
 
80
- - name: Validate package
81
- run: |
82
- uv lock --check
83
- uv run --frozen ruff check .
84
- uv run --frozen ruff format --check .
85
- uv run --frozen pyright
86
- uv run --frozen python -m unittest discover -s tests
87
- uv run --frozen python - <<'PY'
88
- import os
89
-
90
- import src_py_lib
91
-
92
- if src_py_lib.__name__ != os.environ["IMPORT_NAME"]:
93
- raise SystemExit(f"unexpected import name: {src_py_lib.__name__}")
94
- PY
95
-
96
103
  - name: Build distributions
97
104
  id: build
98
105
  run: |
@@ -115,17 +122,24 @@ jobs:
115
122
  wheel_path="${project_wheels[0]}"
116
123
  wheel_name="$(basename "${wheel_path}")"
117
124
  source_distribution_path="${source_distributions[0]}"
118
- checksum_path="${wheel_path}.sha256"
125
+ source_distribution_name="$(basename "${source_distribution_path}")"
126
+ wheel_checksum_path="${wheel_path}.sha256"
127
+ source_distribution_checksum_path="${source_distribution_path}.sha256"
119
128
 
120
129
  (
121
130
  cd "$(dirname "${wheel_path}")"
122
- shasum -a 256 "${wheel_name}" > "$(basename "${checksum_path}")"
131
+ shasum -a 256 "${wheel_name}" > "$(basename "${wheel_checksum_path}")"
132
+ shasum -a 256 "${source_distribution_name}" > "$(basename "${source_distribution_checksum_path}")"
123
133
  )
124
134
 
125
- echo "wheel_path=${wheel_path}" >> "${GITHUB_OUTPUT}"
126
- echo "wheel_name=${wheel_name}" >> "${GITHUB_OUTPUT}"
127
- echo "source_distribution_path=${source_distribution_path}" >> "${GITHUB_OUTPUT}"
128
- echo "checksum_path=${checksum_path}" >> "${GITHUB_OUTPUT}"
135
+ {
136
+ echo "wheel_path=${wheel_path}"
137
+ echo "wheel_name=${wheel_name}"
138
+ echo "source_distribution_path=${source_distribution_path}"
139
+ echo "source_distribution_name=${source_distribution_name}"
140
+ echo "wheel_checksum_path=${wheel_checksum_path}"
141
+ echo "source_distribution_checksum_path=${source_distribution_checksum_path}"
142
+ } >> "${GITHUB_OUTPUT}"
129
143
 
130
144
  - name: Smoke test installed wheel
131
145
  run: |
@@ -147,6 +161,7 @@ jobs:
147
161
  run: |
148
162
  release_tag="${{ steps.release.outputs.tag }}"
149
163
  wheel_name="${{ steps.build.outputs.wheel_name }}"
164
+ source_distribution_name="${{ steps.build.outputs.source_distribution_name }}"
150
165
  notes_path="build/release/release-notes.md"
151
166
  cat > "${notes_path}" <<EOF
152
167
  ## Install
@@ -163,13 +178,19 @@ jobs:
163
178
  pip install "https://github.com/sourcegraph/src-py-lib/releases/download/${release_tag}/${wheel_name}"
164
179
  \`\`\`
165
180
 
181
+ Source distribution:
182
+
183
+ \`\`\`sh
184
+ curl -LO "https://github.com/sourcegraph/src-py-lib/releases/download/${release_tag}/${source_distribution_name}"
185
+ \`\`\`
186
+
166
187
  Or install this tag with uv:
167
188
 
168
189
  \`\`\`sh
169
190
  uv add "git+https://github.com/sourcegraph/src-py-lib.git@${release_tag}"
170
191
  \`\`\`
171
192
 
172
- Verify downloaded wheel assets with the matching \`.sha256\` file.
193
+ Verify downloaded assets with the matching \`.sha256\` files.
173
194
  EOF
174
195
  echo "path=${notes_path}" >> "${GITHUB_OUTPUT}"
175
196
 
@@ -179,7 +200,9 @@ jobs:
179
200
  name: src-py-lib-release
180
201
  path: |
181
202
  ${{ steps.build.outputs.wheel_path }}
182
- ${{ steps.build.outputs.checksum_path }}
203
+ ${{ steps.build.outputs.source_distribution_path }}
204
+ ${{ steps.build.outputs.wheel_checksum_path }}
205
+ ${{ steps.build.outputs.source_distribution_checksum_path }}
183
206
  ${{ steps.notes.outputs.path }}
184
207
 
185
208
  - name: Upload PyPI artifact
@@ -196,16 +219,23 @@ jobs:
196
219
  run: |
197
220
  release_tag="${{ steps.release.outputs.tag }}"
198
221
  wheel_path="${{ steps.build.outputs.wheel_path }}"
199
- checksum_path="${{ steps.build.outputs.checksum_path }}"
222
+ source_distribution_path="${{ steps.build.outputs.source_distribution_path }}"
223
+ wheel_checksum_path="${{ steps.build.outputs.wheel_checksum_path }}"
224
+ source_distribution_checksum_path="${{ steps.build.outputs.source_distribution_checksum_path }}"
200
225
  notes_path="${{ steps.notes.outputs.path }}"
226
+ release_assets=(
227
+ "${wheel_path}"
228
+ "${source_distribution_path}"
229
+ "${wheel_checksum_path}"
230
+ "${source_distribution_checksum_path}"
231
+ )
201
232
 
202
233
  if gh release view "${release_tag}" >/dev/null 2>&1; then
203
234
  gh release edit "${release_tag}" --title "${release_tag}" --notes-file "${notes_path}"
204
- gh release upload "${release_tag}" "${wheel_path}" "${checksum_path}" --clobber
235
+ gh release upload "${release_tag}" "${release_assets[@]}" --clobber
205
236
  else
206
237
  gh release create "${release_tag}" \
207
- "${wheel_path}" \
208
- "${checksum_path}" \
238
+ "${release_assets[@]}" \
209
239
  --title "${release_tag}" \
210
240
  --notes-file "${notes_path}" \
211
241
  --verify-tag
@@ -233,3 +263,4 @@ jobs:
233
263
  uses: pypa/gh-action-pypi-publish@release/v1
234
264
  with:
235
265
  packages-dir: dist
266
+ skip-existing: true
@@ -1,10 +1,12 @@
1
- name: CI
1
+ name: Validate
2
2
 
3
3
  on:
4
- pull_request:
5
- push:
6
- branches:
7
- - main
4
+ workflow_call:
5
+ inputs:
6
+ ref:
7
+ description: "Git ref to validate. Defaults to the caller's ref."
8
+ required: false
9
+ type: string
8
10
 
9
11
  permissions:
10
12
  contents: read
@@ -14,11 +16,13 @@ defaults:
14
16
  shell: bash
15
17
 
16
18
  jobs:
17
- test:
18
- name: Build and test
19
+ package:
20
+ name: Validate package
19
21
  runs-on: ubuntu-24.04
20
22
  env:
23
+ ACTIONLINT_VERSION: "1.7.12"
21
24
  IMPORT_NAME: src_py_lib
25
+ MARKDOWNLINT_CLI2_VERSION: "0.22.1"
22
26
  PYTHON_VERSION: "3.11"
23
27
  UV_VERSION: "0.11.7"
24
28
 
@@ -27,6 +31,34 @@ jobs:
27
31
  uses: actions/checkout@v6
28
32
  with:
29
33
  persist-credentials: false
34
+ ref: ${{ inputs.ref || github.ref }}
35
+
36
+ - name: Cache actionlint
37
+ id: cache-actionlint
38
+ uses: actions/cache@v4
39
+ with:
40
+ path: ~/.local/bin/actionlint
41
+ key: actionlint-${{ runner.os }}-${{ runner.arch }}-${{ env.ACTIONLINT_VERSION }}
42
+
43
+ - name: Install actionlint
44
+ if: steps.cache-actionlint.outputs.cache-hit != 'true'
45
+ run: |
46
+ mkdir -p "${HOME}/.local/bin"
47
+ go install "github.com/rhysd/actionlint/cmd/actionlint@v${ACTIONLINT_VERSION}"
48
+ install -m 0755 "${HOME}/go/bin/actionlint" "${HOME}/.local/bin/actionlint"
49
+
50
+ - name: Lint GitHub Actions
51
+ run: |
52
+ "${HOME}/.local/bin/actionlint"
53
+
54
+ - name: Cache npm
55
+ uses: actions/cache@v4
56
+ with:
57
+ path: ~/.npm
58
+ key: npm-${{ runner.os }}-markdownlint-cli2-${{ env.MARKDOWNLINT_CLI2_VERSION }}
59
+
60
+ - name: Lint Markdown
61
+ run: npx --yes "markdownlint-cli2@${MARKDOWNLINT_CLI2_VERSION}"
30
62
 
31
63
  - name: Set up Python
32
64
  uses: actions/setup-python@v6
@@ -34,6 +66,14 @@ jobs:
34
66
  python-version: ${{ env.PYTHON_VERSION }}
35
67
  cache: pip
36
68
 
69
+ - name: Cache uv
70
+ uses: actions/cache@v4
71
+ with:
72
+ path: ~/.cache/uv
73
+ key: uv-${{ runner.os }}-py${{ env.PYTHON_VERSION }}-${{ hashFiles('uv.lock') }}
74
+ restore-keys: |
75
+ uv-${{ runner.os }}-py${{ env.PYTHON_VERSION }}-
76
+
37
77
  - name: Install uv
38
78
  run: |
39
79
  python -m pip install --upgrade pip
@@ -42,9 +82,6 @@ jobs:
42
82
  - name: Validate lockfile
43
83
  run: uv lock --check
44
84
 
45
- - name: Lint Markdown
46
- run: npx --yes markdownlint-cli2
47
-
48
85
  - name: Lint Python
49
86
  run: uv run --frozen ruff check .
50
87
 
@@ -85,9 +122,3 @@ jobs:
85
122
  if src_py_lib.__name__ != os.environ["IMPORT_NAME"]:
86
123
  raise SystemExit(f"unexpected import name: {src_py_lib.__name__}")
87
124
  PY
88
-
89
- - name: Upload wheel artifact
90
- uses: actions/upload-artifact@v7
91
- with:
92
- name: src-py-lib-wheel
93
- path: dist/*.whl
@@ -14,7 +14,8 @@
14
14
  ## Standard commands
15
15
 
16
16
  ```sh
17
- npx --yes markdownlint-cli2
17
+ actionlint
18
+ npx --yes markdownlint-cli2@0.22.1
18
19
  uv sync
19
20
  uv run ruff format .
20
21
  uv run ruff check .
@@ -56,11 +57,14 @@ uv run python -m unittest discover -s tests
56
57
  verifies that it matches `project.version` before building GitHub release
57
58
  assets and publishing to PyPI.
58
59
  - Prepare releases on a branch from current `main`. Set `VERSION`, then run:
60
+ - As part of every release bump, find old release-version literals in
61
+ `AGENTS.md`, `README.md`, and release snippets, and replace them with the
62
+ new version where they are meant to stay current.
59
63
 
60
64
  ```sh
61
65
  set -euo pipefail
62
66
 
63
- VERSION=0.1.1
67
+ VERSION=0.1.2
64
68
  BRANCH="release-v${VERSION}"
65
69
 
66
70
  git fetch origin --tags --prune
@@ -96,7 +100,8 @@ uv lock
96
100
  set -euo pipefail
97
101
 
98
102
  uv lock --check
99
- npx --yes markdownlint-cli2
103
+ actionlint
104
+ npx --yes markdownlint-cli2@0.22.1
100
105
  uv run ruff check .
101
106
  uv run ruff format --check .
102
107
  uv run pyright
@@ -111,7 +116,7 @@ rm -rf /tmp/src-py-lib-release-check
111
116
  ```sh
112
117
  set -euo pipefail
113
118
 
114
- VERSION=0.1.1
119
+ VERSION=0.1.2
115
120
  BRANCH="release-v${VERSION}"
116
121
  GH_REPO="sourcegraph/src-py-lib"
117
122
 
@@ -135,7 +140,7 @@ gh pr merge "${BRANCH}" --repo "${GH_REPO}" --squash --delete-branch
135
140
  ```sh
136
141
  set -euo pipefail
137
142
 
138
- VERSION=0.1.1
143
+ VERSION=0.1.2
139
144
 
140
145
  git fetch origin --tags --prune
141
146
  git switch main
@@ -149,7 +154,7 @@ git push origin "v${VERSION}"
149
154
  ```sh
150
155
  set -euo pipefail
151
156
 
152
- VERSION=0.1.1
157
+ VERSION=0.1.2
153
158
  GH_REPO="sourcegraph/src-py-lib"
154
159
 
155
160
  RUN_ID="$(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: src-py-lib
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Reusable libraries for Sourcegraph projects
5
5
  Project-URL: Homepage, https://github.com/sourcegraph/src-py-lib
6
6
  Project-URL: Issues, https://github.com/sourcegraph/src-py-lib/issues
@@ -31,6 +31,13 @@ This repo was created for Sourcegraph Implementation Engineering deployments,
31
31
  and is not intended, designed, built, or supported for use in any other scenario.
32
32
  Feel free to open issues or PRs, but responses are best effort.
33
33
 
34
+ ## Semantic Versioning
35
+
36
+ - Release versions are `major.minor.patch`
37
+ - Because this project is still major version 0:
38
+ - Minor version updates are breaking changes
39
+ - Patch version updates are not breaking changes
40
+
34
41
  ## Install
35
42
 
36
43
  From PyPI:
@@ -8,6 +8,13 @@ This repo was created for Sourcegraph Implementation Engineering deployments,
8
8
  and is not intended, designed, built, or supported for use in any other scenario.
9
9
  Feel free to open issues or PRs, but responses are best effort.
10
10
 
11
+ ## Semantic Versioning
12
+
13
+ - Release versions are `major.minor.patch`
14
+ - Because this project is still major version 0:
15
+ - Minor version updates are breaking changes
16
+ - Patch version updates are not breaking changes
17
+
11
18
  ## Install
12
19
 
13
20
  From PyPI:
@@ -10,7 +10,7 @@ dev = [
10
10
 
11
11
  [project]
12
12
  name = "src-py-lib"
13
- version = "0.1.1"
13
+ version = "0.1.2"
14
14
  description = "Reusable libraries for Sourcegraph projects"
15
15
  readme = "README.md"
16
16
  requires-python = ">=3.11"
@@ -45,7 +45,7 @@ Issues = "https://github.com/sourcegraph/src-py-lib/issues"
45
45
  packages = ["src/src_py_lib"]
46
46
 
47
47
  [tool.pyright]
48
- include = ["src/src_py_lib"]
48
+ include = ["src/src_py_lib", "tests"]
49
49
  typeCheckingMode = "strict"
50
50
  extraPaths = ["src"]
51
51
  pythonVersion = "3.11"
@@ -37,6 +37,14 @@ from src_py_lib.clients.slack import (
37
37
  from src_py_lib.clients.sourcegraph import (
38
38
  SourcegraphClient,
39
39
  SourcegraphClientConfig,
40
+ SourcegraphJaegerTraceError,
41
+ SourcegraphJaegerTraceSummary,
42
+ SourcegraphTrace,
43
+ decode_external_service_id,
44
+ decode_repository_id,
45
+ decode_sourcegraph_node_id,
46
+ encode_repository_id,
47
+ encode_sourcegraph_node_id,
40
48
  normalize_sourcegraph_endpoint,
41
49
  sourcegraph_client_from_config,
42
50
  )
@@ -49,7 +57,7 @@ from src_py_lib.utils.config import (
49
57
  from src_py_lib.utils.config import (
50
58
  config_parse_args as parse_args,
51
59
  )
52
- from src_py_lib.utils.http import HTTPClient, HTTPClientError
60
+ from src_py_lib.utils.http import HTTPClient, HTTPClientError, HTTPResponse
53
61
  from src_py_lib.utils.json_cache import load_json_cache, load_json_subset, save_json_cache
54
62
  from src_py_lib.utils.json_types import (
55
63
  JSONDict,
@@ -63,8 +71,10 @@ from src_py_lib.utils.json_types import (
63
71
  from src_py_lib.utils.logging import (
64
72
  LoggingConfig,
65
73
  LoggingSettings,
74
+ TraceContext,
66
75
  configure_logging,
67
76
  critical,
77
+ current_trace_context,
68
78
  debug,
69
79
  error,
70
80
  event,
@@ -73,10 +83,15 @@ from src_py_lib.utils.logging import (
73
83
  log_context,
74
84
  logging_context,
75
85
  logging_settings_from_config,
86
+ new_trace_context,
76
87
  resolve_log_level_name,
88
+ sampled_traceparent,
77
89
  stage,
78
90
  startup_event,
79
91
  submit_with_log_context,
92
+ trace_context,
93
+ trace_context_from_traceparent,
94
+ traceparent_header,
80
95
  warning,
81
96
  )
82
97
  from src_py_lib.utils.tsv import write_tsv
@@ -116,6 +131,7 @@ __all__ = [
116
131
  "GoogleSheetsError",
117
132
  "HTTPClient",
118
133
  "HTTPClientError",
134
+ "HTTPResponse",
119
135
  "JSONDict",
120
136
  "LinearClient",
121
137
  "LinearClientConfig",
@@ -128,12 +144,22 @@ __all__ = [
128
144
  "SlackPacer",
129
145
  "SourcegraphClient",
130
146
  "SourcegraphClientConfig",
147
+ "SourcegraphJaegerTraceError",
148
+ "SourcegraphJaegerTraceSummary",
149
+ "SourcegraphTrace",
150
+ "TraceContext",
131
151
  "aliased_batched_query",
132
152
  "config_field",
133
153
  "config_snapshot",
134
154
  "configure_logging",
135
155
  "critical",
156
+ "current_trace_context",
136
157
  "debug",
158
+ "decode_external_service_id",
159
+ "decode_repository_id",
160
+ "decode_sourcegraph_node_id",
161
+ "encode_repository_id",
162
+ "encode_sourcegraph_node_id",
137
163
  "error",
138
164
  "event",
139
165
  "gh_cli_token",
@@ -153,18 +179,23 @@ __all__ = [
153
179
  "logging_settings_from_config",
154
180
  "log",
155
181
  "log_context",
182
+ "new_trace_context",
156
183
  "normalize_sourcegraph_endpoint",
157
184
  "parse_args",
158
185
  "pr_ref_from_url",
159
186
  "quota_project_from_adc",
160
187
  "resolve_log_level_name",
161
188
  "save_json_cache",
189
+ "sampled_traceparent",
162
190
  "slack_client_from_config",
163
191
  "sourcegraph_client_from_config",
164
192
  "stage",
165
193
  "startup_event",
166
194
  "stream_connection_nodes",
167
195
  "submit_with_log_context",
196
+ "trace_context",
197
+ "trace_context_from_traceparent",
198
+ "traceparent_header",
168
199
  "warning",
169
200
  "write_tsv",
170
201
  ]
@@ -9,11 +9,13 @@ from dataclasses import dataclass, field
9
9
  from pathlib import Path
10
10
  from typing import cast
11
11
 
12
- from src_py_lib.utils.http import HTTPClient, HTTPClientError
12
+ from src_py_lib.utils.http import HTTPClient, HTTPClientError, HTTPResponse
13
13
  from src_py_lib.utils.json_types import JSONDict, JSONValue, json_dict, json_list, json_str
14
14
  from src_py_lib.utils.logging import event
15
15
 
16
16
  _OPERATION_NAME_RE = re.compile(r"\b(?:query|mutation|subscription)\s+(\w+)")
17
+ HeaderProvider = Mapping[str, str] | Callable[[], Mapping[str, str]]
18
+ GraphQLResponseHook = Callable[[HTTPResponse, Mapping[str, str]], None]
17
19
 
18
20
  GRAPHQL_INTROSPECTION_QUERY = """
19
21
  query IntrospectionQuery {
@@ -130,10 +132,11 @@ class GraphQLClient:
130
132
  """POST JSON GraphQL operations and return the `data` object."""
131
133
 
132
134
  url: str
133
- headers: dict[str, str]
135
+ headers: HeaderProvider
134
136
  label: str
135
137
  http: HTTPClient = field(default_factory=HTTPClient)
136
138
  tolerate_partial_errors: bool = False
139
+ response_hook: GraphQLResponseHook | None = None
137
140
 
138
141
  def execute(
139
142
  self,
@@ -251,7 +254,16 @@ class GraphQLClient:
251
254
  query_bytes=len(query.encode("utf-8")),
252
255
  ) as fields:
253
256
  try:
254
- payload = self.http.json("POST", self.url, headers=self.headers, json_body=body)
257
+ request_headers = self._headers()
258
+ if self.response_hook is None:
259
+ payload = self.http.json(
260
+ "POST", self.url, headers=request_headers, json_body=body
261
+ )
262
+ else:
263
+ payload, response = self.http.json_response(
264
+ "POST", self.url, headers=request_headers, json_body=body
265
+ )
266
+ self.response_hook(response, request_headers)
255
267
  except HTTPClientError as exception:
256
268
  raise GraphQLError(
257
269
  f"{self.label} GraphQL request failed: {exception}",
@@ -269,6 +281,11 @@ class GraphQLClient:
269
281
  )
270
282
  return data
271
283
 
284
+ def _headers(self) -> dict[str, str]:
285
+ if callable(self.headers):
286
+ return dict(self.headers())
287
+ return dict(self.headers)
288
+
272
289
 
273
290
  def operation_name(query: str) -> str:
274
291
  """Extract the operation name from a GraphQL document."""