Skip to content
Merged

Dev #11

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 deletions .github/workflows/build_dist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,37 +41,26 @@ jobs:
run: |
. ./venv/bin/activate
poetry version ${{steps.version.outputs.new_tag}}
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"

- name: Push the new pyproject.toml
uses: ad-m/github-push-action@master
with:
github_token: ${{secrets.GITHUB_TOKEN}}
branch: main
force: true

- name: Build dist
run: |
. ./venv/bin/activate
python -m build
twine check dist/*

- name: Commit the new dist
- name: Commit updated pyproject.toml
run: |
git add -f dist
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git commit -m "Update dist"
git add pyproject.toml
git commit -m "Updating version of pyproject.toml"

- name: Push the new dist
- name: Push the new pyproject.toml
uses: ad-m/github-push-action@master
with:
github_token: ${{secrets.GITHUB_TOKEN}}
branch: main
force: true
directory: dist

- name: Create Tag
id: tag
Expand All @@ -87,3 +76,19 @@ jobs:
with:
tag_name: ${{ steps.tag.outputs.new_tag }}
generate_release_notes: true

- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
skip_existing: true
verbose: true

- name: Merge 'main' branch -> 'dev' branch
uses: devmasx/merge-branch@master
with:
type: now
from_branch: main
target_branch: dev
message: "Automatic merge from 'main' into 'dev' [skip actions]"
label_name: "gh-actions"
github_token: ${{ secrets.GITHUB_TOKEN }}
7 changes: 0 additions & 7 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ jobs:
python -m venv ./venv
. ./venv/bin/activate
python -m pip install --upgrade pip
pip install wheel build
pip install poetry
poetry install --with=docs --no-interaction --no-ansi

Expand Down Expand Up @@ -62,9 +61,3 @@ jobs:
branch: gh-pages
force: true
directory: docs

- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
skip_existing: true
verbose: true
44 changes: 34 additions & 10 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,32 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: Tests

on:
pull_request:
branches: [ "*" ]

permissions:
contents: read
contents: write
pull-requests: write
actions: write
checks: write
statuses: write
issues: write
discussions: write

jobs:
Run-tests-on-Ubuntu:
name: Run tests on Ubuntu-latest
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ "3.10", "3.11", "3.12", "3.13" ]

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: "3.10"
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m venv ./venv
Expand All @@ -46,18 +52,36 @@ jobs:
- name: Test Unittests with pytest
run: |
. ./venv/bin/activate
python run_pytests.py tests --N_RANDOM_TESTS_PER_CASE=3 --run_slow=False
python run_pytests.py --tests_folder=tests --N_RANDOM_TESTS_PER_CASE=3 --no-run_slow --cov-report=xml:tests/.tmp/coverage.xml

- name: Code Coverage
uses: orgoro/coverage@v3.2
with:
token: ${{ secrets.GITHUB_TOKEN }}
coverageFile: tests/.tmp/coverage.xml
thresholdAll: 0.98
thresholdNew: 0.98
thresholdModified: 0.98

- name: Test Build
run: |
. ./venv/bin/activate
python -m build
twine check dist/*

Run-tests-on-Windows:
name: Run tests on Windows-latest
runs-on: windows-latest
strategy:
matrix:
python-version: [ "3.10", "3.11", "3.12", "3.13" ]

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: "3.10"
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m venv ./venv
Expand All @@ -68,4 +92,4 @@ jobs:
- name: Test Unittests with pytest
run: |
. ./venv/Scripts/activate
python run_pytests.py tests --N_RANDOM_TESTS_PER_CASE=3 --run_slow=False
python run_pytests.py --tests_folder=tests --N_RANDOM_TESTS_PER_CASE=1 --no-run_slow
17 changes: 12 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[project]
name = "PythonTemplate"
name = "python_template"
version = "0.0.1"
dynamic = ["readme"]
description = ""
Expand Down Expand Up @@ -78,10 +78,17 @@ sphinx-mdinclude = "^0.6.2"
pythonpath = [
".", "src",
]
addopts = [
"--cov=src",
"--no-cov",
"--durations=10",

[tool.coverage.report]
exclude_also = [
'def __repr__',
'if self.debug:',
'if settings.DEBUG',
'raise AssertionError',
'raise NotImplementedError',
'if __name__ == .__main__.:',
'if TYPE_CHECKING:',
'@(abc\.)?abstractmethod',
]

[[tool.mypy.overrides]]
Expand Down
91 changes: 70 additions & 21 deletions run_pytests.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,80 @@

from tests import configs
from tests.conftest import RUN_SLOW_ARG_NAME
import argparse

if __name__ == '__main__':
# TODO: use argparse
sys_args_dict = pbt.cmds.get_cmd_kwargs(
{
1 : os.path.join(os.getcwd(), "tests"),
"N_RANDOM_TESTS_PER_CASE": configs.N_RANDOM_TESTS_PER_CASE,
"save_report" : "True",
"cov" : "src",
"cov-report" : "xml",
RUN_SLOW_ARG_NAME : "True",
"durations" : 10,
}

def get_args_parser():
parser = argparse.ArgumentParser(description="Tests Runner")
parser.add_argument(
"--tests_folder",
type=str,
default=os.path.join(os.getcwd(), "tests"),
help="Path to the folder containing tests.",
)
parser.add_argument(
"--N_RANDOM_TESTS_PER_CASE",
type=int,
default=configs.N_RANDOM_TESTS_PER_CASE,
help="Number of random tests to run per test case.",
)
parser.add_argument(
"--save_report",
type=bool,
default=False,
action=argparse.BooleanOptionalAction,
help="Whether to save the report in JSON format.",
)
parser.add_argument(
"--cov",
type=str,
default="src",
help="Path to the source code for coverage.",
)
parser.add_argument(
"--cov-report",
type=str,
default="xml:tests/.tmp/coverage.xml",
help="Format of the coverage report.",
)
parser.add_argument(
f"--{RUN_SLOW_ARG_NAME}",
type=bool,
default=False,
action=argparse.BooleanOptionalAction,
help="Whether to run slow tests.",
)
sys_args_dict["cov-report"] = sys_args_dict["cov_report"] # fix
configs.N_RANDOM_TESTS_PER_CASE = sys_args_dict["N_RANDOM_TESTS_PER_CASE"]
configs.RUN_SLOW_TESTS = 't' in str(sys_args_dict[RUN_SLOW_ARG_NAME]).lower()
parser.add_argument(
"--durations",
type=int,
default=10,
help="Number of slowest test durations to report.",
)
return parser


def main():
parser = get_args_parser()
args = parser.parse_args()
configs.N_RANDOM_TESTS_PER_CASE = args.N_RANDOM_TESTS_PER_CASE
configs.RUN_SLOW_TESTS = args.run_slow
json_plugin = JSONReport()
pytest_main_args_names = ["cov", "cov-report", "durations"]
pytest_main_args = [f"--{k}={sys_args_dict[k]}" for k in pytest_main_args_names]
pytest.main([sys_args_dict[1], *pytest_main_args], plugins=[json_plugin])
json_path = os.path.join(os.getcwd(), "tests", "tmp", f"tests_report_rn{configs.N_RANDOM_TESTS_PER_CASE}.json")
save_report = 't' in str(sys_args_dict["save_report"]).lower()
if save_report:
pytest_main_args = [
args.tests_folder,
f"--cov={args.cov}",
f"--cov-report={args.cov_report}",
f"--cov-report=term-missing",
f"--durations={args.durations}",
]
pytest.main(pytest_main_args, plugins=[json_plugin])
json_path = os.path.join(args.tests_folder, ".tmp", f"tests_report_rn{configs.N_RANDOM_TESTS_PER_CASE}.json")
if args.save_report:
json_plugin.save_report(json_path)
json_data = json.load(open(json_path))
with open(json_path, "w") as f:
json.dump(json_data, f, indent=4)
return 0


if __name__ == '__main__':
exit(main())
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"Intended Audience :: Science/Research",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
"Topic :: Software Development :: Libraries :: Python Modules",
Expand Down
2 changes: 1 addition & 1 deletion src/python_template/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

__author__ = "Jeremie Gince"
__email__ = "gincejeremie@gmail.com"
__copyright__ = "Copyright 2024, Jeremie Gince"
__copyright__ = "Copyright 2025, Jeremie Gince"
__license__ = "Apache 2.0"
__url__ = "https://github.com/JeremieGince/PythonProject-Template"
__package__ = "python_template"
Expand Down
13 changes: 8 additions & 5 deletions src/python_template/__main__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import argparse
import sys
from typing import Optional


def parse_args():
paser = argparse.ArgumentParser()
return paser.parse_args()
def get_args_parser():
parser = argparse.ArgumentParser(description="Python Template")
return parser


def main():
pass
def main(args: Optional[str] = None) -> Optional[int]:
parser = get_args_parser()
args = parser.parse_args(args)
return 0


if __name__ == "__main__":
Expand Down
Empty file added src/python_template/py.typed
Empty file.
45 changes: 45 additions & 0 deletions tests/test_dummy.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,51 @@
import argparse

import pytest
import python_template
from python_template.__main__ import main, get_args_parser
import runpy


@pytest.mark.parametrize("dummy", list(range(10)))
def test_dummy(dummy):
assert dummy < 10


@pytest.mark.parametrize(
"attr",
[
"__author__",
"__email__",
"__copyright__",
"__license__",
"__url__",
"__package__",
"__version__",
],
)
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"


def test_main():
exit_code = main("")
assert exit_code == 0, f"Main function did not return 0, got {exit_code}"


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"
# 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"