145
A Few of My Favorite Things Mike Pirnat • AG Interactive • CodeMash 2012

A Few of My Favorite (Python) Things

Embed Size (px)

DESCRIPTION

Python's "batteries included" philosophy means that it comes with an astonishing amount of great stuff. On top of that, there's a vibrant world of third-party libraries that help make Python even more wonderful. We'll go on a breezy, example-filled tour through some of my favorites, from treasures in the standard library to great third-party packages that I don't think I could live without, and we'll touch on some of the fuzzier aspects of the Python culture that make it such a joy to be part of.

Citation preview

Page 1: A Few of My Favorite (Python) Things

A Few of My Favorite Things

Mike Pirnat • AG Interactive • CodeMash 2012

Page 2: A Few of My Favorite (Python) Things

A Few of My Favorite Things�������

Mike Pirnat • AG Interactive • CodeMash 2012

Page 3: A Few of My Favorite (Python) Things
Page 4: A Few of My Favorite (Python) Things

Disclaimers

Page 5: A Few of My Favorite (Python) Things
Page 6: A Few of My Favorite (Python) Things
Page 7: A Few of My Favorite (Python) Things
Page 8: A Few of My Favorite (Python) Things
Page 9: A Few of My Favorite (Python) Things

The Language

Page 10: A Few of My Favorite (Python) Things

_ = ( 255, lambda V ,B,c :c and Y(V*V+B,B, c -1)if(abs(V)<6)else ( 2+c-4*abs(V)**-0.4)/i ) ;v, x=1500,1000;C=range(v*x );import struct;P=struct.pack;M,\ j ='<QIIHHHH',open('M.bmp','wb').writefor X in j('BM'+P(M,v*x*3+26,26,12,v,x,1,24))or C: i ,Y=_;j(P('BBB',*(lambda T:(T*80+T**9 *i-950*T **99,T*70-880*T**18+701* T **9 ,T*i**(1-T**45*2)))(sum( [ Y(0,(A%3/3.+X%v+(X/v+ A/3/3.-x/2)/1j)*2.5 /x -2.7,i)**2 for \ A in C [:9]]) /9) ) )

http://preshing.com/20110926/high-resolution-mandelbrot-in-obfuscated-python

Page 12: A Few of My Favorite (Python) Things

The Interactive Shell

$ pythonPython 2.7.1 (r271:86832, Jun 16 2011, 16:59:05) [GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2335.15.00)] on darwinType "help", "copyright", "credits" or "license" for more information.>>>

Page 13: A Few of My Favorite (Python) Things

class Frobulator(object): """It frobulates things."""

def __init__(self, doohickey): """A Frobulator needs a doohickey.""" self.doohickey = doohickey

def frobulate(self): """Frobulate ALL the things!""" print "Frobulating..." doodad = self.doohickey() return FrobulatedThing(doodad)

class FrobulatedThing(object): """A thing which has been frobulated."""

def __init__(self, thing): """Make a thing into a frobulated thing.""" self.thing = thing

Docstrings...

Page 14: A Few of My Favorite (Python) Things

>>> help(Frobulator)

Help on class Frobulator in module frobulator:

class Frobulator(__builtin__.object) | It frobulates things. | | Methods defined here: | | __init__(self, doohickey) | A Frobulator needs a doohickey. | | frobulate(self) | Frobulate ALL the things! | ...

...and Help

Page 15: A Few of My Favorite (Python) Things

Comprehensions

• List comprehensions

• Set comprehensions

• Dictionary comprehensions

• Generator expressions

Page 16: A Few of My Favorite (Python) Things

List Comprehensions

x = [item for item in series]

x = [do_something(item) for item in series if expression]

things = [Thingy.from_data(x) for x in database_results]

partiers = [x for x in codemashers if x.slides_done()]

Page 17: A Few of My Favorite (Python) Things

List Comprehensions

booze = ['beer', 'wine', 'scotch', 'gin']soft_drinks = ['water', 'soda', 'juice']

a = [(x, y) for x in booze for y in soft_drinks]

[('beer', 'water'), ('beer', 'soda'), ('beer', 'juice'), ('wine', 'water'), ('wine', 'soda'), ('wine', 'juice'), ('scotch', 'water'), ('scotch', 'soda'), ('scotch', 'juice'), ('gin', 'water'), ('gin', 'soda'), ('gin', 'juice')]

Page 18: A Few of My Favorite (Python) Things

List Comprehensions

b = [x for x in zip(booze, soft_drinks)]

[('beer', 'water'), ('wine', 'soda'), ('scotch', 'juice')]

Page 19: A Few of My Favorite (Python) Things

Set Comprehensions

s = {v for v in 'CODEMASH ROCKS' if v not in 'ABCD'}

set([' ', 'E', 'H', 'K', 'M', 'O', 'S', 'R'])

Page 20: A Few of My Favorite (Python) Things

Dictionary Comprehensions

d = {key: value for key, value in list_of_tuples}

d = { 'Mike': 'Python', 'Jim': 'Ruby', 'Brad': 'UX', ... }

d1 = {val: key for key, val in d.items()}

{'Python': 'Mike', 'Ruby': 'Jim', 'UX': 'Brad', ...}

Page 21: A Few of My Favorite (Python) Things

Generators

def f(how_many): for number in range(how_many): if number**2 > 3: yield number * 2

for number in f(5): print number

468

Page 22: A Few of My Favorite (Python) Things

