Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1841a1b
Updated ruff to 16.3, includes new default formatting rules that stil…
MitchellAV Aug 19, 2026
0e1dc83
Temp disable ruff to push progress
MitchellAV Aug 20, 2026
3687ad7
Ruff formatting and fixed numerous ruff errors; still unfinished
MitchellAV Aug 20, 2026
64a8be1
Restored ruff pre-commit-hook post progress update
MitchellAV Aug 20, 2026
92fd478
Fixed the rest of the ruff issues, except ignored
MitchellAV Aug 21, 2026
7412653
Removed accidental file, nothing imports repo as root package
MitchellAV Aug 21, 2026
61a40a7
Removing pinned mamba-version to check test
MitchellAV Aug 21, 2026
d918991
Addresses Queue and mp.Queue by deferring annotation evaluation
MitchellAV Aug 21, 2026
f5906df
Narrowed bare Exception or added # noqa with explanation
MitchellAV Aug 21, 2026
d557e60
Merge branch 'main' into fix/ruff-16
MitchellAV Sep 15, 2026
903e774
Fixed incorrect VOCS import
MitchellAV Sep 15, 2026
f324739
Merge branch 'main' into fix/ruff-16
MitchellAV Sep 21, 2026
79747aa
Updated ruff to v0.16.8
MitchellAV Sep 21, 2026
342b008
Ruff formatting fixes in docs
MitchellAV Sep 21, 2026
469f37a
Refactor type hints: replace ClassVar with instance variables in mult…
MitchellAV Sep 21, 2026
0bfc8df
Changed wording of exception description
MitchellAV Sep 21, 2026
85882e2
Added reason for exec
MitchellAV Sep 21, 2026
d0b6ecd
Added better reason descriptions
MitchellAV Sep 21, 2026
ddcf461
Remove exception info from debug logs in BadgerOptMonitor
MitchellAV Sep 21, 2026
04f258b
Updated AGENTS.md to include Ruff linting exception format and remove…
MitchellAV Sep 22, 2026
9fff4c3
Merge branch 'main' into fix/ruff-16
MitchellAV Sep 22, 2026
d9c47ba
Update uninstall.py
roussel-ryan Sep 22, 2026
72dbd83
Removed unused code and added deprecated decorator to function
MitchellAV Sep 22, 2026
4f8fdbb
Merge branch 'fix/ruff-16' of https://github.com/xopt-org/Badger into…
MitchellAV Sep 22, 2026
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
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ repos:
exclude: ^src/badger/tests/

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.16
rev: v0.16.8
hooks:
- id: ruff-check
args: [--fix]
Expand Down
46 changes: 44 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ All tests should pass. GUI tests use `pytest-qt` and require a display server (t

- **`suppress_popups` is autouse.** All tests automatically mock `ExpandableMessageBox.exec_` to prevent Qt dialogs from blocking test execution.

- **Disabled tests use `x-` prefix.** Files like `x-test_db.py` are excluded from pytest collection (they don't match the `test_*.py` pattern). These require `BADGER_DB_ROOT` to be configured.
- **Disabled tests use `x_` prefix.** Files like `x_test_db.py` are excluded from pytest collection (they don't match the `test_*.py` pattern). These require `BADGER_DB_ROOT` to be configured.

- **Coverage targets the `badger` module.** `pyproject.toml` uses `--cov=badger` (module name, not a path), which resolves correctly under the `src/` layout.

Expand Down Expand Up @@ -168,6 +168,48 @@ A pre-commit hook (`check-module-docstrings`) enforces presence. Empty `__init__

3. **`Interface.reset_interface()`** is called after process fork — use it to reset any non-fork-safe state (file descriptors, connections, etc.) in custom interfaces.

4. **The `db.py` module is semi-deprecated** — it requires `BADGER_DB_ROOT` config which is not in the default `BadgerConfig` model. The `x-test_db.py` and `x-test_routine_id.py` files test this functionality but are excluded from normal test runs.
4. **The `db.py` module is semi-deprecated** — it requires `BADGER_DB_ROOT` config which is not in the default `BadgerConfig` model. The `x_test_db.py` and `x_test_routine_id.py` files test this functionality but are excluded from normal test runs.

5. **`utils.py` has Qt dependencies** — `BlockSignalsContext` and related utilities import from `PyQt5.QtWidgets` at the module level, so `badger.utils` cannot be imported without PyQt5 installed.

## Conforming to Ruff linting rules

Ruff adds many rules to the pre-commit python linting. Any of these rules can be ignored on a case-by-case basis going forward if given a valid reasoning behind the inclusion using the following format: `# noqa: <RUFF_ERROR_CODE> - <REASON FOR EXCEPTION>`

Common Ruff errors going forward.

### BLE001 - blind-except

```python
try:
foo()
except Exception:
...
```

Use instead:

```python
try:
foo()
except FileNotFoundError: # specific expected error to be thrown
...
```

There are valid reasons to catch just the base exception in certain cases; however, catching the specific expected error will be preferred, and catching the base exception should now always include the reason for doing so.

```python
try:
foo()
except Exception: # noqa: BLE001 - Reason for exception
...
```

Alternatively, re-raising the error or exceptions logged with `exc_info` will not be flagged.

```python
try:
foo()
except BaseException:
logger.exception("Something went wrong")
```
Empty file removed __init__.py
Empty file.
28 changes: 15 additions & 13 deletions documentation/docs/getting-started/tutorial_0.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,24 +77,23 @@ from badger import environment


class Environment(environment.Environment):

name = 'sphere_3d' # name of the environment
name = "sphere_3d" # name of the environment
variables = { # variables and their hard-limited ranges
'x0': [-1, 1],
'x1': [-1, 1],
'x2': [-1, 1],
"x0": [-1, 1],
"x1": [-1, 1],
"x2": [-1, 1],
}
observables = ['f'] # measurements
observables = ["f"] # measurements

# Internal variables to store the current values of
# the variables and observables
_variables = {
'x0': 0.0,
'x1': 0.0,
'x2': 0.0,
"x0": 0.0,
"x1": 0.0,
"x2": 0.0,
}
_observations = {
'f': None,
"f": None,
}

# Variable getter -- tells Badger how to get current values of the variables
Expand All @@ -109,10 +108,13 @@ class Environment(environment.Environment):
self._variables[var] = x

# Filling up the observations
f = self._variables['x0'] ** 2 + self._variables['x1'] ** 2 + \
self._variables['x2'] ** 2
f = (
self._variables["x0"] ** 2
+ self._variables["x1"] ** 2
+ self._variables["x2"] ** 2
)

self._observations['f'] = [f]
self._observations["f"] = [f]

# Observable getter -- how to get current values of the observables
def get_observables(self, observable_names):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,7 @@ from badger import interface


class Interface(interface.Interface):

name = 'myintf'
name = "myintf"

def get_values(self, channel_names: list):
pass
Expand Down Expand Up @@ -98,8 +97,7 @@ from badger.interface import Interface


class Environment(environment.Environment):

name = 'myenv'
name = "myenv"
variables = {}
observables = []

Expand Down Expand Up @@ -136,52 +134,54 @@ Try to avoid doing time-consuming thing in `__init__` method. Badger would creat
Okay, now we can start to implement the methods. Assume that our sample environment has 3 variables: `x`, `y`, and `z`, with range of [0, 1]. It also has 2 observations: `norm`, and `mean`. Then the `variables` and `observables` class variables should look like:

```python
variables = {
'x': [0, 1],
'y': [0, 1],
'z': [0, 1],
}
observables = ['norm', 'mean']
variables = {
"x": [0, 1],
"y": [0, 1],
"z": [0, 1],
}
observables = ["norm", "mean"]
```

Our custom env is so simple that we don't really need an interface here. Let's implement the getter and setter for the variables:

```python
# Internal variables start with a single underscore
_variables = {
'x': 0,
'y': 0,
'z': 0,
}
# Internal variables start with a single underscore
_variables = {
"x": 0,
"y": 0,
"z": 0,
}

def get_variables(self, variable_names: list[str]) -> dict:
variable_outputs = {v: self._variables[v] for v in variable_names}

return variable_outputs
def get_variables(self, variable_names: list[str]) -> dict:
variable_outputs = {v: self._variables[v] for v in variable_names}

def set_variables(self, variable_inputs: dict[str, float]):
for var, x in variable_inputs.items():
self._variables[var] = x
return variable_outputs


def set_variables(self, variable_inputs: dict[str, float]):
for var, x in variable_inputs.items():
self._variables[var] = x
```

Here we use a dictionary called `_variables` to hold the values for the variables.

Now let's add observable related logic:

```python
def get_observables(self, observable_names: list[str]) -> dict:
x = self._variables['x']
y = self._variables['y']
z = self._variables['z']

observable_outputs = {}
for obs in observable_names:
if obs == 'norm':
observable_outputs[obs] = (x ** 2 + y ** 2 + z ** 2) ** 0.5
elif obs == 'mean':
observable_outputs[obs] = (x + y + z) / 3

return observable_outputs
def get_observables(self, observable_names: list[str]) -> dict:
x = self._variables["x"]
y = self._variables["y"]
z = self._variables["z"]

observable_outputs = {}
for obs in observable_names:
if obs == "norm":
observable_outputs[obs] = (x**2 + y**2 + z**2) ** 0.5
elif obs == "mean":
observable_outputs[obs] = (x + y + z) / 3

return observable_outputs
```

At this point, the content of `__init__.py` should be:
Expand All @@ -192,21 +192,20 @@ from badger import environment


class Environment(environment.Environment):

name = 'myenv'
name = "myenv"

variables = {
'x': [0, 1],
'y': [0, 1],
'z': [0, 1],
"x": [0, 1],
"y": [0, 1],
"z": [0, 1],
}
observables = ['norm', 'mean']
observables = ["norm", "mean"]

# Internal variables start with a single underscore
_variables = {
'x': 0,
'y': 0,
'z': 0,
"x": 0,
"y": 0,
"z": 0,
}

def get_variables(self, variable_names: list[str]) -> dict:
Expand All @@ -219,15 +218,15 @@ class Environment(environment.Environment):
self._variables[var] = x

def get_observables(self, observable_names: list[str]) -> dict:
x = self._variables['x']
y = self._variables['y']
z = self._variables['z']
x = self._variables["x"]
y = self._variables["y"]
z = self._variables["z"]

observable_outputs = {}
for obs in observable_names:
if obs == 'norm':
observable_outputs[obs] = (x ** 2 + y ** 2 + z ** 2) ** 0.5
elif obs == 'mean':
if obs == "norm":
observable_outputs[obs] = (x**2 + y**2 + z**2) ** 0.5
elif obs == "mean":
observable_outputs[obs] = (x + y + z) / 3

return observable_outputs
Expand Down
4 changes: 0 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ where = ["src"]
include = ["badger"]
namespaces = false

[tool.ruff.lint]
extend-select = ["TID252"] # Defaults + check imports
ignore = ["E722"] # Until bare except blocks get fixed

[tool.pytest.ini_options]
addopts = "--cov=badger"
log_cli_level = "INFO"
Expand Down
5 changes: 2 additions & 3 deletions scripts/run_tests.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#!/usr/bin/env python
#!/usr/bin/env python3
import sys

import pytest


if __name__ == "__main__":
# Show output results from every test function
# Show the message output for skipped and expected failures
Expand All @@ -18,6 +17,6 @@
args.extend(["--cov=badger", "--cov-report", "term-missing"])
args.remove("--show-cov")

print("pytest arguments: {}".format(args))
print(f"pytest arguments: {args}")
print(f"Running tests on Python {sys.version}")
sys.exit(pytest.main(args))
8 changes: 4 additions & 4 deletions src/badger/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
import logging

from badger.actions import show_info
from badger.actions.config import config_settings
from badger.actions.doctor import self_check
from badger.actions.routine import show_routine
from badger.actions.generator import show_generator
from badger.actions.env import show_env
from badger.actions.generator import show_generator
from badger.actions.install import plugin_install
from badger.actions.uninstall import plugin_remove
from badger.actions.intf import show_intf
from badger.actions.config import config_settings
from badger.actions.routine import show_routine
from badger.actions.uninstall import plugin_remove
from badger.log import setup_logging

logger = logging.getLogger("badger")
Expand Down
21 changes: 10 additions & 11 deletions src/badger/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
GUI or prints config details; sub-command handlers are re-exported from
their own modules (doctor, routine, env, generator, etc.)."""

import argparse
import os
from importlib import metadata

Expand All @@ -10,27 +11,25 @@
from badger.utils import yprint


def show_info(args):
def show_info(args: argparse.Namespace) -> None:
config_path = None

if args.config_filepath:
config_path = args.config_filepath

if args.gui or args.gui_acr:
if check_n_config_paths(args.config_filepath):
from badger.gui import launch_gui
if (args.gui or args.gui_acr) and check_n_config_paths(args.config_filepath):
from badger.gui import launch_gui

launch_gui(config_path)
launch_gui(config_path)

return
return

if args.mini:
if check_n_config_paths(args.config_filepath):
from badger.gui.mini import launch_gui
if args.mini and check_n_config_paths(args.config_filepath):
from badger.gui.mini import launch_gui

launch_gui(config_path, template_filename=args.template)
launch_gui(config_path, template_filename=args.template)

return
return

if not check_n_config_paths():
return
Expand Down
Loading
Loading