Skip to content

Commit dd1bfbe

Browse files
committed
Switch development dependencies to Pipenv lockfile
- add Pipfile and Dependabot-compatible Pipfile.lock - update lint, tox, CI, Travis, and Makefile workflows - support Python 3.10 through 3.14 - remove the pip-tools requirements lockfile - standardize linting, type checking, and tests
1 parent 74151cf commit dd1bfbe

61 files changed

Lines changed: 1286 additions & 1475 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/dependabot.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
version: 2
2+
updates:
3+
- package-ecosystem: pip
4+
directory: /
5+
schedule:
6+
interval: weekly
7+
versioning-strategy: increase-if-necessary
8+
9+
- package-ecosystem: github-actions
10+
directory: /
11+
schedule:
12+
interval: weekly

.github/workflows/lint_pr.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,16 @@ jobs:
8282
- uses: actions/cache@v5
8383
with:
8484
path: ~/.cache/pip
85-
key: ${{ runner.os }}-pip-${{ hashFiles('requirements-dev.txt') }}
85+
key: ${{ runner.os }}-pip-${{ hashFiles('Pipfile.lock') }}
8686
restore-keys: |
8787
${{ runner.os }}-pip-
8888
8989
- name: Install dependencies
9090
run: |
9191
python -m pip install --upgrade pip
92-
pip install -r requirements-dev.txt
92+
pip install pipenv
93+
pipenv sync --dev --system
94+
pip install --no-build-isolation --no-deps -e .
9395
9496
# Flake8 linting
9597
- name: Lint with flake8
@@ -159,7 +161,7 @@ jobs:
159161
- name: Check Python version compatibility
160162
if: ${{ matrix.tool == 'pyupgrade' }}
161163
id: pyupgrade
162-
run: pyupgrade --py312-plus ${{ needs.check_changes.outputs.files }}
164+
run: pyupgrade --py310-plus ${{ needs.check_changes.outputs.files }}
163165

164166
# Run tox
165167
- name: Run tox
@@ -171,14 +173,13 @@ jobs:
171173
172174
# Create a temporary tox configuration that extends the original one
173175
echo "[tox]" > tox_pr.ini
174-
echo "envlist = py312" >> tox_pr.ini
175-
echo "skip_missing_interpreters = true" >> tox_pr.ini
176+
echo "envlist = py" >> tox_pr.ini
176177
177178
echo "[testenv]" >> tox_pr.ini
178179
echo "setenv =" >> tox_pr.ini
179180
echo " COVERAGE_FILE = .coverage.{envname}" >> tox_pr.ini
180181
echo "deps =" >> tox_pr.ini
181-
echo " -r requirements-dev.txt" >> tox_pr.ini
182+
echo " -r requirements-dev.lock" >> tox_pr.ini
182183
echo "allowlist_externals =" >> tox_pr.ini
183184
echo " pytest" >> tox_pr.ini
184185
echo " coverage" >> tox_pr.ini

.github/workflows/lint_python.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ jobs:
1111
- name: Install dependencies
1212
run: |
1313
python -m pip install --upgrade pip
14-
pip install .[dev]
14+
pip install pipenv
15+
pipenv sync --dev --system
16+
pip install --no-build-isolation --no-deps -e .
1517
- name: Lint with flake8
1618
run: flake8 ./patterns --count --show-source --statistics
1719
continue-on-error: true
@@ -29,7 +31,7 @@ jobs:
2931
pytest --doctest-modules ./patterns || true
3032
continue-on-error: true
3133
- name: Check Python version compatibility
32-
run: shopt -s globstar && pyupgrade --py312-plus ./patterns/**/*.py
34+
run: shopt -s globstar && pyupgrade --py310-plus ./patterns/**/*.py
3335
continue-on-error: true
3436
- name: Run tox
3537
run: tox

.travis.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@ os: linux
22
dist: noble
33
language: python
44

5-
jobs:
6-
include:
7-
- python: "3.12"
8-
env: TOXENV=py312
5+
python:
6+
- "3.10"
7+
- "3.11"
8+
- "3.12"
9+
- "3.13"
10+
- "3.14"
911

1012
cache:
1113
- pip
1214

1315
install:
14-
- pip install codecov tox
16+
- pip install pipenv
17+
- pipenv sync --dev --system
18+
- pip install --no-build-isolation --no-deps -e .
1519

1620
script:
1721
- tox

Makefile

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# Usage (line =black line length, path = action path, ignore= exclude folders)
77
# ------
88
# make pylinter [make pylinter line=88 path=.]
9-
# make pyupgrade
9+
# make lock
1010

