Contributing Guide¶
Thank you for your interest in contributing to the ReZEN Python API client! This guide will help you get started with contributing code, documentation, and improvements.
๐ Table of Contents¶
- Getting Started
- Development Setup
- Code Standards
- Testing Guidelines
- Documentation
- Pull Request Process
- Issue Reporting
- Release Process
Getting Started¶
Prerequisites¶
- Python: 3.7 or higher
- Git: For version control
- GitHub account: For submitting contributions
- ReZEN API key: For testing (contact support if needed)
Ways to Contribute¶
- ๐ Bug fixes - Fix issues found in the codebase
- ๐ New features - Add new API endpoints or functionality
- ๐ Documentation - Improve docs, examples, or guides
- ๐งช Tests - Add test coverage or improve existing tests
- ๐ง Performance - Optimize code for better performance
- ๐ก Examples - Create real-world usage examples
Development Setup¶
1. Fork and Clone¶
# Fork the repository on GitHub, then clone your fork
git clone https://github.com/YOUR_USERNAME/rezen-python-client.git
cd rezen-python-client
# Add upstream remote
git remote add upstream https://github.com/original-org/rezen-python-client.git
2. Set Up Development Environment¶
# Create virtual environment
python -m venv venv
# Activate virtual environment
source venv/bin/activate # Linux/macOS
# or venv\Scripts\activate # Windows
# Install development dependencies
pip install -r requirements-dev.txt
# Install package in development mode
pip install -e .
3. Verify Setup¶
# Run tests to ensure everything works
pytest
# Check code formatting
black --check rezen tests
isort --check-only rezen tests
# Run type checking
mypy rezen
# Check test coverage
pytest --cov=rezen --cov-report=html
4. Set Up Pre-commit Hooks (Recommended)¶
# Install pre-commit
pip install pre-commit
# Install hooks
pre-commit install
# Test hooks
pre-commit run --all-files
Code Standards¶
Python Style¶
We follow PEP 8 with some specific guidelines:
# โ
Good: Clear function names with type hints
def get_agent_by_email(email_address: str) -> Dict[str, Any]:
"""Get agent information by email address.
Args:
email_address: The agent's email address
Returns:
Agent data dictionary
Raises:
NotFoundError: If agent is not found
ValidationError: If email format is invalid
"""
if '@' not in email_address:
raise ValidationError(f"Invalid email format: {email_address}")
return self.get("agents", params={"email": email_address})
# โ Bad: No type hints, unclear naming
def get_agent(email):
return self.get("agents", params={"email": email})
Code Formatting¶
We use automated tools for consistent formatting:
# Format code with Black
black rezen tests
# Sort imports with isort
isort rezen tests
# Check with flake8
flake8 rezen tests
Type Hints¶
All public methods must have complete type hints:
from typing import Dict, List, Optional, Any, Union
# โ
Complete type hints
def search_teams(
self,
status: Optional[Union[TeamStatus, str]] = None,
page_size: Optional[int] = None
) -> Dict[str, Any]:
pass
# โ Missing type hints
def search_teams(self, status=None, page_size=None):
pass
Docstrings¶
Use Google-style docstrings for all public methods:
def create_transaction_builder(self, builder_type: str = "TRANSACTION") -> Dict[str, Any]:
"""Create a new transaction builder.
Creates a new transaction builder instance that can be used to construct
real estate transactions with participants, property details, and financial information.
Args:
builder_type: Type of builder to create. Must be "TRANSACTION" or "LISTING".
Defaults to "TRANSACTION".
Returns:
Dictionary containing the created transaction builder data with at least:
- id: Unique identifier for the transaction builder
- type: The builder type that was created
- status: Current status of the builder
Raises:
ValidationError: If builder_type is not valid
AuthenticationError: If API key is invalid
ServerError: If the API server encounters an error
Example:
Create a basic transaction builder:
>>> client = RezenClient()
>>> response = client.transaction_builder.create_transaction_builder()
>>> transaction_id = response['id']
Create a listing builder:
>>> response = client.transaction_builder.create_transaction_builder("LISTING")
"""
Error Handling¶
Use specific exceptions and provide helpful error messages:
from .exceptions import ValidationError, NotFoundError
def get_transaction(self, transaction_id: str) -> Dict[str, Any]:
"""Get transaction details."""
if not transaction_id:
raise ValidationError("Transaction ID cannot be empty")
if not isinstance(transaction_id, str):
raise ValidationError(f"Transaction ID must be string, got {type(transaction_id)}")
try:
return self.get(f"transactions/{transaction_id}")
except NotFoundError:
raise NotFoundError(
f"Transaction '{transaction_id}' not found. "
f"Verify the ID is correct and the transaction exists."
)
Testing Guidelines¶
Test Structure¶
We use pytest with a specific structure:
tests/
โโโ __init__.py
โโโ conftest.py # Shared fixtures
โโโ test_client.py # Main client tests
โโโ test_transaction_builder.py # Transaction builder tests
โโโ test_transactions.py # Transactions API tests
โโโ test_teams.py # Teams API tests
โโโ test_agents.py # Agents API tests
โโโ test_exceptions.py # Exception handling tests
โโโ test_integration.py # Integration tests
Writing Tests¶
Unit Tests¶
import pytest
from unittest.mock import Mock, patch
from rezen import RezenClient
from rezen.exceptions import ValidationError, NotFoundError
class TestTransactionBuilder:
def setup_method(self):
"""Set up test fixtures before each test method."""
self.client = RezenClient(api_key="test_key")
@patch('rezen.transaction_builder.TransactionBuilderClient._request')
def test_create_transaction_builder_success(self, mock_request):
"""Test successful transaction builder creation."""
# Arrange
expected_response = {"id": "tx-12345", "type": "TRANSACTION"}
mock_request.return_value = expected_response
# Act
result = self.client.transaction_builder.create_transaction_builder()
# Assert
assert result == expected_response
mock_request.assert_called_once_with(
"POST", "transaction-builder",
params={"type": "TRANSACTION"}
)
def test_create_transaction_builder_invalid_type(self):
"""Test transaction builder creation with invalid type."""
with pytest.raises(ValidationError, match="Invalid builder type"):
self.client.transaction_builder.create_transaction_builder("INVALID")
@pytest.mark.parametrize("builder_type,expected_params", [
("TRANSACTION", {"type": "TRANSACTION"}),
("LISTING", {"type": "LISTING"}),
])
def test_create_transaction_builder_types(self, builder_type, expected_params):
"""Test transaction builder creation with different types."""
with patch.object(self.client.transaction_builder, '_request') as mock_request:
mock_request.return_value = {"id": "test"}
self.client.transaction_builder.create_transaction_builder(builder_type)
mock_request.assert_called_once_with(
"POST", "transaction-builder", params=expected_params
)
Integration Tests¶
import pytest
from rezen import RezenClient
class TestIntegration:
"""Integration tests that hit real API endpoints."""
@pytest.mark.integration
def test_teams_search_integration(self):
"""Test actual teams search API call."""
client = RezenClient() # Uses real API key from environment
teams = client.teams.search_teams(status="ACTIVE", page_size=5)
assert isinstance(teams, list)
assert len(teams) <= 5
if teams:
team = teams[0]
assert 'id' in team
assert 'name' in team
assert team.get('status') == 'ACTIVE'
Test Configuration¶
Add to conftest.py
:
import pytest
import os
def pytest_configure(config):
"""Configure pytest with custom markers."""
config.addinivalue_line(
"markers", "integration: marks tests as integration tests"
)
@pytest.fixture
def mock_api_key():
"""Provide a test API key."""
return "test_api_key_12345"
@pytest.fixture
def sample_transaction_data():
"""Provide sample transaction data for tests."""
return {
"type": "PURCHASE",
"property": {
"address": "123 Test St",
"city": "Test City",
"state": "CA",
"zipCode": "90210"
},
"purchase_price": 500000
}
Running Tests¶
# Run all tests
pytest
# Run specific test file
pytest tests/test_teams.py
# Run tests with coverage
pytest --cov=rezen --cov-report=html
# Run only unit tests (skip integration)
pytest -m "not integration"
# Run only integration tests
pytest -m integration
# Run tests in parallel
pytest -n auto
# Run tests with verbose output
pytest -v
Test Coverage Requirements¶
- Minimum coverage: 95% overall
- New code: 100% coverage required
- Critical paths: Authentication, API calls, error handling must have 100% coverage
Check coverage:
# Generate coverage report
pytest --cov=rezen --cov-report=html
open htmlcov/index.html # View in browser
# Check coverage for specific module
pytest --cov=rezen.teams --cov-report=term-missing
Documentation¶
Code Documentation¶
All public methods require docstrings:
def search_active_agents(
self,
page_number: Optional[int] = None,
page_size: Optional[int] = None,
name: Optional[str] = None
) -> Dict[str, Any]:
"""Search for active agents with filtering options.
Searches the agent database for active agents matching the specified
criteria. Results are paginated and can be filtered by various attributes.
Args:
page_number: Page number for pagination (0-based). Defaults to 0.
page_size: Number of results per page (1-200). Defaults to 20.
name: Filter agents by name (partial match, case-insensitive).
Searches both first and last names.
Returns:
Dictionary containing:
- content: List of agent dictionaries
- page: Current page information
- total: Total number of matching agents
Raises:
ValidationError: If page_size is outside valid range (1-200)
AuthenticationError: If API key is invalid or missing
RateLimitError: If too many requests made in short time
Example:
Search for agents named "John":
>>> client = RezenClient()
>>> agents = client.agents.search_active_agents(name="John", page_size=10)
>>> print(f"Found {len(agents['content'])} agents")
Paginate through all agents:
>>> page = 0
>>> all_agents = []
>>> while True:
... result = client.agents.search_active_agents(page_number=page)
... if not result['content']:
... break
... all_agents.extend(result['content'])
... page += 1
"""
User Documentation¶
When adding new features, update relevant documentation:
- API Reference (
docs/api/index.md
) - Examples (
docs/guides/examples.md
) - README (if major feature)
- Changelog (
docs/changelog.md
)
Documentation Style¶
- Use clear, concise language
- Include working code examples
- Add parameter descriptions and return value info
- Note any breaking changes
- Use emoji for visual organization (sparingly)
Pull Request Process¶
1. Create Feature Branch¶
# Update main branch
git checkout main
git pull upstream main
# Create feature branch
git checkout -b feature/add-agent-search
# or
git checkout -b fix/transaction-validation-error
2. Make Changes¶
- Write code following our standards
- Add/update tests for all changes
- Update documentation if needed
- Ensure all tests pass locally
3. Commit Changes¶
Use conventional commit messages:
# Feature additions
git commit -m "feat: add agent search by location endpoint"
# Bug fixes
git commit -m "fix: handle empty response in transaction search"
# Documentation
git commit -m "docs: add examples for team management"
# Tests
git commit -m "test: add coverage for error handling paths"
# Refactoring
git commit -m "refactor: simplify exception handling logic"
4. Push and Create Pull Request¶
# Push to your fork
git push origin feature/add-agent-search
# Create pull request on GitHub
# Use the PR template provided
Pull Request Template¶
## Description
Brief description of changes made.
## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
## Testing
- [ ] All existing tests pass
- [ ] New tests added for new functionality
- [ ] Manual testing performed
- [ ] Integration tests updated if needed
## Documentation
- [ ] Code documentation updated (docstrings)
- [ ] User documentation updated
- [ ] Examples updated/added
- [ ] Changelog updated
## Checklist
- [ ] Code follows project style guidelines
- [ ] Self-review completed
- [ ] Breaking changes documented
- [ ] Tests provide adequate coverage
Review Process¶
- Automated checks: CI/CD runs tests, linting, type checking
- Code review: Maintainers review code for quality and standards
- Testing: Verify functionality works as expected
- Documentation: Ensure docs are complete and accurate
- Approval: At least one maintainer approval required
- Merge: Squash and merge to main branch
Issue Reporting¶
Bug Reports¶
Use the bug report template:
**Bug Description**
Clear description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Create client with '...'
2. Call method '....'
3. Pass parameters '....'
4. See error
**Expected Behavior**
What you expected to happen.
**Actual Behavior**
What actually happened.
**Environment**
- OS: [e.g. macOS 12.0]
- Python version: [e.g. 3.9.0]
- ReZEN client version: [e.g. 1.0.7]
**Code Sample**
```python
# Minimal code sample that reproduces the issue
client = RezenClient()
# ... rest of code
Error Output
Additional Context Any other context about the problem.
### Feature Requests
Use the feature request template:
```markdown
**Feature Description**
Clear description of the feature you'd like to see.
**Use Case**
Explain why this feature would be useful.
**Proposed Solution**
How you think this should work.
**Alternative Solutions**
Any alternative approaches you've considered.
**Additional Context**
Any other context or screenshots about the feature request.
Release Process¶
Version Numbering¶
We use Semantic Versioning:
- MAJOR (1.0.0 โ 2.0.0): Breaking changes
- MINOR (1.0.0 โ 1.1.0): New features, backwards compatible
- PATCH (1.0.0 โ 1.0.1): Bug fixes, backwards compatible
Release Checklist¶
-
Update version numbers:
-
Update changelog:
-
Run full test suite:
-
Build and test package:
-
Create release PR:
- Include version bump and changelog
-
Get approval from maintainers
-
Tag and release:
Getting Help¶
Development Questions¶
- GitHub Discussions: For general questions about contributing
- Discord/Slack: For real-time development chat (if available)
- Email: support@rezen.com for private questions
Resources¶
- Style Guide: Follow PEP 8 and project conventions above
- API Documentation: docs/api/index.md
- Examples: docs/guides/examples.md
- Python Docs: docs.python.org
Recognition¶
Contributors will be:
- Listed in the project contributors
- Mentioned in release notes for significant contributions
- Invited to the contributors' Discord/Slack channel (if available)
Thank you for contributing to the ReZEN Python client! ๐
Your contributions help make real estate technology more accessible to developers worldwide.