Skip to content
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
51 changes: 24 additions & 27 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
- name: Install uv and set up python ${{ matrix.python-version }}
uses: astral-sh/setup-uv@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[test,lint]"
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Install the project
run: uv sync --only-group check
- name: Lint
run: |
make lint
run: make lint
- name: Type check
run: make typecheck
- name: Test
run: |
make test
run: make test

deploy:
needs: test
Expand All @@ -43,34 +43,31 @@ jobs:

steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
- name: Set up uv
uses: astral-sh/setup-uv@v5
with:
python-version: '3.13'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[build]"
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Install the project
run: uv sync --no-dev
- name: Build package
run: python -m build
run: make build
- name: Publish package
uses: pypa/gh-action-pypi-publish@release/v1

security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
- name: Set up uv
uses: astral-sh/setup-uv@v5
with:
python-version: '3.13'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e ".[security]"
enable-cache: true
cache-dependency-glob: "uv.lock"
- name: Install the project
run: uv sync --only-group security
- name: Run Bandit security checks
run: |
bandit -c pyproject.toml -r .
run: make bandit
- name: Run Safety CLI to check for vulnerabilities
uses: pyupio/safety-action@v1
with:
Expand Down
68 changes: 43 additions & 25 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
.PHONY: clean clean-test clean-pyc clean-build docs help
.DEFAULT_GOAL := help

# If NO_UV is set, don't use uv to run commands
ifdef NO_UV
PY_CMD_PREFIX :=
else
PY_CMD_PREFIX := uv run
endif

define BROWSER_PYSCRIPT
import os, webbrowser, sys

Expand All @@ -24,10 +31,10 @@ for line in sys.stdin:
endef
export PRINT_HELP_PYSCRIPT

BROWSER := python -c "$$BROWSER_PYSCRIPT"
BROWSER := $(PY_CMD_PREFIX) python -c "$$BROWSER_PYSCRIPT"

help:
@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)
@$(PY_CMD_PREFIX) python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST)

clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts

Expand All @@ -52,31 +59,38 @@ clean-test: ## remove test and coverage artifacts
rm -fr .pytest_cache/

lint: ## check style
black --check .
isort --check .
flake8 .
$(PY_CMD_PREFIX) black --check .
$(PY_CMD_PREFIX) isort --check .
$(PY_CMD_PREFIX) flake8 .

format: ## format code
black .
isort .
$(PY_CMD_PREFIX) black .
$(PY_CMD_PREFIX) isort .

typecheck: ## type check code
$(PY_CMD_PREFIX) mypy

bandit: ## run bandit security checks
$(PY_CMD_PREFIX) bandit -c pyproject.toml -r .

security: ## run security checks
bandit -c pyproject.toml -r .
safety scan
safety: ## run safety checks
$(PY_CMD_PREFIX) safety scan

security: bandit safety ## run security checks

pre-commit: ## run pre-commit checks
pre-commit run --all-files
$(PY_CMD_PREFIX) pre-commit run --all-files

test: ## run tests quickly with the default Python
pytest
$(PY_CMD_PREFIX) pytest

test-all: ## run tests on every Python version with tox
tox
check: ## run all checks
$(PY_CMD_PREFIX) tox

coverage: ## check code coverage quickly with the default Python
coverage run --source test_a_ble -m pytest
coverage report -m
coverage html
$(PY_CMD_PREFIX) coverage run --source test_a_ble -m pytest
$(PY_CMD_PREFIX) coverage report -m
$(PY_CMD_PREFIX) coverage html
$(BROWSER) htmlcov/index.html

docs: ## generate Sphinx HTML documentation, including API docs
Expand All @@ -85,17 +99,21 @@ docs: ## generate Sphinx HTML documentation, including API docs
$(BROWSER) docs/build/html/index.html

servedocs: docs ## compile the docs watching for changes
watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D .

