Aller au contenu

Utilities Reference

Ce contenu n’est pas encore disponible dans votre langue.

Apprise includes a set of utility helpers used across plugins and supported integrations. Plugin authors should prefer these helpers over ad hoc parsing and formatting because they provide consistent behaviour, edge case handling, and safer logging.

This page focuses on the helpers you are most likely to use when writing or maintaining plugins. Additional utilities exist, but they are intentionally not covered here.

Helpers are exposed through apprise.utils (recommended for plugin authors), and are implemented in the apprise.utils.* modules.

# Parsing and validation
from apprise.utils.parse import (
parse_bool,
parse_call_sign,
parse_emails,
parse_list,
parse_phone_no,
parse_url,
parse_urls,
is_call_sign,
is_email,
is_hostname,
is_ipaddr,
is_phone_no,
is_uuid,
validate_regex,
)
# Safer debug logging
from apprise.utils.sanitize import sanitize_payload
# Encoding helpers
from apprise.utils.base64 import (
base64_urlencode,
base64_urldecode,
encode_b64_dict,
decode_b64_dict,
)

A practical way to think about the parsing utilities is:

  • parse_*() functions split and normalize lists of inputs, often forgiving. Many accept a store_unparseable option so you can log which values were rejected later.
  • is_*() functions validate a single candidate value. They return False on failure, otherwise they return normalized data that you can safely store.
  • validate_regex() is a strict helper used to validate and optionally format a value using a regex match.

Most plugins use both styles together. For example, an SMS plugin can call parse_phone_no() to break up a user-supplied target list, then call is_phone_no() on each entry to validate and normalize it.

Use this in parse_url() or parse_native_url() implementations to convert a user-provided URL into a normalized structure. It handles schema detection, quoting, user and password extraction, host verification, port parsing, and query-string parsing.

Typical usage:

from apprise.utils.parse import parse_url
results = parse_url(url, verify_host=False)
if not results:
return None
schema = results["schema"]
host = results.get("host")
qsd = results.get("qsd", {}) # query-string dictionary
  • verify_host
    When True, parse_url() validates the host and returns None if it is missing or invalid. This is a good default for network targets. When False, host validation is relaxed, which is useful for plugins that treat the host as an identifier or do not require a host.

  • default_schema
    Used when a user omits the schema:// portion.

  • simple
    When True, returns a smaller result structure. Most plugins should use the default simple=False.

  • plus_to_space
    Controls whether + in the query string becomes a space. Apprise defaults this to False because + is common in tokens and passwords.

The returned dictionary varies by input, but typically includes:

  • schema, and optionally host and port
  • user and password when provided
  • fullpath, and sometimes path and query
  • Parsed query-string data, via qsd

Converts common boolean representations into a Python bool. This is the preferred way to parse flags from URL query strings or configuration values.

from apprise.utils.parse import parse_bool
include_image = parse_bool(qsd.get("image"), default=False)
batch = parse_bool(qsd.get("batch"), default=True)
  • False-like: "0", "no", "off", "false", "deny", "disable", "never"
  • True-like: "1", "yes", "on", "true", "allow", "enable"

If a string cannot be interpreted, default is returned. For non-string values, bool(value) is used.

Sometimes you want different behaviour for:

  • not provided at all (unset)
  • explicitly enabled
  • explicitly disabled

A common case is a feature that is auto-enabled only when another setting is present, unless the user explicitly disables it.

raw = qsd.get("discovery") # None when not provided
if raw is None:
# Unset: choose a default based on other configuration
discovery = True if (self.secure and self.host) else False
else:
# Explicitly set: honor user intent
discovery = parse_bool(raw, default=False)

Breaks string and list-like inputs into a single list. It accepts multiple inputs and merges them. By default it returns a sorted, unique list.

