- Introduced .copilot-instructions.md for GitHub Copilot usage guidelines. - Created DEVELOPMENT.md detailing environment setup, workflow, and common tasks. - Established STYLE_GUIDE.md as a quick reference for code style, formatting, and conventions.
15 KiB
Development Guide for Elytra PIM Client
This guide provides comprehensive instructions for developers working on the Elytra PIM Client project.
Table of Contents
Environment Setup
Prerequisites
- Python 3.9 or higher
- pip 20.0 or higher
- Git
Initial Setup
-
Clone the repository
git clone https://git.him-tools.de/HIM-public/elytra_client.git cd elytra_client -
Create virtual environment
python -m venv .venv -
Activate virtual environment
Windows (PowerShell):
.\.venv\Scripts\Activate.ps1Windows (Command Prompt):
.\.venv\Scripts\activate.batmacOS/Linux:
source .venv/bin/activate -
Install project with development dependencies
pip install -e ".[dev]" -
Verify installation
pytest tests/ -v
Environment Configuration
-
Copy example environment file
cp .env.example .env -
Edit
.envwith your settingsELYTRA_API_URL=https://your-api-url/api/v1 ELYTRA_API_KEY=your-api-key-here -
Never commit
.envfile (it's in.gitignore)
Development Workflow
Daily Development Cycle
-
Start your work session
# Activate virtual environment .venv\Scripts\activate # Windows source .venv/bin/activate # macOS/Linux # Create a feature branch git checkout -b feature/your-feature-name -
Write tests first (TDD)
# Create test in tests/test_client.py or new test file # Implement your feature # Run tests frequently pytest tests/ -v -
Before commits: Run all checks
# Auto-format code black elytra_client tests # Organize imports isort elytra_client tests # Lint code flake8 elytra_client tests # Type check mypy elytra_client # Run tests pytest tests/ -v --cov=elytra_client -
Commit your changes
git add . git commit -m "feat: add new feature description" -
Push and create pull request
git push origin feature/your-feature-name
Quick Workflow Commands
For convenience, run all checks in one command:
# Complete pre-commit check
black elytra_client tests && isort elytra_client tests && flake8 elytra_client tests && mypy elytra_client && pytest tests/ -v
Or create an alias:
# Add to .bashrc, .zshrc, or PowerShell profile
alias check-all='black elytra_client tests && isort elytra_client tests && flake8 elytra_client tests && mypy elytra_client && pytest tests/ -v'
# Then just run:
check-all
Code Standards
Language: English Only
- All code must be in English
- All documentation must be in English
- All comments must be in English
- All variable/function/class names must be in English
Style Guide
Line Length
- Maximum 100 characters
- Enforced by Black formatter
- Configure in editor: View → Toggle Rulers
Imports
- Group: Standard library → Third-party → Local
- Use absolute imports
- Sort with isort (Black-compatible)
Type Hints
# Good
def get_product(product_id: str) -> ProductResponse:
"""Get a product by ID."""
pass
# Bad
def get_product(product_id):
return response
# Good - with Optional
from typing import Optional
def find_product(name: Optional[str] = None) -> Optional[ProductResponse]:
"""Find product by name."""
pass
# Good - with Union (Python 3.10+)
def process_response(data: dict | list) -> str:
"""Process response data."""
pass
Docstrings (Google Style)
def create_product(
name: str,
description: str,
lang: str = "en",
) -> SingleProductResponse:
"""Create a new product.
Creates a new product in the Elytra PIM system with the provided
information. The product will be created with the specified language.
Args:
name: Product name (required)
description: Product description (required)
lang: Language code for the product (default: "en")
Returns:
SingleProductResponse: The created product with assigned ID
Raises:
ElytraAuthenticationError: If authentication fails
ElytraValidationError: If required fields are missing
ElytraAPIError: For other API errors
Example:
>>> client = ElytraClient(base_url="...", api_key="...")
>>> product = client.create_product("Widget", "A useful widget")
>>> print(product.id)
"""
pass
Error Handling
# Good - specific exceptions with context
from .exceptions import ElytraAPIError
try:
response = requests.get(url, timeout=self.timeout)
response.raise_for_status()
except requests.HTTPError as e:
status_code = e.response.status_code
if status_code == 404:
raise ElytraNotFoundError("Resource not found", status_code) from e
raise ElytraAPIError(f"API error: {status_code}", status_code) from e
except requests.RequestException as e:
raise ElytraAPIError(f"Network error: {str(e)}") from e
# Bad - too broad exception handling
try:
response = requests.get(url)
except:
print("Error occurred")
Naming Conventions
# Constants: UPPER_CASE
API_TIMEOUT = 30
MAX_RETRIES = 3
# Classes: PascalCase
class ElytraClient:
pass
class ProductResponse:
pass
# Functions/Methods: snake_case
def get_products(self):
pass
def create_new_product(self):
pass
# Private methods: _leading_underscore
def _make_request(self):
pass
def _handle_error(self, error):
pass
# Variables: snake_case
product_id = "123"
api_key = "secret"
response_data = {}
Testing
Test Structure
tests/
├── test_client.py # Client functionality tests
├── test_models.py # Model validation tests (if needed)
├── test_exceptions.py # Exception handling tests (if needed)
└── conftest.py # Pytest fixtures
Writing Tests
import pytest
from unittest.mock import Mock, patch
from elytra_client import ElytraClient
from elytra_client.exceptions import ElytraAPIError
class TestElytraClient:
"""Tests for ElytraClient."""
@pytest.fixture
def client(self):
"""Create a test client."""
return ElytraClient(
base_url="https://api.example.com",
api_key="test-key-123"
)
def test_get_products_success(self, client):
"""Test successful product retrieval."""
with patch.object(client.session, 'request') as mock_request:
# Setup mock response
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {
"items": [
{"id": "1", "name": "Product 1"},
{"id": "2", "name": "Product 2"}
]
}
mock_request.return_value = mock_response
# Execute
result = client.get_products()
# Assert
assert isinstance(result, dict)
assert "items" in result
assert len(result["items"]) == 2
def test_get_products_not_found(self, client):
"""Test product retrieval when not found."""
with patch.object(client.session, 'request') as mock_request:
# Setup mock for 404
mock_request.side_effect = Mock(
side_effect=requests.HTTPError("404 Not Found")
)
mock_request.return_value.status_code = 404
# Assert exception is raised
with pytest.raises(ElytraNotFoundError):
client.get_products()
def test_authentication_error(self, client):
"""Test authentication error handling."""
with patch.object(client.session, 'request') as mock_request:
mock_request.side_effect = requests.HTTPError("401 Unauthorized")
with pytest.raises(ElytraAuthenticationError):
client.get_products()
Running Tests
# Run all tests
pytest tests/ -v
# Run with coverage report
pytest tests/ -v --cov=elytra_client --cov-report=html
# Run specific test file
pytest tests/test_client.py -v
# Run specific test class
pytest tests/test_client.py::TestElytraClient -v
# Run specific test function
pytest tests/test_client.py::TestElytraClient::test_get_products_success -v
# Run tests matching pattern
pytest tests/ -k "test_get" -v
# Run with minimal output
pytest tests/ -q
# Run with detailed output (show local variables on failure)
pytest tests/ -vv
Test Coverage
# Generate HTML coverage report
pytest tests/ --cov=elytra_client --cov-report=html
# Open the report
# Open htmlcov/index.html in your browser
# Target minimum coverage
pytest tests/ --cov=elytra_client --cov-fail-under=80
Common Tasks
Adding a New API Method
-
Add test first in
tests/test_client.py:def test_get_product_by_id(self, client): """Test getting a product by ID.""" with patch.object(client.session, 'request') as mock_request: mock_response = Mock() mock_response.status_code = 200 mock_response.json.return_value = { "id": "123", "name": "Test Product" } mock_request.return_value = mock_response result = client.get_product(product_id="123") assert result.id == "123" assert result.name == "Test Product" -
Create Pydantic model if needed in
elytra_client/models.py:class ProductDetailsResponse(BaseModel): """Response model for detailed product information.""" id: str name: str description: Optional[str] = None -
Implement method in
elytra_client/client.py:def get_product(self, product_id: str) -> ProductDetailsResponse: """Get product details by ID. Args: product_id: The product ID Returns: ProductDetailsResponse: The product details Raises: ElytraNotFoundError: If product not found ElytraAuthenticationError: If authentication fails """ endpoint = f"/products/{product_id}" return self._make_request( method="GET", endpoint=endpoint, response_model=ProductDetailsResponse ) -
Run tests:
pytest tests/test_client.py::TestElytraClient::test_get_product_by_id -v -
Run all checks:
black elytra_client tests && isort elytra_client tests && flake8 elytra_client tests && mypy elytra_client && pytest tests/ -v
Fixing a Bug
- Create failing test that reproduces the bug
- Verify the test fails
- Fix the bug
- Verify the test passes
- Run full test suite to ensure no regressions
- Run all code checks
- Commit with clear message:
git commit -m "fix: resolve issue with product validation"
Updating OpenAPI Spec
- Update
openapi.yaml(source of truth) - Update models in
models.pyto match spec - Update methods in
client.pyif endpoints changed - Write/update tests
- Run all checks
Creating a Release
- Update version in
pyproject.toml - Update CHANGELOG (if exists)
- Commit changes:
git commit -m "chore: bump version to X.Y.Z" - Tag release:
git tag vX.Y.Z git push origin vX.Y.Z
Troubleshooting
Issue: Import Errors
Problem: ModuleNotFoundError: No module named 'elytra_client'
Solution:
# Ensure virtual environment is activated
.venv\Scripts\activate # Windows
source .venv/bin/activate # macOS/Linux
# Install package in development mode
pip install -e .
Issue: Type Checking Failures
Problem: error: Cannot find implementation or library stub for module named "module_name"
Solution:
# Install type stubs
pip install types-requests
# Run mypy with more details
mypy elytra_client --show-error-codes
# Check specific file
mypy elytra_client/client.py
Issue: Tests Fail with "Connection Error"
Problem: Tests are making real API calls instead of using mocks
Solution:
- Verify you're using
patch.object(client.session, 'request') - Check that mock is set up before calling client methods
- Use
mock_request.side_effectfor exceptions
# Correct way to mock
with patch.object(client.session, 'request') as mock_request:
mock_request.return_value = Mock(status_code=200, ...)
# Now this won't make real request
result = client.get_products()
Issue: Formatting Conflicts
Problem: Black and isort produce different results or conflicts
Solution:
# Always run in this order
black elytra_client tests
isort elytra_client tests
If you encounter persistent conflicts, check .isort.cfg or pyproject.toml for Black-compatible settings.
Issue: Virtual Environment Issues
Problem: Changes to venv not reflected, or "python: command not found"
Solution:
# Deactivate current environment
deactivate
# Remove old venv
rm -rf .venv # macOS/Linux
rmdir /s .venv # Windows
# Create fresh venv
python -m venv .venv
# Activate and reinstall
.venv\Scripts\activate # Windows
pip install -e ".[dev]"
Issue: Pydantic Validation Errors
Problem: ValidationError: X validation error(s) for ModelName
Solution:
- Check API response matches model definition in
models.py - Verify OpenAPI spec matches response data
- Use
response_model.model_validate(data)to see detailed error - Consider using
Field(default=None)for optional fields
VS Code Integration
Recommended Extensions
- Python (ms-python.python)
- Pylance (ms-python.vscode-pylance)
- Black Formatter (ms-python.black-formatter)
- Flake8 (ms-python.flake8)
- Mypy Type Checker (ms-python.mypy-type-checker)
- Prettier (esbenp.prettier-vscode)
Keyboard Shortcuts
- Format document:
Shift+Alt+F - Sort imports: Run command palette
isort: sort imports - Run tests:
Ctrl+; Ctrl+T(with Python extension) - Quick fix:
Ctrl+.
Command Palette Commands
Python: Create Terminal- Creates Python terminal with venv activatedTest: Run All Tests- Runs all testsPython: Run Selection/Line in Python Terminal- Test code snippets
Additional Resources
- Project README: README.md
- Copilot Instructions: .copilot-instructions.md
- Agent Instructions: .agent-instructions.md
- Pydantic Docs: https://docs.pydantic.dev/latest/
- Pytest Docs: https://docs.pytest.org/
- Type Hints Docs: https://docs.python.org/3/library/typing.html