Skip to content

Latest commit

 

History

History
778 lines (556 loc) · 18.1 KB

File metadata and controls

778 lines (556 loc) · 18.1 KB

Testing, Linting, and Code Coverage with UV

This guide covers how to run tests, perform linting, and generate code coverage reports using UV after the migration from setuptools to modern UV-based dependency management.

Quick Reference

# Install dependencies (production)
uv sync --frozen

# Install with development tools
uv sync --extra dev --frozen

# Run all tests
uv run pytest

# Run tests with coverage
uv run coverage run -m pytest
uv run coverage report

# Run linting
uv run ruff check .
uv run ruff check --fix .  # Auto-fix issues
uv run pylint aiagents4pharma/

# Run security scanning
uv run bandit -r aiagents4pharma/
uv run pip-audit

# Format code
uv run ruff format .

# Pre-commit hooks (runs all checks at once)
uv run pre-commit run --all-files

# Run individual pre-commit checks
uv run pre-commit run ruff --all-files         # Linting only
uv run pre-commit run ruff-format --all-files  # Formatting only
uv run pre-commit run bandit --all-files       # Security only
uv run pre-commit run pip-audit --all-files    # Vulnerabilities only

Setup & Installation

1. Install UV

Follow the official UV installation guide:

# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"

# With pip
pip install uv

2. Install Dependencies

# Clone repository
git clone https://github.com/VirtualPatientEngine/AIAgents4Pharma
cd AIAgents4Pharma

# Sync dependencies (creates .venv and installs everything)
uv sync --frozen

# Install with development dependencies (recommended)
uv sync --extra dev --frozen

3. System Prerequisites

macOS/Linux users:

# macOS
brew install libmagic

# Linux (Ubuntu/Debian)
sudo apt-get install libmagic1

# Linux (RHEL/CentOS)
sudo yum install file-libs

Windows users: libmagic is bundled automatically with python-magic.


Package Management with UV

Adding New Dependencies

Add Runtime Dependencies

# Add a new package to main dependencies
uv add "package-name"

# Add with specific version constraint
uv add "package-name>=1.0.0,<2.0.0"

# Add with exact version
uv add "package-name==1.2.3"

# Add from specific index
uv add "package-name" --index-url https://pypi.org/simple/

Add Development Dependencies

# Add to development dependencies
uv add --dev "pytest-mock"

# Add to specific extra group
uv add --extra dev "pip-audit==2.9.0"

Add from Git Repository

# Install from Git repository
uv add "git+https://github.com/user/repo.git"

# Install from specific branch/tag
uv add "git+https://github.com/user/repo.git@main"
uv add "git+https://github.com/user/repo.git@v1.0.0"

Removing Dependencies

# Remove a package
uv remove "package-name"

# Remove development dependency
uv remove --dev "package-name"

Updating Dependencies

# Update all dependencies
uv lock --upgrade

# Update specific package
uv add "package-name" --upgrade

# Update to latest compatible versions
uv sync --upgrade

Managing Virtual Environments

# Create virtual environment (automatic with uv sync)
uv venv

# Activate virtual environment
source .venv/bin/activate  # Unix/macOS
.venv\Scripts\activate     # Windows

# Install current project in development mode
uv pip install -e .

# List installed packages
uv pip list

# Show package information
uv pip show "package-name"

Lock File Management

# Generate/update uv.lock
uv lock

# Install from lock file (exact versions)
uv sync --frozen

# Check for dependency conflicts
uv pip check

Testing

Running Tests

Basic Test Execution

# Run all tests
uv run pytest

# Run with verbose output
uv run pytest -v

# Run specific test file
uv run pytest aiagents4pharma/talk2scholars/tests/test_main_agent.py

# Run specific test function
uv run pytest aiagents4pharma/talk2scholars/tests/test_main_agent.py::test_specific_function

# Run tests matching a pattern
uv run pytest -k "test_pdf"

Component-Specific Testing

# Test individual agents
uv run pytest aiagents4pharma/talk2scholars/tests/
uv run pytest aiagents4pharma/talk2biomodels/tests/
uv run pytest aiagents4pharma/talk2knowledgegraphs/tests/
uv run pytest aiagents4pharma/talk2aiagents4pharma/tests/
uv run pytest aiagents4pharma/talk2cells/tests/

Submodule-Specific Quality Checks

Pylint for Individual Submodules

# Run pylint on specific submodules (configuration in pyproject.toml)
uv run pylint aiagents4pharma/talk2scholars/
uv run pylint aiagents4pharma/talk2biomodels/
uv run pylint aiagents4pharma/talk2knowledgegraphs/
uv run pylint aiagents4pharma/talk2aiagents4pharma/
uv run pylint aiagents4pharma/talk2cells/