Generator Expressions

gen = (2*x for x in range(5) if x**2 > 3)

for number in gen: print number

468

Page 23: A Few of My Favorite (Python) Things

Properties

class Foo(object):

def __init__(self, bar=42): self.set_bar(bar)

def get_bar(self): return self.bar

def set_bar(self, bar): self.bar = int(bar)

Page 24: A Few of My Favorite (Python) Things

Properties

class Foo(object):

def __init__(self, bar=42): self.bar = bar

@property def bar(self): return self._bar

@bar.setter def bar(self, x): self._bar = int(x)

Page 25: A Few of My Favorite (Python) Things

Properties

class Foo(object):

def __init__(self, bar=42): self.bar = bar

@property def bar(self): return self._bar

@bar.setter def bar(self, x): self._bar = int(x)

Page 26: A Few of My Favorite (Python) Things

Properties

>>> foo = Foo()>>> foo.bar42>>> foo.bar = 'abc'Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 9, in barValueError: invalid literal for int() with base 10: 'abc'

Page 27: A Few of My Favorite (Python) Things

Decorators

• That funny-looking @ thing

• Similar to Java’s annotations

• Replaces a function with a wrapper

• Augment functionality in a reusable way

Page 28: A Few of My Favorite (Python) Things

Decorators

def be_excellent(wrapped): def wrapper(*args, **kwargs): print "Be excellent to each other..." return wrapped(*args, **kwargs) return wrapper

@be_excellentdef party_on(who): print "...and party on, {0}!".format(who)

>>> party_on('dudes')Be excellent to each other......and party on, dudes!

Page 29: A Few of My Favorite (Python) Things

Context Managers

• That funny “with” thing

• Simplify calling code that needs setup and teardown

• Alternative to try/finally structures

• Acquire locks, open and close files, do database transactions, etc.

Page 30: A Few of My Favorite (Python) Things

frobulator = Frobulator(...)

try: frobulator.frobulate()finally: frobulator.cleanup()

Before Context Managers

Page 31: A Few of My Favorite (Python) Things

with Frobulator(...) as frobulator: frobulator.frobulate()

After Context Managers

Page 32: A Few of My Favorite (Python) Things

class Frobulator(object):

def __init__(self, ...): ...

def __enter__(self): print "Preparing to frobulate..." return self

def __exit__(self, exc_type, exc_value, exc_tb): print "Frobulation complete!"

def frobulate(self): print "Frobulating!" ...

Page 33: A Few of My Favorite (Python) Things

with Frobulator() as frobulator: frobulator.frobulate()

Preparing to frobulate...Frobulating!Frobulation complete!

Page 34: A Few of My Favorite (Python) Things

import contextlib

@contextlib.contextmanagerdef make_frobulator(): print "Preparing to frobulate..." yield Frobulator() print "Frobulation complete!"

with make_frobulator() as frobulator: frobulator.frobulate()

Preparing to frobulate...Frobulating!Frobulation complete!

Page 35: A Few of My Favorite (Python) Things

The Standard Library

Page 36: A Few of My Favorite (Python) Things

Itertools

• Tools for iterating on sequences

• Inspired by functional languages

• Fast and memory efficient

• Combine to express more complicated algorithms

Page 37: A Few of My Favorite (Python) Things

Itertools: chain

from itertools import chain

for x in chain([1, 2, 3], [42, 1138, 2112]): print x,

1 2 3 42 1138 2112

Page 38: A Few of My Favorite (Python) Things

Itertools: izip

from itertools import izip

for x in izip([1, 2, 3], [42, 1138, 2112]): print x,

(1, 42) (2, 1138) (3, 2112)

Page 39: A Few of My Favorite (Python) Things

Itertools: islicefrom itertools import islice, count

for x in islice(count(), 5): print x,

for x in islice(count(), 5, 10): print x,

for x in islice(count(), 0, 100, 10): print x,

0 1 2 3 45 6 7 8 90 10 20 30 40 50 60 70 80 90

Page 40: A Few of My Favorite (Python) Things

Itertools: islicefrom itertools import islice, count

for x in islice(count(), 5): print x,

for x in islice(count(), 5, 10): print x,

for x in islice(count(), 0, 100, 10): print x,

0 1 2 3 45 6 7 8 90 10 20 30 40 50 60 70 80 90

Page 41: A Few of My Favorite (Python) Things

Itertools: imap

from itertools import imap

for thingy in imap(Thingy.from_data, database_results): ...

Page 42: A Few of My Favorite (Python) Things

Itertools: cyclefrom itertools import cycle

for x in cycle(['wake up', 'meet Ned', 'romance Rita']): print x

wake upmeet Nedromance Ritawake upmeet Nedromance Ritawake upmeet Ned...

Page 43: A Few of My Favorite (Python) Things

Itertools: repeat

from itertools import repeat

for x in repeat("stop hitting yourself", 5): print x

stop hitting yourselfstop hitting yourselfstop hitting yourselfstop hitting yourselfstop hitting yourself

Page 44: A Few of My Favorite (Python) Things

Functools

• Tools for manipulating functions

• Partial

• Wraps a callable with default arguments

• Alternative to lambdas and closures

Page 45: A Few of My Favorite (Python) Things

Functools: Partial

from functools import partial

def f(a, b=2): print a, b

f1 = partial(f, 'fixed_a')f2 = partial(f, b='fixed_b')

>>> f1(b=1138)fixed_a 1138

