Repository navigation
Provider schemas are now pydantic (v2) models instead of JSON Schema dicts
validated with jsonschema. The public library API (notify(), get_notifier(), Provider.notify(),
Response, exceptions, environment variables and the logging handler) is unchanged. See the migration guide
for details and for how to port custom providers.
Breaking changes
- Dropped support for Python 3.8 and 3.9. Python 3.10+ is required. Python 3.10 to 3.15 are supported and tested.
- Replaced the
jsonschemadependency withpydantic>=2.7,<3. - Custom providers declare their arguments with a
schema_model(anotifiers.models.SchemaModelsubclass)
instead of the_requiredand_schemadicts. Providers that still define_required/_schemacan't be
instantiated. See porting custom providers. - Removed
notifiers.utils.schema(one_or_more,list_to_commasand theformat_checkerwith itsis_*
format checks).list_to_commasmoved tonotifiers.utils.helpers, the format checks are replaced by the types in
notifiers.models. - Removed
SchemaResource.validatorandSchemaResource._validate_schema().SchemaErroris no longer raised. SchemaResource._validate_data()returns the validated data instead ofNone.- Removed the Gitter provider: the Gitter API (
api.gitter.im) no longer exists, Gitter moved to Matrix. - Removed the PopcornNotify provider: the service is down.
- Removed the
importlib-metadatadependency: entry point providers are loaded with the standard library
importlib.metadata.
Changes
- Argument values are converted to their declared type when possible, e.g.
port="587"becomes587. This makes
environment variables usable for integer and boolean arguments. - Arguments with an alternative spelling (
from/from_,type/type_) can also be set via environment
variables using either spelling. - Validation error messages for anything other than missing or unknown arguments and provider specific rules use pydantic's
wording, e.g.'priority': Input should be less than or equal to 2. - Added
BadArguments.errors: a list of all validation errors (loc,msgandtypefor each). provider.schemareturns pydantic'smodel_json_schema(by_alias=True)of the schema model, and
provider.argumentsitsproperties. The layout follows pydantic (descriptionandtitleper argument,
optional arguments asanyOfwithnull, nested models under$defs).provider.requiredlists the required arguments only. Conditional requirements (anyOf/oneOf/
dependencies) are enforced on validation but no longer listed.provider.defaultsis derived from the defaults declared on the schema model.- Schema model fields are snake_case. Arguments named differently by the remote API keep that name as an alias, so both
spellings are accepted (e.g. JoindeviceId/device_id,smsnumber/sms_number; DingTalkmsgtype/
msg_type,atMobiles/at_mobiles). - Added
notifiers.models: theSchemaModelbase class,OneOrMore/one_or_moreand field types
(Email,Url,Hostname,Port,Timestamp,ISO8601Datetime,RFC2822Datetime,DateString,
AsciiStr,FilePath,E164). - Importing notifiers (or any non email provider) no longer performs a hostname lookup via
socket.getfqdn(), which
could block for seconds depending on the system resolver
(#483). The default emailfromaddress
(notifiers@<hostname>) is computed only when sending an email without afromaddress, via
notifiers.providers.email.default_from(). Replaces theDEFAULT_FROMconstant.
CLI
-
notifyand resource command options are generated from the provider's schema model. Every option and flag that
existed before 2.0 keeps its name and type. Additions:- Arguments named in camelCase by the remote API also accept a
--kebab-casespelling, e.g. Join--deviceIdor
--device-id,--smsnumberor--sms-number. - List arguments can be passed several times: Notify
--tags, Statuspage--component-ids. - Options for arguments without a description before 2.0 have help text, e.g. Telegram
--chat-id.
- Arguments named in camelCase by the remote API also accept a
-
Removed the duplicate
--type-option of Pushbullet and Zulip, use--type. -
Boolean flags that aren't passed don't send a value, so the provider's default applies, like in the library. Before 2.0
they were sent asFalse: e.g.notifiers gmail notifyconnected without TLS and without logging in unless
--tlsand--loginwere passed. -
Provider command groups are built on demand, only for the invoked provider, which makes CLI startup faster.
notifiers_cli.core.provider_group_factory()still builds all of them up front, but isn't needed to run the CLI. -
Provider documentation lists each provider's arguments in a table generated from its schema model.
Docker
- The image is published to the GitHub Container Registry as
ghcr.io/liiight/notifiers, forlinux/amd64and
linux/arm64, by a GitHub Actions workflow: release tags publish<version>,<major>.<minor>andlatest,
mainpublishesmain. Pull requests build and smoke test the image. - The image runs: it failed to start with
unable to find user user, and thenotifierscommand wasn't onPATH. - Rebuilt as a two stage build: installs the locked dependencies with uv, then copies only the virtual environment into a
python:3.14-alpineimage running as anotifiersuser. The image is about 25 MB instead of about 100 MB, without
uv, the source tree or development files.docker runwithout arguments shows the CLI help.
Fixes
- DingTalk: the schema could never validate and sending failed. Rewritten with nested models. Send a text message with
message='...'or any message type withmsg_data. get_notifier()failed for providers registered only via thenotifiersentry point.- Email / Gmail / iCloud: a single attachment path (
attachments='/path/to/file') failed with
Is a directory: '/'. - Email / Gmail / iCloud: when the SMTP connection was closed (e.g. the server replied 421 after an idle timeout, or
dropped the connection), every following email failed withplease run connect() first. The cached connection is
checked withNOOPbefore reuse and reopened when needed
(#478, by @broper2). - Documentation: the custom provider guide used APIs that don't exist (
notifiers.utils.schema.one_of,_notify).
Development
- Tests: added
tests/test_schema_types.py(replacestests/test_json_schema.py),
tests/providers/test_generic_provider_tests.py(checks every provider and resource, including snake_case field
names) andtests/providers/test_email_offline.py(email, Gmail and iCloud end to end against a fake SMTP server). - CI runs the offline test suite (
-m "not online") on every Python version (fail-fast: false), plus a ruff lint and
format check. Tests that need network access are markedonline. - Python 3.14 and 3.15 are in the CI test matrix and the package classifiers. The online tests, the docs build (CI and
Read the Docs) and the Docker image use Python 3.14. - CI runs the online tests (real provider APIs, with the repository secrets) in a separate required
onlinejob, on
push, on manual dispatch and weekly.--run-skipped-online(orNOTIFIERS_RUN_SKIPPED_ONLINE=1, a manual dispatch
input) also runs online tests marked as skipped, to check whether an unavailable test account works again. - Online tests of providers whose test account is unavailable are skipped with the reason: Pushover
(#488), Statuspage
(#489), Gmail sending
(#490), iCloud, DingTalk, VictorOps, Mailgun, Join and Notify. - Pushbullet online tests run again.
- ruff 0.16.10 in pre-commit and in the dev dependency group.
- Offline tests run without
NOTIFIERS_*credentials in the environment, so they behave the same locally and in CI. - Tests: added
tests/test_cli_options.py(CLI option generation, email / Gmail / iCloud via the CLI against a fake SMTP
server, on demand provider groups). - Docs: added the
provider-argumentsSphinx directive (source/_ext/provider_arguments.py). - Docs: added Releasing, the release steps for maintainers, including the Homebrew formula update for 2.0.0.
- Homebrew: the formula is kept in
packaging/homebrew/notifiers.rb. A CI workflow installs this repository's code with
it on macOS and Linux (brew install --build-from-source,brew test,brew audit --strict), on pull requests,
onmainand weekly, and checks that its resources match the runtime dependencies. - Tests: entry point provider discovery is tested with a fake installed plugin. The
magic_mock_providerfixture no
longer leaks its patchedMockProviderinto other tests. - Docs: the Sphinx dependencies are in a
docsdependency group, the Read the Docs build installs it with uv, and CI
builds the docs with warnings as errors, like Read the Docs. Removed the unusedsphinx-autodoc-annotationextension,
the missing static and template paths, and the alabaster sidebar settings. Updated the intersphinx URLs. The PDF is
built with xelatex. - The statuspage incident cleanup runs for online tests only, and tolerates API errors.
- Coverage: CI measures the
notifiersandnotifiers_clipackages only and uploadscoverage.xmlwith
codecov/codecov-action@v5, one flag per Python version. A failed upload fails the job instead of passing silently
(#452). Removed the deprecatedcodecovpackage from the dev
dependencies. - Coverage: added
codecov.yml(#53). Codecov reports once all six
Python versions uploaded, the project check allows a 1% drop, the patch check is informational, and the pull request
comment is posted only when coverage changes.
Full documentation: https://notifiers.readthedocs.io/en/stable/