Thanks for your interest in contributing to comlink-python! This guide covers everything you need to get started — from setting up a local dev environment to getting your pull request merged.
- Code of Conduct
- Getting Started
- Project Structure
- Making Changes
- Submitting a Pull Request
- Issue Guidelines
- Release Process
- Getting Help
Be respectful, constructive, and patient. We're all here because we enjoy the game and want to build useful tools for the community. Harassment, insults, and unconstructive negativity won't be tolerated.
- Python 3.10+ (3.11 or 3.12 recommended)
- uv — used for dependency management and virtual environments
- Git
- Docker (optional) — for running a local swgoh-comlink service for integration tests
- GitHub CLI : https://cli.github.com/
-
Fork and clone the repository
gh auth login gh repo fork swgoh-utils/comlink-python
git clone https://github.com/<your-username>/comlink-python.git cd comlink-python
git remote add upstream https://github.com/swgoh-utils/comlink-python.git git config --local branch.main.remote upstream git remote set-url --push upstream github@github.com:<your-username>/comlink-python.git
-
Install uv (if you don't have it)
# macOS / Linux curl -LsSf https://astral.sh/uv/install.sh | sh # Windows powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
-
Create a virtual environment and install dependencies
uv venv source .venv/bin/activate # Linux/macOS # .venv\Scripts\activate # Windows uv sync --group dev
-
Verify the installation
uv run python -c "from swgoh_comlink import SwgohComlink; print('OK')"
Some tests (integration tests) require a running swgoh-comlink instance. The easiest way is Docker:
docker run -d --name swgoh-comlink -p 3000:3000 ghcr.io/swgoh-utils/swgoh-comlink:latestThis starts comlink on http://localhost:3000, which is the default URL the library connects to.
Note: Unit tests should use mocks and never require a running comlink service. See Writing Tests below.
comlink-python/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml # Bug report template
│ │ └── feature_request.yml # Feature request template
│ ├── workflows/
│ │ ├── ci.yml # CI pipeline (lint, type-check, test, docs, build)
│ │ ├── commitlint.yml # Commit message validation (npm-based)
│ │ ├── integration.yml # Integration tests against live comlink services
│ │ ├── labeler.yml # Auto-label PRs by file path
│ │ ├── release.yml # Tag-driven release (build → PyPI → tag) via hatch-vcs
│ │ └── test.yml # Quick test + docs build
│ ├── CODEOWNERS # Code ownership
│ ├── dependabot.yml # Automated dependency updates
│ ├── labeler.yml # Label-to-path configuration
│ └── pull_request_template.md # PR checklist template
├── docs/
│ ├── api/
│ │ ├── async.md # SwgohComlinkAsync API reference
│ │ ├── comlink.md # SwgohComlink API reference
│ │ ├── exceptions.md # Exception classes reference
│ │ ├── helpers.md # Helper functions reference
│ │ └── statcalc.md # StatCalc API reference
│ ├── index.md # Documentation home
│ ├── logging.md # Logging configuration guide
│ └── migration.md # v1.x → v2.x migration guide
├── examples/ # Usage examples for each endpoint
├── src/
│ └── swgoh_comlink/
│ ├── StatCalc/
│ │ ├── data_builder/
│ │ │ ├── _builder_base.py # Shared game data transformation logic
│ │ │ ├── builder.py # Sync GameDataBuilder
│ │ │ └── builder_async.py # Async GameDataBuilderAsync
│ │ ├── __init__.py # StatCalc package exports
│ │ ├── calculator.py # Local stat/GP calculator (sync)
│ │ └── calculator_async.py # Async StatCalcAsync
│ ├── helpers/
│ │ ├── __init__.py # Helpers package exports
│ │ ├── _arena.py # Arena payout time calculations
│ │ ├── _constants.py # Game constants and enum lookups
│ │ ├── _data_items.py # DataItems IntFlag for game data collections
│ │ ├── _decorators.py # Timing and debug logging decorators
│ │ ├── _gac.py # Grand Arena Championship bracket helpers
│ │ ├── _game_data.py # Raid IDs, datacrons, localization, units
│ │ ├── _guild.py # Guild member extraction helpers
│ │ ├── _localization.py # SWGOH string parsing and localization utilities
│ │ ├── _omicron.py # Omicron ability detection
│ │ ├── _sentinels.py # Sentinel values (REQUIRED, MISSING)
│ │ ├── _stat_data.py # Stat name/ID mapping tables
│ │ └── _utils.py # Enum lookup, file I/O, path utilities
│ ├── migrate/
│ │ ├── __init__.py # Migration tool entry point
│ │ └── ... # v1 → v2 migration scanner and rules
│ ├── __init__.py # Package entry point, public exports
│ ├── _base.py # SwgohComlinkBase (shared sync/async logic)
│ ├── exceptions.py # Custom exception classes
│ ├── globals.py # Logging configuration
│ ├── swgoh_comlink.py # SwgohComlink (sync client)
│ ├── swgoh_comlink_async.py # SwgohComlinkAsync (async client)
│ └── version.py # Exposes __version__ (derived from the Git tag via hatch-vcs)
├── tests/
│ ├── integration/ # Tests requiring a running comlink service
│ ├── resources/ # Test fixture data (example-player.json, etc.)
│ ├── statcalc/ # StatCalc-specific tests (import, parity, offline)
│ ├── unit/ # Mocked unit tests for sync/async clients
│ ├── conftest.py # Shared pytest fixtures and markers
│ └── test_*.py # Additional test modules
├── pyproject.toml # Project metadata, build config, tool settings
├── uv.lock # Locked dependency versions
├── CHANGELOG.md # Auto-generated from commit history
├── CONTRIBUTING.md # This file
├── LICENSE # MIT License
└── README.md
| Module | Purpose |
|---|---|
_base.py |
SwgohComlinkBase — shared payload construction, HMAC signing, and config for both clients |
swgoh_comlink.py |
SwgohComlink — synchronous HTTP client (all endpoint methods) |
swgoh_comlink_async.py |
SwgohComlinkAsync — asynchronous HTTP client (mirrors sync API) |
StatCalc/calculator.py |
StatCalc — local stat and GP calculation for game units |
helpers/ |
DataItems IntFlag enum, Constants class, 25+ utility functions split across focused submodules |
exceptions.py |
SwgohComlinkException, SwgohComlinkValueError, and SwgohComlinkTypeError |
globals.py |
Shared logging setup (get_logger()) |
version.py |
Exposes __version__, derived from the Git tag at build time via hatch-vcs |
| Branch | Purpose |
|---|---|
main |
Stable release branch. All PRs target this branch. |
feature/async |
v2.x development branch (async support, httpx migration, architectural work) |
1.0-maintenance |
Legacy v1.x maintenance branch |
For most contributions:
git checkout main
git pull upstream main
git checkout -b <type>/<short-description>Use a descriptive branch name following the pattern <type>/<description>:
fix/param-alias-falsy-values
feat/async-client
refactor/split-helpers-module
docs/contributing-guide
This project follows standard Python conventions. Please keep these in mind:
General principles:
- Follow PEP 8 for formatting
- Use complete type annotations on all functions, parameters, and return values — the project type-checks with
ty(Astral) - Use
from __future__ import annotationsat the top of each module for modern annotation syntax - Use docstrings (Google style) on all public classes and methods
- Keep lines to 120 characters max (the project doesn't enforce 79)
Naming:
- Public methods use
snake_case:get_player(),get_guild() - camelCase aliases exist for backward compatibility but don't add new ones — they are legacy
- Private/internal methods are prefixed with underscore:
_post(),_get_game_version() - Constants use
UPPER_SNAKE_CASE
Docstring format:
def get_player(
self,
allycode: str | int | None = None,
player_id: str | None = None,
enums: bool = False,
) -> dict[str, Any]:
"""
Get player information from game. Either allycode or player_id must be provided.
Args:
allycode: integer or string representing player allycode
player_id: string representing player game ID
enums: boolean [Defaults to False]
Returns:
A dictionary containing the player information.
"""Imports:
- Standard library imports first, then third-party, then local — separated by blank lines
- Use
from __future__ import annotationsat the top of each module - Prefer specific imports over wildcard:
from json import dumps, loads
Tests live in the tests/ directory and use pytest (configured in pyproject.toml). Both sync and async tests are supported via pytest-asyncio. Run tests with:
# Run all tests
uv run pytest tests/
# Run a specific test file
uv run pytest tests/test_get_player.py
# Run with verbose output
uv run pytest tests/ -vUnit tests vs integration tests:
| Type | Requires comlink? | Mocking | When to use |
|---|---|---|---|
| Unit test | No | Mock _post() |
All new code should have unit tests |
| Integration test | Yes | None | Optional; validates real API behavior |
Writing a unit test (preferred):
New tests should mock the _post() method so they can run anywhere without a comlink service:
from unittest import TestCase, mock
from swgoh_comlink import SwgohComlink
class TestGetPlayer(TestCase):
@mock.patch.object(SwgohComlink, '_post')
def test_get_player_by_allycode(self, mock_post):
"""Test that get_player() builds correct payload for allycode lookup"""
mock_post.return_value = {
'name': 'TestPlayer',
'allyCode': '123456789',
'level': 85
}
comlink = SwgohComlink()
result = comlink.get_player(allycode=123456789)
# Verify the method was called with expected payload
mock_post.assert_called_once_with(
endpoint='player',
payload={
'payload': {'allyCode': '123456789'},
'enums': False
}
)
self.assertEqual(result['name'], 'TestPlayer')Test file naming: test_<feature_or_method>.py
What to test:
- Payload construction — verify the correct JSON payload is built for each method
- Parameter validation — edge cases, invalid inputs, boundary values
- Error handling — how the client handles HTTP errors, bad JSON, connection failures
- Helper functions — each utility function in the
helpers/package should have its own tests
This project uses the Angular commit convention with git-changelog to auto-generate CHANGELOG.md. Your commit messages directly become release notes, so please follow this format:
<type>(<scope>): <short description>
Types (recognized by the changelog generator):
| Type | Use for | CHANGELOG section |
|---|---|---|
feat |
New features or capabilities | Features |
fix |
Bug fixes | Bug Fixes |
refactor |
Code restructuring (no behavior change) | Code Refactoring |
build |
Build system or dependency changes | Build |
deps |
Dependency updates | Dependencies |
chore |
Routine maintenance, config changes, version bumps | Chores |
docs |
Documentation additions or updates | Documentation |
Other types (test, style, ci) are valid conventional commits but are not included in the generated changelog.
Changelog.
CHANGELOG.mdis generated bygit-changelogfrom Conventional Commit messages — do not edit it by hand. Preview locally withuv run git-changelog --bumped-versionanduv run git-changelog. Releases: dispatch prepare release ondevelop(opens the release PR intomain), then comlink-python release onmainafter that PR merges. A back-merge main into develop PR opens automatically after each release.
Scope is optional but encouraged. Common scopes:
core— changes to the client classes (swgoh_comlink.py,swgoh_comlink_async.py,_base.py)helpers— changes to thehelpers/packagestatcalc— changes to theStatCalc/packagedeps— dependency updates
Examples:
# Good
feat(core): add verify_ssl parameter to SwgohComlink constructor
fix(helpers): remove debug print statement from human_time()
refactor(core): generalize _post() into _request() for GET/POST support
fix: correct param_alias decorator to handle falsy values
chore(deps): update requests to version 2.32.4
chore(release): bump version to 1.18.0 and update workflow
docs: add contributing guide
test: add mocked unit tests for get_player and get_guild
# Bad — too vague, not conventional
updated stuff
fix bug
changesMulti-line commits (for complex changes):
feat(core): add async client support
Introduces SwgohComlinkAsync using httpx for non-blocking API calls.
Extracts shared payload construction into _ComlinkBase mixin.
Closes #12
-
Sync your fork with upstream and rebase your branch:
git fetch upstream git rebase upstream/main
If you have merge conflicts, resolve them locally before pushing.
-
Run the checks locally:
# Lint uvx ruff check src/ tests/ # Type check uv run ty check src/swgoh_comlink/ # Tests uv run pytest tests/ -v
-
Push your branch to your fork:
git push origin <your-branch-name>
-
Open a pull request:
Using GitHub CLI:
gh pr create --repo swgoh-utils/comlink-python --base main --fill
Or go to github.com/swgoh-utils/comlink-python/pulls and click "New pull request" → "compare across forks" → select your fork and branch.
-
PR description should include:
- What the change does and why
- Which issue it addresses (e.g., "Closes #51")
- Any breaking changes or migration notes
- How you tested the change
-
PR checklist:
- Code follows the project's style conventions
- All new public methods have docstrings with type hints
- New functionality includes unit tests (mocked, not requiring live comlink)
- Existing tests still pass (
uv run pytest tests/) - Commit messages follow Angular convention
- Ruff linter passes (
uvx ruff check src/ tests/) - Ruff formatter passes (
uvx ruff format --check src/ tests/) - Type check passes (
uv run ty check src/swgoh_comlink/) - No unrelated changes bundled in
-
After submitting:
CI checks (lint, tests, commit message validation, build) will run automatically. Address any failures before requesting review — the maintainer is automatically assigned via CODEOWNERS when a PR is opened.
If you need to update your PR after feedback, push additional commits to the same branch on your fork. The PR updates automatically:
# Make changes, then: git add . git commit -m "fix(core): address review feedback" git push origin <your-branch-name>
Before opening an issue, check the existing issues to avoid duplicates.
Bug reports should include:
- Python version (
python --version) - Package version (
python -c "from swgoh_comlink import version; print(version)") - Comlink version (if relevant)
- Minimal code to reproduce the issue
- Expected vs actual behavior
- Full traceback (if applicable)
Feature requests should include:
- What you're trying to accomplish
- How you're currently working around it (if applicable)
- A proposed API or approach (if you have one in mind)
Labels used in this project:
| Label | Meaning |
|---|---|
bug |
Something isn't working correctly |
security |
Security-related issue |
enhancement |
New feature or capability |
feature request |
Community-requested feature |
code maintenance |
Internal cleanup, refactoring, tech debt |
testing |
Test coverage or infrastructure |
Releases are handled by the maintainer through the GitHub Actions release.yml workflow. Contributors don't need to manage versioning or releases, but here's how it works for reference:
- The release workflow is triggered manually (
workflow_dispatch) with abumpinput (patch,minor, ormajor) - The next version is computed from the latest release tag and the chosen bump level
- The package is built — hatch-vcs derives the version from that target tag, so no version string is committed to the repo
- The distributions are published to PyPI via trusted publishing
- Only after a successful publish is the Git tag created and a GitHub Release cut, with release notes auto-generated from the commit/PR history
Because the version comes from the Git tag (not a committed file), the release workflow never pushes to the protected main branch. This is why conventional commit messages matter — they become the auto-generated release notes.
Version scheme: Semantic Versioning
- Patch (1.17.x): Bug fixes, documentation
- Minor (1.x.0): New features, backward-compatible changes
- Major (x.0.0): Breaking API changes
- Issues: github.com/swgoh-utils/comlink-python/issues
- Discord: Join the server for real-time discussion
- Wiki: swgoh-comlink wiki for general comlink documentation
Thank you for contributing! Every fix, feature, test, and docs improvement helps the SWGOH developer community.