Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ All notable changes to this project are documented in this file.
### Fixed

- **TensorFlow/Keras 3 compatibility**: Teachable Machine's exported `.h5` models embed a legacy `DepthwiseConv2D` layer config (`'groups': 1`) that current Keras (Keras 3, bundled by default since TensorFlow 2.16) rejects during deserialization, causing model loading to fail with `TypeError`/`ValueError: Unrecognized keyword arguments passed to DepthwiseConv2D: {'groups': 1}`. `TeachableMachine` now loads models with a compatibility shim that discards the unused key, so exported models work on up-to-date TensorFlow/Keras installs without pinning an old TensorFlow version. Fixes [#2](https://github.com/MeqdadDev/teachable-machine/issues/2).
- **Nested-Sequential `.h5` exports fail to load under Keras 3**: some Teachable Machine `.h5` exports save the model as a `Sequential` wrapping nested `Sequential`/`Functional` submodels. Keras 3's legacy H5 loader mis-rebuilds that shape (a `Functional` submodel's single output comes back as a length-1 list, which the next layer misreads as two inputs), previously surfacing as a misleading `FileNotFoundError: Model file not found`. `TeachableMachine` now falls back to a manual reconstruction that flattens the nested wrappers, rebuilds the graph via the Functional API, and restores weights by matching each layer's name against the saved weight paths.
- **Prediction annotation crashes on Windows / recent Pillow**: `show_prediction_on_image()` used `ImageFont.truetype("DejaVuSans-Bold.ttf", ...)`, not resolvable by bare name on Windows (`OSError`), and `ImageDraw.textsize()`, removed in Pillow 10+. Both are now handled: a `ImageFont.load_default()` fallback when the named font can't be found, and `textbbox()` in place of `textsize()`. Also fixed the highlight box behind the prediction label clipping text descenders (e.g. "g", "%") by measuring the box at the actual draw position instead of reusing an origin-measured extent.

### Changed

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ Teachable Machine's exported `.h5` models embed a legacy `DepthwiseConv2D` layer
TypeError: Unrecognized keyword arguments passed to DepthwiseConv2D: {'groups': 1}
```

Since `v1.3.0`, this package patches the model loader so exported models load correctly on up-to-date TensorFlow/Keras installs, with no need to pin an old TensorFlow version. See [issue #2](https://github.com/MeqdadDev/teachable-machine/issues/2) for background.
Some exports also save the model as a `Sequential` wrapping nested `Sequential`/`Functional` submodels, a shape Keras 3's legacy H5 loader mis-rebuilds, which previously surfaced as a misleading `FileNotFoundError: Model file not found`.

Since `v1.3.0`, this package patches the model loader to handle both cases, so exported models load correctly on up-to-date TensorFlow/Keras installs, with no need to pin an old TensorFlow version. It also fixes prediction-annotation crashes on Windows / recent Pillow versions (`show_prediction_on_image`). See [issue #2](https://github.com/MeqdadDev/teachable-machine/issues/2) and the [changelog](https://meqdaddev.github.io/teachable-machine/changelog/) for background.

## Requirements

Expand Down
2 changes: 2 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ All notable changes to this project are documented in this file.
### Fixed

- **TensorFlow/Keras 3 compatibility**: Teachable Machine's exported `.h5` models embed a legacy `DepthwiseConv2D` layer config (`'groups': 1`) that current Keras (Keras 3, bundled by default since TensorFlow 2.16) rejects during deserialization, causing model loading to fail with `TypeError`/`ValueError: Unrecognized keyword arguments passed to DepthwiseConv2D: {'groups': 1}`. `TeachableMachine` now loads models with a compatibility shim that discards the unused key, so exported models work on up-to-date TensorFlow/Keras installs without pinning an old TensorFlow version. Fixes [#2](https://github.com/MeqdadDev/teachable-machine/issues/2).
- **Nested-Sequential `.h5` exports fail to load under Keras 3**: some Teachable Machine `.h5` exports save the model as a `Sequential` wrapping nested `Sequential`/`Functional` submodels. Keras 3's legacy H5 loader mis-rebuilds that shape (a `Functional` submodel's single output comes back as a length-1 list, which the next layer misreads as two inputs), previously surfacing as a misleading `FileNotFoundError: Model file not found`. `TeachableMachine` now falls back to a manual reconstruction that flattens the nested wrappers, rebuilds the graph via the Functional API, and restores weights by matching each layer's name against the saved weight paths.
- **Prediction annotation crashes on Windows / recent Pillow**: `show_prediction_on_image()` used `ImageFont.truetype("DejaVuSans-Bold.ttf", ...)`, not resolvable by bare name on Windows (`OSError`), and `ImageDraw.textsize()`, removed in Pillow 10+. Both are now handled: a `ImageFont.load_default()` fallback when the named font can't be found, and `textbbox()` in place of `textsize()`. Also fixed the highlight box behind the prediction label clipping text descenders (e.g. "g", "%") by measuring the box at the actual draw position instead of reusing an origin-measured extent.

### Changed

Expand Down
5 changes: 5 additions & 0 deletions docs/contribution.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ Pull requests are welcome. For major changes, please open an issue first to disc
For new features, please make sure you've tested your code in real environment.
And to avoid duplications, please take a sneak peek on the project structure and practices before making your PR.

If your PR includes a user-facing fix or feature, add an entry to
[`CHANGELOG.md`](https://github.com/MeqdadDev/teachable-machine/blob/main/CHANGELOG.md)
as part of it — see [Releasing](releasing.md) for the versioning and
changelog conventions this project follows.

For more details and questions, contact me via links below.

### 🔗 Contact me
Expand Down
12 changes: 8 additions & 4 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ Source Code is published on [GitHub](https://github.com/MeqdadDev/teachable-mach

1. [How-To Guide](how-to-guide.md)
2. [Requirements](requirements.md)
2. [Code Examples](codeExamples.md)
3. [Explanation](explanation.md)
4. [Contributing](contribution.md)
3. [Code Examples](codeExamples.md)
4. [Explanation](explanation.md)
5. [Changelog](changelog.md)
6. [Contributing](contribution.md)
7. [Releasing](releasing.md)

## Supported Classifiers

Expand All @@ -37,7 +39,9 @@ Teachable Machine's exported `.h5` models embed a legacy `DepthwiseConv2D` layer
TypeError: Unrecognized keyword arguments passed to DepthwiseConv2D: {'groups': 1}
```

