This file contains build commands, code style guidelines, and development practices for agentic coding agents working in this netlab repository.
# Type checking
python3 -m mypy -p netsim
python3 -m mypy netsim/extra/*/plugin.py # Type check extra plugins
# Linting and formatting
ruff check netsim/ # Lint with ruff
ruff format netsim/ # Format with ruff
# YAML linting
yamllint netsim/*yml netsim/**/*yml tests/**/*yml
# Testing
python3 -m pytest -vvv -k 'xform_ or error_cases' # Main test suite
python3 -m pytest -vvv -k 'coverage' # Coverage tests
python3 -m pytest tests/integration/ # Integration tests
python3 -m pytest tests/validation/ # Validation tests
# Run single test file
python3 -m pytest -vvv tests/path/to/test_file.py
# Run tests with CI mode (fail fast)
cd tests && ./run-tests.sh ci
cd tests && ./run-coverage-tests.sh ci# Install dependencies
pip3 install -r requirements.txt
pip3 install -r requirements-dev.txt
# Install in development mode
pip3 install -e .- Use
rufffor import sorting (configured in ruff.toml) - Standard library imports first, then third-party, then local imports
- Avoid wildcard imports except in
__init__.pyfiles
- Line length: 120 characters (configured in ruff.toml)
- Indentation: 2 spaces (not tabs)
- Quotes: Double quotes for strings
- Trailing commas: Enabled for better diffs
- Align the comments
- Required: All functions must have type hints (mypy enforces this)
- Strict mode:
disallow_untyped_defs = Truein mypy.ini - Use
typingmodule for generic types - Use
Boxtype for configuration objects
- Variables and functions:
snake_case - Classes:
PascalCase - Constants:
UPPER_SNAKE_CASE - Private members: Prefix with underscore (
_)
- Use custom exception classes from
netsim.utils.log:FatalErrorfor unrecoverable errorsIncorrectAttr,IncorrectType,IncorrectValuefor validation errorsMissingValuefor required but missing data
- Use
log.fatal(),log.error(),log.warning()for user-facing messages - Never use
print()in library code (allowed in CLI modules)
- CLI modules:
netsim/cli/- can use print statements - Device modules:
netsim/devices/- device-specific implementations - Extra modules:
netsim/extra/- optional plugins - Output modules:
netsim/outputs/- data export formats - Augment modules:
netsim/augment/- topology transformation
- The topology data structure is a Box object with
default_box=Trueandbox_dots=True - In most cases, the Box object is converted into a dictionary before being used in a Jinja2 template
With the Box settings used in the topology data structure:
- The interim dictionaries are created automatically:
a.b.c=1works even whena.bdoes not exist yet. - The dotted paths can be used in indices (for example,
a['b.c'] = 1) and in in tests (for example'b.c' in a)
- The Jinja2 environment uses a custom
undefinedmethod that can handle dictionary hierarchy. For example,a.b.c is definedreturns False instead of crashing even whena.bdoes not exist. There is no need for an extraa.b is definedguard. - Use
for ... if ...loops whenever possible instead of separateforfollowed byif/endifblock. - Prefer
for key,value in some.dict|default({})|dictsortoverif some.dict is definedfollowed by theforloop. - When in doubt, check the device configuration developer documentation. If that feels off, check the module topology transformation code.
- Test files in
tests/directory - Use
pytestframework - Integration tests in
tests/integration/ - Validation tests in
tests/validation/ - Coverage tests in
tests/coverage/ - Error case tests in
tests/errors/
- Use
Boxobjects for hierarchical configuration - YAML files for configuration and topology definitions
- Jinja2 templates for configuration generation
- Global settings in
netsim/defaults/
- Docstrings follow Google style (not strictly enforced but preferred)
- CLI help in
netsim/cli/help.py - User documentation in
docs/directory
From .github/copilot-instructions.md:
- Think Different: Question assumptions, seek elegant solutions
- Obsess Over Details: Understand existing patterns and codebase philosophy
- Plan Like Da Vinci: Document architecture before implementation
- Craft, Don't Code: Write elegant, self-documenting code
- Iterate Relentlessly: Test, refine, and improve continuously
- Simplify Ruthlessly: Remove complexity without losing power
When you don't have to change the code for other reasons:
- DO NOT change single/double quotes
- DO NOT insert whitespaces
- DO NOT reformat code that would otherwise not be changed
- Keep comments aligned
- Jinja2: Template rendering (>=3.1.4)
- PyYAML: YAML parsing (>=5.0.0)
- netaddr: Network address manipulation (>=0.8.0)
- python-box: Dictionary-like objects (>=7.2.0)
- rich: Terminal formatting
- requests: HTTP client
- Python: 3.8+ (due to subprocess.capture_output and typing.Final)
- OS: Linux, macOS
- Package managers: pip, setuptools
- All code must pass
mypytype checking - All code must pass
rufflinting and formatting - All YAML must pass
yamllint - Tests must pass before merging
- No unused imports or variables (ruff enforces)
- The documentation files must be in
docs/dev/config - The new files have to be included in ToC in
docs/dev/config/index.md - The configuration template developer documentation is not the user guide. The data structures described must match the output of the transformation process, not the input attribute schema, and there are significant differences between the two.
- There is no need to include supported platforms or more than three sample templates.
- The documentation must describe all relevant node attributes, followed by all relevant interface attributes. When needed, add VLAN- or VRF attributes.
- Do not explain the lab topology attributes; they have their own documentation.
- Find the relevant tests in the
tests/integrationdirectory tree and mention them - Do not use
jinja2syntax highlighter; it does not work. - Mention where the configuration templates should be stored, and how the platform name is calculated (based on
netlab_device_typeoransible_network_osvariable)