From ca0ca5dec4710fd3922881e216a786ee83b7ded4 Mon Sep 17 00:00:00 2001 From: Lars Kellogg-Stedman Date: Thu, 30 Oct 2025 23:14:59 -0400 Subject: [PATCH] Reorganize project following standard Python conventions This commit relocates all of the Python source files into the `batchtools` package. This, along with appropriate modifications to `pyproject.toml`, mean that: - We now have an installable package. From within this directory, you can run `pip install .` to install the package on the local system. You can also install the package straight from the GitHub repository: pip install git+https://github.com/memalhot/python-batchtools - We install a runnable `batchtools` script. By setting `project.scripts` in `pyproject.toml`, we install a `batchtools` command when we install the package. This permits you to run the code by simply typing `batchtools` at the command line, just like any other command. You can run the script without installing it using the `uv run` command: uv run batchtools - We have a distributable package. Running `uv build` will produce a distributable Python package (both a `.tar.gz` file and a Python wheel [1]) in the `dist/` directory. This can be uploaded to PyPi using `uv publish`, or simply copied and installed somewhere using `pip`. [1]: https://pythonwheels.com/ --- batchtools/__init__.py | 0 basecommand.py => batchtools/basecommand.py | 0 batchtools.py => batchtools/batchtools.py | 20 +++++++-------- bd.py => batchtools/bd.py | 6 ++--- bj.py => batchtools/bj.py | 2 +- bl.py => batchtools/bl.py | 6 ++--- bp.py => batchtools/bp.py | 4 +-- bps.py => batchtools/bps.py | 4 +-- bq.py => batchtools/bq.py | 2 +- br.py => batchtools/br.py | 12 ++++----- build_yaml.py => batchtools/build_yaml.py | 0 file_setup.py => batchtools/file_setup.py | 0 helpers.py => batchtools/helpers.py | 0 pyproject.toml | 28 +++++++++++++++------ requirements.txt | 2 -- tests/test_batchtools.py | 2 +- tests/test_bd.py | 2 +- tests/test_bj.py | 2 +- tests/test_bl.py | 2 +- tests/test_bp.py | 2 +- tests/test_bps.py | 2 +- tests/test_br.py | 6 ++--- uv.lock | 2 +- 23 files changed, 59 insertions(+), 47 deletions(-) create mode 100644 batchtools/__init__.py rename basecommand.py => batchtools/basecommand.py (100%) rename batchtools.py => batchtools/batchtools.py (83%) rename bd.py => batchtools/bd.py (96%) rename bj.py => batchtools/bj.py (97%) rename bl.py => batchtools/bl.py (94%) rename bp.py => batchtools/bp.py (96%) rename bps.py => batchtools/bps.py (98%) rename bq.py => batchtools/bq.py (98%) rename br.py => batchtools/br.py (97%) rename build_yaml.py => batchtools/build_yaml.py (100%) rename file_setup.py => batchtools/file_setup.py (100%) rename helpers.py => batchtools/helpers.py (100%) delete mode 100644 requirements.txt diff --git a/batchtools/__init__.py b/batchtools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/basecommand.py b/batchtools/basecommand.py similarity index 100% rename from basecommand.py rename to batchtools/basecommand.py diff --git a/batchtools.py b/batchtools/batchtools.py similarity index 83% rename from batchtools.py rename to batchtools/batchtools.py index af5e133..dd24804 100644 --- a/batchtools.py +++ b/batchtools/batchtools.py @@ -1,16 +1,16 @@ import argparse import sys -from basecommand import Command -from basecommand import SubParserFactory -from bj import ListJobsCommand -from bd import DeleteJobsCommand -from bl import LogsCommand -from bp import PrintJobsCommand -from bq import GpuQueuesCommand -from br import CreateJobCommand -from bps import ListPodsCommand -from helpers import is_logged_in +from .basecommand import Command +from .basecommand import SubParserFactory +from .bj import ListJobsCommand +from .bd import DeleteJobsCommand +from .bl import LogsCommand +from .bp import PrintJobsCommand +from .bq import GpuQueuesCommand +from .br import CreateJobCommand +from .bps import ListPodsCommand +from .helpers import is_logged_in class BatchTools: diff --git a/bd.py b/batchtools/bd.py similarity index 96% rename from bd.py rename to batchtools/bd.py index 95d6fed..145f86a 100644 --- a/bd.py +++ b/batchtools/bd.py @@ -6,9 +6,9 @@ import openshift_client as oc -from basecommand import Command -from basecommand import SubParserFactory -from helpers import oc_delete +from .basecommand import Command +from .basecommand import SubParserFactory +from .helpers import oc_delete class DeleteJobsCommandArgs(argparse.Namespace): diff --git a/bj.py b/batchtools/bj.py similarity index 97% rename from bj.py rename to batchtools/bj.py index 328e393..a28d474 100644 --- a/bj.py +++ b/batchtools/bj.py @@ -4,7 +4,7 @@ import sys import openshift_client as oc -from basecommand import Command +from .basecommand import Command class ListJobsCommand(Command): diff --git a/bl.py b/batchtools/bl.py similarity index 94% rename from bl.py rename to batchtools/bl.py index 352c48e..1a72e02 100644 --- a/bl.py +++ b/batchtools/bl.py @@ -5,9 +5,9 @@ import argparse import openshift_client as oc -from basecommand import Command -from basecommand import SubParserFactory -from helpers import pretty_print +from .basecommand import Command +from .basecommand import SubParserFactory +from .helpers import pretty_print class LogsCommandArgs(argparse.Namespace): diff --git a/bp.py b/batchtools/bp.py similarity index 96% rename from bp.py rename to batchtools/bp.py index a158fbb..960b330 100644 --- a/bp.py +++ b/batchtools/bp.py @@ -6,8 +6,8 @@ import openshift_client as oc -from basecommand import Command -from basecommand import SubParserFactory +from .basecommand import Command +from .basecommand import SubParserFactory class PrintJobsCommandArgs(argparse.Namespace): diff --git a/bps.py b/batchtools/bps.py similarity index 98% rename from bps.py rename to batchtools/bps.py index 63a6804..e16e6a9 100644 --- a/bps.py +++ b/batchtools/bps.py @@ -6,8 +6,8 @@ import argparse import openshift_client as oc -from basecommand import Command -from basecommand import SubParserFactory +from .basecommand import Command +from .basecommand import SubParserFactory class ListPodsCommandArgs(argparse.Namespace): diff --git a/bq.py b/batchtools/bq.py similarity index 98% rename from bq.py rename to batchtools/bq.py index 8c65287..0d51217 100644 --- a/bq.py +++ b/batchtools/bq.py @@ -5,7 +5,7 @@ import openshift_client as oc -from basecommand import Command +from .basecommand import Command class GpuQueuesCommand(Command): diff --git a/br.py b/batchtools/br.py similarity index 97% rename from br.py rename to batchtools/br.py index 60392ea..7edf5a9 100644 --- a/br.py +++ b/batchtools/br.py @@ -11,12 +11,12 @@ import openshift_client as oc -from basecommand import Command -from basecommand import SubParserFactory -from build_yaml import build_job_body -from helpers import pretty_print -from helpers import oc_delete -from file_setup import prepare_context +from .basecommand import Command +from .basecommand import SubParserFactory +from .build_yaml import build_job_body +from .helpers import pretty_print +from .helpers import oc_delete +from .file_setup import prepare_context class CreateJobCommandArgs(argparse.Namespace): diff --git a/build_yaml.py b/batchtools/build_yaml.py similarity index 100% rename from build_yaml.py rename to batchtools/build_yaml.py diff --git a/file_setup.py b/batchtools/file_setup.py similarity index 100% rename from file_setup.py rename to batchtools/file_setup.py diff --git a/helpers.py b/batchtools/helpers.py similarity index 100% rename from helpers.py rename to batchtools/helpers.py diff --git a/pyproject.toml b/pyproject.toml index b4bc539..7369a1d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,20 +1,26 @@ +[build-system] +requires = ["setuptools>=42"] +build-backend = "setuptools.build_meta" + [project] name = "python-batchtools" version = "0.1.0" description = "Add your description here" readme = "README.md" -requires-python = ">=3.10" -dependencies = [ - "kubernetes-typed>=18.20.2", - "openshift-client>=2.0.5", - "typing-extensions>=4.15.0", -] +requires-python = ">=3.11" +dependencies = ["openshift-client>=2.0.5", "typing-extensions>=4.15.0"] + +[project.scripts] +# When we install the project, this causes the installer to create a +# "batchtools" script that will run our main function. +batchtools = "batchtools.batchtools:main" [dependency-groups] dev = ["pytest>=8.4.2", "pytest-cov>=7.0.0", "ty>=0.0.1a24"] [tool.basedpyright] - +# This configures the "basedpyright" type checker to ignore a variety of +# issues that crop up when working with non-type-aware Python code. ignore = ["**/tests"] reportUnusedCallResult = false reportUnknownMemberType = false @@ -28,7 +34,15 @@ analyzeUnannotatedFunctions = false [tool.pytest.ini_options] +# These options will be added to the pytest command line automatically. addopts = "--cov=. --cov-report=html" [tool.coverage.report] omit = ["tests/*"] + +[tool.setuptools] +# This tells packaging tools which top-level directories correspond to +# Python packages. Without this, package builds will fail if there are +# multiple top-level directories ("error: Multiple top-level packages +# discovered in a flat-layout") +packages = ["batchtools"] diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index f2bc1d0..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -openshift-client -typing_extensions diff --git a/tests/test_batchtools.py b/tests/test_batchtools.py index 4b55eb6..0b8804f 100644 --- a/tests/test_batchtools.py +++ b/tests/test_batchtools.py @@ -1,5 +1,5 @@ import pytest -from batchtools import BatchTools +from batchtools.batchtools import BatchTools def test_no_command(): diff --git a/tests/test_bd.py b/tests/test_bd.py index 31bc29b..6a0316e 100644 --- a/tests/test_bd.py +++ b/tests/test_bd.py @@ -5,7 +5,7 @@ import argparse -from bd import DeleteJobsCommand +from batchtools.bd import DeleteJobsCommand from tests.helpers import DictToObject diff --git a/tests/test_bj.py b/tests/test_bj.py index af67db5..e82b333 100644 --- a/tests/test_bj.py +++ b/tests/test_bj.py @@ -3,7 +3,7 @@ import argparse -from bj import ListJobsCommand +from batchtools.bj import ListJobsCommand from tests.helpers import DictToObject diff --git a/tests/test_bl.py b/tests/test_bl.py index 362a809..f272203 100644 --- a/tests/test_bl.py +++ b/tests/test_bl.py @@ -6,7 +6,7 @@ from typing import Any from typing import Callable -from bl import LogsCommand +from batchtools.bl import LogsCommand from tests.helpers import DictToObject diff --git a/tests/test_bp.py b/tests/test_bp.py index 1a10a74..0130d9b 100644 --- a/tests/test_bp.py +++ b/tests/test_bp.py @@ -5,7 +5,7 @@ from typing import Any import argparse -from bp import PrintJobsCommand +from batchtools.bp import PrintJobsCommand from tests.helpers import DictToObject diff --git a/tests/test_bps.py b/tests/test_bps.py index d65bd2c..5c98181 100644 --- a/tests/test_bps.py +++ b/tests/test_bps.py @@ -4,7 +4,7 @@ from contextlib import contextmanager from typing import Any -from bps import ListPodsCommand, summarize_gpu_pods +from batchtools.bps import ListPodsCommand, summarize_gpu_pods def create_pod( diff --git a/tests/test_br.py b/tests/test_br.py index 4c9b324..56cb6c6 100644 --- a/tests/test_br.py +++ b/tests/test_br.py @@ -4,8 +4,8 @@ import tempfile import argparse -import build_yaml -from br import CreateJobCommand +import batchtools.build_yaml +from batchtools.br import CreateJobCommand from tests.helpers import DictToObject @@ -98,7 +98,7 @@ def test_create_job_nowait( }, } - build_yaml.rsync_script = "testcommand {cmdline}" + batchtools.build_yaml.rsync_script = "testcommand {cmdline}" CreateJobCommand.run(args) assert mock_create.call_args.args[0] == expected diff --git a/uv.lock b/uv.lock index 86c4442..3b48d8c 100644 --- a/uv.lock +++ b/uv.lock @@ -389,7 +389,7 @@ wheels = [ [[package]] name = "python-batchtools" version = "0.1.0" -source = { virtual = "." } +source = { editable = "." } dependencies = [ { name = "kubernetes-typed" }, { name = "openshift-client" },