Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,27 @@ jobs:
strategy:
matrix:
python-version:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
if: matrix.python-version != '3.8'
run: |
python -m pip install --upgrade pip
pip install -r tests/requirements.txt
- name: Lint with flake8
if: matrix.python-version != '3.8'
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
if: matrix.python-version != '3.8'
run: |
pytest
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
rev: v6.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-added-large-files
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.7
rev: v0.14.8
hooks:
- id: ruff
args: [ --fix ]
- id: ruff-format
- repo: https://github.com/PyCQA/isort
rev: 6.0.0
rev: 7.0.0
hooks:
- id: isort
args: [--profile, black]
- repo: https://github.com/psf/black
rev: 25.1.0
rev: 25.11.0
hooks:
- id: black
language_version: python3
2 changes: 1 addition & 1 deletion conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#
# This file is part of VIRL 2
# Copyright (c) 2019-2025, Cisco Systems, Inc.
# Copyright (c) 2019-2026, Cisco Systems, Inc.
# All rights reserved.
#
#
Expand Down
4 changes: 2 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
#
# This file is part of VIRL 2
# Copyright (c) 2019-2025, Cisco Systems, Inc.
# Copyright (c) 2019-2026, Cisco Systems, Inc.
# All rights reserved.
#
# Configuration file for the Sphinx documentation builder.
Expand All @@ -26,7 +26,7 @@
# -- Project information -----------------------------------------------------

project = "virl2_client"
copyright = "Copyright (c) 2019-2025, Cisco Systems, Inc."
copyright = "Copyright (c) 2019-2026, Cisco Systems, Inc."
author = "VIRL2 team <virl@cisco.com>"

# The short X.Y version
Expand Down
2 changes: 1 addition & 1 deletion examples/licensing.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
#
# This file is part of VIRL 2
# Copyright (c) 2019-2025, Cisco Systems, Inc.
# Copyright (c) 2019-2026, Cisco Systems, Inc.
# All rights reserved.
#
# Python bindings for the Cisco VIRL 2 Network Simulation Platform
Expand Down
2 changes: 1 addition & 1 deletion examples/link_conditioning.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
#
# This file is part of VIRL 2
# Copyright (c) 2019-2025, Cisco Systems, Inc.
# Copyright (c) 2019-2026, Cisco Systems, Inc.
# All rights reserved.
#
# Python bindings for the Cisco VIRL 2 Network Simulation Platform
Expand Down
2 changes: 1 addition & 1 deletion examples/sample.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
#
# This file is part of VIRL 2
# Copyright (c) 2019-2025, Cisco Systems, Inc.
# Copyright (c) 2019-2026, Cisco Systems, Inc.
# All rights reserved.
#
# Python bindings for the Cisco VIRL 2 Network Simulation Platform
Expand Down
4,037 changes: 1,790 additions & 2,247 deletions poetry.lock

Large diffs are not rendered by default.

27 changes: 18 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
#
# This file is part of VIRL 2
# Copyright (c) 2019-2026, Cisco Systems, Inc.
# All rights reserved.
#
[tool.poetry]
name = "virl2_client"
version = "2.9.1"
version = "2.10.0"
description = "VIRL2 Client Library"
authors = ["Simon Knight <simknigh@cisco.com>", "Ralph Schmieder <rschmied@cisco.com>"]
license = "Apache-2.0"
readme = "README.md"
homepage = "https://github.com/ciscodevnet/virl2-client"
repository = "https://github.com/ciscodevnet/virl2-client"
include = ["examples/*"]
exclude = ["tests/**/*"]
exclude = [
"tests/**/*",
"examples/*"
]

