Syntax-highlighting, declarative and composable pretty printer for Python 3.5+
—
Low-level document creation and manipulation functions for building custom layout algorithms and pretty printers. The document system provides the foundation for prettyprinter's flexible and composable formatting capabilities.
Core functions for creating and combining document elements that represent formatted output structure.
def concat(docs):
"""
Create document by concatenating a sequence of documents.
Parameters:
- docs: Iterable of documents (Doc instances or strings)
Returns:
- Doc: Concatenated document
Notes:
- Empty documents (NIL) are automatically filtered out
- String arguments are automatically converted to document format
"""
def group(doc):
"""
Create document that attempts single-line layout when possible.
Parameters:
- doc: Document to attempt to lay out on single line
Returns:
- Doc: Grouped document that tries flat layout first
Notes:
- Uses 'when_flat' branch of FlatChoice documents when fitting on one line
- Falls back to multiline layout when content doesn't fit
"""
def nest(i, doc):
"""
Create document with increased indentation level.
Parameters:
- i (int): Number of spaces to add to indentation
- doc: Document to indent
Returns:
- Doc: Document with increased indentation
"""Functions for adding metadata and annotations to documents for syntax highlighting and other purposes.
def annotate(annotation, doc):
"""
Annotate document with arbitrary metadata value.
Parameters:
- annotation: Arbitrary annotation value (often Token for syntax highlighting)
- doc: Document to annotate
Returns:
- Doc: Annotated document with metadata
Notes:
- Annotations are preserved through layout process
- Used extensively for syntax highlighting with Token values
"""Create documents that are evaluated lazily based on layout context, enabling adaptive formatting based on available space and current formatting state.
def contextual(fn):
"""
Create document that is lazily evaluated during layout.
Parameters:
- fn: Function accepting (indent, column, page_width, ribbon_width) parameters
Returns:
- Doc: Contextual document evaluated during layout
Notes:
- Enables adaptive formatting based on current layout state
- Function is called during layout with current position and constraints
- Returned value from function must be a valid document
"""Functions providing fine-grained control over document layout behavior and line breaking.
def always_break(doc):
"""
Create document that forces multiline layout.
Parameters:
- doc: Document to force to multiple lines
Returns:
- Doc: Document that will always break to multiple lines
Notes:
- Forces parent documents to also break to multiple lines
- Nested documents may still be laid out flat independently
"""
def flat_choice(when_broken, when_flat):
"""
Create document with conditional layout options.
Parameters:
- when_broken: Document used when parent is broken to multiple lines
- when_flat: Document used when parent fits on single line
Returns:
- Doc: Document with conditional layout behavior
Notes:
- Layout algorithm chooses appropriate branch based on fitting constraints
- Used internally by LINE, SOFTLINE constants
"""
def align(doc):
"""
Align each new line in document with the first new line.
Parameters:
- doc: Document to align
Returns:
- Doc: Document with aligned continuation lines
"""
def hang(i, doc):
"""
Create hanging indent document.
Parameters:
- i (int): Hanging indentation amount
- doc: Document to apply hanging indent to
Returns:
- Doc: Document with hanging indentation
"""
def fill(docs):
"""
Create document that fills lines optimally with content.
Parameters:
- docs: Iterable of documents to fill
Returns:
- Doc: Document with optimal line filling
Notes:
- Attempts to fit as much content as possible on each line
- Breaks to new lines when content doesn't fit
"""Pre-defined document constants for common layout elements.
# Document constants
NIL # Empty document (no output)
LINE # Line break or space (context-dependent)
SOFTLINE # Line break or nothing (context-dependent)
HARDLINE # Forced line breakfrom prettyprinter.doc import concat, group, nest, always_break
from prettyprinter.doc import NIL, LINE, SOFTLINE, HARDLINE
# Simple concatenation
doc = concat(['Hello', ' ', 'World'])
# Grouped content that tries to fit on one line
grouped = group(concat(['[', '1', ',', LINE, '2', ',', LINE, '3', ']']))
# Nested indentation
indented = nest(4, concat(['def foo():', HARDLINE, 'return 42']))from prettyprinter import register_pretty
from prettyprinter.doc import concat, group, nest, annotate
from prettyprinter.doc import LINE, HARDLINE
from prettyprinter.syntax import Token
class Matrix:
def __init__(self, rows):
self.rows = rows
@register_pretty(Matrix)
def pretty_matrix(matrix, ctx):
# Create rows as documents
row_docs = []
for row in matrix.rows:
row_doc = concat([
'[',
concat([
str(item) if i == 0 else concat([',', LINE, str(item)])
for i, item in enumerate(row)
]),
']'
])
row_docs.append(row_doc)
# Combine rows with proper indentation
return group(concat([
annotate(Token.NAME_FUNCTION, 'Matrix'),
'(',
nest(ctx.indent, concat([
HARDLINE,
concat([
row_doc if i == 0 else concat([',', HARDLINE, row_doc])
for i, row_doc in enumerate(row_docs)
])
])),
HARDLINE,
')'
]))
# Usage
matrix = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
pprint(matrix)from prettyprinter.doc import contextual, concat
from prettyprinter import register_pretty
class AdaptiveList:
def __init__(self, items):
self.items = items
@register_pretty(AdaptiveList)
def pretty_adaptive_list(alist, ctx):
def make_adaptive_doc(indent, column, page_width, ribbon_width):
available_width = page_width - column
if available_width > 50:
# Wide format: all on one line
return concat([
'AdaptiveList([',
', '.join(str(item) for item in alist.items),
'])'
])
else:
# Narrow format: vertical layout
item_docs = [concat([str(item), ',' if i < len(alist.items)-1 else ''])
for i, item in enumerate(alist.items)]
return concat([
'AdaptiveList([',
nest(4, concat([HARDLINE, *[concat([item_doc, HARDLINE])
for item_doc in item_docs]])),
'])'
])
return contextual(make_adaptive_doc)from prettyprinter.doc import flat_choice, concat, always_break
from prettyprinter import register_pretty
class FlexibleDict:
def __init__(self, data):
self.data = data
@register_pretty(FlexibleDict)
def pretty_flexible_dict(fdict, ctx):
if not fdict.data:
return 'FlexibleDict({})'
# Create key-value pair documents
pairs = []
for key, value in fdict.data.items():
pair = flat_choice(
when_broken=concat([
repr(key), ':', HARDLINE,
nest(4, str(value))
]),
when_flat=concat([repr(key), ': ', str(value)])
)
pairs.append(pair)
# Combine pairs
combined = concat([
pairs[0] if i == 0 else concat([',', LINE, pair])
for i, pair in enumerate(pairs)
])
return group(concat([
'FlexibleDict({',
nest(4, concat([SOFTLINE, combined])),
SOFTLINE,
'})'
]))from prettyprinter.doc import concat
from prettyprinter.doctypes import Doc
def debug_document_structure(doc, level=0):
"""Print document structure for debugging."""
indent = ' ' * level
if isinstance(doc, str):
print(f"{indent}String: {repr(doc)}")
elif isinstance(doc, Doc):
print(f"{indent}{type(doc).__name__}")
if hasattr(doc, 'docs'): # Concat, Fill
for subdoc in doc.docs:
debug_document_structure(subdoc, level + 1)
elif hasattr(doc, 'doc'): # Group, Nest, AlwaysBreak, Annotated
debug_document_structure(doc.doc, level + 1)
elif hasattr(doc, 'when_flat'): # FlatChoice
print(f"{indent} when_flat:")
debug_document_structure(doc.when_flat, level + 2)
print(f"{indent} when_broken:")
debug_document_structure(doc.when_broken, level + 2)
# Example usage
doc = group(concat(['hello', LINE, 'world']))
debug_document_structure(doc)from prettyprinter.doctypes import Doc
from prettyprinter.doc import concat
class Highlight(Doc):
"""Custom document type for highlighting."""
def __init__(self, doc, color='red'):
self.doc = doc
self.color = color
def normalize(self):
from prettyprinter.doc import validate_doc # Internal function
return Highlight(validate_doc(self.doc), self.color)
def __repr__(self):
return f'Highlight({self.doc!r}, {self.color!r})'
# Example class
class ImportantData:
def __init__(self, value):
self.value = value
# Use in pretty printer
@register_pretty(ImportantData)
def pretty_important_data(data, ctx):
return concat([
'ImportantData(',
Highlight(str(data.value), 'yellow'),
')'
])Install with Tessl CLI
npx tessl i tessl/pypi-prettyprinter