Coverage for Individual Submodules

# Run coverage on specific submodules
uv run coverage run --include="aiagents4pharma/talk2scholars/*" -m pytest --cache-clear aiagents4pharma/talk2scholars/tests/ && uv run coverage report
uv run coverage run --include="aiagents4pharma/talk2biomodels/*" -m pytest --cache-clear aiagents4pharma/talk2biomodels/tests/ && uv run coverage report
uv run coverage run --include="aiagents4pharma/talk2knowledgegraphs/*" -m pytest --cache-clear aiagents4pharma/talk2knowledgegraphs/tests/ && uv run coverage report
uv run coverage run --include="aiagents4pharma/talk2aiagents4pharma/*" -m pytest --cache-clear aiagents4pharma/talk2aiagents4pharma/tests/ && uv run coverage report
uv run coverage run --include="aiagents4pharma/talk2cells/*" -m pytest --cache-clear aiagents4pharma/talk2cells/tests/ && uv run coverage report

Pre-commit Hooks for Specific Files/Directories

# Run pre-commit on specific submodule
uv run pre-commit run --files aiagents4pharma/talk2scholars/*.py
uv run pre-commit run --files aiagents4pharma/talk2biomodels/*.py
uv run pre-commit run --files aiagents4pharma/talk2knowledgegraphs/*.py

# Run specific hook on specific submodule
uv run pre-commit run ruff --files aiagents4pharma/talk2scholars/*.py
uv run pre-commit run bandit --files aiagents4pharma/talk2knowledgegraphs/*.py

Test Configuration

Tests are configured in pyproject.toml:

[tool.pytest.ini_options]
asyncio_default_fixture_loop_scope = "function"
markers = ["integration: marks tests as integration tests"]
filterwarnings = ["ignore::DeprecationWarning"]

Environment Variables for Testing

# Required for certain tests
export OPENAI_API_KEY="your-openai-key"
export ZOTERO_API_KEY="your-zotero-key"  # For Talk2Scholars tests
export ZOTERO_USER_ID="your-zotero-id"   # For Talk2Scholars tests

# Optional for LangSmith tracing
export LANGCHAIN_TRACING_V2=true
export LANGCHAIN_API_KEY="your-langsmith-key"

Code Coverage

Basic Coverage Commands

Generate Coverage Reports

# Run tests with coverage
uv run coverage run -m pytest

# Show coverage report in terminal
uv run coverage report

# Generate HTML coverage report
uv run coverage html

# Generate XML coverage report (for CI/CD)
uv run coverage xml

Advanced Coverage Usage

Component-Specific Coverage:

# Coverage for specific component (standard configuration)
# Note: Use quotes on macOS/zsh shell, GitHub Actions CI doesn't require them
uv run coverage run --include="aiagents4pharma/talk2scholars/*" -m pytest --cache-clear aiagents4pharma/talk2scholars/tests/ && uv run coverage report

# Run coverage on each subfolder individually (local development - use quotes for macOS/zsh)
uv run coverage run --include="aiagents4pharma/talk2scholars/*" -m pytest --cache-clear aiagents4pharma/talk2scholars/tests/ && uv run coverage report
uv run coverage run --include="aiagents4pharma/talk2biomodels/*" -m pytest --cache-clear aiagents4pharma/talk2biomodels/tests/ && uv run coverage report
uv run coverage run --include="aiagents4pharma/talk2knowledgegraphs/*" -m pytest --cache-clear aiagents4pharma/talk2knowledgegraphs/tests/ && uv run coverage report
uv run coverage run --include="aiagents4pharma/talk2aiagents4pharma/*" -m pytest --cache-clear aiagents4pharma/talk2aiagents4pharma/tests/ && uv run coverage report
uv run coverage run --include="aiagents4pharma/talk2cells/*" -m pytest --cache-clear aiagents4pharma/talk2cells/tests/ && uv run coverage report

# Alternative format for CI environments (without quotes)
# uv run coverage run --include=aiagents4pharma/talk2scholars/* -m pytest --cache-clear aiagents4pharma/talk2scholars/tests/

# Coverage with source specification (all components)
uv run coverage run --source=aiagents4pharma -m pytest

# Show missing lines
uv run coverage report -m

# Show coverage by file with line numbers
uv run coverage report --show-missing

Coverage Configuration (pyproject.toml):

[tool.coverage.run]
source = ["aiagents4pharma"]
omit = [
    "*/tests/*",
    "*/test_*",
    "docs/*",
    "app/*",
]