>>> f2(1138)1138 fixed_b

Page 46: A Few of My Favorite (Python) Things

Functools: Partial

def category_is(category, item): categories = [x.lower() for x in item.categories] if category.lower() in categories: return True return False

is_python = partial(category_is, 'python')is_cat_pictures = partial(category_is, 'catpix')

...

python_posts = [item for item in blog_posts if is_python(item)]

cat_posts = [item for item in blog_posts if is_cat_pictures(item)]

Page 47: A Few of My Favorite (Python) Things

Collections

• Beyond the basic list, dict, tuple, and set...

• Counter

• Defaultdict

• OrderedDict

• Namedtuple

• ...and more

Page 48: A Few of My Favorite (Python) Things

counter = {}for char in "Hello there, CodeMash!": if char not in counter: counter[char] = 1 else: counter[char] += 1

print counter

{'a': 1, ' ': 2, 'C': 1, 'e': 4, 'd': 1, 'H': 1, 'M': 1, 'l': 2, 'o': 2, ',': 1, 's': 1, 'r': 1, '!': 1, 't': 1, 'h': 2}

Counter

Page 49: A Few of My Favorite (Python) Things

counter = {}for char in "Hello there, CodeMash!": if char not in counter: counter[char] = 1 else: counter[char] += 1

print counter

{'a': 1, ' ': 2, 'C': 1, 'e': 4, 'd': 1, 'H': 1, 'M': 1, 'l': 2, 'o': 2, ',': 1, 's': 1, 'r': 1, '!': 1, 't': 1, 'h': 2}

Counter

Page 50: A Few of My Favorite (Python) Things

Counter

from collections import Countercounter = Counter("Hello there, CodeMash!")print counter

Counter({'e': 4, ' ': 2, 'l': 2, 'o': 2, 'h': 2, 'a': 1, 'C': 1, 'd': 1, 'H': 1, 'M': 1, ',': 1, 's': 1, 'r': 1, '!': 1, 't': 1})

Page 51: A Few of My Favorite (Python) Things

Counter

from collections import Countercounter = Counter("Hello there, CodeMash!")print counter

Counter({'e': 4, ' ': 2, 'l': 2, 'o': 2, 'h': 2, 'a': 1, 'C': 1, 'd': 1, 'H': 1, 'M': 1, ',': 1, 's': 1, 'r': 1, '!': 1, 't': 1})

Page 52: A Few of My Favorite (Python) Things

Namedtupleimport math

def distance(a, b): return math.sqrt( (a[0] - b[0])**2 + (a[1] - b[1])**2 + (a[2] - b[2])**2 )

a = (1, 2, 3)b = (-1, -2, 42)print distance(a, b)

39.25557285278104

Page 53: A Few of My Favorite (Python) Things

Namedtuplefrom collections import namedtuplePoint = namedtuple('Point', 'x y z')

def distance(a, b): return math.sqrt( (a.x - b.x)**2 + (a.y - b.y)**2 + (a.z - b.z)**2 )

a = Point(x=1, y=2, z=3)b = Point(-1, -2, 42)print distance(a, b)

39.25557285278104

Page 54: A Few of My Favorite (Python) Things

s1 = """Lorem ipsum dolor sit amet, consectetur adipiscing elit.Phasellus dui nunc, faucibus id ullamcorper eget, tempusvitae nisl. Donec quis semper risus. Curabitur sit amettellus eget metus accumsan porta nec nec lorem. Ut vitaesem nisl. Praesent pulvinar feugiat nibh fringilla semper. Nullam cursus tempor lorem ut egestas. Nullamsuscipit gravida turpis ac porttitor. Curabitur eleifendaugue at risus commodo pretium. Aliquam eget magnarisus, ut lobortis metus. Cum sociis natoque penatibuset magnis dis parturient montes, nascetur ridiculus mus.Etiam non magna sit amet nulla porttitor molestie sitamet vel sem. Vestibulum sit amet nisl a velitadipiscing porta id non urna. Duis ullamcorper dictumipsum sit amet congue."""

Difflib

Page 55: A Few of My Favorite (Python) Things

Difflibs2 = """Lorem ipsum dolor sit amet, consectetur adipiscing elit.Phasellus dui nunc, faucibus id ullamcorper eget, tempusvitae nisl. Donec quis semper risus. Curabitur sit amettellus eget metus accumsan porta nec nec lorem. Ut vitaesem nisl. Praesent pulvinar feugiat nibh fringilla semper. Nullam cursus tempor lorem ut egestas. Nullamsuscipit gravida turpis ac porttitor. Curabitur eleifendaugue at risus commodo pretium. Aliquam eget magnarisus, ut lobortis montes. Cum sociis natoque penatibuset magnis dis parturient metus, nascetur ridiculus mus.Etiam non magna sit amet nulla porttitor molestie sitamet vel sem. Vestibulum sit amet nisl a velitadipiscing porta id non urna. Duis ullamcorper dictumipsum sit amet congue."""

Page 56: A Few of My Favorite (Python) Things

Difflib

import difflib

differ = difflib.Differ()diff = differ.compare(s1.splitlines(), s2.splitlines())print '\n'.join(diff)

Page 57: A Few of My Favorite (Python) Things

