Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove prospector, add ruff #12

Merged
merged 1 commit into from
Feb 8, 2024
Merged
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: 3 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13-dev']

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand Down
51 changes: 0 additions & 51 deletions .prospector.yaml

This file was deleted.

86 changes: 30 additions & 56 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ name = "pytest-reserial"
authors = [{name = "Alexander Bessman", email = "[email protected]"}]
dynamic = ["version", "description"]
readme = "README.md"
requires-python = ">=3.7"
requires-python = ">=3.8"
license = {file = "LICENSE"}
dependencies = [
"pytest",
"pyserial",
]
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Development Status :: 4 - Beta",
"License :: OSI Approved :: MIT License",
]
Expand All @@ -40,61 +40,35 @@ Home = "https://github.com/bessman/pytest-reserial"
[project.entry-points."pytest11"]
reserial = "pytest_reserial.reserial"

[tool.tox]
legacy_tox_ini = """
[tox]
isolated_build = true
envlist =
isort
black
prospector
pydocstyle
test
report

[testenv:isort]
deps = isort
commands = isort --check-only .

[testenv:black]
deps = black
commands = black --check .

[testenv:prospector]
deps =
prospector
mypy
bandit
commands = prospector .

[testenv:pydocstyle]
deps = pydocstyle
commands = pydocstyle --convention=numpy src/

[testenv:test]
deps =
pytest
coverage
commands = coverage run --source=pytest_reserial -m pytest
[project.optional-dependencies]
test = [
"coverage",
]
dev = [
"black",
"isort",
"mypy",
"ruff",
"tox",
]

[testenv:report]
deps = coverage
commands = coverage report
depends = test
[tool.isort]
profile = "black"

[testenv:xml]
deps = coverage
commands = coverage xml
depends = test
[tool.ruff.lint]
select = ["ALL"]
ignore = [
"D203", # one-blank-line-before-class
"D213", # multi-line-summary-second-line
"G004", # logging-f-string
"N818", # error-suffix-on-exception-name
"S101", # assert
"ANN101", # missing-type-self
"PT004", # pytest-missing-fixture-name-underscore
]

[gh-actions]
python =
3.7: test
3.8: test
3.9: test
3.10: test
3.11: black, prospector, test, report
"""
[tool.ruff.lint.pydocstyle]
convention = "numpy"

[tool.isort]
profile = "black"
[tool.ruff.lint.per-file-ignores]
"**/{tests,docs}/*" = ["ALL"]
73 changes: 35 additions & 38 deletions src/pytest_reserial/reserial.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,37 @@
"""Record or replay serial traffic when running tests."""

from __future__ import annotations

import json
from enum import IntEnum
from pathlib import Path
from typing import Callable, Dict, Generator, List, Tuple

import pytest
from serial import Serial # type: ignore[import]
from serial import Serial # type: ignore[import-untyped]

TrafficLog = Dict[str, List[int]]
PatchMethods = Tuple[
Callable[[Serial, int], bytes],
Callable[[Serial, bytes], int],
Callable[[Serial], None],
Callable[[Serial], None],
]


def pytest_addoption(parser: pytest.Parser) -> None: # noqa: D103
group = parser.getgroup("reserial")
group.addoption(
"--record", action="store_true", default=False, help="Record serial traffic."
"--record",
action="store_true",
default=False,
help="Record serial traffic.",
)
group.addoption(
"--replay", action="store_true", default=False, help="Replay serial traffic."
"--replay",
action="store_true",
default=False,
help="Replay serial traffic.",
)


Expand All @@ -29,7 +45,8 @@ class Mode(IntEnum):


def reconfigure_port_patch(
self: Serial, force_update: bool = False # pylint: disable=unused-argument
self: Serial, # noqa: ARG001
force_update: bool = False, # noqa: ARG001, FBT001, FBT002
) -> None:
"""Don't try to set parameters on the mocked port.

Expand All @@ -39,7 +56,7 @@ def reconfigure_port_patch(
"""


