Skip to content

Compatibility API

Names and signatures matching the docxtpl distribution, so existing code migrates by changing its import line. See Migrating from docxtpl.

compat

The docxtpl-compatible surface.

Existing code needs no change at all: this distribution installs the docxtpl package, so the import line a project already has keeps working.

from docxtpl import DocxTemplate, RichText, InlineImage

Everything here is a thin adapter over the modern API in docxtpl.template and docxtpl.content. The names, argument names and return types match what docxtpl documents; the implementations are this package's own. Where the two disagree the compatibility surface wins for these classes, because code depending on it cannot be changed by us.

New code should use |Template|, |Text|, |Fragment|, |Image| and |Preformatted| instead. Nothing here is deprecated — the compatibility surface is supported indefinitely — but the modern names are typed more precisely and are keyword-only where an argument is easy to pass in the wrong position.

DocxTemplate

DocxTemplate(source: Source)

Bases: Template

docxtpl.DocxTemplate, implemented over |Template|.

Source code in src/docxtpl/template.py
def __init__(self, source: Source) -> None:
    # -- the template's own bytes are kept so `reload()` can go back to it.
    # -- A stream is read once here rather than seeked later, since a caller
    # -- may hand over something that is not seekable --
    self._given_source = source
    self._source = bytes_of(source)
    self._document = open_document(io.BytesIO(self._source))
    self._is_rendered = False
    self._has_been_saved = False
    self._replacements = ReplacementRegistry()
    self._current_part: Optional[XmlPart] = None
    self._update_fields: Optional[bool] = None
    # -- survives `reload()`, which is the whole point: it describes the
    # -- template rather than the render --
    self._prepared: Dict[Tuple[bytes, Options], _Prepared] = {}
    self._environments: Dict[Options, Environments] = {}

docx property

docx: Document

docxtpl.DocxTemplate.docx — the underlying python-docx document.

The same object as |Template.document|, under the name existing code reaches for: tpl.docx.add_paragraph(…), tpl.docx.tables[0].

is_saved property

is_saved: bool

docxtpl.DocxTemplate.is_saved — whether save() has been called.

template_file property

template_file: Any

docxtpl.DocxTemplate.template_file — what the template was loaded from.

allow_missing_pics property writable

allow_missing_pics: bool

docxtpl.DocxTemplate.allow_missing_pics.

The same flag as |Template.allow_missing_replacements|, which covers media and embedded objects as well as pictures.

render

render(
    context: Any,
    jinja_env: Optional[Environment] = None,
    autoescape: bool = False,
    *,
    sandboxed: bool = False,
    strict: bool = False,
) -> None

docxtpl.DocxTemplate.render().

sandboxed and strict are this package's additions and are keyword-only, so the two positional arguments stay exactly docxtpl's. See |Template.render|.

Source code in src/docxtpl/compat.py
def render(  # pyright: ignore[reportIncompatibleMethodOverride]
    self,
    context: Any,
    jinja_env: Optional[Environment] = None,
    autoescape: bool = False,
    *,
    sandboxed: bool = False,
    strict: bool = False,
) -> None:
    """`docxtpl.DocxTemplate.render()`.

    `sandboxed` and `strict` are this package's additions and are
    keyword-only, so the two positional arguments stay exactly `docxtpl`'s.
    See |Template.render|.
    """
    super().render(
        context,
        jinja_env=jinja_env,
        autoescape=autoescape,
        sandboxed=sandboxed,
        strict=strict,
    )

get_undeclared_template_variables

get_undeclared_template_variables(
    jinja_env: Optional[Environment] = None,
    context: Optional[Dict[str, Any]] = None,
) -> Set[str]

docxtpl.DocxTemplate.get_undeclared_template_variables().

With context, the answer is what the template needs and that context does not supply — the missing variables rather than all of them.

Source code in src/docxtpl/compat.py
def get_undeclared_template_variables(
    self,
    jinja_env: Optional[Environment] = None,
    context: Optional[Dict[str, Any]] = None,
) -> Set[str]:
    """`docxtpl.DocxTemplate.get_undeclared_template_variables()`.

    With `context`, the answer is what the template needs and that context
    does *not* supply — the missing variables rather than all of them.
    """
    needed = self.undeclared_variables(jinja_env=jinja_env)
    return needed - set(context) if context is not None else needed

