Contributing¶
Thank you for your interest in contributing to PIPolars! This guide will help you get started.
Development Setup¶
Prerequisites¶
Windows 10/11 or Windows Server 2016+
Python 3.10 or later
OSIsoft PI AF SDK 2.x
uv (recommended) or pip
Clone and Install¶
# Clone the repository
git clone https://github.com/pipolars/pipolars.git
cd pipolars
# Install with all development dependencies
uv sync --all-extras
# Or using pip
pip install -e ".[dev,docs]"
Running Tests¶
# Run all tests
uv run pytest
# Run unit tests only
uv run pytest tests/unit
# Run with coverage
uv run pytest --cov=pipolars --cov-report=html
# Run integration tests (requires PI connection)
PI_SERVER=my-server uv run pytest -m integration
Code Quality¶
# Type checking
uv run mypy src
# Linting
uv run ruff check src
# Format code
uv run ruff format src
Building Documentation¶
# Install docs dependencies
uv sync --extra docs
# Build HTML docs
cd docs
make html
# Or on Windows
make.bat html
# View docs (Windows)
start _build/html/index.html
Code Style¶
PIPolars follows these conventions:
Python Style¶
Formatting: Ruff formatter (line length 100)
Imports: isort (via Ruff), first-party imports grouped
Docstrings: Google style
Type hints: Required for all public APIs
Example:
def recorded_values(
self,
tags: str | list[str],
start: PITimestamp,
end: PITimestamp,
max_count: int = 0,
include_quality: bool = False,
) -> pl.DataFrame:
"""Get recorded values for one or more tags.
Args:
tags: Single tag or list of tags
start: Start time (datetime, string, or AFTime)
end: End time
max_count: Maximum values per tag (0 = no limit)
include_quality: Include quality column
Returns:
DataFrame with recorded values
Raises:
PIPointNotFoundError: If tag doesn't exist
PIConnectionError: If not connected
Example:
>>> df = client.recorded_values("SINUSOID", "*-1d", "*")
"""
Commit Messages¶
Use conventional commits:
feat:New featurefix:Bug fixdocs:Documentationtest:Testsrefactor:Code refactoringchore:Maintenance
Example:
feat: add bulk snapshot retrieval
- Add snapshots() method to PIClient
- Implement parallel fetching in BulkExtractor
- Add tests for bulk operations
Pull Request Process¶
Fork the repository
Create a feature branch (
git checkout -b feature/my-feature)Write tests for new functionality
Ensure all tests pass (
uv run pytest)Check types (
uv run mypy src)Check linting (
uv run ruff check src)Commit with meaningful messages
Push to your fork
Open a Pull Request
PR Checklist¶
[ ] Tests pass locally
[ ] New code has tests
[ ] Type hints added
[ ] Docstrings added
[ ] Documentation updated (if needed)
[ ] CHANGELOG updated (for features/fixes)
[ ] No linting errors
Project Structure¶
pipolars/
├── src/pipolars/
│ ├── api/ # User-facing API
│ │ ├── client.py # PIClient
│ │ ├── query.py # PIQuery
│ │ └── lazy.py # LazyFrame support
│ ├── connection/ # PI connectivity
│ │ ├── server.py # PI Server connection
│ │ ├── af_database.py
│ │ ├── sdk.py # AF SDK wrapper
│ │ └── auth.py # Authentication
│ ├── extraction/ # Data retrieval
│ │ ├── points.py # Single point extraction
│ │ ├── bulk.py # Bulk operations
│ │ └── ...
│ ├── transform/ # Data conversion
│ │ ├── converters.py
│ │ └── ...
│ ├── cache/ # Caching
│ │ ├── storage.py
│ │ └── strategies.py
│ └── core/ # Core types
│ ├── config.py
│ ├── types.py
│ └── exceptions.py
├── tests/
│ ├── unit/
│ └── integration/
├── docs/
└── examples/
Testing Guidelines¶
Unit Tests¶
Test individual functions/methods in isolation
Mock external dependencies (PI SDK)
Located in
tests/unit/
Integration Tests¶
Test against actual PI Server
Marked with
@pytest.mark.integrationLocated in
tests/integration/Require
PI_SERVERenvironment variable
import pytest
from pipolars import PIClient
@pytest.mark.integration
def test_recorded_values_real_server():
server = os.environ.get("PI_SERVER")
with PIClient(server) as client:
df = client.recorded_values("SINUSOID", "*-1h", "*")
assert len(df) > 0
Test Fixtures¶
Common fixtures are in tests/conftest.py:
import pytest
from pipolars import PIConfig
from pipolars.core.config import PIServerConfig
@pytest.fixture
def pi_config():
return PIConfig(
server=PIServerConfig(host="test-server"),
)
Reporting Issues¶
Bug Reports¶
Include:
Python version (
python --version)PIPolars version (
pip show pipolars)PI AF SDK version
Windows version
Minimal reproducible example
Full error traceback
Feature Requests¶
Include:
Use case description
Expected behavior
Example API (if applicable)
License¶
By contributing to PIPolars, you agree that your contributions will be licensed under the MIT License.
Questions?¶
Open a GitHub Issue
Check existing issues and discussions