Skip to main content

A simple way to quote and wrap text

Project description

In dealing with text, one quotes values all the time. Single quotes. Double quotes. Curly quotes. Backticks. Funny Unicode quotes. HTML or XML markup code. Et cetera.

For simple cases, this isn’t hard, and there a lot of ways to do it:

value = 'something'
print '{x}'.replace('x', value)             # {something}
print "'{}'".format(value)                  # 'value'
print "'" + value + "'"                     # 'value'
print "{}{}{}".format('"', value, '"')      # "value"
print ''.join(['"', value, '"'])            # "value"

But for such a simple, common task as wrapping values in surrounding text, it looks pretty ugly, it’s very low-level, and it’s easy to type the wrong character here or there. The ad hoc nature makes quoting tiresome and error-prone. It’s never more so than when you’re constructing multi-level quoted strings, such as Unix command line arguments, SQL commands, or HTML attributes.

So this module provides an clean, consistent, higher-level alternative.

Usage

from quoter import single, double, backticks, braces

print single('this')       # 'this'
print double('that')       # "that"
print backticks('ls -l')   # `ls -l`
print braces('curlycue')   # {curlycue}

It pre-defines callable Quoters for a handful of the most common quoting styles:

  • braces {example}

  • brackets [example]

  • angles <example>

  • parens (example)

  • double “example”

  • single ‘example’

  • backticks `example`

  • anglequote «example»

  • curlysingle ‘example’

  • curlysdouble “example”

But there are a huge number of ways you might want to wrap or quote text. Even considering just “quotation marks,” there are well over a dozen. There are also numerous bracketing symbols in common use. That’s to say nothing of the constructs seen in markup, programming, and templating languages. So quoter couldn’t possibly provide an option for every possible quoting style. Instead, it provides a general-purpose mechanism for defining your own:

from quoter import Quoter

bars = Quoter('|')
print bars('x')                    # |x|

plus = Quoter('+','')
print plus('x')                    # +x

para = Quoter('<p>', '</p>')
print para('this is a paragraph')  # <p>this is a paragraph</p>

variable = Quoter('${', '}')
print variable('x')                # ${x}

Note that bars is specified with just one symbol. If only one is given, the prefix and suffix are considered to be identical. If you really only want a prefix or a suffix, and not both, then instantiate the Quoter with two, one of which is an empty string, as in plus above. For symmetrical quotes, where the length of the prefix and the suffix are the same, you can specify the prefix and suffix all in one go. The prefix will be the first half, the second, the second half

In most cases, it’s cleaner and more efficient to define a style, but there’s nothing preventing you from an on-the-fly usage:

print Quoter('+[ ', ' ]+')('castle')   # +[ castle ]+

Formatting and Encoding

The Devil, as they say, is in the details. We often don’t just want quote marks wrapped around values. We also want those values set apart from the rest of the text. quoter supports this with padding and margin settings patterned on the CSS box model. In CSS, moving out from content one finds padding, a border, and then a margin. Padding can be thought of as an internal margin, and the prefix and suffix strings like the border. With that in mind:

print braces('this')                      # '{this}'
print braces('this', padding=1)           # '{ this }'
print braces('this', margin=1)            # ' {this} '
print braces('this', padding=1, margin=1) # ' { this } '

If desired, the padding and margin can be given as strings, though usually they will be integers specifying the number of spaces surrounding the text.

One can also define the encoding used for each call, per instance, or globally. If some of your quote symbols use Unicode characters, yet your output medium doesn’t support them directly, this is an easy fix. E.g.:

Quoter.options.encoding = 'utf-8'
print curlydouble('something something')

Now curlydouble will output UTF-8 bytes. But in general, you should work in Unicode strings in Python, encoding or decoding only at the time of input and output, not as each piece of content is constructed.

Shortcuts

One often sees very long function calls and expressions as text parts are being assembled. In order to reduce this problem, quoter defines aliases for single, double, and triple quoting, as well as backticks:

from quoter import qs, qd, qt, qb

print qs('one'), qd('two'), qb('and'), qt('three')
# 'one' "two" `and` """three"""

You can, of course, define your own aliases as well, and/or redefine existing styles. If, for example, you like braces but wish it added a padding space by default, it’s simple to redefine:

braces = Quoter('{', '}', padding=1, name='braces')
print braces('braces plus spaces!')  # '{ braces plus spaces! }'

You could still get the no-padding variation with:

print braces('no space braces', padding=0) # '{no space braces}'

Dynamic Quoters

It is possible to define Quoters that don’t just concatenate text, but that examine it and provide dynamic rewriting on the fly. For example, in finance, one often wants to present numbers with a special formatting:

