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.
Files changed (66) hide show
  1. senderkit-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +48 -0
  2. senderkit-0.1.0/.github/ISSUE_TEMPLATE/config.yml +11 -0
  3. senderkit-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +25 -0
  4. senderkit-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +21 -0
  5. senderkit-0.1.0/.github/dependabot.yml +20 -0
  6. senderkit-0.1.0/.github/workflows/ci.yml +71 -0
  7. senderkit-0.1.0/.github/workflows/codeql.yml +30 -0
  8. senderkit-0.1.0/.github/workflows/release.yml +99 -0
  9. senderkit-0.1.0/.gitignore +21 -0
  10. senderkit-0.1.0/.pre-commit-config.yaml +18 -0
  11. senderkit-0.1.0/.release-please-manifest.json +3 -0
  12. senderkit-0.1.0/CHANGELOG.md +21 -0
  13. senderkit-0.1.0/CODE_OF_CONDUCT.md +15 -0
  14. senderkit-0.1.0/CONTRIBUTING.md +75 -0
  15. senderkit-0.1.0/LICENSE +21 -0
  16. senderkit-0.1.0/Makefile +33 -0
  17. senderkit-0.1.0/PKG-INFO +412 -0
  18. senderkit-0.1.0/README.md +366 -0
  19. senderkit-0.1.0/SECURITY.md +29 -0
  20. senderkit-0.1.0/examples/batch.py +23 -0
  21. senderkit-0.1.0/examples/celery_task.py +27 -0
  22. senderkit-0.1.0/examples/django_settings.py +38 -0
  23. senderkit-0.1.0/examples/send.py +16 -0
  24. senderkit-0.1.0/examples/send_async.py +30 -0
  25. senderkit-0.1.0/examples/webhook_fastapi.py +27 -0
  26. senderkit-0.1.0/examples/webhook_flask.py +31 -0
  27. senderkit-0.1.0/pyproject.toml +92 -0
  28. senderkit-0.1.0/release-please-config.json +12 -0
  29. senderkit-0.1.0/src/senderkit/__init__.py +95 -0
  30. senderkit-0.1.0/src/senderkit/_http.py +280 -0
  31. senderkit-0.1.0/src/senderkit/_serialize.py +184 -0
  32. senderkit-0.1.0/src/senderkit/_version.py +3 -0
  33. senderkit-0.1.0/src/senderkit/client.py +305 -0
  34. senderkit-0.1.0/src/senderkit/errors.py +89 -0
  35. senderkit-0.1.0/src/senderkit/integrations/__init__.py +10 -0
  36. senderkit-0.1.0/src/senderkit/integrations/celery.py +56 -0
  37. senderkit-0.1.0/src/senderkit/integrations/django/__init__.py +25 -0
  38. senderkit-0.1.0/src/senderkit/integrations/django/backends.py +108 -0
  39. senderkit-0.1.0/src/senderkit/integrations/django/client.py +43 -0
  40. senderkit-0.1.0/src/senderkit/integrations/django/views.py +55 -0
  41. senderkit-0.1.0/src/senderkit/integrations/fastapi.py +86 -0
  42. senderkit-0.1.0/src/senderkit/integrations/flask.py +107 -0
  43. senderkit-0.1.0/src/senderkit/models.py +335 -0
  44. senderkit-0.1.0/src/senderkit/py.typed +0 -0
  45. senderkit-0.1.0/src/senderkit/resources/__init__.py +6 -0
  46. senderkit-0.1.0/src/senderkit/resources/messages.py +132 -0
  47. senderkit-0.1.0/src/senderkit/resources/templates.py +53 -0
  48. senderkit-0.1.0/src/senderkit/webhooks.py +103 -0
  49. senderkit-0.1.0/tests/__init__.py +0 -0
  50. senderkit-0.1.0/tests/conftest.py +28 -0
  51. senderkit-0.1.0/tests/helpers.py +19 -0
  52. senderkit-0.1.0/tests/integrations/__init__.py +0 -0
  53. senderkit-0.1.0/tests/integrations/test_celery.py +30 -0
  54. senderkit-0.1.0/tests/integrations/test_django_backend.py +130 -0
  55. senderkit-0.1.0/tests/integrations/test_fastapi.py +69 -0
  56. senderkit-0.1.0/tests/integrations/test_flask.py +63 -0
  57. senderkit-0.1.0/tests/test_batch.py +60 -0
  58. senderkit-0.1.0/tests/test_context.py +34 -0
  59. senderkit-0.1.0/tests/test_errors.py +54 -0
  60. senderkit-0.1.0/tests/test_messages.py +97 -0
  61. senderkit-0.1.0/tests/test_retries.py +66 -0
  62. senderkit-0.1.0/tests/test_send.py +67 -0
  63. senderkit-0.1.0/tests/test_send_raw.py +55 -0
  64. senderkit-0.1.0/tests/test_templates.py +73 -0
  65. senderkit-0.1.0/tests/test_timeout.py +21 -0
  66. 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,3 @@
1
+ {
2
+ ".": "0.1.0"
3
+ }
@@ -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).
@@ -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.
@@ -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 {} +