get_docx

get_docx() -> Document

docxtpl.DocxTemplate.get_docx().

Source code in src/docxtpl/compat.py
def get_docx(self) -> Document:
    """`docxtpl.DocxTemplate.get_docx()`."""
    return self.document

init_docx

init_docx(reload: bool = True) -> None

docxtpl.DocxTemplate.init_docx() — go back to the unrendered template.

Rendering mutates the document, so a second render() applies to the already-rendered one. Reloading is how existing code renders the same template against several contexts in a loop.

Source code in src/docxtpl/compat.py
def init_docx(self, reload: bool = True) -> None:  # noqa: A002 -- docxtpl's name
    """`docxtpl.DocxTemplate.init_docx()` — go back to the unrendered template.

    Rendering mutates the document, so a second `render()` applies to the
    already-rendered one. Reloading is how existing code renders the same
    template against several contexts in a loop.
    """
    if reload:
        self.reload()

render_footnotes

render_footnotes(
    context: Dict[str, Any],
    jinja_env: Optional[Environment] = None,
) -> None

docxtpl.DocxTemplate.render_footnotes() — the notes and nothing else.

Endnotes as well as footnotes: they are the same kind of thing, and a template with a tag in one usually has one in the other.

Source code in src/docxtpl/compat.py
def render_footnotes(
    self,
    context: Dict[str, Any],
    jinja_env: Optional[Environment] = None,
) -> None:
    """`docxtpl.DocxTemplate.render_footnotes()` — the notes and nothing else.

    Endnotes as well as footnotes: they are the same kind of thing, and a
    template with a tag in one usually has one in the other.
    """
    self.render_parts(
        ("footnotes", "endnotes"),
        context,
        jinja_env=jinja_env,
        autoescape=False,
    )

render_properties

render_properties(
    context: Dict[str, Any],
    jinja_env: Optional[Environment] = None,
) -> None

docxtpl.DocxTemplate.render_properties() — the property parts.

All three of them: core, extended and custom. docxtpl renders the first two, and a template whose custom properties held tags would have no other way to reach them.

Source code in src/docxtpl/compat.py
def render_properties(
    self,
    context: Dict[str, Any],
    jinja_env: Optional[Environment] = None,
) -> None:
    """`docxtpl.DocxTemplate.render_properties()` — the property parts.

    All three of them: core, extended and custom. `docxtpl` renders the
    first two, and a template whose custom properties held tags would have
    no other way to reach them.
    """
    self.render_parts(
        ("core properties", "extended properties", "custom properties"),
        context,
        jinja_env=jinja_env,
        autoescape=False,
    )

new_subdoc

new_subdoc(
    docpath: Optional[FragmentSource] = None,
) -> Subdoc

docxtpl.DocxTemplate.new_subdoc().

docpath is a path, as docxtpl documents it, and also a stream or an open docx.document.Document — which is what composing a document from pieces rendered by other templates needs.

Source code in src/docxtpl/compat.py
def new_subdoc(self, docpath: Optional[FragmentSource] = None) -> Subdoc:
    """`docxtpl.DocxTemplate.new_subdoc()`.

    `docpath` is a path, as `docxtpl` documents it, and also a stream or an
    open `docx.document.Document` — which is what composing a document from
    pieces rendered by other templates needs.
    """
    return Subdoc(self, docpath)

build_url_id

build_url_id(url: str) -> str

docxtpl.DocxTemplate.build_url_id().

Source code in src/docxtpl/compat.py
def build_url_id(self, url: str) -> str:
    """`docxtpl.DocxTemplate.build_url_id()`."""
    return self.url_id(url)

replace_pic

replace_pic(
    embedded_file: str, dst_file: ImageSource
) -> None

docxtpl.DocxTemplate.replace_pic().

Source code in src/docxtpl/compat.py
def replace_pic(self, embedded_file: str, dst_file: ImageSource) -> None:
    """`docxtpl.DocxTemplate.replace_pic()`."""
    self.replace_picture(embedded_file, dst_file)

replace_zipname

replace_zipname(
    zipname: str, dst_file: ImageSource
) -> None

docxtpl.DocxTemplate.replace_zipname().