Difflib Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus dui nunc, faucibus id ullamcorper eget, tempus vitae nisl. Donec quis semper risus. Curabitur sit amet tellus eget metus accumsan porta nec nec lorem. Ut vitae sem nisl. Praesent pulvinar feugiat nibh fringilla semper. Nullam cursus tempor lorem ut egestas. Nullam suscipit gravida turpis ac porttitor. Curabitur eleifend augue at risus commodo pretium. Aliquam eget magna- risus, ut lobortis metus. Cum sociis natoque penatibus? --

+ risus, ut lobortis montes. Cum sociis natoque penatibus? +++

- et magnis dis parturient montes, nascetur ridiculus mus.? ^^ ^

+ et magnis dis parturient metus, nascetur ridiculus mus.? ^ ^

Etiam non magna sit amet nulla porttitor molestie sit amet vel sem. Vestibulum sit amet nisl a velit adipiscing porta id non urna. Duis ullamcorper dictum ipsum sit amet congue.

Page 58: A Few of My Favorite (Python) Things

Difflib

diff = difflib.unified_diff( s1.splitlines(), s2.splitlines(), lineterm='')print '\n'.join(diff)

Page 59: A Few of My Favorite (Python) Things

Difflib

--- +++ @@ -7,8 +7,8 @@ semper. Nullam cursus tempor lorem ut egestas. Nullam suscipit gravida turpis ac porttitor. Curabitur eleifend augue at risus commodo pretium. Aliquam eget magna-risus, ut lobortis metus. Cum sociis natoque penatibus-et magnis dis parturient montes, nascetur ridiculus mus.+risus, ut lobortis montes. Cum sociis natoque penatibus+et magnis dis parturient metus, nascetur ridiculus mus. Etiam non magna sit amet nulla porttitor molestie sit amet vel sem. Vestibulum sit amet nisl a velit adipiscing porta id non urna. Duis ullamcorper dictum

Page 60: A Few of My Favorite (Python) Things

Ast

• Use Python to process abstract syntax trees of Python grammar

• Helps introspect about what the current grammar looks like

• Helps write secure code

Page 61: A Few of My Favorite (Python) Things

Ast

# Danger:foo = eval(bar)

# Safe:foo = ast.literal_eval(bar)

Page 62: A Few of My Favorite (Python) Things

Multiprocessing

• Like threading, but with subprocesses

• Local and remote concurrency

• Spawn processes or entire pools

• Communicate via queues or pipes

• Shared state via shared memory or manager/server process

Page 63: A Few of My Favorite (Python) Things

import osfrom multiprocessing import Process

def info(title): print title print 'parent process:', os.getppid() print 'process id:', os.getpid()

def f(name): info('function f') print 'hello', name

if __name__ == '__main__': info('main') p = Process(target=f, args=('world',)) p.start() p.join()

Page 64: A Few of My Favorite (Python) Things

$ python process.py mainparent process: 18647process id: 31317----------function fparent process: 31317process id: 31318----------hello world

Page 65: A Few of My Favorite (Python) Things

import osfrom multiprocessing import Pool

def g(x): info('function g('+str(x)+')') return x * x

if __name__ == '__main__': info('main') pool = Pool(2) print pool.map(g, range(100))

Page 66: A Few of My Favorite (Python) Things

$ python process.py mainmodule name: __main__parent process: 18647process id: 31369----------function g(0)module name: __main__parent process: 31369process id: 31370----------function g(1)module name: __main__parent process: 31369process id: 31370----------function g(13)function g(2)module name: __main__parent process: 31369process id: 31370----------module name: __main__function g(3)module name: __main__

parent process: 31369parent process: 31369process id: 31370process id: 31371--------------------function g(4)module name: __main__parent process: 31369function g(14)process id: 31370module name: __main__----------parent process: 31369function g(5)process id: 31371module name: __main__

...

[0, 1, 4, 9, 16, 25, ..., 8836, 9025, 9216, 9409, 9604, 9801]

Page 67: A Few of My Favorite (Python) Things

Third Party Packages

Page 68: A Few of My Favorite (Python) Things

Third Party Packages

• Most available on PyPI--http://pypi.python.org

• pip install packagename

• easy_install packagename

Page 69: A Few of My Favorite (Python) Things

Virtualenv

• Makes an isolated Python environment

• Don’t pollute your global site-packages

• Insulates Python projects from one another

• Don't have to be root

Page 70: A Few of My Favorite (Python) Things

Virtualenv

$ virtualenv directory

$ virtualenv --python=/path/to/specific/python directory

$ cd directory$ . bin/activate

$ easy_install whatever$ pip install whatever

...do stuff...

$ deactivate

Page 71: A Few of My Favorite (Python) Things

Datetime and Dateutil

• Python’s datetime provides date, time, datetime, and timedelta objects

• Dateutil provides powerful extensions

• Parsing

• Olson-driven timezones

• Recurrence

Page 72: A Few of My Favorite (Python) Things

Parsing

>>> from dateutil.parser import parse

>>> parse('01/01/2012')datetime.datetime(2012, 1, 1, 0, 0)

>>> parse('2012-01-01')datetime.datetime(2012, 1, 1, 0, 0)

Page 73: A Few of My Favorite (Python) Things

>>> from dateutil import zoneinfo

>>> zone = zoneinfo.gettz('US/Eastern')>>> zonetzfile('America/New_York')>>> zone2 = zoneinfo.gettz('US/Hawaii')

>>> dt = datetime.now(zone)>>> dtdatetime.datetime(2012, 1, 13, 13, 46, 54, 997825, tzinfo=tzfile('America/New_York'))

