Skip to main content

Fill Word document templates using YAML configs with a powerful expression language, built-in functions, and SQLite integration.

Project description

📄 DocumentPlaceholder

Automatically fill Word templates using YAML configs, expressions, and SQL.

Generate invoices, reports, statements, and any other documents with a single command.

PyPI Version Python Versions License Tests

FeaturesInstallationQuick StartConfigurationFunctionsGUI


🚀 Features

DocumentPlaceholder turns .docx templates into ready-to-use documents based on YAML configs with a powerful expression language.

  • 📝 Word templates — placeholders like {KEY} in text, tables, and headers/footers.
  • Expression language — arithmetic, comparisons, nested function calls, and template strings.
  • 🛢 SQLite out of the box — run database queries directly inside configs for counters, lookups, and client data.
  • 📅 59 built-in functions — date/time, strings, math, logic, and conditions.
  • 📤 PDF export — automatic conversion through LibreOffice.
  • 🖥 GUI with syntax highlighting — config editor, live preview, SQL manager.
  • 🔌 Extensible — add your own functions with a single decorator.

📦 Installation

pip install document-placeholder

Optional extras:

Extra Includes
document-placeholder[gui] GUI interface (CustomTkinter)
document-placeholder[dev] Development tools (pytest)
document-placeholder[all] Everything

⚡ Quick Start

1. Create a Word template (template.docx)

Insert placeholders into the document:

Invoice #{INVOICE_NUM}
Date: {DAY_NUM}.{MONTH_STR}.{YEAR_NUM}
Amount: ${PRICE}
{DESCRIPTION}

2. Write a config (template.yaml)

ON_START:
  - SQL('CREATE TABLE IF NOT EXISTS doc (num INTEGER DEFAULT 0)')
  - SQL('INSERT OR IGNORE INTO doc (rowid, num) VALUES (1, 0)')

INVOICE_NUM:
  SQL('SELECT num FROM doc WHERE rowid = 1') + 1

MONTH_STR:
  CURRENT_DATE_STR(month)

DAY_NUM:
  CURRENT_DATE_NUM(day)

YEAR_NUM:
  CURRENT_DATE_NUM(year)

PRICE:
  500

DESCRIPTION:
  "Software Development Services
   (Period: {CURRENT_DATE_NUM(day, month, year) - DAYS(7)}  {CURRENT_DATE_NUM(day, month, year)})"

OUTPUT_NAME:
  "Invoice-{INVOICE_NUM}"

OUTPUT_FORMAT:
  - docx
  - pdf

ON_END:
  SQL('UPDATE doc SET num = num + 1 WHERE rowid = 1')

3. Run

docplaceholder -c template.yaml -t template.docx
  INVOICE_NUM = 2026-2-5
  MONTH_STR = February
  DAY_NUM = 16
  YEAR_NUM = 2026
  PRICE = 500
  DESCRIPTION = Software Development Services (Period: 09.02.2026 — 16.02.2026)

  Output: Invoice-2026-2-5 [docx, pdf]
  -> Invoice-2026-2-5.docx
  -> Invoice-2026-2-5.pdf

🎨 Expression Language

A config is not just key-value mapping. Every value is an expression that gets evaluated.

Arithmetic and comparisons

TAX: ROUND(PRICE * 0.2, 2)
TOTAL: PRICE + TAX
IS_PREMIUM: TOTAL > 1000

Template strings

Inside "...", expressions like {expr} are interpolated into the final string:

PERIOD: "{CURRENT_DATE_NUM(day, month, year) - DAYS(30)}  {CURRENT_DATE_NUM(day, month, year)}"

Nested calls

INVOICE_NUM:
  "{CURRENT_DATE_NUM(year)}-{SQL('SELECT num FROM doc WHERE rowid = 1') + 1}"

Conditional logic

STATUS: IF(TOTAL > 1000, 'Premium', 'Standard')
DISCOUNT: IF(TOTAL >= 500, TOTAL * 0.1, 0)
LABEL: SWITCH(STATUS, 'Premium', '⭐ Premium', 'Standard', '📋 Standard')

Supported operators: + - * / % > < >= <= == != ()


⚙️ Configuration

CLI arguments

docplaceholder [-c CONFIG] [-t TEMPLATE] [-o OUTPUT] [--db DATABASE]
Argument Default Description
-c, --config template.yaml Path to YAML config
-t, --template template.docx Path to Word template
-o, --output output.docx Path to output file
--db data.db Path to SQLite database
-V, --version Print program version

Special YAML keys

Key Description
ON_START Expressions executed before processing (table creation, initialization)
ON_END Expressions executed after processing (increment counters, cleanup)
OUTPUT_NAME Output filename template: "Invoice-{INVOICE_NUM}"
OUTPUT_FORMAT List of output formats: [docx, pdf]

