Thank you for your interest in contributing to PyKotor! This guide covers development setup, coding standards, and the contribution workflow.
- Prerequisites
- Development Setup
- Development Workflow
- Code Standards
- Testing
- Submitting Changes
- Resources
- Python 3.8+ (3.9+ recommended for development)
- Platforms: Windows 7–11, macOS, Linux; common architectures (amd64/arm64) supported
- Git for version control
- A code editor with Python support (VS Code, PyCharm, etc.)
git clone https://github.com/OpenKotOR/PyKotor.git
cd PyKotorInitialize the wiki submodule when you need the full Markdown corpus under wiki/ (link checks, Holocron help packaging, bulk doc edits):
git submodule update --init wikiThe GitHub wiki web UI mirrors that content. If you change wiki/*.md, follow the dual-repository workflow in .github/copilot-instructions.md: commit and push to PyKotor.wiki, then commit the updated submodule pointer in this repository.
Faster clone (if full clone is slow or stalls): The repository is large (~380 MB). If the clone hangs around 8–15%, use a shallow clone:
# Shallow clone: only latest commit (fast, ~50–100 MB)
git clone --depth 1 https://github.com/OpenKotOR/PyKotor.git
cd PyKotorTo later fetch full history if needed (e.g. for bisect): git fetch --unshallow.
Optional: clone without blob content first, then fetch as needed:
git clone --filter=blob:none --sparse https://github.com/OpenKotOR/PyKotor.git
cd PyKotorOption A: Using uv (Recommended - Fastest)
Install uv from astral-sh/uv installation.
Run tools with --with-editable to use your local source:
# PyKotor CLI
uvx --with-editable Libraries/PyKotor pykotor --help
# Tools (add --with-editable for each package you're developing)
uvx --with-editable Libraries/PyKotor --with-editable Tools/HolocronToolset holocrontoolset
uvx --with-editable Libraries/PyKotor --with-editable Tools/HoloPatcher holopatcher
uvx --with-editable Libraries/PyKotor --with-editable Tools/KotorDiff kotordiffOr run directly from source:
uv run --directory Libraries/PyKotor/src --module pykotor --help
uv run --directory Tools/HolocronToolset/src --module toolset
uv run --directory Tools/HoloPatcher/src --module holopatcherTo install packages in editable mode with uv:
uv pip install -e "Libraries/PyKotor[all,dev]"
# PyKotor depends on workspace member ``bioware-kaitai-formats`` (import ``bioware_kaitai_formats``).
# From repo root, ``uv sync`` installs it; for isolated ``pip install -e Libraries/PyKotor``, install
# ``Libraries/bioware-kaitai-formats`` editable first or use a published ``bioware-kaitai-formats`` wheel on PyPI.
uv pip install -e "Tools/HolocronToolset"
uv pip install -e "Tools/HoloPatcher"
uv pip install -e "Tools/KotorDiff"Option B: Using pip with venv
# Windows
python -m venv .venv
.venv\Scripts\Activate.ps1
python -m ensurepip
python -m pip install --upgrade pip
python -m pip install -r Tools/HolocronToolset/requirements.txt
python Tools/HolocronToolset/src/toolset/__main__.py
# Repeat for HoloPatcher, KotorDiff, etc. as needed
# macOS/Linux
python3 -m venv .venv
source .venv/bin/activate
python3 -m ensurepip
python3 -m pip install --upgrade pip
python3 -m pip install -r Tools/HolocronToolset/requirements.txt
python3 Tools/HolocronToolset/src/toolset/__main__.pyOr install in editable mode:
# Windows
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install -e "Libraries/PyKotor[all,dev]"
python -m pip install -e Tools/HolocronToolset -e Tools/HoloPatcher
# holocrontoolset, holopatcher should now be on PATH
# macOS/Linux
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -e "Libraries/PyKotor[all,dev]"
python3 -m pip install -e Tools/HolocronToolset -e Tools/HoloPatcher
# holocrontoolset, holopatcher should now be on PATHOption C: Using Poetry
poetry install --with dev
poetry shell# Check library import
python -c "import pykotor; print('PyKotor installed successfully')"
# Check tools (if installed via pip/pipx)
holocrontoolset --version
holopatcher --version
kotordiff --version # or kotordiff --version
# PyKotor CLI (included with pykotor package)
pykotor --help
pykotorcli --helpgit checkout -b feature/your-feature-nameUse meaningful branch names:
feature/for new featuresfix/for bug fixesdocs/for documentationrefactor/for code refactoring
- Write clean, readable code following project conventions
- Add tests for new functionality
- Update documentation as needed
- Keep commits focused and atomic
Run the scoped test suite before committing (see Testing for the full command):
QT_QPA_PLATFORM=offscreen uv run pytest --import-mode=importlib -m "not gui and not slow" --timeout=120 \
--ignore=Libraries/PyKotor/tests/resource/formats/test_mdl_ascii.py \
--ignore=Libraries/PyKotor/tests/test_utility/test_registry_strict_typing.py \
--ignore=Libraries/PyKotor/tests/test_utility/test_file_dialog_components.py \
--ignore=Libraries/PyKotor/tests/test_utility/test_keyboard_accessibility_conformance.py \
Libraries/PyKotor/tests# Lint code
ruff check .
# Auto-fix linting issues
ruff check --fix .
# Format code
ruff format .
# Type check
mypy Libraries/PyKotor/src/pykotorFollow Conventional Commits:
git add .
git commit -m "feat: add new feature"Commit types:
feat:- New featurefix:- Bug fixdocs:- Documentation changesstyle:- Code style changes (formatting, missing semi colons, etc)refactor:- Code refactoringtest:- Adding or updating testschore:- Maintenance tasks
git push origin feature/your-feature-nameThen create a pull request on GitHub with:
- Clear description of changes
- Reference to related issues
- Screenshots for UI changes
- Python Version: Support Python 3.8+ (avoid 3.8+ only features)
- Code Style: Follow PEP 8
- Docstrings: Use Google style
- Type Hints: Add type hints where appropriate (see PEP 484)
- Line Length: 120 characters maximum
from typing import Optional
def process_resource(resource_name: str, game_path: Optional[str] = None) -> bool:
"""Process a game resource file.
Args:
resource_name: Name of the resource to process.
game_path: Optional path to game installation.
Returns:
True if processing succeeded, False otherwise.
Raises:
ValueError: If resource_name is empty.
"""
if not resource_name:
raise ValueError("resource_name cannot be empty")
# Implementation here
return TrueWe use the following tools:
- ruff: Fast Python linter and formatter
- mypy: Static type checker
- pytest: Testing framework
Run all checks:
# Lint
ruff check .
# Format
ruff format .
# Type check
mypy Libraries/PyKotor/src/pykotor
# Test (scoped; see Testing section)
QT_QPA_PLATFORM=offscreen uv run pytest --import-mode=importlib -m "not gui and not slow" --timeout=120 \
--ignore=Libraries/PyKotor/tests/resource/formats/test_mdl_ascii.py \
--ignore=Libraries/PyKotor/tests/test_utility/test_registry_strict_typing.py \
--ignore=Libraries/PyKotor/tests/test_utility/test_file_dialog_components.py \
--ignore=Libraries/PyKotor/tests/test_utility/test_keyboard_accessibility_conformance.py \
Libraries/PyKotor/testsUse the scoped command from AGENTS.md (matches .github/workflows/python-package.yml):
QT_QPA_PLATFORM=offscreen uv run pytest --import-mode=importlib -m "not gui and not slow" --timeout=120 \
--ignore=Libraries/PyKotor/tests/resource/formats/test_mdl_ascii.py \
--ignore=Libraries/PyKotor/tests/test_utility/test_registry_strict_typing.py \
--ignore=Libraries/PyKotor/tests/test_utility/test_file_dialog_components.py \
--ignore=Libraries/PyKotor/tests/test_utility/test_keyboard_accessibility_conformance.py \
Libraries/PyKotor/testsGotchas:
- Scope tests to
Libraries/PyKotor/tests. Running pytest from the repo root without that path collects other packages (e.g. Toolset) and often fails during collection. - Linux requires
--import-mode=importlib. Without it, pytest fails withModuleNotFoundError: No module named 'resource.formats'because the test directoryLibraries/PyKotor/tests/resource/collides with Python's stdlibresourcemodule.
With verbose output:
QT_QPA_PLATFORM=offscreen uv run pytest --import-mode=importlib -m "not gui and not slow" --timeout=120 \
--ignore=Libraries/PyKotor/tests/resource/formats/test_mdl_ascii.py \
--ignore=Libraries/PyKotor/tests/test_utility/test_registry_strict_typing.py \
--ignore=Libraries/PyKotor/tests/test_utility/test_file_dialog_components.py \
--ignore=Libraries/PyKotor/tests/test_utility/test_keyboard_accessibility_conformance.py \
Libraries/PyKotor/tests -vRun specific tests:
# Specific file
uv run pytest --import-mode=importlib Libraries/PyKotor/tests/test_specific.py
# Specific test
uv run pytest --import-mode=importlib Libraries/PyKotor/tests/test_specific.py::test_function_name
# Pattern matching
uv run pytest --import-mode=importlib -k "test_pattern" Libraries/PyKotor/testsPlace tests under Libraries/PyKotor/tests/ following these conventions:
File naming:
test_*.pyfor test files- Match the module being tested:
pykotor/module.py->tests/test_module.py
Test naming:
- Use descriptive names:
test_function_name_with_valid_input() - Use
test_prefix for all test functions
Example test:
import pytest
from pykotor.resource.type import ResourceType
def test_resource_type_from_extension():
"""Test ResourceType creation from file extension."""
assert ResourceType.from_extension("utc") == ResourceType.UTC
assert ResourceType.from_extension(".utc") == ResourceType.UTC
def test_resource_type_invalid_extension():
"""Test ResourceType with invalid extension raises error."""
with pytest.raises(ValueError):
ResourceType.from_extension("invalid")Ensure your changes:
- Pass all tests (scoped command in
AGENTS.md) - Pass linting (
ruff check .) - Are formatted correctly (
ruff format .) - Include type hints where appropriate
- Have docstrings for public APIs
- Include tests for new functionality
- Update relevant documentation
Title:
- Use conventional commit format:
feat: add new feature - Be descriptive but concise
Description:
- Explain what changes were made and why
- Reference related issues:
Fixes #123orRelates to #456 - Include screenshots for UI changes
- List any breaking changes
Review Process:
- Automated checks must pass (linting, tests, type checking)
- At least one maintainer review required
- Address review feedback promptly
- Keep the PR focused on a single concern
- Maintainers will merge your PR
- Your changes will be included in the next release
- You'll be credited in the release notes
- Python Style Guide (PEP 8) - Python coding conventions
- Google Python Style Guide - Docstring conventions
- Conventional Commits - Commit message format
- Project Wiki (GitHub UI) — browsable docs; source of truth for edits is the
wiki/git submodule (PyKotor.wiki.git) aftergit submodule update --init wiki - Wiki Conventions — style, link rules, and recommended validation scripts (
helper_scripts/wiki_scripts/) - Issue Tracker - Report bugs or request features
If you need help:
- Check existing documentation
- Search closed issues
- Open a new issue with the question label
- Review similar code in the codebase
Thank you for contributing to PyKotor!