Skip to content

Commit

Permalink
chore: improve build, check and dependencies system
Browse files Browse the repository at this point in the history
I removed the following configurations:
- tox.ini
- requirements files
- setup.py

In favor of a unified build and dependency management system. Everything is configured inside the pyproject.toml and still accessible using the Makefile.
Dependencies are locked in standard using uv with the uv.lock. Everything will now be managed through uv, all at once with a single user interface and a single tool.
Please check the README for instructions

Signed-off-by: Arno Dubois <[email protected]>
  • Loading branch information
Arno Dubois committed Oct 28, 2024
1 parent a930da3 commit 861403d
Show file tree
Hide file tree
Showing 25 changed files with 996 additions and 1,530 deletions.
17 changes: 12 additions & 5 deletions .github/workflows/python-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
build:
name: Check if every commit in the PR works
runs-on: ubuntu-latest
timeout-minutes: 60
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
Expand All @@ -24,16 +24,23 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: ${{ env.PR_FETCH_DEPTH }}

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
python-version: ${{ matrix.python-version }}
version: "0.4.27"
enable-cache: true

- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}

- name: Test every commit in the PR for Python ${{ matrix.python-version }}
run: |
COMMITS=$(git rev-list ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }})
for commit in $COMMITS; do
git checkout $commit || exit 1
git show --no-patch --format='Testing commit %h %s'
make check || exit 1
make sync_deps check bundle || exit 1
done
- name: Minimize uv cache
run: uv cache prune --ci
14 changes: 10 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# ignore misc build/test files
.env-lint
# ignore uv venv
.venv

# ignore standard cache folders
.*_cache

# ignore built files
hwbench.egg-info
*.pyc
.ruff-cache
dist

hwbench-out-*
hwbench.egg-info
.coverage
junit-python*.xml
42 changes: 16 additions & 26 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,36 +1,26 @@
all:

.PHONY: update_deps clean check format
.PHONY: update_deps sync_deps clean check bundle format

UPDATE_DEPS_ENV = .env-deps
LINT_ENV = .env-lint
SOURCES = hwbench csv graph

SOURCES = hwbench setup.py csv graph
update_deps:
uv sync -U

update_env:
python3 -m venv $(UPDATE_DEPS_ENV)
./$(UPDATE_DEPS_ENV)/bin/pip install --upgrade --quiet pip-tools

update_deps: update_env
./$(UPDATE_DEPS_ENV)/bin/pip-compile --upgrade --output-file=requirements/base.txt requirements/base.in
./$(UPDATE_DEPS_ENV)/bin/pip-compile --upgrade --output-file=requirements/test.txt requirements/test.in

regen_hashes: update_env
./$(UPDATE_DEPS_ENV)/bin/pip-compile --output-file=requirements/base.txt requirements/base.in
./$(UPDATE_DEPS_ENV)/bin/pip-compile --output-file=requirements/test.txt requirements/test.in
sync_deps:
uv sync --all-extras --dev

clean:
rm -fr $(UPDATE_DEPS_ENV) $(LINT_ENV)

$(LINT_ENV):
python3 -m venv $(LINT_ENV)
./$(LINT_ENV)/bin/pip install -r requirements/test.txt
uv venv

check: $(LINT_ENV)
env PYTHON=python3 ./$(LINT_ENV)/bin/tox
check:
uv tool run ruff format --diff
uv tool run ruff check
uv run mypy $(SOURCES)
uv run pytest

bundle: $(LINT_ENV)
env PYTHON=python3 ./$(LINT_ENV)/bin/tox -e bundle
bundle:
uv build

format: $(LINT_ENV)
./$(LINT_ENV)/bin/ruff format $(SOURCES)
format:
uv tool run ruff format $(SOURCES)
39 changes: 32 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,47 @@ If multiple output files are passed as arguments, and only if they were generate

For more details, see the specific documentation.

# Examples
Running the **simple.conf** job:
<code>python3 -m hwbench.hwbench -j configs/simple.conf -m monitoring.cfg</code>
---

# Installation

# Requirements
## Mandatory
You will first need the following packages on your machine, depending on what you want to run:
1. The "main" tool: `hwbench`; you will install this on your server, or the machine you want to analyse
2. The "graphing" tool: `hwgraph`; this is the part used to parse `hwbench`'s output that will create those nice graphs for you to analyse and compare the runs!
You can install both of them on the server, but `hwgraph` requires some graphics library not always convenient to install in reduced environments.

### Requirements to run hwbench
#### Mandatory
- python >= 3.9
- [python dependencies](./requirements/base.in)
- turbostat >= 2022.04.16
- numactl
- dmidecode
- util-linux >= 2.32
- lspci
- rpm

## Optional
#### Optional
- ipmitool
- ilorest (for HPE servers)
- stress-ng >= 0.17.04

### Requirements to run hwgraph
#### Mandatory
- python >= 3.9
- Headers for Cairo (`cairo-devel` on RHEL-based or `libcairo2-dev` for Debian-based)
- Python 3 headers for your current interpreter (`python3-devel` on RHEL-based or `python3-dev` for Debian-based)


