WebHelpers is a package designed to ease common tasks developers need that are usually done for formatting or displaying data in templates.
Helpers available by module:
distance_of_time_in_words(from_time, to_time=0, granularity='second', round=False)¶
Return the absolute time-distance string for two datetime objects, ints or any combination you can dream of.
If times are integers, they are interpreted as seconds from now.
granularity dictates where the string calculation is stopped.
If set to seconds (default) you will receive the full string. If
another accuracy is supplied you will receive an approximation.
Available granularities are:
‘century’, ‘decade’, ‘year’, ‘month’, ‘day’, ‘hour’, ‘minute’,
round to true will increase the result by 1 if the fractional
value is greater than 50% of the granularity unit.
>>> distance_of_time_in_words(86399, round=True, granularity='day') '1 day' >>> distance_of_time_in_words(86399, granularity='day') 'less than 1 day' >>> distance_of_time_in_words(86399) '23 hours, 59 minutes and 59 seconds' >>> distance_of_time_in_words(datetime(2008,3,21, 16,34), ... datetime(2008,2,6,9,45)) '1 month, 15 days, 6 hours and 49 minutes' >>> distance_of_time_in_words(datetime(2008,3,21, 16,34), ... datetime(2008,2,6,9,45), granularity='decade') 'less than 1 decade' >>> distance_of_time_in_words(datetime(2008,3,21, 16,34), ... datetime(2008,2,6,9,45), granularity='second') '1 month, 15 days, 6 hours and 49 minutes'
time_ago_in_words(from_time, granularity='second', round=False)¶
Return approximate-time-distance string for
from_time till now.
distance_of_time_in_words but the endpoint is now.
Functions that convert from text markup languages to HTML
markdown(text, markdown=None, **kwargs)¶
Format the text to HTML with Markdown formatting.
Markdown is a wiki-like text markup language, originally written by John Gruber for Perl. The helper converts Markdown text to HTML.
There are at least two Python implementations of Markdown. Markdown <http://www.freewisdom.org/projects/python-markdown/>`_is the original port, and version 2.x contains extensions for footnotes, RSS, etc. Markdown2 is another port which claims to be faster and to handle edge cases better.
You can pass the desired Markdown module as the
argument, or the helper will try to import
markdown. If neither is
available, it will fall back to
webhelpers.markdown, which is
Freewisdom’s Markdown 1.7 without extensions.
If your source text is untrusted and may contain malicious HTML markup,
safe_mode="escape" to escape it,
replace it with a scolding message, or
safe_mode="remove" to strip it.
Format the text to HTML with Textile formatting.
This function uses the PyTextile library which is included with WebHelpers.
Additionally, the output can be sanitized which will fix tags like <img />, <br /> and <hr /> for proper XHTML output.
Powerful HTML helpers that produce more than just simple tags.
Turn all urls and email addresses into clickable links.
>>> auto_link("Go to http://www.planetpython.com and say hello to firstname.lastname@example.org") literal(u'Go to <a href="http://www.planetpython.com">http://www.planetpython.com</a> and say hello to <a href="mailto:email@example.com">firstname.lastname@example.org</a>')
Generate a form containing a sole button that submits to
Use this method instead of
link_to for actions that do not have
the safe HTTP GET semantics implied by using a hypertext link.
The parameters are the same as for
html_attrs that you pass will be applied to the inner
input element. In particular, pass
disabled = True/False
as part of
html_attrs to control whether the button is
disabled. The generated form element is given the class
‘button-to’, to which you can attach CSS styles for display
The submit button itself will be displayed as an image if you
src as followed:
src path should be the exact URL desired. A previous version of
this helper added magical prefixes but this is no longer the case.
# inside of controller for "feeds" >> button_to("Edit", url(action='edit', id=3)) <form method="post" action="/feeds/edit/3" class="button-to"> <div><input value="Edit" type="submit" /></div> </form>
>> button_to("Destroy", url(action='destroy', id=3), .. method='DELETE') <form method="POST" action="/feeds/destroy/3" class="button-to"> <div> <input type="hidden" name="_method" value="DELETE" /> <input value="Destroy" type="submit" /> </div> </form>
# Button as an image. >> button_to("Edit", url(action='edit', id=3), type='image', .. src='icon_delete.gif') <form method="POST" action="/feeds/edit/3" class="button-to"> <div><input alt="Edit" src="/images/icon_delete.gif" type="image" value="Edit" /></div> </form>
This method generates HTML code that represents a form. Forms
are “block” content, which means that you should not try to
insert them into your HTML where only inline content is
expected. For example, you can legally insert a form inside of
td element or in between
p elements, but
not in the middle of a run of text, nor can you place a form
within another form.
(Bottom line: Always validate your HTML before going public.)
Changed in WebHelpers 1.2: Preserve case of “method” arg for XHTML compatibility. E.g., “POST” or “PUT” causes method=”POST”; “post” or “put” causes method=”post”.
highlight(text, phrase, highlighter=None, case_sensitive=False, class_='highlight', **attrs)¶
Highlight all occurrences of
This inserts “<strong class=”highlight”>...</strong>” around every occurrence.
phrasemust be a string if
highlighteris specified. Overrides
phraseis a regex object.
Changed in WebHelpers 1.0b2: new implementation using HTML builder.
phrase to be list or regex. Deprecate
change its default value to None. Add
mail_to(email_address, name=None, cc=None, bcc=None, subject=None, body=None, replace_at=None, replace_dot=None, encode=None, **html_attrs)¶
Create a link tag for starting an email to the specified
email_address is also used as the name of the link unless
name is specified. Additional HTML options, such as class or
id, can be passed in the
You can also make it difficult for spiders to harvest email address by obfuscating them.
You can also specify the cc address, bcc address, subject, and body
parts of the message header to create a complex e-mail using the
arguments. Each of these options are URI escaped and then appended
email_address before being output. Be aware that
>>> mail_to("email@example.com", "My email", cc="firstname.lastname@example.org", bcc="email@example.com", subject="This is an example email", body= "This is the body of the message.") literal(u'<a href="mailto:firstname.lastname@example.org?cc=ccaddress%40domain.com&bcc=bccaddress%40domain.com&subject=This%20is%20an%20example%20email&body=This%20is%20the%20body%20of%20the%20message.">My email</a>')
Strip link tags from
text leaving just the link label.
>>> strip_links('<a href="something">else</a>') 'else'
The MIMEType helper assists in delivering appropriate content types for a single action in a controller, based on several requirements:
If the URL ends in an extension, the mime-type associated with that is given the highest preference. Since some browsers fail to properly set their Accept headers to indicate they should be served HTML, the next check looks to see if its at least in the list. This way those browsers will still get the HTML they are expecting.
Finally, if the client didn’t include an extension, and doesn’t have HTML in the list of Accept headers, than the desired mime-type is returned if the server can send it.
MIMETypes registration mapping
The MIMETypes object class provides a single point to hold onto all the registered mimetypes, and their association extensions. It’s used by the mimetypes method to determine the appropriate content type to return to a client.
Create a MIMEType alias to a full mimetype.
alias may not contain the
Loads a default mapping of extensions and mimetypes
These are suitable for most web applications by default. Additional types can be added by using the mimetypes module.
Check the PATH_INFO of the current request and client’s HTTP Accept to attempt to use the appropriate mime-type.
If a content-type is matched, return the appropriate response
content type, and if running under Pylons, set the response content
type directly. If a content-type is not matched, return
This works best with URLs that end in extensions that differentiate
Since browsers generally allow for any content-type, but should be sent HTML when possible, the html mimetype check should always come first, as shown in the example below.
# some code likely in environment.py MIMETypes.init() MIMETypes.add_alias('html', 'text/html') MIMETypes.add_alias('xml', 'application/xml') MIMETypes.add_alias('csv', 'text/csv') # code in a Pylons controller def someaction(self): # prepare a bunch of data # ...... # prepare MIMETypes object m = MIMETypes(request.environ) if m.mimetype('html'): return render('/some/template.html') elif m.mimetype('atom'): return render('/some/xml_template.xml') elif m.mimetype('csv'): # write the data to a csv file return csvfile else: abort(404) # Code in a non-Pylons controller. m = MIMETypes(environ) response_type = m.mimetype('html') # ``response_type`` is a MIME type or ``False``.
Number formatting and calculation helpers.
format_number(n, thousands=', ', decimal='.')¶
Format a number with a thousands separator and decimal delimiter.
n may be an int, long, float, or numeric string.
thousands is a separator to put after each thousand.
decimal is the delimiter to put before the fractional portion if any.
The default style has a thousands comma and decimal point per American usage:
>>> format_number(1234567.89) '1,234,567.89' >>> format_number(123456) '123,456' >>> format_number(-123) '-123'
Various European and international styles are also possible:
>>> format_number(1234567.89, " ") '1 234 567.89' >>> format_number(1234567.89, " ", ",") '1 234 567,89' >>> format_number(1234567.89, ".", ",") '1.234.567,89'
Return the mean (i.e., average) of a sequence of numbers.
>>> mean([5, 10]) 7.5
Return the median of an iterable of numbers.
The median is the point at which half the numbers are lower than it and half the numbers are higher. This gives a better sense of the majority level than the mean (average) does, because the mean can be skewed by a few extreme numbers at either end. For instance, say you want to calculate the typical household income in a community and you’ve sampled four households:
>>> incomes =  # Fast food crew >>> incomes.append(24000) # Janitor >>> incomes.append(32000) # Journeyman >>> incomes.append(44000) # Experienced journeyman >>> incomes.append(67000) # Manager >>> incomes.append(9999999) # Bill Gates >>> median(incomes) 49500.0 >>> mean(incomes) 1697499.8333333333
The median here is somewhat close to the majority of incomes, while the mean is far from anybody’s income.
This implementation makes a temporary list of all numbers in memory.
What percent of
>>> percent_of(5, 100) 5.0 >>> percent_of(13, 26) 50.0
From the Python Cookbook. Population mode contributed by Lorenzo Catucci.
Standard deviation shows the variability within a sequence of numbers. A small standard deviation means the numbers are close to each other. A large standard deviation shows they are widely different. In fact it shows how far the numbers tend to deviate from the average. This can be used to detect whether the average has been skewed by a few extremely high or extremely low values.
Most natural and random phenomena follow the normal distribution (aka the bell curve), which says that most values are close to average but a few are extreme. E.g., most people are close to 5‘9” tall but a few are very tall or very short. If the data does follow the bell curve, 68% of the values will be within 1 standard deviation (stdev) of the average, and 95% will be within 2 standard deviations. So a university professor grading exams on a curve might give a “C” (mediocre) grade to students within 1 stdev of the average score, “B” (better than average) to those within 2 stdevs above, and “A” (perfect) to the 0.25% higher than 2 stdevs. Those between 1 and 2 stdevs below get a “D” (poor), and those below 2 stdevs... we won’t talk about them.
By default the helper computes the unbiased estimate for the population standard deviation, by applying an unbiasing factor of sqrt(N/(N-1)).
If you’d rather have the function compute the population standard
The following examples are taken from Wikipedia. http://en.wikipedia.org/wiki/Standard_deviation
>>> standard_deviation([0, 0, 14, 14]) 8.082903768654761... >>> standard_deviation([0, 6, 8, 14]) 5.773502691896258... >>> standard_deviation([6, 6, 8, 8]) 1.1547005383792515 >>> standard_deviation([0, 0, 14, 14], sample=False) 7.0 >>> standard_deviation([0, 6, 8, 14], sample=False) 5.0 >>> standard_deviation([6, 6, 8, 8], sample=False) 1.0
(The results reported in Wikipedia are those expected for whole
population statistics and therefore are equal to the ones we get
sample=False in the later tests.)
# Fictitious average monthly temperatures in Southern California. # Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec >>> standard_deviation([70, 70, 70, 75, 80, 85, 90, 95, 90, 80, 75, 70]) 9.003366373785... >>> standard_deviation([70, 70, 70, 75, 80, 85, 90, 95, 90, 80, 75, 70], sample=False) 8.620067027323... # Fictitious average monthly temperatures in Montana. # Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec >>> standard_deviation([-32, -10, 20, 30, 60, 90, 100, 80, 60, 30, 10, -32]) 45.1378360405574... >>> standard_deviation([-32, -10, 20, 30, 60, 90, 100, 80, 60, 30, 10, -32], sample=False) 43.2161878106906...
A container for data and statistics.
This class extends
SimpleStats by calculating additional statistics,
and by storing all data seen. All values must be numeric (
float), and you must call
.finish() to generate
the additional statistics. That’s because the statistics here cannot be
calculated incrementally, but only after all data is known.
>>> stats = Stats() >>> stats.extend([5, 10, 10]) >>> stats.count 3 >>> stats.finish() >>> stats.mean 8.33333333333333... >>> stats.median 10 >>> stats.standard_deviation 2.8867513459481287
All data is stored in a list and a set for later use:
>>> stats.list [5, 10, 10] >> stats.set set([5, 10])
(The double prompt “>>” is used to hide the example from doctest.)
The stat attributes are
None until you call
permissible – though not recommended – to add data after calling
.finish() and then call
.finish() again. This recalculates the
stats over the entire data set.
In addition to the hook methods provided by
._finish-stats to provide additional statistics.
Calculate a few simple statistics on data.
This class calculates the minimum, maximum, and count of all the values given to it. The values are not saved in the object. Usage:
>>> stats = SimpleStats() >>> stats(2) # Add one data value. >>> stats.extend([6, 4]) # Add several data values at once.
The statistics are available as instance attributes:
>>> stats.count 3 >>> stats.min 2 >>> stats.max 6
Non-numeric data is also allowed:
>>> stats2 = SimpleStats() >>> stats2("foo") >>> stats2("bar") >>> stats2.count 2 >>> stats2.min 'bar' >>> stats2.max 'foo'
None until the first data value is
Subclasses can override
._update_stats to add
The constructor accepts one optional argument,
numeric. If true, the
instance accepts only values that are
The default is false, which accepts any value. This is meant for instances
or subclasses that don’t want non-numeric values.
Helpers that are neither text, numeric, container, or date.
pred(elm) true for all elements?
With the default predicate, this is the same as Python 2.5’s
function; i.e., it returns true if all elements are true.
>>> all(["A", "B"]) True >>> all(["A", ""]) False >>> all(["", ""]) False >>> all(["A", "B", "C"], lambda x: x <= "C") True >>> all(["A", "B", "C"], lambda x: x < "C") False
From recipe in itertools docs.
pred(elm) is true for any element?
With the default predicate, this is the same as Python 2.5’s
function; i.e., it returns true if any element is true.
>>> any(["A", "B"]) True >>> any(["A", ""]) True >>> any(["", ""]) False >>> any(["A", "B", "C"], lambda x: x <= "C") True >>> any(["A", "B", "C"], lambda x: x < "C") True
From recipe in itertools docs.
pred(elm) false for all elements?
With the default predicate, this returns true if all elements are false.
>>> no(["A", "B"]) False >>> no(["A", ""]) False >>> no(["", ""]) True >>> no(["A", "B", "C"], lambda x: x <= "C") False >>> no(["X", "Y", "Z"], lambda x: x <="C") True
From recipe in itertools docs.
count_true(seq, pred=<function <lambda> at 0xba553e4>)¶
How many elements is
pred(elm) true for?
With the default predicate, this counts the number of true elements.
>>> count_true([1, 2, 0, "A", ""]) 3 >>> count_true([1, "A", 2], lambda x: isinstance(x, int)) 2
This is equivalent to the
itertools.quantify recipe, which I couldn’t
get to work.
Return the value converted to the type, or None if error.
type_ may be a Python type or any function taking one argument.
>>> print convert_or_none("5", int) 5 >>> print convert_or_none("A", int) None
Flash(session_key='flash', categories=None, default_category=None)¶
Accumulate a list of messages to show at the next page request.
__init__(session_key='flash', categories=None, default_category=None)¶
session_key is the key to save the messages under in the user’s
categories is an optional list which overrides the default list
default_category overrides the default category used for messages
when none is specified.
__call__(message, category=None, ignore_duplicate=False)¶
Add a message to the session.
message is the message text.
category is the message’s category. If not specified, the default
category will be used. Raise
ValueError if the category is not
in the list of allowed categories.
ignore_duplicate is true, don’t add the message if another
message with identical text has already been added. If the new
message has a different category than the original message, change the
original message to the new category.
Return all accumulated messages and delete them from the session.
The return value is a list of
Functions that output text (not HTML).
Helpers for filtering, formatting, and transforming strings.
chop_at(s, sub, inclusive=False)¶
s at the first occurrence of
inclusive is true, truncate just after
sub rather than at it.
>>> chop_at("plutocratic brats", "rat") 'plutoc' >>> chop_at("plutocratic brats", "rat", True) 'plutocrat'
excerpt(text, phrase, radius=100, excerpt_string='...')¶
Extract an excerpt from the
text, or ‘’ if the phrase isn’t
>>> excerpt("hello my world", "my", 3) '...lo my wo...'
sub off the front of
s if present.
>>> lchop("##This is a comment.##", "##") 'This is a comment.##'
The difference between
s.lstrip is that
only the exact prefix, while
s.lstrip treats the argument as a set of
leading characters to delete regardless of order.
plural(n, singular, plural, with_number=True)¶
Return the singular or plural form of a word, according to the number.
with_number is true (default), the return value will be the number
followed by the word. Otherwise the word alone will be returned.
>>> plural(2, "ox", "oxen") '2 oxen' >>> plural(2, "ox", "oxen", False) 'oxen'
sub off the end of
s if present.
>>> rchop("##This is a comment.##", "##") '##This is a comment.'
The difference between
s.rstrip is that
only the exact suffix, while
s.rstrip treats the argument as a set of
trailing characters to delete regardless of order.
Strip the leading whitespace in all lines in
This deletes all leading whitespace.
textwrap.dedent deletes only
the whitespace common to all lines.
truncate(text, length=30, indicator='...', whole_word=False)¶
text with replacement characters.
length, this string will replace the end of the string
>>> truncate('Once upon a time in a world far far away', 14) 'Once upon a...'
Wrap all paragraphs in a text string to the specified width.
width may be an int or a
The latter allows you to set other options besides the width, and is more
efficient when wrapping many texts.