mailkube 1.0.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 (57) hide show
  1. mailkube-1.0.0/.editorconfig +18 -0
  2. mailkube-1.0.0/.github/CODEOWNERS +3 -0
  3. mailkube-1.0.0/.github/ISSUE_TEMPLATE/bug_report.yml +52 -0
  4. mailkube-1.0.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
  5. mailkube-1.0.0/.github/ISSUE_TEMPLATE/feature_request.yml +32 -0
  6. mailkube-1.0.0/.github/PULL_REQUEST_TEMPLATE.md +32 -0
  7. mailkube-1.0.0/.github/dependabot.yml +22 -0
  8. mailkube-1.0.0/.github/workflows/ci.yml +60 -0
  9. mailkube-1.0.0/.github/workflows/pr-title.yml +20 -0
  10. mailkube-1.0.0/.github/workflows/release.yml +60 -0
  11. mailkube-1.0.0/.gitignore +25 -0
  12. mailkube-1.0.0/.jscpd.json +19 -0
  13. mailkube-1.0.0/.pre-commit-config.yaml +36 -0
  14. mailkube-1.0.0/.rules/RELEASE.md +26 -0
  15. mailkube-1.0.0/.rules/SOLID_DRY_KISS.md +43 -0
  16. mailkube-1.0.0/CHANGELOG.md +7 -0
  17. mailkube-1.0.0/CLAUDE.md +30 -0
  18. mailkube-1.0.0/CODE_OF_CONDUCT.md +133 -0
  19. mailkube-1.0.0/CONTRIBUTING.md +55 -0
  20. mailkube-1.0.0/LICENSE +201 -0
  21. mailkube-1.0.0/NOTICE +11 -0
  22. mailkube-1.0.0/PKG-INFO +134 -0
  23. mailkube-1.0.0/README.md +114 -0
  24. mailkube-1.0.0/SECURITY.md +40 -0
  25. mailkube-1.0.0/commitlint.config.js +5 -0
  26. mailkube-1.0.0/examples/async_send.py +35 -0
  27. mailkube-1.0.0/examples/send_with_attachments.py +26 -0
  28. mailkube-1.0.0/examples/send_with_template.py +17 -0
  29. mailkube-1.0.0/examples/simple_send.py +18 -0
  30. mailkube-1.0.0/examples/webhook_receiver_flask.py +30 -0
  31. mailkube-1.0.0/pyproject.toml +97 -0
  32. mailkube-1.0.0/scripts/check-rule-index.sh +36 -0
  33. mailkube-1.0.0/src/mailkube/__init__.py +88 -0
  34. mailkube-1.0.0/src/mailkube/_async_client.py +87 -0
  35. mailkube-1.0.0/src/mailkube/_base_client.py +183 -0
  36. mailkube-1.0.0/src/mailkube/_client.py +88 -0
  37. mailkube-1.0.0/src/mailkube/_exceptions.py +125 -0
  38. mailkube-1.0.0/src/mailkube/_logging.py +62 -0
  39. mailkube-1.0.0/src/mailkube/_transport.py +46 -0
  40. mailkube-1.0.0/src/mailkube/_version.py +9 -0
  41. mailkube-1.0.0/src/mailkube/py.typed +0 -0
  42. mailkube-1.0.0/src/mailkube/resources/__init__.py +7 -0
  43. mailkube-1.0.0/src/mailkube/resources/emails.py +62 -0
  44. mailkube-1.0.0/src/mailkube/types/__init__.py +62 -0
  45. mailkube-1.0.0/src/mailkube/types/events.py +253 -0
  46. mailkube-1.0.0/src/mailkube/types/params.py +89 -0
  47. mailkube-1.0.0/src/mailkube/types/responses.py +31 -0
  48. mailkube-1.0.0/src/mailkube/webhooks.py +141 -0
  49. mailkube-1.0.0/tests/conftest.py +29 -0
  50. mailkube-1.0.0/tests/test_async_client.py +75 -0
  51. mailkube-1.0.0/tests/test_client.py +166 -0
  52. mailkube-1.0.0/tests/test_exceptions.py +54 -0
  53. mailkube-1.0.0/tests/test_logging.py +29 -0
  54. mailkube-1.0.0/tests/test_package.py +22 -0
  55. mailkube-1.0.0/tests/test_serialization.py +55 -0
  56. mailkube-1.0.0/tests/test_webhooks.py +206 -0
  57. mailkube-1.0.0/uv.lock +561 -0
