Contributing
This guide summarizes how to set up your environment, validate changes, and submit pull requests. Please review our Code of Conduct and see CONTRIBUTING.md for complete details.
Contributor Guides
Setup
./scripts/setup.sh
Public API stability
The following table describes the stability contract for each sub-package. Only
symbols that appear in a module’s __all__ list are considered part of the public
API.
Package |
Stability |
Notes |
|---|---|---|
|
Stable |
All exports in |
|
Stable |
Pydantic v2 model schemas. Field additions are non-breaking; field removals or type changes are breaking. |
|
Stable |
Exception hierarchy. New sub-classes are non-breaking. |
|
Stable |
Typed resource endpoint classes. Method signatures follow |
|
Stable (exported symbols only) |
|
|
Stable |
Authentication strategy classes. |
|
Stable |
Schema cache and validation helpers. |
|
Semi-stable |
Paginator re-exports from |
|
Internal |
Implementation details. May change without notice. Prefer stable public packages. |
|
Internal |
HTTP execution internals. No stability guarantees. |
|
Unstable |
Test-support utilities. API may change between minor releases. |
|
Stable (CLI commands) |
CLI command-line interface is stable; the Python module internals are not. |
|
Semi-stable |
Export helpers. Functions in |
Deprecation policy
Symbols removed from the public API follow this process:
Issue a
DeprecationWarningviawarnings.warn()withstacklevel=2for at least one minor release before removal.Document the migration path in
CHANGELOG.mdunder a Deprecated heading.Remove the symbol in the next major version bump only.
Internal modules
Modules not listed as Stable above should not be imported directly in
application code. Import from the stable namespaces (e.g. imednet,
imednet.models, imednet.endpoints) instead.
Type aliases
The following type aliases are exported from imednet and imednet.utils for use
in downstream code:
JsonDict–Dict[str, Any]: a generic JSON object.ItemId–str | int: an endpoint item identifier.FilterScalar–str | int | float | bool | None: a single filter value.FilterValue– union ofFilterScalar, operator tuples, and lists: the full filter value accepted bylist()/async_list()endpoint methods.
Issue reporting and triage
The project uses a documented issue operating model so epics, work items, and maintainer triage follow the same rules across the repository.
Use
<type>(<area>): <concise outcome>issue titles.Capture acceptance criteria, test impact, docs impact, and compatibility notes.
Apply the label taxonomy and lifecycle in
issue_management.Follow the intake and rewrite workflow in
triage_playbook.
Validation
Run these commands and ensure ≥90% test coverage before opening a pull request:
poetry run black --check .
poetry run isort --check --profile black .
poetry run ruff check .
poetry run mypy packages/core/src/imednet
poetry run mypy packages/plugins-workflows/src/imednet_workflows
poetry run mypy packages/providers-airflow/src/apache_airflow_providers_imednet
poetry run pytest -q \
--cov=imednet \
--cov=imednet_workflows \
--cov=apache_airflow_providers_imednet \
--cov-fail-under=90
make docs
HTTP transport mocking
Use respx for any test that exercises Client or AsyncClient HTTP behavior.
Do not patch Client._client.request, AsyncClient._client.request, or executor
send callables just to intercept outbound httpx traffic; mock at the transport
layer instead.
When using respx, prefer strict routers so tests cannot leak live requests and stale
routes fail fast:
import httpx
import respx
@respx.mock(assert_all_called=True, assert_all_mocked=True)
def test_retry_and_query_params() -> None:
calls = {"count": 0}
def handler(request: httpx.Request) -> httpx.Response:
calls["count"] += 1
assert request.url.params["page"] == "2"
if calls["count"] == 1:
raise httpx.RequestError("boom", request=request)
return httpx.Response(200, json={"items": []})
route = respx.route(
method="GET",
url__regex=r"https://api\.test/items(?:\?.*)?$",
)
route.mock(side_effect=handler)
This keeps production clients free of test-only wrappers while still validating request construction, retry behavior, dynamic URLs, and query parameters.
Release workflow
Releases are fully automated and driven by merged PR titles:
Ensure the PR title uses a Conventional Commit prefix. Supported prefixes are
feat:,fix:,chore:,docs:,ci:,test:,refactor:,perf:, andrevert:. CI enforces this via theSemantic PR Titlecheck.Run validation locally:
poetry run black --check . poetry run isort --check --profile black . poetry run ruff check . poetry run mypy packages/core/src/imednet poetry run mypy packages/plugins-workflows/src/imednet_workflows poetry run mypy packages/providers-airflow/src/apache_airflow_providers_imednet poetry run pytest -q \ --cov=imednet \ --cov=imednet_workflows \ --cov=apache_airflow_providers_imednet \ --cov-fail-under=90 make docs
Merge to
mainwith Squash and merge so the PR title becomes the merged commit message.The
Automated Releaseworkflow runsrelease-pleasein manifest mode onmainpushes and opens/updates a Release PR with semantic version and changelog updates for the package manifests underpackages/.Maintainers trigger publication by approving and merging the bot-created Release PR.
Configuration requirements:
Package versions in
packages/*/pyproject.tomlmust never be edited manually;release-pleaseupdates it automatically in the generated Release PR changeset.Publishing requires
PYPI_API_TOKENin repository secrets (or migration to PyPI Trusted Publishers/OIDC).Configure branch protection on
mainto require pull request reviews and required checks, includingSemantic PR Title.
Conventions
Apply DRY and SOLID principles.
Limit lines to 100 characters.
Use Conventional Commit prefixes in PR titles.
Pull request process
Include paths changed and validation output in the PR description.
Add or update tests with any code change.
Update docs and examples for public API or CLI changes.