classifiers = [
"Development Status :: 4 - Beta",
Expand All @@ -20,13 +27,12 @@ classifiers = [
]

[tool.poetry.dependencies]
# cryptography requires python>=3.9.2
python = "^3.9.2"
python = "^3.10"
httpx = "^0.28.1"

# https://github.com/python-poetry/poetry/pull/606 -- no support for optional dev-deps
# optional package for events
aiohttp = {version = "~3.11.18", optional = true}
aiohttp = {version = "^3", optional = true}

# optional pyATS package
# (this pulls in a lot more dependencies)
Expand All @@ -35,21 +41,24 @@ pyats = {version = "^25", optional = true}

# optional packages for documentation build
sphinx_rtd_theme = {version="^3", optional = true}
# version 8 is available but requires python>=3.10
sphinx = {version="^7", optional = true}
# version 8.2.3 is available but requires python>=3.11
sphinx = {version="<8.2", optional = true}

[tool.poetry.group.dev.dependencies]
flake8 = "^7"
poetry-plugin-export = "*"
pre-commit = "^4"
pytest = "*"
pytest-xdist = "*"
respx = "^0.22.0"

[tool.poetry.extras]
events = ["aiohttp"]
pyats = ["pyats"]
docs = ["sphinx", "sphinx_rtd_theme"]

[tool.poetry.requires-plugins]
poetry-plugin-export = "*"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
7 changes: 0 additions & 7 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,7 @@ addopts =
# --cov-branch
# --cov-report term-missing

filterwarnings =
# genie/pyats/unicon need some updates
ignore: pkg_resources is deprecated.*:DeprecationWarning
ignore: Deprecated call to `pkg_resources.declare_namespace.*:DeprecationWarning

junit_family=xunit1
asyncio_mode=auto
asyncio_default_fixture_loop_scope=function

# start pytest with --log-level=info
# log_cli=True
Expand Down
52 changes: 42 additions & 10 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#
# This file is part of VIRL 2
# Copyright (c) 2019-2025, Cisco Systems, Inc.
# Copyright (c) 2019-2026, Cisco Systems, Inc.
# All rights reserved.
#
# Python bindings for the Cisco VIRL 2 Network Simulation Platform
Expand All @@ -18,45 +18,58 @@
# limitations under the License.
#

import sys
from collections.abc import Iterator
from functools import partial
from pathlib import Path
from unittest.mock import patch
from unittest.mock import MagicMock, patch

import httpx
import pytest
from respx import MockRouter

from virl2_client.models import authentication
from virl2_client.virl2_client import ClientLibrary

# Patch sys.stdin.isatty to simulate an interactive terminal
sys.stdin.isatty = lambda: True

CURRENT_VERSION = ClientLibrary.VERSION.version_str
FAKE_HOST = "https://0.0.0.0"
FAKE_HOST_API = f"{FAKE_HOST}/api/v0/"


def client_library_patched_system_info(version):
def client_library_patched_system_info(version: str) -> Iterator[MagicMock]:
with patch.object(
ClientLibrary, "system_info", return_value={"version": version, "ready": True}
) as cl:
yield cl


@pytest.fixture
def client_library_server_current():
def client_library_server_current() -> Iterator[MagicMock]:
yield from client_library_patched_system_info(version=CURRENT_VERSION)


@pytest.fixture
def client_library_server_2_0_0():
def client_library_server_2_0_0() -> Iterator[MagicMock]:
yield from client_library_patched_system_info(version="2.0.0")


@pytest.fixture
def client_library_server_2_19_0():
def client_library_server_2_19_0() -> Iterator[MagicMock]:
yield from client_library_patched_system_info(version="2.19.0")


@pytest.fixture
def mocked_session():
def client_library_server_2_9_0() -> Iterator[MagicMock]:
"""Simulate a controller running CML version 2.9.0."""

yield from client_library_patched_system_info(version="2.9.0")


@pytest.fixture
def mocked_session() -> Iterator[MagicMock]:
with patch.object(authentication, "CustomClient", autospec=True) as session:
yield session

Expand Down Expand Up @@ -86,7 +99,7 @@ def resp_body_from_file(test_data_dir: Path, request: httpx.Request) -> httpx.Re


@pytest.fixture
def respx_mock_with_labs(respx_mock, test_data_dir: Path):
def respx_mock_with_labs(respx_mock: MockRouter, test_data_dir: Path):
"""
A test fixture that provides basic lab data with respx_mock so that unit tests can
call ``client.all_labs`` or ``client.join_existing_lab``. The sample data includes
Expand All @@ -96,7 +109,15 @@ def respx_mock_with_labs(respx_mock, test_data_dir: Path):
json={"version": CURRENT_VERSION, "ready": True, "oui": "52:54:00:00:00:00"},
)
respx_mock.post(FAKE_HOST_API + "authenticate").respond(json="BOGUS_TOKEN")
respx_mock.get(FAKE_HOST_API + "authok")
respx_mock.get(FAKE_HOST_API + "authentication").respond(
json={
"username": "username",
"id": "6c7dd461-1cbe-428f-bdd5-545a0d766ed7",
"token": "BOGUS_TOKEN",
"admin": True,
"error": None,
}
)
respx_mock.get(
FAKE_HOST_API + "labs/444a78d1-575c-4746-8469-696e580f17b6/resource_pools"
).respond(json=[])
Expand All @@ -119,6 +140,16 @@ def respx_mock_with_labs(respx_mock, test_data_dir: Path):
).respond(
json={"operational": {"compute_id": "99c887f5-052e-4864-a583-49fa7c4b68a9"}}
)
respx_mock.get(
FAKE_HOST_API
+ f"labs/444a78d1-575c-4746-8469-696e580f17b6/nodes/{node}/interfaces"
+ "?data=true&operational=true"
).respond(json=[])

respx_mock.get(
FAKE_HOST_API + "labs/444a78d1-575c-4746-8469-696e580f17b6/interfaces"
).respond(json=[])

respx_mock.get(
FAKE_HOST_API
+ "labs/444a78d1-575c-4746-8469-696e580f17b6/nodes?data=true&operational=true&"
Expand Down Expand Up @@ -154,6 +185,7 @@ def respx_mock_with_labs(respx_mock, test_data_dir: Path):


@pytest.fixture
def client_library(respx_mock_with_labs):
def client_library(respx_mock_with_labs: MockRouter) -> Iterator[ClientLibrary]:
_ = respx_mock_with_labs
client = ClientLibrary(url=FAKE_HOST, username="test", password="pa$$")
yield client
Loading
Loading