mirror of
https://github.com/kristoferssolo/Traffic-Light-Detector.git
synced 2025-10-21 20:00:36 +00:00
Minor release update
Removed `setup.py` and its adjasant files. Removed `tox`. Wrote meta data in `pyproject.toml`. Migrated from `playsound` to `magicsound` (not tested).
This commit is contained in:
parent
9480d8ccb3
commit
0af409cf1d
8
.github/workflows/ruff.yml
vendored
Normal file
8
.github/workflows/ruff.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
name: Ruff
|
||||||
|
on: [push, pull_request]
|
||||||
|
jobs:
|
||||||
|
ruff:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: chartboost/ruff-action@v1
|
||||||
23
.github/workflows/tests.yml
vendored
23
.github/workflows/tests.yml
vendored
@ -1,23 +0,0 @@
|
|||||||
name: Tests
|
|
||||||
on:
|
|
||||||
- push
|
|
||||||
- pull_request
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, windows-latest]
|
|
||||||
python-version: ["3.10"]
|
|
||||||
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
|
|
||||||
run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install tox tox-gh-actions
|
|
||||||
- name: Test with tox
|
|
||||||
run: tox
|
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
# Traffic Light Detector
|
||||||
## Likvidēt cilvēka reakcijas laika ietekmi uz satiksmes sastrēgumu realizāciju ar datorsistēmu, kas balstīta tiešā norobežota krustojuma izmaiņu komunikācijas metodikā, maršruta posmā, kas aprīkots ar gaismas signalizācijas satiksmes regulatoru.
|
## Likvidēt cilvēka reakcijas laika ietekmi uz satiksmes sastrēgumu realizāciju ar datorsistēmu, kas balstīta tiešā norobežota krustojuma izmaiņu komunikācijas metodikā, maršruta posmā, kas aprīkots ar gaismas signalizācijas satiksmes regulatoru.
|
||||||
|
|
||||||
> Reduce traffic congestion effects due to human reaction times at intersections with traffic lights by utilizing a computer system based on direct communication method of changes in the circumscribed route intersection.
|
> Reduce traffic congestion effects due to human reaction times at intersections with traffic lights by utilizing a computer system based on direct communication method of changes in the circumscribed route intersection.
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
# Traffic Light Detector
|
# Traffic Light Detector
|
||||||
Traffic Light recognition and color detection
|
Traffic Light recognition and color detection
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
See [DESCRIPTION.md](./DESCRIPTION.md)
|
See [DESCRIPTION.md](./DESCRIPTION.md) [lv]
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@ -29,5 +29,4 @@ pip install .
|
|||||||
Replace `<int>` with your camera number specified by the operating system. Probably `0` or `1`, but can be higher.
|
Replace `<int>` with your camera number specified by the operating system. Probably `0` or `1`, but can be higher.
|
||||||
|
|
||||||
## To Do
|
## To Do
|
||||||
- [ ] Write unit tests.
|
|
||||||
- [ ] Create/find better traffic light model for better traffic light recognition.
|
- [ ] Create/find better traffic light model for better traffic light recognition.
|
||||||
|
|||||||
18
main.py
18
main.py
@ -3,11 +3,9 @@ import argparse
|
|||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from TrafficLightDetector.paths import IMAGES_IN_PATH, create_dirs
|
from TrafficLightDetector.paths import create_dirs, IMAGES_IN_PATH
|
||||||
from TrafficLightDetector.traffic_light_camera import \
|
from TrafficLightDetector.traffic_light_camera import TrafficLightDetectorCamera
|
||||||
TrafficLightDetectorCamera
|
from TrafficLightDetector.traffic_light_images import TrafficLightDetectorImages
|
||||||
from TrafficLightDetector.traffic_light_images import \
|
|
||||||
TrafficLightDetectorImages
|
|
||||||
|
|
||||||
|
|
||||||
def pos_int(string: str) -> int:
|
def pos_int(string: str) -> int:
|
||||||
@ -37,11 +35,7 @@ group.add_argument(
|
|||||||
metavar="int",
|
metavar="int",
|
||||||
help="Reads camera inputs to determine traffic light color. (Default: %(default)s)",
|
help="Reads camera inputs to determine traffic light color. (Default: %(default)s)",
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument("-s", "--sound", action="store_true")
|
||||||
"-s",
|
|
||||||
"--sound",
|
|
||||||
action="store_true"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@logger.catch
|
@logger.catch
|
||||||
@ -54,7 +48,9 @@ def main(args) -> None:
|
|||||||
image.draw()
|
image.draw()
|
||||||
|
|
||||||
if args.camera is not None:
|
if args.camera is not None:
|
||||||
camera = TrafficLightDetectorCamera(args.camera, sound=args.sound) # Change number if webcam didn't detect
|
camera = TrafficLightDetectorCamera(
|
||||||
|
args.camera, sound=args.sound
|
||||||
|
) # Change number if webcam didn't detect
|
||||||
camera.enable()
|
camera.enable()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,32 @@
|
|||||||
[build-system]
|
[build-system]
|
||||||
requires = ["setuptools>=42.0", "wheel"]
|
requires = ["setuptools>=61.0"]
|
||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[project]
|
||||||
addopts = "--cov=TrafficLightDetector"
|
name = "TrafficLightDetector"
|
||||||
testpaths = ["tests"]
|
version = "0.1.0"
|
||||||
|
description = "Reduce traffic congestion effects due to human reaction times at intersections with traffic lights by utilizing a computer system based on direct communication method of changes in the circumscribed route intersection."
|
||||||
|
authors = [
|
||||||
|
{ name = "Kristofers Solo", email = "dev@kristofers.xyz" },
|
||||||
|
{ name = "Alan Alexander Cerna" },
|
||||||
|
]
|
||||||
|
keywords = ["detection", "traffic light"]
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.10"
|
||||||
|
license = { text = "GPLv3" }
|
||||||
|
classifiers = [
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"Programming Language :: Python :: 3:10",
|
||||||
|
"Programming Language :: Python :: 3:11",
|
||||||
|
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||||
|
"Operating System :: Linux",
|
||||||
|
]
|
||||||
|
dependencies = ["loguru==0.7", "opencv-python~=4.8", "magicsound==0.0.5"]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
"Source" = "https://github.com/kristoferssolo/Traffic-Light-Detector"
|
||||||
|
"Description" = "https://github.com/kristoferssolo/Traffic-Light-Detector/blob/main/DESCRIPTION.md"
|
||||||
|
"Bug Tracker" = "https://github.com/kristoferssolo/Traffic-Light-Detector/issues"
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
check_untyped_defs = true
|
check_untyped_defs = true
|
||||||
@ -19,3 +41,10 @@ warn_redundant_casts = true
|
|||||||
warn_return_any = true
|
warn_return_any = true
|
||||||
warn_unreachable = true
|
warn_unreachable = true
|
||||||
warn_unused_configs = true
|
warn_unused_configs = true
|
||||||
|
|
||||||
|
|
||||||
|
[tool.ruff]
|
||||||
|
line-length = 160
|
||||||
|
|
||||||
|
[tool.ruff.flake8-quotes]
|
||||||
|
docstring-quotes = "double"
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
loguru==0.6.0
|
loguru==0.7.*
|
||||||
opencv-python==4.6.0.66
|
opencv-python==4.8.*
|
||||||
playsound==1.3.0
|
magicsound==0.0.*
|
||||||
|
|||||||
@ -1,5 +1,2 @@
|
|||||||
flake8==6.0.0
|
mypy==1.5.*
|
||||||
mypy==0.991
|
ruff==0.0.*
|
||||||
pytest-cov==4.0.0
|
|
||||||
pytest==7.2.0
|
|
||||||
tox==3.27.1
|
|
||||||
|
|||||||
34
setup.cfg
34
setup.cfg
@ -1,34 +0,0 @@
|
|||||||
[metadata]
|
|
||||||
name = TrafficLightDetector
|
|
||||||
description = Reduce traffic congestion effects due to human reaction times at intersections with traffic lights by utilizing a computer system based on direct communication method of changes in the circumscribed route intersection.
|
|
||||||
author = Kristofers Solo
|
|
||||||
license = MIT
|
|
||||||
license_files = LICENSE
|
|
||||||
platforms = unix, linux, osx, cygwin, win32
|
|
||||||
classifiers =
|
|
||||||
Programming Language :: Python :: 3.10
|
|
||||||
|
|
||||||
[options]
|
|
||||||
packages = TrafficLightDetector
|
|
||||||
install_requires =
|
|
||||||
loguru>=0.6.0
|
|
||||||
opencv-python>=4.6.0.66
|
|
||||||
playsound>=1.3.0
|
|
||||||
|
|
||||||
python_requires = >=3.10
|
|
||||||
package_dir = =src
|
|
||||||
zip_safe = no
|
|
||||||
|
|
||||||
[options.extras_require]
|
|
||||||
testing =
|
|
||||||
flake8>=6.0.0
|
|
||||||
mypy>=0.991
|
|
||||||
pytest-cov>=4.0.0
|
|
||||||
pytest>=7.2.0
|
|
||||||
tox>=3.27.1
|
|
||||||
|
|
||||||
[options.package_data]
|
|
||||||
detector = py.typed
|
|
||||||
|
|
||||||
[flake8]
|
|
||||||
max-line-length = 160
|
|
||||||
@ -2,7 +2,6 @@ from typing import NamedTuple
|
|||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from loguru import logger
|
|
||||||
|
|
||||||
|
|
||||||
class ColorRange(NamedTuple):
|
class ColorRange(NamedTuple):
|
||||||
@ -10,24 +9,40 @@ class ColorRange(NamedTuple):
|
|||||||
color2: tuple[int, int, int]
|
color2: tuple[int, int, int]
|
||||||
|
|
||||||
|
|
||||||
@logger.catch
|
|
||||||
class ColorAttributes:
|
class ColorAttributes:
|
||||||
def __init__(self, name: str, color: tuple[int, int, int], lowers: ColorRange,
|
def __init__(
|
||||||
uppers: ColorRange, hsv: cv2.cvtColor, minDist: int, param2: int) -> None:
|
self,
|
||||||
|
name: str,
|
||||||
|
color: tuple[int, int, int],
|
||||||
|
lowers: ColorRange,
|
||||||
|
uppers: ColorRange,
|
||||||
|
hsv: np.ndarray,
|
||||||
|
minDist: int,
|
||||||
|
param2: int,
|
||||||
|
) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.color = color
|
self.color = color
|
||||||
|
|
||||||
# set mask
|
# set mask
|
||||||
masks: list[cv2.inRange] = []
|
masks = []
|
||||||
for lower, upper in zip(lowers, uppers):
|
for lower, upper in zip(lowers, uppers):
|
||||||
masks.append(cv2.inRange(hsv, lower, upper))
|
masks.append(cv2.inRange(hsv, lower, upper))
|
||||||
|
|
||||||
self.mask = masks[0]
|
self.mask = masks[0]
|
||||||
if len(masks) > 1:
|
if len(masks) > 1:
|
||||||
for mask in masks:
|
for mask in masks:
|
||||||
self.mask = cv2.add(self.mask, mask)
|
self.mask = cv2.add(self.mask, mask)
|
||||||
|
|
||||||
# set circle
|
# set circle
|
||||||
self.circle = cv2.HoughCircles(self.mask, cv2.HOUGH_GRADIENT, 1, minDist=minDist, param1=50,
|
self.circle = cv2.HoughCircles(
|
||||||
param2=param2, minRadius=0, maxRadius=30)
|
self.mask,
|
||||||
|
cv2.HOUGH_GRADIENT,
|
||||||
|
1,
|
||||||
|
minDist=minDist,
|
||||||
|
param1=50,
|
||||||
|
param2=param2,
|
||||||
|
minRadius=0,
|
||||||
|
maxRadius=30,
|
||||||
|
)
|
||||||
if self.circle is not None:
|
if self.circle is not None:
|
||||||
self.circle = np.uint16(np.around(self.circle))
|
self.circle = np.uint16(np.around(self.circle))
|
||||||
|
|||||||
@ -17,7 +17,13 @@ PATHS = (LOGS_PATH, IMAGES_IN_PATH, IMAGES_OUT_PATH, SOUND_PATH)
|
|||||||
|
|
||||||
log_level = "DEBUG" if BASE_PATH.joinpath("debug").exists() else "INFO"
|
log_level = "DEBUG" if BASE_PATH.joinpath("debug").exists() else "INFO"
|
||||||
# Set up logging
|
# Set up logging
|
||||||
logger.add(LOGS_PATH.joinpath("detection.log"), format="{time} | {level} | {message}", level=log_level, rotation="1 MB", compression="zip")
|
logger.add(
|
||||||
|
LOGS_PATH.joinpath("detection.log"),
|
||||||
|
format="{time} | {level} | {message}",
|
||||||
|
level=log_level,
|
||||||
|
rotation="1 MB",
|
||||||
|
compression="zip",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@logger.catch
|
@logger.catch
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
import cv2
|
import cv2
|
||||||
from playsound import playsound
|
from magicsound import magicsound
|
||||||
|
|
||||||
from TrafficLightDetector.paths import SOUND_PATH
|
from TrafficLightDetector.paths import SOUND_PATH
|
||||||
from TrafficLightDetector.traffic_light_detector import TrafficLightDetector
|
from TrafficLightDetector.traffic_light_detector import TrafficLightDetector
|
||||||
|
|
||||||
|
|
||||||
class TrafficLightDetectorCamera(TrafficLightDetector):
|
class TrafficLightDetectorCamera(TrafficLightDetector):
|
||||||
|
|
||||||
def __init__(self, source: int, sound: bool = False) -> None:
|
def __init__(self, source: int, sound: bool = False) -> None:
|
||||||
self.video_capture = cv2.VideoCapture(source)
|
self.video_capture = cv2.VideoCapture(source)
|
||||||
self.sound = sound
|
self.sound = sound
|
||||||
@ -29,4 +28,4 @@ class TrafficLightDetectorCamera(TrafficLightDetector):
|
|||||||
|
|
||||||
def _make_sound(self) -> None:
|
def _make_sound(self) -> None:
|
||||||
if self.signal_color == "GREEN":
|
if self.signal_color == "GREEN":
|
||||||
playsound(str(SOUND_PATH.joinpath("move.mp3")))
|
magicsound(str(SOUND_PATH.joinpath("move.mp3")))
|
||||||
|
|||||||
@ -28,9 +28,27 @@ class TrafficLightDetector:
|
|||||||
self.roi = self.image if roi is None else roi
|
self.roi = self.image if roi is None else roi
|
||||||
self.size = self.roi.shape if roi is not None else self.image.shape
|
self.size = self.roi.shape if roi is not None else self.image.shape
|
||||||
hsv = cv2.cvtColor(self.image if roi is None else self.roi, cv2.COLOR_BGR2HSV)
|
hsv = cv2.cvtColor(self.image if roi is None else self.roi, cv2.COLOR_BGR2HSV)
|
||||||
self.red = ColorAttributes("RED", self.RED, self.RED_LOWER, self.RED_UPPER, hsv, minDist=80, param2=10)
|
self.red = ColorAttributes(
|
||||||
self.yellow = ColorAttributes("YELLOW", self.YELLOW, self.YELLOW_LOWER, self.YELLOW_UPPER, hsv, minDist=60, param2=10)
|
"RED", self.RED, self.RED_LOWER, self.RED_UPPER, hsv, minDist=80, param2=10
|
||||||
self.green = ColorAttributes("GREEN", self.GREEN, self.GREEN_LOWER, self.GREEN_UPPER, hsv, minDist=30, param2=5)
|
)
|
||||||
|
self.yellow = ColorAttributes(
|
||||||
|
"YELLOW",
|
||||||
|
self.YELLOW,
|
||||||
|
self.YELLOW_LOWER,
|
||||||
|
self.YELLOW_UPPER,
|
||||||
|
hsv,
|
||||||
|
minDist=60,
|
||||||
|
param2=10,
|
||||||
|
)
|
||||||
|
self.green = ColorAttributes(
|
||||||
|
"GREEN",
|
||||||
|
self.GREEN,
|
||||||
|
self.GREEN_LOWER,
|
||||||
|
self.GREEN_UPPER,
|
||||||
|
hsv,
|
||||||
|
minDist=30,
|
||||||
|
param2=5,
|
||||||
|
)
|
||||||
self.colors = [self.red, self.yellow, self.green]
|
self.colors = [self.red, self.yellow, self.green]
|
||||||
self.detect_traffic_lights = detectTrafficLights
|
self.detect_traffic_lights = detectTrafficLights
|
||||||
self.signal_color = ""
|
self.signal_color = ""
|
||||||
@ -39,8 +57,10 @@ class TrafficLightDetector:
|
|||||||
gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
|
gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
|
||||||
# draw rectangle around traffic lights
|
# draw rectangle around traffic lights
|
||||||
for x, y, width, height in self.CASCADE.detectMultiScale(gray, 1.2, 5):
|
for x, y, width, height in self.CASCADE.detectMultiScale(gray, 1.2, 5):
|
||||||
cv2.rectangle(self.image, (x, y), (x + width, y + height), (255, 0, 0), self.BOUNDARY)
|
cv2.rectangle(
|
||||||
roi = self.image[y:y + height, x:x + width]
|
self.image, (x, y), (x + width, y + height), (255, 0, 0), self.BOUNDARY
|
||||||
|
)
|
||||||
|
roi = self.image[y : y + height, x : x + width]
|
||||||
self._set_image(self.image, roi)
|
self._set_image(self.image, roi)
|
||||||
self._draw_circle()
|
self._draw_circle()
|
||||||
|
|
||||||
@ -49,20 +69,46 @@ class TrafficLightDetector:
|
|||||||
if color.circle is None:
|
if color.circle is None:
|
||||||
continue
|
continue
|
||||||
for value in color.circle[0, :]:
|
for value in color.circle[0, :]:
|
||||||
if value[0] > self.size[1] or value[1] > self.size[0] or value[1] > self.size[0] * self.BOUNDARY:
|
if (
|
||||||
|
value[0] > self.size[1]
|
||||||
|
or value[1] > self.size[0]
|
||||||
|
or value[1] > self.size[0] * self.BOUNDARY
|
||||||
|
):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
h, s = 0, 0
|
h, s = 0, 0
|
||||||
for i in range(-self.RADIUS, self.RADIUS):
|
for i in range(-self.RADIUS, self.RADIUS):
|
||||||
for j in range(-self.RADIUS, self.RADIUS):
|
for j in range(-self.RADIUS, self.RADIUS):
|
||||||
if (value[1] + i) >= self.size[0] or (value[0] + j) >= self.size[1]:
|
if (value[1] + i) >= self.size[0] or (
|
||||||
|
value[0] + j
|
||||||
|
) >= self.size[1]:
|
||||||
continue
|
continue
|
||||||
h += color.mask[value[1] + i, value[0] + j]
|
h += color.mask[value[1] + i, value[0] + j]
|
||||||
s += 1
|
s += 1
|
||||||
if h / s > 100:
|
if h / s > 100:
|
||||||
cv2.circle(self.roi if self.detect_traffic_lights else self.image, (value[0], value[1]), value[2] + 10, color.color, 2) # draws circle
|
cv2.circle(
|
||||||
cv2.circle(color.mask, (value[0], value[1]), value[2] + 30, (255, 255, 255), 2)
|
self.roi if self.detect_traffic_lights else self.image,
|
||||||
|
(value[0], value[1]),
|
||||||
|
value[2] + 10,
|
||||||
|
color.color,
|
||||||
|
2,
|
||||||
|
) # draws circle
|
||||||
|
cv2.circle(
|
||||||
|
color.mask,
|
||||||
|
(value[0], value[1]),
|
||||||
|
value[2] + 30,
|
||||||
|
(255, 255, 255),
|
||||||
|
2,
|
||||||
|
)
|
||||||
if self.TEXT:
|
if self.TEXT:
|
||||||
cv2.putText(self.roi if self.detect_traffic_lights else self.image, color.name,
|
cv2.putText(
|
||||||
(value[0], value[1]), self.FONT, 1, color.color, 2, cv2.LINE_AA) # draws text
|
self.roi if self.detect_traffic_lights else self.image,
|
||||||
|
color.name,
|
||||||
|
(value[0], value[1]),
|
||||||
|
self.FONT,
|
||||||
|
1,
|
||||||
|
color.color,
|
||||||
|
2,
|
||||||
|
cv2.LINE_AA,
|
||||||
|
) # draws text
|
||||||
self.signal_color = color.name
|
self.signal_color = color.name
|
||||||
|
|||||||
@ -7,7 +7,6 @@ from TrafficLightDetector.traffic_light_detector import TrafficLightDetector
|
|||||||
|
|
||||||
|
|
||||||
class TrafficLightDetectorImages(TrafficLightDetector):
|
class TrafficLightDetectorImages(TrafficLightDetector):
|
||||||
|
|
||||||
def __init__(self, path: Path) -> None:
|
def __init__(self, path: Path) -> None:
|
||||||
self.path = path
|
self.path = path
|
||||||
self._set_image(cv2.imread(str(self.path)))
|
self._set_image(cv2.imread(str(self.path)))
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
import unittest
|
|
||||||
|
|
||||||
|
|
||||||
class TestTask(unittest.TestCase):
|
|
||||||
def test_nothing(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
unittest.main()
|
|
||||||
27
tox.ini
27
tox.ini
@ -1,27 +0,0 @@
|
|||||||
[tox]
|
|
||||||
minversion = 3.8.0
|
|
||||||
envlist = py310, flake8, mypy
|
|
||||||
isolated_build = true
|
|
||||||
|
|
||||||
[gh-actions]
|
|
||||||
python =
|
|
||||||
3.10: py310, mypy, flake8
|
|
||||||
|
|
||||||
[testenv]
|
|
||||||
setenv =
|
|
||||||
PYTHONPATH = {toxinidir}
|
|
||||||
deps =
|
|
||||||
-r{toxinidir}/requirements_dev.txt
|
|
||||||
commands =
|
|
||||||
pytest --basetemp={envtmpdir}
|
|
||||||
|
|
||||||
[testenv:flake8]
|
|
||||||
basepython = python3.10
|
|
||||||
deps = flake8
|
|
||||||
commands = flake8 src tests
|
|
||||||
|
|
||||||
[testenv:mypy]
|
|
||||||
basepython = python3.10
|
|
||||||
deps =
|
|
||||||
-r{toxinidir}/requirements_dev.txt
|
|
||||||
commands = mypy src
|
|
||||||
Loading…
Reference in New Issue
Block a user