senderkit 0.1.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.
- senderkit-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +48 -0
- senderkit-0.1.0/.github/ISSUE_TEMPLATE/config.yml +11 -0
- senderkit-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +25 -0
- senderkit-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +21 -0
- senderkit-0.1.0/.github/dependabot.yml +20 -0
- senderkit-0.1.0/.github/workflows/ci.yml +71 -0
- senderkit-0.1.0/.github/workflows/codeql.yml +30 -0
- senderkit-0.1.0/.github/workflows/release.yml +99 -0
- senderkit-0.1.0/.gitignore +21 -0
- senderkit-0.1.0/.pre-commit-config.yaml +18 -0
- senderkit-0.1.0/.release-please-manifest.json +3 -0
- senderkit-0.1.0/CHANGELOG.md +21 -0
- senderkit-0.1.0/CODE_OF_CONDUCT.md +15 -0
- senderkit-0.1.0/CONTRIBUTING.md +75 -0
- senderkit-0.1.0/LICENSE +21 -0
- senderkit-0.1.0/Makefile +33 -0
- senderkit-0.1.0/PKG-INFO +412 -0
- senderkit-0.1.0/README.md +366 -0
- senderkit-0.1.0/SECURITY.md +29 -0
- senderkit-0.1.0/examples/batch.py +23 -0
- senderkit-0.1.0/examples/celery_task.py +27 -0
- senderkit-0.1.0/examples/django_settings.py +38 -0
- senderkit-0.1.0/examples/send.py +16 -0
- senderkit-0.1.0/examples/send_async.py +30 -0
- senderkit-0.1.0/examples/webhook_fastapi.py +27 -0
- senderkit-0.1.0/examples/webhook_flask.py +31 -0
- senderkit-0.1.0/pyproject.toml +92 -0
- senderkit-0.1.0/release-please-config.json +12 -0
- senderkit-0.1.0/src/senderkit/__init__.py +95 -0
- senderkit-0.1.0/src/senderkit/_http.py +280 -0
- senderkit-0.1.0/src/senderkit/_serialize.py +184 -0
- senderkit-0.1.0/src/senderkit/_version.py +3 -0
- senderkit-0.1.0/src/senderkit/client.py +305 -0
- senderkit-0.1.0/src/senderkit/errors.py +89 -0
- senderkit-0.1.0/src/senderkit/integrations/__init__.py +10 -0
- senderkit-0.1.0/src/senderkit/integrations/celery.py +56 -0
- senderkit-0.1.0/src/senderkit/integrations/django/__init__.py +25 -0
- senderkit-0.1.0/src/senderkit/integrations/django/backends.py +108 -0
- senderkit-0.1.0/src/senderkit/integrations/django/client.py +43 -0
- senderkit-0.1.0/src/senderkit/integrations/django/views.py +55 -0
- senderkit-0.1.0/src/senderkit/integrations/fastapi.py +86 -0
- senderkit-0.1.0/src/senderkit/integrations/flask.py +107 -0
- senderkit-0.1.0/src/senderkit/models.py +335 -0
- senderkit-0.1.0/src/senderkit/py.typed +0 -0
- senderkit-0.1.0/src/senderkit/resources/__init__.py +6 -0
- senderkit-0.1.0/src/senderkit/resources/messages.py +132 -0
- senderkit-0.1.0/src/senderkit/resources/templates.py +53 -0
- senderkit-0.1.0/src/senderkit/webhooks.py +103 -0
- senderkit-0.1.0/tests/__init__.py +0 -0
- senderkit-0.1.0/tests/conftest.py +28 -0
- senderkit-0.1.0/tests/helpers.py +19 -0
- senderkit-0.1.0/tests/integrations/__init__.py +0 -0
- senderkit-0.1.0/tests/integrations/test_celery.py +30 -0
- senderkit-0.1.0/tests/integrations/test_django_backend.py +130 -0
- senderkit-0.1.0/tests/integrations/test_fastapi.py +69 -0
- senderkit-0.1.0/tests/integrations/test_flask.py +63 -0
- senderkit-0.1.0/tests/test_batch.py +60 -0
- senderkit-0.1.0/tests/test_context.py +34 -0
- senderkit-0.1.0/tests/test_errors.py +54 -0
- senderkit-0.1.0/tests/test_messages.py +97 -0
- senderkit-0.1.0/tests/test_retries.py +66 -0
- senderkit-0.1.0/tests/test_send.py +67 -0
- senderkit-0.1.0/tests/test_send_raw.py +55 -0
- senderkit-0.1.0/tests/test_templates.py +73 -0
- senderkit-0.1.0/tests/test_timeout.py +21 -0
- senderkit-0.1.0/tests/test_webhooks.py +72 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report a problem with the SenderKit Python SDK
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
Thanks for taking the time to file a bug. Please do **not** include API
|
|
9
|
+
keys, signing secrets, or other credentials in this report.
|
|
10
|
+
- type: textarea
|
|
11
|
+
id: what-happened
|
|
12
|
+
attributes:
|
|
13
|
+
label: What happened?
|
|
14
|
+
description: A clear description of the bug and what you expected instead.
|
|
15
|
+
placeholder: When I call `client.send(...)`, I get ...
|
|
16
|
+
validations:
|
|
17
|
+
required: true
|
|
18
|
+
- type: textarea
|
|
19
|
+
id: repro
|
|
20
|
+
attributes:
|
|
21
|
+
label: Minimal reproduction
|
|
22
|
+
description: The smallest code snippet that reproduces the issue.
|
|
23
|
+
render: python
|
|
24
|
+
validations:
|
|
25
|
+
required: true
|
|
26
|
+
- type: input
|
|
27
|
+
id: sdk-version
|
|
28
|
+
attributes:
|
|
29
|
+
label: SDK version
|
|
30
|
+
description: "Output of `python -c 'import senderkit; print(senderkit.__version__)'`"
|
|
31
|
+
placeholder: 0.1.0
|
|
32
|
+
validations:
|
|
33
|
+
required: true
|
|
34
|
+
- type: input
|
|
35
|
+
id: python-version
|
|
36
|
+
attributes:
|
|
37
|
+
label: Python version
|
|
38
|
+
placeholder: "3.12"
|
|
39
|
+
validations:
|
|
40
|
+
required: true
|
|
41
|
+
- type: textarea
|
|
42
|
+
id: traceback
|
|
43
|
+
attributes:
|
|
44
|
+
label: Traceback / logs
|
|
45
|
+
description: Full traceback, with any secrets redacted.
|
|
46
|
+
render: shell
|
|
47
|
+
validations:
|
|
48
|
+
required: false
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
2
|
+
contact_links:
|
|
3
|
+
- name: Documentation
|
|
4
|
+
url: https://docs.senderkit.com
|
|
5
|
+
about: Guides and API reference for SenderKit.
|
|
6
|
+
- name: Security vulnerability
|
|
7
|
+
url: https://github.com/senderkit/senderkit-sdk-python/security/advisories/new
|
|
8
|
+
about: Report security issues privately — please do not open a public issue.
|
|
9
|
+
- name: Questions & support
|
|
10
|
+
url: https://senderkit.com
|
|
11
|
+
about: For account or product questions, contact SenderKit support.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest an improvement or new capability for the SDK
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: Problem / use case
|
|
9
|
+
description: What are you trying to do that the SDK makes hard today?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: proposal
|
|
14
|
+
attributes:
|
|
15
|
+
label: Proposed solution
|
|
16
|
+
description: What would the ideal API or behavior look like?
|
|
17
|
+
render: python
|
|
18
|
+
validations:
|
|
19
|
+
required: false
|
|
20
|
+
- type: textarea
|
|
21
|
+
id: alternatives
|
|
22
|
+
attributes:
|
|
23
|
+
label: Alternatives considered
|
|
24
|
+
validations:
|
|
25
|
+
required: false
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Thanks for contributing! Please read CONTRIBUTING.md first.
|
|
3
|
+
Keep PRs focused — one logical change per PR.
|
|
4
|
+
-->
|
|
5
|
+
|
|
6
|
+
## Summary
|
|
7
|
+
|
|
8
|
+
<!-- What does this PR do and why? -->
|
|
9
|
+
|
|
10
|
+
## Related issues
|
|
11
|
+
|
|
12
|
+
<!-- e.g. Closes #123 -->
|
|
13
|
+
|
|
14
|
+
## Checklist
|
|
15
|
+
|
|
16
|
+
- [ ] `ruff check .` passes
|
|
17
|
+
- [ ] `ruff format .` applied
|
|
18
|
+
- [ ] `mypy src` passes
|
|
19
|
+
- [ ] `pytest` passes (new behavior is covered by tests)
|
|
20
|
+
- [ ] PR title / commits follow [Conventional Commits](https://www.conventionalcommits.org/) (drives versioning & changelog)
|
|
21
|
+
- [ ] Docs / docstrings updated if the public API changed
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
- package-ecosystem: pip
|
|
4
|
+
directory: "/"
|
|
5
|
+
schedule:
|
|
6
|
+
interval: weekly
|
|
7
|
+
groups:
|
|
8
|
+
python-dependencies:
|
|
9
|
+
patterns: ["*"]
|
|
10
|
+
open-pull-requests-limit: 5
|
|
11
|
+
labels: ["dependencies"]
|
|
12
|
+
|
|
13
|
+
- package-ecosystem: github-actions
|
|
14
|
+
directory: "/"
|
|
15
|
+
schedule:
|
|
16
|
+
interval: weekly
|
|
17
|
+
groups:
|
|
18
|
+
github-actions:
|
|
19
|
+
patterns: ["*"]
|
|
20
|
+
labels: ["dependencies", "ci"]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
# Cancel superseded runs on the same ref to save CI minutes.
|
|
12
|
+
concurrency:
|
|
13
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
14
|
+
cancel-in-progress: true
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
lint:
|
|
18
|
+
name: Lint & type-check
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v6
|
|
22
|
+
- uses: actions/setup-python@v6
|
|
23
|
+
with:
|
|
24
|
+
python-version: "3.12"
|
|
25
|
+
cache: pip
|
|
26
|
+
cache-dependency-path: pyproject.toml
|
|
27
|
+
- name: Install
|
|
28
|
+
run: python -m pip install -e ".[dev]"
|
|
29
|
+
- name: ruff check
|
|
30
|
+
run: ruff check --output-format=github .
|
|
31
|
+
- name: ruff format
|
|
32
|
+
run: ruff format --check .
|
|
33
|
+
- name: mypy
|
|
34
|
+
run: mypy src
|
|
35
|
+
|
|
36
|
+
test:
|
|
37
|
+
name: Test (py${{ matrix.python-version }})
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
strategy:
|
|
40
|
+
fail-fast: false
|
|
41
|
+
matrix:
|
|
42
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@v6
|
|
45
|
+
- uses: actions/setup-python@v6
|
|
46
|
+
with:
|
|
47
|
+
python-version: ${{ matrix.python-version }}
|
|
48
|
+
cache: pip
|
|
49
|
+
cache-dependency-path: pyproject.toml
|
|
50
|
+
- name: Install
|
|
51
|
+
run: python -m pip install -e ".[dev]"
|
|
52
|
+
- name: pytest
|
|
53
|
+
run: pytest --cov=senderkit --cov-report=term-missing --cov-report=xml
|
|
54
|
+
- name: Upload coverage to Codecov
|
|
55
|
+
uses: codecov/codecov-action@v7
|
|
56
|
+
with:
|
|
57
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
58
|
+
flags: py${{ matrix.python-version }}
|
|
59
|
+
fail_ci_if_error: false
|
|
60
|
+
|
|
61
|
+
# Single required status check that gates merges, so branch protection only
|
|
62
|
+
# needs to reference one job regardless of how the matrix grows.
|
|
63
|
+
ci-ok:
|
|
64
|
+
name: CI
|
|
65
|
+
if: always()
|
|
66
|
+
needs: [lint, test]
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
steps:
|
|
69
|
+
- name: Verify all jobs succeeded
|
|
70
|
+
if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
|
|
71
|
+
run: exit 1
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
schedule:
|
|
9
|
+
- cron: "27 4 * * 1" # weekly, Mondays
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
analyze:
|
|
16
|
+
name: Analyze (Python)
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
permissions:
|
|
19
|
+
actions: read # read workflow run metadata
|
|
20
|
+
contents: read # required for actions/checkout
|
|
21
|
+
security-events: write # upload CodeQL results
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v6
|
|
24
|
+
- name: Initialize CodeQL
|
|
25
|
+
uses: github/codeql-action/init@v4
|
|
26
|
+
with:
|
|
27
|
+
languages: python
|
|
28
|
+
queries: security-and-quality
|
|
29
|
+
- name: Perform CodeQL analysis
|
|
30
|
+
uses: github/codeql-action/analyze@v4
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Automated releases via release-please + PyPI Trusted Publishing.
|
|
4
|
+
#
|
|
5
|
+
# Flow:
|
|
6
|
+
# 1. Pushes to main run release-please, which maintains a "release PR" that
|
|
7
|
+
# bumps the version (src/senderkit/_version.py) and CHANGELOG.md from the
|
|
8
|
+
# Conventional Commit history.
|
|
9
|
+
# 2. Merging that release PR tags the version + creates the GitHub Release and,
|
|
10
|
+
# in the same run, builds and publishes to PyPI.
|
|
11
|
+
#
|
|
12
|
+
# Everything stays in ONE workflow run so it does not depend on the GitHub
|
|
13
|
+
# Release event (events raised by GITHUB_TOKEN do not trigger other workflows).
|
|
14
|
+
#
|
|
15
|
+
# One-time repo setup:
|
|
16
|
+
# - Settings → Actions → General → Workflow permissions:
|
|
17
|
+
# enable "Allow GitHub Actions to create and approve pull requests".
|
|
18
|
+
# - A PyPI Trusted Publisher for this repo → workflow "release.yml" →
|
|
19
|
+
# environment "pypi". https://docs.pypi.org/trusted-publishers/
|
|
20
|
+
|
|
21
|
+
on:
|
|
22
|
+
push:
|
|
23
|
+
branches: [main]
|
|
24
|
+
|
|
25
|
+
permissions:
|
|
26
|
+
contents: read
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
release-please:
|
|
30
|
+
name: Release please
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
permissions:
|
|
33
|
+
contents: write # create tags + GitHub releases
|
|
34
|
+
pull-requests: write # open/update the release PR
|
|
35
|
+
outputs:
|
|
36
|
+
release_created: ${{ steps.release.outputs.release_created }}
|
|
37
|
+
tag_name: ${{ steps.release.outputs.tag_name }}
|
|
38
|
+
steps:
|
|
39
|
+
- uses: googleapis/release-please-action@v4
|
|
40
|
+
id: release
|
|
41
|
+
with:
|
|
42
|
+
config-file: release-please-config.json
|
|
43
|
+
manifest-file: .release-please-manifest.json
|
|
44
|
+
|
|
45
|
+
build:
|
|
46
|
+
name: Build distributions
|
|
47
|
+
needs: release-please
|
|
48
|
+
if: needs.release-please.outputs.release_created == 'true'
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
permissions:
|
|
51
|
+
contents: read
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v6
|
|
54
|
+
- uses: actions/setup-python@v6
|
|
55
|
+
with:
|
|
56
|
+
python-version: "3.12"
|
|
57
|
+
- name: Install build tooling
|
|
58
|
+
run: python -m pip install build twine
|
|
59
|
+
- name: Build sdist and wheel
|
|
60
|
+
run: python -m build
|
|
61
|
+
- name: Check metadata
|
|
62
|
+
run: twine check dist/*
|
|
63
|
+
- name: Verify built version matches the release tag
|
|
64
|
+
env:
|
|
65
|
+
TAG: ${{ needs.release-please.outputs.tag_name }}
|
|
66
|
+
run: |
|
|
67
|
+
# Install the freshly built wheel so we verify the artifact that is
|
|
68
|
+
# about to be published, not an editable/source checkout.
|
|
69
|
+
python -m pip install dist/*.whl
|
|
70
|
+
VERSION="$(python -c 'import senderkit; print(senderkit.__version__)')"
|
|
71
|
+
echo "Package version: $VERSION"
|
|
72
|
+
echo "Release tag: $TAG"
|
|
73
|
+
if [ "${TAG#v}" != "$VERSION" ]; then
|
|
74
|
+
echo "::error::Release tag ($TAG) does not match package version ($VERSION)."
|
|
75
|
+
exit 1
|
|
76
|
+
fi
|
|
77
|
+
- uses: actions/upload-artifact@v7
|
|
78
|
+
with:
|
|
79
|
+
name: dist
|
|
80
|
+
path: dist/
|
|
81
|
+
|
|
82
|
+
publish:
|
|
83
|
+
name: Publish to PyPI
|
|
84
|
+
needs: [release-please, build]
|
|
85
|
+
if: needs.release-please.outputs.release_created == 'true'
|
|
86
|
+
runs-on: ubuntu-latest
|
|
87
|
+
environment:
|
|
88
|
+
name: pypi
|
|
89
|
+
url: https://pypi.org/p/senderkit
|
|
90
|
+
permissions:
|
|
91
|
+
contents: read
|
|
92
|
+
id-token: write # required for Trusted Publishing
|
|
93
|
+
steps:
|
|
94
|
+
- uses: actions/download-artifact@v8
|
|
95
|
+
with:
|
|
96
|
+
name: dist
|
|
97
|
+
path: dist/
|
|
98
|
+
- name: Publish
|
|
99
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
venv/
|
|
3
|
+
src/*.egg-info/
|
|
4
|
+
**/__pycache__/
|
|
5
|
+
.pytest_cache/
|
|
6
|
+
.mypy_cache/
|
|
7
|
+
.ruff_cache/
|
|
8
|
+
.python-version
|
|
9
|
+
.DS_Store
|
|
10
|
+
dist/
|
|
11
|
+
build/
|
|
12
|
+
.env
|
|
13
|
+
.env.local
|
|
14
|
+
.coverage
|
|
15
|
+
htmlcov/
|
|
16
|
+
|
|
17
|
+
# Local editor / tooling (not part of the SDK)
|
|
18
|
+
.vscode/
|
|
19
|
+
.claude/
|
|
20
|
+
.idea
|
|
21
|
+
.gitattributes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Run `pre-commit install` to enable. Mirrors the CI lint/format gates.
|
|
2
|
+
repos:
|
|
3
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
4
|
+
rev: v5.0.0
|
|
5
|
+
hooks:
|
|
6
|
+
- id: trailing-whitespace
|
|
7
|
+
- id: end-of-file-fixer
|
|
8
|
+
- id: check-yaml
|
|
9
|
+
- id: check-toml
|
|
10
|
+
- id: check-merge-conflict
|
|
11
|
+
- id: check-added-large-files
|
|
12
|
+
|
|
13
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
14
|
+
rev: v0.15.17
|
|
15
|
+
hooks:
|
|
16
|
+
- id: ruff
|
|
17
|
+
args: [--fix]
|
|
18
|
+
- id: ruff-format
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 (2026-06-13)
|
|
4
|
+
|
|
5
|
+
Initial release of the hand-written SenderKit Python SDK (replacing the earlier
|
|
6
|
+
Speakeasy-generated client).
|
|
7
|
+
|
|
8
|
+
- Sync (`SenderKit`) and async (`AsyncSenderKit`) clients over `httpx`.
|
|
9
|
+
- `send`, `send_raw` (email/SMS/push/web-push), and concurrent `send_batch`.
|
|
10
|
+
- `messages` (list/iter/get/cancel) and `templates` (list/get/render) resources.
|
|
11
|
+
- Idempotency keys by default; automatic retries with backoff honoring `Retry-After`.
|
|
12
|
+
- Typed error hierarchy (`SenderKitError` → `APIError` subclasses, `TimeoutError`,
|
|
13
|
+
`NetworkError`, `SignatureVerificationError`).
|
|
14
|
+
- `WebhookVerifier` for HMAC-SHA256 signature verification.
|
|
15
|
+
- Framework integrations: Django (email backend, client, webhook view), FastAPI
|
|
16
|
+
(dependencies), Flask (extension), Celery (send task).
|
|
17
|
+
|
|
18
|
+
### Not yet included
|
|
19
|
+
|
|
20
|
+
- SSE streaming client for `messages.list(tail=1)` (the raw parameter is available).
|
|
21
|
+
- An `mcp` submodule (present in the TypeScript SDK).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
This project adopts the [Contributor Covenant](https://www.contributor-covenant.org),
|
|
4
|
+
version 2.1, as its code of conduct. The full text is available at:
|
|
5
|
+
|
|
6
|
+
https://www.contributor-covenant.org/version/2/1/code_of_conduct/
|
|
7
|
+
|
|
8
|
+
By participating in this project — contributing code, opening issues, or taking
|
|
9
|
+
part in discussions — you agree to uphold this code of conduct.
|
|
10
|
+
|
|
11
|
+
## Reporting
|
|
12
|
+
|
|
13
|
+
To report unacceptable behavior, use GitHub's private reporting on this
|
|
14
|
+
repository's **Security** tab, or contact the maintainers privately. All reports
|
|
15
|
+
will be reviewed and handled confidentially.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Contributing to the SenderKit Python SDK
|
|
2
|
+
|
|
3
|
+
Thanks for your interest in improving the SDK! This document covers how to set
|
|
4
|
+
up a development environment and the checks your change needs to pass.
|
|
5
|
+
|
|
6
|
+
## Development setup
|
|
7
|
+
|
|
8
|
+
Requires Python 3.10+.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
git clone https://github.com/senderkit/senderkit-sdk-python.git
|
|
12
|
+
cd senderkit-sdk-python
|
|
13
|
+
python -m venv .venv && source .venv/bin/activate
|
|
14
|
+
pip install -e ".[dev]"
|
|
15
|
+
pre-commit install # optional: runs lint/format on every commit
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Running the checks
|
|
19
|
+
|
|
20
|
+
All of these run in CI; run them locally before opening a PR. The `Makefile`
|
|
21
|
+
wraps them:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
make lint # ruff check
|
|
25
|
+
make format # ruff format (apply)
|
|
26
|
+
make typecheck # mypy src
|
|
27
|
+
make test # pytest with coverage
|
|
28
|
+
make check # everything CI runs (format --check, lint, typecheck, test)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or invoke the tools directly:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
ruff check .
|
|
35
|
+
ruff format .
|
|
36
|
+
mypy src
|
|
37
|
+
pytest --cov=senderkit
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Guidelines
|
|
41
|
+
|
|
42
|
+
- **Tests.** New behavior needs tests. We use `pytest` with
|
|
43
|
+
[`respx`](https://lundberg.github.io/respx/) to mock HTTP — no live network
|
|
44
|
+
calls in the suite.
|
|
45
|
+
- **Typing.** The package ships `py.typed`; keep the public API fully typed and
|
|
46
|
+
`mypy`-clean.
|
|
47
|
+
- **Style.** `ruff` handles linting and formatting (100-char lines). Match the
|
|
48
|
+
surrounding code; the CI format gate is enforced.
|
|
49
|
+
- **Commits & PRs.** Keep PRs focused on one logical change. Use
|
|
50
|
+
[Conventional Commit](https://www.conventionalcommits.org/) messages — the
|
|
51
|
+
version bump and changelog are derived from them, so the prefix matters:
|
|
52
|
+
- `fix:` → patch release, `feat:` → minor release.
|
|
53
|
+
- `feat!:` or a `BREAKING CHANGE:` footer → breaking change.
|
|
54
|
+
- `docs:`, `chore:`, `test:`, `refactor:`, `ci:` → no release on their own.
|
|
55
|
+
- On squash-merge GitHub uses the **PR title**, so keep it conventional too.
|
|
56
|
+
- **Changelog.** Generated automatically from commit messages — don't edit
|
|
57
|
+
`CHANGELOG.md` by hand.
|
|
58
|
+
|
|
59
|
+
## Releasing (maintainers)
|
|
60
|
+
|
|
61
|
+
Releases are automated with
|
|
62
|
+
[release-please](https://github.com/googleapis/release-please):
|
|
63
|
+
|
|
64
|
+
1. Merge PRs to `main` with Conventional Commit messages.
|
|
65
|
+
2. release-please maintains a **release PR** that bumps `VERSION` in
|
|
66
|
+
`src/senderkit/_version.py` (the single source of truth) and updates
|
|
67
|
+
`CHANGELOG.md`. Review and edit it as needed.
|
|
68
|
+
3. Merging the release PR tags `vX.Y.Z`, creates the GitHub Release, and the
|
|
69
|
+
`Release` workflow builds and publishes to PyPI via Trusted Publishing — all
|
|
70
|
+
in one run.
|
|
71
|
+
|
|
72
|
+
## Code of Conduct
|
|
73
|
+
|
|
74
|
+
By participating in this project you agree to abide by the
|
|
75
|
+
[Code of Conduct](CODE_OF_CONDUCT.md).
|
senderkit-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) SenderKit
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
senderkit-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
.DEFAULT_GOAL := help
|
|
2
|
+
.PHONY: help install lint format format-check typecheck test check build clean
|
|
3
|
+
|
|
4
|
+
help: ## Show this help
|
|
5
|
+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
|
|
6
|
+
awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-13s\033[0m %s\n", $$1, $$2}'
|
|
7
|
+
|
|
8
|
+
install: ## Install the package with dev dependencies
|
|
9
|
+
pip install -e ".[dev]"
|
|
10
|
+
|
|
11
|
+
lint: ## Lint with ruff
|
|
12
|
+
ruff check .
|
|
13
|
+
|
|
14
|
+
format: ## Format with ruff (apply changes)
|
|
15
|
+
ruff format .
|
|
16
|
+
|
|
17
|
+
format-check: ## Check formatting without changing files
|
|
18
|
+
ruff format --check .
|
|
19
|
+
|
|
20
|
+
typecheck: ## Type-check with mypy
|
|
21
|
+
mypy src
|
|
22
|
+
|
|
23
|
+
test: ## Run the test suite with coverage
|
|
24
|
+
pytest --cov=senderkit --cov-report=term-missing
|
|
25
|
+
|
|
26
|
+
check: format-check lint typecheck test ## Run everything CI runs
|
|
27
|
+
|
|
28
|
+
build: ## Build sdist and wheel
|
|
29
|
+
python -m build
|
|
30
|
+
|
|
31
|
+
clean: ## Remove build and cache artifacts
|
|
32
|
+
rm -rf dist build *.egg-info src/*.egg-info .pytest_cache .mypy_cache .ruff_cache .coverage htmlcov
|
|
33
|
+
find . -type d -name __pycache__ -exec rm -rf {} +
|