From 46eacc43d9b336b49951b5886d7afb5776f1f56a Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 7 Nov 2025 15:18:54 +0100 Subject: [PATCH 01/18] Move `bin/*.py` into `bapctools/` and adjust imports --- {bin => bapctools}/check_testing_tool.py | 12 ++++---- {bin => bapctools}/config.py | 0 {bin => bapctools}/conftest.py | 0 {bin => bapctools}/constraints.py | 8 ++--- {bin => bapctools}/contest.py | 4 +-- {bin => bapctools}/download_submissions.py | 10 +++---- {bin => bapctools}/export.py | 14 ++++----- {bin => bapctools}/fuzz.py | 18 ++++++------ {bin => bapctools}/generate.py | 20 ++++++------- {bin => bapctools}/interactive.py | 10 +++---- {bin => bapctools}/latex.py | 8 ++--- {bin => bapctools}/parallel.py | 4 +-- {bin => bapctools}/problem.py | 24 +++++++-------- {bin => bapctools}/program.py | 6 ++-- {bin => bapctools}/run.py | 20 ++++++------- {bin => bapctools}/skel.py | 12 ++++---- {bin => bapctools}/slack.py | 6 ++-- {bin => bapctools}/solve_stats.py | 8 ++--- {bin => bapctools}/stats.py | 14 ++++----- {bin => bapctools}/testcase.py | 10 +++---- {bin => bapctools}/tools.py | 34 +++++++++++----------- {bin => bapctools}/upgrade.py | 8 ++--- {bin => bapctools}/util.py | 6 ++-- {bin => bapctools}/validate.py | 12 ++++---- {bin => bapctools}/validator_tests.py | 2 +- {bin => bapctools}/verdicts.py | 8 ++--- {bin => bapctools}/visualize.py | 6 ++-- 27 files changed, 142 insertions(+), 142 deletions(-) rename {bin => bapctools}/check_testing_tool.py (97%) rename {bin => bapctools}/config.py (100%) rename {bin => bapctools}/conftest.py (100%) rename {bin => bapctools}/constraints.py (98%) rename {bin => bapctools}/contest.py (99%) rename {bin => bapctools}/download_submissions.py (94%) rename {bin => bapctools}/export.py (98%) rename {bin => bapctools}/fuzz.py (97%) rename {bin => bapctools}/generate.py (99%) rename {bin => bapctools}/interactive.py (99%) rename {bin => bapctools}/latex.py (99%) rename {bin => bapctools}/parallel.py (99%) rename {bin => bapctools}/problem.py (99%) rename {bin => bapctools}/program.py (99%) rename {bin => bapctools}/run.py (98%) rename {bin => bapctools}/skel.py (98%) rename {bin => bapctools}/slack.py (95%) rename {bin => bapctools}/solve_stats.py (97%) rename {bin => bapctools}/stats.py (98%) rename {bin => bapctools}/testcase.py (99%) rename {bin => bapctools}/tools.py (98%) rename {bin => bapctools}/upgrade.py (99%) rename {bin => bapctools}/util.py (99%) rename {bin => bapctools}/validate.py (98%) rename {bin => bapctools}/validator_tests.py (98%) rename {bin => bapctools}/verdicts.py (99%) rename {bin => bapctools}/visualize.py (96%) diff --git a/bin/check_testing_tool.py b/bapctools/check_testing_tool.py similarity index 97% rename from bin/check_testing_tool.py rename to bapctools/check_testing_tool.py index 64bebd591..b57fb2036 100644 --- a/bin/check_testing_tool.py +++ b/bapctools/check_testing_tool.py @@ -4,11 +4,11 @@ from pathlib import Path from typing import Optional, TYPE_CHECKING -import config -import parallel -from program import Program -from run import Submission -from util import ( +from bapctools import config +from bapctools import parallel +from bapctools.program import Program +from bapctools.run import Submission +from bapctools.util import ( command_supports_memory_limit, default_exec_code_map, ensure_symlink, @@ -19,7 +19,7 @@ ) if TYPE_CHECKING: # Prevent circular import: https://stackoverflow.com/a/39757388 - from problem import Problem + from bapctools.problem import Problem """DISCLAIMER: diff --git a/bin/config.py b/bapctools/config.py similarity index 100% rename from bin/config.py rename to bapctools/config.py diff --git a/bin/conftest.py b/bapctools/conftest.py similarity index 100% rename from bin/conftest.py rename to bapctools/conftest.py diff --git a/bin/constraints.py b/bapctools/constraints.py similarity index 98% rename from bin/constraints.py rename to bapctools/constraints.py index 5dc9c41ca..9cc73d65f 100644 --- a/bin/constraints.py +++ b/bapctools/constraints.py @@ -3,10 +3,10 @@ from colorama import Fore, Style from typing import Optional -import latex -import validate -from problem import Problem -from util import eprint, error, log, warn +from bapctools import latex +from bapctools import validate +from bapctools.problem import Problem +from bapctools.util import eprint, error, log, warn """DISCLAIMER: diff --git a/bin/contest.py b/bapctools/contest.py similarity index 99% rename from bin/contest.py rename to bapctools/contest.py index 39aa7dac4..44d527cb5 100644 --- a/bin/contest.py +++ b/bapctools/contest.py @@ -4,8 +4,8 @@ from pathlib import Path from typing import Any, Optional, TYPE_CHECKING -import config -from util import ( +from bapctools import config +from bapctools.util import ( error, fatal, has_ryaml, diff --git a/bin/download_submissions.py b/bapctools/download_submissions.py similarity index 94% rename from bin/download_submissions.py rename to bapctools/download_submissions.py index 41271e45a..ce7f44040 100644 --- a/bin/download_submissions.py +++ b/bapctools/download_submissions.py @@ -5,11 +5,11 @@ from pathlib import Path from typing import Any -import config -import parallel -from contest import call_api_get_json, get_contest_id -from util import fatal, ProgressBar -from verdicts import from_string, Verdict +from bapctools import config +from bapctools import parallel +from bapctools.contest import call_api_get_json, get_contest_id +from bapctools.util import fatal, ProgressBar +from bapctools.verdicts import from_string, Verdict # Example usage: # bt download_submissions [--user ] [--password ] [--contest ] [--api ] diff --git a/bin/export.py b/bapctools/export.py similarity index 98% rename from bin/export.py rename to bapctools/export.py index 3d7bb03d5..df29ea119 100644 --- a/bin/export.py +++ b/bapctools/export.py @@ -5,11 +5,11 @@ from pathlib import Path from typing import Any, Optional -import config -from contest import call_api, call_api_get_json, contest_yaml, get_contests, problems_yaml -from latex import PdfType -from problem import Problem -from util import ( +from bapctools import config +from bapctools.contest import call_api, call_api_get_json, contest_yaml, get_contests, problems_yaml +from bapctools.latex import PdfType +from bapctools.problem import Problem +from bapctools.util import ( ask_variable_bool, drop_suffix, ensure_symlink, @@ -31,8 +31,8 @@ warn, write_yaml, ) -from validate import AnswerValidator, InputValidator, OutputValidator -from visualize import InputVisualizer, OutputVisualizer +from bapctools.validate import AnswerValidator, InputValidator, OutputValidator +from bapctools.visualize import InputVisualizer, OutputVisualizer def select_languages(problems: list[Problem]) -> list[str]: diff --git a/bin/fuzz.py b/bapctools/fuzz.py similarity index 97% rename from bin/fuzz.py rename to bapctools/fuzz.py index d9b8d32ac..d5ebfdc1c 100644 --- a/bin/fuzz.py +++ b/bapctools/fuzz.py @@ -7,13 +7,13 @@ from pathlib import Path from typing import Any, Optional -import config -import generate -import parallel -import problem -from run import Run, Submission -from testcase import Testcase -from util import ( +from bapctools import config +from bapctools import generate +from bapctools import parallel +from bapctools import problem +from bapctools.run import Run, Submission +from bapctools.testcase import Testcase +from bapctools.util import ( eprint, error, fatal, @@ -25,8 +25,8 @@ ryaml_get_or_add, write_yaml, ) -from validate import Mode, OutputValidator -from verdicts import Verdict +from bapctools.validate import Mode, OutputValidator +from bapctools.verdicts import Verdict if has_ryaml: from ruamel.yaml.comments import CommentedMap, CommentedSeq diff --git a/bin/generate.py b/bapctools/generate.py similarity index 99% rename from bin/generate.py rename to bapctools/generate.py index 39cfda183..ecaa4e1d3 100644 --- a/bin/generate.py +++ b/bapctools/generate.py @@ -11,15 +11,15 @@ from pathlib import Path, PurePosixPath from typing import cast, Final, Literal, Optional, overload, TypeVar -import config -import parallel -import program -import run -import validate -import visualize -from problem import Problem -from testcase import Testcase -from util import ( +from bapctools import config +from bapctools import parallel +from bapctools import program +from bapctools import run +from bapctools import validate +from bapctools import visualize +from bapctools.problem import Problem +from bapctools.testcase import Testcase +from bapctools.util import ( combine_hashes, combine_hashes_dict, ensure_symlink, @@ -46,7 +46,7 @@ warn, write_yaml, ) -from verdicts import Verdict +from bapctools.verdicts import Verdict if has_ryaml: import ruamel.yaml diff --git a/bin/interactive.py b/bapctools/interactive.py similarity index 99% rename from bin/interactive.py rename to bapctools/interactive.py index cd1753c71..a20d9e7a3 100644 --- a/bin/interactive.py +++ b/bapctools/interactive.py @@ -9,9 +9,9 @@ from pathlib import Path from typing import Any, Final, IO, Literal, Optional, TYPE_CHECKING -import config -import validate -from util import ( +from bapctools import config +from bapctools import validate +from bapctools.util import ( eprint, error, exec_command, @@ -23,10 +23,10 @@ PrintBar, ProgressBar, ) -from verdicts import Verdict +from bapctools.verdicts import Verdict if TYPE_CHECKING: - from run import Run + from bapctools.run import Run BUFFER_SIZE: Final[int] = 2**20 diff --git a/bin/latex.py b/bapctools/latex.py similarity index 99% rename from bin/latex.py rename to bapctools/latex.py index d21cf6800..9db7475a8 100644 --- a/bin/latex.py +++ b/bapctools/latex.py @@ -8,9 +8,9 @@ from pathlib import Path from typing import Optional, TextIO, TYPE_CHECKING -import config -from contest import contest_yaml, problems_yaml -from util import ( +from bapctools import config +from bapctools.contest import contest_yaml, problems_yaml +from bapctools.util import ( copy_and_substitute, ensure_symlink, eprint, @@ -24,7 +24,7 @@ ) if TYPE_CHECKING: # Prevent circular import: https://stackoverflow.com/a/39757388 - from problem import Problem + from bapctools.problem import Problem class PdfType(Enum): diff --git a/bin/parallel.py b/bapctools/parallel.py similarity index 99% rename from bin/parallel.py rename to bapctools/parallel.py index 4121f003e..0d49b0e6d 100644 --- a/bin/parallel.py +++ b/bapctools/parallel.py @@ -5,8 +5,8 @@ from collections.abc import Callable, Sequence from typing import Any, Generic, Literal, Optional, TypeVar -import config -import util +from bapctools import config +from bapctools import util T = TypeVar("T") diff --git a/bin/problem.py b/bapctools/problem.py similarity index 99% rename from bin/problem.py rename to bapctools/problem.py index 07995c27c..ad0cf92ad 100644 --- a/bin/problem.py +++ b/bapctools/problem.py @@ -8,21 +8,21 @@ from typing import Final, Literal, Optional, overload, TYPE_CHECKING if TYPE_CHECKING: # Prevent circular import: https://stackoverflow.com/a/39757388 - from program import Program + from bapctools.program import Program import math -import check_testing_tool -import config -import latex -import parallel -import run -import testcase -import validate -import validator_tests -import verdicts -import visualize -from util import ( +from bapctools import check_testing_tool +from bapctools import config +from bapctools import latex +from bapctools import parallel +from bapctools import run +from bapctools import testcase +from bapctools import validate +from bapctools import validator_tests +from bapctools import verdicts +from bapctools import visualize +from bapctools.util import ( BAR_TYPE, combine_hashes_dict, drop_suffix, diff --git a/bin/program.py b/bapctools/program.py similarity index 99% rename from bin/program.py rename to bapctools/program.py index cb92cd270..0d47c3e62 100644 --- a/bin/program.py +++ b/bapctools/program.py @@ -9,8 +9,8 @@ from pathlib import Path from typing import Any, Final, Optional, TYPE_CHECKING -import config -from util import ( +from bapctools import config +from bapctools.util import ( combine_hashes, copy_and_substitute, ensure_symlink, @@ -31,7 +31,7 @@ ) if TYPE_CHECKING: # Prevent circular import: https://stackoverflow.com/a/39757388 - from problem import Problem + from bapctools.problem import Problem class Language: diff --git a/bin/run.py b/bapctools/run.py similarity index 98% rename from bin/run.py rename to bapctools/run.py index eb4f29564..1f419d17f 100644 --- a/bin/run.py +++ b/bapctools/run.py @@ -8,15 +8,15 @@ from pathlib import Path from typing import Optional -import config -import interactive -import parallel -import problem -import program -import validate -import visualize -from testcase import Testcase -from util import ( +from bapctools import config +from bapctools import interactive +from bapctools import parallel +from bapctools import problem +from bapctools import program +from bapctools import validate +from bapctools import visualize +from bapctools.testcase import Testcase +from bapctools.util import ( BAR_TYPE, crop_output, ensure_symlink, @@ -30,7 +30,7 @@ shorten_path, warn, ) -from verdicts import from_string, from_string_domjudge, RunUntil, Verdict, Verdicts, VerdictTable +from bapctools.verdicts import from_string, from_string_domjudge, RunUntil, Verdict, Verdicts, VerdictTable class Run: diff --git a/bin/skel.py b/bapctools/skel.py similarity index 98% rename from bin/skel.py rename to bapctools/skel.py index a5c18d4c7..7c951ecd8 100644 --- a/bin/skel.py +++ b/bapctools/skel.py @@ -5,11 +5,11 @@ from pathlib import Path # Local imports -import config -import contest -import latex -from problem import Problem -from util import ( +from bapctools import config +from bapctools import contest +from bapctools import latex +from bapctools.problem import Problem +from bapctools.util import ( ask_variable_bool, ask_variable_choice, ask_variable_string, @@ -27,7 +27,7 @@ warn, write_yaml, ) -from validate import OutputValidator +from bapctools.validate import OutputValidator # Returns the alphanumeric version of a string: diff --git a/bin/slack.py b/bapctools/slack.py similarity index 95% rename from bin/slack.py rename to bapctools/slack.py index e6bad1e86..b231f2c60 100644 --- a/bin/slack.py +++ b/bapctools/slack.py @@ -1,8 +1,8 @@ from typing import Any, TYPE_CHECKING -import config -from problem import Problem -from util import error, fatal, log, verbose +from bapctools import config +from bapctools.problem import Problem +from bapctools.util import error, fatal, log, verbose if TYPE_CHECKING: import requests diff --git a/bin/solve_stats.py b/bapctools/solve_stats.py similarity index 97% rename from bin/solve_stats.py rename to bapctools/solve_stats.py index c84ce064a..6ee30f4fd 100644 --- a/bin/solve_stats.py +++ b/bapctools/solve_stats.py @@ -3,10 +3,10 @@ from pathlib import Path from typing import Any, Optional -import config -import parallel -from contest import call_api_get_json, get_contest_id -from util import ProgressBar +from bapctools import config +from bapctools import parallel +from bapctools.contest import call_api_get_json, get_contest_id +from bapctools.util import ProgressBar # Note on multiprocessing: # Our custom parallel module uses light-weight threads, which all compete for the global interpreter lock: diff --git a/bin/stats.py b/bapctools/stats.py similarity index 98% rename from bin/stats.py rename to bapctools/stats.py index f339e2f2d..181d3c5e1 100644 --- a/bin/stats.py +++ b/bapctools/stats.py @@ -6,13 +6,13 @@ from pathlib import Path from typing import Any, cast, Literal, Optional -import config -import generate -import latex -import program -import validate -from problem import Problem -from util import eprint, error, glob, log, ShellCommand, warn +from bapctools import config +from bapctools import generate +from bapctools import latex +from bapctools import program +from bapctools import validate +from bapctools.problem import Problem +from bapctools.util import eprint, error, glob, log, ShellCommand, warn Selector = ( str | Callable[[Problem], int | float] | list[str] | list[Callable[[set[Path]], set[str]]] diff --git a/bin/testcase.py b/bapctools/testcase.py similarity index 99% rename from bin/testcase.py rename to bapctools/testcase.py index 294a9f54c..c55947b01 100644 --- a/bin/testcase.py +++ b/bapctools/testcase.py @@ -5,9 +5,9 @@ from pathlib import Path from typing import Optional, TYPE_CHECKING -import config -import validate -from util import ( +from bapctools import config +from bapctools import validate +from bapctools.util import ( BAR_TYPE, combine_hashes_dict, ExecStatus, @@ -18,8 +18,8 @@ ) if TYPE_CHECKING: # Prevent circular import: https://stackoverflow.com/a/39757388 - import problem - import visualize + from bapctools import problem + from bapctools import visualize # TODO #102: Consistently separate the compound noun "test case", e.g. "TestCase" or "test_case" diff --git a/bin/tools.py b/bapctools/tools.py similarity index 98% rename from bin/tools.py rename to bapctools/tools.py index afa5c32fa..db2cf193a 100755 --- a/bin/tools.py +++ b/bapctools/tools.py @@ -29,23 +29,23 @@ from typing import Any, Optional # Local imports -import config -import constraints -import contest -import download_submissions -import export -import fuzz -import generate -import latex -import skel -import slack -import solve_stats -import stats -import upgrade -import validate -from contest import call_api_get_json, contest_yaml, get_contest_id, problems_yaml -from problem import Problem -from util import ( +from bapctools import config +from bapctools import constraints +from bapctools import contest +from bapctools import download_submissions +from bapctools import export +from bapctools import fuzz +from bapctools import generate +from bapctools import latex +from bapctools import skel +from bapctools import slack +from bapctools import solve_stats +from bapctools import stats +from bapctools import upgrade +from bapctools import validate +from bapctools.contest import call_api_get_json, contest_yaml, get_contest_id, problems_yaml +from bapctools.problem import Problem +from bapctools.util import ( AbortException, ask_variable_bool, eprint, diff --git a/bin/upgrade.py b/bapctools/upgrade.py similarity index 99% rename from bin/upgrade.py rename to bapctools/upgrade.py index 00bc1ecd5..2fdb71dfc 100644 --- a/bin/upgrade.py +++ b/bapctools/upgrade.py @@ -6,9 +6,9 @@ from pathlib import Path from typing import Any, cast, Optional -import config -import generate -from util import ( +from bapctools import config +from bapctools import generate +from bapctools.util import ( fatal, has_ryaml, is_problem_directory, @@ -20,7 +20,7 @@ ryaml_replace, write_yaml, ) -from validate import AnswerValidator, InputValidator, OutputValidator +from bapctools.validate import AnswerValidator, InputValidator, OutputValidator if has_ryaml: from ruamel.yaml.comments import CommentedMap, CommentedSeq diff --git a/bin/util.py b/bapctools/util.py similarity index 99% rename from bin/util.py rename to bapctools/util.py index 2add520c4..5598f1179 100644 --- a/bin/util.py +++ b/bapctools/util.py @@ -36,7 +36,7 @@ ) from uuid import UUID -import config +from bapctools import config try: import ruamel.yaml @@ -51,8 +51,8 @@ has_ryaml = False if TYPE_CHECKING: # Prevent circular import: https://stackoverflow.com/a/39757388 - from problem import Problem - from verdicts import Verdict + from bapctools.problem import Problem + from bapctools.verdicts import Verdict # For some reason ryaml.load doesn't work well in parallel. diff --git a/bin/validate.py b/bapctools/validate.py similarity index 98% rename from bin/validate.py rename to bapctools/validate.py index 86bacbd54..b909b237e 100644 --- a/bin/validate.py +++ b/bapctools/validate.py @@ -4,14 +4,14 @@ from pathlib import Path from typing import Any, Final, Optional, TYPE_CHECKING -import config -import program -from util import ExecResult, ExecStatus, fatal, ProgressBar, validator_exec_code_map +from bapctools import config +from bapctools import program +from bapctools.util import ExecResult, ExecStatus, fatal, ProgressBar, validator_exec_code_map if TYPE_CHECKING: # Prevent circular import: https://stackoverflow.com/a/39757388 - import run - import testcase - from problem import Problem + from bapctools import run + from bapctools import testcase + from bapctools.problem import Problem class Mode(Enum): diff --git a/bin/validator_tests.py b/bapctools/validator_tests.py similarity index 98% rename from bin/validator_tests.py rename to bapctools/validator_tests.py index e1d6b8b56..91ab888d5 100644 --- a/bin/validator_tests.py +++ b/bapctools/validator_tests.py @@ -1,7 +1,7 @@ from collections.abc import Callable, Sequence from typing import Final, Optional, TypeVar -from validate import AnswerValidator, AnyValidator, InputValidator, OutputValidator +from bapctools.validate import AnswerValidator, AnyValidator, InputValidator, OutputValidator # helper function diff --git a/bin/verdicts.py b/bapctools/verdicts.py similarity index 99% rename from bin/verdicts.py rename to bapctools/verdicts.py index 8833742da..348f4fa6b 100644 --- a/bin/verdicts.py +++ b/bapctools/verdicts.py @@ -8,12 +8,12 @@ from pathlib import Path from typing import Any, Literal, Optional, TYPE_CHECKING -import config -import testcase -from util import eprint, ITEM_TYPE, ProgressBar +from bapctools import config +from bapctools import testcase +from bapctools.util import eprint, ITEM_TYPE, ProgressBar if TYPE_CHECKING: - import run + from bapctools import run class Verdict(Enum): diff --git a/bin/visualize.py b/bapctools/visualize.py similarity index 96% rename from bin/visualize.py rename to bapctools/visualize.py index 6275c1830..9a58d7b09 100644 --- a/bin/visualize.py +++ b/bapctools/visualize.py @@ -2,11 +2,11 @@ from pathlib import Path from typing import Any, Final, Optional, TYPE_CHECKING -import program -from util import ExecResult +from bapctools import program +from bapctools.util import ExecResult if TYPE_CHECKING: # Prevent circular import: https://stackoverflow.com/a/39757388 - from problem import Problem + from bapctools.problem import Problem class InputVisualizer(program.Program): From d43759c72e19d69038c0853d40e4ee1845a02bc4 Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 7 Nov 2025 15:33:11 +0100 Subject: [PATCH 02/18] Make `pip install .` work --- bapctools/__init__.py | 0 bapctools/__main__.py | 7 +++++++ pyproject.toml | 17 +++++++++++++++++ 3 files changed, 24 insertions(+) create mode 100644 bapctools/__init__.py create mode 100644 bapctools/__main__.py diff --git a/bapctools/__init__.py b/bapctools/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/bapctools/__main__.py b/bapctools/__main__.py new file mode 100644 index 000000000..f4c4fa520 --- /dev/null +++ b/bapctools/__main__.py @@ -0,0 +1,7 @@ +import sys + +from bapctools.tools import main + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/pyproject.toml b/pyproject.toml index 08f1bd9e7..1834a6695 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,20 @@ +[project] +name = "bapctools" +version = "0.0.0" +dependencies = [ + "pyyaml", + "colorama", + "argcomplete", + "python-dateutil", +] + +[project.optional-dependencies] +update-yaml = ["ruamel.yaml"] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + [tool.ruff] # Line length 100 line-length = 100 From 4801ac4f6e67e600af0cf4c42bf29806a9b916cc Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 7 Nov 2025 15:35:09 +0100 Subject: [PATCH 03/18] Add bt console script --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 1834a6695..bd85a1385 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,9 @@ dependencies = [ [project.optional-dependencies] update-yaml = ["ruamel.yaml"] +[project.scripts] +bt = "bapctools.tools:main" + [build-system] requires = ["hatchling"] build-backend = "hatchling.build" From 645980cc2dbdf78cdd15371072ad5c1eff1eb9da Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 7 Nov 2025 15:40:21 +0100 Subject: [PATCH 04/18] dependencies --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index bd85a1385..e5bfaefbd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,9 @@ dependencies = [ [project.optional-dependencies] update-yaml = ["ruamel.yaml"] +solve-stats = ["matplotlib"] +domjudge = ["requests"] +new = ["questionary"] [project.scripts] bt = "bapctools.tools:main" From 071eff51328c55d69d8bf11656968877988bbcac Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 7 Nov 2025 15:49:00 +0100 Subject: [PATCH 05/18] Replace selected files in pre-commit config, fix some imports, ruff format --- .pre-commit-config.yaml | 2 +- bapctools/program.py | 2 +- bapctools/run.py | 9 ++++++++- bapctools/validate.py | 2 +- bapctools/verdicts.py | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c53218981..e6ee15546 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ repos: - id: ruff args: [ --fix ] - id: ruff - files: ^bin/.*\.py$ + files: ^bapctools/.*\.py$ args: ["--select=I", "--fix"] - id: ruff-format - repo: https://github.com/pre-commit/mirrors-mypy diff --git a/bapctools/program.py b/bapctools/program.py index 0d47c3e62..9064aaacf 100644 --- a/bapctools/program.py +++ b/bapctools/program.py @@ -390,7 +390,7 @@ def _checks(self, bar: ProgressBar) -> None: pass # Warn for known bad (non-deterministic) patterns in generators - from validate import Validator + from bapctools.validate import Validator if isinstance(self, Generator) or isinstance(self, Validator): if "c++" in self.language.name.lower(): diff --git a/bapctools/run.py b/bapctools/run.py index 1f419d17f..aa4e90d45 100644 --- a/bapctools/run.py +++ b/bapctools/run.py @@ -30,7 +30,14 @@ shorten_path, warn, ) -from bapctools.verdicts import from_string, from_string_domjudge, RunUntil, Verdict, Verdicts, VerdictTable +from bapctools.verdicts import ( + from_string, + from_string_domjudge, + RunUntil, + Verdict, + Verdicts, + VerdictTable, +) class Run: diff --git a/bapctools/validate.py b/bapctools/validate.py index b909b237e..892d92984 100644 --- a/bapctools/validate.py +++ b/bapctools/validate.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: # Prevent circular import: https://stackoverflow.com/a/39757388 from bapctools import run - from bapctools import testcase + from bapctools import testcase from bapctools.problem import Problem diff --git a/bapctools/verdicts.py b/bapctools/verdicts.py index 348f4fa6b..c240d8f45 100644 --- a/bapctools/verdicts.py +++ b/bapctools/verdicts.py @@ -695,7 +695,7 @@ def _print(self, *args: Any, **kwargs: Any) -> None: eprint(*args, **kwargs) def start(self, item: ITEM_TYPE = "") -> "TableProgressBar": - from run import Run + from bapctools.run import Run assert isinstance(item, Run) self.table.add_test_case(item.testcase.name) From b55d6754b3f86bce06899aff437b420f9753fa99 Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 7 Nov 2025 15:54:39 +0100 Subject: [PATCH 06/18] Move stuff to `bapctools/resources` --- .../resources/config}/languages.yaml | 0 .../resources/headers}/compile_flags.txt | 0 .../resources/headers}/validation.h | 0 {latex => bapctools/resources/latex}/bapc.cls | 0 .../resources/latex}/contest-problem-slide.tex | 0 .../resources/latex}/contest-problem.tex | 0 .../resources/latex}/contest-solution.tex | 0 .../resources/latex}/contest-web.tex | 0 {latex => bapctools/resources/latex}/contest.tex | 0 .../resources/latex}/contest_data.tex | 0 .../resources/latex}/images/blank-page-fr.png | Bin .../resources/latex}/images/blank-page-fr.tex | 0 .../resources/latex}/images/cc-by-sa.pdf | Bin .../resources/latex}/images/logo-not-found.pdf | Bin {latex => bapctools/resources/latex}/lang/da.tex | 0 {latex => bapctools/resources/latex}/lang/de.tex | 0 {latex => bapctools/resources/latex}/lang/en.tex | 0 {latex => bapctools/resources/latex}/lang/es.tex | 0 {latex => bapctools/resources/latex}/lang/fr.tex | 0 {latex => bapctools/resources/latex}/lang/is.tex | 0 {latex => bapctools/resources/latex}/lang/nl.tex | 0 .../resources/latex}/problem-slide.tex | 0 .../resources/latex}/problem-slides-base.tex | 0 .../resources/latex}/problem-slides.tex | 0 {latex => bapctools/resources/latex}/problem.tex | 0 .../resources/latex}/solution-web.tex | 0 {latex => bapctools/resources/latex}/solution.tex | 0 .../resources/latex}/solutions-base.tex | 0 .../resources/latex}/solutions-web.tex | 0 {latex => bapctools/resources/latex}/solutions.tex | 0 .../resources/skel}/contest/.gitignore | 0 .../resources/skel}/contest/contest.yaml | 0 .../resources/skel}/contest/languages.yaml | 0 {skel => bapctools/resources/skel}/contest/logo.pdf | Bin .../resources/skel}/contest/problems.yaml | 0 .../resources/skel}/contest/solution_footer.tex | 0 .../resources/skel}/contest/solution_header.tex | 0 .../skel}/forgejo_actions_docker_bt/contest.yaml | 0 .../skel}/forgejo_actions_docker_bt/problem.yaml | 0 .../skel}/forgejo_actions_latest_bt/contest.yaml | 0 .../skel}/forgejo_actions_latest_bt/problem.yaml | 0 .../skel}/forgejo_actions_latest_bt/setup.yaml | 0 .../resources/skel}/gitlab_ci/contest.yaml | 0 .../resources/skel}/gitlab_ci/header_docker_bt.yaml | 0 .../resources/skel}/gitlab_ci/header_latest_bt.yaml | 0 .../resources/skel}/gitlab_ci/problem.yaml | 0 .../resources/skel}/multiple_validators/build | 0 .../resources/skel}/multiple_validators/run | 0 .../answer_validator/answer_validator.cpp | 0 .../answer_validators/answer_validator/validation.h | 0 .../skel}/problem/generators/example_generator.py | 0 .../skel}/problem/generators/generators.yaml | 0 .../input_validator/input_validator.cpp | 0 .../input_validators/input_validator/validation.h | 0 .../input_visualizer/example_input_visualizer.py | 0 .../skel}/problem/input_visualizer/readme.md | 0 .../problem/output_validator/output_validator.cpp | 0 .../skel}/problem/output_validator/validation.h | 0 .../output_visualizer/example_output_visualizer.py | 0 .../resources/skel}/problem/problem.yaml | 0 .../problem/problem_slide/problem-slide.en.tex | 0 .../skel}/problem/solution/solution.en.tex | 0 .../skel}/problem/statement/problem.en.tex | 0 .../skel}/problem/submissions/accepted/.gitkeep | 0 .../problem/submissions/run_time_error/.gitkeep | 0 .../submissions/time_limit_exceeded/.gitkeep | 0 .../skel}/problem/submissions/wrong_answer/.gitkeep | 0 .../resources/skel}/problem_cfp/AUTHOR.md | 0 .../resources/skel}/problem_cfp/README.md | 0 .../resources/skel}/problem_cfp/SOLUTION.md | 0 .../resources/skel}/problem_cfp/data/sample/1.ans | 0 .../resources/skel}/problem_cfp/data/sample/1.in | 0 .../skel}/problem_cfp/data/test_group.yaml | 0 .../resources/skel}/problem_cfp/problem.yaml | 0 .../skel}/problem_cfp/solution/solution.en.tex | 0 .../skel}/problem_cfp/statement/problem.en.tex | 0 .../problem_cfp/submissions/accepted/author.cpp | 0 .../problem_cfp/submissions/accepted/author.py | 0 {skel => bapctools/resources/skel}/testing_tool.py | 0 .../resources/skel}/testing_tool_multi_pass.py | 0 .../resources/support}/default_output_validator.cpp | 0 .../resources/support}/schemas/generators.cue | 0 .../support}/schemas/generators_yaml_schema.json | 0 .../resources/support}/schemas/problemformat.cue | 0 .../resources/third_party}/checktestdata | Bin .../resources/third_party}/readme.md | 0 .../third_party}/viva/VIVA User's Guide.pdf | Bin .../resources/third_party}/viva/viva.jar | Bin .../resources/third_party}/viva/vivagui.jar | Bin 89 files changed, 0 insertions(+), 0 deletions(-) rename {config => bapctools/resources/config}/languages.yaml (100%) rename {headers => bapctools/resources/headers}/compile_flags.txt (100%) rename {headers => bapctools/resources/headers}/validation.h (100%) rename {latex => bapctools/resources/latex}/bapc.cls (100%) rename {latex => bapctools/resources/latex}/contest-problem-slide.tex (100%) rename {latex => bapctools/resources/latex}/contest-problem.tex (100%) rename {latex => bapctools/resources/latex}/contest-solution.tex (100%) rename {latex => bapctools/resources/latex}/contest-web.tex (100%) rename {latex => bapctools/resources/latex}/contest.tex (100%) rename {latex => bapctools/resources/latex}/contest_data.tex (100%) rename {latex => bapctools/resources/latex}/images/blank-page-fr.png (100%) rename {latex => bapctools/resources/latex}/images/blank-page-fr.tex (100%) rename {latex => bapctools/resources/latex}/images/cc-by-sa.pdf (100%) rename {latex => bapctools/resources/latex}/images/logo-not-found.pdf (100%) rename {latex => bapctools/resources/latex}/lang/da.tex (100%) rename {latex => bapctools/resources/latex}/lang/de.tex (100%) rename {latex => bapctools/resources/latex}/lang/en.tex (100%) rename {latex => bapctools/resources/latex}/lang/es.tex (100%) rename {latex => bapctools/resources/latex}/lang/fr.tex (100%) rename {latex => bapctools/resources/latex}/lang/is.tex (100%) rename {latex => bapctools/resources/latex}/lang/nl.tex (100%) rename {latex => bapctools/resources/latex}/problem-slide.tex (100%) rename {latex => bapctools/resources/latex}/problem-slides-base.tex (100%) rename {latex => bapctools/resources/latex}/problem-slides.tex (100%) rename {latex => bapctools/resources/latex}/problem.tex (100%) rename {latex => bapctools/resources/latex}/solution-web.tex (100%) rename {latex => bapctools/resources/latex}/solution.tex (100%) rename {latex => bapctools/resources/latex}/solutions-base.tex (100%) rename {latex => bapctools/resources/latex}/solutions-web.tex (100%) rename {latex => bapctools/resources/latex}/solutions.tex (100%) rename {skel => bapctools/resources/skel}/contest/.gitignore (100%) rename {skel => bapctools/resources/skel}/contest/contest.yaml (100%) rename {skel => bapctools/resources/skel}/contest/languages.yaml (100%) rename {skel => bapctools/resources/skel}/contest/logo.pdf (100%) rename {skel => bapctools/resources/skel}/contest/problems.yaml (100%) rename {skel => bapctools/resources/skel}/contest/solution_footer.tex (100%) rename {skel => bapctools/resources/skel}/contest/solution_header.tex (100%) rename {skel => bapctools/resources/skel}/forgejo_actions_docker_bt/contest.yaml (100%) rename {skel => bapctools/resources/skel}/forgejo_actions_docker_bt/problem.yaml (100%) rename {skel => bapctools/resources/skel}/forgejo_actions_latest_bt/contest.yaml (100%) rename {skel => bapctools/resources/skel}/forgejo_actions_latest_bt/problem.yaml (100%) rename {skel => bapctools/resources/skel}/forgejo_actions_latest_bt/setup.yaml (100%) rename {skel => bapctools/resources/skel}/gitlab_ci/contest.yaml (100%) rename {skel => bapctools/resources/skel}/gitlab_ci/header_docker_bt.yaml (100%) rename {skel => bapctools/resources/skel}/gitlab_ci/header_latest_bt.yaml (100%) rename {skel => bapctools/resources/skel}/gitlab_ci/problem.yaml (100%) rename {skel => bapctools/resources/skel}/multiple_validators/build (100%) rename {skel => bapctools/resources/skel}/multiple_validators/run (100%) rename {skel => bapctools/resources/skel}/problem/answer_validators/answer_validator/answer_validator.cpp (100%) rename {skel => bapctools/resources/skel}/problem/answer_validators/answer_validator/validation.h (100%) rename {skel => bapctools/resources/skel}/problem/generators/example_generator.py (100%) rename {skel => bapctools/resources/skel}/problem/generators/generators.yaml (100%) rename {skel => bapctools/resources/skel}/problem/input_validators/input_validator/input_validator.cpp (100%) rename {skel => bapctools/resources/skel}/problem/input_validators/input_validator/validation.h (100%) rename {skel => bapctools/resources/skel}/problem/input_visualizer/example_input_visualizer.py (100%) rename {skel => bapctools/resources/skel}/problem/input_visualizer/readme.md (100%) rename {skel => bapctools/resources/skel}/problem/output_validator/output_validator.cpp (100%) rename {skel => bapctools/resources/skel}/problem/output_validator/validation.h (100%) rename {skel => bapctools/resources/skel}/problem/output_visualizer/example_output_visualizer.py (100%) rename {skel => bapctools/resources/skel}/problem/problem.yaml (100%) rename {skel => bapctools/resources/skel}/problem/problem_slide/problem-slide.en.tex (100%) rename {skel => bapctools/resources/skel}/problem/solution/solution.en.tex (100%) rename {skel => bapctools/resources/skel}/problem/statement/problem.en.tex (100%) rename {skel => bapctools/resources/skel}/problem/submissions/accepted/.gitkeep (100%) rename {skel => bapctools/resources/skel}/problem/submissions/run_time_error/.gitkeep (100%) rename {skel => bapctools/resources/skel}/problem/submissions/time_limit_exceeded/.gitkeep (100%) rename {skel => bapctools/resources/skel}/problem/submissions/wrong_answer/.gitkeep (100%) rename {skel => bapctools/resources/skel}/problem_cfp/AUTHOR.md (100%) rename {skel => bapctools/resources/skel}/problem_cfp/README.md (100%) rename {skel => bapctools/resources/skel}/problem_cfp/SOLUTION.md (100%) rename {skel => bapctools/resources/skel}/problem_cfp/data/sample/1.ans (100%) rename {skel => bapctools/resources/skel}/problem_cfp/data/sample/1.in (100%) rename {skel => bapctools/resources/skel}/problem_cfp/data/test_group.yaml (100%) rename {skel => bapctools/resources/skel}/problem_cfp/problem.yaml (100%) rename {skel => bapctools/resources/skel}/problem_cfp/solution/solution.en.tex (100%) rename {skel => bapctools/resources/skel}/problem_cfp/statement/problem.en.tex (100%) rename {skel => bapctools/resources/skel}/problem_cfp/submissions/accepted/author.cpp (100%) rename {skel => bapctools/resources/skel}/problem_cfp/submissions/accepted/author.py (100%) rename {skel => bapctools/resources/skel}/testing_tool.py (100%) rename {skel => bapctools/resources/skel}/testing_tool_multi_pass.py (100%) rename {support => bapctools/resources/support}/default_output_validator.cpp (100%) rename {support => bapctools/resources/support}/schemas/generators.cue (100%) rename {support => bapctools/resources/support}/schemas/generators_yaml_schema.json (100%) rename {support => bapctools/resources/support}/schemas/problemformat.cue (100%) rename {third_party => bapctools/resources/third_party}/checktestdata (100%) rename {third_party => bapctools/resources/third_party}/readme.md (100%) rename {third_party => bapctools/resources/third_party}/viva/VIVA User's Guide.pdf (100%) rename {third_party => bapctools/resources/third_party}/viva/viva.jar (100%) rename {third_party => bapctools/resources/third_party}/viva/vivagui.jar (100%) diff --git a/config/languages.yaml b/bapctools/resources/config/languages.yaml similarity index 100% rename from config/languages.yaml rename to bapctools/resources/config/languages.yaml diff --git a/headers/compile_flags.txt b/bapctools/resources/headers/compile_flags.txt similarity index 100% rename from headers/compile_flags.txt rename to bapctools/resources/headers/compile_flags.txt diff --git a/headers/validation.h b/bapctools/resources/headers/validation.h similarity index 100% rename from headers/validation.h rename to bapctools/resources/headers/validation.h diff --git a/latex/bapc.cls b/bapctools/resources/latex/bapc.cls similarity index 100% rename from latex/bapc.cls rename to bapctools/resources/latex/bapc.cls diff --git a/latex/contest-problem-slide.tex b/bapctools/resources/latex/contest-problem-slide.tex similarity index 100% rename from latex/contest-problem-slide.tex rename to bapctools/resources/latex/contest-problem-slide.tex diff --git a/latex/contest-problem.tex b/bapctools/resources/latex/contest-problem.tex similarity index 100% rename from latex/contest-problem.tex rename to bapctools/resources/latex/contest-problem.tex diff --git a/latex/contest-solution.tex b/bapctools/resources/latex/contest-solution.tex similarity index 100% rename from latex/contest-solution.tex rename to bapctools/resources/latex/contest-solution.tex diff --git a/latex/contest-web.tex b/bapctools/resources/latex/contest-web.tex similarity index 100% rename from latex/contest-web.tex rename to bapctools/resources/latex/contest-web.tex diff --git a/latex/contest.tex b/bapctools/resources/latex/contest.tex similarity index 100% rename from latex/contest.tex rename to bapctools/resources/latex/contest.tex diff --git a/latex/contest_data.tex b/bapctools/resources/latex/contest_data.tex similarity index 100% rename from latex/contest_data.tex rename to bapctools/resources/latex/contest_data.tex diff --git a/latex/images/blank-page-fr.png b/bapctools/resources/latex/images/blank-page-fr.png similarity index 100% rename from latex/images/blank-page-fr.png rename to bapctools/resources/latex/images/blank-page-fr.png diff --git a/latex/images/blank-page-fr.tex b/bapctools/resources/latex/images/blank-page-fr.tex similarity index 100% rename from latex/images/blank-page-fr.tex rename to bapctools/resources/latex/images/blank-page-fr.tex diff --git a/latex/images/cc-by-sa.pdf b/bapctools/resources/latex/images/cc-by-sa.pdf similarity index 100% rename from latex/images/cc-by-sa.pdf rename to bapctools/resources/latex/images/cc-by-sa.pdf diff --git a/latex/images/logo-not-found.pdf b/bapctools/resources/latex/images/logo-not-found.pdf similarity index 100% rename from latex/images/logo-not-found.pdf rename to bapctools/resources/latex/images/logo-not-found.pdf diff --git a/latex/lang/da.tex b/bapctools/resources/latex/lang/da.tex similarity index 100% rename from latex/lang/da.tex rename to bapctools/resources/latex/lang/da.tex diff --git a/latex/lang/de.tex b/bapctools/resources/latex/lang/de.tex similarity index 100% rename from latex/lang/de.tex rename to bapctools/resources/latex/lang/de.tex diff --git a/latex/lang/en.tex b/bapctools/resources/latex/lang/en.tex similarity index 100% rename from latex/lang/en.tex rename to bapctools/resources/latex/lang/en.tex diff --git a/latex/lang/es.tex b/bapctools/resources/latex/lang/es.tex similarity index 100% rename from latex/lang/es.tex rename to bapctools/resources/latex/lang/es.tex diff --git a/latex/lang/fr.tex b/bapctools/resources/latex/lang/fr.tex similarity index 100% rename from latex/lang/fr.tex rename to bapctools/resources/latex/lang/fr.tex diff --git a/latex/lang/is.tex b/bapctools/resources/latex/lang/is.tex similarity index 100% rename from latex/lang/is.tex rename to bapctools/resources/latex/lang/is.tex diff --git a/latex/lang/nl.tex b/bapctools/resources/latex/lang/nl.tex similarity index 100% rename from latex/lang/nl.tex rename to bapctools/resources/latex/lang/nl.tex diff --git a/latex/problem-slide.tex b/bapctools/resources/latex/problem-slide.tex similarity index 100% rename from latex/problem-slide.tex rename to bapctools/resources/latex/problem-slide.tex diff --git a/latex/problem-slides-base.tex b/bapctools/resources/latex/problem-slides-base.tex similarity index 100% rename from latex/problem-slides-base.tex rename to bapctools/resources/latex/problem-slides-base.tex diff --git a/latex/problem-slides.tex b/bapctools/resources/latex/problem-slides.tex similarity index 100% rename from latex/problem-slides.tex rename to bapctools/resources/latex/problem-slides.tex diff --git a/latex/problem.tex b/bapctools/resources/latex/problem.tex similarity index 100% rename from latex/problem.tex rename to bapctools/resources/latex/problem.tex diff --git a/latex/solution-web.tex b/bapctools/resources/latex/solution-web.tex similarity index 100% rename from latex/solution-web.tex rename to bapctools/resources/latex/solution-web.tex diff --git a/latex/solution.tex b/bapctools/resources/latex/solution.tex similarity index 100% rename from latex/solution.tex rename to bapctools/resources/latex/solution.tex diff --git a/latex/solutions-base.tex b/bapctools/resources/latex/solutions-base.tex similarity index 100% rename from latex/solutions-base.tex rename to bapctools/resources/latex/solutions-base.tex diff --git a/latex/solutions-web.tex b/bapctools/resources/latex/solutions-web.tex similarity index 100% rename from latex/solutions-web.tex rename to bapctools/resources/latex/solutions-web.tex diff --git a/latex/solutions.tex b/bapctools/resources/latex/solutions.tex similarity index 100% rename from latex/solutions.tex rename to bapctools/resources/latex/solutions.tex diff --git a/skel/contest/.gitignore b/bapctools/resources/skel/contest/.gitignore similarity index 100% rename from skel/contest/.gitignore rename to bapctools/resources/skel/contest/.gitignore diff --git a/skel/contest/contest.yaml b/bapctools/resources/skel/contest/contest.yaml similarity index 100% rename from skel/contest/contest.yaml rename to bapctools/resources/skel/contest/contest.yaml diff --git a/skel/contest/languages.yaml b/bapctools/resources/skel/contest/languages.yaml similarity index 100% rename from skel/contest/languages.yaml rename to bapctools/resources/skel/contest/languages.yaml diff --git a/skel/contest/logo.pdf b/bapctools/resources/skel/contest/logo.pdf similarity index 100% rename from skel/contest/logo.pdf rename to bapctools/resources/skel/contest/logo.pdf diff --git a/skel/contest/problems.yaml b/bapctools/resources/skel/contest/problems.yaml similarity index 100% rename from skel/contest/problems.yaml rename to bapctools/resources/skel/contest/problems.yaml diff --git a/skel/contest/solution_footer.tex b/bapctools/resources/skel/contest/solution_footer.tex similarity index 100% rename from skel/contest/solution_footer.tex rename to bapctools/resources/skel/contest/solution_footer.tex diff --git a/skel/contest/solution_header.tex b/bapctools/resources/skel/contest/solution_header.tex similarity index 100% rename from skel/contest/solution_header.tex rename to bapctools/resources/skel/contest/solution_header.tex diff --git a/skel/forgejo_actions_docker_bt/contest.yaml b/bapctools/resources/skel/forgejo_actions_docker_bt/contest.yaml similarity index 100% rename from skel/forgejo_actions_docker_bt/contest.yaml rename to bapctools/resources/skel/forgejo_actions_docker_bt/contest.yaml diff --git a/skel/forgejo_actions_docker_bt/problem.yaml b/bapctools/resources/skel/forgejo_actions_docker_bt/problem.yaml similarity index 100% rename from skel/forgejo_actions_docker_bt/problem.yaml rename to bapctools/resources/skel/forgejo_actions_docker_bt/problem.yaml diff --git a/skel/forgejo_actions_latest_bt/contest.yaml b/bapctools/resources/skel/forgejo_actions_latest_bt/contest.yaml similarity index 100% rename from skel/forgejo_actions_latest_bt/contest.yaml rename to bapctools/resources/skel/forgejo_actions_latest_bt/contest.yaml diff --git a/skel/forgejo_actions_latest_bt/problem.yaml b/bapctools/resources/skel/forgejo_actions_latest_bt/problem.yaml similarity index 100% rename from skel/forgejo_actions_latest_bt/problem.yaml rename to bapctools/resources/skel/forgejo_actions_latest_bt/problem.yaml diff --git a/skel/forgejo_actions_latest_bt/setup.yaml b/bapctools/resources/skel/forgejo_actions_latest_bt/setup.yaml similarity index 100% rename from skel/forgejo_actions_latest_bt/setup.yaml rename to bapctools/resources/skel/forgejo_actions_latest_bt/setup.yaml diff --git a/skel/gitlab_ci/contest.yaml b/bapctools/resources/skel/gitlab_ci/contest.yaml similarity index 100% rename from skel/gitlab_ci/contest.yaml rename to bapctools/resources/skel/gitlab_ci/contest.yaml diff --git a/skel/gitlab_ci/header_docker_bt.yaml b/bapctools/resources/skel/gitlab_ci/header_docker_bt.yaml similarity index 100% rename from skel/gitlab_ci/header_docker_bt.yaml rename to bapctools/resources/skel/gitlab_ci/header_docker_bt.yaml diff --git a/skel/gitlab_ci/header_latest_bt.yaml b/bapctools/resources/skel/gitlab_ci/header_latest_bt.yaml similarity index 100% rename from skel/gitlab_ci/header_latest_bt.yaml rename to bapctools/resources/skel/gitlab_ci/header_latest_bt.yaml diff --git a/skel/gitlab_ci/problem.yaml b/bapctools/resources/skel/gitlab_ci/problem.yaml similarity index 100% rename from skel/gitlab_ci/problem.yaml rename to bapctools/resources/skel/gitlab_ci/problem.yaml diff --git a/skel/multiple_validators/build b/bapctools/resources/skel/multiple_validators/build similarity index 100% rename from skel/multiple_validators/build rename to bapctools/resources/skel/multiple_validators/build diff --git a/skel/multiple_validators/run b/bapctools/resources/skel/multiple_validators/run similarity index 100% rename from skel/multiple_validators/run rename to bapctools/resources/skel/multiple_validators/run diff --git a/skel/problem/answer_validators/answer_validator/answer_validator.cpp b/bapctools/resources/skel/problem/answer_validators/answer_validator/answer_validator.cpp similarity index 100% rename from skel/problem/answer_validators/answer_validator/answer_validator.cpp rename to bapctools/resources/skel/problem/answer_validators/answer_validator/answer_validator.cpp diff --git a/skel/problem/answer_validators/answer_validator/validation.h b/bapctools/resources/skel/problem/answer_validators/answer_validator/validation.h similarity index 100% rename from skel/problem/answer_validators/answer_validator/validation.h rename to bapctools/resources/skel/problem/answer_validators/answer_validator/validation.h diff --git a/skel/problem/generators/example_generator.py b/bapctools/resources/skel/problem/generators/example_generator.py similarity index 100% rename from skel/problem/generators/example_generator.py rename to bapctools/resources/skel/problem/generators/example_generator.py diff --git a/skel/problem/generators/generators.yaml b/bapctools/resources/skel/problem/generators/generators.yaml similarity index 100% rename from skel/problem/generators/generators.yaml rename to bapctools/resources/skel/problem/generators/generators.yaml diff --git a/skel/problem/input_validators/input_validator/input_validator.cpp b/bapctools/resources/skel/problem/input_validators/input_validator/input_validator.cpp similarity index 100% rename from skel/problem/input_validators/input_validator/input_validator.cpp rename to bapctools/resources/skel/problem/input_validators/input_validator/input_validator.cpp diff --git a/skel/problem/input_validators/input_validator/validation.h b/bapctools/resources/skel/problem/input_validators/input_validator/validation.h similarity index 100% rename from skel/problem/input_validators/input_validator/validation.h rename to bapctools/resources/skel/problem/input_validators/input_validator/validation.h diff --git a/skel/problem/input_visualizer/example_input_visualizer.py b/bapctools/resources/skel/problem/input_visualizer/example_input_visualizer.py similarity index 100% rename from skel/problem/input_visualizer/example_input_visualizer.py rename to bapctools/resources/skel/problem/input_visualizer/example_input_visualizer.py diff --git a/skel/problem/input_visualizer/readme.md b/bapctools/resources/skel/problem/input_visualizer/readme.md similarity index 100% rename from skel/problem/input_visualizer/readme.md rename to bapctools/resources/skel/problem/input_visualizer/readme.md diff --git a/skel/problem/output_validator/output_validator.cpp b/bapctools/resources/skel/problem/output_validator/output_validator.cpp similarity index 100% rename from skel/problem/output_validator/output_validator.cpp rename to bapctools/resources/skel/problem/output_validator/output_validator.cpp diff --git a/skel/problem/output_validator/validation.h b/bapctools/resources/skel/problem/output_validator/validation.h similarity index 100% rename from skel/problem/output_validator/validation.h rename to bapctools/resources/skel/problem/output_validator/validation.h diff --git a/skel/problem/output_visualizer/example_output_visualizer.py b/bapctools/resources/skel/problem/output_visualizer/example_output_visualizer.py similarity index 100% rename from skel/problem/output_visualizer/example_output_visualizer.py rename to bapctools/resources/skel/problem/output_visualizer/example_output_visualizer.py diff --git a/skel/problem/problem.yaml b/bapctools/resources/skel/problem/problem.yaml similarity index 100% rename from skel/problem/problem.yaml rename to bapctools/resources/skel/problem/problem.yaml diff --git a/skel/problem/problem_slide/problem-slide.en.tex b/bapctools/resources/skel/problem/problem_slide/problem-slide.en.tex similarity index 100% rename from skel/problem/problem_slide/problem-slide.en.tex rename to bapctools/resources/skel/problem/problem_slide/problem-slide.en.tex diff --git a/skel/problem/solution/solution.en.tex b/bapctools/resources/skel/problem/solution/solution.en.tex similarity index 100% rename from skel/problem/solution/solution.en.tex rename to bapctools/resources/skel/problem/solution/solution.en.tex diff --git a/skel/problem/statement/problem.en.tex b/bapctools/resources/skel/problem/statement/problem.en.tex similarity index 100% rename from skel/problem/statement/problem.en.tex rename to bapctools/resources/skel/problem/statement/problem.en.tex diff --git a/skel/problem/submissions/accepted/.gitkeep b/bapctools/resources/skel/problem/submissions/accepted/.gitkeep similarity index 100% rename from skel/problem/submissions/accepted/.gitkeep rename to bapctools/resources/skel/problem/submissions/accepted/.gitkeep diff --git a/skel/problem/submissions/run_time_error/.gitkeep b/bapctools/resources/skel/problem/submissions/run_time_error/.gitkeep similarity index 100% rename from skel/problem/submissions/run_time_error/.gitkeep rename to bapctools/resources/skel/problem/submissions/run_time_error/.gitkeep diff --git a/skel/problem/submissions/time_limit_exceeded/.gitkeep b/bapctools/resources/skel/problem/submissions/time_limit_exceeded/.gitkeep similarity index 100% rename from skel/problem/submissions/time_limit_exceeded/.gitkeep rename to bapctools/resources/skel/problem/submissions/time_limit_exceeded/.gitkeep diff --git a/skel/problem/submissions/wrong_answer/.gitkeep b/bapctools/resources/skel/problem/submissions/wrong_answer/.gitkeep similarity index 100% rename from skel/problem/submissions/wrong_answer/.gitkeep rename to bapctools/resources/skel/problem/submissions/wrong_answer/.gitkeep diff --git a/skel/problem_cfp/AUTHOR.md b/bapctools/resources/skel/problem_cfp/AUTHOR.md similarity index 100% rename from skel/problem_cfp/AUTHOR.md rename to bapctools/resources/skel/problem_cfp/AUTHOR.md diff --git a/skel/problem_cfp/README.md b/bapctools/resources/skel/problem_cfp/README.md similarity index 100% rename from skel/problem_cfp/README.md rename to bapctools/resources/skel/problem_cfp/README.md diff --git a/skel/problem_cfp/SOLUTION.md b/bapctools/resources/skel/problem_cfp/SOLUTION.md similarity index 100% rename from skel/problem_cfp/SOLUTION.md rename to bapctools/resources/skel/problem_cfp/SOLUTION.md diff --git a/skel/problem_cfp/data/sample/1.ans b/bapctools/resources/skel/problem_cfp/data/sample/1.ans similarity index 100% rename from skel/problem_cfp/data/sample/1.ans rename to bapctools/resources/skel/problem_cfp/data/sample/1.ans diff --git a/skel/problem_cfp/data/sample/1.in b/bapctools/resources/skel/problem_cfp/data/sample/1.in similarity index 100% rename from skel/problem_cfp/data/sample/1.in rename to bapctools/resources/skel/problem_cfp/data/sample/1.in diff --git a/skel/problem_cfp/data/test_group.yaml b/bapctools/resources/skel/problem_cfp/data/test_group.yaml similarity index 100% rename from skel/problem_cfp/data/test_group.yaml rename to bapctools/resources/skel/problem_cfp/data/test_group.yaml diff --git a/skel/problem_cfp/problem.yaml b/bapctools/resources/skel/problem_cfp/problem.yaml similarity index 100% rename from skel/problem_cfp/problem.yaml rename to bapctools/resources/skel/problem_cfp/problem.yaml diff --git a/skel/problem_cfp/solution/solution.en.tex b/bapctools/resources/skel/problem_cfp/solution/solution.en.tex similarity index 100% rename from skel/problem_cfp/solution/solution.en.tex rename to bapctools/resources/skel/problem_cfp/solution/solution.en.tex diff --git a/skel/problem_cfp/statement/problem.en.tex b/bapctools/resources/skel/problem_cfp/statement/problem.en.tex similarity index 100% rename from skel/problem_cfp/statement/problem.en.tex rename to bapctools/resources/skel/problem_cfp/statement/problem.en.tex diff --git a/skel/problem_cfp/submissions/accepted/author.cpp b/bapctools/resources/skel/problem_cfp/submissions/accepted/author.cpp similarity index 100% rename from skel/problem_cfp/submissions/accepted/author.cpp rename to bapctools/resources/skel/problem_cfp/submissions/accepted/author.cpp diff --git a/skel/problem_cfp/submissions/accepted/author.py b/bapctools/resources/skel/problem_cfp/submissions/accepted/author.py similarity index 100% rename from skel/problem_cfp/submissions/accepted/author.py rename to bapctools/resources/skel/problem_cfp/submissions/accepted/author.py diff --git a/skel/testing_tool.py b/bapctools/resources/skel/testing_tool.py similarity index 100% rename from skel/testing_tool.py rename to bapctools/resources/skel/testing_tool.py diff --git a/skel/testing_tool_multi_pass.py b/bapctools/resources/skel/testing_tool_multi_pass.py similarity index 100% rename from skel/testing_tool_multi_pass.py rename to bapctools/resources/skel/testing_tool_multi_pass.py diff --git a/support/default_output_validator.cpp b/bapctools/resources/support/default_output_validator.cpp similarity index 100% rename from support/default_output_validator.cpp rename to bapctools/resources/support/default_output_validator.cpp diff --git a/support/schemas/generators.cue b/bapctools/resources/support/schemas/generators.cue similarity index 100% rename from support/schemas/generators.cue rename to bapctools/resources/support/schemas/generators.cue diff --git a/support/schemas/generators_yaml_schema.json b/bapctools/resources/support/schemas/generators_yaml_schema.json similarity index 100% rename from support/schemas/generators_yaml_schema.json rename to bapctools/resources/support/schemas/generators_yaml_schema.json diff --git a/support/schemas/problemformat.cue b/bapctools/resources/support/schemas/problemformat.cue similarity index 100% rename from support/schemas/problemformat.cue rename to bapctools/resources/support/schemas/problemformat.cue diff --git a/third_party/checktestdata b/bapctools/resources/third_party/checktestdata similarity index 100% rename from third_party/checktestdata rename to bapctools/resources/third_party/checktestdata diff --git a/third_party/readme.md b/bapctools/resources/third_party/readme.md similarity index 100% rename from third_party/readme.md rename to bapctools/resources/third_party/readme.md diff --git a/third_party/viva/VIVA User's Guide.pdf b/bapctools/resources/third_party/viva/VIVA User's Guide.pdf similarity index 100% rename from third_party/viva/VIVA User's Guide.pdf rename to bapctools/resources/third_party/viva/VIVA User's Guide.pdf diff --git a/third_party/viva/viva.jar b/bapctools/resources/third_party/viva/viva.jar similarity index 100% rename from third_party/viva/viva.jar rename to bapctools/resources/third_party/viva/viva.jar diff --git a/third_party/viva/vivagui.jar b/bapctools/resources/third_party/viva/vivagui.jar similarity index 100% rename from third_party/viva/vivagui.jar rename to bapctools/resources/third_party/viva/vivagui.jar From 966b103ca9c4249c76225273fb199564c6745280 Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 7 Nov 2025 16:02:36 +0100 Subject: [PATCH 07/18] Rename TOOLS_ROOT to RESOURCES_ROOT --- bapctools/config.py | 8 +++++--- bapctools/latex.py | 10 +++++----- bapctools/problem.py | 2 +- bapctools/program.py | 4 ++-- bapctools/skel.py | 20 ++++++++++---------- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/bapctools/config.py b/bapctools/config.py index 20149e24a..cfa4dcf0b 100644 --- a/bapctools/config.py +++ b/bapctools/config.py @@ -9,6 +9,8 @@ from pathlib import Path from typing import Any, Final, Literal, Optional, TypeVar +import bapctools + # Randomly generated uuid4 for BAPCtools BAPC_UUID: Final[str] = "8ee7605a-d1ce-47b3-be37-15de5acd757e" BAPC_UUID_PREFIX: Final[int] = 8 @@ -98,14 +100,14 @@ SEED_DEPENDENCY_RETRIES: Final[int] = 10 -# The root directory of the BAPCtools repository. -TOOLS_ROOT: Final[Path] = Path(__file__).absolute().parent.parent +# The directory containing all non-python resources +RESOURCES_ROOT: Final[Path] = Path(bapctools.__file__).parent / "resources" # The directory from which BAPCtools is invoked. current_working_directory: Final[Path] = Path.cwd().absolute() # Add third_party/ to the $PATH for checktestdata. -os.environ["PATH"] += os.pathsep + str(TOOLS_ROOT / "third_party") +os.environ["PATH"] += os.pathsep + str(RESOURCES_ROOT / "third_party") # Below here is some global state that will be filled in main(). diff --git a/bapctools/latex.py b/bapctools/latex.py index 9db7475a8..be46c3b7a 100644 --- a/bapctools/latex.py +++ b/bapctools/latex.py @@ -230,7 +230,7 @@ def make_environment(builddir: Path) -> dict[str, str]: cwd / "solve_stats", cwd / "solve_stats" / "activity", cwd / "latex", - config.TOOLS_ROOT / "latex", + config.RESOURCES_ROOT / "latex", # The default empty element at the end makes sure that the new TEXINPUTS ends with a path separator. # This is required to make LaTeX look in the default global paths: https://tex.stackexchange.com/a/410353 env.get("TEXINPUTS", ""), @@ -375,7 +375,7 @@ def build_problem_pdf( local_data = Path(main_file) copy_and_substitute( - local_data if local_data.is_file() else config.TOOLS_ROOT / "latex" / main_file, + local_data if local_data.is_file() else config.RESOURCES_ROOT / "latex" / main_file, builddir / main_file, problem_data(problem, language), bar=bar, @@ -419,7 +419,7 @@ def find_logo() -> Path: logo = Path(directory + "logo." + extension) if logo.exists(): return logo - return config.TOOLS_ROOT / "latex" / "images" / "logo-not-found.pdf" + return config.RESOURCES_ROOT / "latex" / "images" / "logo-not-found.pdf" def build_contest_pdf( @@ -458,7 +458,7 @@ def build_contest_pdf( ( local_contest_data if local_contest_data.is_file() - else config.TOOLS_ROOT / "latex" / "contest_data.tex" + else config.RESOURCES_ROOT / "latex" / "contest_data.tex" ), builddir / "contest_data.tex", config_data, @@ -480,7 +480,7 @@ def build_contest_pdf( per_problem_data_tex = ( local_per_problem_data if local_per_problem_data.is_file() - else config.TOOLS_ROOT / "latex" / local_per_problem_data.name + else config.RESOURCES_ROOT / "latex" / local_per_problem_data.name ).read_text() for prob in problems: diff --git a/bapctools/problem.py b/bapctools/problem.py index ad0cf92ad..282291230 100644 --- a/bapctools/problem.py +++ b/bapctools/problem.py @@ -1047,7 +1047,7 @@ def _validators( if problem.custom_output: paths = [problem.path / validate.OutputValidator.source_dir] else: - paths = [config.TOOLS_ROOT / "support" / "default_output_validator.cpp"] + paths = [config.RESOURCES_ROOT / "support" / "default_output_validator.cpp"] else: paths = list(glob(problem.path / cls.source_dir, "*")) diff --git a/bapctools/program.py b/bapctools/program.py index 9064aaacf..e5887f868 100644 --- a/bapctools/program.py +++ b/bapctools/program.py @@ -128,7 +128,7 @@ def languages() -> Sequence[Language]: if languages_path.is_file(): raw_languages = read_yaml(languages_path) else: - raw_languages = read_yaml(config.TOOLS_ROOT / "config/languages.yaml") + raw_languages = read_yaml(config.RESOURCES_ROOT / "config/languages.yaml") if not isinstance(raw_languages, dict): fatal("could not parse languages.yaml.") @@ -359,7 +359,7 @@ def _get_language(self, bar: ProgressBar) -> bool: self.tmpdir / "build" if (self.tmpdir / "build") in self.input_files else "" ), "run": self.tmpdir / "run", - "viva_jar": config.TOOLS_ROOT / "third_party/viva/viva.jar", + "viva_jar": config.RESOURCES_ROOT / "third_party/viva/viva.jar", } return True diff --git a/bapctools/skel.py b/bapctools/skel.py index 7c951ecd8..b5e848e9c 100644 --- a/bapctools/skel.py +++ b/bapctools/skel.py @@ -63,7 +63,7 @@ def new_contest() -> None: rights_owner = f"rights_owner: {rights_owner}\n" if rights_owner else "" title = title.replace("_", "-") - skeldir = config.TOOLS_ROOT / "skel/contest" + skeldir = config.RESOURCES_ROOT / "skel/contest" log(f"Copying {skeldir} to {dirname}.") copytree_and_substitute( skeldir, Path(dirname), locals(), exist_ok=False, preserve_symlinks=False @@ -71,7 +71,7 @@ def new_contest() -> None: def get_skel_dir(target_dir: Path) -> tuple[Path, bool]: - skeldir = config.TOOLS_ROOT / "skel/problem" + skeldir = config.RESOURCES_ROOT / "skel/problem" preserve_symlinks = False if (target_dir / "skel/problem").is_dir(): skeldir = target_dir / "skel/problem" @@ -311,12 +311,12 @@ def problem_source_dir(problem: Problem) -> Path: return problem.path.absolute().relative_to(git_root_path) if config.args.latest_bt: - header_yml = (config.TOOLS_ROOT / "skel/gitlab_ci/header_latest_bt.yaml").read_text() + header_yml = (config.RESOURCES_ROOT / "skel/gitlab_ci/header_latest_bt.yaml").read_text() else: - header_yml = (config.TOOLS_ROOT / "skel/gitlab_ci/header_docker_bt.yaml").read_text() + header_yml = (config.RESOURCES_ROOT / "skel/gitlab_ci/header_docker_bt.yaml").read_text() print(header_yml) - contest_yml = (config.TOOLS_ROOT / "skel/gitlab_ci/contest.yaml").read_text() + contest_yml = (config.RESOURCES_ROOT / "skel/gitlab_ci/contest.yaml").read_text() contest_path = Path(".").absolute().relative_to(git_root_path) changes = "".join( f" - {problem_source_dir(problem)}/{pdf_type.path().parent}/**/*\n" @@ -329,7 +329,7 @@ def problem_source_dir(problem: Problem) -> Path: ) ) - problem_yml = (config.TOOLS_ROOT / "skel/gitlab_ci/problem.yaml").read_text() + problem_yml = (config.RESOURCES_ROOT / "skel/gitlab_ci/problem.yaml").read_text() for problem_obj in problems: problem_path = problem_source_dir(problem_obj) problem = problem_obj.name @@ -351,9 +351,9 @@ def create_forgejo_actions(contest: str, problems: list[Problem]) -> None: fatal(".git and ../.git not found after changing to contest directory.") if config.args.latest_bt: - src = config.TOOLS_ROOT / "skel/forgejo_actions_latest_bt" + src = config.RESOURCES_ROOT / "skel/forgejo_actions_latest_bt" else: - src = config.TOOLS_ROOT / "skel/forgejo_actions_docker_bt" + src = config.RESOURCES_ROOT / "skel/forgejo_actions_docker_bt" if config.args.latest_bt: # Copy the 'setup' action: @@ -403,7 +403,7 @@ def create_github_actions(contest: str, problems: list[Problem]) -> None: # Copy the contest-level workflow. contest_workflow_source = ( - config.TOOLS_ROOT / "skel/forgejo_actions_docker_bt/contest.yaml" + config.RESOURCES_ROOT / "skel/forgejo_actions_docker_bt/contest.yaml" ).read_text() contest_workflow = substitute( contest_workflow_source, {"contest": contest, "contest_path": str(contest_path)} @@ -417,7 +417,7 @@ def create_github_actions(contest: str, problems: list[Problem]) -> None: # Copy the problem-level workflows. problem_workflow_source = ( - config.TOOLS_ROOT / "skel/forgejo_actions_docker_bt/problem.yaml" + config.RESOURCES_ROOT / "skel/forgejo_actions_docker_bt/problem.yaml" ).read_text() for problem_obj in problems: problem = problem_obj.name From 7b34caf1041c1c5a61862d0a371f71f3db052ff6 Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 14 Nov 2025 14:19:19 +0100 Subject: [PATCH 08/18] add venv and dist to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index cfd8d14e7..8f9a1687f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ latex/solution*.pdf skel/problem*.zip __pycache__ *.swp +/venv +/dist From 63f11f45c1238de83d560ed0be57e9ceac406947 Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 14 Nov 2025 14:26:27 +0100 Subject: [PATCH 09/18] make all dependencies required --- pyproject.toml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e5bfaefbd..0245ae6a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,14 +6,12 @@ dependencies = [ "colorama", "argcomplete", "python-dateutil", + "ruamel.yaml", + "matplotlib", + "requests", + "questionary", ] -[project.optional-dependencies] -update-yaml = ["ruamel.yaml"] -solve-stats = ["matplotlib"] -domjudge = ["requests"] -new = ["questionary"] - [project.scripts] bt = "bapctools.tools:main" From 07374e1a088d6323f5462f081f71b04eebc9153b Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 14 Nov 2025 14:38:09 +0100 Subject: [PATCH 10/18] Move tools.py to cli.py, add bin/tools.py compatibility script --- bapctools/__main__.py | 7 ++----- bapctools/{tools.py => cli.py} | 0 bin/tools.py | 12 ++++++++++++ pyproject.toml | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) rename bapctools/{tools.py => cli.py} (100%) create mode 100755 bin/tools.py diff --git a/bapctools/__main__.py b/bapctools/__main__.py index f4c4fa520..becb70671 100644 --- a/bapctools/__main__.py +++ b/bapctools/__main__.py @@ -1,7 +1,4 @@ -import sys +from bapctools.cli import main -from bapctools.tools import main - -if __name__ == "__main__": - sys.exit(main()) +main() diff --git a/bapctools/tools.py b/bapctools/cli.py similarity index 100% rename from bapctools/tools.py rename to bapctools/cli.py diff --git a/bin/tools.py b/bin/tools.py new file mode 100755 index 000000000..90ad10955 --- /dev/null +++ b/bin/tools.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +# PYTHON_ARGCOMPLETE_OK + +from pathlib import Path +import sys + +if __name__ == "__main__": + sys.path.append(str(Path(__file__).parent.parent.resolve())) + + from bapctools.cli import main + + main() diff --git a/pyproject.toml b/pyproject.toml index 0245ae6a3..baa5a4985 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ dependencies = [ ] [project.scripts] -bt = "bapctools.tools:main" +bt = "bapctools.cli:main" [build-system] requires = ["hatchling"] From f40ef48283b714db5411f1f8f3b8bfbfd1d8e7c5 Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 14 Nov 2025 14:40:18 +0100 Subject: [PATCH 11/18] Require argcomplete only if not on Windows --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index baa5a4985..3d8889aad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ version = "0.0.0" dependencies = [ "pyyaml", "colorama", - "argcomplete", + "argcomplete; platform_system != 'Windows'", "python-dateutil", "ruamel.yaml", "matplotlib", From 2fa066a100aad87e4fc003a20199c85c96b37850 Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 14 Nov 2025 14:53:19 +0100 Subject: [PATCH 12/18] Update tool configuration in pyproject.toml --- pyproject.toml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 3d8889aad..7ef38e555 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,7 @@ dependencies = [ "requests", "questionary", ] +requires-python = ">=3.10" [project.scripts] bt = "bapctools.cli:main" @@ -20,12 +21,7 @@ requires = ["hatchling"] build-backend = "hatchling.build" [tool.ruff] -# Line length 100 line-length = 100 -# Assume Python 3.10 -target-version = "py310" -# Source -src = ["bin"] [tool.ruff.lint.isort] # organize imports in two blocks, non local and local @@ -35,8 +31,5 @@ default-section = "standard-library" order-by-type = false [tool.pyright] -include = ["bin"] -exclude = ["bin/misc"] -executionEnvironments = [ - { root = "bin" }, -] +include = ["bapctools"] +exclude = ["bapctools/resources"] From 0478a490265ec4f6ce4365417f0ce05a53d9078c Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 14 Nov 2025 15:07:21 +0100 Subject: [PATCH 13/18] update pre-commit config exclude --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e6ee15546..109dc18af 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,4 +36,4 @@ repos: - --python-version=3.10 - --scripts-are-modules - --strict - exclude: ^(test|skel|scripts|bin/misc)/ + exclude: ^(test|scripts|bin/misc|bapctools/resources)/ From 0a16250f42f1020cd6e5903e5c465ab3bc77039f Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 14 Nov 2025 15:12:08 +0100 Subject: [PATCH 14/18] Run ruff isort --- bapctools/__main__.py | 1 - bapctools/check_testing_tool.py | 3 +-- bapctools/cli.py | 30 ++++++++++++++++-------------- bapctools/constraints.py | 3 +-- bapctools/download_submissions.py | 3 +-- bapctools/fuzz.py | 5 +---- bapctools/generate.py | 7 +------ bapctools/interactive.py | 3 +-- bapctools/parallel.py | 3 +-- bapctools/problem.py | 22 ++++++++++++---------- bapctools/run.py | 8 +------- bapctools/skel.py | 4 +--- bapctools/solve_stats.py | 3 +-- bapctools/stats.py | 6 +----- bapctools/testcase.py | 6 ++---- bapctools/upgrade.py | 3 +-- bapctools/validate.py | 6 ++---- bapctools/verdicts.py | 3 +-- bin/tools.py | 2 +- 19 files changed, 46 insertions(+), 75 deletions(-) diff --git a/bapctools/__main__.py b/bapctools/__main__.py index becb70671..b49cd3768 100644 --- a/bapctools/__main__.py +++ b/bapctools/__main__.py @@ -1,4 +1,3 @@ from bapctools.cli import main - main() diff --git a/bapctools/check_testing_tool.py b/bapctools/check_testing_tool.py index b57fb2036..1a69d3c2b 100644 --- a/bapctools/check_testing_tool.py +++ b/bapctools/check_testing_tool.py @@ -4,8 +4,7 @@ from pathlib import Path from typing import Optional, TYPE_CHECKING -from bapctools import config -from bapctools import parallel +from bapctools import config, parallel from bapctools.program import Program from bapctools.run import Submission from bapctools.util import ( diff --git a/bapctools/cli.py b/bapctools/cli.py index db2cf193a..f2d4dccef 100755 --- a/bapctools/cli.py +++ b/bapctools/cli.py @@ -29,20 +29,22 @@ from typing import Any, Optional # Local imports -from bapctools import config -from bapctools import constraints -from bapctools import contest -from bapctools import download_submissions -from bapctools import export -from bapctools import fuzz -from bapctools import generate -from bapctools import latex -from bapctools import skel -from bapctools import slack -from bapctools import solve_stats -from bapctools import stats -from bapctools import upgrade -from bapctools import validate +from bapctools import ( + config, + constraints, + contest, + download_submissions, + export, + fuzz, + generate, + latex, + skel, + slack, + solve_stats, + stats, + upgrade, + validate, +) from bapctools.contest import call_api_get_json, contest_yaml, get_contest_id, problems_yaml from bapctools.problem import Problem from bapctools.util import ( diff --git a/bapctools/constraints.py b/bapctools/constraints.py index 9cc73d65f..d5ac9dc35 100644 --- a/bapctools/constraints.py +++ b/bapctools/constraints.py @@ -3,8 +3,7 @@ from colorama import Fore, Style from typing import Optional -from bapctools import latex -from bapctools import validate +from bapctools import latex, validate from bapctools.problem import Problem from bapctools.util import eprint, error, log, warn diff --git a/bapctools/download_submissions.py b/bapctools/download_submissions.py index ce7f44040..ecb607140 100644 --- a/bapctools/download_submissions.py +++ b/bapctools/download_submissions.py @@ -5,8 +5,7 @@ from pathlib import Path from typing import Any -from bapctools import config -from bapctools import parallel +from bapctools import config, parallel from bapctools.contest import call_api_get_json, get_contest_id from bapctools.util import fatal, ProgressBar from bapctools.verdicts import from_string, Verdict diff --git a/bapctools/fuzz.py b/bapctools/fuzz.py index d5ebfdc1c..d8e7fea77 100644 --- a/bapctools/fuzz.py +++ b/bapctools/fuzz.py @@ -7,10 +7,7 @@ from pathlib import Path from typing import Any, Optional -from bapctools import config -from bapctools import generate -from bapctools import parallel -from bapctools import problem +from bapctools import config, generate, parallel, problem from bapctools.run import Run, Submission from bapctools.testcase import Testcase from bapctools.util import ( diff --git a/bapctools/generate.py b/bapctools/generate.py index ecaa4e1d3..78a5bb27e 100644 --- a/bapctools/generate.py +++ b/bapctools/generate.py @@ -11,12 +11,7 @@ from pathlib import Path, PurePosixPath from typing import cast, Final, Literal, Optional, overload, TypeVar -from bapctools import config -from bapctools import parallel -from bapctools import program -from bapctools import run -from bapctools import validate -from bapctools import visualize +from bapctools import config, parallel, program, run, validate, visualize from bapctools.problem import Problem from bapctools.testcase import Testcase from bapctools.util import ( diff --git a/bapctools/interactive.py b/bapctools/interactive.py index a20d9e7a3..4ef9aacce 100644 --- a/bapctools/interactive.py +++ b/bapctools/interactive.py @@ -9,8 +9,7 @@ from pathlib import Path from typing import Any, Final, IO, Literal, Optional, TYPE_CHECKING -from bapctools import config -from bapctools import validate +from bapctools import config, validate from bapctools.util import ( eprint, error, diff --git a/bapctools/parallel.py b/bapctools/parallel.py index 0d49b0e6d..bd23a385f 100644 --- a/bapctools/parallel.py +++ b/bapctools/parallel.py @@ -5,8 +5,7 @@ from collections.abc import Callable, Sequence from typing import Any, Generic, Literal, Optional, TypeVar -from bapctools import config -from bapctools import util +from bapctools import config, util T = TypeVar("T") diff --git a/bapctools/problem.py b/bapctools/problem.py index 282291230..874651f74 100644 --- a/bapctools/problem.py +++ b/bapctools/problem.py @@ -12,16 +12,18 @@ import math -from bapctools import check_testing_tool -from bapctools import config -from bapctools import latex -from bapctools import parallel -from bapctools import run -from bapctools import testcase -from bapctools import validate -from bapctools import validator_tests -from bapctools import verdicts -from bapctools import visualize +from bapctools import ( + check_testing_tool, + config, + latex, + parallel, + run, + testcase, + validate, + validator_tests, + verdicts, + visualize, +) from bapctools.util import ( BAR_TYPE, combine_hashes_dict, diff --git a/bapctools/run.py b/bapctools/run.py index aa4e90d45..c5b5718d1 100644 --- a/bapctools/run.py +++ b/bapctools/run.py @@ -8,13 +8,7 @@ from pathlib import Path from typing import Optional -from bapctools import config -from bapctools import interactive -from bapctools import parallel -from bapctools import problem -from bapctools import program -from bapctools import validate -from bapctools import visualize +from bapctools import config, interactive, parallel, problem, program, validate, visualize from bapctools.testcase import Testcase from bapctools.util import ( BAR_TYPE, diff --git a/bapctools/skel.py b/bapctools/skel.py index b5e848e9c..f65f12254 100644 --- a/bapctools/skel.py +++ b/bapctools/skel.py @@ -5,9 +5,7 @@ from pathlib import Path # Local imports -from bapctools import config -from bapctools import contest -from bapctools import latex +from bapctools import config, contest, latex from bapctools.problem import Problem from bapctools.util import ( ask_variable_bool, diff --git a/bapctools/solve_stats.py b/bapctools/solve_stats.py index 6ee30f4fd..437148a1f 100644 --- a/bapctools/solve_stats.py +++ b/bapctools/solve_stats.py @@ -3,8 +3,7 @@ from pathlib import Path from typing import Any, Optional -from bapctools import config -from bapctools import parallel +from bapctools import config, parallel from bapctools.contest import call_api_get_json, get_contest_id from bapctools.util import ProgressBar diff --git a/bapctools/stats.py b/bapctools/stats.py index 181d3c5e1..50f467635 100644 --- a/bapctools/stats.py +++ b/bapctools/stats.py @@ -6,11 +6,7 @@ from pathlib import Path from typing import Any, cast, Literal, Optional -from bapctools import config -from bapctools import generate -from bapctools import latex -from bapctools import program -from bapctools import validate +from bapctools import config, generate, latex, program, validate from bapctools.problem import Problem from bapctools.util import eprint, error, glob, log, ShellCommand, warn diff --git a/bapctools/testcase.py b/bapctools/testcase.py index c55947b01..08db17a94 100644 --- a/bapctools/testcase.py +++ b/bapctools/testcase.py @@ -5,8 +5,7 @@ from pathlib import Path from typing import Optional, TYPE_CHECKING -from bapctools import config -from bapctools import validate +from bapctools import config, validate from bapctools.util import ( BAR_TYPE, combine_hashes_dict, @@ -18,8 +17,7 @@ ) if TYPE_CHECKING: # Prevent circular import: https://stackoverflow.com/a/39757388 - from bapctools import problem - from bapctools import visualize + from bapctools import problem, visualize # TODO #102: Consistently separate the compound noun "test case", e.g. "TestCase" or "test_case" diff --git a/bapctools/upgrade.py b/bapctools/upgrade.py index 2fdb71dfc..53055c8bb 100644 --- a/bapctools/upgrade.py +++ b/bapctools/upgrade.py @@ -6,8 +6,7 @@ from pathlib import Path from typing import Any, cast, Optional -from bapctools import config -from bapctools import generate +from bapctools import config, generate from bapctools.util import ( fatal, has_ryaml, diff --git a/bapctools/validate.py b/bapctools/validate.py index 892d92984..241158765 100644 --- a/bapctools/validate.py +++ b/bapctools/validate.py @@ -4,13 +4,11 @@ from pathlib import Path from typing import Any, Final, Optional, TYPE_CHECKING -from bapctools import config -from bapctools import program +from bapctools import config, program from bapctools.util import ExecResult, ExecStatus, fatal, ProgressBar, validator_exec_code_map if TYPE_CHECKING: # Prevent circular import: https://stackoverflow.com/a/39757388 - from bapctools import run - from bapctools import testcase + from bapctools import run, testcase from bapctools.problem import Problem diff --git a/bapctools/verdicts.py b/bapctools/verdicts.py index c240d8f45..150c411c1 100644 --- a/bapctools/verdicts.py +++ b/bapctools/verdicts.py @@ -8,8 +8,7 @@ from pathlib import Path from typing import Any, Literal, Optional, TYPE_CHECKING -from bapctools import config -from bapctools import testcase +from bapctools import config, testcase from bapctools.util import eprint, ITEM_TYPE, ProgressBar if TYPE_CHECKING: diff --git a/bin/tools.py b/bin/tools.py index 90ad10955..a05e8cc43 100755 --- a/bin/tools.py +++ b/bin/tools.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 # PYTHON_ARGCOMPLETE_OK -from pathlib import Path import sys +from pathlib import Path if __name__ == "__main__": sys.path.append(str(Path(__file__).parent.parent.resolve())) From 84691e6dee4871f60684aa397eacf9fb879f4173 Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 14 Nov 2025 16:02:38 +0100 Subject: [PATCH 15/18] add dev group containing just pytest for convenience --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 7ef38e555..45dc38e28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,11 @@ dependencies = [ ] requires-python = ">=3.10" +[project.optional-dependencies] +dev = [ + "pytest", +] + [project.scripts] bt = "bapctools.cli:main" From 0ddb8221cc30ccc5367d2dc8f8c9ad2623ca0df4 Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Fri, 14 Nov 2025 16:03:08 +0100 Subject: [PATCH 16/18] fix most (all?) tests --- .../input_validator/validation.h | 2 +- .../input_validator/validation.h | 2 +- .../constants/output_validator/validation.h | 2 +- .../answer_validator/validation.h | 2 +- .../input_validator/validation.h | 2 +- test/test_default_output_validator.py | 8 ++------ test/test_generators_yaml.py | 3 +-- test/test_problem_yaml.py | 5 ++--- test/test_problems.py | 17 +++++++++++------ test/test_verdicts.py | 2 +- 10 files changed, 22 insertions(+), 23 deletions(-) diff --git a/test/problems/alternativeencryption/input_validators/input_validator/validation.h b/test/problems/alternativeencryption/input_validators/input_validator/validation.h index 2b74c5d6a..09e9d67ee 120000 --- a/test/problems/alternativeencryption/input_validators/input_validator/validation.h +++ b/test/problems/alternativeencryption/input_validators/input_validator/validation.h @@ -1 +1 @@ -../../../../../headers/validation.h \ No newline at end of file +../../../../../bapctools/resources/headers/validation.h \ No newline at end of file diff --git a/test/problems/constants/input_validators/input_validator/validation.h b/test/problems/constants/input_validators/input_validator/validation.h index 2b74c5d6a..09e9d67ee 120000 --- a/test/problems/constants/input_validators/input_validator/validation.h +++ b/test/problems/constants/input_validators/input_validator/validation.h @@ -1 +1 @@ -../../../../../headers/validation.h \ No newline at end of file +../../../../../bapctools/resources/headers/validation.h \ No newline at end of file diff --git a/test/problems/constants/output_validator/validation.h b/test/problems/constants/output_validator/validation.h index 9a928c978..c24a808b4 120000 --- a/test/problems/constants/output_validator/validation.h +++ b/test/problems/constants/output_validator/validation.h @@ -1 +1 @@ -../../../../headers/validation.h \ No newline at end of file +../../../../bapctools/resources/headers/validation.h \ No newline at end of file diff --git a/test/problems/identity/answer_validators/answer_validator/validation.h b/test/problems/identity/answer_validators/answer_validator/validation.h index 2b74c5d6a..09e9d67ee 120000 --- a/test/problems/identity/answer_validators/answer_validator/validation.h +++ b/test/problems/identity/answer_validators/answer_validator/validation.h @@ -1 +1 @@ -../../../../../headers/validation.h \ No newline at end of file +../../../../../bapctools/resources/headers/validation.h \ No newline at end of file diff --git a/test/problems/identity/input_validators/input_validator/validation.h b/test/problems/identity/input_validators/input_validator/validation.h index 2b74c5d6a..09e9d67ee 120000 --- a/test/problems/identity/input_validators/input_validator/validation.h +++ b/test/problems/identity/input_validators/input_validator/validation.h @@ -1 +1 @@ -../../../../../headers/validation.h \ No newline at end of file +../../../../../bapctools/resources/headers/validation.h \ No newline at end of file diff --git a/test/test_default_output_validator.py b/test/test_default_output_validator.py index ed47bdbea..fb9bba51b 100644 --- a/test/test_default_output_validator.py +++ b/test/test_default_output_validator.py @@ -5,11 +5,7 @@ import tempfile from pathlib import Path -import problem -import testcase -import validate -import util -import config +from bapctools import problem, testcase, validate, util, config RUN_DIR = Path.cwd().absolute() # Note: the python version isn't tested by default, because it's quite slow. @@ -49,7 +45,7 @@ def validator(request): tmpdir = Path(tempfile.gettempdir()) / ("bapctools_" + h) tmpdir.mkdir(exist_ok=True) p = problem.Problem(Path("."), tmpdir) - validator = validate.OutputValidator(p, RUN_DIR / "support" / request.param) + validator = validate.OutputValidator(p, config.RESOURCES_ROOT / "support" / request.param) print(util.ProgressBar.current_bar) bar = util.ProgressBar("build", max_len=1) validator.build(bar) diff --git a/test/test_generators_yaml.py b/test/test_generators_yaml.py index 6b7be3a62..ef97b3f0e 100644 --- a/test/test_generators_yaml.py +++ b/test/test_generators_yaml.py @@ -3,8 +3,7 @@ import yaml from pathlib import Path -import generate -import config +from bapctools import generate, config config.RUNNING_TEST = True diff --git a/test/test_problem_yaml.py b/test/test_problem_yaml.py index 31da03e99..bf955a3ca 100644 --- a/test/test_problem_yaml.py +++ b/test/test_problem_yaml.py @@ -4,8 +4,7 @@ from typing import cast, Any from unittest.mock import call, MagicMock -import config -import problem +from bapctools import config, problem RUN_DIR = Path.cwd().absolute() @@ -72,7 +71,7 @@ def test_invalid(self, monkeypatch, test_data): fatal = MagicMock(name="fatal", side_effect=SystemExit(-42)) error = MagicMock(name="error") warn = MagicMock(name="warn") - for module in ["problem", "util"]: + for module in ["bapctools.problem", "bapctools.util"]: monkeypatch.setattr(f"{module}.fatal", fatal) monkeypatch.setattr(f"{module}.error", error) monkeypatch.setattr(f"{module}.warn", warn) diff --git a/test/test_problems.py b/test/test_problems.py index c7108fc34..ac35d9b73 100644 --- a/test/test_problems.py +++ b/test/test_problems.py @@ -4,10 +4,7 @@ from pathlib import Path from zipfile import ZipFile -import tools -import problem -import config -import util +from bapctools import cli as tools, problem, config, util # Run `bt run` on these problems. PROBLEMS = [ @@ -195,7 +192,11 @@ def test_zip(self): info.filename for info in ZipFile(zip_path).infolist() if info.filename.endswith(".pdf") ) == [ f"identity/{path}.{lang}.pdf" - for path in ["problem_slide/problem-slide", "solution/solution", "statement/problem"] + for path in [ + "problem_slide/problem-slide", + "solution/solution", + "statement/problem", + ] for lang in ["de", "en"] ], "Zip contents for PDFs with both languages are not correct" @@ -206,7 +207,11 @@ def test_zip(self): info.filename for info in ZipFile(zip_path).infolist() if info.filename.endswith(".pdf") ) == [ f"identity/{path}.en.pdf" - for path in ["problem_slide/problem-slide", "solution/solution", "statement/problem"] + for path in [ + "problem_slide/problem-slide", + "solution/solution", + "statement/problem", + ] ], "Zip contents for PDFs with `--lang en` are not correct" zip_path.unlink() diff --git a/test/test_verdicts.py b/test/test_verdicts.py index f81aff3c0..5f4eccf3a 100644 --- a/test/test_verdicts.py +++ b/test/test_verdicts.py @@ -1,4 +1,4 @@ -import verdicts +from bapctools import verdicts class MockTestcase: From 8a6cf640976f21971eb96fd13c0ad97838ddf85d Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Mon, 17 Nov 2025 14:19:36 +0100 Subject: [PATCH 17/18] fix bin/tools.py resolution --- bin/tools.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bin/tools.py b/bin/tools.py index a05e8cc43..06deda4ce 100755 --- a/bin/tools.py +++ b/bin/tools.py @@ -5,7 +5,10 @@ from pathlib import Path if __name__ == "__main__": - sys.path.append(str(Path(__file__).parent.parent.resolve())) + # Add repository root to python path so that bapctools is importable. Notably, we need to + # resolve __file__ as it would otherwise refer to the location of the symlink used to invoke + # this script. + sys.path.append(str(Path(__file__).resolve().parents[1])) from bapctools.cli import main From d19c37251ff323863513bd9a83b40be7a52b5a76 Mon Sep 17 00:00:00 2001 From: Niklas Mohrin Date: Mon, 17 Nov 2025 14:26:57 +0100 Subject: [PATCH 18/18] add current dependency versions to pyproject.toml --- pyproject.toml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 45dc38e28..b8a1c5a90 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,20 +2,20 @@ name = "bapctools" version = "0.0.0" dependencies = [ - "pyyaml", - "colorama", - "argcomplete; platform_system != 'Windows'", - "python-dateutil", - "ruamel.yaml", - "matplotlib", - "requests", - "questionary", + "pyyaml~=6.0.3", + "colorama~=0.4.6", + "argcomplete~=3.6.3; platform_system != 'Windows'", + "python-dateutil~=2.9.0", + "ruamel.yaml~=0.18.16", + "matplotlib~=3.10.7", + "requests~=2.32.5", + "questionary~=2.1.1", ] requires-python = ">=3.10" [project.optional-dependencies] dev = [ - "pytest", + "pytest~=9.0.1", ] [project.scripts]