from apprise.utils.parse import parse_list
tags = parse_list(qsd.get("tag"), cast=str)
targets = parse_list(qsd.get("to"), cast=str, allow_whitespace=False)
  • cast converts values before parsing when possible (useful when values may be numeric).
  • allow_whitespace controls whether whitespace is treated as a delimiter.
  • sort controls whether the result is normalized into a unique sorted list (True), or returned in parsed order (False).

These helpers are commonly used in email-like integrations and anywhere a user can pass multiple recipients.

  • parse_emails() extracts multiple candidate emails from strings and recursively walks through tuples, lists, and sets.
  • is_email() validates a single email and returns a structured result.
from apprise.utils.parse import parse_emails, is_email
recipients = []
for candidate in parse_emails(qsd.get("to")):
result = is_email(candidate)
if not result:
self.logger.warning("Dropped invalid email (%s) specified.", candidate)
continue
recipients.append(result["full_email"])

Tip: is_email() returns a dictionary (not just a boolean). When present, you can use fields like name, domain, and full_email to build a canonical representation and keep name mappings for later.

Extracts URLs from strings or list-like input and can preserve unparseable entries to aid error reporting.

from apprise.utils.parse import parse_urls
endpoints = parse_urls(qsd.get("endpoint"))

If no valid items are detected and store_unparseable=True, the input is split on common delimiters and returned anyway. This allows you to log which values were rejected, rather than silently discarding everything.

A practical plugin pattern:

emails = parse_emails(qsd.get("to"), store_unparseable=True)
valid = []
invalid = []
for entry in emails:
if is_email(entry):
valid.append(entry)
else:
invalid.append(entry)
for entry in invalid:
self.logger.warning("Ignoring invalid email: %s", entry)

Used by SMS, voice, and telecom-style plugins.

parse_phone_no() splits a target list into candidate entries, then is_phone_no() validates a single entry and returns False or a dictionary.

from apprise.utils.parse import parse_phone_no, is_phone_no
valid = []
invalid = []
for candidate in parse_phone_no(targets):
result = is_phone_no(candidate)
if result:
valid.append(f"+{result['full']}")
else:
invalid.append(candidate)
for candidate in invalid:
self.logger.warning("Dropped invalid phone number (%s) specified.", candidate)

is_phone_no() result dictionary commonly includes:

  • full (digits only)
  • pretty (human-friendly formatting, when available)
  • country, area, line (may be empty)

Important constraints:

  • The digit count must be between min_len (default 10) and 14 inclusive.
  • Common separators are accepted on input; you should store the normalized form.

Used by APRS and ham radio-style integrations.

parse_call_sign() splits a callsign list, then is_call_sign() validates a single callsign and returns False or a dictionary.

from apprise.utils.parse import parse_call_sign, is_call_sign
targets = []
for candidate in parse_call_sign(qsd.get("to")):
result = is_call_sign(candidate)
if not result:
self.logger.warning("Dropping invalid call sign (%s).", candidate)
continue
targets.append(result["callsign"].upper())

is_call_sign() validates a single callsign and returns False or a dictionary:

  • callsign (uppercased)
  • ssid (string, may be empty)

A practical pattern mirrors phone and email handling:

  • Parse a list with parse_call_sign(...)
  • Validate each entry with is_call_sign(...)
  • Warn on invalid values, do not fail the entire configuration unless the service requires at least one valid target

These helpers are frequently used by plugins to validate inputs early and produce clear error messages.

  • is_hostname(hostname, ipv4=True, ipv6=True, underscore=True)
    Validates hostnames and optionally IP addresses. It supports underscores when underscore=True to accommodate practical Docker and local naming conventions.

  • is_ipaddr(addr, ipv4=True, ipv6=True)
    Validates IPv4 and IPv6. For IPv6, it returns the address enclosed in brackets ([addr]) to align with URL formatting expectations.

  • is_uuid(value)
    Validates UUID strings.

These helpers return False when invalid. When valid, they return a normalized string (not a boolean).

Validates a value against a regular expression. On success it returns the (cleaned) value, otherwise it returns None.