>>> dt.astimezone(zone2)datetime.datetime(2012, 1, 13, 8, 46, 54, 997825, tzinfo=tzfile('Pacific/Honolulu'))

Timezones

Page 74: A Few of My Favorite (Python) Things

Recurrence

>>> from dateutil.rrule import rrule, YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, SECONDLY, BY_EASTER

>>> rr = rrule(YEARLY, dtstart=datetime(1948, 2, 24))

>>> rr.after(datetime(2012, 1, 1))datetime(2012, 2, 24, 0, 0)

>>> rr.between(datetime(2010, 1, 1), datetime(2012, 1, 1))[datetime(2010, 2, 24, 0, 0), datetime(2011, 2, 24, 0, 0)]

Page 75: A Few of My Favorite (Python) Things

howoldismykid.comdef periods_between(freq, start_date, end_date):

    # dateutil.rrule falls down for monthly recurrences where the start # date's day is greater than the number of days in a subsequent month # in the range; ie, when start date is 10/31/2010, and end date is # 3/13/2011, rrule's between will only produce 2 instances, 12/31 and # 1/31, rather than 4.    if freq == MONTHLY and start_date.day > 28:        start_date = datetime.datetime(start_date.year, start_date.month,                28, start_date.hour, start_date.minute, start_date.second)

    # Same problem but for "Pirates of Penzance" leap day birthdays...    elif freq == YEARLY and is_leap_day(start_date):        start_date = datetime.datetime(start_date.year, start_date.month,                28, start_date.hour, start_date.minute, start_date.second)

    rr = rrule(freq, dtstart=start_date)    periods = len(rr.between(start_date, end_date))    return periods

Page 76: A Few of My Favorite (Python) Things

howoldismykid.com>>> start_date = datetime(2007, 9, 10)>>> end_date = datetime(2012, 1, 13)

>>> periods_between(YEARLY, start_date, end_date)4

>>> periods_between(MONTHLY, start_date, end_date)52

>>> periods_between(WEEKLY, start_date, end_date)226

>>> periods_between(DAILY, start_date, end_date)1585

Page 77: A Few of My Favorite (Python) Things

Nose

• Find, run, and report results of test suites

• Low-friction setup

• Extendable with plugins (more later)

• http://readthedocs.org/docs/nose/en/latest/

Page 78: A Few of My Favorite (Python) Things

Nose

# test_foo.py

def test_a_thing(): assert False

def test_another_thing(): assert True

Page 79: A Few of My Favorite (Python) Things

$ nosetests tests/test_foo.py F.======================================================================FAIL: test_foo.test_a_thing----------------------------------------------------------------------Traceback (most recent call last): File "/Users/mpirnat/Code/frobulator/lib/python2.7/site-packages/nose-1.1.2-py2.7.egg/nose/case.py", line 197, in runTest self.test(*self.arg) File "/Users/mpirnat/Code/frobulator/src/frobulator/tests/test_foo.py", line 2, in test_a_thing assert FalseAssertionError

----------------------------------------------------------------------Ran 2 tests in 0.001s

FAILED (failures=1)

Nose

Page 80: A Few of My Favorite (Python) Things

Nose

class TestWhenFrobulating(object):

def setup(self): self.frobulator = Frobulator() self.frobulator.frobulate()

def test_that_it_frobulated(self): assert self.frobulator.has_frobulated

def teardown(self): self.frobulator.cleanup()

Page 81: A Few of My Favorite (Python) Things

Nose

$ nosetests.--------------------------------------------------------Ran 1 test in 0.005s

OK

Page 82: A Few of My Favorite (Python) Things

Nose: Useful Flags

• --processes=number

• -x

• --pdb

• --pdb-failures

• --failed

Page 83: A Few of My Favorite (Python) Things

Nose Plugins

• Add custom behavior to your test run

• Get coverage statistics (coverage)

• Measure cleanliness with PEP-8 (tissue)

• Emit human-readable spec-style results (pinocchio)

• Or write your own...

Page 84: A Few of My Favorite (Python) Things

class TestGeneratesLotsOfFailures(object):

def test_generates_failures(self):

def _make_a_test(i): # start with some wins if i < 7: assert True

# but then it hits the fan... elif i < 30: assert False

# then be a little random elif i % 3 == 0: assert False else: assert True

for i in range(50): yield _make_a_test, i

Page 85: A Few of My Favorite (Python) Things

$ nosetests test_epic_fail.py

.......FFFFFFFFFFFFFFFFFFFFFFFF..F..F..F..F..F..F.[lots of test failure output; use your imagination...]--------------------------------------------------------Ran 50 tests in 0.010s

FAILED (errors=30)

Page 86: A Few of My Favorite (Python) Things
Page 87: A Few of My Favorite (Python) Things

import osfrom nose.plugins import Plugin

class F7U12(Plugin):

name = 'f7u12' enabled = True

def options(self, parser, env=os.environ): super(F7U12, self).options(parser, env=env)

def configure(self, options, config): super(F7U12, self).configure(options, config) self.config = config if not self.enabled: return

Page 88: A Few of My Favorite (Python) Things

def setOutputStream(self, stream): self.stream = stream return self.stream

def begin(self): self.failure_count = 0 def handleFailure(self, test, err): self.failure_count += 1

if self.failure_count < 8: self.stream.write('F') else: self.stream.write('U') return True

Page 89: A Few of My Favorite (Python) Things