1111
path := .
1212
line := 88
@@ -25,22 +25,11 @@ ifeq ("$(VIRTUAL_ENV)","")
2525
exit 1
2626
endif
2727

28-
.PHONY: pyupgrade
29-
pyupgrade: checkvenv
30-
# checks if pip-tools is installed
31-
ifeq ("$(wildcard venv/bin/pip-compile)","")
32-
@echo "Installing Pip-tools..."
33-
@pip install pip-tools
34-
endif
35-
36-
ifeq ("$(wildcard venv/bin/pip-sync)","")
37-
@echo "Installing Pip-tools..."
38-
@pip install pip-tools
39-
endif
40-
41-
# pip-tools
42-
# @pip-compile --upgrade requirements-dev.txt
43-
@pip-sync requirements-dev.txt
28+
.PHONY: lock
29+
lock: checkvenv
30+
@command -v pipenv >/dev/null || python -m pip install pipenv
31+
@pipenv lock --dev
32+
@pipenv sync --dev
4433

4534

4635
.PHONY: pylinter

Pipfile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[[source]]
2+
name = "pypi"
3+
url = "https://pypi.org/simple"
4+
verify_ssl = true
5+
6+
[packages]
7+
8+
[dev-packages]
9+
black = ">=25.1.0"
10+
build = ">=1.2.2"
11+
codecov = "*"
12+
codespell = "*"
13+
flake8 = ">=7.1.0"
14+
isort = ">=5.7.0"
15+
mypy = "*"
16+
pipx = ">=1.7.1"
17+
pyupgrade = "*"
18+
pytest = ">=6.2.0"
19+
pytest-cov = ">=2.11.0"
20+
pytest-randomly = ">=3.1.0"
21+
tox = ">=4.25.0"

Pipfile.lock

Lines changed: 813 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,18 @@ graph TD
5353
Composite --> Leaf2
5454
```
5555

56-
| Pattern | Description |
57-
|:-------:| ----------- |
58-
| [3-tier](patterns/structural/3-tier.py) | data<->business logic<->presentation separation (strict relationships) |
59-
| [adapter](patterns/structural/adapter.py) | adapt one interface to another using a white-list |
60-
| [bridge](patterns/structural/bridge.py) | a client-provider middleman to soften interface changes |
61-
| [composite](patterns/structural/composite.py) | lets clients treat individual objects and compositions uniformly |
62-
| [decorator](patterns/structural/decorator.py) | wrap functionality with other functionality in order to affect outputs |
63-
| [facade](patterns/structural/facade.py) | use one class as an API to a number of others |
64-
| [flyweight](patterns/structural/flyweight.py) | transparently reuse existing instances of objects with similar/identical state |
65-
| [front_controller](patterns/structural/front_controller.py) | single handler requests coming to the application |
66-
| [mvc](patterns/structural/mvc.py) | model<->view<->controller (non-strict relationships) |
67-
| [proxy](patterns/structural/proxy.py) | an object funnels operations to something else |
56+
| Pattern | Description |
57+
|:-----------------------------------------------------------:|--------------------------------------------------------------------------------|
58+
| [3-tier](patterns/structural/3-tier.py) | data<->business logic<->presentation separation (strict relationships) |
59+
| [adapter](patterns/structural/adapter.py) | adapt one interface to another using a white-list |
60+
| [bridge](patterns/structural/bridge.py) | a client-provider middleman to soften interface changes |
61+
| [composite](patterns/structural/composite.py) | lets clients treat individual objects and compositions uniformly |
62+
| [decorator](patterns/structural/decorator.py) | wrap functionality with other functionality in order to affect outputs |
63+
| [facade](patterns/structural/facade.py) | use one class as an API to a number of others |
64+
| [flyweight](patterns/structural/flyweight.py) | transparently reuse existing instances of objects with similar/identical state |
65+
| [front_controller](patterns/structural/front_controller.py) | single handler requests coming to the application |
66+
| [mvc](patterns/structural/mvc.py) | model<->view<->controller (non-strict relationships) |
67+
| [proxy](patterns/structural/proxy.py) | an object funnels operations to something else |
6868

6969
## Behavioral Patterns
7070

@@ -86,46 +86,46 @@ graph LR
8686
Caretaker -->|holds| Memento
8787
```
8888