release: dist ## package and upload a release
twine upload dist/*
$(PY_CMD_PREFIX) watchmedo shell-command -p '*.rst' -c '$(MAKE) -C docs html' -R -D .

dist: clean ## builds source and wheel package
python -m build
ls -l dist
build: clean ## builds source and wheel package
$(PY_CMD_PREFIX) python -m build

install: clean ## install the package to the active Python's site-packages
ifdef USE_UV
uv sync --no-default-groups
else
pip install -e .
endif

dev-install: clean ## install the package and development dependencies
dev-install: clean ## install the package and development dependencies. When using uv, dev dependencies are installed by default.
ifdef USE_UV
uv sync
else
pip install -e ".[dev]"
endif
11 changes: 9 additions & 2 deletions docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build

# If NO_UV is set, don't use uv to run commands
ifdef NO_UV
PY_CMD_PREFIX :=
else
PY_CMD_PREFIX := uv run
endif

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@$(PY_CMD_PREFIX) $(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@$(PY_CMD_PREFIX) $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
1 change: 0 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
]

templates_path = ["_templates"]
exclude_patterns = []

# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
Expand Down
131 changes: 105 additions & 26 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,77 @@
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "test-a-ble"
version = "0.1.0"
description = "Framework for testing BLE IoT devices"
authors = [
{name = "Nick Brook", email = "nick@nrbtech.io"}
]
readme = "README.md"
requires-python = ">=3.12"
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Software Development :: Testing",
"Topic :: System :: Hardware",
]
keywords = ["bluetooth", "ble", "iot", "testing", "automation"]
dependencies = [
"bleak>=0.22.3",
"rich>=13.9.4",
"packaging",
"prompt_toolkit>=3.0.0",
]

[project.scripts]
test-a-ble = "test_a_ble.cli:main"

[dependency-groups]
test = [
"pytest>=8.3.5",
"pytest-cov>=6.0.0",
"pytest-asyncio>=0.22.0",
"tox>=4.24.2",
"tox-uv>=1.25.0",
]
lint = [
"black>=25.1.0",
"isort>=6.0.1",
"flake8>=7.1.2",
"flake8-docstrings>=1.7.0",
"flake8-pyproject>=1.2.3",
]
type = [
"mypy>=1.15.0",
]
check = [
{include-group = "test"},
{include-group = "lint"},
{include-group = "type"},
]
security = [
"bandit>=1.8.3",
"safety>=3.3.1",
]
docs = [
"sphinx>=8.2.3",
"sphinx-rtd-theme>=3.0.2",
"myst-parser>=4.0.1",
]
dev = [
"pre-commit>=4.1.0",
{include-group = "check"},
{include-group = "security"},
{include-group = "docs"},
]

# Formatting and linting

[tool.black]
line-length = 120
Expand All @@ -16,7 +87,10 @@ exclude = [
# No need to traverse our git directory
".git",
# There's no value in checking cache directories
"__pycache__"
"__pycache__",
"*.pyc",
".venv",
".tox",
]
# Use extend-ignore to add to already ignored checks which are anti-patterns like W503.
extend-ignore = [
Expand All @@ -30,6 +104,9 @@ extend-ignore = [
"D202"
]

[tool.mypy]
files = ["test_a_ble", "docs", "tests"]

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = "test_*.py"
Expand All @@ -40,42 +117,44 @@ python_classes = "Test[A-Z][a-zA-Z0-9]*(?<!Context|Status|Exception|Failure|Skip
norecursedirs = ["examples","test_discovery_test_package"]

[tool.bandit]
exclude_dirs = [".tox"]
exclude_dirs = [".tox", ".venv"]
skips = ["B101", "B404", "B607"]

# Tox

[tool.tox]
min_version = "4.0"
env_list = ["py312", "py313"]
env_list = ["py312", "py313", "lint", "type"]
isolated_build = true
skip_missing_interpreters = false
requires = ["virtualenv>=20.0.0"]

[tool.tox.env.default]
deps = [".[lint,test]"]
commands = [
"black --check .",
"isort --check .",
"flake8 .",
"pytest {posargs:tests}",
]
[tool.tox.env_run_base]
runner = "uv-venv-lock-runner"
description = "Run test under {base_python}"
dependency_groups = ["test"]
commands = [["pytest"]]

[tool.tox.env.lint]
deps = [".[lint]"]
runner = "uv-venv-lock-runner"
description = "format code"
dependency_groups = ["lint"]
commands = [
"black --check .",
"isort --check .",
"flake8 .",
["black", "--check", "."],
["isort", "--check", "."],
["flake8", "."],
]

[tool.tox.env.format]
deps = [".[lint]"]
runner = "uv-venv-lock-runner"
description = "format code"
dependency_groups = ["lint"]
commands = [
"black .",
"isort .",
["black", "."],
["isort", "."],
]

[tool.tox.env.py312]
base_python = ["python3.12"]

[tool.tox.env.py313]
base_python = ["python3.13"]
[tool.tox.env.type]
runner = "uv-venv-lock-runner"
description = "type check code"
dependency_groups = ["type"]
commands = [["mypy"]]
Loading