Skip to main content

Indenter

pypi badge

Indenter is a Python package that assists with programmatically indenting text to arbitrary levels using with blocks and +/- operators. It has zero dependencies.

Use Indenter to get easy structured output, even as your control flow weaves through multiple functions:

from indenter import Indenter

def validate_email(ind, email):
  print(ind + 'Validating email...')
  # ...

def parse_date(ind, date):
  print(ind + f'Parsing date {date}')
  # ...
  with ind:
    print(ind + 'Checking components')
    y, m, d = date.split('-')
  print(ind + 'Date is valid!')

def get_user_record(ind):
  print(ind + 'Fetching user from database...')
  validate_email(ind + 1, 'ben@twos.dev')
  birthday = parse_date(ind + 1, '1990-08-21')
  print(ind + 'Fetched user!')
  # ...

ind = Indenter(symbol='→ ')
get_user_record(ind)

Output:

Fetching user from database...
→ Validating email...
→ Parsing date 1990-08-21
→ → Checking components
→ Date is valid!
Fetched user!

Getting started

Requirements

  • Python 3.6+

Installing

pip install indenter

Usage

from indenter import Indenter
with Indenter() as ind:
  print(ind + "Text to be indented")

# Output:
#   Text to be indented

You can nest an arbitrary number of with calls:

from indenter import Indenter
ind = Indenter()
with ind:
  print(ind + "I'm one level deep")
  with ind:
    print(ind + "I'm two levels deep")
  print(ind + "I'm one level deep again")

# Output:
#   I'm one level deep
#     I'm two levels deep
#   I'm one level deep again

You can manually adjust the indentation level using +/-. Use this to indent output by nested function calls correctly:

import logging
from indenter import Indenter

ind = Indenter()

func do_work(ind):
  logging.debug(ind + "Doing some work")

func do_business_logic():
  ind = Indenter()
  logging.debug(ind + "Doing some business logic")
  do_work(ind + 1)
  logging.debug(ind + "Finishing some busines logic")

do_some_business_logic()

# Output:
# Doing some business logic
#   Doing some work
# Finishing some busines logic

Customization

The default indentation symbol is two spaces. You can override this by passing symbol:

from indenter import Indenter

# Indent with arrows
with Indenter(symbol="→ ") as ind:
  print(ind + "I'm indented with an arrow")
  with ind:
    print(ind + "I'm indented with two arrows")

# Indent with 4 spaces per level
with Indenter(symbol="    ") as ind:
  print(ind + "I'm indented by four spaces")

# Indent with tabs
with Indenter(symbol="\t") as ind:
  print(ind + "I'm indented by one tab")

# Output:
# → I'm indented by an arrow
# → → I'm indented by two arrows
#     I'm indented by four spaces
# 	I'm indented by one tab

When using custom symbols, nested withs will inherit the symbol of their parent. If you need more than one type of indentation at once, you must make and manage multiple Indenters.

The indenter starts at zero levels of indentation by default, and increases by one level for each with block. This can be overridden to start at other levels, such as 1:

from indenter import Indenter
ind = Indenter(start=1)
print(ind + "First-level indentation")
with ind:
  print(ind + "Second-level indentation")

# Output:
#   First-level indentation
#     Second-level indentation

Contributing

I welcome contributions and foster an inclusive environment.

Testing

Testing requires pytest (pip install pytest).

pytest

Releasing

(mostly notes for myself)

To release, first bump the version number in setup.cfg, commit the result, then run:

rm -rf dist
pip install build twine
python -m build --wheel
twine check dist/* # optional; check for common issues
twine upload dist/*

For ease of browsing by users, you should also cut a release on GitHub.

Metadata

Release files for indenter 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for indenter 1.1.0
File Interpreter ABI Platform Actions
indenter-1.1.0-py3-none-any.whl Python 3 none any Details for indenter-1.1.0-py3-none-any.whl

Release files / indenter-1.1.0-py3-none-any.whl

Download URL indenter-1.1.0-py3-none-any.whl
Size 5.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9167e3cc61d6ddfbbe5892fb7a288a52cf640dda516beace756192fd57bbbb0e
BLAKE2b-256 checksum
How to use checksums
088504213e58c39c012490867d81f1729de04f921dcc3b96052ee5ba4bb75eac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/3.8.0 pkginfo/1.8.2 readme-renderer/32.0 requests/2.27.1 requests-toolbelt/0.9.1 urllib3/1.26.8 tqdm/4.62.3 importlib-metadata/4.10.1 keyring/23.5.0 rfc3986/2.0.0 colorama/0.4.3 CPython/3.10.1

Security for indenter 1.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page