Code Style
Python Version
JAFF supports Python 3.9 and higher. Write code that is compatible with Python 3.9+.
# Good - use modern type hints
from typing import List, Dict, Optional
def process(items: List[str]) -> Dict[str, int]:
pass
# Avoid - Python 3.8 syntax
def process(items: list[str]) -> dict[str, int]: # Requires 3.9+
pass
Code Formatting
Use Ruff for both formatting and linting, replacing Black entirely.
# Format all code
ruff format src/ tests/
# Check formatting without modifying (Black --check equivalent)
ruff format --check src/
# Format specific file
ruff format src/jaff/network.py
## Code Formatting
Naming Conventions
Variables and Functions
Use snake_case:
# Good
user_name = "Alice"
def calculate_rate(temperature):
pass
# Avoid
userName = "Alice"
def CalculateRate(temperature):
pass
Classes
Use PascalCase:
Constants
Use UPPER_SNAKE_CASE:
Private Members
Prefix with underscore:
class Network:
def __init__(self):
self._internal_data = [] # Private
self.public_data = [] # Public
def _helper_method(self): # Private
pass
def public_method(self): # Public
pass
Type Hints
Always Use Type Hints
# Good
def compute_rate(temperature: float, alpha: float) -> float:
return alpha * temperature
# Avoid
def compute_rate(temperature, alpha):
return alpha * temperature
Import Types
from typing import List, Dict, Optional, Union, Tuple, Any
from pathlib import Path
import numpy as np
def process_species(
names: List[str],
masses: np.ndarray,
options: Optional[Dict[str, Any]] = None
) -> Tuple[List[str], np.ndarray]:
pass
Complex Types
from typing import List, Dict, Union, Optional
# Type aliases for clarity
SpeciesDict = Dict[str, int]
RateExpression = Union[str, float]
def load_network(
filename: str,
species_map: Optional[SpeciesDict] = None
) -> Network:
pass
Docstrings
Google Style
Use Google-style docstrings:
def compute_rates(network: Network, temperature: float) -> np.ndarray:
"""Compute reaction rate coefficients.
This function calculates rate coefficients for all reactions
in the network at the specified temperature.
Args:
network: Chemical reaction network
temperature: Gas temperature in Kelvin
Returns:
Array of rate coefficients in cm³/s
Raises:
ValueError: If temperature is negative
Example:
>>> net = Network("network.dat")
>>> rates = compute_rates(net, 100.0)
>>> print(rates[0])
1.2e-10
"""
if temperature < 0:
raise ValueError("Temperature must be non-negative")
return network.calculate_rates(temperature)
Class Docstrings
class Codegen:
"""Multi-language code generator for chemical networks.
This class generates optimized code for evaluating reaction rates,
ODEs, and Jacobians in multiple programming languages.
Attributes:
net: Chemical reaction network
lang: Target programming language
ioff: Array indexing offset (0 or 1)
Example:
>>> net = Network("network.dat")
>>> cg = Codegen(network=net, lang="c++")
>>> rates = cg.get_rates(use_cse=True)
"""
def __init__(self, network: Network, lang: str = "c++"):
"""Initialize code generator.
Args:
network: Chemical reaction network
lang: Target language (c++, c, fortran, python)
"""
pass
Module Docstrings
"""Chemical reaction network module.
This module provides the Network class for loading and managing
chemical reaction networks from various file formats.
Example:
>>> from jaff import Network
>>> net = Network("network.dat")
"""
Error Handling
Specific Exceptions
# Good
try:
net = Network(filename)
except FileNotFoundError:
print(f"File not found: {filename}")
except ValueError as e:
print(f"Invalid network format: {e}")
# Avoid
try:
net = Network(filename)
except: # Too broad
pass
Custom Exceptions
class NetworkError(Exception):
"""Base exception for network errors."""
pass
class ParseError(NetworkError):
"""Error parsing network file."""
pass
def load_network(filename: str) -> Network:
try:
return Network(filename)
except ValueError as e:
raise ParseError(f"Failed to parse {filename}: {e}")
Error Messages
# Good - descriptive
raise ValueError(f"Temperature must be positive, got {temp}")
# Avoid - vague
raise ValueError("Bad temperature")
Testing Style
Test Function Names
# Good - descriptive
def test_network_loads_krome_format():
pass
def test_codegen_raises_error_for_invalid_language():
pass
# Avoid
def test1():
pass
Test Organization
class TestNetwork:
"""Tests for Network class."""
def test_load_from_file(self):
"""Test loading network from file."""
pass
def test_validate_species(self):
"""Test species validation."""
pass
Assertions
# Good - clear assertions
assert len(net.species) == 35
assert net.label == "react_COthin"
assert_almost_equal(rate, 1.2e-10, decimal=15)
# Avoid - unclear
assert x
Performance
List Comprehensions
# Good - fast
species_names = [s.name for s in network.species]
# Slower
species_names = []
for s in network.species:
species_names.append(s.name)
String Formatting
# Good - f-strings (fast, readable)
message = f"Network has {n} species"
# Avoid - slow
message = "Network has " + str(n) + " species"
Best Practices
Use Context Managers
# Good
with open(filename, 'w') as f:
f.write(content)
# Avoid
f = open(filename, 'w')
f.write(content)
f.close()
Use Pathlib
from pathlib import Path
# Good
path = Path("networks") / "react.dat"
if path.exists():
content = path.read_text()
# Avoid
import os
path = os.path.join("networks", "react.dat")
if os.path.exists(path):
with open(path) as f:
content = f.read()
Early Returns
# Good - early return
def process(value: Optional[int]) -> int:
if value is None:
return 0
if value < 0:
return -1
return value * 2
# Avoid - nested if
def process(value):
if value is not None:
if value >= 0:
return value * 2
else:
return -1
else:
return 0
Code Review Checklist
Before submitting code:
- Code formatted with ruff
- Sort imports with ruff
- No linting errors (ruff)
- Type hints added
- Docstrings written
- Tests added/updated
- No commented-out code
- No hardcoded paths
- Error handling in place