All other keys are treated as placeholders and replaced in the document.


🧰 Built-in Functions

59 functions in 5 categories. Full reference: FUNCTIONS.md

📅 Date and time

TODAY: TODAY()                                         # 16.02.2026
YEAR: CURRENT_DATE_NUM(year)                           # 2026
MONTH: CURRENT_DATE_STR(month)                         # February
CUSTOM: DATE_FORMAT(DATE(2026, 3, 8), '%d %B %Y')     # 08 March 2026
WEEK_AGO: "{TODAY() - DAYS(7)}"                        # 09.02.2026
DIFF: DAYS_BETWEEN(DATE(2026, 1, 1), TODAY())          # 46

🔤 Strings

UPPER('hello')                    # HELLO
TITLE('john doe')                 # John Doe
PAD_LEFT('42', 6, '0')           # 000042
JOIN(', ', 'a', 'b', 'c')        # a, b, c
REPLACE('foo bar', 'bar', 'baz') # foo baz
SPLIT('user@mail.com', '@', 1)   # mail.com

🔢 Math

ROUND(19.956, 2)                  # 19.96
FORMAT_NUM(1234567, 2)            # 1,234,567.00
MIN(3, 1, 4, 1, 5)               # 1
AVG(10, 20, 30)                   # 20.0
SQRT(144)                         # 12.0

🧠 Logic

IF(PRICE > 1000, 'expensive', 'cheap')
COALESCE(SQL('SELECT name FROM clients'), 'Unknown')
DEFAULT(value, 'N/A')
SWITCH(status, 'draft', 'Draft', 'sent', 'Sent', 'Unknown')

🛢 SQL

SQL('SELECT count(*) FROM orders WHERE user_id = 1')
SQL('INSERT INTO log (event) VALUES ("generated")')

🖥 Graphical Interface

pip install document-placeholder[gui]
docplaceholder-gui

The GUI includes:

  • Config editor with YAML and custom syntax highlighting (SQL(...), {expressions})
  • Live preview of evaluated values
  • SQL manager for running queries and viewing tables/schema
  • Keyboard shortcutsCtrl+S save, Ctrl+F search, F5 refresh

🔌 Extending Functions

Add a custom function with a single decorator:

from document_placeholder.functions import FunctionRegistry

@FunctionRegistry.register("MY_FUNC")
def my_func(arg1, arg2):
    """Your custom logic."""
    return f"{arg1}-{arg2}"

After importing the module, the function becomes available in config expressions:

VALUE: MY_FUNC('hello', 'world')   # hello-world

📁 Library Usage

from document_placeholder.config import Config
from document_placeholder.evaluator import Evaluator
from document_placeholder.processor import DocumentProcessor

config = Config.from_string("""
NAME: UPPER('john doe')
DATE: TODAY()
""")

evaluator = Evaluator()
values = {k: evaluator.evaluate_value(v) for k, v in config.placeholders.items()}
# {'NAME': 'JOHN DOE', 'DATE': DateValue(2026-02-16)}

processor = DocumentProcessor("template.docx")
processor.replace_placeholders(values)
processor.save("output.docx")

🧪 Testing

pip install document-placeholder[dev]
pytest
295 passed in 0.36s

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Commit your changes
  4. Open a Pull Request

Bugs and feature requests → Issues


📄 License

This project is released under the MIT license. See LICENSE for details.

Developed with ❤️ by FlacSy

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

document_placeholder-1.0.0.post1.tar.gz (52.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

document_placeholder-1.0.0.post1-py3-none-any.whl (30.0 kB view details)

Uploaded Python 3

File details

Details for the file document_placeholder-1.0.0.post1.tar.gz.

File metadata

File hashes

Hashes for document_placeholder-1.0.0.post1.tar.gz
Algorithm Hash digest
SHA256 311a0555490fc964b353d265885adec0251fc3e3b2de98a59d4911f68d0894d0
MD5 ddc63751a62729ab791e0a9c18fe50c1
BLAKE2b-256 e84a1d0c98b8c30d4bc3d83a6a47d695b02af20a3f5239f97092a960a248a00d

See more details on using hashes here.

File details

Details for the file document_placeholder-1.0.0.post1-py3-none-any.whl.

File metadata

File hashes

Hashes for document_placeholder-1.0.0.post1-py3-none-any.whl
Algorithm Hash digest
SHA256 a21e77ccff58516bc62673b2446d5145788ae32a5fe3d4b5d71d63f8d9758370
MD5 50e97a454ced5916183e5311040cbd0f
BLAKE2b-256 5ef17bc26b92b9785c2887217e4d3d2c1a914e7bfac331a709decc1ad54ff568

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page