* ref: Update download and hash chunk sizes to speed up * build: Adopt uv with uv.lock and drop requirements.txt * ref: Centralize softmax helper and minor cleanups
8.0 KiB
Contributing to UniFace
Thank you for considering contributing to UniFace! We welcome contributions of all kinds.
How to Contribute
Reporting Issues
- Use GitHub Issues to report bugs or suggest features
- Include clear descriptions and reproducible examples
- Check existing issues before creating new ones
Pull Requests
- Fork the repository
- Create a new branch for your feature
- Write clear, documented code with type hints
- Add tests for new functionality
- Ensure all tests pass and pre-commit hooks are satisfied
- Submit a pull request with a clear description
Development Setup
We use uv for reproducible dev installs. The committed uv.lock pins every transitive dependency so contributors and CI resolve to identical versions.
# Install uv (https://docs.astral.sh/uv/getting-started/installation/)
curl -LsSf https://astral.sh/uv/install.sh | sh
git clone https://github.com/yakhyo/uniface.git
cd uniface
# Sync runtime + cpu + dev extras from uv.lock (use --extra gpu instead of cpu for CUDA)
uv sync --extra cpu --extra dev
uv sync creates a project-local .venv/ and installs everything pinned in uv.lock. Run commands with uv run <cmd> (e.g. uv run pytest), or activate the venv with source .venv/bin/activate.
Setting Up Pre-commit Hooks
We use pre-commit to ensure code quality and consistency. pre-commit is included in the [dev] extra, so it's already installed after uv sync.
# Install the git hooks
uv run pre-commit install
# (Optional) Run against all files
uv run pre-commit run --all-files
Once installed, pre-commit will automatically run on every commit to check:
- Code formatting and linting (Ruff)
- Security issues (Bandit)
- General file hygiene (trailing whitespace, YAML/TOML validity, etc.)
Note: All PRs are automatically checked by CI. The merge button will only be available after all checks pass.
Code Style
This project uses Ruff for linting and formatting, following modern Python best practices. Pre-commit handles all formatting automatically.
Style Guidelines
General Rules
- Line length: 120 characters maximum
- Python version: 3.10+ (use modern syntax)
- Quote style: Single quotes for strings, double quotes for docstrings
Type Hints
Use modern Python 3.10+ type hints (PEP 585 and PEP 604):
# Preferred (modern)
def process(items: list[str], config: dict[str, int] | None = None) -> tuple[int, str]:
...
# Avoid (legacy)
from typing import List, Dict, Optional, Tuple
def process(items: List[str], config: Optional[Dict[str, int]] = None) -> Tuple[int, str]:
...
Docstrings
Use Google-style docstrings for all public APIs:
def create_detector(method: str = 'retinaface', **kwargs: Any) -> BaseDetector:
"""Factory function to create face detectors.
Args:
method: Detection method. Options: 'retinaface', 'scrfd', 'yolov5face', 'yolov8face'.
**kwargs: Detector-specific parameters.
Returns:
Initialized detector instance.
Raises:
ValueError: If method is not supported.
Example:
>>> from uniface import create_detector
>>> detector = create_detector('retinaface', confidence_threshold=0.8)
>>> faces = detector.detect(image)
>>> print(f"Found {len(faces)} faces")
"""
Import Order
Imports are automatically sorted by Ruff with the following order:
- Future imports (
from __future__ import annotations) - Standard library (
os,sys,typing, etc.) - Third-party (
numpy,cv2,onnxruntime, etc.) - First-party (
uniface.*) - Local (relative imports like
.base,.models)
from __future__ import annotations
import os
from typing import Any
import cv2
import numpy as np
from uniface.constants import RetinaFaceWeights
from uniface.log import Logger
from .base import BaseDetector
Code Comments
- Add comments for complex logic, magic numbers, and non-obvious behavior
- Avoid comments that merely restate the code
- Use
# TODO:with issue links for planned improvements
# RetinaFace FPN strides and corresponding anchor sizes per level
steps = [8, 16, 32]
min_sizes = [[16, 32], [64, 128], [256, 512]]
# Add small epsilon to prevent division by zero
similarity = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-5)
Running Tests
# Run all tests
pytest tests/
# Run with verbose output
pytest tests/ -v
# Run specific test file
pytest tests/test_factory.py
# Run with coverage
pytest tests/ --cov=uniface --cov-report=html
Adding New Features
When adding a new model or feature:
- Create the model class in the appropriate submodule (e.g.,
uniface/detection/) - Add weight constants to
uniface/constants.pywith URLs and SHA256 hashes - Export in
__init__.pyfiles at both module and package levels - Write tests in
tests/directory - Add example usage in
tools/or update existing notebooks - Update documentation if needed
Examples
Example notebooks demonstrating library usage:
| Example | Notebook |
|---|---|
| Face Detection | 01_face_detection.ipynb |
| Face Alignment | 02_face_alignment.ipynb |
| Face Verification | 03_face_verification.ipynb |
| Face Search | 04_face_search.ipynb |
| Face Analyzer | 05_face_analyzer.ipynb |
| Face Parsing | 06_face_parsing.ipynb |
| Face Anonymization | 07_face_anonymization.ipynb |
| Gaze Estimation | 08_gaze_estimation.ipynb |
| Face Segmentation | 09_face_segmentation.ipynb |
| Face Vector Store | 10_face_vector_store.ipynb |
| Head Pose Estimation | 11_head_pose_estimation.ipynb |
Release Process
Releases are fully automated via GitHub Actions. Only maintainers with branch-protection bypass privileges on main can trigger a release.
Cutting a release
- Go to Actions → Release Pipeline → Run workflow on GitHub.
- Enter the version following PEP 440:
- Stable:
0.7.0,1.0.0 - Pre-release:
0.7.0rc1,0.7.0b1,0.7.0a1,0.7.0.dev1
- Stable:
- Click Run workflow.
What happens automatically
The Release Pipeline workflow runs all stages in sequence:
- Validate — checks the version string against PEP 440 and confirms the tag does not already exist.
- Test — runs the test suite on Python 3.10–3.14.
- Release — updates
pyproject.tomlanduniface/__init__.py, commitschore: Release vX.Y.Ztomain, creates and pushes tagvX.Y.Z. - Publish — builds the package, uploads to PyPI, and creates a GitHub Release (flagged as pre-release for
a/b/rc/.devversions). - Deploy docs — runs only for stable versions. Pre-releases do not update the live documentation site.
Verifying a release
- PyPI: https://pypi.org/project/uniface/
- GitHub Releases: https://github.com/yakhyo/uniface/releases
- Docs (stable only): https://yakhyo.github.io/uniface/
Installing a pre-release
End users can opt in to pre-releases with the --pre flag:
pip install uniface --pre # latest pre-release
pip install uniface==0.7.0rc1 # specific pre-release
Without --pre, pip install uniface always resolves to the latest stable version.
Questions?
Open an issue or start a discussion on GitHub.