@@ -0,0 +1,18 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ trim_trailing_whitespace = true
8
+ indent_style = space
9
+ indent_size = 4
10
+
11
+ [*.py]
12
+ max_line_length = 120
13
+
14
+ [*.{yml,yaml,json,toml,md,js}]
15
+ indent_size = 2
16
+
17
+ [Makefile]
18
+ indent_style = tab
@@ -0,0 +1,3 @@
1
+ # Default owners for everything in this repo.
2
+ # Update the team/handle below to your actual maintainers before publishing.
3
+ * @mailkube/maintainers
@@ -0,0 +1,52 @@
1
+ name: 🐞 Bug report
2
+ description: Report something that isn't working as expected.
3
+ labels: ["bug"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Thanks for taking the time to file a bug! For **security vulnerabilities**, do not use
9
+ this form — follow our [Security Policy](https://github.com/mailkube/mailkube/security/policy).
10
+ - type: textarea
11
+ id: what-happened
12
+ attributes:
13
+ label: What happened?
14
+ description: A clear description of the bug, including what you expected instead.
15
+ validations:
16
+ required: true
17
+ - type: textarea
18
+ id: repro
19
+ attributes:
20
+ label: Steps to reproduce
21
+ description: Minimal steps (and the code you ran) to reproduce the behavior.
22
+ validations:
23
+ required: true
24
+ - type: textarea
25
+ id: logs
26
+ attributes:
27
+ label: Console output / logs
28
+ description: Paste relevant output. This is automatically formatted as code.
29
+ render: shell
30
+ - type: input
31
+ id: version
32
+ attributes:
33
+ label: "mailkube version"
34
+ placeholder: "1.2.3 (or git commit)"
35
+ validations:
36
+ required: true
37
+ - type: input
38
+ id: runtime
39
+ attributes:
40
+ label: Runtime version & OS
41
+ placeholder: "e.g. Python 3.12 on macOS 14"
42
+ validations:
43
+ required: true
44
+ - type: checkboxes
45
+ id: checks
46
+ attributes:
47
+ label: Pre-flight
48
+ options:
49
+ - label: I searched existing issues and this is not a duplicate.
50
+ required: true
51
+ - label: This is not a security vulnerability (those go through the Security Policy).
52
+ required: true
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: 🔐 Report a security vulnerability
4
+ url: https://github.com/mailkube/mailkube/security/advisories/new
5
+ about: Please report vulnerabilities privately — do not open a public issue.
6
+ - name: 💬 mailkube product support
7
+ url: https://support.mailkube.com
8
+ about: Questions about the mailkube product/API (not this library) belong here.
@@ -0,0 +1,32 @@
1
+ name: 💡 Feature request
2
+ description: Suggest an improvement or new capability.
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: What problem are you trying to solve?
9
+ description: Describe the use case or pain point, not just the proposed solution.
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: proposal
14
+ attributes:
15
+ label: Proposed solution
16
+ description: What would you like mailkube to do?
17
+ validations:
18
+ required: true
19
+ - type: textarea
20
+ id: alternatives
21
+ attributes:
22
+ label: Alternatives considered
23
+ description: Other approaches or workarounds you've tried or thought about.
24
+ - type: checkboxes
25
+ id: checks
26
+ attributes:
27
+ label: Pre-flight
28
+ options:
29
+ - label: I searched existing issues and this is not a duplicate.
30
+ required: true
31
+ - label: This fits the scope of this library (not the mailkube product itself).
32
+ required: true
@@ -0,0 +1,32 @@
1
+ <!--
2
+ PR titles MUST follow Conventional Commits (e.g. `fix(client): ...`) — it is CI-enforced and
3
+ becomes the squash-merge commit message. Only feat/fix/perf trigger a release.
4
+ -->
5
+
6
+ ## What
7
+
8
+ <!-- Describe the change in 1–2 sentences. -->
9
+
10
+ ## Why
11
+
12
+ <!-- The user-visible problem this solves, or the motivation. -->
13
+
14
+ ## Quality checklist
15
+
16
+ - [ ] `uv run ruff check .` and `uv run ruff format --check .` pass
17
+ - [ ] `uv run mypy src` passes
18
+ - [ ] `uv run pytest` passes (coverage ≥ 90% line + branch)
19
+ - [ ] `npx jscpd --config .jscpd.json .` clean (no new duplication)
20
+ - [ ] Docs updated (`README.md`) if user-visible
21
+
22
+ ## Engineering standards (SOLID / DRY / KISS)
23
+
24
+ - [ ] Single-responsibility: new/changed units do one thing; no god-functions
25
+ - [ ] No duplication introduced; shared logic extracted (DRY)
26
+ - [ ] Public APIs documented (docstrings — doc-lint passes)
27
+ - [ ] Complexity within limit (no lint-complexity waivers added)
28
+ - [ ] Depends on abstractions/interfaces where a boundary is crossed (DIP)
29
+
30
+ ## Notes
31
+
32
+ <!-- Optional: screenshots, follow-ups, breaking-change details. -->
@@ -0,0 +1,22 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "uv"
4
+ directory: "/"
5
+ target-branch: "develop"
6
+ schedule:
7
+ interval: "weekly"
8
+ commit-message:
9
+ prefix: "chore"
10
+ include: "scope"
11
+ labels:
12
+ - "dependencies"
13
+
14
+ - package-ecosystem: "github-actions"
15
+ directory: "/"
16
+ target-branch: "develop"
17
+ schedule:
18
+ interval: "weekly"
19
+ commit-message:
20
+ prefix: "ci"
21
+ labels:
22
+ - "dependencies"
@@ -0,0 +1,60 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_call: # so release.yml can gate on the full CI
8
+
9
+ concurrency:
10
+ group: ci-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ test:
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ python-version: ["3.12", "3.13", "3.14"]
20
+ steps:
21
+ - uses: actions/checkout@v7
22
+
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v7
25
+ with:
26
+ enable-cache: true
27
+
28
+ - name: Install dependencies
29
+ run: uv sync --frozen --python ${{ matrix.python-version }}
30
+
31
+ - name: Lint (ruff)
32
+ run: uv run ruff check .
33
+
34
+ - name: Format check (ruff)
35
+ run: uv run ruff format --check .
36
+
37
+ - name: Type check (mypy)
38
+ run: uv run mypy src
39
+
40
+ - name: Tests + coverage gate (pytest, 90% line+branch)
41
+ run: uv run pytest
42
+
43
+ dry:
44
+ # Language-agnostic duplication (DRY) gate — identical across every mailkube repo.
45
+ runs-on: ubuntu-latest
46
+ steps:
47
+ - uses: actions/checkout@v7
48
+ - uses: actions/setup-node@v7
49
+ with:
50
+ node-version: "20"
51
+ - name: Copy-paste detection (jscpd)
52
+ run: npx --yes jscpd@4 --config .jscpd.json .
53
+
54
+ docs:
55
+ # Governance gate: every .rules/*.md must be indexed in CLAUDE.md.
56
+ runs-on: ubuntu-latest
57
+ steps:
58
+ - uses: actions/checkout@v7
59
+ - name: Rule-index sync check
60
+ run: ./scripts/check-rule-index.sh
@@ -0,0 +1,20 @@
1
+ name: PR Title
2
+
3
+ # Enforce Conventional Commits on the PR title, since PRs are squash-merged using it.
4
+ # Only `feat:`, `fix:`, and `perf:` trigger a release on `main`.
5
+ # NOTE: rendered verbatim (see `_copy_without_render` in cookiecutter.json) so GitHub Actions
6
+ # `${{ ... }}` expressions are not mangled by Cookiecutter's Jinja pass.
7
+ on:
8
+ pull_request_target:
9
+ types: [opened, edited, synchronize, reopened]
10
+
11
+ permissions:
12
+ pull-requests: read
13
+
14
+ jobs:
15
+ conventional-title:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: amannn/action-semantic-pull-request@v6
19
+ env:
20
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,60 @@
1
+ name: Release
2
+
3
+ # Conventional-Commits-driven release: version bump + changelog + GitHub Release + tag,
4
+ # then publish to PyPI via OIDC Trusted Publishing (no long-lived tokens).
5
+ on:
6
+ push:
7
+ branches: [main]
8
+
9
+ concurrency:
10
+ group: release-${{ github.ref }}
11
+ cancel-in-progress: false
12
+
13
+ jobs:
14
+ ci-gate:
15
+ # Release only proceeds if the full gated CI (test + dry + docs) is green.
16
+ uses: ./.github/workflows/ci.yml
17
+
18
+ release:
19
+ needs: ci-gate
20
+ runs-on: ubuntu-latest
21
+ environment: release
22
+ permissions:
23
+ contents: write # push version bump, tag, create GitHub Release
24
+ outputs:
25
+ released: ${{ steps.release.outputs.released }}
26
+ version: ${{ steps.release.outputs.version }}
27
+ steps:
28
+ - uses: actions/checkout@v7
29
+ with:
30
+ fetch-depth: 0
31
+ token: ${{ secrets.GITHUB_TOKEN }}
32
+
33
+ - name: Compute release (python-semantic-release)
34
+ id: release
35
+ uses: python-semantic-release/python-semantic-release@v10
36
+ with:
37
+ github_token: ${{ secrets.GITHUB_TOKEN }}
38
+
39
+ publish:
40
+ needs: release
41
+ if: needs.release.outputs.released == 'true'
42
+ runs-on: ubuntu-latest
43
+ environment: pypi
44
+ permissions:
45
+ id-token: write # OIDC for PyPI Trusted Publishing — no password/token stored
46
+ steps:
47
+ - uses: actions/checkout@v7
48
+ with:
49
+ ref: v${{ needs.release.outputs.version }}
50
+
51
+ - name: Install uv
52
+ uses: astral-sh/setup-uv@v7
53
+
54
+ - name: Build distribution
55
+ run: uv build
56
+
57
+ - name: Publish to PyPI (OIDC Trusted Publishing)
58
+ uses: pypa/gh-action-pypi-publish@release/v1
59
+ # No `password:` — relies purely on OIDC. Register the Trusted Publisher on PyPI first:
60
+ # org=<github-org>, repo=<repo-slug>, workflow=release.yml, environment=pypi.
@@ -0,0 +1,25 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+
7
+ # Virtual envs
8
+ .venv/
9
+ venv/
10
+
11
+ # Build artifacts
12
+ build/
13
+ dist/
14
+
15
+ # Tooling caches
16
+ .ruff_cache/
17
+ .mypy_cache/
18
+ .pytest_cache/
19
+ .jscpd/
20
+ .coverage
21
+ htmlcov/
22
+ coverage.xml
23
+
24
+ # Secrets / local config
25
+ .env
@@ -0,0 +1,19 @@
1
+ {
2
+ "threshold": 1,
3
+ "minTokens": 50,
4
+ "reporters": ["console"],
5
+ "absolute": true,
6
+ "gitignore": true,
7
+ "failOnDuplication": true,
8
+ "ignore": [
9
+ "**/tests/**",
10
+ "**/test/**",
11
+ "**/examples/**",
12
+ "**/*.lock",
13
+ "**/*.lock.json",
14
+ "**/CHANGELOG.md",
15
+ "**/.github/**",
16
+ "**/dist/**",
17
+ "**/build/**"
18
+ ]
19
+ }
@@ -0,0 +1,36 @@
1
+ # Run `uv run pre-commit install && uv run pre-commit install --hook-type commit-msg` after cloning.
2
+ repos:
3
+ - repo: https://github.com/pre-commit/pre-commit-hooks
4
+ rev: v5.0.0
5
+ hooks:
6
+ - id: end-of-file-fixer
7
+ - id: trailing-whitespace
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.8.4
15
+ hooks:
16
+ - id: ruff
17
+ args: [--fix]
18
+ - id: ruff-format
19
+
20
+ # Language-agnostic DRY gate — identical config in every mailkube repo.
21
+ - repo: local
22
+ hooks:
23
+ - id: jscpd
24
+ name: jscpd (copy-paste detector)
25
+ entry: npx --yes jscpd@4 --config .jscpd.json .
26
+ language: system
27
+ pass_filenames: false
28
+
29
+ # Enforce Conventional Commits on the commit message (matches the org house style).
30
+ - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
31
+ rev: v9.22.0
32
+ hooks:
33
+ - id: commitlint
34
+ stages: [commit-msg]
35
+ additional_dependencies:
36
+ - "@commitlint/config-conventional"
@@ -0,0 +1,26 @@
1
+ # Release & Publishing
2
+
3
+ Load this when touching `release.yml`, `[tool.semantic_release]`, versioning, or PyPI publishing.
4
+
5
+ ## The contract
6
+
7
+ 1. **Conventional Commits drive the version.** On push to `main`, `python-semantic-release` reads the
8
+ commit history since the last tag: `fix:` → patch, `feat:` → minor, `feat!:`/`BREAKING CHANGE:` → major.
9
+ `perf:` also releases. Anything else (`chore`, `docs`, `ci`, `refactor`, `test`) does **not** release.
10
+ 2. **It bumps `project.version` in `pyproject.toml`**, regenerates `CHANGELOG.md`, commits, tags `vX.Y.Z`,
11
+ and creates a GitHub Release. `major_on_zero = false`, so `0.x` stays in `0.x` on `feat:`.
12
+ 3. **Publishing is OIDC-only.** The `publish` job builds with `uv build` and uploads via
13
+ `pypa/gh-action-pypi-publish` using GitHub's OIDC token — **no PyPI token is stored anywhere**.
14
+
15
+ ## Required GitHub / PyPI setup (one-time, per repo)
16
+
17
+ - GitHub **environments** `release` and `pypi` must exist (Settings → Environments), with protection rules.
18
+ - A **PyPI Trusted Publisher** must be registered for this project pointing at:
19
+ org = this GitHub org, repo = this repo, workflow = `release.yml`, environment = `pypi`.
20
+ For a brand-new package name, use PyPI's **pending publisher** flow (the project need not exist yet).
21
+
22
+ ## Do not
23
+
24
+ - Do not hand-edit `project.version` or `CHANGELOG.md` — semantic-release owns them.
25
+ - Do not add a `password:`/token to the publish step — that defeats OIDC and reintroduces a secret.
26
+ - Do not gate `release.yml` on anything weaker than the full `ci.yml` (`test` + `dry` + `docs`).
@@ -0,0 +1,43 @@
1
+ # Engineering Standards: SOLID · DRY · KISS · Coverage · Docs
2
+
3
+ These are **enforced by CI** — a PR that violates them cannot merge. This file tells you the exact
4
+ thresholds and how to satisfy each gate locally *before* pushing.
5
+
6
+ ## The five pillars
7
+
8
+ | Pillar | Rule | Enforced by |
9
+ |---|---|---|
10
+ | **Coverage** | ≥ 90% **line and branch** | `pytest --cov-branch --cov-fail-under=90` (the `test` CI job) |
11
+ | **DRY** | ≤ 1% duplicated code | `jscpd` (the `dry` CI job) |
12
+ | **KISS** | cyclomatic complexity ≤ 10 per unit | ruff `C901` (the `test` CI job) |
13
+ | **Documentation** | every public module/class/function has a docstring | ruff `D` / google convention |
14
+ | **SOLID** | see below — approximated by lint + review | ruff `PL`/`SIM`/`ARG`/`B` + PR checklist |
15
+
16
+ ## Run the gates locally
17
+
18
+ ```bash
19
+ uv run ruff check . # lint incl. C901 (complexity), D (docstrings), PL/SIM/ARG (SOLID smells)
20
+ uv run ruff format --check . # formatting
21
+ uv run mypy src # strict types
22
+ uv run pytest # tests + 90% line+branch coverage gate
23
+ npx --yes jscpd@4 --config .jscpd.json . # duplication (DRY) gate
24
+ ./scripts/check-rule-index.sh # every .rules/*.md indexed in CLAUDE.md
25
+ ```
26
+
27
+ `uv run pre-commit run --all-files` runs the ruff + jscpd + commitlint hooks in one shot.
28
+
29
+ ## SOLID, concretely (paradigm-neutral guidance)
30
+
31
+ SOLID is not a single lint rule; keep these in mind and confirm them in the PR checklist:
32
+
33
+ - **S**ingle responsibility — a function/class does one thing; if you need "and" to describe it, split it.
34
+ - **O**pen/closed — extend via new functions/subclasses/strategies, not by editing stable call sites.
35
+ - **L**iskov — subtypes honor their base's contract (types, exceptions, invariants).
36
+ - **I**nterface segregation — small, focused protocols; unused parameters (ruff `ARG`) are a smell.
37
+ - **D**ependency inversion — depend on an abstraction/`Protocol` at I/O and network boundaries, inject it.
38
+
39
+ ## Requesting a waiver
40
+
41
+ If a threshold is genuinely wrong for a specific line, add a **scoped, commented** ignore
42
+ (e.g. `# noqa: C901 # parser dispatch, intentionally flat`) and call it out in the PR. Blanket
43
+ config relaxations (lowering `fail_under`, disabling a rule globally) require maintainer sign-off.
@@ -0,0 +1,7 @@
1
+ # CHANGELOG
2
+
3
+ <!-- version list -->
4
+
5
+ ## v1.0.0 (2026-07-14)
6
+
7
+ - Initial Release
@@ -0,0 +1,30 @@
1
+ # Project Rules
2
+
3
+ `mailkube` is a public (Apache-2.0) mailkube SDK published to PyPI.
4
+ Load the relevant rule file from `.rules/` based on the task.
5
+
6
+ ## Rule Index
7
+
8
+ > **Index every rule (required).** Every file in `.rules/` MUST have a row in the table below. When you
9
+ > add or rename a `.rules/` file, add or update its row in the **same change** — an unindexed rule is
10
+ > invisible, because this index is what drives progressive disclosure. The `docs` CI job (`scripts/check-rule-index.sh`)
11
+ > fails the build if `.rules/` and this index drift. This convention holds for every mailkube repo.
12
+
13
+ | Rule File | Load When |
14
+ |---|---|
15
+ | `.rules/SOLID_DRY_KISS.md` | Writing or changing any code — the enforced engineering standards (SOLID, DRY, KISS, coverage, docs) and how to run each gate locally. |
16
+ | `.rules/RELEASE.md` | Touching `release.yml`, `[tool.semantic_release]`, versioning, or the PyPI OIDC publish flow. |
17
+
18
+ ## Key Conventions (always apply)
19
+
20
+ - **Tooling is `uv`**: `uv sync`, `uv run …`; deps in PEP 621 `[project]` + `[dependency-groups]`; `uv.lock` committed.
21
+ - **`src/` layout** — code lives in `src/mailkube/`; tests in `tests/`.
22
+ - **Ruff** for lint **and** format; **line length ≤ 120**; **mypy strict** on `src`.
23
+ - **Type-annotate** every function; `from __future__ import annotations` at the top of modules.
24
+ - **≥ 90% coverage, line + branch** — enforced by `--cov-fail-under=90`; never lower the gate to make a change pass.
25
+ - **Max cyclomatic complexity 10** (ruff `C901`) — split, don't waive.
26
+ - **Every public module/class/function has a docstring** (ruff `D`, google convention).
27
+ - **No duplication** — the `jscpd` gate blocks at > 1% duplicated code; extract shared logic.
28
+ - **Conventional Commits** for PR titles (squash-merged); only `feat:`/`fix:`/`perf:` cut a release.
29
+ - **No secrets in the repo** — local config lives in a git-ignored `.env`, excluded from the built package.
30
+ - **Keep `README` / `CHANGELOG` current** with user-visible changes (the changelog is generated on release).
@@ -0,0 +1,133 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, caste, color, religion, or sexual
10
+ identity and orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment for our
18
+ community include:
19
+
20
+ - Demonstrating empathy and kindness toward other people
21
+ - Being respectful of differing opinions, viewpoints, and experiences
22
+ - Giving and gracefully accepting constructive feedback
23
+ - Accepting responsibility and apologizing to those affected by our mistakes,
24
+ and learning from the experience
25
+ - Focusing on what is best not just for us as individuals, but for the overall
26
+ community
27
+
28
+ Examples of unacceptable behavior include:
29
+
30
+ - The use of sexualized language or imagery, and sexual attention or advances of
31
+ any kind
32
+ - Trolling, insulting or derogatory comments, and personal or political attacks
33
+ - Public or private harassment
34
+ - Publishing others' private information, such as a physical or email address,
35
+ without their explicit permission
36
+ - Other conduct which could reasonably be considered inappropriate in a
37
+ professional setting
38
+
39
+ ## Enforcement Responsibilities
40
+
41
+ Community leaders are responsible for clarifying and enforcing our standards of
42
+ acceptable behavior and will take appropriate and fair corrective action in
43
+ response to any behavior that they deem inappropriate, threatening, offensive,
44
+ or harmful.
45
+
46
+ Community leaders have the right and responsibility to remove, edit, or reject
47
+ comments, commits, code, wiki edits, issues, and other contributions that are
48
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
49
+ decisions when appropriate.
50
+
51
+ ## Scope
52
+
53
+ This Code of Conduct applies within all community spaces, and also applies when
54
+ an individual is officially representing the community in public spaces.
55
+ Examples of representing our community include using an official email address,
56
+ posting via an official social media account, or acting as an appointed
57
+ representative at an online or offline event.
58
+
59
+ ## Enforcement
60
+
61
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
+ reported to the community leaders responsible for enforcement at
63
+ **conduct@opensource.mailkube.com**.
64
+
65
+ All complaints will be reviewed and investigated promptly and fairly.
66
+
67
+ All community leaders are obligated to respect the privacy and security of the
68
+ reporter of any incident.
69
+
70
+ ## Enforcement Guidelines
71
+
72
+ Community leaders will follow these Community Impact Guidelines in determining
73
+ the consequences for any action they deem in violation of this Code of Conduct:
74
+
75
+ ### 1. Correction
76
+
77
+ **Community Impact**: Use of inappropriate language or other behavior deemed
78
+ unprofessional or unwelcome in the community.
79
+
80
+ **Consequence**: A private, written warning from community leaders, providing
81
+ clarity around the nature of the violation and an explanation of why the
82
+ behavior was inappropriate. A public apology may be requested.
83
+
84
+ ### 2. Warning
85
+
86
+ **Community Impact**: A violation through a single incident or series of
87
+ actions.
88
+
89
+ **Consequence**: A warning with consequences for continued behavior. No
90
+ interaction with the people involved, including unsolicited interaction with
91
+ those enforcing the Code of Conduct, for a specified period of time. This
92
+ includes avoiding interactions in community spaces as well as external channels
93
+ like social media. Violating these terms may lead to a temporary or permanent
94
+ ban.
95
+
96
+ ### 3. Temporary Ban
97
+
98
+ **Community Impact**: A serious violation of community standards, including
99
+ sustained inappropriate behavior.
100
+
101
+ **Consequence**: A temporary ban from any sort of interaction or public
102
+ communication with the community for a specified period of time. No public or
103
+ private interaction with the people involved, including unsolicited interaction
104
+ with those enforcing the Code of Conduct, is allowed during this period.
105
+ Violating these terms may lead to a permanent ban.
106
+
107
+ ### 4. Permanent Ban
108
+
109
+ **Community Impact**: Demonstrating a pattern of violation of community
110
+ standards, including sustained inappropriate behavior, harassment of an
111
+ individual, or aggression toward or disparagement of classes of individuals.
112
+
113
+ **Consequence**: A permanent ban from any sort of public interaction within the
114
+ community.
115
+
116
+ ## Attribution
117
+
118
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
119
+ version 2.1, available at
120
+ [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
121
+
122
+ Community Impact Guidelines were inspired by
123
+ [Mozilla's code of conduct enforcement ladder][mozilla].
124
+
125
+ For answers to common questions about this code of conduct, see the FAQ at
126
+ [https://www.contributor-covenant.org/faq][faq]. Translations are available at
127
+ [https://www.contributor-covenant.org/translations][translations].
128
+
129
+ [homepage]: https://www.contributor-covenant.org
130
+ [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
131
+ [mozilla]: https://github.com/mozilla/diversity
132
+ [faq]: https://www.contributor-covenant.org/faq
133
+ [translations]: https://www.contributor-covenant.org/translations