Source code in src/docxtpl/compat.py
def replace_zipname(self, zipname: str, dst_file: ImageSource) -> None:
    """`docxtpl.DocxTemplate.replace_zipname()`."""
    self.replace_zip_member(zipname, dst_file)

RichText

RichText(
    text: str = "",
    style: Optional[str] = None,
    color: Optional[str] = None,
    highlight: Optional[str] = None,
    size: Optional[int] = None,
    subscript: Optional[bool] = None,
    superscript: Optional[bool] = None,
    bold: bool = False,
    italic: bool = False,
    underline: Union[bool, str, None] = False,
    strike: bool = False,
    font: Optional[str] = None,
    url_id: Optional[str] = None,
    rtl: bool = False,
    lang: Optional[str] = None,
)

Bases: Text

docxtpl.RichText, implemented over |Text|.

The formatting arguments are positional here, in the order docxtpl declares them, because existing code passes them that way: R().add("x", None, "ff0000"). |Text| makes the same arguments keyword-only, which is the improvement and also the reason this override exists.

docxtpl.RichText().

Source code in src/docxtpl/compat.py
def __init__(  # noqa: PLR0913 -- docxtpl's parameter list, reproduced
    self,
    text: str = "",
    style: Optional[str] = None,
    color: Optional[str] = None,
    highlight: Optional[str] = None,
    size: Optional[int] = None,
    subscript: Optional[bool] = None,
    superscript: Optional[bool] = None,
    bold: bool = False,
    italic: bool = False,
    underline: Union[bool, str, None] = False,
    strike: bool = False,
    font: Optional[str] = None,
    url_id: Optional[str] = None,
    rtl: bool = False,
    lang: Optional[str] = None,
) -> None:
    """`docxtpl.RichText()`."""
    super().__init__(
        text,
        style=style,
        color=color,
        highlight=highlight,
        size=size,
        subscript=bool(subscript),
        superscript=bool(superscript),
        bold=bold,
        italic=italic,
        underline=underline,
        strike=strike,
        font=font,
        url_id=url_id,
        rtl=rtl,
        lang=lang,
    )

add

add(
    text: str = "",
    style: Optional[str] = None,
    color: Optional[str] = None,
    highlight: Optional[str] = None,
    size: Optional[int] = None,
    subscript: Optional[bool] = None,
    superscript: Optional[bool] = None,
    bold: bool = False,
    italic: bool = False,
    underline: Union[bool, str, None] = False,
    strike: bool = False,
    font: Optional[str] = None,
    url_id: Optional[str] = None,
    rtl: bool = False,
    lang: Optional[str] = None,
) -> RichText

docxtpl.RichText.add(), which takes its arguments positionally.

Source code in src/docxtpl/compat.py
def add(  # pyright: ignore[reportIncompatibleMethodOverride] # noqa: PLR0913
    self,
    text: str = "",
    style: Optional[str] = None,
    color: Optional[str] = None,
    highlight: Optional[str] = None,
    size: Optional[int] = None,
    subscript: Optional[bool] = None,
    superscript: Optional[bool] = None,
    bold: bool = False,
    italic: bool = False,
    underline: Union[bool, str, None] = False,
    strike: bool = False,
    font: Optional[str] = None,
    url_id: Optional[str] = None,
    rtl: bool = False,
    lang: Optional[str] = None,
) -> RichText:
    """`docxtpl.RichText.add()`, which takes its arguments positionally."""
    super().add(
        text,
        style=style,
        color=color,
        highlight=highlight,
        size=size,
        subscript=bool(subscript),
        superscript=bool(superscript),
        bold=bold,
        italic=italic,
        underline=underline,
        strike=strike,
        font=font,
        url_id=url_id,
        rtl=rtl,
        lang=lang,
    )
    return self

RichTextParagraph

RichTextParagraph(
    text: Union[str, RichText, None] = None,
    parastyle: Optional[str] = None,
    *,
    align: Optional[str] = None,
    spacing: Optional[int] = None,
)

docxtpl.RichTextParagraph — a paragraph with its own properties.

Compatibility-only, and deliberately so: |Fragment| covers producing a styled paragraph with the whole document API behind it, and a second way to do it in the modern surface would be a second thing to keep working.

The content of each paragraph is a string or a |RichText|; parastyle names a paragraph style defined in the document. align and spacing are this package's additions and are keyword-only, so that the positional arguments stay exactly docxtpl's.

