Development#
This guide covers development workflow, testing, and documentation for FOLIO Data Import.
Environment Setup#
Prerequisites#
This project uses uv for dependency management and virtual environments. Install uv first:
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Or with Homebrew
brew install uv
# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
1. Clone Repository#
git clone https://github.com/FOLIO-FSE/folio_data_import.git
cd folio_data_import
2. Install Dependencies#
uv automatically creates and manages the virtual environment:
# Install all dependencies including dev group
uv sync
# Or install with docs dependencies as well
uv sync --group docs
The virtual environment is created at .venv/ and activated automatically when using uv run.
3. Running Commands#
Use uv run to execute commands in the project environment:
# Run a Python script
uv run python script.py
# Run the CLI tools
uv run folio-batch-poster --help
uv run folio-marc-import --help
uv run folio-user-import --help
# Run pytest
uv run pytest
Running Tests#
Basic Test Run#
uv run pytest
With Coverage Report#
uv run pytest --cov=folio_data_import --cov-report=html
Open htmlcov/index.html to view coverage details.
Specific Test File#
uv run pytest tests/test_batch_poster.py -v
Running Specific Test#
uv run pytest tests/test_batch_poster.py::test_initialization -v
Code Quality#
This project uses Ruff for both linting and formatting. Ruff is a fast Python linter and formatter written in Rust that replaces Black, Flake8, isort, and many other tools.
Check Formatting#
# Check if files are formatted correctly
uv run ruff format --check src/ tests/
# Format all files
uv run ruff format src/ tests/
Linting#
# Check for issues
uv run ruff check src/ tests/
# Fix issues automatically where possible
uv run ruff check src/ tests/ --fix
# Show what fixes would be applied
uv run ruff check src/ tests/ --show-fixes
Run All Quality Checks#
# Format code
uv run ruff format .
# Lint and auto-fix
uv run ruff check . --fix
# Run tests
uv run pytest
Ruff Configuration#
Ruff is configured in pyproject.toml:
[tool.ruff]
line-length = 99
target-version = "py310"
[tool.ruff.lint]
select = ["B", "B9", "C", "E", "F", "S", "W"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
Key lint rule categories enabled:
B - flake8-bugbear (common bugs)
C - mccabe complexity
E/W - pycodestyle errors and warnings
F - pyflakes (undefined names, unused imports)
S - flake8-bandit (security issues)
Project Structure#
folio_data_import/
├── src/
│ └── folio_data_import/
│ ├── __init__.py
│ ├── __main__.py
│ ├── BatchPoster.py # Inventory batch posting
│ ├── MARCDataImport.py # MARC record import
│ ├── UserImport.py # User data import
│ ├── _progress.py # Progress tracking
│ ├── custom_exceptions.py # Custom exception classes
│ └── marc_preprocessors/ # MARC preprocessing utilities
├── tests/
│ ├── test_batch_poster.py
│ ├── test_marc_data_import.py
│ ├── test_user_import.py
│ └── test_progress.py
├── docs/
│ ├── conf.py # Sphinx configuration
│ └── source/ # Documentation source files
├── pyproject.toml # Project metadata & tool config
├── pytest.ini # Pytest configuration
└── README.md
Key Files#
pyproject.toml#
Project configuration including:
Project metadata and version
Dependencies and dependency groups
Build system configuration (uses
uv_build)Tool configurations (ruff, pytest)
src/folio_data_import/#
Main package source code:
BatchPoster.py - Core batch posting functionality for Instances, Holdings, Items
MARCDataImport.py - MARC record import via FOLIO’s Change Manager APIs
UserImport.py - User data import via FOLIO’s User Import API
_progress.py - Progress reporting utilities (Rich-based CLI, Redis backend)
custom_exceptions.py - Custom exception definitions
tests/#
Test suite:
Unit tests for each module
Test utilities
Making Changes#
1. Create a Branch#
git checkout -b feature/my-feature
Branch naming conventions:
feature/description- New featuresfix/description- Bug fixesdocs/description- Documentation changesrefactor/description- Code refactoring
2. Make Your Changes#
# Edit files in src/folio_data_import/
# Add tests in tests/
# Format code
uv run ruff format src/ tests/
# Lint and fix
uv run ruff check src/ tests/ --fix
# Run tests
uv run pytest
3. Commit and Push#
git add .
git commit -m "Clear description of changes"
git push origin feature/my-feature
4. Create Pull Request#
Go to GitHub and create a PR with:
Clear title
Description of changes
Link to related issues
Any testing instructions
Documentation#
Building Documentation#
# Install docs dependencies
uv sync --group docs
# Build HTML documentation
cd docs
uv run sphinx-build -b html -d _build/doctrees -c . source _build/html
Open docs/_build/html/index.html in a browser.
Live Reload During Development#
For faster documentation iteration:
cd docs
uv run sphinx-autobuild -b html -d _build/doctrees -c . source _build/html
This watches for changes and auto-rebuilds.
Editing Documentation#
Documentation source files are in docs/source/ as Markdown (via MyST parser):
# Title
Paragraph with introduction.
## Section
Details about the section.
### Subsection
More specific details.
Adding API Documentation#
Use Google-style docstrings for automatic API documentation:
def my_function(param1: str, param2: int = 5) -> bool:
"""Brief description.
Longer description with details.
Args:
param1: Description of param1.
param2: Description of param2. Defaults to 5.
Returns:
Description of return value.
Raises:
ValueError: When validation fails.
Example::
result = my_function("test", param2=10)
"""
Release Process#
Version Bump#
Update version in pyproject.toml:
[project]
version = "0.6.0"
Create a GitHub Release#
Go to the Releases page
Click Draft a new release
Create a new tag (e.g.,
v0.6.0) or select an existing oneAdd release notes describing the changes
Click Publish release
Publishing to PyPI#
This project uses PyPI Trusted Publishers via GitHub Actions. When you publish a new GitHub Release, the CI workflow automatically:
Builds the package
Publishes to PyPI using OIDC authentication
Do not publish directly - create a GitHub Release and let CI handle the PyPI upload.
Debugging#
Logging#
import logging
logger = logging.getLogger(__name__)
logger.debug("Debug message: %s", variable)
Run with debug logging:
uv run pytest --log-level=DEBUG
Interactive Debugging#
Use the built-in debugger:
breakpoint() # Python 3.7+ built-in
Or with pytest:
uv run pytest --pdb # Drop into debugger on failure
Useful Commands#
# List all test files
uv run pytest --collect-only
# Run with detailed output
uv run pytest -vv
# Run tests in parallel (install pytest-xdist)
uv run pytest -n auto
# Generate coverage report
uv run pytest --cov --cov-report=html
# Update all dependencies to latest compatible versions
uv lock --upgrade
# Add a new dependency
uv add package-name
# Add a dev dependency
uv add --group dev package-name
# Show dependency tree
uv tree
Getting Help#
Check existing issues and discussions
Review code in related modules
Ask in pull request comments
Contact a maintainer