[build-system] requires = ["setuptools>=61"] build-backend = "setuptools.build_meta" [project] name = "code_runner" version = "0.1.0" description = "Run a learner solution (Python, JavaScript, TypeScript) against a problem's examples in a bubblewrap sandbox" requires-python = ">=3.14" # Stdlib only on purpose: the tutor server imports it from the system python. dependencies = [] [tool.setuptools.packages.find] include = ["code_runner*"] [tool.setuptools.package-data] code_runner = ["py.typed", "harness/*"] [tool.ruff] target-version = "py314" include = ["*.py", "**/*.py"] exclude = [".git", ".venv", "__pycache__", "build", "dist", ".eggs"] [tool.ruff.lint] select = ["ALL"] ignore = [ "CPY001", "D100", "D101", "D102", "D103", "D104", "D105", "D107", "D203", "D213", "COM812", "ISC001", "S603", ] fixable = ["ALL"] unfixable = [] [tool.ruff.lint.per-file-ignores] "scripts/*.py" = ["INP001"] # The sandbox-side harness is a program of its own: it reads its job from files. # The sandbox mounts its own private tmpfs at /tmp. "**/_sandbox.py" = ["S108"] "**/harness/*.py" = ["INP001", "T201", "S102", "BLE001", "PTH123"] # Tests embed the learner programs they run as source strings. "**/tests/**/*.py" = ["ARG", "D", "E501", "PLC0415", "PLR2004", "S101", "SLF001"] "**/test_*.py" = ["ARG", "D", "E501", "PLC0415", "PLR2004", "S101", "SLF001"] [tool.ruff.lint.pylint] max-args = 6 [tool.ruff.lint.pydocstyle] convention = "google" [tool.ruff.lint.isort] force-single-line = false force-sort-within-sections = true known-first-party = ["code_runner"] [tool.ruff.lint.flake8-quotes] docstring-quotes = "double" inline-quotes = "double" [tool.ruff.lint.flake8-tidy-imports] ban-relative-imports = "all" [tool.ruff.format] quote-style = "double" indent-style = "space" skip-magic-trailing-comma = false line-ending = "auto" docstring-code-format = true [tool.mypy] python_version = "3.14" strict = true warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true disallow_incomplete_defs = true check_untyped_defs = true disallow_untyped_decorators = true no_implicit_optional = true warn_redundant_casts = true warn_unused_ignores = true warn_no_return = true warn_unreachable = true disallow_any_unimported = true disallow_any_explicit = false disallow_any_generics = true disallow_subclassing_any = true strict_equality = true extra_checks = true ignore_missing_imports = true show_error_codes = true color_output = true exclude = [".venv/"] [tool.pylint.main] # The repo root on the path, so pylint resolves the package's own imports the # same way pytest does instead of reporting import-error for them. init-hook = "import sys; sys.path.insert(0, 'code_runner')" analyse-fallback-blocks = true persistent = true jobs = 0 py-version = "3.14" ignore = [".venv", "__pycache__"] # Test suites intentionally use patterns (protected-access, fixture-arg # shadowing) that don't apply to source code; ruff's per-file-ignores carry # the equivalent exemptions, so tests stay linted -- by ruff. This is the same # source-only pylint scope wake-alarm / diet-guard / testsAndMisc use. It has # to be `ignore-paths`, not `ignore = ["tests"]`: `ignore` only matches the # basename of what pylint is *given*, and pre-commit hands it explicit file # paths, so a directory name in `ignore` never fires under the hook. # harness/ runs inside the sandbox and must exec the solution it is given. ignore-paths = ["^(.*/)?tests/.*$", "^(.*/)?harness/.*$"] ignore-patterns = [".*\\.pyi$"] [tool.pylint.messages_control] enable = "all" disable = [ "missing-module-docstring", "missing-class-docstring", "missing-function-docstring", "use-implicit-booleaness-not-comparison-to-zero", ] [tool.pylint.design] max-args = 6 min-public-methods = 0 max-module-lines = 1000 max-attributes = 10 [tool.pylint.typecheck] generated-members = [ ".*\\.assert_called_once_with", ".*\\.assert_called_once", ".*\\.assert_called", ".*\\.assert_not_called", ".*\\.assert_any_call", ".*\\.call_args", ".*\\.call_args_list", ".*\\.call_count", ] [tool.bandit] exclude_dirs = ["tests", ".venv"] [tool.pytest.ini_options] markers = ["sandbox: needs bubblewrap with unprivileged user namespaces (skipped otherwise)"] testpaths = ["code_runner/tests"] python_files = ["test_*.py", "*_test.py"] python_classes = ["Test*"] python_functions = ["test_*"] addopts = [ "-v", "--strict-markers", "--strict-config", "-ra", "--cov=code_runner", "--cov-branch", "--cov-report=term-missing", "--cov-report=lcov", ] filterwarnings = [ "error", "ignore::DeprecationWarning", "default::pytest.PytestUnraisableExceptionWarning", ] [tool.coverage.run] source = ["code_runner"] branch = true omit = [ # Runs inside the sandbox in another process; behaviour is tested through run(). "*/harness/*", "*/__pycache__/*", "*/tests/*", "*/.venv/*", ] [tool.coverage.report] fail_under = 100 show_missing = true skip_covered = false exclude_lines = [ "pragma: no cover", "raise NotImplementedError", "raise AssertionError", "if TYPE_CHECKING:", 'if __name__ == "__main__":', ] partial_branches = ["pragma: no branch"]