Advertisement
Advertisement

More Related Content

Advertisement

A Few of My Favorite (Python) Things

  1. A Few of My Favorite Things Mike Pirnat • AG Interactive • CodeMash 2012
  2. A Few of My Favorite Things Mike Pirnat • AG Interactive • CodeMash 2012
  3. Disclaimers
  4. The Language
  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').write for 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. http://preshing.com/20110926/high-resolution-mandelbrot-in-obfuscated-python
  7. The Interactive Shell $ python Python 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 darwin Type "help", "copyright", "credits" or "license" for more information. >>>
  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. ...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. Comprehensions • List comprehensions • Set comprehensions • Dictionary comprehensions • Generator expressions
  11. 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()]
  12. 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')]
  13. List Comprehensions b = [x for x in zip(booze, soft_drinks)] [('beer', 'water'), ('wine', 'soda'), ('scotch', 'juice')]
  14. Set Comprehensions s = {v for v in 'CODEMASH ROCKS' if v not in 'ABCD'} set([' ', 'E', 'H', 'K', 'M', 'O', 'S', 'R'])
  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. 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 4 6 8
  17. Generator Expressions gen = (2*x for x in range(5) if x**2 > 3) for number in gen: print number 4 6 8
  18. 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)
  19. 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)
  20. 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)
  21. Properties >>> foo = Foo() >>> foo.bar 42 >>> foo.bar = 'abc' Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 9, in bar ValueError: invalid literal for int() with base 10: 'abc'
  22. Decorators • That funny-looking @ thing • Similar to Java’s annotations • Replaces a function with a wrapper • Augment functionality in a reusable way
  23. Decorators def be_excellent(wrapped): def wrapper(*args, **kwargs): print "Be excellent to each other..." return wrapped(*args, **kwargs) return wrapper @be_excellent def party_on(who): print "...and party on, {0}!".format(who) >>> party_on('dudes') Be excellent to each other... ...and party on, dudes!
  24. 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.
  25. Before Context Managers frobulator = Frobulator(...) try: frobulator.frobulate() finally: frobulator.cleanup()
  26. After Context Managers with Frobulator(...) as frobulator: frobulator.frobulate()
  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. with Frobulator() as frobulator: frobulator.frobulate() Preparing to frobulate... Frobulating! Frobulation complete!
  29. import contextlib @contextlib.contextmanager def 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. The Standard Library
  31. Itertools • Tools for iterating on sequences • Inspired by functional languages • Fast and memory efficient • Combine to express more complicated algorithms
  32. Itertools: chain from itertools import chain for x in chain([1, 2, 3], [42, 1138, 2112]): print x, 1 2 3 42 1138 2112
  33. Itertools: izip from itertools import izip for x in izip([1, 2, 3], [42, 1138, 2112]): print x, (1, 42) (2, 1138) (3, 2112)
  34. Itertools: islice from 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 4 5 6 7 8 9 0 10 20 30 40 50 60 70 80 90
  35. Itertools: islice from 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 4 5 6 7 8 9 0 10 20 30 40 50 60 70 80 90
  36. Itertools: imap from itertools import imap for thingy in imap(Thingy.from_data, database_results): ...
  37. Itertools: cycle from itertools import cycle for x in cycle(['wake up', 'meet Ned', 'romance Rita']): print x wake up meet Ned romance Rita wake up meet Ned romance Rita wake up meet Ned ...
  38. Itertools: repeat from itertools import repeat for x in repeat("stop hitting yourself", 5): print x stop hitting yourself stop hitting yourself stop hitting yourself stop hitting yourself stop hitting yourself
  39. Functools • Tools for manipulating functions • Partial • Wraps a callable with default arguments • Alternative to lambdas and closures
  40. 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
  41. 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)]
  42. Collections • Beyond the basic list, dict, tuple, and set... • Counter • Defaultdict • OrderedDict • Namedtuple • ...and more
  43. Counter 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}
  44. Counter 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}
  45. Counter from collections import Counter counter = 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})
  46. Counter from collections import Counter counter = 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})
  47. Namedtuple import 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
  48. Namedtuple from collections import namedtuple Point = 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. Difflib s1 = """ 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 et magnis dis parturient montes, 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. """
  50. Difflib s2 = """ 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 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 ipsum sit amet congue. """
  51. Difflib import difflib differ = difflib.Differ() diff = differ.compare(s1.splitlines(), s2.splitlines()) print 'n'.join(diff)
  52. 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.
  53. Difflib diff = difflib.unified_diff( s1.splitlines(), s2.splitlines(), lineterm='') print 'n'.join(diff)
  54. 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
  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. Ast # Danger: foo = eval(bar) # Safe: foo = ast.literal_eval(bar)
  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. import os from 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()
  59. $ python process.py main parent process: 18647 process id: 31317 ---------- function f parent process: 31317 process id: 31318 ---------- hello world
  60. import os from 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))
  61. $ python process.py parent process: 31369 main parent process: 31369 module name: __main__ process id: 31370 parent process: 18647 process id: 31371 process id: 31369 ---------- ---------- ---------- function g(0) function g(4) module name: __main__ module name: __main__ parent process: 31369 parent process: 31369 process id: 31370 function g(14) ---------- process id: 31370 function g(1) module name: __main__ module name: __main__ ---------- parent process: 31369 parent process: 31369 process id: 31370 function g(5) ---------- process id: 31371 function g(13) module name: __main__ function g(2) module name: __main__ ... parent process: 31369 process id: 31370 [0, 1, 4, 9, 16, 25, ..., 8836, ---------- 9025, 9216, 9409, 9604, 9801] module name: __main__ function g(3) module name: __main__
  62. Third Party Packages
  63. Third Party Packages • Most available on PyPI--http://pypi.python.org • pip install packagename • easy_install packagename
  64. 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
  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. Datetime and Dateutil • Python’s datetime provides date, time, datetime, and timedelta objects • Dateutil provides powerful extensions • Parsing • Olson-driven timezones • Recurrence
  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. Timezones >>> from dateutil import zoneinfo >>> zone = zoneinfo.gettz('US/Eastern') >>> zone tzfile('America/New_York') >>> zone2 = zoneinfo.gettz('US/Hawaii') >>> dt = datetime.now(zone) >>> dt datetime.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. 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. howoldismykid.com def 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
  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. 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. Nose # test_foo.py def test_a_thing(): assert False def test_another_thing(): assert True
  74. Nose $ 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 False AssertionError ---------------------------------------------------------------------- Ran 2 tests in 0.001s FAILED (failures=1)
  75. 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()
  76. Nose $ nosetests . -------------------------------------------------------- Ran 1 test in 0.005s OK
  77. Nose: Useful Flags • --processes=number • -x • --pdb • --pdb-failures • --failed
  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. 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. $ 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)
  81. import os from 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
  82. 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
  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.010s FAILED (errors=30)
  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. Mock from mock import Mock foo = Mock() foo.bar() foo.bar.baz(42) foo.bar.baz.return_value = "whatever" assert foo.bar.called foo.bar.baz.assert_called_with(42)
  86. 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
  87. Mock from 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
  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. #!/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)
  90. #!/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)
  91. $ coverage run frobulator.py $ coverage report -m Name Stmts Miss Cover Missing ------------------------------------------ frobulator 12 4 67% 8, 12-14
  92. from mock import Mock, patch from 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)
  93. $ nosetests --with-coverage --cover-package frobulator .. Name Stmts Miss Cover Missing ------------------------------------------ frobulator 12 1 92% 26 ------------------------------------------ Ran 2 tests in 0.008s OK
  94. Useful Flags • --cover-erase • --cover-inclusive • --cover-tests
  95. Lettuce • Like Cucumber • BDD test framework • Natural language augmented by code • Big wins pairing Devs + QA + Biz • http://lettuce.it/
  96. Lettuce Vocabulary • Features • Scenarios • Steps • World • Terrain
  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. from lettuce import step, world from nose.tools import assert_equals import 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)
  99. Snake-Guice • Framework for Dependency Injection • Reduce coupling! • Improve testability! • http://code.google.com/p/snake-guice/
  100. 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
  101. Define the Identifier class IFrobulator(object): pass
  102. Decorate Your Code from snakeguice import inject class WankelRotator(object): @inject(frobulator=IFrobulator) def __init__(self, frobulator): self.frobulator = frobulator
  103. Decorate Your Code from snakeguice import inject class WankelRotator(object): @inject(frobulator=IFrobulator) def __init__(self, frobulator): self.frobulator = frobulator
  104. Configure Bindings from frobulator import Frobulator class BindingModule(object): def configure(self, binder): binder.bind(IFrobulator, to=Frobulator)
  105. Get an Instance from snakeguice import Injector import WankelRotator injector = Injector(BindingModule()) rotator = injector.get_instance(WankelRotator)
  106. Requests • A human-friendly alternative to urllib2 • Maturing rapidly • http://python-requests.org/
  107. Using Requests import requests # Authentication! r = requests.get('https://api.github.com', auth=('user', 'pass')) print r.status_code print r.headers['content-type']
  108. Using Requests # Posting name-value pair form data post_data = {'foo': 'bar', 'baz': 'quux', ...} r = requests.post(url, data=post_data) # Posting a glob of JSON r = requests.post(url, data=json.dumps(post_data)) # Multipart file attachment files = {'my_file.xls': open('my_file.xls', 'rb')} r = requests.post(url, files=files)
  109. 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)
  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. Pylons • Request/response: webob • URL dispatch: routes • Input validation: formencode • Persistence/ORM: sqlalchemy • Session: beaker • Templates: mako
  112. 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
  113. from howoldismykid.lib.base import BaseController, render class MainController(BaseController): def index(self): # Return a rendered template return render('/main.mako')
  114. 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)
  115. from pylons.decorators import validate from howoldismykid.lib.base import BaseController, render from 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')
  116. <!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>
  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 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>
  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. 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'), )
  120. from django.shortcuts import render def main(request): return render(request, 'main.html')
  121. 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)
  122. from contact.forms import ContactForm from 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 })
  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. {% 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 %}
  125. The Culture
  126. 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
  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. The Hitchhiker’s Guide • http://docs.python-guide.org/ • Opinionated advice about using Python • Basics • Scenario-specific details and recommendations
  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/
  130. 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...
  131. PyOhio • Free as in $0.00 • Columbus, Ohio • Last weekend in July • http://pyohio.org
  132. PyCon • Great people • Great vibe • All volunteer–personal ownership • Video! http://pycon.blip.tv/ • 2012 & 2013: Santa Clara • 2014 & 2015: Montreal
  133. TiP BoF • Testing in Python Birds of a Feather • Lightning Talks • Heckling...
  134. ...and goats
  135. The Zen • Guiding principles • Sage advice • >>> import this
  136. The Zen • Beautiful is better than ugly. • Readability counts. • Explicit is better than • Special cases aren't special implicit. enough to break the rules. • Simple is better than • Although practicality beats complex. purity. • Complex is better than • Errors should never pass complicated. silently. • Flat is better than nested. • Unless explicitly silenced. • Sparse is better than dense.
  137. The Zen • In the face of ambiguity, • Although never is often refuse the temptation to better than right now. guess. • If the implementation is • There should be one– hard to explain, it's a bad and preferably only one– idea. obvious way to do it. • If the implementation is • Although that way may not easy to explain, it may be a be obvious at first unless good idea. you're Dutch. • Namespaces are one • Now is better than never. honking great idea -- let's do more of those!
  138. What About You?
  139. Fin • Twitter: @mpirnat • Blog: http://mike.pirnat.com • Win a special prize–name all the movies • Thanks for coming!
Advertisement