Skip to content

Commit 2157cde

Browse files
authored
Merge pull request #357 from ahmedfgad/master
Sync
2 parents 6a71b1b + 4ded7c5 commit 2157cde

10 files changed

Lines changed: 271 additions & 8 deletions

File tree

.github/workflows/main.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,13 @@ jobs:
8080
python3 -m build
8181
8282
# Install the newly built .whl file to verify the package is installable.
83+
# Install with the 'visualize' extra so matplotlib is available for the
84+
# plotting tests (tests/test_visualize.py). This also validates that the
85+
# optional 'visualize' extra resolves correctly.
8386
- name: Install PyGAD from Wheel
8487
run: |
85-
pip install dist/*.whl
88+
WHEEL=$(ls dist/*.whl)
89+
pip install "${WHEEL}[visualize]"
8690
8791
- name: Install PyTest
8892
run: pip install pytest

.github/workflows/release.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: release
2+
3+
# On a version tag this builds the package once, publishes it to PyPI via
4+
# trusted publishing (no API token stored in the repo), and attaches the built
5+
# wheel and sdist to a GitHub Release for the tag. The PyPI project must list
6+
# this repo and workflow as a trusted publisher first.
7+
8+
on:
9+
push:
10+
# PyGAD tags releases as the bare version number, e.g. 3.6.0 (no "v").
11+
tags:
12+
- '[0-9]+.[0-9]+.[0-9]+'
13+
14+
jobs:
15+
build:
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
- uses: actions/setup-python@v5
20+
with:
21+
python-version: "3.12"
22+
- name: Build distributions
23+
run: |
24+
pip install build
25+
python -m build
26+
- name: Check distributions
27+
run: |
28+
pip install twine
29+
python -m twine check dist/*
30+
- uses: actions/upload-artifact@v4
31+
with:
32+
name: dist
33+
path: dist/
34+
35+
publish:
36+
needs: build
37+
runs-on: ubuntu-latest
38+
environment: pypi
39+
permissions:
40+
id-token: write
41+
steps:
42+
- uses: actions/download-artifact@v4
43+
with:
44+
name: dist
45+
path: dist/
46+
- name: Publish to PyPI
47+
uses: pypa/gh-action-pypi-publish@release/v1
48+
49+
github-release:
50+
needs: build
51+
runs-on: ubuntu-latest
52+
permissions:
53+
contents: write
54+
steps:
55+
- uses: actions/download-artifact@v4
56+
with:
57+
name: dist
58+
path: dist/
59+
- name: Attach the built files to the GitHub release
60+
env:
61+
GH_TOKEN: ${{ github.token }}
62+
run: |
63+
gh release create "$GITHUB_REF_NAME" dist/* \
64+
--repo "$GITHUB_REPOSITORY" \
65+
--title "$GITHUB_REF_NAME" \
66+
--generate-notes \
67+
|| gh release upload "$GITHUB_REF_NAME" dist/* \
68+
--repo "$GITHUB_REPOSITORY" --clobber

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ Install PyGAD with the following command:
3333
pip install pygad
3434
```
3535

36+
PyGAD's core install is intentionally lightweight (only `numpy` and `cloudpickle`). Some features need extra libraries, which are available as optional extras:
37+
38+
```
39+
# Plotting features (e.g. plot_fitness(), plot_genes()) need matplotlib:
40+
pip install pygad[visualize]
41+
42+
# Training Keras/PyTorch models (pygad.kerasga, pygad.torchga):
43+
pip install pygad[deep_learning]
44+
```
45+
3646
To get started with PyGAD, read the documentation at [Read the Docs](https://pygad.readthedocs.io).
3747

3848
# PyGAD Source Code

RELEASING.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Releasing
2+
3+
Releases are automated. Pushing a version tag builds the package, publishes it to
4+
PyPI, and attaches the built files to a GitHub Release. Nothing is uploaded by
5+
hand.
6+
7+
## Steps
8+
9+
1. Bump the version in `pygad/_version.py`. This is the only place the version
10+
lives.
11+
2. Update the release notes in the docs if you keep them there.
12+
3. Commit and push:
13+
```bash
14+
git add pygad/_version.py
15+
git commit -m "Release 3.6.1"
16+
git push
17+
```
18+
4. Wait for the test workflow (`main.yml`) to pass on that commit.
19+
5. Tag the release and push the tag:
20+
```bash
21+
git tag 3.6.1
22+
git push origin 3.6.1
23+
```
24+
25+
The `release` workflow does the rest: it builds the wheel and sdist, publishes
26+
them to PyPI, and creates a GitHub Release with both files attached. Follow it
27+
with `gh run watch` or the Actions tab.
28+
29+
## Rules
30+
31+
- The tag must match `pygad/_version.py` and is the bare version number with no
32+
`v` prefix, for example `3.6.1`. The tag is what triggers the release.
33+
- Every release needs a new version number. PyPI does not allow re-uploading or
34+
overwriting a version that already exists.
35+
- Do not run `twine upload` or upload files to the GitHub Release by hand. The
36+
tag does both for you.
37+
38+
## Manual fallback
39+
40+
`publish.sh` can build and upload to PyPI from your machine if you ever need it.
41+
42+
## One-time setup (maintainers)
43+
44+
Done once per project. No API token is involved, because PyPI trusted publishing
45+
is tokenless.
46+
47+
- On the PyPI `pygad` project, open Settings, then Publishing, and add a GitHub
48+
publisher: owner `ahmedfgad`, repository `GeneticAlgorithmPython`, workflow
49+
`release.yml`, environment `pypi`.
50+
- In the GitHub repo, open Settings, then Environments, and create an environment
51+
named `pypi`.

publish.sh

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#!/bin/bash
2+
# Publish the `pygad` Python package to PyPI (or TestPyPI).
3+
#
4+
# This is the manual release script. Run it from anywhere, it resolves
5+
# paths relative to its own location and uses whatever Python environment
6+
# is currently active (installing `build` + `twine` into it if they are
7+
# missing).
8+
#
9+
# Pipeline:
10+
# 1. Check build tooling
11+
# 2. Wipe stale dist/ artefacts (confirmation prompt)
12+
# 3. Build sdist + wheel into dist/
13+
# 4. `twine check` the artefacts for README/metadata issues
14+
# 5. Prompt to upload to TestPyPI
15+
# 6. Pause so you can `pip install -i https://test.pypi.org/simple/ pygad`
16+
# in a scratch venv to confirm the release works end-to-end
17+
# 7. Prompt to upload to production PyPI (the irreversible step)
18+
#
19+
# All prompts default to the SAFE answer ('no' for irreversible
20+
# actions) and require a typed 'y' to proceed.
21+
22+
set -euo pipefail
23+
24+
# Colour helpers — silently no-op when stdout isn't a TTY (e.g. piped to
25+
# `tee` or run from a CI runner).
26+
if [ -t 1 ]; then
27+
CYAN='\033[0;36m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
28+
RED='\033[0;31m'; BOLD='\033[1m'; NC='\033[0m'
29+
else
30+
CYAN=''; GREEN=''; YELLOW=''; RED=''; BOLD=''; NC=''
31+
fi
32+
33+
heading() { echo -e "\n${CYAN}${BOLD}== $* ==${NC}"; }
34+
info() { echo -e "${CYAN}$*${NC}"; }
35+
warn() { echo -e "${YELLOW}$*${NC}"; }
36+
error() { echo -e "${RED}$*${NC}" >&2; }
37+
success() { echo -e "${GREEN}$*${NC}"; }
38+
39+
# Default to "no" — caller has to type `y` (case-insensitive) to confirm.
40+
# Used for every destructive / irreversible step.
41+
confirm() {
42+
local prompt="$1"
43+
local answer
44+
read -r -p "$(echo -e "${YELLOW}${prompt} [y/N]:${NC} ")" answer
45+
case "${answer:-}" in
46+
y|Y|yes|YES) return 0 ;;
47+
*) return 1 ;;
48+
esac
49+
}
50+
51+
# Resolve the script's own directory so it works from any cwd.
52+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
53+
54+
# ---- 1. Check build tooling ----
55+
heading "Checking build tooling"
56+
if ! command -v python >/dev/null 2>&1; then
57+
error "No 'python' on PATH. Activate a virtual environment and rerun."
58+
exit 1
59+
fi
60+
info "Active python: $(command -v python)"
61+
info "Active pip: $(command -v pip)"
62+
63+
# Confirm `build` and `twine` are present — install if missing so a
64+
# fresh checkout works without a separate setup step.
65+
if ! python -c "import build" >/dev/null 2>&1 || \
66+
! python -c "import twine" >/dev/null 2>&1; then
67+
warn "Installing missing build tooling (build, twine)..."
68+
pip install --quiet build twine
69+
fi
70+
71+
cd "$SCRIPT_DIR"
72+
73+
# ---- 2. Wipe stale dist/ ----
74+
heading "Cleaning previous artefacts"
75+
if [ -d "dist" ] && [ -n "$(ls -A dist 2>/dev/null)" ]; then
76+
echo "Existing dist/ contents:"
77+
ls -1 dist
78+
if confirm "Delete dist/ before building?"; then
79+
rm -rf dist
80+
success "Removed stale dist/"
81+
else
82+
warn "Keeping existing dist/. Note: twine will refuse to upload duplicates."
83+
fi
84+
else
85+
info "No stale artefacts found."
86+
fi
87+
88+
# ---- 3. Build ----
89+
heading "Building sdist + wheel"
90+
python -m build
91+
ls -1 dist
92+
93+
# ---- 4. twine check ----
94+
heading "Running twine check"
95+
python -m twine check dist/*
96+
97+
# ---- 5. Upload to TestPyPI ----
98+
heading "Upload to TestPyPI"
99+
warn "TestPyPI lives at https://test.pypi.org/ and is the safe place to"
100+
warn "verify the upload before touching production."
101+
if confirm "Upload to TestPyPI now?"; then
102+
python -m twine upload --repository testpypi dist/*
103+
success "Uploaded to TestPyPI."
104+
echo
105+
info "Verify with (in a fresh scratch venv):"
106+
info " pip install --index-url https://test.pypi.org/simple/ \\"
107+
info " --extra-index-url https://pypi.org/simple/ pygad"
108+
echo
109+
read -r -p "Press Enter once the TestPyPI install looks good..."
110+
else
111+
warn "Skipped TestPyPI upload."
112+
fi
113+
114+
# ---- 6. Upload to production PyPI ----
115+
heading "Upload to PRODUCTION PyPI"
116+
warn "This step is IRREVERSIBLE. Once a version is published you cannot"
117+
warn "re-upload the same filename — you'd have to bump the version"
118+
warn "(pyproject.toml, setup.py, and pygad/__init__.py) and rebuild."
119+
warn "Make sure the TestPyPI smoke test passed."
120+
if confirm "Upload to production PyPI now?"; then
121+
python -m twine upload dist/*
122+
success "Uploaded to PyPI: https://pypi.org/project/pygad/"
123+
else
124+
warn "Skipped production upload. Run this script again when ready."
125+
fi
126+
127+
heading "Done"

pygad/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .pygad import * # Relative import.
22

3-
__version__ = "3.6.0"
3+
from ._version import __version__

pygad/_version.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__version__ = "3.6.0"

pyproject.toml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"
99

1010
[project]
1111
name = "pygad"
12-
version = "3.6.0"
12+
dynamic = ["version"]
1313
description = "PyGAD: A Python Library for Building the Genetic Algorithm and Training Machine Learning Algoithms (Keras & PyTorch)."
1414
readme = {file = "README.md", content-type = "text/markdown"}
1515
requires-python = ">=3"
@@ -42,7 +42,6 @@ classifiers = [
4242
keywords = ["genetic algorithm", "GA", "optimization", "evolutionary algorithm", "natural evolution", "pygad", "machine learning", "deep learning", "neural networks", "tensorflow", "keras", "pytorch"]
4343
dependencies = [
4444
"numpy",
45-
"matplotlib",
4645
"cloudpickle",
4746
]
4847

@@ -58,7 +57,11 @@ dependencies = [
5857

5958
[project.optional-dependencies]
6059
deep_learning = ["keras", "tensorflow", "torch"]
60+
visualize = ["matplotlib"]
6161

6262
# PyTest Configuration. Later, PyTest will support the [tool.pytest] table.
6363
[tool.pytest.ini_options]
64-
testpaths = ["tests"]
64+
testpaths = ["tests"]
65+
66+
[tool.setuptools.dynamic]
67+
version = { attr = "pygad._version.__version__" }

requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
numpy
2-
matplotlib
32
cloudpickle

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55

66
setuptools.setup(
77
name="pygad",
8-
version="3.6.0",
98
author="Ahmed Fawzy Gad",
10-
install_requires=["numpy", "matplotlib", "cloudpickle",],
9+
install_requires=["numpy", "cloudpickle",],
1110
extras_require={
1211
"deep_learning": ["keras", "tensorflow", "torch"],
12+
"visualize": ["matplotlib"],
1313
},
1414
author_email="ahmed.f.gad@gmail.com",
1515

0 commit comments

Comments
 (0)