$ nosetests --with-f7u12 test_epic_fail.py

.......FFFFFFFFUUUUUUUUUUUUUUUU..U..U..U..U..U..U.[lots of test failure output; use your imagination...]--------------------------------------------------------Ran 50 tests in 0.010s

FAILED (errors=30)

Page 90: A Few of My Favorite (Python) Things

Mock

• Mock object framework

• Avoid writing custom stubs

• Uses “Action/Assert” model (not “Record/Replay”)

• Can also patch out module and class attributes in the scope of a test

• http://www.voidspace.org.uk/python/mock/

Page 91: A Few of My Favorite (Python) Things

Mock

from mock import Mock

foo = Mock()foo.bar()foo.bar.baz(42)foo.bar.baz.return_value = "whatever"

assert foo.bar.calledfoo.bar.baz.assert_called_with(42)

Page 92: A Few of My Favorite (Python) Things

Mock

class TestWhenFrobulating(object):

def setup(self): self.doohickey = Mock() self.frobulator = Frobulator(self.doohickey) self.frobulator.frobulate()

def test_it_uses_the_doohickey(self): assert self.doohickey.called

Page 93: A Few of My Favorite (Python) Things

Mockfrom mock import patch

class TestWhenFrobulating(object):

@patch('frobulator.DingleHopper') def setup(self, dingle_hopper_class): self.dingle_hopper = Mock() dingle_hopper_class.return_value = \ self.dingle_hopper self.frobulator = Frobulator() self.frobulator.frobulate()

def test_it_uses_a_dingle_hopper(self): assert self.dingle_hopper.called

Page 94: A Few of My Favorite (Python) Things

Coverage

• Measure coverage of your codebase during program execution

• Integrates with several test runners to measure test coverage

• http://nedbatchelder.com/code/coverage/

Page 95: A Few of My Favorite (Python) Things

#!/usr/bin/env python

class Frobulator(object): """It frobulates things."""

def __init__(self, doohickey): """A Frobulator needs a doohickey.""" self.doohickey = doohickey

def frobulate(self): """Frobulate ALL the things!""" print "Frobulating..." doodad = self.doohickey() return FrobulatedThing(doodad)

class FrobulatedThing(object): """A thing which has been frobulated."""

def __init__(self, thing): """Make a thing into a frobulated thing.""" self.thing = thing

if __name__ == '__main__': x = FrobulatedThing(42)

Page 96: A Few of My Favorite (Python) Things

#!/usr/bin/env python

class Frobulator(object): """It frobulates things."""

def __init__(self, doohickey): """A Frobulator needs a doohickey.""" self.doohickey = doohickey

def frobulate(self): """Frobulate ALL the things!""" print "Frobulating..." doodad = self.doohickey() return FrobulatedThing(doodad)

class FrobulatedThing(object): """A thing which has been frobulated."""

def __init__(self, thing): """Make a thing into a frobulated thing.""" self.thing = thing

if __name__ == '__main__': x = FrobulatedThing(42)

Page 97: A Few of My Favorite (Python) Things

$ coverage run frobulator.py

$ coverage report -mName Stmts Miss Cover Missing------------------------------------------frobulator 12 4 67% 8, 12-14

Page 98: A Few of My Favorite (Python) Things

from mock import Mock, patchfrom frobulator import Frobulator, FrobulatedThing

class TestWhenFrobulating(object):

def setup(self): self.doohickey = Mock() self.doohickey.return_value = 42

self.frobulator = Frobulator(self.doohickey) self.result = self.frobulator.frobulate()

def test_it_uses_a_doohickey(self): assert self.doohickey.called

def test_it_returns_a_frobulated_thing(self): assert isinstance(self.result, FrobulatedThing)

Page 99: A Few of My Favorite (Python) Things

$ nosetests --with-coverage --cover-package frobulator..Name Stmts Miss Cover Missing------------------------------------------frobulator 12 1 92% 26------------------------------------------Ran 2 tests in 0.008s

OK

Page 100: A Few of My Favorite (Python) Things

Useful Flags

• --cover-erase

• --cover-inclusive

• --cover-tests

Page 101: A Few of My Favorite (Python) Things

Lettuce

• Like Cucumber

• BDD test framework

• Natural language augmented by code

• Big wins pairing Devs + QA + Biz

• http://lettuce.it/

Page 102: A Few of My Favorite (Python) Things

Lettuce Vocabulary

• Features

• Scenarios

• Steps

• World

• Terrain

Page 103: A Few of My Favorite (Python) Things

Feature: The website has a homepage In order to demo Lettuce As a presenter I want to test the homepage of a website

Scenario: Verify the site is up Given I access the url "http://localhost:8000/" Then I get a status "200"

Page 104: A Few of My Favorite (Python) Things

from lettuce import step, worldfrom nose.tools import assert_equalsimport requests

@step(r'I access the url "(.*)"')def access_url(step, url): world.response = requests.get(url)

@step(r'I get a status "(.*)"')def get_status(step, expected_status): expected_status = int(expected_status) assert_equals(world.response.status_code, expected_status)

Page 105: A Few of My Favorite (Python) Things

Snake-Guice

• Framework for Dependency Injection

• Reduce coupling!

• Improve testability!

• http://code.google.com/p/snake-guice/

Page 106: A Few of My Favorite (Python) Things

Snake-Guice

• Bind identifiers to things you want to inject

