Skip to content

Commit faddc9d

Browse files
authored
Merge pull request #11 from JeremieGince/dev
Dev
2 parents 12240c3 + bfeff42 commit faddc9d

10 files changed

Lines changed: 192 additions & 66 deletions

File tree

.github/workflows/build_dist.yml

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,37 +41,26 @@ jobs:
4141
run: |
4242
. ./venv/bin/activate
4343
poetry version ${{steps.version.outputs.new_tag}}
44-
git config --local user.email "action@github.com"
45-
git config --local user.name "GitHub Action"
46-
git add pyproject.toml
47-
git commit -m "Updating version of pyproject.toml"
48-
49-
- name: Push the new pyproject.toml
50-
uses: ad-m/github-push-action@master
51-
with:
52-
github_token: ${{secrets.GITHUB_TOKEN}}
53-
branch: main
54-
force: true
5544
5645
- name: Build dist
5746
run: |
5847
. ./venv/bin/activate
5948
python -m build
49+
twine check dist/*
6050
61-
- name: Commit the new dist
51+
- name: Commit updated pyproject.toml
6252
run: |
63-
git add -f dist
6453
git config --local user.email "action@github.com"
6554
git config --local user.name "GitHub Action"
66-
git commit -m "Update dist"
55+
git add pyproject.toml
56+
git commit -m "Updating version of pyproject.toml"
6757
68-
- name: Push the new dist
58+
- name: Push the new pyproject.toml
6959
uses: ad-m/github-push-action@master
7060
with:
7161
github_token: ${{secrets.GITHUB_TOKEN}}
7262
branch: main
7363
force: true
74-
directory: dist
7564

7665
- name: Create Tag
7766
id: tag
@@ -87,3 +76,19 @@ jobs:
8776
with:
8877
tag_name: ${{ steps.tag.outputs.new_tag }}
8978
generate_release_notes: true
79+
80+
- name: Publish distribution to PyPI
81+
uses: pypa/gh-action-pypi-publish@release/v1
82+
with:
83+
skip_existing: true
84+
verbose: true
85+
86+
- name: Merge 'main' branch -> 'dev' branch
87+
uses: devmasx/merge-branch@master
88+
with:
89+
type: now
90+
from_branch: main
91+
target_branch: dev
92+
message: "Automatic merge from 'main' into 'dev' [skip actions]"
93+
label_name: "gh-actions"
94+
github_token: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/docs.yml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ jobs:
2323
python -m venv ./venv
2424
. ./venv/bin/activate
2525
python -m pip install --upgrade pip
26-
pip install wheel build
2726
pip install poetry
2827
poetry install --with=docs --no-interaction --no-ansi
2928
@@ -62,9 +61,3 @@ jobs:
6261
branch: gh-pages
6362
force: true
6463
directory: docs
65-
66-
- name: Publish distribution to PyPI
67-
uses: pypa/gh-action-pypi-publish@release/v1
68-
with:
69-
skip_existing: true
70-
verbose: true

.github/workflows/tests.yml

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,32 @@
1-
# This workflow will install Python dependencies, run tests and lint with a single version of Python
2-
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3-
41
name: Tests
52

63
on:
74
pull_request:
85
branches: [ "*" ]
96

107
permissions:
11-
contents: read
8+
contents: write
9+
pull-requests: write
10+
actions: write
11+
checks: write
12+
statuses: write
13+
issues: write
14+
discussions: write
1215

1316
jobs:
1417
Run-tests-on-Ubuntu:
1518
name: Run tests on Ubuntu-latest
1619
runs-on: ubuntu-latest
20+
strategy:
21+
matrix:
22+
python-version: [ "3.10", "3.11", "3.12", "3.13" ]
1723

1824
steps:
1925
- uses: actions/checkout@v3
20-
- name: Set up Python 3.10
26+
- name: Set up Python ${{ matrix.python-version }}
2127
uses: actions/setup-python@v3
2228
with:
23-
python-version: "3.10"
29+
python-version: ${{ matrix.python-version }}
2430
- name: Install dependencies
2531
run: |
2632
python -m venv ./venv
@@ -46,18 +52,36 @@ jobs:
4652
- name: Test Unittests with pytest
4753
run: |
4854
. ./venv/bin/activate
49-
python run_pytests.py tests --N_RANDOM_TESTS_PER_CASE=3 --run_slow=False
55+
python run_pytests.py --tests_folder=tests --N_RANDOM_TESTS_PER_CASE=3 --no-run_slow --cov-report=xml:tests/.tmp/coverage.xml
56+
57+
- name: Code Coverage
58+
uses: orgoro/coverage@v3.2
59+
with:
60+
token: ${{ secrets.GITHUB_TOKEN }}
61+
coverageFile: tests/.tmp/coverage.xml
62+
thresholdAll: 0.98
63+
thresholdNew: 0.98
64+
thresholdModified: 0.98
65+
66+
- name: Test Build
67+
run: |
68+
. ./venv/bin/activate
69+
python -m build
70+
twine check dist/*
5071
5172
Run-tests-on-Windows:
5273
name: Run tests on Windows-latest
5374
runs-on: windows-latest
75+
strategy:
76+
matrix:
77+
python-version: [ "3.10", "3.11", "3.12", "3.13" ]
5478

5579
steps:
5680
- uses: actions/checkout@v3
57-
- name: Set up Python 3.10
81+
- name: Set up Python ${{ matrix.python-version }}
5882
uses: actions/setup-python@v3
5983
with:
60-
python-version: "3.10"
84+
python-version: ${{ matrix.python-version }}
6185
- name: Install dependencies
6286
run: |
6387
python -m venv ./venv
@@ -68,4 +92,4 @@ jobs:
6892
- name: Test Unittests with pytest
6993
run: |
7094
. ./venv/Scripts/activate
71-
python run_pytests.py tests --N_RANDOM_TESTS_PER_CASE=3 --run_slow=False
95+
python run_pytests.py --tests_folder=tests --N_RANDOM_TESTS_PER_CASE=1 --no-run_slow

pyproject.toml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[project]
2-
name = "PythonTemplate"
2+
name = "python_template"
33
version = "0.0.1"
44
dynamic = ["readme"]
55
description = ""
@@ -78,10 +78,17 @@ sphinx-mdinclude = "^0.6.2"
7878
pythonpath = [
7979
".", "src",
8080
]
81-
addopts = [
82-
"--cov=src",
83-
"--no-cov",
84-
"--durations=10",
81+
82+
[tool.coverage.report]
83+
exclude_also = [
84+
'def __repr__',
85+
'if self.debug:',
86+
'if settings.DEBUG',
87+
'raise AssertionError',
88+
'raise NotImplementedError',
89+
'if __name__ == .__main__.:',
90+
'if TYPE_CHECKING:',
91+
'@(abc\.)?abstractmethod',
8592
]
8693

8794
[[tool.mypy.overrides]]

run_pytests.py

Lines changed: 70 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,80 @@
88

99
from tests import configs
1010
from tests.conftest import RUN_SLOW_ARG_NAME
11+
import argparse
1112

12-
if __name__ == '__main__':
13-
# TODO: use argparse
14-
sys_args_dict = pbt.cmds.get_cmd_kwargs(
15-
{
16-
1 : os.path.join(os.getcwd(), "tests"),
17-
"N_RANDOM_TESTS_PER_CASE": configs.N_RANDOM_TESTS_PER_CASE,
18-
"save_report" : "True",
19-
"cov" : "src",
20-
"cov-report" : "xml",
21-
RUN_SLOW_ARG_NAME : "True",
22-
"durations" : 10,
23-
}
13+
14+
def get_args_parser():
15+
parser = argparse.ArgumentParser(description="Tests Runner")
16+
parser.add_argument(
17+
"--tests_folder",
18+
type=str,
19+
default=os.path.join(os.getcwd(), "tests"),
20+
help="Path to the folder containing tests.",
21+
)
22+
parser.add_argument(
23+
"--N_RANDOM_TESTS_PER_CASE",
24+
type=int,
25+
default=configs.N_RANDOM_TESTS_PER_CASE,
26+
help="Number of random tests to run per test case.",
27+
)
28+
parser.add_argument(
29+
"--save_report",
30+
type=bool,
31+
default=False,
32+
action=argparse.BooleanOptionalAction,
33+
help="Whether to save the report in JSON format.",
34+
)
35+
parser.add_argument(
36+
"--cov",
37+
type=str,
38+
default="src",
39+
help="Path to the source code for coverage.",
40+
)
41+
parser.add_argument(
42+
"--cov-report",
43+
type=str,
44+
default="xml:tests/.tmp/coverage.xml",
45+
help="Format of the coverage report.",
46+
)
47+
parser.add_argument(
48+
f"--{RUN_SLOW_ARG_NAME}",
49+
type=bool,
50+
default=False,
51+
action=argparse.BooleanOptionalAction,
52+
help="Whether to run slow tests.",
2453
)
25-
sys_args_dict["cov-report"] = sys_args_dict["cov_report"] # fix
26-
configs.N_RANDOM_TESTS_PER_CASE = sys_args_dict["N_RANDOM_TESTS_PER_CASE"]
27-
configs.RUN_SLOW_TESTS = 't' in str(sys_args_dict[RUN_SLOW_ARG_NAME]).lower()
54+
parser.add_argument(
55+
"--durations",
56+
type=int,
57+
default=10,
58+
help="Number of slowest test durations to report.",
59+
)
60+
return parser
61+
62+
63+
def main():
64+
parser = get_args_parser()
65+
args = parser.parse_args()
66+
configs.N_RANDOM_TESTS_PER_CASE = args.N_RANDOM_TESTS_PER_CASE
67+
configs.RUN_SLOW_TESTS = args.run_slow
2868
json_plugin = JSONReport()
29-
pytest_main_args_names = ["cov", "cov-report", "durations"]
30-
pytest_main_args = [f"--{k}={sys_args_dict[k]}" for k in pytest_main_args_names]
31-
pytest.main([sys_args_dict[1], *pytest_main_args], plugins=[json_plugin])
32-
json_path = os.path.join(os.getcwd(), "tests", "tmp", f"tests_report_rn{configs.N_RANDOM_TESTS_PER_CASE}.json")
33-
save_report = 't' in str(sys_args_dict["save_report"]).lower()
34-
if save_report:
69+
pytest_main_args = [
70+
args.tests_folder,
71+
f"--cov={args.cov}",
72+
f"--cov-report={args.cov_report}",
73+
f"--cov-report=term-missing",
74+
f"--durations={args.durations}",
75+
]
76+
pytest.main(pytest_main_args, plugins=[json_plugin])
77+
json_path = os.path.join(args.tests_folder, ".tmp", f"tests_report_rn{configs.N_RANDOM_TESTS_PER_CASE}.json")
78+
if args.save_report:
3579
json_plugin.save_report(json_path)
3680
json_data = json.load(open(json_path))
3781
with open(json_path, "w") as f:
3882
json.dump(json_data, f, indent=4)
83+
return 0
84+
85+
86+
if __name__ == '__main__':
87+
exit(main())

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"Intended Audience :: Science/Research",
1414
"License :: OSI Approved :: Apache Software License",
1515
"Programming Language :: Python :: 3",
16-
"Programming Language :: Python :: 3.9",
16+
"Programming Language :: Python :: 3.10",
1717
"Topic :: Scientific/Engineering",
1818
"Topic :: Scientific/Engineering :: Artificial Intelligence",
1919
"Topic :: Software Development :: Libraries :: Python Modules",

src/python_template/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
__author__ = "Jeremie Gince"
88
__email__ = "gincejeremie@gmail.com"
9-
__copyright__ = "Copyright 2024, Jeremie Gince"
9+
__copyright__ = "Copyright 2025, Jeremie Gince"
1010
__license__ = "Apache 2.0"
1111
__url__ = "https://github.com/JeremieGince/PythonProject-Template"
1212
__package__ = "python_template"

src/python_template/__main__.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import argparse
22
import sys
3+
from typing import Optional
34

45

5-
def parse_args():
6-
paser = argparse.ArgumentParser()
7-
return paser.parse_args()
6+
def get_args_parser():
7+
parser = argparse.ArgumentParser(description="Python Template")
8+
return parser
89

910

10-
def main():
11-
pass
11+
def main(args: Optional[str] = None) -> Optional[int]:
12+
parser = get_args_parser()
13+
args = parser.parse_args(args)
14+
return 0
1215

1316

1417
if __name__ == "__main__":

src/python_template/py.typed

Whitespace-only changes.

tests/test_dummy.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,51 @@
1+
import argparse
2+
13
import pytest
4+
import python_template
5+
from python_template.__main__ import main, get_args_parser
6+
import runpy
27

38

49
@pytest.mark.parametrize("dummy", list(range(10)))
510
def test_dummy(dummy):
611
assert dummy < 10
12+
13+
14+
@pytest.mark.parametrize(
15+
"attr",
16+
[
17+
"__author__",
18+
"__email__",
19+
"__copyright__",
20+
"__license__",
21+
"__url__",
22+
"__package__",
23+
"__version__",
24+
],
25+
)
26+
def test_attributes(attr):
27+
assert hasattr(python_template, attr), f"Module does not have attribute {attr}"
28+
assert getattr(python_template, attr) is not None, f"Attribute {attr} is None"
29+
assert isinstance(
30+
getattr(python_template, attr), str
31+
), f"Attribute {attr} is not a string"
32+
33+
34+
def test_main():
35+
exit_code = main("")
36+
assert exit_code == 0, f"Main function did not return 0, got {exit_code}"
37+
38+
39+
def test_get_args_parser():
40+
parser = get_args_parser()
41+
assert parser is not None, "get_args_parser returned None"
42+
assert isinstance(
43+
parser, argparse.ArgumentParser
44+
), "get_args_parser did not return an ArgumentParser instance"
45+
# Check if the parser has the expected attributes
46+
assert hasattr(
47+
parser, "description"
48+
), "ArgumentParser does not have a description attribute"
49+
assert (
50+
parser.description == "Python Template"
51+
), "ArgumentParser description does not match expected value"

0 commit comments

Comments
 (0)