Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
21f8ac9
Adding mypy requirement to pre-commit
MitchellAV Feb 9, 2026
8e337ff
Updated pre-commit hooks
MitchellAV Feb 9, 2026
5a0bf1d
Merge branch 'main' into feature/python-type-hinting
MitchellAV Feb 10, 2026
d1f4da6
Adding more type hints
MitchellAV Feb 18, 2026
8bb728a
WIP: Type hinting improvements
MitchellAV Jun 10, 2026
afd5ebc
First pass at addressing the various mypy type errors using claude
MitchellAV Jun 10, 2026
e2fd13e
Re-enable mypy hook
MitchellAV Jun 11, 2026
717e7c5
Merge branch 'develop-bax-extension' into develop-mypy-type-hints
MitchellAV Jun 12, 2026
2e62733
Working way through repository adding types
MitchellAV Jun 12, 2026
e4dc21a
Working on typing of root badger directory files
MitchellAV Jun 16, 2026
0c23bfb
Merge branch 'main' into develop-mypy-type-hints
MitchellAV Sep 23, 2026
666add3
Merge branch 'feature/python-type-hinting' into develop-mypy-type-hints
MitchellAV Sep 23, 2026
edd0083
Addresses many errors with extensions and supporting modules
MitchellAV Sep 24, 2026
c3c9567
Removed pyqt5-stubs as it has not been maintained; preferring native …
MitchellAV Sep 25, 2026
aa810fb
working through components folder
MitchellAV Sep 29, 2026
793e1eb
Working through routine and run monitor
MitchellAV Sep 29, 2026
ba282cb
continued progress with typing
MitchellAV Sep 30, 2026
032c49f
Refactor tests and enhance type annotations with new dataclasses
MitchellAV Sep 30, 2026
e6c29b1
Enhance type annotations and refactor code across multiple components…
MitchellAV Oct 1, 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
10 changes: 10 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,13 @@ repos:
- id: ruff-check
args: [--fix]
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v2.3.1
hooks:
- id: mypy
args: [--strict, --ignore-missing-imports]
additional_dependencies:
- pydantic
- pandas-stubs
- types-requests
13 changes: 11 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,16 @@ dynamic = ["version"]
version_file = "src/badger/_version.py"

[project.optional-dependencies]
dev = ["pytest", "pytest-cov", "pytest-qt", "pytest-mock", "pyqt5-stubs"]
dev = [
"pytest",
"pytest-cov",
"pytest-qt",
"pytest-mock",
"pandas-stubs",
"types-requests",
"types-PyYAML",
"mypy",
]

[project.urls]
Homepage = "https://github.com/xopt-org/Badger"
Expand All @@ -57,7 +66,7 @@ include = ["badger"]
namespaces = false

[tool.pytest.ini_options]
addopts = "--cov=badger"
addopts = "--cov=badger/"
log_cli_level = "INFO"
log_level = "DEBUG"
testpaths = ["src/badger/tests"]
2 changes: 1 addition & 1 deletion src/badger/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
logger = logging.getLogger("badger")


def main():
def main() -> None:
# Create the top-level parser
parser = argparse.ArgumentParser(description="Badger the optimizer")
parser.add_argument("-g", "--gui", action="store_true", help="launch the GUI")
Expand Down
8 changes: 4 additions & 4 deletions src/badger/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
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 argparse import Namespace
from importlib import metadata

from badger.actions.doctor import check_n_config_paths
from badger.settings import get_user_config_folder, init_settings
from badger.utils import yprint


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

if args.config_filepath:
Expand All @@ -25,9 +25,9 @@ def show_info(args: argparse.Namespace) -> None:
return

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

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

return

Expand Down
17 changes: 12 additions & 5 deletions src/badger/actions/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@

import logging
import os
from argparse import Namespace

from badger.settings import init_settings
from badger.utils import convert_str_to_value, yprint

logger = logging.getLogger(__name__)


def config_settings(args):
def config_settings(args: Namespace) -> bool | None:
config_singleton = init_settings()
key = args.key

if key is None:
yprint(config_singleton.list_path_settings())
return
return None