• Decorate code with injection hints

• Use injector to get instance with all dependencies resolved and injected

• Construct with mocks in tests, minimize the need for patching

Page 107: A Few of My Favorite (Python) Things

Define the Identifier

class IFrobulator(object): pass

Page 108: A Few of My Favorite (Python) Things

Decorate Your Code

from snakeguice import inject

class WankelRotator(object):

@inject(frobulator=IFrobulator) def __init__(self, frobulator): self.frobulator = frobulator

Page 109: A Few of My Favorite (Python) Things

Decorate Your Code

from snakeguice import inject

class WankelRotator(object):

@inject(frobulator=IFrobulator) def __init__(self, frobulator): self.frobulator = frobulator

Page 110: A Few of My Favorite (Python) Things

Configure Bindings

from frobulator import Frobulator

class BindingModule(object):

def configure(self, binder): binder.bind(IFrobulator, to=Frobulator)

Page 111: A Few of My Favorite (Python) Things

Get an Instance

from snakeguice import Injectorimport WankelRotator

injector = Injector(BindingModule())rotator = injector.get_instance(WankelRotator)

Page 112: A Few of My Favorite (Python) Things

Requests

• A human-friendly alternative to urllib2

• Maturing rapidly

• http://python-requests.org/

Page 113: A Few of My Favorite (Python) Things

Using Requests

import requests

# Authentication!r = requests.get('https://api.github.com', auth=('user', 'pass'))

print r.status_codeprint r.headers['content-type']

Page 114: A Few of My Favorite (Python) Things

Using Requests

# Posting name-value pair form datapost_data = {'foo': 'bar', 'baz': 'quux', ...}r = requests.post(url, data=post_data)

# Posting a glob of JSONr = requests.post(url, data=json.dumps(post_data))

# Multipart file attachmentfiles = {'my_file.xls': open('my_file.xls', 'rb')}r = requests.post(url, files=files)

Page 115: A Few of My Favorite (Python) Things

Using Requests

# Custom headers!headers = {'X-foo': 'bar'}r = requests.get(url, headers=headers)

# Cookies!cookies = {'a_cookie': 'is delicious'}r = requests.get(url, cookies=cookies)

Page 116: A Few of My Favorite (Python) Things

Pylons

• http://docs.pylonsproject.org/en/latest/docs/pylons.html

• MVCish web framework

• Glues together several component projects

• Can use your preferred components

• Somewhat dead (evolved into Pyramid)

Page 117: A Few of My Favorite (Python) Things

Pylons

• Request/response: webob

• URL dispatch: routes

• Input validation: formencode

• Persistence/ORM: sqlalchemy

• Session: beaker

• Templates: mako

Page 118: A Few of My Favorite (Python) Things

from routes import Mapper

def make_map(config): """Create, configure and return the routes Mapper""" map = Mapper(directory=config['pylons.paths']['controllers'], always_scan=config['debug']) ...

map.connect('/', controller='main', action='index') map.connect('/contact', controller='contact', action='index', conditions={'method':'GET'}) map.connect('/contact', controller='contact', action='send', conditions={'method':'POST'})

return map

Page 119: A Few of My Favorite (Python) Things

from howoldismykid.lib.base import BaseController, \ render

class MainController(BaseController):

def index(self): # Return a rendered template return render('/main.mako')

Page 120: A Few of My Favorite (Python) Things

from formencode import Schema, validators

class ContactForm(Schema): email = validators.Email(not_empty=True) subject = validators.String(not_empty=True) message = validators.String(not_empty=True)

Page 121: A Few of My Favorite (Python) Things

from pylons.decorators import validatefrom howoldismykid.lib.base import BaseController, renderfrom howoldismykid.model.forms import ContactForm

class ContactController(BaseController):

def __init__(self, *args, **kwargs): BaseController.__init__(self, *args, **kwargs) self.emailer = Emailer(SMTP, USER, PW, FAKE_EMAIL)

def index(self): # Return a rendered template return render('/contact.mako')

@validate(schema=ContactForm(), form="index", prefix_error=False) def send(self): validated = self.form_result self.emailer.send_mail(validated['email'], EMAIL, validated['subject'], validated['message']) return render('/contact_done.mako')

Page 122: A Few of My Favorite (Python) Things

<!DOCTYPE HTML><html> <head> ${self.head_tags()} ... </head> <body> <div id="header">...</div> <div id="content"> ${self.body()} </div> <div id="footer">...</div>

<%include file="google_analytics.mako"/> ${self.foot_tags()}

</body></html>

Page 123: A Few of My Favorite (Python) Things

<%inherit file="/base.mako" />

<%def name="head_tags()"><title>How Old Is My Kid?</title></%def>

<h2>Contact Us</h2>

<p>Suggest a new feature, let us know how we're doing, or just say hi.</p>

<form method="POST" action="/contact" id="contact-form"> <p><label for="email">Email:</label> <input type="text" name="email" id="email" /></p> <p><label for="subject">Subject:</label> <input type="text" name="subject" id="subject" /></p> <p><label for="message">Message:</label><br /><textarea name="message" id="message"></textarea></p> <p><input type="submit" value="Submit" /></p></form>

<%def name="foot_tags()"><script type="text/javascript">$(document).ready(function() { $("button, input:submit").button();});</script></%def>

Page 124: A Few of My Favorite (Python) Things

Django

• https://www.djangoproject.com/

• Full stack/harder to replace components

