Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@
- name: breaking-change
description: for breaking changes in the changelog.
color: ff0000
- name: security
description: for security fixes and improvements in the changelog.
color: AA1111
- name: community-contribution
description: contributions from the community.
color: 22ee47
- name: hotfix
description: urgent fixes for production issues.
color: ff0000
- name: ignore-for-release
description: PRs you do not want to render in the changelog
color: 7b8eac
Expand Down
3 changes: 3 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ changelog:
- title: ⚠️ Breaking Change
labels:
- breaking-change
- title: 🔒 Security Fixes
labels:
- security
- title: 🐛 Bug Fixes
labels:
- bugfix
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
uses: actions/checkout@v7

- name: setup python 3
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.x'

Expand All @@ -62,7 +62,7 @@ jobs:
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
- uses: actions/setup-python@v7
with:
python-version: ${{ matrix.python-version }}
- name: Run tests
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/e2e-test-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Setup Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.x'

Expand All @@ -112,9 +112,13 @@ jobs:
run: |
timestamp=$(date +'%Y%m%d%H%M')
report_filename="${timestamp}_sdk_test_report.xml"
make test-int RUN_DB_FORK_TESTS=${{ github.event.inputs.run_db_fork_tests }} RUN_DB_TESTS=${{ github.event.inputs.run_db_tests }} RUN_ACLP_LOGS_STREAM_TESTS=${{ github.event.inputs.run_aclp_logs_stream_tests }} TEST_ARGS="--junitxml=${report_filename}" TEST_SUITE="${{ github.event.inputs.test_suite }}"
make test-int RUN_DB_FORK_TESTS="$RUN_DB_FORK_TESTS" RUN_DB_TESTS="$RUN_DB_TESTS" RUN_ACLP_LOGS_STREAM_TESTS="$RUN_ACLP_LOGS_STREAM_TESTS" TEST_ARGS="--junitxml=${report_filename}" TEST_SUITE="$TEST_SUITE"
env:
LINODE_TOKEN: ${{ secrets.LINODE_TOKEN }}
RUN_DB_FORK_TESTS: ${{ github.event.inputs.run_db_fork_tests }}
RUN_DB_TESTS: ${{ github.event.inputs.run_db_tests }}
RUN_ACLP_LOGS_STREAM_TESTS: ${{ github.event.inputs.run_aclp_logs_stream_tests }}
TEST_SUITE: ${{ github.event.inputs.test_suite }}

- name: Upload test results
if: always() && github.repository == 'linode/linode_api4-python' && (github.event_name == 'pull_request' || (github.event_name == 'workflow_dispatch' && inputs.test_report_upload == 'true'))
Expand Down Expand Up @@ -192,7 +196,7 @@ jobs:

steps:
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.x'

Expand Down
16 changes: 10 additions & 6 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ jobs:
submodules: 'recursive'

- name: Setup Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: ${{ inputs.run-eol-python-version == 'true' && env.EOL_PYTHON_VERSION || inputs.python-version || env.DEFAULT_PYTHON_VERSION }}

Expand All @@ -107,9 +107,13 @@ jobs:
run: |
timestamp=$(date +'%Y%m%d%H%M')
report_filename="${timestamp}_sdk_test_report.xml"
make test-int RUN_DB_FORK_TESTS=${{ github.event.inputs.run_db_fork_tests }} RUN_DB_TESTS=${{ github.event.inputs.run_db_tests }} RUN_ACLP_LOGS_STREAM_TESTS=${{ github.event.inputs.run_aclp_logs_stream_tests }} TEST_SUITE="${{ github.event.inputs.test_suite }}" TEST_ARGS="--junitxml=${report_filename}"
make test-int RUN_DB_FORK_TESTS="$RUN_DB_FORK_TESTS" RUN_DB_TESTS="$RUN_DB_TESTS" RUN_ACLP_LOGS_STREAM_TESTS="$RUN_ACLP_LOGS_STREAM_TESTS" TEST_SUITE="$TEST_SUITE" TEST_ARGS="--junitxml=${report_filename}"
env:
LINODE_TOKEN: ${{ env.LINODE_TOKEN }}
RUN_DB_FORK_TESTS: ${{ github.event.inputs.run_db_fork_tests }}
RUN_DB_TESTS: ${{ github.event.inputs.run_db_tests }}
RUN_ACLP_LOGS_STREAM_TESTS: ${{ github.event.inputs.run_aclp_logs_stream_tests }}
TEST_SUITE: ${{ github.event.inputs.test_suite }}

- name: Upload Test Report as Artifact
if: always()
Expand Down Expand Up @@ -157,7 +161,7 @@ jobs:

steps:
- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.x'

Expand Down Expand Up @@ -206,7 +210,7 @@ jobs:
name: test-report-file

- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.x'

Expand Down Expand Up @@ -249,7 +253,7 @@ jobs:
steps:
- name: Notify Slack
id: main_message
uses: slackapi/slack-github-action@v3
uses: slackapi/slack-github-action@v4
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
Expand Down Expand Up @@ -281,7 +285,7 @@ jobs:

- name: Test summary thread
if: success()
uses: slackapi/slack-github-action@v3
uses: slackapi/slack-github-action@v4
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/nightly-smoke-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
ref: dev

- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.x'

Expand All @@ -45,7 +45,7 @@ jobs:

- name: Notify Slack
if: always() && github.repository == 'linode/linode_api4-python'
uses: slackapi/slack-github-action@v3
uses: slackapi/slack-github-action@v4
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/publish-pypi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
uses: actions/checkout@v7

- name: Setup Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.x'

Expand All @@ -28,4 +28,4 @@ jobs:
LINODE_SDK_VERSION: ${{ github.event.release.tag_name }}

