Contributing
Contributing to eos-downloader¶
Thank you for your interest in contributing to the eos-downloader project! This guide will help you get started with contributing to this Python CLI tool for downloading Arista EOS and CloudVision Portal software packages.
Overview¶
eos-downloader is a Python CLI application built with Click, Rich, and pytest. It provides both a command-line interface and a programmatic API for downloading Arista software packages. The project follows modern Python development practices with comprehensive testing, type hints, and automated CI/CD.
Getting Started¶
1. Fork the Repository¶
- Navigate to the eos-downloader repository
- Click the “Fork” button in the top-right corner
- Select your GitHub account to create a fork
2. Clone Your Fork¶
# Clone your forked repository
git clone https://github.com/YOUR_USERNAME/eos-downloader.git
cd eos-downloader
# Add the original repository as upstream
git remote add upstream https://github.com/titom73/eos-downloader.git
3. Set Up Development Environment¶
Prerequisites¶
- Python 3.9 or higher
- Git
- A virtual environment tool (venv, conda, etc.)
Environment Setup¶
# Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install the package in development mode with all dependencies
pip install -e ".[dev]"
# Install pre-commit hooks for code quality
pre-commit install
Verify Installation¶
# Run tests to ensure everything is working
pytest
# Check code style
tox -e lint
# Run type checking
tox -e type
Development Workflow¶
1. Create a Feature Branch¶
# Sync your fork with upstream
git fetch upstream
git checkout main
git merge upstream/main
# Create a new feature branch
git checkout -b feature/your-feature-name
# or
git checkout -b fix/issue-description
2. Python Development Guidelines¶
Code Style¶
The project follows strict Python coding standards:
- PEP 8 compliance with 120 character line limit
- Type hints for all function signatures and class attributes
- NumPy-style docstrings for all public functions and classes
- Black for code formatting
- isort for import organization
Key Principles¶
# Always use type hints
from typing import Optional, List, Dict, Any
from pathlib import Path
def download_eos_image(
version: str,
image_format: str,
output_dir: Path,
token: str,
) -> Path:
"""Download an EOS image from Arista server.
Parameters
----------
version : str
EOS version in format MAJOR.MINOR.PATCH[TYPE] (e.g., "4.29.3M")
image_format : str
Image format (e.g., "64", "vEOS", "cEOS")
output_dir : Path
Directory where the image will be saved
token : str
Arista API authentication token
Returns
-------
Path
Path to the downloaded image file
Raises
------
TokenExpiredError
If the authentication token has expired
DownloadError
If the download fails
"""
# Implementation here
Project Structure¶
eos_downloader/- Main packagecli/- Click-based command line interfacelogics/- Business logic and core functionalitymodels/- Data models with Pydantic validationexceptions/- Custom exception classeshelpers/- Utility functionstests/- Test suite organized by package structuredocs/- Documentation files
Domain Knowledge¶
Understanding Arista’s ecosystem is crucial:
- EOS versions: Format is
MAJOR.MINOR.PATCH[TYPE](e.g., “4.29.3M”) - Release types: M (Maintenance), F (Feature), INT (Internal)
- Image formats: 64-bit, vEOS (virtual), cEOS (container)
- CVP versions: CloudVision Portal with different format
3. Writing and Running Tests¶
Test Organization¶
tests/
├── unit/ # Unit tests (isolated, fast)
│ ├── cli/ # CLI command tests
│ ├── logics/ # Business logic tests
│ └── models/ # Data model tests
├── integration/ # Integration tests (with external dependencies)
└── lib/ # Test utilities and fixtures
Writing Unit Tests¶
import pytest
from pathlib import Path
from unittest.mock import Mock, patch
from eos_downloader.models.version import EosVersion
class TestEosVersion:
"""Test suite for EOS version parsing and validation."""
def test_valid_version_parsing(self):
"""Test parsing of valid EOS version strings."""
version = EosVersion.from_str("4.29.3M")
assert version.major == 4
assert version.minor == 29
assert version.patch == 3
assert version.rtype == "M"
assert version.branch == "4.29"
def test_invalid_version_raises_error(self):
"""Test that invalid version strings raise ValueError."""
with pytest.raises(ValueError, match="Invalid version format"):
EosVersion.from_str("invalid-version")
@pytest.mark.parametrize(
"version_str,expected_branch",
[
("4.29.3M", "4.29"),
("4.30.1F", "4.30"),
("4.28.10M", "4.28"),
]
)
def test_branch_calculation(self, version_str: str, expected_branch: str):
"""Test branch calculation for different versions."""
version = EosVersion.from_str(version_str)
assert version.branch == expected_branch
Running Tests¶
# Run all tests
pytest
# Run with coverage
pytest --cov=eos_downloader --cov-report=term-missing
# Run specific test file
pytest tests/unit/models/test_version.py
# Run tests matching a pattern
pytest -k "test_version"
# Run with verbose output
pytest -v
# Run tests in parallel (if you have pytest-xdist installed)
pytest -n auto
Test Best Practices¶
- Use descriptive test names that explain what is being tested
- Follow AAA pattern: Arrange, Act, Assert
- Mock external dependencies (API calls, file system operations)
- Use fixtures for common setup and teardown
- Test both success and failure scenarios
- Parametrize tests to cover multiple cases efficiently
4. Code Quality Checks¶
Before submitting your changes, ensure they pass all quality checks:
# Run all checks with tox
tox
# Individual checks
tox -e lint # Linting with flake8 and pylint
tox -e type # Type checking with mypy
tox -e py310 # Tests on Python 3.10
# Format code
black eos_downloader/ tests/
isort eos_downloader/ tests/
# Pre-commit checks (runs automatically on commit)
pre-commit run --all-files
Submitting Your Contribution¶
1. Commit Your Changes¶
Follow conventional commit messages:
# Stage your changes
git add .
# Commit with descriptive message
git commit -m "feat(cli): add support for EOS 4.31 versions
- Added parsing for new version format
- Updated XML querier to handle new structure
- Added comprehensive tests for version parsing
Closes #123"
2. Push to Your Fork¶
git push origin feature/your-feature-name
3. Create a Pull Request¶
- Navigate to your fork on GitHub
- Click “Compare & pull request”
- Fill out the PR template with:
- Clear description of changes
- Reference to related issues
- Testing performed
- Breaking changes (if any)
4. PR Review Process¶
- Automated checks will run (tests, linting, type checking)
- Code coverage must not decrease significantly
- Maintainers will review your code
- Address any feedback or requested changes
- Once approved, your PR will be merged
Development Tips¶
Working with Arista API¶
# Always use environment variables for tokens
token = os.environ.get('ARISTA_TOKEN')
if not token:
raise ValueError("ARISTA_TOKEN not found")
# Use the project's exception hierarchy
from eos_downloader.exceptions import TokenExpiredError, DownloadError
try:
download_file(url, token)
except requests.HTTPError as e:
if e.response.status_code == 401:
raise TokenExpiredError("Token expired") from e
raise DownloadError(f"HTTP error: {e}") from e
Debugging¶
# Enable debug logging
export ARDL_LOG_LEVEL=debug
# Run with Python debugger
python -m pdb -m eos_downloader.cli get eos --version 4.29.3M
# Use pytest debugger
pytest --pdb tests/unit/models/test_version.py::test_specific_function
Performance Considerations¶
- Use lazy evaluation and generators for large datasets
- Implement caching for expensive operations
- Consider concurrent downloads for multiple files
- Profile code with
cProfilefor performance bottlenecks
Getting Help¶
- Check existing issues
- Read the documentation
- Ask questions in discussions or create a new issue
- Join our community discussions
Code of Conduct¶
This project follows the Contributor Covenant Code of Conduct. Please read and follow it in all your interactions with the project.
License¶
By contributing to eos-downloader, you agree that your contributions will be licensed under the same Apache License 2.0 that covers the project.