diff --git a/docs/_static/table-formatting.js b/docs/_static/table-formatting.js new file mode 100644 index 0000000..c3f225f --- /dev/null +++ b/docs/_static/table-formatting.js @@ -0,0 +1,40 @@ +/** + * Return the first sibling of el that matches CSS selector, or null if no matches. + * @param {HTMLElement} el + * @param {string} selector + * @returns {HTMLElement|null} + */ +function nextSiblingMatching(el, selector) { + while (el && el.nextElementSibling) { + el = el.nextElementSibling; + if (el.matches(selector)) { + return el; + } + } + return null; +} + +/** + * Convert runs of empty elements to a colspan on the first . + */ +function collapseEmptyTableCells() { + document.querySelectorAll(".rst-content tr:has(td:empty)").forEach((tr) => { + for ( + let spanStart = tr.querySelector("td"); + spanStart; + spanStart = nextSiblingMatching(spanStart, "td") + ) { + let emptyCell; + while ((emptyCell = nextSiblingMatching(spanStart, "td:empty"))) { + emptyCell.remove(); + spanStart.colSpan++; + } + } + }); +} + +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", collapseEmptyTableCells); +} else { + collapseEmptyTableCells(); +} diff --git a/docs/conf.py b/docs/conf.py index 820249d..e1d575e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -279,6 +279,7 @@ def setup(app): anymail_config_js = (DOCS_PATH / "_static/anymail-config.js").read_text() app.add_js_file(None, body=anymail_config_js) app.add_js_file("version-alert.js", **{"async": "async"}) + app.add_js_file("table-formatting.js", **{"async": "async"}) app.add_js_file("https://unpkg.com/rate-the-docs", **{"async": "async"}) # Django-specific roles, from diff --git a/docs/esps/esp-feature-matrix.csv b/docs/esps/esp-feature-matrix.csv new file mode 100644 index 0000000..affd044 --- /dev/null +++ b/docs/esps/esp-feature-matrix.csv @@ -0,0 +1,19 @@ +Email Service Provider,:ref:`amazon-ses-backend`,:ref:`brevo-backend`,:ref:`mailersend-backend`,:ref:`mailgun-backend`,:ref:`mailjet-backend`,:ref:`mandrill-backend`,:ref:`postal-backend`,:ref:`postmark-backend`,:ref:`resend-backend`,:ref:`sendgrid-backend`,:ref:`sparkpost-backend` +.. rubric:: :ref:`Anymail send options `,,,,,,,,,,, +:attr:`~AnymailMessage.envelope_sender`,Yes,No,No,Domain only,Yes,Domain only,Yes,No,No,No,Yes +:attr:`~AnymailMessage.metadata`,Yes,Yes,No,Yes,Yes,Yes,No,Yes,Yes,Yes,Yes +:attr:`~AnymailMessage.merge_metadata`,No,No,No,Yes,Yes,Yes,No,Yes,No,Yes,Yes +:attr:`~AnymailMessage.send_at`,No,Yes,Yes,Yes,No,Yes,No,No,No,Yes,Yes +:attr:`~AnymailMessage.tags`,Yes,Yes,Yes,Yes,Max 1 tag,Yes,Max 1 tag,Max 1 tag,Yes,Yes,Max 1 tag +:attr:`~AnymailMessage.track_clicks`,No,No,Yes,Yes,Yes,Yes,No,Yes,No,Yes,Yes +:attr:`~AnymailMessage.track_opens`,No,No,Yes,Yes,Yes,Yes,No,Yes,No,Yes,Yes +:ref:`amp-email`,Yes,No,No,Yes,No,No,No,No,No,Yes,Yes +.. rubric:: :ref:`templates-and-merge`,,,,,,,,,,, +:attr:`~AnymailMessage.template_id`,Yes,Yes,Yes,Yes,Yes,Yes,No,Yes,No,Yes,Yes +:attr:`~AnymailMessage.merge_data`,Yes,No,Yes,Yes,Yes,Yes,No,Yes,No,Yes,Yes +:attr:`~AnymailMessage.merge_global_data`,Yes,Yes,(emulated),(emulated),Yes,Yes,No,Yes,No,Yes,Yes +.. rubric:: :ref:`Status ` and :ref:`event tracking `,,,,,,,,,,, +:attr:`~AnymailMessage.anymail_status`,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes +:class:`~anymail.signals.AnymailTrackingEvent` from webhooks,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes +.. rubric:: :ref:`Inbound handling `,,,,,,,,,,, +:class:`~anymail.signals.AnymailInboundEvent` from webhooks,Yes,Yes,Yes,Yes,Yes,Yes,Yes,Yes,No,Yes,Yes diff --git a/docs/esps/index.rst b/docs/esps/index.rst index 06f8ae9..19a54c6 100644 --- a/docs/esps/index.rst +++ b/docs/esps/index.rst @@ -33,56 +33,25 @@ The table below summarizes the Anymail features supported for each ESP. .. currentmodule:: anymail.message -.. rst-class:: sticky-left - -============================================ ============ ======= ============ =========== ========== =========== ========== ========== ======== ========== =========== -Email Service Provider |Amazon SES| |Brevo| |MailerSend| |Mailgun| |Mailjet| |Mandrill| |Postal| |Postmark| |Resend| |SendGrid| |SparkPost| -============================================ ============ ======= ============ =========== ========== =========== ========== ========== ======== ========== =========== -.. rubric:: :ref:`Anymail send options ` ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -:attr:`~AnymailMessage.envelope_sender` Yes No No Domain only Yes Domain only Yes No No No Yes -:attr:`~AnymailMessage.metadata` Yes Yes No Yes Yes Yes No Yes Yes Yes Yes -:attr:`~AnymailMessage.merge_metadata` No No No Yes Yes Yes No Yes No Yes Yes -:attr:`~AnymailMessage.send_at` No Yes Yes Yes No Yes No No No Yes Yes -:attr:`~AnymailMessage.tags` Yes Yes Yes Yes Max 1 tag Yes Max 1 tag Max 1 tag Yes Yes Max 1 tag -:attr:`~AnymailMessage.track_clicks` No No Yes Yes Yes Yes No Yes No Yes Yes -:attr:`~AnymailMessage.track_opens` No No Yes Yes Yes Yes No Yes No Yes Yes -:ref:`amp-email` Yes No No Yes No No No No No Yes Yes - -.. rubric:: :ref:`templates-and-merge` ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -:attr:`~AnymailMessage.template_id` Yes Yes Yes Yes Yes Yes No Yes No Yes Yes -:attr:`~AnymailMessage.merge_data` Yes No Yes Yes Yes Yes No Yes No Yes Yes -:attr:`~AnymailMessage.merge_global_data` Yes Yes (emulated) (emulated) Yes Yes No Yes No Yes Yes -.. rubric:: :ref:`Status ` and :ref:`event tracking ` ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -:attr:`~AnymailMessage.anymail_status` Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes -|AnymailTrackingEvent| from webhooks Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes Yes -.. rubric:: :ref:`Inbound handling ` ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -|AnymailInboundEvent| from webhooks Yes Yes Yes Yes Yes Yes Yes Yes No Yes Yes -============================================ ============ ======= ============ =========== ========== =========== ========== ========== ======== ========== =========== +.. It's much easier to edit esp-feature-matrix.csv with a CSV-aware editor, such as: +.. PyCharm (Pro has native CSV support; use a CSV editor plugin with Community) +.. VSCode with a CSV editor extension +.. Excel (watch out for charset issues), Apple Numbers, or Google Sheets +.. Every row must have the same number of columns. If you add a column, you must +.. also add a comma to each sub-heading row. (A CSV editor should handle this for you.) +.. Please keep columns sorted alphabetically by ESP name. +.. csv-table:: + :file: esp-feature-matrix.csv + :header-rows: 1 + :widths: auto + :class: sticky-left Trying to choose an ESP? Please **don't** start with this table. It's far more important to consider things like an ESP's deliverability stats, latency, uptime, and support for developers. The *number* of extra features an ESP offers is almost meaningless. (And even specific features don't matter if you don't plan to use them.) -.. |Amazon SES| replace:: :ref:`amazon-ses-backend` -.. |Brevo| replace:: :ref:`brevo-backend` -.. |MailerSend| replace:: :ref:`mailersend-backend` -.. |Mailgun| replace:: :ref:`mailgun-backend` -.. |Mailjet| replace:: :ref:`mailjet-backend` -.. |Mandrill| replace:: :ref:`mandrill-backend` -.. |Postal| replace:: :ref:`postal-backend` -.. |Postmark| replace:: :ref:`postmark-backend` -.. |Resend| replace:: :ref:`resend-backend` -.. |SendGrid| replace:: :ref:`sendgrid-backend` -.. |SparkPost| replace:: :ref:`sparkpost-backend` -.. |AnymailTrackingEvent| replace:: :class:`~anymail.signals.AnymailTrackingEvent` -.. |AnymailInboundEvent| replace:: :class:`~anymail.signals.AnymailInboundEvent` - Other ESPs ---------- diff --git a/pyproject.toml b/pyproject.toml index 1d75d5e..ee1fb7b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -109,8 +109,6 @@ max-line-length = 88 target-version = ["py37"] [tool.doc8] -# ignore very long lines in ESP support table: -ignore-path-errors = ["docs/esps/index.rst;D001"] # for now, Anymail allows longer lines in docs source: max-line-length = 120