[tool.coverage.report]
exclude_lines = [
    "pragma: no cover",
    "def __repr__",
    "if self.debug:",
    "if settings.DEBUG",
    "raise AssertionError",
    "raise NotImplementedError",
    "if 0:",
    "if __name__ == .__main__.:",
    "class .*\\bProtocol\\):",
    "@(abc\\.)?abstractmethod",
]

[tool.coverage.xml]
output = "coverage.xml"

Coverage Thresholds

Check Coverage Threshold:

# Check if coverage meets minimum threshold
COVERAGE=$(uv run coverage report -m | awk 'END {print int($NF)}')
if [[ $COVERAGE -lt 80 ]]; then
    echo "Coverage is below 80%"
    exit 1
fi

HTML Reports: Open htmlcov/index.html after running uv run coverage html to see detailed coverage information with highlighted missing lines.


Linting & Code Quality

Ruff (Fast Python Linter)

Basic Ruff Commands

# Check all files
uv run ruff check .

# Check specific directory
uv run ruff check aiagents4pharma/

# Fix auto-fixable issues
uv run ruff check --fix .

# Check with specific rules
uv run ruff check --select E,W,F .

# Format code
uv run ruff format .

Ruff Configuration (pyproject.toml)

[tool.ruff]
target-version = "py312"
line-length = 100

[tool.ruff.lint]
select = [
    "E",  # pycodestyle errors
    "W",  # pycodestyle warnings
    "F",  # pyflakes
    "I",  # isort
    "B",  # flake8-bugbear
    "C4", # flake8-comprehensions
    "UP", # pyupgrade
]
ignore = [
    "B008",  # do not perform function calls in argument defaults
    "C901",  # too complex
]

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401"]

Pylint (Comprehensive Static Analysis)

Basic Pylint Commands

# Run pylint on entire codebase
uv run pylint aiagents4pharma/

# Run on specific component
uv run pylint aiagents4pharma/talk2scholars/

# Standard configuration (disabled rules configured in pyproject.toml)
uv run pylint aiagents4pharma/

# Run on each subfolder individually
uv run pylint aiagents4pharma/talk2scholars/
uv run pylint aiagents4pharma/talk2biomodels/
uv run pylint aiagents4pharma/talk2knowledgegraphs/
uv run pylint aiagents4pharma/talk2cells/

# Generate JSON report
uv run pylint aiagents4pharma/ --output-format=json --reports=no > pylint-report.json

# Show only errors and warnings
uv run pylint aiagents4pharma/ --errors-only

Pylint Configuration (pyproject.toml)

[tool.pylint.MASTER]
extension-pkg-allow-list = ["pcst_fast"]

[tool.pylint.messages_control]
disable = ["R0801", "R0902", "W0221", "W0122"]

Security Scanning

Bandit (Security Linter)

Basic Bandit Commands

# Scan entire codebase
uv run bandit -r aiagents4pharma/

# Use configuration from pyproject.toml
uv run bandit -c pyproject.toml -r aiagents4pharma/

# Generate JSON report
uv run bandit -c pyproject.toml -f json -o bandit-report.json -r aiagents4pharma/

# Show only high severity issues
uv run bandit -r aiagents4pharma/ -ll

Bandit Configuration (pyproject.toml)

[tool.bandit]
exclude_dirs = ["tests", "test_*"]
skips = ["B101", "B601"]

[tool.bandit.assert_used]
skips = ["*_test.py", "*/test_*.py"]

Vulnerability Scanning

pip-audit (Dependency Vulnerability Scanner)

# Basic vulnerability scan
uv run pip-audit

# Generate reports
uv run pip-audit --desc --format=json --output=audit-report.json
uv run pip-audit --desc --format=markdown --output=audit-report.md

# Scan specific requirements
uv run pip-audit --desc --format=table

safety (Alternative Vulnerability Scanner)

# Basic safety check
uv run safety check

# JSON output
uv run safety check --json --output safety-report.json

# Check specific files
uv run safety check --file=uv.lock

Type Checking (MyPy)

Current Status

MyPy is currently disabled in pre-commit due to 1,121 type annotation errors that require dedicated cleanup effort.

Manual MyPy Execution

# Run mypy (will show many errors)
uv run mypy aiagents4pharma/

# Run with lenient settings
uv run mypy aiagents4pharma/ --ignore-missing-imports --show-error-codes

# Check specific component
uv run mypy aiagents4pharma/talk2scholars/ --ignore-missing-imports