from quoter import LambdaQuoter

f = lambda v: ('(', abs(v), ')') if v < 0 else ('', v, '')
financial = LambdaQuoter(f)
print financial(-3)            # (3)
print financial(45)            # 45

password = LambdaQuoter(lambda v: ('', 'x' * len(v), ''))
print password('secret!')      # xxxxxxx

wf = lambda v:  ('**', v, '**') if v < 0 else ('', v, '')
warning = LambdaQuoter(wf)
print warning(12)              # 12
print warning(-99)             # **-99**

The trick is instantiating LambdaQuoter with a callable (e.g. lambda expression or function) that accepts one value and returns a tuple of three values: the quote prefix, the value (possibly rewritten), and the suffix.

LambdaQuoter is an edge case, arcing over towards being a general formatting function. That has the virtue of providing a consistent mechanism for tactical output transformation with built-in margin and padding support. But, one could argue that such full transformations are “a bridge too far” for a quoting module. So use the dynamic component of``quoter``, or not, as you see fit.

Extended X/HTML Usage

There is an extended quoting mode designed for XML and HTML construction.

Instead of prefix and suffix strings, they use tag names. Or more accurately, tag specifications. Like jQuery XMLQuoter and HTMLQuoter support id and class attributes in a style similar to that of CSS selectors. It also understands that some elements are ‘void’, meaning they do not want or need closing tags.:

from quoter import HTMLQuoter

para = HTMLQuoter('p')
print para('this is great!', {'class':'emphatic'})
print para('this is great!', '.emphatic')

print para('First para!', '#first')

para_e = HTMLQuoter('p.emphatic')
print para_e('this is great!')
print para_e('this is great?', '.question')

br = HTMLQuoter('br', void=True)
print br()

yields:

<p class='emphatic'>this is great!</p>
<p class='emphatic'>this is great!</p>
<p id='first'>First para!</p>
<p class='emphatic'>this is great!</p>
<p class='question'>this is great?</p>
<br>

HTMLQuoter quotes attributes by default with single quotes. If you prefer double quotes, you may set them when the element is defined:

div = HTMLQuoter('div', attquote=double)

HTMLQuoter basically works, but buyer beware: It’s not as well tested as the rest of the module.

XMLQuoter adds one additional attribute: ns for namespaces. Thus:

item = XMLQuoter(tag='item', ns='inv')
print item('an item')

yields:

<inv:item>an item</inv:item>

Alternate API

As an organizational assist, various quoters are available as named attributes of pre-defined quote, xml and html objects. This will also assist in strict imports, such as from quoter import quote, yet without much loss of generality.

For quote, attribute-accessed styles are generally the normal expected styles such as quote.double() or quote.braces() as qualified access points for double() and braces() respectively.

For XML and HTML, individual quoters/styles are automagically generated upon first use based on pre-defined xml and html items. For example, html.b('this') creates an HTMLQuoter(tag='b', name='b') quoter that is cached as html.b for subsequent uses. Want a <strong> tag instead? No problem. html.strong('this'). There is no restriction on the tags you can request, including tags that are not valid HTML. You can also access html.comment() and xml.comment() for commenting purposes.

In the future, additional quoting styles such as ones for Markdown or RST format styles might appear.

A final tweak, if you specify a style attribute and provide the name of a style, that’s the style you get. quote.double(style='single') gives the effect of quote.single. If you don’t want confusing double-bucky forms, don’t use them. qs(...), quote.single(...) or quote(..., style='single') are much simpler, anyway.

Notes

  • quoter provides simple transformations that could be alternatively implemented as a series of small functions. The problem is that such “little functions” tend to be constantly re-implemented, in different ways, and spread through many programs. That need to constantly re-implement such common and straightforward text formatting has led me to re-think how software should format text. quoter is one facet of a project to systematize higher-level formatting operations. See say and show for the larger effort.

  • quoter is also a test case for options, a module that supports flexible option handling.

  • Automated multi-version testing is managed with the magnificent pytest and tox. Now successfully packaged for, and tested against, Python 2.6, 2.7, 3.2, and 3.3, as well as PyPy 2.1 (based on 2.7.3).

  • The author, Jonathan Eunice or @jeunice on Twitter welcomes your comments and suggestions.

Installation

pip install -U quoter

To easy_install under a specific Python version (3.3 in this example):

python3.3 -m easy_install --upgrade quoter

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

Project details


Download files

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

Source Distributions

quoter-1.0.2.zip (24.0 kB view hashes)

Uploaded Source

quoter-1.0.2.tar.gz (16.9 kB view hashes)

Uploaded Source

Supported by

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