diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..41cfeeb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,64 @@ +name: Bug report +description: File a bug report +title: '[BUG]' +labels: "bug :bug:" + +body: + - type: markdown + id: description + attributes: + value: | + ## Before posting a bug report + Search existing GitHub issues to make sure the issue does not already exist. + + # Issue description + Description of the issue - include code snippets in the Source code section below and screenshots if relevant. + - type: textarea + id: expected + attributes: + label: Expected behavior + description: | + What do you expect to happen? + validations: + required: true + - type: textarea + id: actual + attributes: + label: Actual behavior + description: | + What actually happens? + validations: + required: true + - type: textarea + id: info + attributes: + label: Additional information + description: | + Please provide any additional information here that might be necessary to reproduce the issue. + - type: textarea + id: sourcecode + attributes: + label: Source code + description: Please include a minimal non-working example, or any other code related to the issue. + render: shell + - type: textarea + id: tracebacks + attributes: + label: Tracebacks + description: Please include the error tracebacks related to the issue here. + render: shell + - type: textarea + id: system + attributes: + label: System information + description: Please provide informations about you python version and os. The command `python -c "import sys; print(sys.version, sys.platform)"` can be used to get this information. Provide also the output of `pip list` command. + render: shell + validations: + required: true + - type: checkboxes + id: terms + attributes: + label: Existing GitHub issues + options: + - label: I have searched existing GitHub issues to make sure the issue does not already exist. + required: true \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..34051e4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,44 @@ +name: Feature request +description: Suggest a new feature +labels: "enhancement :sparkles:" + +body: + - type: markdown + id: description + attributes: + value: | + ## Before posting a feature request + Search existing GitHub issues to make sure the issue does not already exist. + + # Feature description + Description of the feature request - include code snippets and screenshots here if relevant. + - type: textarea + id: details + attributes: + label: Feature details + description: | + What feature would you like to have? + validations: + required: true + - type: textarea + id: implementation + attributes: + label: Implementation + description: | + Do you have an idea for how this could be implemented? Please include those details here. + - type: dropdown + id: urgency + attributes: + label: How important would you say this feature is? + options: + - "1: Not important. Would be nice to have." + - "2: Somewhat important. Needed this quarter." + - "3: Very important! Blocking work." + validations: + required: true + - type: textarea + id: info + attributes: + label: Additional information + description: | + Please provide any additional information here. Include potential alternative solutions, or workarounds, as well as references, if any. \ No newline at end of file diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..ee67225 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,22 @@ +# Description + +Please provide a brief description of the changes you are making in this pull request. + +------------------------------------------------------------------------------------------------------------ + +# Checklist + +Please complete the following checklist when submitting a PR. The PR will not be reviewed until all items are checked. + +- [ ] All new features include a unit test. + Make sure that the tests passed and the coverage is + sufficient by running `python run_pytests.py --tests_folder=tests` or + `pytest tests --cov=src --cov-report=term-missing`. + +- [ ] All new functions and code are clearly documented. + +- [ ] The code is formatted using Black. + You can do this by running `black src tests`. + +- [ ] The code is type-checked using Mypy. + You can do this by running `mypy src tests`. diff --git a/.github/workflows/build_dist.yml b/.github/workflows/build_dist.yml index c34b047..ecd718a 100644 --- a/.github/workflows/build_dist.yml +++ b/.github/workflows/build_dist.yml @@ -6,6 +6,12 @@ on: permissions: contents: write + pull-requests: write + actions: write + checks: write + statuses: write + issues: write + discussions: write jobs: Build-Dist: @@ -53,7 +59,7 @@ jobs: git config --local user.email "action@github.com" git config --local user.name "GitHub Action" git add pyproject.toml - git commit -m "Updating version of pyproject.toml" + git diff-index --quiet HEAD || git commit -m "Updating version of pyproject.toml" - name: Push the new pyproject.toml uses: ad-m/github-push-action@master diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5e4f0ac..eee80f0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -37,13 +37,11 @@ jobs: - name: Test Linting run: | . ./venv/bin/activate - black src --check --diff - black tests --check --diff + black src tests --check --diff - name: Test Typing run: | . ./venv/bin/activate - mypy src - mypy tests + mypy src tests - name: Test Notebooks run: | . ./venv/bin/activate diff --git a/Dockerfile b/Dockerfile index 8b86107..a30caa9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -38,7 +38,6 @@ FROM base_stage AS main_stage # Files WORKDIR $ROOT_DIR COPY --from=build_stage $ROOT_DIR ./ -VOLUME data # Entrypoint ENV PATH="venv/bin:$PATH" diff --git a/docker_files/build_image_and_run.py b/docker_files/build_image_and_run.py index 68548e6..7161f3b 100644 --- a/docker_files/build_image_and_run.py +++ b/docker_files/build_image_and_run.py @@ -44,6 +44,12 @@ def get_args_parser(): default=False, help="If set, only runs the container without building the image.", ) + parser.add_argument( + "--only_build", + action=argparse.BooleanOptionalAction, + default=False, + help="If set, only build the container without running the image.", + ) return parser @@ -73,6 +79,15 @@ def build_images( print(f"{cmd = }") subprocess.run(cmd, shell=True, check=True) print(f"Pruned old images") + + save_images_folder = os.path.join(os.path.dirname(__file__), "..", "data", "dockerfiles") + os.makedirs(save_images_folder, exist_ok=True) + for image in images: + savefile = os.path.abspath(os.path.join(save_images_folder, image.replace(":", "-") + ".tar")) + print(f"Saving {image} to {savefile}") + save_cmd = f"docker save -o {savefile} {image}" + subprocess.run(save_cmd, shell=True, check=True) + print(f"{image} saved to {savefile}.") return images def run_container( @@ -100,6 +115,7 @@ def run_container( print(f"Running container {container_name} from image {image_name}:{tag}") container_args = [ "--name", container_name, + "--gpus all", "--detach", f"--volume=" f"{os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'data'))}" @@ -116,8 +132,6 @@ def run_container( def main(): args = get_args_parser().parse_args() - if not args.image_name: - args.image_name = os.path.basename(os.path.dirname(os.path.dirname(__file__))) print(f"Running with args: {json.dumps(vars(args), indent=4)}") if args.only_run: images = [f"{args.image_name}:{args.tag}"] @@ -127,12 +141,20 @@ def main(): tag=args.tag, image_name=args.image_name, ) + if args.only_build: + return 0 for image in images: + container_img_part = ( + image + .replace("/", "-") + .replace(":", "-") + .replace(".", "-") + ) run_container( image_name=image, tag=args.tag, args=json.loads(args.args) if args.args else None, - container_name=f"{args.image_name}-{args.mode}", + container_name=f"{container_img_part}-{args.mode}", run_args=["-e", f"MODE={args.mode}"], ) return 0 diff --git a/pyproject.toml b/pyproject.toml index 4d15813..ab1155f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -96,3 +96,6 @@ module = [ "pythonbasictools", ] ignore_missing_imports = true + +[tool.black] +line-length = 120 diff --git a/run_pytests.py b/run_pytests.py index d3bb4bc..dbc62c0 100644 --- a/run_pytests.py +++ b/run_pytests.py @@ -1,14 +1,12 @@ +import argparse import json import os import pytest -import pythonbasictools as pbt from pytest_jsonreport.plugin import JSONReport -from pytest_cov.plugin import CovPlugin from tests import configs from tests.conftest import RUN_SLOW_ARG_NAME -import argparse def get_args_parser(): diff --git a/tests/conftest.py b/tests/conftest.py index 7149b9d..d3672af 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -39,9 +39,7 @@ def pytest_collection_modifyitems(config, items): pass else: # print("Skipping slow tests") - skipper = pytest.mark.skip( - reason=f"Only run when '--{RUN_SLOW_ARG_NAME}=True' is given" - ) + skipper = pytest.mark.skip(reason=f"Only run when '--{RUN_SLOW_ARG_NAME}=True' is given") for item in items: if "slow" in item.keywords: item.add_marker(skipper) diff --git a/tests/test_dummy.py b/tests/test_dummy.py index d664e8e..f7c2a96 100644 --- a/tests/test_dummy.py +++ b/tests/test_dummy.py @@ -26,9 +26,7 @@ def test_dummy(dummy): def test_attributes(attr): assert hasattr(python_template, attr), f"Module does not have attribute {attr}" assert getattr(python_template, attr) is not None, f"Attribute {attr} is None" - assert isinstance( - getattr(python_template, attr), str - ), f"Attribute {attr} is not a string" + assert isinstance(getattr(python_template, attr), str), f"Attribute {attr} is not a string" def test_main(): @@ -39,13 +37,7 @@ def test_main(): def test_get_args_parser(): parser = get_args_parser() assert parser is not None, "get_args_parser returned None" - assert isinstance( - parser, argparse.ArgumentParser - ), "get_args_parser did not return an ArgumentParser instance" + assert isinstance(parser, argparse.ArgumentParser), "get_args_parser did not return an ArgumentParser instance" # Check if the parser has the expected attributes - assert hasattr( - parser, "description" - ), "ArgumentParser does not have a description attribute" - assert ( - parser.description == "Python Template" - ), "ArgumentParser description does not match expected value" + assert hasattr(parser, "description"), "ArgumentParser does not have a description attribute" + assert parser.description == "Python Template", "ArgumentParser description does not match expected value"