MyPy Configuration (pyproject.toml)

[tool.mypy]
python_version = "3.12"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
warn_no_return = true
warn_unreachable = true
strict_equality = true

[[tool.mypy.overrides]]
module = [
    "setuptools.*",
    "pkg_resources.*",
]
ignore_missing_imports = true

Pre-commit Hooks

Installation & Setup

# Install pre-commit hooks
uv run pre-commit install

# Run hooks on all files
uv run pre-commit run --all-files

# Run specific hook
uv run pre-commit run ruff

# Update hooks to latest versions
uv run pre-commit autoupdate

Pre-commit Configuration (.pre-commit-config.yaml)

The repository includes comprehensive pre-commit hooks:

  • Ruff: Fast linting and code formatting
  • Bandit: Security vulnerability scanning
  • General hooks: Trailing whitespace, YAML validation, large file checks, etc.
  • pip-audit: Dependency vulnerability scanning

Individual Pre-commit Commands

# Run all hooks
uv run pre-commit run --all-files

# Run specific hooks
uv run pre-commit run ruff --all-files         # Linting with auto-fix
uv run pre-commit run ruff-format --all-files  # Code formatting
uv run pre-commit run bandit --all-files       # Security scanning
uv run pre-commit run pip-audit --all-files    # Dependency vulnerabilities

# General quality checks
uv run pre-commit run trailing-whitespace --all-files
uv run pre-commit run end-of-file-fixer --all-files
uv run pre-commit run check-yaml --all-files
uv run pre-commit run check-added-large-files --all-files

# Run on specific files
uv run pre-commit run ruff --files aiagents4pharma/talk2scholars/main.py
uv run pre-commit run bandit --files aiagents4pharma/talk2knowledgegraphs/tools/*.py

Bypassing Hooks (Emergency Use)

# Skip all hooks for urgent commits
git commit -m "urgent fix" --no-verify

# Skip specific hooks
SKIP=ruff git commit -m "work in progress"

Automation & CI Integration

GitHub Actions Integration

All these tools are integrated into our CI/CD pipeline:

# Simulate CI locally
uv run pytest                                    # Tests
uv run coverage run -m pytest && coverage report # Coverage
uv run ruff check .                              # Linting
uv run pylint aiagents4pharma/                   # Static analysis
uv run bandit -c pyproject.toml -r aiagents4pharma/ # Security
uv run pip-audit                                 # Vulnerabilities

Local Development Workflow

# 1. Make changes to code
git checkout -b feature/my-feature

# 2. Run tests and quality checks
uv run pytest
uv run coverage run -m pytest && uv run coverage report
uv run ruff check --fix .

# 3. Run pre-commit hooks
uv run pre-commit run --all-files

# 4. Commit changes
git add .
git commit -m "feat: add new feature"

# 5. Push and create PR
git push origin feature/my-feature

Performance & Optimization

UV Advantages

  • Speed: UV is 10-100x faster than pip
  • Reliability: Deterministic dependency resolution
  • Caching: Smart caching reduces repeat installations
  • Lock files: uv.lock ensures reproducible environments

Development Tips

Faster Test Iteration

# Run tests with pytest-xdist for parallel execution
uv add --dev pytest-xdist
uv run pytest -n auto

# Use pytest cache for faster re-runs
uv run pytest --cache-clear  # Clear cache when needed
uv run pytest               # Reuse cache for speed

Efficient Coverage

# Run coverage only on changed files
git diff --name-only | grep "\.py$" | xargs uv run coverage run -m pytest

# Use coverage's fast mode
uv run coverage run --parallel-mode -m pytest
uv run coverage combine

Troubleshooting

Common Issues

Dependency Conflicts

# Fix dependency conflicts using uv
uv add "package>=new-version"
uv lock --upgrade

# Example: Fix PyArrow compatibility
uv add "pyarrow>=14.0.0"
uv add "datasets>=4.0.0"

# Clear and reinstall if needed
rm -rf .venv
uv sync --extra dev --frozen

Import Errors

# Ensure you're in the right environment
uv run python -c "import sys; print(sys.executable)"

# Check installed packages
uv pip list

Permission Issues (Linux/macOS)

# Fix ownership if needed
sudo chown -R $USER:$USER .venv

# Alternative: use --user flag
uv sync --user

Windows-Specific Issues

# Use PowerShell execution policy
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

# Use forward slashes in paths
uv run pytest aiagents4pharma/talk2scholars/tests/

Getting Help

This guide provides a comprehensive overview of testing, linting, and code coverage using UV. The modern toolchain ensures fast, reliable, and secure development practices.