## Actual installation
We do not (yet, coming at some point) provide a PyPi package. However, installation is almost just as simple:
1. Clone the repository
2. Make sure that you have all the requirements above already installed on your system
3. Install a recent version of `uv` on your system: we require a version above 0.4.27, so you can just do a `pip install uv` on your system to install the latest release
4. Run `uv sync` in the repository. If you want to also include the dependencies for the plotting, run `uv sync --extra graph` instead!
5. Have fun running `uv run hwbench` (as root) and `uv run hwgraph`!

---

# Examples
Running the **simple.conf** job:
<code>python3 -m hwbench.hwbench -j configs/simple.conf -m monitoring.cfg</code>
4 changes: 3 additions & 1 deletion graph/graph.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from typing import Optional
import matplotlib
from matplotlib.pylab import Axes
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import FuncFormatter, AutoMinorLocator, MultipleLocator
Expand Down Expand Up @@ -32,7 +34,7 @@ def __init__(
square=False,
show_source_file=None,
) -> None:
self.ax2 = None
self.ax2: Optional[Axes] = None
self.args = args
self.fig, self.ax = plt.subplots()
self.dpi = 100
Expand Down
31 changes: 19 additions & 12 deletions graph/hwgraph.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,25 @@
import sys
from typing import Any # noqa: F401

from graph.common import fatal
from graph.graph import init_matplotlib, generic_graph, yerr_graph
from graph.individual import individual_graph
from graph.scaling import scaling_graph
from graph.chassis import graph_chassis
from graph.trace import Trace
from hwbench.bench.monitoring_structs import (
FanContext,
PowerCategories,
PowerContext,
Metrics,
)
try:
from graph.common import fatal
from graph.graph import init_matplotlib, generic_graph, yerr_graph
from graph.individual import individual_graph
from graph.scaling import scaling_graph
from graph.chassis import graph_chassis
from graph.trace import Trace
from hwbench.bench.monitoring_structs import (
FanContext,
PowerCategories,
PowerContext,
Metrics,
)
except ImportError as exc:
print(exc)
print(
'Could not start hwgraph: did you make sure to also install the "graph" optional dependencies using `uv sync --extra graph` or `pip install hwbench[graph]`?'
)
sys.exit(1)


def valid_trace_file(trace_arg: str) -> Trace:
Expand Down
15 changes: 8 additions & 7 deletions hwbench/bench/test_benchmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,18 @@ def __init__(self, *args, **kwargs):
with patch(
"hwbench.engines.stressng.EngineModuleCpu.list_module_parameters"
) as p:
print(pathlib.Path("."))
p.return_value = (
pathlib.Path("./tests/parsing/stressngmethods/v17/stdout")
pathlib.Path("hwbench/tests/parsing/stressngmethods/v17/stdout")
.read_bytes()
.split(b":", 1)
)
self.load_mocked_hardware(
cpucores="./tests/parsing/cpu_cores/v2321",
cpuinfo="./tests/parsing/cpu_info/v2321",
numa="./tests/parsing/numa/8domainsllc",
cpucores="hwbench/tests/parsing/cpu_cores/v2321",
cpuinfo="hwbench/tests/parsing/cpu_info/v2321",
numa="hwbench/tests/parsing/numa/8domainsllc",
)
self.load_benches("./config/sample.ini")
self.load_benches("./hwbench/config/sample.ini")
self.parse_jobs_config()

def test_parsing(self):
Expand Down Expand Up @@ -100,11 +101,11 @@ def test_stream_short(self):
"hwbench.engines.stressng.EngineModuleCpu.list_module_parameters"
) as p:
p.return_value = (
pathlib.Path("./tests/parsing/stressngmethods/v17/stdout")
pathlib.Path("./hwbench/tests/parsing/stressngmethods/v17/stdout")
.read_bytes()
.split(b":", 1)
)
self.load_benches("./config/stream.ini")
self.load_benches("./hwbench/config/stream.ini")
assert self.get_jobs_config().get_config().getint("global", "runtime") == 5
self.get_jobs_config().get_config().set("global", "runtime", "2")
with self.assertRaises(SystemExit):
Expand Down
2 changes: 1 addition & 1 deletion hwbench/bench/test_benchmarks_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def parse_jobs_config(self, validate_parameters=True):
with patch("hwbench.environment.turbostat.Turbostat.check_version") as cv:
cv.return_value = True
with patch("hwbench.environment.turbostat.Turbostat.run") as ts:
with open("tests/parsing/turbostat/run", "r") as f:
with open("hwbench/tests/parsing/turbostat/run", "r") as f:
ts.return_value = ast.literal_eval(f.read())
return self.benches.parse_jobs_config(validate_parameters)

