ascendkit 0.2.2__tar.gz → 0.2.5__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.
- ascendkit-0.2.5/.github/workflows/auto-release.yml +13 -0
- ascendkit-0.2.5/.github/workflows/publish.yml +58 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/CLAUDE.md +5 -11
- ascendkit-0.2.5/COMMANDS.md +6 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/PKG-INFO +1 -1
- {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/_client.py +27 -4
- {ascendkit-0.2.2 → ascendkit-0.2.5}/pyproject.toml +1 -1
- ascendkit-0.2.2/.github/workflows/publish.yml +0 -29
- {ascendkit-0.2.2 → ascendkit-0.2.5}/.github/workflows/enforce-source-branch.yml +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/.gitignore +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/__init__.py +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/_access_token.py +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/_analytics.py +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/_errors.py +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/_version.py +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/_webhooks.py +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/auth.py +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/fastapi.py +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/ascendkit/py.typed +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/tests/__init__.py +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/tests/test_client.py +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/tests/test_version_header.py +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/tests/test_webhooks.py +0 -0
- {ascendkit-0.2.2 → ascendkit-0.2.5}/uv.lock +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- pyproject.toml
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
publish-and-release:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
environment: pypi
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Read version
|
|
21
|
+
id: version
|
|
22
|
+
run: echo "value=$(python3 -c 'import pathlib, re; m = re.search(r\"^version\\s*=\\s*\\\"([^\\\"]+)\\\"\", pathlib.Path(\"pyproject.toml\").read_text(), re.M); print(m.group(1))')" >> "$GITHUB_OUTPUT"
|
|
23
|
+
|
|
24
|
+
- name: Check if tag exists
|
|
25
|
+
id: tag
|
|
26
|
+
run: |
|
|
27
|
+
if git ls-remote --tags origin "refs/tags/v${{ steps.version.outputs.value }}" | grep -q .; then
|
|
28
|
+
echo "exists=true" >> "$GITHUB_OUTPUT"
|
|
29
|
+
else
|
|
30
|
+
echo "exists=false" >> "$GITHUB_OUTPUT"
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
- uses: actions/setup-python@v5
|
|
34
|
+
if: steps.tag.outputs.exists == 'false'
|
|
35
|
+
with:
|
|
36
|
+
python-version: "3.12"
|
|
37
|
+
|
|
38
|
+
- name: Install build tools
|
|
39
|
+
if: steps.tag.outputs.exists == 'false'
|
|
40
|
+
run: pip install build
|
|
41
|
+
|
|
42
|
+
- name: Build package
|
|
43
|
+
if: steps.tag.outputs.exists == 'false'
|
|
44
|
+
run: python -m build
|
|
45
|
+
|
|
46
|
+
- name: Publish to PyPI (trusted publishing)
|
|
47
|
+
if: steps.tag.outputs.exists == 'false'
|
|
48
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
49
|
+
|
|
50
|
+
- name: Create release
|
|
51
|
+
if: steps.tag.outputs.exists == 'false'
|
|
52
|
+
env:
|
|
53
|
+
GH_TOKEN: ${{ github.token }}
|
|
54
|
+
run: |
|
|
55
|
+
gh release create "v${{ steps.version.outputs.value }}" \
|
|
56
|
+
--target main \
|
|
57
|
+
--title "v${{ steps.version.outputs.value }}" \
|
|
58
|
+
--generate-notes
|
|
@@ -2,24 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
Python 3.10+ SDK. httpx (async), published to PyPI.
|
|
4
4
|
|
|
5
|
-
## Commands
|
|
6
|
-
```bash
|
|
7
|
-
pip install -e ".[dev]" # Install editable
|
|
8
|
-
pytest # Tests
|
|
9
|
-
ruff check . && ruff format . # Lint + format
|
|
10
|
-
mypy ascendkit/ # Type checking
|
|
11
|
-
```
|
|
12
|
-
|
|
13
5
|
## Rules
|
|
14
6
|
- MUST use `httpx` — not `requests`. Support both sync and async patterns.
|
|
15
7
|
- Logger: drop-in for `logging` module. Keyword args for context: `logger.info("msg", port=3000)`
|
|
16
8
|
- Batch logs (5s / 100 events) via `POST /api/logs/ingest` — NEVER send one-at-a-time
|
|
17
9
|
- Flush on interpreter shutdown via `atexit`
|
|
18
|
-
- Public API through `ascendkit/__init__.py` — internal modules prefixed `_` (e.g., `_client.py`)
|
|
19
|
-
- Typed exceptions (`AscendKitError`, `AuthenticationError`) — NEVER swallow errors
|
|
20
10
|
- All public functions MUST have type annotations
|
|
21
11
|
- Gracefully handle network failures on flush — buffer and retry, NEVER crash host app
|
|
22
12
|
- In async frameworks, use `httpx.AsyncClient` — sync client blocks the event loop
|
|
23
13
|
|
|
14
|
+
## Release & Publish
|
|
15
|
+
Trigger: GitHub release (`release: published`) → `python -m build` → PyPI trusted publishing (`pypa/gh-action-pypi-publish`)
|
|
16
|
+
To ship: bump `pyproject.toml` version on `dev` → merge to `main` → create GitHub release tagged `vX.Y.Z`
|
|
17
|
+
|
|
24
18
|
## GUIDANCE
|
|
25
|
-
You are a thin client, rely on backend api for business logic validation
|
|
19
|
+
You are a thin client, rely on backend api for business logic validation
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
import os
|
|
7
|
+
import uuid
|
|
7
8
|
from typing import Any
|
|
8
9
|
|
|
9
10
|
import httpx
|
|
@@ -60,23 +61,34 @@ class AscendKit:
|
|
|
60
61
|
reads ``ASCENDKIT_ENV_KEY`` from the environment.
|
|
61
62
|
"""
|
|
62
63
|
|
|
63
|
-
def __init__(
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
public_key: str | None = None,
|
|
67
|
+
api_url: str = _DEFAULT_API_URL,
|
|
68
|
+
invocation_id: str | None = None,
|
|
69
|
+
) -> None:
|
|
64
70
|
resolved_key = public_key or os.environ.get("ASCENDKIT_ENV_KEY")
|
|
65
71
|
if not resolved_key:
|
|
66
72
|
raise ValueError(
|
|
67
73
|
"AscendKit: missing public key. Pass public_key or set ASCENDKIT_ENV_KEY."
|
|
68
74
|
)
|
|
69
75
|
self.public_key = resolved_key
|
|
76
|
+
self._invocation_id = invocation_id or str(uuid.uuid4())
|
|
70
77
|
self._client = httpx.Client(
|
|
71
78
|
base_url=api_url,
|
|
72
79
|
headers={
|
|
73
80
|
"X-AscendKit-Public-Key": resolved_key,
|
|
81
|
+
"X-AscendKit-Invocation-Id": self._invocation_id,
|
|
74
82
|
**_VERSION_HEADER,
|
|
75
83
|
},
|
|
76
84
|
)
|
|
77
85
|
|
|
78
86
|
def _request(self, method: str, path: str, **kwargs: Any) -> Any:
|
|
79
|
-
|
|
87
|
+
per_request_headers = {"X-AscendKit-Client-Request-Id": str(uuid.uuid4())}
|
|
88
|
+
existing = kwargs.pop("headers", None)
|
|
89
|
+
if existing:
|
|
90
|
+
per_request_headers.update(existing)
|
|
91
|
+
response = self._client.request(method, path, headers=per_request_headers, **kwargs)
|
|
80
92
|
_check_upgrade(response)
|
|
81
93
|
_handle_error(response)
|
|
82
94
|
body = response.json()
|
|
@@ -99,23 +111,34 @@ class AsyncAscendKit:
|
|
|
99
111
|
reads ``ASCENDKIT_ENV_KEY`` from the environment.
|
|
100
112
|
"""
|
|
101
113
|
|
|
102
|
-
def __init__(
|
|
114
|
+
def __init__(
|
|
115
|
+
self,
|
|
116
|
+
public_key: str | None = None,
|
|
117
|
+
api_url: str = _DEFAULT_API_URL,
|
|
118
|
+
invocation_id: str | None = None,
|
|
119
|
+
) -> None:
|
|
103
120
|
resolved_key = public_key or os.environ.get("ASCENDKIT_ENV_KEY")
|
|
104
121
|
if not resolved_key:
|
|
105
122
|
raise ValueError(
|
|
106
123
|
"AscendKit: missing public key. Pass public_key or set ASCENDKIT_ENV_KEY."
|
|
107
124
|
)
|
|
108
125
|
self.public_key = resolved_key
|
|
126
|
+
self._invocation_id = invocation_id or str(uuid.uuid4())
|
|
109
127
|
self._client = httpx.AsyncClient(
|
|
110
128
|
base_url=api_url,
|
|
111
129
|
headers={
|
|
112
130
|
"X-AscendKit-Public-Key": resolved_key,
|
|
131
|
+
"X-AscendKit-Invocation-Id": self._invocation_id,
|
|
113
132
|
**_VERSION_HEADER,
|
|
114
133
|
},
|
|
115
134
|
)
|
|
116
135
|
|
|
117
136
|
async def _request(self, method: str, path: str, **kwargs: Any) -> Any:
|
|
118
|
-
|
|
137
|
+
per_request_headers = {"X-AscendKit-Client-Request-Id": str(uuid.uuid4())}
|
|
138
|
+
existing = kwargs.pop("headers", None)
|
|
139
|
+
if existing:
|
|
140
|
+
per_request_headers.update(existing)
|
|
141
|
+
response = await self._client.request(method, path, headers=per_request_headers, **kwargs)
|
|
119
142
|
_check_upgrade(response)
|
|
120
143
|
_handle_error(response)
|
|
121
144
|
body = response.json()
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
name: Publish to PyPI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
release:
|
|
5
|
-
types: [published]
|
|
6
|
-
|
|
7
|
-
permissions:
|
|
8
|
-
contents: read
|
|
9
|
-
id-token: write
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
publish:
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
environment: pypi
|
|
15
|
-
steps:
|
|
16
|
-
- uses: actions/checkout@v4
|
|
17
|
-
|
|
18
|
-
- uses: actions/setup-python@v5
|
|
19
|
-
with:
|
|
20
|
-
python-version: "3.12"
|
|
21
|
-
|
|
22
|
-
- name: Install build tools
|
|
23
|
-
run: pip install build
|
|
24
|
-
|
|
25
|
-
- name: Build package
|
|
26
|
-
run: python -m build
|
|
27
|
-
|
|
28
|
-
- name: Publish to PyPI (trusted publishing)
|
|
29
|
-
uses: pypa/gh-action-pypi-publish@release/v1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|