89-
| Pattern | Description |
90-
|:-------:| ----------- |
91-
| [chain_of_responsibility](patterns/behavioral/chain_of_responsibility.py) | apply a chain of successive handlers to try and process the data |
92-
| [catalog](patterns/behavioral/catalog.py) | general methods will call different specialized methods based on construction parameter |
93-
| [chaining_method](patterns/behavioral/chaining_method.py) | continue callback next object method |
94-
| [command](patterns/behavioral/command.py) | bundle a command and arguments to call later |
95-
| [interpreter](patterns/behavioral/interpreter.py) | define a grammar for a language and use it to interpret statements |
96-
| [iterator](patterns/behavioral/iterator.py) | traverse a container and access the container's elements |
97-
| [iterator](patterns/behavioral/iterator_alt.py) (alt. impl.)| traverse a container and access the container's elements |
98-
| [mediator](patterns/behavioral/mediator.py) | an object that knows how to connect other objects and act as a proxy |
99-
| [memento](patterns/behavioral/memento.py) | generate an opaque token that can be used to go back to a previous state |
100-
| [observer](patterns/behavioral/observer.py) | provide a callback for notification of events/changes to data |
101-
| [publish_subscribe](patterns/behavioral/publish_subscribe.py) | a source syndicates events/data to 0+ registered listeners |
102-
| [registry](patterns/behavioral/registry.py) | keep track of all subclasses of a given class |
103-
| [servant](patterns/behavioral/servant.py) | provide common functionality to a group of classes without using inheritance |
104-
| [specification](patterns/behavioral/specification.py) | business rules can be recombined by chaining the business rules together using boolean logic |
105-
| [state](patterns/behavioral/state.py) | logic is organized into a discrete number of potential states and the next state that can be transitioned to |
106-
| [strategy](patterns/behavioral/strategy.py) | selectable operations over the same data |
107-
| [template](patterns/behavioral/template.py) | an object imposes a structure but takes pluggable components |
108-
| [visitor](patterns/behavioral/visitor.py) | invoke a callback for all items of a collection |
89+
| Pattern | Description |
90+
|:-------------------------------------------------------------------------:|--------------------------------------------------------------------------------------------------------------|
91+
| [chain_of_responsibility](patterns/behavioral/chain_of_responsibility.py) | apply a chain of successive handlers to try and process the data |
92+
| [catalog](patterns/behavioral/catalog.py) | general methods will call different specialized methods based on construction parameter |
93+
| [chaining_method](patterns/behavioral/chaining_method.py) | continue callback next object method |
94+
| [command](patterns/behavioral/command.py) | bundle a command and arguments to call later |
95+
| [interpreter](patterns/behavioral/interpreter.py) | define a grammar for a language and use it to interpret statements |
96+
| [iterator](patterns/behavioral/iterator.py) | traverse a container and access the container's elements |
97+
| [iterator](patterns/behavioral/iterator_alt.py) (alt. impl.) | traverse a container and access the container's elements |
98+
| [mediator](patterns/behavioral/mediator.py) | an object that knows how to connect other objects and act as a proxy |
99+
| [memento](patterns/behavioral/memento.py) | generate an opaque token that can be used to go back to a previous state |
100+
| [observer](patterns/behavioral/observer.py) | provide a callback for notification of events/changes to data |
101+
| [publish_subscribe](patterns/behavioral/publish_subscribe.py) | a source syndicates events/data to 0+ registered listeners |
102+
| [registry](patterns/behavioral/registry.py) | keep track of all subclasses of a given class |
103+
| [servant](patterns/behavioral/servant.py) | provide common functionality to a group of classes without using inheritance |
104+
| [specification](patterns/behavioral/specification.py) | business rules can be recombined by chaining the business rules together using boolean logic |
105+
| [state](patterns/behavioral/state.py) | logic is organized into a discrete number of potential states and the next state that can be transitioned to |
106+
| [strategy](patterns/behavioral/strategy.py) | selectable operations over the same data |
107+
| [template](patterns/behavioral/template.py) | an object imposes a structure but takes pluggable components |
108+
| [visitor](patterns/behavioral/visitor.py) | invoke a callback for all items of a collection |
109109

110110
## Design for Testability Patterns
111111

112-
| Pattern | Description |
113-
|:-------:| ----------- |
112+
| Pattern | Description |
113+
|:--------------------------------------------------------:|------------------------------------|
114114
| [dependency_injection](patterns/dependency_injection.py) | 3 variants of dependency injection |
115115

116116
## Fundamental Patterns
117117

118-
| Pattern | Description |
119-
|:-------:| ----------- |
118+
| Pattern | Description |
119+
|:----------------------------------------------------------------:|-----------------------------------------------------------------------------|
120120
| [delegation_pattern](patterns/fundamental/delegation_pattern.py) | an object handles a request by delegating to a second object (the delegate) |
121121

122122
## Others
123123