Expand Down
10 changes: 5 additions & 5 deletions hwbench/bench/test_cores.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ class TestCores(tbc.TestCommon):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_mocked_hardware(
cpucores="./tests/parsing/cpu_cores/v2321",
cpuinfo="./tests/parsing/cpu_info/v2321",
numa="./tests/parsing/numa/8domainsllc",
cpucores="./hwbench/tests/parsing/cpu_cores/v2321",
cpuinfo="./hwbench/tests/parsing/cpu_info/v2321",
numa="./hwbench/tests/parsing/numa/8domainsllc",
)
self.load_benches("./config/cores.conf")
self.load_benches("./hwbench/config/cores.conf")
self.parse_jobs_config()

def test_cores(self):
Expand All @@ -30,7 +30,7 @@ def test_cores(self):
assert self.get_bench_parameters(3).get_pinned_cpu() == CPU0_1

# Testing broken syntax that must fail
self.load_benches("./config/sample_weirds.conf")
self.load_benches("./hwbench/config/sample_weirds.conf")
for test_name in [
"invalid_cpu_core",
"alpha_cpu_core",
Expand Down
24 changes: 12 additions & 12 deletions hwbench/bench/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ class TestHelpers(tbc.TestCommon):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_mocked_hardware(
cpucores="./tests/parsing/cpu_cores/v2321",
cpuinfo="./tests/parsing/cpu_info/v2321",
numa="./tests/parsing/numa/8domainsllc",
cpucores="./hwbench/tests/parsing/cpu_cores/v2321",
cpuinfo="./hwbench/tests/parsing/cpu_info/v2321",
numa="./hwbench/tests/parsing/numa/8domainsllc",
)
self.load_benches("./config/helpers.conf")
self.load_benches("./hwbench/config/helpers.conf")
self.parse_jobs_config()

def test_helpers(self):
Expand All @@ -32,11 +32,11 @@ class TestHelpers_CPUSTORAGE(tbc.TestCommon):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_mocked_hardware(
cpucores="./tests/parsing/cpu_cores/cpustorage",
cpuinfo="./tests/parsing/cpu_info/cpustorage",
numa="./tests/parsing/numa/2domains",
cpucores="./hwbench/tests/parsing/cpu_cores/cpustorage",
cpuinfo="./hwbench/tests/parsing/cpu_info/cpustorage",
numa="./hwbench/tests/parsing/numa/2domains",
)
self.load_benches("./config/helpers.conf")
self.load_benches("./hwbench/config/helpers.conf")
self.parse_jobs_config()

def test_helpers(self):
Expand All @@ -59,11 +59,11 @@ class TestHelpersImpossible(tbc.TestCommon):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_mocked_hardware(
cpucores="./tests/parsing/cpu_cores/v2321",
cpuinfo="./tests/parsing/cpu_info/v2321",
numa="./tests/parsing/numa/8domainsllc",
cpucores="./hwbench/tests/parsing/cpu_cores/v2321",
cpuinfo="./hwbench/tests/parsing/cpu_info/v2321",
numa="./hwbench/tests/parsing/numa/8domainsllc",
)
self.load_benches("./config/helpers_fail.conf")
self.load_benches("./hwbench/config/helpers_fail.conf")

def test_helpers_impossible(self):
"""Testing impossible helper usecase."""
Expand Down
12 changes: 6 additions & 6 deletions hwbench/bench/test_numa.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ class TestNuma(tbc.TestCommon):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_mocked_hardware(
cpucores="./tests/parsing/cpu_cores/v2321",
cpuinfo="./tests/parsing/cpu_info/v2321",
numa="./tests/parsing/numa/8domainsllc",
cpucores="./hwbench/tests/parsing/cpu_cores/v2321",
cpuinfo="./hwbench/tests/parsing/cpu_info/v2321",
numa="./hwbench/tests/parsing/numa/8domainsllc",
)
self.NUMA0 = list(range(0, 8)) + list(range(64, 72))
self.NUMA1 = list(range(8, 16)) + list(range(72, 80))
self.NUMA0_1 = sorted(self.NUMA0 + self.NUMA1)
self.NUMA7 = list(range(56, 64)) + list(range(120, 128))
self.NUMA07 = list(range(0, self.hw.get_cpu().get_logical_cores_count()))
self.load_benches("./config/numa.conf")
self.load_benches("./hwbench/config/numa.conf")
self.parse_jobs_config()

def test_quadrant(self):
Expand All @@ -28,7 +28,7 @@ def test_quadrant(self):

# Testing broken syntax that must fail
# Testing quadrants
self.load_benches("./config/sample_weirds.conf")
self.load_benches("./hwbench/config/sample_weirds.conf")
for test_name in [
"invalid_quadrant",
"alpha_quadrant",
Expand Down Expand Up @@ -59,7 +59,7 @@ def test_numa(self):

# Testing broken syntax that must fail
# Testing quadrants
self.load_benches("./config/sample_weirds.conf")
self.load_benches("./hwbench/config/sample_weirds.conf")
for test_name in [
"invalid_numa_nodes",
"alpha_numa_nodes",
Expand Down
Loading

0 comments on commit 861403d

Please sign in to comment.