Skip to content

Commit 378322a

Browse files
authored
Merge branch 'master' into master
2 parents 0b35054 + d1e262f commit 378322a

36 files changed

+918
-209
lines changed

.codespellignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
__pycache__
2+
*.pyc
3+
.idea
4+
*.egg-info/
5+
.tox/
6+
env/
7+
venv/
8+
.env
9+
.venv
10+
.vscode/
11+
.python-version
12+
.coverage
13+
build/
14+
dist/

.coveragerc

Lines changed: 0 additions & 6 deletions
This file was deleted.

.github/workflows/lint_pr.yml

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
name: lint_pull_request
2+
on: [pull_request, push]
3+
jobs:
4+
check_changes:
5+
runs-on: ubuntu-24.04
6+
outputs:
7+
has_python_changes: ${{ steps.changed-files.outputs.has_python_changes }}
8+
files: ${{ steps.changed-files.outputs.files }}
9+
steps:
10+
- uses: actions/checkout@v3
11+
with:
12+
fetch-depth: 0 # To get all history for git diff commands
13+
14+
- name: Get changed Python files
15+
id: changed-files
16+
run: |
17+
if [ "${{ github.event_name }}" == "pull_request" ]; then
18+
# For PRs, compare against base branch
19+
CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRT origin/${{ github.base_ref }} HEAD | grep '\.py$' | grep -v "^setup\.py$" || echo "")
20+
# Check if setup.py specifically changed
21+
SETUP_PY_CHANGED=$(git diff --name-only --diff-filter=ACMRT origin/${{ github.base_ref }} HEAD | grep "^setup\.py$" || echo "")
22+
if [ ! -z "$SETUP_PY_CHANGED" ]; then
23+
CHANGED_FILES="$CHANGED_FILES $SETUP_PY_CHANGED"
24+
fi
25+
else
26+
# For pushes, use the before/after SHAs
27+
CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRT ${{ github.event.before }} ${{ github.event.after }} | grep '\.py$' | grep -v "^setup\.py$" || echo "")
28+
# Check if setup.py specifically changed
29+
SETUP_PY_CHANGED=$(git diff --name-only --diff-filter=ACMRT ${{ github.event.before }} ${{ github.event.after }} | grep "^setup\.py$" || echo "")
30+
if [ ! -z "$SETUP_PY_CHANGED" ]; then
31+
CHANGED_FILES="$CHANGED_FILES $SETUP_PY_CHANGED"
32+
fi
33+
fi
34+
35+
# Check if any Python files were changed and set the output accordingly
36+
if [ -z "$CHANGED_FILES" ]; then
37+
echo "No Python files changed"
38+
echo "has_python_changes=false" >> $GITHUB_OUTPUT
39+
echo "files=" >> $GITHUB_OUTPUT
40+
else
41+
echo "Changed Python files: $CHANGED_FILES"
42+
echo "has_python_changes=true" >> $GITHUB_OUTPUT
43+
# Use proper delimiter formatting for GitHub Actions
44+
FILES_SINGLE_LINE=$(echo "$CHANGED_FILES" | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g' | sed 's/^[[:space:]]*//' | sed 's/[[:space:]]*$//')
45+
echo "files=$FILES_SINGLE_LINE" >> $GITHUB_OUTPUT
46+
fi
47+
48+
- name: PR information
49+
if: ${{ github.event_name == 'pull_request' }}
50+
run: |
51+
if [[ "${{ steps.changed-files.outputs.has_python_changes }}" == "true" ]]; then
52+
echo "This PR contains Python changes that will be linted."
53+
else
54+
echo "This PR contains no Python changes, but still requires manual approval."
55+
fi
56+
57+
lint:
58+
needs: check_changes
59+
if: ${{ needs.check_changes.outputs.has_python_changes == 'true' }}
60+
runs-on: ubuntu-24.04
61+
strategy:
62+
fail-fast: false
63+
matrix:
64+
tool: [flake8, format, mypy, pytest, pyupgrade, tox]
65+
steps:
66+
# Additional check to ensure we have Python files before proceeding
67+
- name: Verify Python changes
68+
run: |
69+
if [[ "${{ needs.check_changes.outputs.has_python_changes }}" != "true" ]]; then
70+
echo "No Python files were changed. Skipping linting."
71+
exit 0
72+
fi
73+
74+
- uses: actions/checkout@v3
75+
with:
76+
fetch-depth: 0
77+
78+
- uses: actions/setup-python@v4
79+
with:
80+
python-version: 3.12
81+
82+
- uses: actions/cache@v3
83+
with:
84+
path: ~/.cache/pip
85+
key: ${{ runner.os }}-pip-${{ hashFiles('requirements-dev.txt') }}
86+
restore-keys: |
87+
${{ runner.os }}-pip-
88+
89+
- name: Install dependencies
90+
run: |
91+
python -m pip install --upgrade pip
92+
pip install -r requirements-dev.txt
93+
94+
# Flake8 linting
95+
- name: Lint with flake8
96+
if: ${{ matrix.tool == 'flake8' }}
97+
id: flake8
98+
run: |
99+
echo "Linting files: ${{ needs.check_changes.outputs.files }}"
100+
flake8 ${{ needs.check_changes.outputs.files }} --count --show-source --statistics
101+
102+
# Format checking with isort and black
103+
- name: Format check
104+
if: ${{ matrix.tool == 'format' }}
105+
id: format
106+
run: |
107+
echo "Checking format with isort for: ${{ needs.check_changes.outputs.files }}"
108+
isort --profile black --check ${{ needs.check_changes.outputs.files }}
109+
echo "Checking format with black for: ${{ needs.check_changes.outputs.files }}"
110+
black --check ${{ needs.check_changes.outputs.files }}
111+
112+
# Type checking with mypy
113+
- name: Type check with mypy
114+
if: ${{ matrix.tool == 'mypy' }}
115+
id: mypy
116+
run: |
117+
echo "Type checking: ${{ needs.check_changes.outputs.files }}"
118+
mypy --ignore-missing-imports ${{ needs.check_changes.outputs.files }}
119+
120+
# Run tests with pytest
121+
- name: Run tests with pytest
122+
if: ${{ matrix.tool == 'pytest' }}
123+
id: pytest
124+
run: |
125+
echo "Running pytest discovery..."
126+
python -m pytest --collect-only -v
127+
128+
# First run any test files that correspond to changed files
129+
echo "Running tests for changed files..."
130+
changed_files="${{ needs.check_changes.outputs.files }}"
131+
132+
# Extract module paths from changed files
133+
modules=()
134+
for file in $changed_files; do
135+
# Convert file path to module path (remove .py and replace / with .)
136+
if [[ $file == patterns/* ]]; then
137+
module_path=${file%.py}
138+
module_path=${module_path//\//.}
139+
modules+=("$module_path")
140+
fi
141+
done
142+
143+
# Run tests for each module
144+
for module in "${modules[@]}"; do
145+
echo "Testing module: $module"
146+
python -m pytest -xvs tests/ -k "$module" || true
147+
done
148+
149+
# Then run doctests on the changed files
150+
echo "Running doctests for changed files..."
151+
for file in $changed_files; do
152+
if [[ $file == *.py ]]; then
153+
echo "Running doctest for $file"
154+
python -m pytest --doctest-modules -v $file || true
155+
fi
156+
done
157+
158+
# Check Python version compatibility
159+
- name: Check Python version compatibility
160+
if: ${{ matrix.tool == 'pyupgrade' }}
161+
id: pyupgrade
162+
run: pyupgrade --py312-plus ${{ needs.check_changes.outputs.files }}
163+
164+
# Run tox
165+
- name: Run tox
166+
if: ${{ matrix.tool == 'tox' }}
167+
id: tox
168+
run: |
169+
echo "Running tox integration for changed files..."
170+
changed_files="${{ needs.check_changes.outputs.files }}"
171+
172+
# Create a temporary tox configuration that extends the original one
173+
echo "[tox]" > tox_pr.ini
174+
echo "envlist = py312" >> tox_pr.ini
175+
echo "skip_missing_interpreters = true" >> tox_pr.ini
176+
177+
echo "[testenv]" >> tox_pr.ini
178+
echo "setenv =" >> tox_pr.ini
179+
echo " COVERAGE_FILE = .coverage.{envname}" >> tox_pr.ini
180+
echo "deps =" >> tox_pr.ini
181+
echo " -r requirements-dev.txt" >> tox_pr.ini
182+
echo "allowlist_externals =" >> tox_pr.ini
183+
echo " pytest" >> tox_pr.ini
184+
echo " coverage" >> tox_pr.ini
185+
echo " python" >> tox_pr.ini
186+
echo "commands =" >> tox_pr.ini
187+
188+
# Check if we have any implementation files that changed
189+
pattern_files=0
190+
test_files=0
191+
192+
for file in $changed_files; do
193+
if [[ $file == patterns/* ]]; then
194+
pattern_files=1
195+
elif [[ $file == tests/* ]]; then
196+
test_files=1
197+
fi
198+
done
199+
200+
# Only run targeted tests, no baseline
201+
echo " # Run specific tests for changed files" >> tox_pr.ini
202+
203+
has_tests=false
204+
205+
# Add coverage-focused test commands
206+
for file in $changed_files; do
207+
if [[ $file == *.py ]]; then
208+
# Run coverage tests for implementation files
209+
if [[ $file == patterns/* ]]; then
210+
module_name=$(basename $file .py)
211+
212+
# Get the pattern type (behavioral, structural, etc.)
213+
if [[ $file == patterns/behavioral/* ]]; then
214+
pattern_dir="behavioral"
215+
elif [[ $file == patterns/creational/* ]]; then
216+
pattern_dir="creational"
217+
elif [[ $file == patterns/structural/* ]]; then
218+
pattern_dir="structural"
219+
elif [[ $file == patterns/fundamental/* ]]; then
220+
pattern_dir="fundamental"
221+
elif [[ $file == patterns/other/* ]]; then
222+
pattern_dir="other"
223+
else
224+
pattern_dir=""
225+
fi
226+
227+
echo " # Testing $file" >> tox_pr.ini
228+
229+
# Check if specific test exists
230+
if [ -n "$pattern_dir" ]; then
231+
test_path="tests/${pattern_dir}/test_${module_name}.py"
232+
echo " if [ -f \"${test_path}\" ]; then echo \"Test file ${test_path} exists: true\" && coverage run -m pytest -xvs --cov=patterns --cov-append ${test_path}; else echo \"Test file ${test_path} exists: false\"; fi" >> tox_pr.ini
233+
234+
# Also try to find any test that might include this module
235+
echo " coverage run -m pytest -xvs --cov=patterns --cov-append tests/${pattern_dir}/ -k \"${module_name}\" --no-header" >> tox_pr.ini
236+
fi
237+
238+
# Run doctests for the file
239+
echo " coverage run -m pytest --doctest-modules -v --cov=patterns --cov-append $file" >> tox_pr.ini
240+
241+
has_tests=true
242+
fi
243+
244+
# Run test files directly if modified
245+
if [[ $file == tests/* ]]; then
246+
echo " coverage run -m pytest -xvs --cov=patterns --cov-append $file" >> tox_pr.ini
247+
has_tests=true
248+
fi
249+
fi
250+
done
251+
252+
# If we didn't find any specific tests to run, mention it
253+
if [ "$has_tests" = false ]; then
254+
echo " python -c \"print('No specific tests found for changed files. Consider adding tests.')\"" >> tox_pr.ini
255+
# Add a minimal test to avoid failure, but ensure it generates coverage data
256+
echo " coverage run -m pytest -xvs --cov=patterns --cov-append -k \"not integration\" --no-header" >> tox_pr.ini
257+
fi
258+
259+
# Add coverage report command
260+
echo " coverage combine" >> tox_pr.ini
261+
echo " coverage report -m" >> tox_pr.ini
262+
263+
# Run tox with the custom configuration
264+
echo "Running tox with custom PR configuration..."
265+
echo "======================== TOX CONFIG ========================"
266+
cat tox_pr.ini
267+
echo "==========================================================="
268+
tox -c tox_pr.ini
269+
270+
summary:
271+
needs: [check_changes, lint]
272+
# Run summary in all cases, regardless of whether lint job ran
273+
if: ${{ always() }}
274+
runs-on: ubuntu-24.04
275+
steps:
276+
- uses: actions/checkout@v3
277+
278+
- name: Summarize results
279+
run: |
280+
echo "## Pull Request Lint Results" >> $GITHUB_STEP_SUMMARY
281+
if [[ "${{ needs.check_changes.outputs.has_python_changes }}" == "true" ]]; then
282+
echo "Linting has completed for all Python files changed in this PR." >> $GITHUB_STEP_SUMMARY
283+
echo "See individual job logs for detailed results." >> $GITHUB_STEP_SUMMARY
284+
else
285+
echo "No Python files were changed in this PR. Linting was skipped." >> $GITHUB_STEP_SUMMARY
286+
fi
287+
echo "" >> $GITHUB_STEP_SUMMARY
288+
echo "⚠️ **Note:** This PR still requires manual approval regardless of linting results." >> $GITHUB_STEP_SUMMARY

.github/workflows/lint_python.yml

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,35 @@ name: lint_python
22
on: [pull_request, push]
33
jobs:
44
lint_python:
5-
runs-on: ubuntu-latest
5+
runs-on: ubuntu-24.04
66
steps:
77
- uses: actions/checkout@v3
88
- uses: actions/setup-python@v4
99
with:
10-
python-version: 3.x
11-
- run: pip install --upgrade pip
12-
- run: pip install black codespell flake8 isort mypy pytest pyupgrade tox
13-
- run: black --check .
14-
- run: codespell --quiet-level=2 # --ignore-words-list="" --skip=""
15-
- run: flake8 . --count --show-source --statistics
16-
- run: isort --profile black .
17-
- run: tox
18-
- run: pip install -e .
19-
- run: mypy --ignore-missing-imports . || true
20-
- run: pytest .
21-
- run: pytest --doctest-modules . || true
22-
- run: shopt -s globstar && pyupgrade --py37-plus **/*.py
10+
python-version: 3.12
11+
- name: Install dependencies
12+
run: |
13+
python -m pip install --upgrade pip
14+
pip install .[dev]
15+
- name: Lint with flake8
16+
run: flake8 ./patterns --count --show-source --statistics
17+
continue-on-error: true
18+
- name: Format check with isort and black
19+
run: |
20+
isort --profile black --check ./patterns
21+
black --check ./patterns
22+
continue-on-error: true
23+
- name: Type check with mypy
24+
run: mypy --ignore-missing-imports ./patterns || true
25+
continue-on-error: true
26+
- name: Run tests with pytest
27+
run: |
28+
pytest ./patterns
29+
pytest --doctest-modules ./patterns || true
30+
continue-on-error: true
31+
- name: Check Python version compatibility
32+
run: shopt -s globstar && pyupgrade --py312-plus ./patterns/**/*.py
33+
continue-on-error: true
34+
- name: Run tox
35+
run: tox
36+
continue-on-error: true

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ venv/
1313
.project
1414
.pydevproject
1515
/.pytest_cache/
16+
build/
17+
dist/

.travis.yml

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
os: linux
2-
dist: focal
2+
dist: noble
33
language: python
44

55
jobs:
66
include:
7-
- python: "3.8"
8-
env: TOXENV=py38
9-
- python: "3.9"
10-
env: TOXENV=py39
7+
- python: "3.12"
8+
env: TOXENV=py312
119

1210
cache:
1311
- pip

0 commit comments

Comments
 (0)