Upcoming SlideShare
×

# A Few of My Favorite (Python) Things

20,191 views

Published on

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.

1 Comment
48 Likes
Statistics
Notes
• Full Name
Comment goes here.

Are you sure you want to Yes No
• Goodies ...

Are you sure you want to  Yes  No
Views
Total views
20,191
On SlideShare
0
From Embeds
0
Number of Embeds
3,199
Actions
Shares
0
436
1
Likes
48
Embeds 0
No embeds

No notes for slide

### A Few of My Favorite (Python) Things

1. 1. A Few of My Favorite Things Mike Pirnat • AG Interactive • CodeMash 2012
2. 2. A Few of My Favorite Things Mike Pirnat • AG Interactive • CodeMash 2012
3. 3. Disclaimers
4. 4. The Language
5. 5. _ ( = 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
6. 6. http://preshing.com/20110926/high-resolution-mandelbrot-in-obfuscated-python
7. 7. 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 build2335.15.00)] on darwinType "help", "copyright", "credits" or "license" formore information.>>>
8. 8. Docstrings...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
9. 9. ...and Help>>> 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! | ...
10. 10. Comprehensions• List comprehensions• Set comprehensions• Dictionary comprehensions• Generator expressions
11. 11. List Comprehensionsx = [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()]
12. 12. List Comprehensionsbooze = [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)]
13. 13. List Comprehensionsb = [x for x in zip(booze, soft_drinks)][(beer, water), (wine, soda), (scotch, juice)]
14. 14. Set Comprehensionss = {v for v in CODEMASH ROCKS if v not in ABCD}set([ , E, H, K, M, O, S, R])
15. 15. 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, ...}
16. 16. Generatorsdef f(how_many): for number in range(how_many): if number**2 > 3: yield number * 2for number in f(5): print number468
17. 17. Generator Expressionsgen = (2*x for x in range(5) if x**2 > 3)for number in gen: print number468
18. 18. Propertiesclass 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)
19. 19. Propertiesclass 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)
20. 20. Propertiesclass 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)
21. 21. Properties>>> foo = Foo()>>> foo.bar42>>> foo.bar = abcTraceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 9, in barValueError: invalid literal for int() with base 10:abc
22. 22. Decorators• That funny-looking @ thing• Similar to Java’s annotations• Replaces a function with a wrapper• Augment functionality in a reusable way
23. 23. Decoratorsdef 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!
24. 24. Context Managers• That funny “with” thing• Simplify calling code that needs setup and teardown• Alternative to try/ﬁnally structures• Acquire locks, open and close ﬁles, do database transactions, etc.
25. 25. Before Context Managersfrobulator = Frobulator(...)try: frobulator.frobulate()finally: frobulator.cleanup()
26. 26. After Context Managerswith Frobulator(...) as frobulator: frobulator.frobulate()
27. 27. 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!" ...
28. 28. with Frobulator() as frobulator: frobulator.frobulate()Preparing to frobulate...Frobulating!Frobulation complete!
29. 29. 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!
30. 30. The Standard Library
31. 31. Itertools• Tools for iterating on sequences• Inspired by functional languages• Fast and memory efﬁcient• Combine to express more complicated algorithms
32. 32. Itertools: chainfrom itertools import chainfor x in chain([1, 2, 3], [42, 1138, 2112]): print x,1 2 3 42 1138 2112
33. 33. Itertools: izipfrom itertools import izipfor x in izip([1, 2, 3], [42, 1138, 2112]): print x,(1, 42) (2, 1138) (3, 2112)
34. 34. Itertools: islicefrom itertools import islice, countfor 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
35. 35. Itertools: islicefrom itertools import islice, countfor 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
36. 36. Itertools: imapfrom itertools import imapfor thingy in imap(Thingy.from_data, database_results): ...
37. 37. Itertools: cyclefrom itertools import cyclefor x in cycle([wake up, meet Ned, romance Rita]): print xwake upmeet Nedromance Ritawake upmeet Nedromance Ritawake upmeet Ned...
38. 38. Itertools: repeatfrom itertools import repeatfor x in repeat("stop hitting yourself", 5): print xstop hitting yourselfstop hitting yourselfstop hitting yourselfstop hitting yourselfstop hitting yourself
39. 39. Functools• Tools for manipulating functions• Partial• Wraps a callable with default arguments• Alternative to lambdas and closures
40. 40. Functools: Partialfrom functools import partialdef f(a, b=2): print a, bf1 = partial(f, fixed_a)f2 = partial(f, b=fixed_b)>>> f1(b=1138)fixed_a 1138>>> f2(1138)1138 fixed_b
41. 41. Functools: Partialdef category_is(category, item): categories = [x.lower() for x in item.categories] if category.lower() in categories: return True return Falseis_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)]
42. 42. Collections• Beyond the basic list, dict, tuple, and set...• Counter• Defaultdict• OrderedDict• Namedtuple• ...and more
43. 43. Countercounter = {}for char in "Hello there, CodeMash!": if char not in counter: counter[char] = 1 else: counter[char] += 1print 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}
44. 44. Countercounter = {}for char in "Hello there, CodeMash!": if char not in counter: counter[char] = 1 else: counter[char] += 1print 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}
45. 45. Counterfrom collections import Countercounter = Counter("Hello there, CodeMash!")print counterCounter({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})
46. 46. Counterfrom collections import Countercounter = Counter("Hello there, CodeMash!")print counterCounter({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})
47. 47. Namedtupleimport mathdef 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
48. 48. 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
49. 49. Difﬂibs1 = """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 fringillasemper. 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."""
50. 50. Difﬂibs2 = """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 fringillasemper. 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."""
51. 51. Difﬂibimport difflibdiffer = difflib.Differ()diff = differ.compare(s1.splitlines(), s2.splitlines())print n.join(diff)
52. 52. Difﬂib 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.
53. 53. Difﬂibdiff = difflib.unified_diff( s1.splitlines(), s2.splitlines(), lineterm=)print n.join(diff)
54. 54. Difﬂib---+++@@ -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
55. 55. Ast• Use Python to process abstract syntax trees of Python grammar• Helps introspect about what the current grammar looks like• Helps write secure code
56. 56. Ast# Danger:foo = eval(bar)# Safe:foo = ast.literal_eval(bar)
57. 57. 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
58. 58. import osfrom multiprocessing import Processdef info(title): print title print parent process:, os.getppid() print process id:, os.getpid()def f(name): info(function f) print hello, nameif __name__ == __main__: info(main) p = Process(target=f, args=(world,)) p.start() p.join()
59. 59. \$ python process.pymainparent process: 18647process id: 31317----------function fparent process: 31317process id: 31318----------hello world
60. 60. import osfrom multiprocessing import Pooldef g(x): info(function g(+str(x)+)) return x * xif __name__ == __main__: info(main) pool = Pool(2) print pool.map(g, range(100))
61. 61. \$ python process.py parent process: 31369main parent process: 31369module name: __main__ process id: 31370parent process: 18647 process id: 31371process id: 31369 -------------------- ----------function g(0) function g(4)module name: __main__ module name: __main__parent process: 31369 parent process: 31369process id: 31370 function g(14)---------- process id: 31370function g(1) module name: __main__module name: __main__ ----------parent process: 31369 parent process: 31369process id: 31370 function g(5)---------- process id: 31371function g(13) module name: __main__function g(2)module name: __main__ ...parent process: 31369process id: 31370 [0, 1, 4, 9, 16, 25, ..., 8836,---------- 9025, 9216, 9409, 9604, 9801]module name: __main__function g(3)module name: __main__
62. 62. Third Party Packages
63. 63. Third Party Packages• Most available on PyPI--http://pypi.python.org• pip install packagename• easy_install packagename
64. 64. Virtualenv• Makes an isolated Python environment• Don’t pollute your global site-packages• Insulates Python projects from one another• Dont have to be root
65. 65. Virtualenv\$ virtualenv directory\$ virtualenv --python=/path/to/specific/python directory\$ cd directory\$ . bin/activate\$ easy_install whatever\$ pip install whatever...do stuff...\$ deactivate
66. 66. Datetime and Dateutil• Python’s datetime provides date, time, datetime, and timedelta objects• Dateutil provides powerful extensions• Parsing• Olson-driven timezones• Recurrence
67. 67. 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)
68. 68. Timezones>>> 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))
69. 69. 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)]
70. 70. howoldismykid.comdef periods_between(freq, start_date, end_date):    # dateutil.rrule falls down for monthly recurrences where the start # dates 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, rrules 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
71. 71. 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
72. 72. Nose• Find, run, and report results of test suites• Low-friction setup• Extendable with plugins (more later)• http://readthedocs.org/docs/nose/en/latest/
73. 73. Nose# test_foo.pydef test_a_thing(): assert Falsedef test_another_thing(): assert True
74. 74. Nose\$ nosetests tests/test_foo.pyF.======================================================================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.001sFAILED (failures=1)
75. 75. Noseclass 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()
76. 76. Nose\$ nosetests.--------------------------------------------------------Ran 1 test in 0.005sOK
77. 77. Nose: Useful Flags• --processes=number• -x• --pdb• --pdb-failures• --failed
78. 78. 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...
79. 79. 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
80. 80. \$ 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.010sFAILED (errors=30)
81. 81. import osfrom nose.plugins import Pluginclass 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
82. 82. def setOutputStream(self, stream): self.stream = stream return self.streamdef begin(self): self.failure_count = 0def handleFailure(self, test, err): self.failure_count += 1 if self.failure_count < 8: self.stream.write(F) else: self.stream.write(U) return True
83. 83. \$ 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.010sFAILED (errors=30)
84. 84. 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/
85. 85. Mockfrom mock import Mockfoo = Mock()foo.bar()foo.bar.baz(42)foo.bar.baz.return_value = "whatever"assert foo.bar.calledfoo.bar.baz.assert_called_with(42)
86. 86. Mockclass 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
87. 87. Mockfrom mock import patchclass 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
88. 88. Coverage• Measure coverage of your codebase during program execution• Integrates with several test runners to measure test coverage• http://nedbatchelder.com/code/coverage/
89. 89. #!/usr/bin/env pythonclass 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 = thingif __name__ == __main__: x = FrobulatedThing(42)
90. 90. #!/usr/bin/env pythonclass 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 = thingif __name__ == __main__: x = FrobulatedThing(42)
91. 91. \$ coverage run frobulator.py\$ coverage report -mName Stmts Miss Cover Missing------------------------------------------frobulator 12 4 67% 8, 12-14
92. 92. from mock import Mock, patchfrom frobulator import Frobulator, FrobulatedThingclass 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)
93. 93. \$ nosetests --with-coverage --cover-package frobulator..Name Stmts Miss Cover Missing------------------------------------------frobulator 12 1 92% 26------------------------------------------Ran 2 tests in 0.008sOK
94. 94. Useful Flags• --cover-erase• --cover-inclusive• --cover-tests
95. 95. Lettuce• Like Cucumber• BDD test framework• Natural language augmented by code• Big wins pairing Devs + QA + Biz• http://lettuce.it/
96. 96. Lettuce Vocabulary• Features• Scenarios• Steps• World• Terrain
97. 97. 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"
98. 98. from lettuce import step, worldfrom nose.tools import assert_equalsimport requests@step(rI access the url "(.*)")def access_url(step, url): world.response = requests.get(url)@step(rI get a status "(.*)")def get_status(step, expected_status): expected_status = int(expected_status) assert_equals(world.response.status_code, expected_status)
99. 99. Snake-Guice• Framework for Dependency Injection• Reduce coupling!• Improve testability!• http://code.google.com/p/snake-guice/
100. 100. Snake-Guice• Bind identiﬁers 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
101. 101. Deﬁne the Identiﬁerclass IFrobulator(object): pass
102. 102. Decorate Your Codefrom snakeguice import injectclass WankelRotator(object): @inject(frobulator=IFrobulator) def __init__(self, frobulator): self.frobulator = frobulator
103. 103. Decorate Your Codefrom snakeguice import injectclass WankelRotator(object): @inject(frobulator=IFrobulator) def __init__(self, frobulator): self.frobulator = frobulator
104. 104. Conﬁgure Bindingsfrom frobulator import Frobulatorclass BindingModule(object): def configure(self, binder): binder.bind(IFrobulator, to=Frobulator)
105. 105. Get an Instancefrom snakeguice import Injectorimport WankelRotatorinjector = Injector(BindingModule())rotator = injector.get_instance(WankelRotator)
106. 106. Requests• A human-friendly alternative to urllib2• Maturing rapidly• http://python-requests.org/
107. 107. Using Requestsimport requests# Authentication!r = requests.get(https://api.github.com, auth=(user, pass))print r.status_codeprint r.headers[content-type]
108. 108. 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)
110. 110. 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)
111. 111. Pylons• Request/response: webob• URL dispatch: routes• Input validation: formencode• Persistence/ORM: sqlalchemy• Session: beaker• Templates: mako
112. 112. from routes import Mapperdef 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
113. 113. from howoldismykid.lib.base import BaseController, renderclass MainController(BaseController): def index(self): # Return a rendered template return render(/main.mako)
114. 114. from formencode import Schema, validatorsclass ContactForm(Schema): email = validators.Email(not_empty=True) subject = validators.String(not_empty=True) message = validators.String(not_empty=True)
115. 115. from pylons.decorators import validatefrom howoldismykid.lib.base import BaseController, renderfrom howoldismykid.model.forms import ContactFormclass 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)
117. 117. <%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 were 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>
118. 118. Django• https://www.djangoproject.com/• Full stack/harder to replace components• Lots of reusable apps• Admin interface• Lots of deployment options (Google)• Not dead
119. 119. from django.conf.urls.defaults import patterns, urlurlpatterns = patterns(, url(r^/\$, howoldismykid.views.main, name=main), url(r^/contact/\$, howoldismykid.views.contact, name=contact),)
120. 120. from django.shortcuts import renderdef main(request): return render(request, main.html)
121. 121. from django import formsclass ContactForm(forms.Form): email = forms.EmailField(label=Email Address) subject = forms.CharField(label=Subject) message = forms.CharField(label=Message, widget=forms.Textarea)
122. 122. from contact.forms import ContactFormfrom django.shortcuts import renderdef 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 })
123. 123. <!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>
124. 124. {% 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 were 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 %}
125. 125. The Culture
126. 126. Planet Python• http://planet.python.org/• Aggregate feed of Python blogs• Great way to follow whats going on• Minimize newsgroup/mailing list burdens• Easy to be included
127. 127. The Python Ecosystem• http://mirnazim.org/writings/python- ecosystem-introduction/• Great introduction to the Python ecosystem• Everything you need to get up and running
128. 128. The Hitchhiker’s Guide• http://docs.python-guide.org/• Opinionated advice about using Python• Basics• Scenario-speciﬁc details and recommendations
129. 129. 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/