[project] name = "jobd" version = "0.5.46" description = "Self-hostable GPU-aware job broker for your own machines, with native MCP/agent integration" readme = "README.md" license = { text = "MIT" } authors = [{ name = "Jaret Arnold" }] keywords = ["job-queue", "gpu", "scheduler", "mcp", "self-hosted", "homelab"] requires-python = ">=3.11" classifiers = [ "Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Operating System :: POSIX :: Linux", "Operating System :: MacOS", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: System :: Distributed Computing", "Topic :: Scientific/Engineering", ] dependencies = [ "fastapi>=0.110", "starlette>=1.0.1", "uvicorn[standard]>=0.27", "sqlalchemy>=2.0", "pydantic>=2.6", "pyyaml>=6.0", "typer>=0.9", "httpx>=0.26", "sse-starlette>=2.0", "prometheus-client>=0.20", ] [project.urls] Homepage = "https://github.com/musharna/jobd" Repository = "https://github.com/musharna/jobd" Issues = "https://github.com/musharna/jobd/issues" Changelog = "https://github.com/musharna/jobd/blob/main/CHANGELOG.md" [project.optional-dependencies] worker = [ "httpx>=0.26", "psutil>=5.9", "nvidia-ml-py>=12", "pyyaml>=6.0", ] dev = [ "pytest>=8.0", "pytest-asyncio>=0.23", "pytest-cov>=4.1", "pytest-timeout>=2.2", "respx>=0.20", "ruff>=0.3", "mypy>=1.8", "types-PyYAML", "hypothesis>=6.0", ] mcp = ["mcp>=2,<3", "httpx>=0.27"] [project.scripts] jobd = "jobd.main:run" job = "job_cli.cli:main" jobd-mcp = "jobd.mcp.server:main" jobd-worker = "jobd.worker.job_worker:main" [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.hatch.build.targets.wheel] packages = ["src/jobd", "src/job_cli"] # `job fleet add` pushes these over ssh at runtime; the canonical copies stay # in scripts/ (where the deploy-lints and CD tests read them), the wheel gets # copies. tests/test_fleet_cli.py builds a real wheel and asserts they landed. [tool.hatch.build.targets.wheel.force-include] "scripts/install-worker.sh" = "jobd/fleet_assets/install-worker.sh" "scripts/update-worker.sh" = "jobd/fleet_assets/update-worker.sh" [tool.pytest.ini_options] testpaths = ["tests"] asyncio_mode = "auto" # Bound any single hung test (a stuck subprocess/socket/watchdog) so CI fails # in minutes instead of stalling to GitHub's 6h job kill. Generous vs the # slowest real test (~50s: the meta-test that reruns the suite as a subprocess). # Per-test override: @pytest.mark.timeout(N). timeout = 300 markers = [ "live: real-execution/live-broker suite behind an explicit opt-in env (RUN_LIVE_JOBD / JOBD_LIVE / JOBD_E2E); structurally deselected in CI via -m 'not live'", ] [tool.mypy] python_version = "3.11" ignore_missing_imports = true warn_unused_ignores = true # Check the bodies of un-annotated functions too. Without this, mypy silently # skips the bodies of every def lacking a full signature — which is most of the # broker/worker hot path (app.py, job_worker.py) — making the "mypy green" gate # vacuous there (audit Quality-4, 2026-07-01). The codebase already passes with # it on; test_deploy_lint.py::test_mypy_checks_untyped_defs keeps it enabled. check_untyped_defs = true [tool.ruff] line-length = 100 target-version = "py311" [tool.ruff.lint] # E/F (default) plus import-sorting, bugbear, pyupgrade, and simplify. # E501 (line-too-long) is intentionally left to the formatter — `ruff format` # wraps code; E501 would otherwise only nag about long strings/comments/URLs. select = ["E", "F", "I", "B", "UP", "SIM"] ignore = ["E501"] [tool.ruff.lint.flake8-bugbear] # Typer requires typer.Argument()/typer.Option() as argument defaults — these # are not the mutable-default antipattern B008 targets. extend-immutable-calls = ["typer.Argument", "typer.Option"] [tool.ruff.lint.per-file-ignores] # Nested `with patch.object(...)` is a readable, idiomatic test pattern. "tests/**" = ["SIM117"] [tool.coverage.report] # Branch-coverage floor enforced by ci.yml's coverage leg (the workflow flag # must agree; tests/test_deploy_lint.py checks). Measured 86.8% on 2026-09-02. fail_under = 83