124-
| Pattern | Description |
125-
|:-------:| ----------- |
126-
| [blackboard](patterns/other/blackboard.py) | architectural model, assemble different sub-system knowledge to build a solution, AI approach - non gang of four pattern |
127-
| [graph_search](patterns/other/graph_search.py) | graphing algorithms - non gang of four pattern |
128-
| [hsm](patterns/other/hsm/hsm.py) | hierarchical state machine - non gang of four pattern |
124+
| Pattern | Description |
125+
|:----------------------------------------------:|--------------------------------------------------------------------------------------------------------------------------|
126+
| [blackboard](patterns/other/blackboard.py) | architectural model, assemble different sub-system knowledge to build a solution, AI approach - non gang of four pattern |
127+
| [graph_search](patterns/other/graph_search.py) | graphing algorithms - non gang of four pattern |
128+
| [hsm](patterns/other/hsm/hsm.py) | hierarchical state machine - non gang of four pattern |
129129

130130
## 🚫 Anti-Patterns
131131

@@ -173,9 +173,12 @@ When everything else is done - update corresponding part of README.
173173

174174
##### Travis CI
175175
Please run the following before submitting a patch:
176-
- `black .` This lints your code.
177-
- Either `tox` or `tox -e ci37` for unit tests.
178-
- If you have a bash compatible shell, use `./lint.sh`.
176+
- If you have a bash-compatible shell, use `./lint.sh` to run formatting,
177+
linting, type checking, and tests.
178+
179+
Development dependencies are pinned in `Pipfile.lock`. After changing
180+
`Pipfile`, activate a Python 3.10 virtual environment and run `make lock` to
181+
regenerate it.
179182

180183
## Contributing via issue triage [![Open Source Helpers](https://www.codetriage.com/faif/python-patterns/badges/users.svg)](https://www.codetriage.com/faif/python-patterns)
181184
You can triage issues and pull requests on [CodeTriage](https://www.codetriage.com/faif/python-patterns).

config_backup/tox.ini

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
[tox]
2-
envlist = py312,cov-report
2+
envlist = py,cov-report
33
skip_missing_interpreters = true
44
usedevelop = true
55

66
[testenv]
77
setenv =
88
COVERAGE_FILE = .coverage.{envname}
99
deps =
10-
-r requirements-dev.txt
10+
pipenv
11+
commands_pre =
12+
pipenv sync --dev --system
1113
allowlist_externals =
1214
pytest
1315
flake8

lint.sh

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,38 @@
1-
#! /bin/bash
1+
#!/usr/bin/env bash
22

3-
pip install --upgrade pip
4-
pip install black codespell flake8 isort mypy pytest pyupgrade tox
5-
pip install -e .
3+
set -euo pipefail
64

7-
source_dir="./patterns"
5+
project_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6+
venv_dir="${project_dir}/.venv"
7+
python_bin="${PYTHON:-python3}"
88

9-
codespell --quiet-level=2 ./patterns # --ignore-words-list="" --skip=""
10-
flake8 "${source_dir}" --count --show-source --statistics
11-
isort --profile black "${source_dir}"
12-
tox
13-
mypy --ignore-missing-imports "${source_dir}" || true
14-
pytest "${source_dir}"
15-
pytest --doctest-modules "${source_dir}" || true
16-
shopt -s globstar && pyupgrade --py312-plus ${source_dir}/*.py
9+
if [[ ! -x "${venv_dir}/bin/python" ]]; then
10+
"${python_bin}" -m venv "${venv_dir}"
11+
"${venv_dir}/bin/python" -m pip install --upgrade pip
12+
fi
13+
14+
venv_python="${venv_dir}/bin/python"
15+
venv_bin="${venv_dir}/bin"
16+
17+
"${venv_python}" -m pip install pipenv
18+
PIPENV_IGNORE_VIRTUALENVS=1 \
19+
PIPENV_VENV_IN_PROJECT=1 \
20+
"${venv_bin}/pipenv" sync --dev
21+
"${venv_python}" -m pip install --no-build-isolation --no-deps -e "${project_dir}"
22+
23+
source_dir="${project_dir}/patterns"
24+
tests_dir="${project_dir}/tests"
25+
26+
"${venv_bin}/codespell" --quiet-level=2 "${source_dir}" "${tests_dir}" "${project_dir}/README.md"
27+
"${venv_bin}/flake8" \
28+
"${source_dir}" \
29+
"${tests_dir}" \
30+
--max-line-length=120 \
31+
--extend-ignore=E266,E731,W503 \
32+
--count \
33+
--show-source \
34+
--statistics
35+
"${venv_bin}/isort" --profile black --check-only "${source_dir}" "${tests_dir}"
36+
"${venv_bin}/black" --check "${source_dir}" "${tests_dir}"
37+
"${venv_bin}/mypy" "${source_dir}"
38+
"${venv_bin}/pytest" "${tests_dir}" "${source_dir}"

0 commit comments

Comments
 (0)