from apprise.utils.parse import validate_regex
token = validate_regex(qsd.get("token"), r"^[A-Z0-9]{32}$", flags="i")
if not token:
self.logger.warning("An invalid token was specified")
return None

If your plugin defines a template token regex (as most plugins do), you can reuse it directly when validating inputs, which keeps the definition in one place.

Example pattern:

self.token = validate_regex(
token, *self.template_tokens["token"]["regex"]
)

This relies on template_tokens["token"]["regex"] being a tuple in the form (regex, flags).

  • Regexes are cached internally after compilation, so repeated validations are inexpensive.
  • flags may be passed as an integer, or as a string of flag characters, for example "imx".
  • When fmt is provided, named capture groups can be reassembled into a normalized string.

Example with formatting:

value = validate_regex(
value="prefix-123",
regex=r"^(?P<prefix>[a-z]+)-(?P<id>[0-9]+)$",
flags="i",
fmt="{prefix}:{id}",
)
# value is now "prefix:123" (or None if no match)

Use this helper before logging request payloads or response details, especially when the structure may include attachments, large encoded blobs, or deep nesting. It reduces log pollution and avoids leaking sensitive or very large values, while still preserving enough structure to troubleshoot.

from apprise.utils.sanitize import sanitize_payload
self.logger.debug("payload=%s", sanitize_payload(payload))

You do not need to sanitize every debug log. sanitize_payload() is most useful when:

  • a payload might contain attachments (base64, bytes, file metadata)
  • a response might contain unexpectedly large objects
  • you want safe previews without copying large values into logs

If your debug log could be heavy, guard the call so it only runs when debug logging is enabled.

import logging
from apprise.utils.sanitize import sanitize_payload
if self.logger.isEnabledFor(logging.DEBUG):
self.logger.debug("Payload: %s", sanitize_payload(payload))
  • Leaves primitives unchanged (e.g., None, booleans, numbers).
  • Summarizes long strings with a compact marker and head and tail previews.
  • Summarizes bytes with a bounded sha256 digest marker.
  • Walks nested dictionaries and sequences safely.
  • Detects recursion and prevents infinite traversal.
  • Applies depth and item limits to prevent excessively large logs.

Practical pattern:

  1. Build your request payload normally.
  2. If debug is enabled, log sanitize_payload(payload).
  3. Send the original payload to the upstream service.

Some environments benefit from stricter bounds, for example to prevent large attachments or Base64-like blobs from entering logs.

from apprise.utils.sanitize import SanitizeOptions, sanitize_payload
opts = SanitizeOptions(
max_str_len=64,
preview=8,
max_depth=4,
max_items=250,
)
self.logger.debug("payload=%s", sanitize_payload(payload, options=opts))

When troubleshooting, avoid raising these limits globally. A safer approach is to temporarily adjust them locally around a specific debug log statement.

URL-safe Base64 helpers for working with bytes.

from apprise.utils.base64 import base64_urlencode, base64_urldecode
encoded = base64_urlencode(b"abc") # "YWJj"
decoded = base64_urldecode(encoded) # b"abc"

These helpers are strict about input types. They return None for unsupported inputs.

Dictionary helpers used when an API expects Base64-wrapped payload components, often for binary-safe transport. Values that can be JSON-encoded are converted to strings and wrapped with a b64: prefix.

from apprise.utils.base64 import encode_b64_dict, decode_b64_dict
original = {"int": 1, "float": 2.3}
encoded, needs_decoding = encode_b64_dict(original)
# encoded == {"int": "b64:MQ==", "float": "b64:Mi4z"}
# needs_decoding is True
decoded = decode_b64_dict(encoded)
# decoded == original

If JSON encoding fails for a value, the helper falls back to string conversion and will indicate that decoding is not required.

Questions or Feedback?

Documentation

Notice a typo or an error? Report it or contribute a fix .

Technical Issues

Having trouble with the code? Open an issue on GitHub:

Made with love from Canada