docxtpl.RichTextParagraph().

Source code in src/docxtpl/compat.py
def __init__(
    self,
    text: Union[str, RichText, None] = None,
    parastyle: Optional[str] = None,
    *,
    align: Optional[str] = None,
    spacing: Optional[int] = None,
) -> None:
    """`docxtpl.RichTextParagraph()`."""
    self._paragraphs: List[Tuple[Union[str, RichText], Dict[str, Any]]] = []
    if text is not None:
        self.add(text, parastyle, align=align, spacing=spacing)

xml property

xml: str

The w:p sequence this object renders to.

add

add(
    text: Union[str, RichText],
    parastyle: Optional[str] = None,
    *,
    align: Optional[str] = None,
    spacing: Optional[int] = None,
) -> RichTextParagraph

Append a paragraph and return self, so calls chain.

Source code in src/docxtpl/compat.py
def add(
    self,
    text: Union[str, RichText],
    parastyle: Optional[str] = None,
    *,
    align: Optional[str] = None,
    spacing: Optional[int] = None,
) -> RichTextParagraph:
    """Append a paragraph and return `self`, so calls chain."""
    self._paragraphs.append(
        (text, {"style": parastyle, "align": align, "spacing": spacing}),
    )
    return self

Subdoc

Subdoc(
    tpl: Template, docpath: Optional[FragmentSource] = None
)

Bases: Fragment

docxtpl.Subdoc, implemented over |Fragment|.

Content is added to the object itself — sd.add_paragraph("…") — as well as through sd.document, because that is how docxtpl's own examples do it. Anything this class does not define is looked up on the document, so the whole python-docx-ng API is available without naming it.

docxtpl.Subdoc(tpl, docpath=None).

docpath takes everything |Fragment.from_file| does: a path, a stream, or a document already open.

Source code in src/docxtpl/compat.py
def __init__(self, tpl: Template, docpath: Optional[FragmentSource] = None) -> None:
    """`docxtpl.Subdoc(tpl, docpath=None)`.

    `docpath` takes everything |Fragment.from_file| does: a path, a stream,
    or a document already open.
    """
    super().__init__(tpl)
    if docpath is not None:
        self._document = Fragment.from_file(tpl, docpath).document

subdocx property

subdocx: Document

docxtpl.Subdoc.subdocx — the document being built.

docx property

docx: Document

docxtpl.Subdoc.docx — the document being built.

tpl property

tpl: Template

docxtpl.Subdoc.tpl — the template this fragment belongs to.

InlineImage

InlineImage(
    tpl: Template,
    image_descriptor: ImageSource,
    width: Optional[Length] = None,
    height: Optional[Length] = None,
    anchor: Optional[str] = None,
)

Bases: Image

docxtpl.InlineImage, implemented over |Image|.

docxtpl.InlineImage(), whose size arguments are positional.

anchor names a bookmark to link the picture to. It is accepted for signature compatibility and, as in the original when the name does not resolve, has no effect on the produced markup.

Source code in src/docxtpl/compat.py
def __init__(
    self,
    tpl: Template,
    image_descriptor: ImageSource,
    width: Optional[Length] = None,
    height: Optional[Length] = None,
    anchor: Optional[str] = None,
) -> None:
    """`docxtpl.InlineImage()`, whose size arguments are positional.

    `anchor` names a bookmark to link the picture to. It is accepted for
    signature compatibility and, as in the original when the name does not
    resolve, has no effect on the produced markup.
    """
    super().__init__(tpl, image_descriptor, width=width, height=height)
    self.anchor = anchor

tpl property

tpl: Template

docxtpl.InlineImage.tpl — the template this image belongs to.

image_descriptor property

image_descriptor: Any

docxtpl.InlineImage.image_descriptor — the path or bytes it holds.

Listing

Listing(text: str)

Bases: Preformatted

docxtpl.Listing, implemented over |Preformatted|.

Source code in src/docxtpl/content/listing.py
def __init__(self, text: str) -> None:
    self._text = text

escape

escape(text: str) -> str

docxtpl.escape() — XML-escape text for placement in a w:t.

Source code in src/docxtpl/compat.py
def escape(text: str) -> str:
    """`docxtpl.escape()` — XML-escape `text` for placement in a `w:t`."""
    return escape_content(text)