try:
try:
Expand All @@ -29,12 +30,13 @@ def config_settings(args):
except IndexError:
pass
except KeyboardInterrupt:
return
return None

logger.error(f"{key} is not a valid Badger config key!")
return None


def _config_path_var(var_name):
def _config_path_var(var_name: str) -> bool:
config_singleton = init_settings()

is_path = config_singleton.read_is_path(var_name)
Expand Down Expand Up @@ -100,7 +102,7 @@ def _config_path_var(var_name):
return False


def _config_core_var(var_name):
def _config_core_var(var_name: str) -> bool:
config_singleton = init_settings()

display_name = config_singleton.read_display_name(var_name)
Expand Down Expand Up @@ -132,6 +134,11 @@ def _config_core_var(var_name):
if res == "R":
config_singleton.write_value(var_name, default)
print(f"You reset the {display_name} setting")
return False # fault value
elif res != "S":
config_singleton.write_value(var_name, convert_str_to_value(res))
print(f"You set {display_name} to {res}")
return True # success value
else:
print(f"You skipped the {display_name} setting")
return False
17 changes: 12 additions & 5 deletions src/badger/actions/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
offers to fix missing ones interactively, and can factory-reset Badger back
to its default state."""

from argparse import Namespace

from badger.actions.config import _config_path_var
from badger.settings import init_settings, mock_settings


def self_check(args):
def self_check(args: Namespace) -> None:
config = init_settings()
# Reset Badger

Expand Down Expand Up @@ -35,17 +37,22 @@ def self_check(args):
print("Badger is healthy!")


def check_n_config_paths(config_filepath=None):
def check_n_config_paths(config_filepath: str | None = None) -> bool:
if config_filepath is not None:
config = init_settings(config_filepath)
else:
config = init_settings()

good = True
all_bad = True # if all config paths are empty, we'll suggest initialization
issue_list = []

for pname, pvalue in config._config.model_dump(by_alias=True).items():
issue_list: list[str] = []

for (
pname,
pvalue,
) in config._config.model_dump( # pyright: ignore[reportPrivateUsage]
by_alias=True
).items():
if config.read_value(pname) is None:
good = False
issue_list.append(pname)
Expand Down
4 changes: 2 additions & 2 deletions src/badger/actions/env.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
"""The `badger env` command. Lists available environment plugins or shows
the details (variables, observations, parameters) of a specific one."""

import argparse
import logging
from argparse import Namespace

from badger.errors import BadgerInvalidPluginError, BadgerPluginNotFoundError
from badger.utils import range_to_str, yprint

logger = logging.getLogger(__name__)


def show_env(args: argparse.Namespace) -> None:
def show_env(args: Namespace) -> None:
try:
from badger.factory import get_env, list_env
except Exception:
Expand Down
3 changes: 2 additions & 1 deletion src/badger/actions/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
or shows the default configuration of a specific one."""

import logging
from argparse import Namespace

from xopt.errors import XoptError

Expand All @@ -10,7 +11,7 @@
logger = logging.getLogger(__name__)


def show_generator(args):
def show_generator(args: Namespace) -> None:
try:
from badger.factory import get_generator, list_generators
except Exception as e: # noqa: BLE001 - import triggers config/plugin loading; report and exit
Expand Down
3 changes: 2 additions & 1 deletion src/badger/actions/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
the current plugin installation workflow."""

import logging
from argparse import Namespace

from typing_extensions import deprecated

logger = logging.getLogger(__name__)


@deprecated("The `badger install` command is currently disabled.")
def plugin_install(args):
def plugin_install(args: Namespace) -> None:
print(
"This command is currently disabled.\n"
"Please refer to the Badger documentation for plugin management.\n\n"
Expand Down
3 changes: 2 additions & 1 deletion src/badger/actions/intf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
shows the details of a specific one."""

import logging
from typing import Any

from badger.errors import BadgerInvalidPluginError, BadgerPluginNotFoundError
from badger.utils import yprint

logger = logging.getLogger(__name__)