- name: Publish the release artifacts to PyPI
uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # pin@release/v1.14.0
uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # pin@release/v1.14.1
2 changes: 1 addition & 1 deletion .github/workflows/release-cross-repo-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
run: sudo apt-get install -y build-essential

- name: Set up Python
uses: actions/setup-python@v6
uses: actions/setup-python@v7
with:
python-version: '3.10'

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-notify-slack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
steps:
- name: Notify Slack - Main Message
id: main_message
uses: slackapi/slack-github-action@v3
uses: slackapi/slack-github-action@v4
with:
method: chat.postMessage
token: ${{ secrets.SLACK_BOT_TOKEN }}
Expand Down
71 changes: 71 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Agent Guide

## Source of Truth

- This is the official synchronous Python SDK for the Linode API. Supported
Python versions come from [pyproject.toml](pyproject.toml) (`>=3.10`); do not
infer support from the older environment names still present in [tox.ini](tox.ini).
- Make changes only in canonical source trees such as `linode_api4/`, `test/`,
`docs/`, `examples/`, and `.github/`. Do not edit generated copies or output in
`build/`, `dist/`, `docs/build/`, `docs/_build/`, or `linode_api4.egg-info/`.
- `linode_api4/version.py` is generated by `make create-version` and `make build`.
Do not hand-edit it for ordinary feature work.
- Use [README.rst](README.rst) for setup and test details and
[CONTRIBUTING.md](CONTRIBUTING.md) for contribution policy. Keep this file
limited to guidance that is easy to miss while navigating the repository.

## Architecture and Implementation

- `linode_api4/linode_client.py` owns HTTP behavior and wires API groups onto
`LinodeClient`; `linode_api4/groups/` exposes operations; `linode_api4/objects/`
defines resource models; `linode_api4/paginated_list.py` handles collection
pagination.
- Follow the nearest existing group method, model, and unit test before adding a
new pattern. Collection methods normally use `client._get_and_filter(...)`;
create/update/delete methods use the client's request helpers and return model
objects where appropriate.
- Models are lazy-loaded, and reading an unknown or stale property may issue an
API request. Read [docs/guides/core_concepts.rst](docs/guides/core_concepts.rst)
before changing model properties, relationships, filtering, or pagination.
- New public models must be exported from `linode_api4/objects/__init__.py`. New
groups must be exported from `linode_api4/groups/__init__.py` and wired into
`LinodeClient`. Preserve public API compatibility unless the task explicitly
requires a breaking change.
- Public behavior belongs in Sphinx-compatible docstrings and, when needed, the
source files under `docs/`. Never edit rendered documentation.

## Tests

- Start with the narrowest relevant unit test, for example
`python -m pytest test/unit/objects/linode_test.py -k test_name`. Run
`make test-unit` for the full mocked unit suite.
- Unit tests normally extend `test.unit.base.ClientBaseCase`. GET requests are
resolved from JSON under `test/fixtures/`; use `mock_post`, `mock_put`, and
`mock_delete` for other verbs and assert the captured URL, body, or headers.
- Fixture names encode endpoint paths: a single underscore becomes `/`, while a
doubled underscore becomes a literal `_`. Paginated fixtures containing
`results` also provide per-ID responses automatically when items include an
`id` field. See
`test/unit/fixtures.py` before inventing custom request mocking.
- `make test-int` and `make test-smoke` call the live Linode API, require
`LINODE_TOKEN`, and may create or delete real resources. Run them only when the
change requires live validation and the environment is intentionally configured.

## Formatting and Validation

- Install development dependencies with `make dev-install`.
- Formatting is Black + isort + autoflake with settings in [pyproject.toml](pyproject.toml).
`make format` rewrites all of `linode_api4/` and `test/`, so review the worktree
before and after using it for a focused change.
- `make lint` is the broad CI-equivalent check: it builds distributions, checks
formatting/imports, runs pylint, and validates package metadata. Prefer focused
tests first because this target regenerates build artifacts and version metadata.

## Pull Requests

- Target the `dev` branch unless the task specifies otherwise.
- PR titles must use `TPT-1234: Description`; CI exempts only the labels documented
in [.github/workflows/ci.yml](.github/workflows/ci.yml).
- Keep changes focused, add tests for behavior changes, and use
[.github/pull_request_template.md](.github/pull_request_template.md) when drafting
the PR description.
1 change: 1 addition & 0 deletions test/integration/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ def test_linode_client():
token,
base_url=api_url,
ca_path=api_ca_file,
retry_statuses=[504],
)
return client

Expand Down
16 changes: 14 additions & 2 deletions test/integration/models/linode/test_linode.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
Type,
)
from linode_api4.objects.linode import InstanceDiskEncryptionType, MigrationType
from linode_api4.objects.region import RegionAvailabilityEntry


@pytest.fixture(scope="session")
Expand Down Expand Up @@ -119,12 +120,23 @@ def linode_and_vpc_for_legacy_interface_tests_offline(
@pytest.fixture(scope="session")
def linode_for_vpu_tests(test_linode_client, e2e_test_firewall):
client = test_linode_client
region = "us-lax"
vpu_type = "g1-accelerated-netint-vpu-t1u1-s"

availability = client.regions.availability(
RegionAvailabilityEntry.filters.plan == vpu_type
)

region = next(
(entry.region for entry in availability if entry.available), None
)

if region is None:
pytest.skip("No VPU capacity is currently available")

label = get_test_label(length=8)

linode_instance = client.linode.instance_create(
"g1-accelerated-netint-vpu-t1u1-s",
vpu_type,
region,
image="linode/debian12",
label=label,
Expand Down
Loading