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
7 changes: 6 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"charliermarsh.ruff",
"eamodio.gitlens",
"usernamehw.errorlens",
"gruntfuggly.todo-tree"
"gruntfuggly.todo-tree",
"xaver.clang-format"
],
"settings": {
"cmake.sourceDirectory": "${workspaceFolder}",
Expand All @@ -30,6 +31,7 @@
"cmake.generator": "Ninja",
"C_Cpp.clang_format_fallbackStyle": "Google",
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
"C_Cpp.default.cppStandard": "c++23",
"editor.formatOnSave": true,
"files.trimTrailingWhitespace": true,
"python.defaultInterpreterPath": "/usr/bin/python3",
Expand All @@ -40,6 +42,9 @@
"source.fixAll": "explicit",
"source.organizeImports": "explicit"
}
},
"[cpp]": {
"editor.defaultFormatter": "xaver.clang-format"
}
}
}
Expand Down
5 changes: 2 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,8 @@ RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-18 100 &&
update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-18 100 && \
update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-18 100

# Install ruff for Python linting and formatting
RUN pipx install ruff && pipx ensurepath
ENV PATH="/root/.local/bin:${PATH}"
# Install uv for fast Python package management (to /usr/local/bin for all users)
RUN curl -LsSf https://astral.sh/uv/install.sh | env UV_INSTALL_DIR=/usr/local/bin sh

# ... Developer comfort tools (optional, for interactive use) ...
ARG INSTALL_DEV_TOOLS=false
Expand Down
53 changes: 41 additions & 12 deletions cmake/python_venv.cmake
Original file line number Diff line number Diff line change
@@ -1,25 +1,54 @@
function (configure_venv venv_name requirements_file)
find_package(Python3 COMPONENTS Interpreter REQUIRED)
# Python virtual environment management using uv
#
# Usage:
# configure_venv(venv_name project_dir)
#
# This creates a virtual environment using uv and installs the project
# in editable mode with dev dependencies.
#
# Sets ${venv_name}_PYTHON to the path of the Python interpreter in the venv.

function(configure_venv venv_name project_dir)
set(venv_path "${CMAKE_BINARY_DIR}/${venv_name}")

if(NOT EXISTS ${venv_path})
# Find uv - prefer system installation, fall back to common locations
find_program(UV_EXECUTABLE uv
HINTS
$ENV{HOME}/.cargo/bin
$ENV{HOME}/.local/bin
/usr/local/bin
)

if(NOT UV_EXECUTABLE)
message(FATAL_ERROR
"uv not found. Install it with: curl -LsSf https://astral.sh/uv/install.sh | sh"
)
endif()

message(STATUS "Using uv: ${UV_EXECUTABLE}")

# Create venv and install project with dev dependencies
if(NOT EXISTS "${venv_path}/bin/python3")
message(STATUS "Creating virtual environment: ${venv_name}")
execute_process(
COMMAND ${Python3_EXECUTABLE} -m venv ${venv_path}
RESULT_VARIABLE venv_creation_result
COMMAND ${UV_EXECUTABLE} venv ${venv_path} --python 3.11
RESULT_VARIABLE venv_result
)
if(NOT venv_creation_result EQUAL 0)
message(FATAL_ERROR "Failed to create virtual environment ${venv_name} at ${venv_path}")
if(NOT venv_result EQUAL 0)
message(FATAL_ERROR "Failed to create virtual environment: ${venv_name}")
endif()
endif()

# Install project in editable mode with dev dependencies using uv pip
message(STATUS "Installing ${venv_name} dependencies with uv")
execute_process(
COMMAND ${venv_path}/bin/pip install -q -r ${requirements_file}
RESULT_VARIABLE pip_install_result
COMMAND ${UV_EXECUTABLE} pip install -e ${project_dir}[dev] --python ${venv_path}/bin/python3
RESULT_VARIABLE install_result
)
if(NOT pip_install_result EQUAL 0)
message(FATAL_ERROR "Failed to install packages from ${requirements_file} into virtual environment ${venv_name}")
if(NOT install_result EQUAL 0)
message(FATAL_ERROR "Failed to install project into ${venv_name}")
endif()

# Export the Python path
set(${venv_name}_PYTHON ${venv_path}/bin/python3 PARENT_SCOPE)

endfunction()
2 changes: 1 addition & 1 deletion py/host-emulator/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
include("${PROJECT_SOURCE_DIR}/cmake/python_venv.cmake")

configure_venv(host_emulator_venv ${CMAKE_CURRENT_SOURCE_DIR}/requirements.txt)
configure_venv(host_emulator_venv ${CMAKE_CURRENT_SOURCE_DIR})

add_test(
NAME host_emulator_test
Expand Down
445 changes: 0 additions & 445 deletions py/host-emulator/poetry.lock

This file was deleted.

77 changes: 58 additions & 19 deletions py/host-emulator/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,27 +1,66 @@
[tool.poetry]
[project]
name = "host-emulator"
version = "0.1.0"
description = ""
authors = ["Nehal Patel <hereisnehal@gmail.com>"]
description = "Host emulator for embedded C++ applications"
authors = [{ name = "Nehal Patel", email = "hereisnehal@gmail.com" }]
readme = "README.md"
packages = [{include = "host_emulator", from = "src"}]
requires-python = ">=3.11"
dependencies = ["pyzmq>=25"]

[tool.poetry.dependencies]
python = "^3.11"
pyzmq = "^25"
pytest-cov = "^4.1.0"
[project.optional-dependencies]
dev = ["pytest>=8", "pytest-cov>=4", "ruff>=0.8", "mypy>=1.14"]

[tool.poetry.dev-dependencies]
black = "*"
isort = "*"
pytest = "*"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.black]
line-length = 79
[tool.hatch.build.targets.wheel]
packages = ["src/host_emulator"]

[tool.isort]
profile = "black"
[tool.ruff]
line-length = 88
target-version = "py311"
src = ["src", "tests"]

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # Pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
"ARG", # flake8-unused-arguments
"SIM", # flake8-simplify
"TCH", # flake8-type-checking
"PTH", # flake8-use-pathlib
"RUF", # Ruff-specific rules
]
ignore = [
"E501", # line too long (handled by formatter)
]

[tool.ruff.lint.isort]
known-first-party = ["host_emulator"]

[tool.mypy]
python_version = "3.11"
strict = true
warn_return_any = true
warn_unused_ignores = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true
disallow_untyped_decorators = true
no_implicit_optional = true
warn_redundant_casts = true
warn_unused_configs = true

[[tool.mypy.overrides]]
module = ["zmq.*"]
ignore_missing_imports = true

[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
3 changes: 0 additions & 3 deletions py/host-emulator/pytest.ini

This file was deleted.

13 changes: 0 additions & 13 deletions py/host-emulator/requirements.txt

This file was deleted.

8 changes: 5 additions & 3 deletions py/host-emulator/src/host_emulator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@
from .common import Status, UnhandledMessageError
from .emulator import DeviceEmulator
from .i2c import I2C
from .pin import Pin
from .pin import Pin, PinDirection, PinState
from .uart import Uart

__all__ = [
"I2C",
"DeviceEmulator",
"Pin",
"Uart",
"I2C",
"PinDirection",
"PinState",
"Status",
"Uart",
"UnhandledMessageError",
]
2 changes: 0 additions & 2 deletions py/host-emulator/src/host_emulator/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
class UnhandledMessageError(Exception):
"""Exception raised when a message cannot be handled."""

pass


class Status(Enum):
"""Status codes for emulator responses."""
Expand Down
Loading