Since `v1.3.0`, this package patches the model loader so exported models load correctly on up-to-date TensorFlow/Keras installs, with no need to pin an old TensorFlow version. See [issue #2](https://github.com/MeqdadDev/teachable-machine/issues/2) for background.
Some exports also save the model as a `Sequential` wrapping nested `Sequential`/`Functional` submodels, a shape Keras 3's legacy H5 loader mis-rebuilds, which previously surfaced as a misleading `FileNotFoundError: Model file not found`.

Since `v1.3.0`, this package patches the model loader to handle both cases, so exported models load correctly on up-to-date TensorFlow/Keras installs, with no need to pin an old TensorFlow version. It also fixes prediction-annotation crashes on Windows / recent Pillow versions (`show_prediction_on_image`). See [issue #2](https://github.com/MeqdadDev/teachable-machine/issues/2) and the [changelog](changelog.md) for background.

## Links

Expand Down
84 changes: 84 additions & 0 deletions docs/releasing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
## Versioning

This project follows [Semantic Versioning](https://semver.org/) (`MAJOR.MINOR.PATCH`):

- **MAJOR** — a breaking change: a public method/parameter is removed or its
behavior changes incompatibly, the minimum supported Python version is
raised, or a dependency's required version range changes in a way that
drops support for environments that previously worked.
- **MINOR** — a backwards-compatible addition: a new method, parameter, or
supported model/classifier type.
- **PATCH** — a backwards-compatible bug fix: a crash, incorrect result, or
compatibility break (e.g. with a newer TensorFlow/Keras/Pillow release) is
fixed without changing the public API.

When a change could plausibly be read as more than one of these (e.g. a bug
fix that also happens to add a new optional parameter), pick the highest
level that applies.

### Where the version lives

The version is defined in exactly one place: `__version__` in
[`src/__init__.py`](https://github.com/MeqdadDev/teachable-machine/blob/main/src/__init__.py).
`setup.py` reads it from there at build time — don't hardcode a version
in `setup.py` or anywhere else, and don't bump it in more than one file.
(This package previously had the version duplicated between `setup.py`
and `src/__init__.py`, and the two drifted out of sync — `setup.py` said
`1.3.0` while `src/__init__.py` still said `1.2`, a version that was never
actually shipped as `v1.3.0` in a release. Keeping a single source of
truth is what this rule exists to prevent.)

## Changelog

Every user-facing change (fix, feature, or breaking change) is recorded in
[`CHANGELOG.md`](https://github.com/MeqdadDev/teachable-machine/blob/main/CHANGELOG.md)
at the repo root, mirrored verbatim to `docs/changelog.md` (the two files
must stay identical — that's what the docs site's Changelog page renders).

- Add the entry **in the same PR** as the change, under the version header
for the *next* release (create that header, e.g. `## [1.4.0]`, if the PR
is the first change since the last release).
- Group entries under `### Added`, `### Changed`, `### Fixed`, or
`### Removed`, following [Keep a Changelog](https://keepachangelog.com/).
- Link the relevant GitHub issue/PR where useful.
- Once that version is actually released (see below), add the release date
next to its header, e.g. `## [1.3.0] - 2026-09-12`.

## Release process

1. **Confirm the changelog is accurate.** The unreleased version's section
at the top of `CHANGELOG.md` (and `docs/changelog.md`) should already
describe everything merged since the last release, since each PR added
its own entry — read through it and fix anything missing before
continuing.
2. **Confirm the version bump matches semver** (see above) given everything
in that section. Adjust the header if the accumulated changes turned out
to need a higher bump than originally expected (e.g. a breaking change
landed alongside what were otherwise just fixes).
3. **Bump `__version__`** in `src/__init__.py` to that version.
4. **Add the release date** to the changelog header, e.g.
`## [1.4.0] - 2026-09-12`.
5. Commit both (e.g. `Release v1.4.0`) directly to `main` (or via a small
PR, same as any other change) once everything above is in place.
6. **Tag and push:**
```bash
git tag v1.4.0
git push origin v1.4.0
```
7. **Create a GitHub Release from that tag** (Releases -> Draft a new
release -> pick the tag). Use the changelog section for that version as
the release notes. Publishing the release triggers
[`publish.yml`](https://github.com/MeqdadDev/teachable-machine/blob/main/.github/workflows/publish.yml),
which builds the package and publishes it to PyPI via
[Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (no
token needed) — this only works once a trusted publisher is registered
for this repo/workflow on the [PyPI project's publishing settings](https://pypi.org/manage/project/teachable_machine/settings/publishing/).
8. **Verify:**
- The [PyPI project page](https://pypi.org/project/teachable-machine/)
shows the new version.
- `pip install --upgrade teachable-machine` picks it up.
- The [docs site](https://meqdaddev.github.io/teachable-machine/) — which
[redeploys automatically](https://github.com/MeqdadDev/teachable-machine/blob/main/.github/workflows/docs.yml)
on every push to `main` that touches `docs/`, `src/`, or `mkdocs.yml` —
reflects the release (Changelog page, and any docstring changes via
`mkdocstrings` on the Explanation page).
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ nav:
- Explanation: explanation.md
- Changelog: changelog.md
- Contributing: contribution.md
- Releasing: releasing.md
19 changes: 18 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
import re
from pathlib import Path

from setuptools import setup

with open("README.md", "r") as fh:
long_description = fh.read()


def _read_version():
"""
Read `__version__` from src/__init__.py so the package version has a
single source of truth instead of being duplicated (and drifting out
of sync, as it previously did) between here and there.
"""
init_contents = (Path(__file__).parent / "src" / "__init__.py").read_text()
match = re.search(r'^__version__\s*=\s*["\']([^"\']+)["\']', init_contents, re.M)
if not match:
raise RuntimeError("Unable to find __version__ in src/__init__.py")
return match.group(1)


setup(
name="teachable_machine",
version="1.3.0",
version=_read_version(),
description="A Python package designed to simplify the integration of exported models from Google's Teachable Machine platform into various environments. \
This tool was specifically crafted to work seamlessly with Teachable Machine, making it easier to implement and use your trained models.",
py_modules=["teachable_machine"],
Expand Down
2 changes: 1 addition & 1 deletion src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


__author__ = "MeqdadDev"
__version__ = "1.2"
__version__ = "1.3.0"

__doc__ = """
Teachable Machine
Expand Down
Loading