• Lots of reusable apps

• Admin interface

• Lots of deployment options (Google)

• Not dead

Page 125: A Few of My Favorite (Python) Things

from django.conf.urls.defaults import patterns, url

urlpatterns = patterns('', url(r'^/$', 'howoldismykid.views.main', name='main'), url(r'^/contact/$', 'howoldismykid.views.contact', name='contact'),)

Page 126: A Few of My Favorite (Python) Things

from django.shortcuts import render

def main(request): return render(request, 'main.html')

Page 127: A Few of My Favorite (Python) Things

from django import forms

class ContactForm(forms.Form):

email = forms.EmailField(label='Email Address') subject = forms.CharField(label='Subject') message = forms.CharField(label='Message', widget=forms.Textarea)

Page 128: A Few of My Favorite (Python) Things

from contact.forms import ContactFormfrom django.shortcuts import render

def contact(request): if request.method == 'POST': form = ContactForm(request.POST) if form.is_valid(): emailer = \ Emailer(SMTP, USER, PW, FAKE_EMAIL) emailer.send_mail( form.cleaned_data['email'], EMAIL, form.cleaned_data['subject'], form.cleaned_data['message']) return render(request, 'contact_done.html') else: form = ContactForm()

return render(request, 'contact.html', {'form': form })

Page 129: A Few of My Favorite (Python) Things

<!DOCTYPE HTML><html> <head> {% block head_tags %}...{% endblock %} ... </head> <body> <div id="header">...</header> <div id="content"> {% block content %}It goes here.{% endblock %} </div> <div id="footer">...</div>

{% include "google_analytics.html" %} {% block foot_tags %}{% endblock %}

</body></html>

Page 130: A Few of My Favorite (Python) Things

{% extends 'base.html' %}

{% block head_tags %}<title>How Old Is My Kid?</title>{% endblock %}

{% block content %}<h2>Contact Us</h2>

<p>Suggest a new feature, let us know how we're doing, or just say hi.</p>

<form method="POST" action="/contact" id="contact-form"> {% csrf_token %} {{ form.as_p }} <p><input type="submit" value="Submit" /></p></form>{% endblock %}

{% block foot_tags %}<script type="text/javascript">$(document).ready(function() { $("button, input:submit").button();});</script>{% endblock %}

Page 131: A Few of My Favorite (Python) Things

The Culture

Page 132: A Few of My Favorite (Python) Things

Planet Python

• http://planet.python.org/

• Aggregate feed of Python blogs

• Great way to follow what's going on

• Minimize newsgroup/mailing list burdens

• Easy to be included

Page 133: A Few of My Favorite (Python) Things

The Python Ecosystem

• http://mirnazim.org/writings/python-ecosystem-introduction/

• Great introduction to the Python ecosystem

• Everything you need to get up and running

Page 134: A Few of My Favorite (Python) Things

The Hitchhiker’s Guide

• http://docs.python-guide.org/

• Opinionated advice about using Python

• Basics

• Scenario-specific details and recommendations

Page 135: A Few of My Favorite (Python) Things

PyMotW

• Explored a different standard library module every week

• Examples, examples, examples

• http://www.doughellmann.com/PyMOTW/

• The Python Standard Library by Example

http://www.doughellmann.com/books/byexample/

Page 136: A Few of My Favorite (Python) Things

Podcasts

• http://www.radiofreepython.com/

• http://frompythonimportpodcast.com/

• http://djangodose.com/

• http://advocacy.python.org/podcasts/

• http://www.awaretek.com/python/

• Start your own! Be the change you want...

Page 137: A Few of My Favorite (Python) Things

PyOhio

• Free as in $0.00

• Columbus, Ohio

• Last weekend in July

• http://pyohio.org

Page 138: A Few of My Favorite (Python) Things

PyCon

• Great people

• Great vibe

• All volunteer–personal ownership

• Video! http://pycon.blip.tv/

• 2012 & 2013: Santa Clara

• 2014 & 2015: Montreal

Page 139: A Few of My Favorite (Python) Things

TiP BoF

• Testing in Python Birds of a Feather

• Lightning Talks

• Heckling...

Page 140: A Few of My Favorite (Python) Things

...and goats

Page 141: A Few of My Favorite (Python) Things

The Zen

• Guiding principles

• Sage advice

• >>> import this

Page 142: A Few of My Favorite (Python) Things

The Zen• Beautiful is better than ugly.

• Explicit is better than implicit.

• Simple is better than complex.

• Complex is better than complicated.

• Flat is better than nested.

• Sparse is better than dense.

• Readability counts.

• Special cases aren't special enough to break the rules.

• Although practicality beats purity.

• Errors should never pass silently.

• Unless explicitly silenced.

Page 143: A Few of My Favorite (Python) Things

The Zen• In the face of ambiguity,

refuse the temptation to guess.

• There should be one– and preferably only one–obvious way to do it.

• Although that way may not be obvious at first unless you're Dutch.

• Now is better than never.

• Although never is often better than right now.

• If the implementation is hard to explain, it's a bad idea.

• If the implementation is easy to explain, it may be a good idea.

• Namespaces are one honking great idea -- let's do more of those!

Page 144: A Few of My Favorite (Python) Things

What About You?

Page 145: A Few of My Favorite (Python) Things

Fin

• Twitter : @mpirnat

• Blog: http://mike.pirnat.com

• Win a special prize–name all the movies

• Thanks for coming!