@pytest.fixture
@pytest.fixture()
def reserial(
monkeypatch: pytest.MonkeyPatch,
request: pytest.FixtureRequest,
Expand Down Expand Up @@ -81,7 +98,7 @@ def reserial(
pytest.fail(msg)


def get_traffic_log(mode: Mode, logpath: Path, testname: str) -> Dict[str, List[int]]:
def get_traffic_log(mode: Mode, logpath: Path, testname: str) -> TrafficLog:
"""Load recorded traffic (replay) or create an empty log (record).

Parameters
Expand All @@ -105,26 +122,20 @@ def get_traffic_log(mode: Mode, logpath: Path, testname: str) -> Dict[str, List[
If both '--replay' and '--record' were specified.
"""
if mode == Mode.INVALID:
raise ValueError("Choose one of 'replay' or 'record', not both.")
msg = "Choose one of 'replay' or 'record', not both"
raise ValueError(msg)

log: Dict[str, List[int]] = {"rx": [], "tx": []}
log: TrafficLog = {"rx": [], "tx": []}

if mode == Mode.REPLAY:
with open(logpath, "r", encoding="utf-8") as logfile:
with Path.open(logpath) as logfile:
logs = json.load(logfile)
log = logs[testname]

return log


def get_patched_methods(
mode: Mode, log: Dict[str, List[int]]
) -> Tuple[
Callable[[Serial, int], bytes],
Callable[[Serial, bytes], int],
Callable[[Serial], None],
Callable[[Serial], None],
]:
def get_patched_methods(mode: Mode, log: TrafficLog) -> PatchMethods:
"""Return patched read, write, open, and closed methods.

The methods should be monkeypatched over the corresponding `Serial` methods.
Expand Down Expand Up @@ -155,14 +166,7 @@ def get_patched_methods(
return Serial.read, Serial.write, Serial.open, Serial.close


def get_replay_methods(
log: Dict[str, List[int]]
) -> Tuple[
Callable[[Serial, int], bytes],
Callable[[Serial, bytes], int],
Callable[[Serial], None],
Callable[[Serial], None],
]:
def get_replay_methods(log: TrafficLog) -> PatchMethods:
"""Return patched read, write, open, and close methods for replaying logged traffic.

Parameters
Expand All @@ -183,7 +187,7 @@ def get_replay_methods(
"""

def replay_write(
self: Serial, # pylint: disable=unused-argument
self: Serial, # noqa: ARG001
data: bytes,
) -> int:
"""Compare TX data to recording instead of writing to the bus.
Expand All @@ -208,7 +212,7 @@ def replay_write(
return len(data)

def replay_read(
self: Serial, # pylint: disable=unused-argument
self: Serial, # noqa: ARG001
size: int = 1,
) -> bytes:
"""Replay RX data from recording instead of reading from the bus.
Expand All @@ -235,14 +239,7 @@ def replay_close(self: Serial) -> None:
self.is_open = False


def get_record_methods(
log: Dict[str, List[int]]
) -> Tuple[
Callable[[Serial, int], bytes],
Callable[[Serial, bytes], int],
Callable[[Serial], None],
Callable[[Serial], None],
]:
def get_record_methods(log: TrafficLog) -> PatchMethods:
"""Return patched read, write, open, and close methods for recording traffic.

Parameters
Expand Down Expand Up @@ -288,7 +285,7 @@ def record_read(self: Serial, size: int = 1) -> bytes:


def write_log(
log: Dict[str, List[int]],
log: TrafficLog,
logpath: Path,
testname: str,
) -> None:
Expand All @@ -305,13 +302,13 @@ def write_log(
"""
try:
# If the file exists, read its contents.
with open(logpath, mode="r", encoding="utf-8") as logfile:
with Path.open(logpath) as logfile:
logs = json.load(logfile)
except FileNotFoundError:
logs = {}

logs[testname] = log

# Wipe the file if it exists, or create a new file if it doesn't.
with open(logpath, mode="w", encoding="utf-8") as logfile:
with Path.open(logpath, mode="w") as logfile:
json.dump(logs, logfile)
37 changes: 37 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[tox]
envlist =
lint
test

[testenv:format]
skip_install=True
allowlist_externals =
black
isort
commands =
black .
isort .

[testenv:lint]
extras = dev
commands =
black --check .
isort --check .
mypy --strict src/
ruff check .

[testenv:test]
usedevelop=True
extras = test
commands =
coverage run --source=. -m pytest
coverage report

[gh-actions]
python =
3.8: test
3.9: test
3.10: test
3.11: test
3.12: test, lint
3.13-dev: test, lint