def show_intf(args):
def show_intf(args: Any) -> None:
try:
from badger.factory import get_intf, list_intf
except Exception as e: # noqa: BLE001 - import triggers config/plugin loading; report and exit
Expand Down
3 changes: 2 additions & 1 deletion src/badger/actions/routine.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@
backwards compatibility."""

import logging
from argparse import Namespace

from typing_extensions import deprecated

logger = logging.getLogger(__name__)


@deprecated("The `badger routine` command is deprecated. Please use the GUI.")
def show_routine(args):
def show_routine(args: Namespace) -> None:
print(
"This command is deprecated.\n"
"Please use 'badger -g' to launch the Badger GUI "
Expand Down
39 changes: 26 additions & 13 deletions src/badger/actions/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import signal
import sys
import time
from argparse import Namespace
from typing import Any, TypedDict

from pandas import DataFrame
from typing_extensions import deprecated
Expand All @@ -26,23 +28,34 @@
logger = logging.getLogger(__name__)


class Storage(TypedDict):
states: Any
ts_last_dump: float | None
paused: bool


def run_n_archive(
routine: Routine, yes=False, save=False, verbose=2, sleep=0, flush_prompt=False
):
routine: Routine,
yes: bool = False,
save: bool = False,
verbose: int = 2,
sleep: float = 0,
flush_prompt: bool = False,
) -> None:
try:
from badger.archive import archive_run
except Exception as e: # noqa: BLE001 - import triggers config/plugin loading; report and exit
logger.error(e)
return

# Store system states and other stuff
storage = {
storage: Storage = {
"states": None,
"ts_last_dump": None,
"paused": False,
}

def handler(*args):
def handler(*args: Any) -> None:
if storage["paused"]:
print() # start a new line
if flush_prompt: # erase the last prompt
Expand All @@ -52,10 +65,10 @@ def handler(*args):

signal.signal(signal.SIGINT, handler)

def check_run_status():
def check_run_status() -> int:
return 0

def before_evaluate(candidates: DataFrame):
def before_evaluate(candidates: DataFrame) -> None:
if storage["paused"]:
res = input(
"Optimization paused. Press Enter to resume or Ctrl/Cmd + C to terminate: "
Expand All @@ -70,7 +83,7 @@ def before_evaluate(candidates: DataFrame):
sys.stdout.write("\033[F")
storage["paused"] = False

def after_evaluate(data: DataFrame):
def after_evaluate(data: DataFrame) -> None:
# vars: ndarray
# obses: ndarray
# cons: ndarray
Expand All @@ -88,16 +101,16 @@ def after_evaluate(data: DataFrame):
try:
path = _run["path"]
filename = _run["filename"][:-4] + "pickle"
routine.environment.interface.dump_recording(
os.path.join(path, filename)
)
interface = getattr(routine.environment, "interface", None)
if interface is not None:
interface.dump_recording(os.path.join(path, filename))
except Exception: # noqa: BLE001 - interface dump is best-effort
logger.warning("Failed to dump interface logs")

# take a break to let the outside signal to change the status
time.sleep(sleep)

def states_ready(states):
def states_ready(states: Any) -> None:
storage["states"] = states

try:
Expand All @@ -114,7 +127,7 @@ def states_ready(states):
logger.error(e)

# Save the run when at least one solution has been evaluated
if len(routine.data):
if routine.data and len(routine.data):
_run = archive_run(routine, storage["states"])
# Try dump the interface logs
try:
Expand All @@ -126,7 +139,7 @@ def states_ready(states):


@deprecated("The `badger run` command is deprecated. Please use the GUI.")
def run_routine(args):
def run_routine(args: Namespace) -> None:
print(
"This command is deprecated.\n"
"Please use 'badger -g' to launch the Badger GUI "
Expand Down
3 changes: 2 additions & 1 deletion src/badger/actions/uninstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
removing plugins — see docs for the current workflow."""

import logging
from argparse import Namespace

from typing_extensions import deprecated

logger = logging.getLogger(__name__)


@deprecated("The `badger uninstall` command is currently disabled.")
def plugin_remove(args):
def plugin_remove(args: Namespace) -> None:
print(
"This command is currently disabled.\n"
"Please refer to the Badger documentation for plugin management.\n\n"
Expand Down
Loading
Loading