skip to navigation
skip to content

say 0.115

Simple formatted printing with templates

Latest Version: 1.4.2

This module is supplementd or replaced Python’s print statement/function, format function/method, and % string interpolation operator with higher-level facilities.

Q: It’s been forty years since C introduced printf() and the basic formatted printing of positional parameters. Isn’t it time for an upgrade?

A: Yes! ZOMG, yes!

say provides straightforward string formatting with a DRY, Pythonic templating approach. It piggybacks the format() method, using its formatting syntax (and underlying engine).


from say import say

x = 12
nums = range(4)

say("There are {x} things.")
say("Nums has {len(nums)} items: {nums}")


There are 12 things.
Nums has 4 items: [1, 2, 3, 4]

say is basically a nicer recasting of:

print "There are {} things.".format(x)
print "Nums has {} items: {}".format(len(nums), nums)

Albeit with a nice inline, DRY, templated style that is clearer and simper than Python’s defaults. The more complicated the format invocation, the more valuable having it stated in-line becomes.

It also provides a printing function that isn’t defined as a statement in Python 2 but a function in Python 3, requiring “what version am I working in?!” tests and setup. say() is the same in both versions.

Printing Were You Like, or Not

say() writes to a list of files–by default just sys.stdout. But with it simple configuration call, it will write to different–even multiple– files:

import sys

say.setfiles(sys.stdout, "report.txt")
say(...)   # now prints to both stdout and report.txt

This has the advantage of allowing you to capture program output without changing any code.

Or if you want to stop printing for a while:

say.set(silent=True)  # no printing until reset to False

Or transiently:

say(...stuff..., silent=not verbose) # prints iff bool(verbose) is True

String Formatting

Of course, you don’t have to print to any file. say(..., silent=True) formats but does not print. More neatly, there there’s a predefined sayer fmt() that works exactly like say() and inherits its options, but doesn’t print. (The C analogy: say : fmt :: printf : sprintf.)

Note that say() and fmt() return Unicode strings. If you are using Python 2.7 with strings containing utf-8 rather than Unicode characters, say will not be greatly happy–but basically in the same places that format() is already not happy. When writing to files, say writes using an encoding (by default, utf-8). But you can get creative:

say('I am a truck!', encoding='base64')  # SSBhbSBhIHRydWNrIQo=

Or change the default:


Knock yourself out with all the exciting opportunites! If you really want the formatted text returned just as it is written to files, use the encoded option. Set to True it returns text in the output encoding. Or set to anything else, that becomes the return encoding.

say() returns the formatted text with one small tweak: it removes the final newline if a newline is the very last character. Though odd, this is exactly what you need if you’re going to print or say the resulting text without a gratuitous “extra” newline.

Titles and Horizontal Rules

say defines a few convenience formatting functions:

say.title('Errors', sep='-')
for i,e in enumerate(errors, start=1):
    say("{i:3}: {e['name'].upper()}")

might yield:

--------------- Errors ---------------
  1: I/O ERROR

A similar method hr produces just a horizontal line, like the HTML <hr> element. For either, one can optionally specify the width (width), character repeated to make the line (sep), and vertical separation/whitespace above and below the item (vsep). Good options for the separator might be be ‘-‘, ‘=’, or parts of the Unicode box drawing character set.


  • ScopeFormatter is a module that provides variable interpolation into strings. It is amazingly compact and elegant. Sadly, it only interpolates Python names, not full expressions. say has full expressions, as well as a framework for higher-level printing features beyond ScopeFormatter’s…um…scope.

  • Even simpler are invocations of % or format() using locals(). E.g.:

    name = "Joe"
    print "Hello, %(name)!" % locals()
    # or
    print "Hello, {name}!".format(**locals())

    Unfortunately this has even more limitations than ScopeFormatter: it only supports local variables, not globals or expressions. And the interpolation code seems gratuitous. Simpler:

    say("Hello, {name}!")


  • The say name was inspired by Perl’s say, but the similarity stops there.
  • My goal is to make this module equally usable from Python 2 and Python 3. Most of my work is currently in Python 2.7, and I am a novice at cross-supporting both major language versions.
  • say has greater ambitions than just simple template printing. It’s part of a larger rethinking of how output should be formatted. Stay tuned.
  • In addition to being a practical module in its own right, say is testbed for options, a package that provides high-flexibility option, configuration, and parameter management.
  • The author, Jonathan Eunice or @jeunice on Twitter welcomes your comments and suggestions.


pip install say

(You may need to prefix this with “sudo ” to authorize installation.)

File Type Py Version Uploaded on Size
say-0.115.tar.gz (md5) Source 2012-10-12 8KB (md5) Source 2012-10-12 15KB