131
Python for Programmers: A Project-Based Tutorial PyCon 2013 Sandy Strong & Katharine Jarmul

Python for Programmers- A Project-Based Tutorial

Embed Size (px)

DESCRIPTION

project of python for beginners

Citation preview

Page 1: Python for Programmers- A Project-Based Tutorial

Python for Programmers: A Project-Based Tutorial

PyCon 2013

Sandy Strong & Katharine Jarmul

Page 2: Python for Programmers- A Project-Based Tutorial

Please visit this page and follow the instructions:

https://us.pycon.org/2013/community/tutorials/5/

Page 3: Python for Programmers- A Project-Based Tutorial

Introduction to Workshop

Page 4: Python for Programmers- A Project-Based Tutorial

Who are we?

Alexandra (Sandy) Strong (@sandymahalo):Systems Engineer at Dreamhost, Python Education, PyLadies

Katharine Jarmul (@kjam):Director of Technology at HYFN, Pythonista, PyLadies

Page 5: Python for Programmers- A Project-Based Tutorial

Who are you?Py-curious computer programmers, with beginning to intermediate level of experience in your language of choice, and a rudimentary understanding of your operating system's command line.

If you have never programmed before, this tutorial is probably not for you.

We will be moving through material extremely quickly in order to complete an entire web app in 3 hours.

Page 6: Python for Programmers- A Project-Based Tutorial

View the tutorial slides here:

http://bit.ly/YXKWic

I recommend downloading and saving the slides as a PDF now!

Page 7: Python for Programmers- A Project-Based Tutorial

A little Python history

● Created by Guido van Rossum in late 80s○ “Benevolent Dictator for Life”○ Now works at Google

● Name is based off Monty Python

Page 8: Python for Programmers- A Project-Based Tutorial

Why Python?● Simple to get started, easy for beginners, powerful

enough for professionals

● Code is clean, modular, and elegant, making it easy to read and edit

● Whitespace enforcement

● Extensive standard library, many modules to import from

● Cross platform - Windows, Mac, Linux

● Supportive, large, and helpful community

Page 9: Python for Programmers- A Project-Based Tutorial
Page 10: Python for Programmers- A Project-Based Tutorial

Who is using Python?

● Google● Reddit● Twitter● Pinterest/Instragram● DreamHost● YouTube● BitTorrent● DropBox● ...and countless more!

Page 11: Python for Programmers- A Project-Based Tutorial

Why a project?Getting your hands dirty is the best way to develop a new skill!

Your efforts in this tutorial will produce a meaningful project, and give you something to show for your work.

Page 12: Python for Programmers- A Project-Based Tutorial

Questions so far?

Page 13: Python for Programmers- A Project-Based Tutorial

Introduction to Project and a Quick Tour of Python

Page 14: Python for Programmers- A Project-Based Tutorial

Libraries: CherryPy and Jinja2Why did we choose these?

● CherryPy: ○ Lightweight and easy to use microframework○ Has direct interface with WSGI○ It's fun to learn new things and we know you've all

used django or flask ;)

● Jinja2○ Commonly used python templating language○ Easy to grok syntax○ Integrates with many python libraries

Page 15: Python for Programmers- A Project-Based Tutorial

Initial environment setup(you should already have this done)

pip install ipythonpip install jinja2pip install cherrypy

Page 16: Python for Programmers- A Project-Based Tutorial

There are a few important additions!!

We're going to use memcache as our storage backend for this app. This requires that we install memcached, and a Python library called pylibmc:

pip install pylibmc

Linux:apt-get install libmemcached-devapt-get install memcached

Mac:brew install memcached

Page 17: Python for Programmers- A Project-Based Tutorial

Important note to Mac users:

If you receive an error referencing a failure in the llvm-gcc-4.2 library when attempting to install pylibmc, please try the following:

brew link libeventbrew install libmemcachedsudo pip install pylibmc

Page 18: Python for Programmers- A Project-Based Tutorial

Raise your hand if you were not able to install memcached and/or pylibmc successfully!

Page 19: Python for Programmers- A Project-Based Tutorial

IPython: Initial debuggingmy-computer:~ sandy$ ipython

In [1]: import urllib4--------------------------------------------------------ImportError Traceback (most recent call last)<ipython-input-1-87716ee6ccca> in <module>()----> 1 import urllib4

ImportError: No module named urllib4

Page 21: Python for Programmers- A Project-Based Tutorial

So what is IPython, anyway?It's a gussied up version of the standard Python interpreter.

Python is an interpreted language. This means that the interpreter executes the program source code directly, statement by statement.

In comparison, compiled languages (like C) must be explicitly translated into a lower-level machine language executable.

Page 22: Python for Programmers- A Project-Based Tutorial

Brief Intro to Python Data Types

● Integers● Strings● Lists● Tuples● Dictionaries

Page 23: Python for Programmers- A Project-Based Tutorial

Integers and basic mathIn [6]: 2 + 2Out[6]: 4

In [7]: 3 * 3Out[7]: 9

In [8]: 2**3Out[8]: 8

In [9]: 4/2Out[9]: 2

In [11]: 2%4Out[11]: 2

Page 24: Python for Programmers- A Project-Based Tutorial

StringsIn [1]: my_name = "Sandy Strong"In [2]: print my_nameSandy Strong

Now type, "my_name." and hit the tab key:

In [3]: my_name.my_name.capitalize my_name.center my_name.count my_name.decode my_name.encode <snip>

Page 25: Python for Programmers- A Project-Based Tutorial

Useful string methods

● strip○ strips leading/trailing whitespace

● upper○ makes all characters upper case

● lower○ makes all characters lower case

● split○ splits string into a list, whitespace delimited

● find○ search for a string within your string

● startswith○ test for what your string starts with

Page 26: Python for Programmers- A Project-Based Tutorial

String formattingYou can pass variables into strings to format them in a specific way, for example:

In [14]: age = 28In [15]: name = 'Sandy'

In [16]: print "Hello, my name is %s." % nameHello, my name is Sandy.

In [17]: print "Hello, my name is %s, and I'm %s years old." % (name, age)Hello, my name is Sandy, and I'm 28 years old.

Page 27: Python for Programmers- A Project-Based Tutorial

Lists[2]: items = ['bacon', 3.14, ['bread', 'milk'] ]

In [3]: print items['bacon', 3.14, ['bread', 'milk']]

You can put lists (and other Python data types) inside of lists.

Page 28: Python for Programmers- A Project-Based Tutorial

Useful list methods

● insert ○ provide index position and item to be inserted into

the list● append

○ append an item to the end of your list● pop

○ provide index position to "pop" an item out of your list

Page 29: Python for Programmers- A Project-Based Tutorial

TuplesIn [12]: colors = ('red', 'blue', 'green')

In [13]: print colors('red', 'blue', 'green')

Tuples are immutable. Once they're created, they cannot be changed. Because they are immutable, they are hashable.

Page 30: Python for Programmers- A Project-Based Tutorial

What does "hashble" mean?

An object is hashable if it has a hash value which never changes during its lifetime.

If we run a hashing algorithm on a tuple, it will return to us the hash value for that object. If we ran that same algorithm again on the same tuple, it would be identical-- because a tuple is immutable (it's values cannot change after it is defined).

Page 31: Python for Programmers- A Project-Based Tutorial

Useful tuple methods

● index ○ provide an item in your tuple, and it returns the index

position for that item

Page 32: Python for Programmers- A Project-Based Tutorial

DictionariesIn [1]: favorite_sports = {'John': 'Football', 'Sally': 'Soccer'}

In [2]: print favorite_sports{'John': 'Football', 'Sally': 'Soccer'}

The first parameter is the "key" and the second parameter is the "value". A dictionary can have as many key/value pairs as you wish, and keys are immutable.

Page 33: Python for Programmers- A Project-Based Tutorial

Useful dictionary methods

● get ○ retrieves the value of the given key or returns None

if the key doesn't exist in the dictionary● values

○ all values in your dictionary● keys

○ all keys in your dictionary● items

○ list of 2-element tuples that correspond to the key/value pairs in your dictionary

● update ○ add a new key/value pair to your dictionary

Page 34: Python for Programmers- A Project-Based Tutorial

Wait, what is None?

It's a Python built-in constant that is frequently used to represent the absence of a value.

None is Python's version of NULL.

In [1]: None == NoneOut[1]: True

Page 35: Python for Programmers- A Project-Based Tutorial

Incrementing and Decrementing Values

# incrementcounter += 1

# decrementcounter -= 1

Page 36: Python for Programmers- A Project-Based Tutorial

Introduction to Python Loops, Code Blocks, and Boolean Logic

● for● while

BUT before we can jump into loops....

Page 37: Python for Programmers- A Project-Based Tutorial

... we have to talk about whitespace awareness

Python is a "whitespace aware" programming language.

This simply means that, within a code block, Python expects uniform indentation, the standard is 4 spaces.

What defines a code block? Let's try out some loops and find out...

Page 38: Python for Programmers- A Project-Based Tutorial

For loops: "for each item in x, do y"In [1]: items = ['socks', 'shoes', 'shirt', 'pants']

In [2]: for x in items: ...: print x ...: socksshoesshirtpants

The green highlighted code represents a code block. See how the line "print x" is indented in 4 spaces from the line "for x in items:"? IPython does this for you, when coding outside of IPython, you must make your own indentations.

Page 39: Python for Programmers- A Project-Based Tutorial

What happens if I don't indent?In [1]: items = ['socks', 'shoes', 'shirt', 'pants']

In [2]: for x in items: ...: print x ...: IndentationError: expected an indented block

I highly recommend this article explaining whitespace further:

http://www.secnetix.de/olli/Python/block_indentation.hawk

Now that that's out of the way, let's move on!

Page 40: Python for Programmers- A Project-Based Tutorial

Enough boolean logic for this tutorial:In [3]: True == TrueOut[3]: True

In [4]: False == TrueOut[4]: False

In [5]: False == FalseOut[5]: True

In [6]: not False == TrueOut[6]: True

In [7]: not True == FalseOut[7]: True

For more practice with booleans in Python, I highly recommend the Boolean Practice chapter from Zed Shaw's Learn Python the Hard Way.

Page 41: Python for Programmers- A Project-Based Tutorial

While loops: "while x is true, do y"In [4]: counter = 3

In [5]: while counter >= 0: ...: print counter ...: # decrement the counter ...: counter -= 1 ...: 3210

This while-loop will continue to execute until the condition "counter >= 0" no longer evaluates to True.

Page 42: Python for Programmers- A Project-Based Tutorial

Watch out for infinite loops!In programming, an infinite loop is a loop of code which your program enters, but never returns from.

While-loops can be tricky, because they continue running while a condition remains True. The slightest logic error can lead to an infinite while-loop.

For-loops are less prone to infinity because they operate on iterable objects (lists, strings, dictionaries), which are finite by nature.

Page 43: Python for Programmers- A Project-Based Tutorial

Basic Control Flow ToolsThese are the keywords you need to know:

● if● else● elif

In Python we use the above keywords to control flows within our program.

Depending on which condition is met (evaluates to True) within a block of control flow code, we may want our application to behave differently.

Page 44: Python for Programmers- A Project-Based Tutorial

Comparison operators

Equal to: ==Less than: <Greater than: >Less than or equal to: <=Greater than or equal to: >=

Let's try it out...

Page 45: Python for Programmers- A Project-Based Tutorial

if and elseIn [70]: age = 20In [71]: if age <= 20: ....: print "You're old enough!" ....: You're old enough!

In [72]: if age > 20: ....: print "You're too old!" ....: else: ....: print "You're the right age." ....: You're the right age.

Page 46: Python for Programmers- A Project-Based Tutorial

Using elif

The elif keyword allows you to expand your control flow block, and perform additional comparisons within an if-else statement.

In [74]: if age < 20: ....: print "You're too young." ....: elif age >= 20: ....: print "You're the right age!" ....: You're the right age!

Page 47: Python for Programmers- A Project-Based Tutorial

List comprehensions: magical!

In [75]: user_pets = {'Sandy': 'Dog', 'Katharine': 'Snake', 'Bob': 'Mouse', 'Sally': 'Fish'}

In [76]: pet_list = [ v for k,v in user_pets.iteritems()]

In [77]: print pet_listOut[77]: ['Snake', 'Mouse', 'Fish', 'Dog']

Python programmers pride themselves on writing tight, legible, efficient code. List comprehensions are a great example of that.

Page 48: Python for Programmers- A Project-Based Tutorial

Using control flow and conditionals in list comprehensionsIn [77]: user_pets = {'Sandy': 'Dog', 'Katharine': 'Snake', 'Bob': 'Mouse', 'Sally': 'Fish'}

In [78]: pet_list = [ v for k,v in user_pets.iteritems() if k != 'Bob']

In [79]: print pet_list['Snake', 'Fish', 'Dog']

We will only add the pet to pet_list if the user is not Bob.

Page 49: Python for Programmers- A Project-Based Tutorial

Writing Python functions

In [6]: def hello_world(user='John'): ...: """ This is a docstring """ ...: message = "Hello, %s!" % user ...: return message ...:

In [7]: my_message = hello_world(user='Sandy')

In [8]: print my_messageHello, Sandy!

Page 50: Python for Programmers- A Project-Based Tutorial

*args and **kwargsIn addition to single or comma-separated arguments, you can also pass lists or dictionaries of parameters into Python functions

def function(*args): # Expects something like: # ['he', 'she', 'it']

def function(**kwargs): # Expects something like: #{'me': 'you', 'we': they'}

We'll revisit this later when we build our app!

Page 51: Python for Programmers- A Project-Based Tutorial

A word about import statementsPython has an extensive standard library, filled with very useful modules that will prevent you from having to write boilerplate code.

# import the random libraryIn [60]: import random

# Print a random intiger between 1 and 5:In [61]: print random.randint(1,5)4

Put import statements at the top of your file.

Page 52: Python for Programmers- A Project-Based Tutorial

Intro to Python classes# import the imaginary 'cars' moduleimport cars

class MyCar: ''' This class defines a car object ''' # member variables go here make = "Honda" model = "Civic"

# class methods go here def car_color(self, clr="black"): self.color = cars.paint(clr) return True

def tire_type(self, type="standard"): self.tires = cars.tire(type) return True

Page 53: Python for Programmers- A Project-Based Tutorial

Wait-- what exactly is a class in Python?

In object-oriented programming, a class is a construct that is used to create instances of itself, called class objects.

A class defines constituent members which enable its instances to have state and behavior. Data field members (member variables) enable a class instance to maintain state.

Other kinds of members, especially methods, enable the behavior of class instances.

http://en.wikipedia.org/wiki/Class_(computer_programming)

Page 54: Python for Programmers- A Project-Based Tutorial

Ok, so how do I use the MyCar class?>>> import MyCar>>> car = MyCar()>>> print car.makeHonda>>> print car.modelCivic>>> car.car_color()>>> print car.colorblack>>> car.tire_type(type="sport")>>> print car.tiressport

Page 55: Python for Programmers- A Project-Based Tutorial

Be aware of the __init__ method

There's a special method, named __init__ , that is implicitly defined when you create a new class. By default, __init__ is empty and does nothing special.

In some cases, you will want to override this method to have it do something in particular, you can do that by simply defining it at the top of your class:

class MyCar: def __init__(): # do stuff here!

Page 56: Python for Programmers- A Project-Based Tutorial

Methods vs. Functions

In Python, what we call a "method" is actually a member function of a class.

A standalone function that belongs to no class, is simply referred to as a "function".

Page 57: Python for Programmers- A Project-Based Tutorial

Web Coding in Python(it's the best, we promise!)

Page 58: Python for Programmers- A Project-Based Tutorial

Intro to MVC (Model View Controller)This is a design pattern followed by most modern web frameworks.

Models: Models represent knowledge-- they can can represent a single object, or a structure of objects.

Views: A view is a visual representation of its model. A view typically highlights certain attributes of the model, and suppresses others-- it is a presentation filter.

Controllers: A controller is the link between a user and the system. It provides the user with input by arranging for relevant views to present themselves in appropriate places on the screen, and provides means for user output by presenting the user with menus or other means of giving commands and data.

http://www.codinghorror.com/blog/2008/05/understanding-model-view-controller.html

Page 59: Python for Programmers- A Project-Based Tutorial

Real world example

Model: UserView: Profile pageController: "Change password" functionality

● user is represented by a model● specific user model data is presented by a

view on the profile page for that user● functionality allowing a user to change his

password-- interact with the model-- is handled by a controller

Page 60: Python for Programmers- A Project-Based Tutorial

What is WSGI (whiskey)?

The Web Server Gateway Interface defines a simple and universal interface between web servers and web applications or frameworks for the Python programming language.

WSGI is what lets your Python web application talk to web servers like Apache and Nginx.

Page 61: Python for Programmers- A Project-Based Tutorial

ORM - Object Relational Mapper

ORM's are libraries that allow your application code to interact with objects in the datastore.

CherryPy doesn't have a native ORM, so we will be writing our own simple ORM that will allow us to perform CRUD (Create, Read, Update, and Delete) operations.

We will be using memcache, a volatile in-memory datastore, as our backend.

Page 62: Python for Programmers- A Project-Based Tutorial

Are there any questions before we start building our project?

Page 63: Python for Programmers- A Project-Based Tutorial

CherryPy SetupYou should already have all libraries installed and working on your computer.

We will be creating a Poll web application. You will be able to create and edit polls, add and edit choices, and vote on the poll.

The first step is to create a directory on your computer named my-cherrypy-poll:

mkdir my-cherrypy-poll

Page 64: Python for Programmers- A Project-Based Tutorial

Grab the app code from Github, you'll need it later:

If you have git installed:

git clone git://github.com/kjam/cherrypy-poll.git

If you do not have git installed, please review the instructions from our tutorial wiki:

https://us.pycon.org/2013/community/tutorials/5/

Page 65: Python for Programmers- A Project-Based Tutorial

db_tools.py: Our ORMimport pylibmc

def get_mc(): ''' Get a memcache connection object ''' mc = pylibmc.Client(['127.0.0.1:11211']) return mc

def get_value(key): ''' Retrieve value for the given key ''' mc = get_mc() val = mc.get(key) # delete the mc connection object del mc return val

Page 66: Python for Programmers- A Project-Based Tutorial

db_tools.py (continued)

def set_value(key,value): ''' Set a given key/value pair in mc ''' mc = get_mc() mc.set(key,value) del mc return True

def del_value(key): ''' Delete a key/value pair from mc ''' mc = get_mc() mc.delete(key) del mc return True

Page 67: Python for Programmers- A Project-Based Tutorial

What do we have so far?We've got a simple ORM module, composed of functions that allow us to:

● connect to our locally-running memcache instance● retrieve values from memcache based on their key● set key/value pairs in memcache● delete key/value pairs from memcache

We leveraged methods from pylibmc to build our ORM. This library allows Python to easily talk to memcache.

Page 68: Python for Programmers- A Project-Based Tutorial

Note our use of pylibmc functions:

def get_value(key): # get value from mc using supplied key val = mc.get(key)

def set_value(key,value): # set supplied key/value into mc mc.set(key,value)

def del_value(key): # del key/val from mc using supplied key mc.delete(key)

Page 69: Python for Programmers- A Project-Based Tutorial

Questions so far?

Page 70: Python for Programmers- A Project-Based Tutorial

utils.py: Our "helper function"import refrom unicodedata import normalize

_punct_re = re.compile(r'[\t !"#$%&\'()*\-/<=>?@\[\\\]^_`{|},.]+')

def slugify(text, delim=u'-'): """Generates an ASCII-only slug.""" result = [] for word in _punct_re.split(text.lower()): word = normalize('NFKD', word).encode('ascii', 'ignore') if word: result.append(word) return str(delim.join(result))

Notice our usage of string functions (lower, split)!

Page 71: Python for Programmers- A Project-Based Tutorial

What is "slugify"?Suppose a user created a poll named:

"My favorite food is oranges, what is yours? ◕‿◕"

This slugify function converts this poll name to:

my-favorite-food-is-oranges-what-is-yours

Why? Prettier URLs, more SEO-friendly, handy unique identifier for the record in the datastore.

Page 72: Python for Programmers- A Project-Based Tutorial

Notes on the slugify function

The variable _punct_re:

Python does not formally enforce public/private variables or functions.

An underscore preceding an object name indicates that the programmer should respect this object and treat it as private.

Page 73: Python for Programmers- A Project-Based Tutorial

Normalizing unicodeUnicode is great-- it represents a much larger character set than ASCII. However, it can be challenging to work with.

For the purpose of storing slugs in our datastore, it's simpler if we normalize our slug string to only ASCII characters.

This code normalizes the string, replacing unicode characters with their ASCII equivalent, and then encodes the string in ASCII format, ignoring any errors raised during the encoding process:

normalize('NFKD', word).encode('ascii', 'ignore')

For a production web application, you would want to use a more robust method for slugifying your Poll names.

Page 74: Python for Programmers- A Project-Based Tutorial

How might we organize our data?Open up initial_json.py:

poll_dict = { 'my-question-slug': { 'question': 'My question is le awesome, no?', 'slug':'my-question-is-le-awesome-no', 'choices': [ { 'id':1, 'text':'yep', 'value':0, }, { 'id':2, 'text':'nerp', 'value':0, }, 'publish': True,}

Page 75: Python for Programmers- A Project-Based Tutorial

models.py

The models.py file in a Python application is often used to define class types and create and inherit model instances.

In our application, we are essentially using the theory of this to help manipulate data within our memcache datastore.

We will use our models to edit and create "poll" instances which we update in our memcache storage.

Page 76: Python for Programmers- A Project-Based Tutorial

def add_poll(**kwargs): choices_arr = [] count = 1 poll_dict = {} poll_dict['question'] = kwargs.get('question') for k,v in kwargs.items(): if 'choice' not in k: continue choice_dict = { 'id': count, 'text': v, 'value': 0 } choices_arr.append(choice_dict) count += 1 slug = slugify(kwargs.get('question')) poll_dict['slug'] = slug poll_dict['choices'] = choices_arr set_value(slug,poll_dict) if kwargs.get('publish'): publish_poll(slug)

We grab the poll from kwargs, and then if choices are present, we build out a choice_dict of the choices associated with that poll.

The poll question gets slugified, and added to the poll_dict, and then a list containing the choices dictionary is added. to poll_dict.

We call set_value and pass in the slug (unique key for the poll), and the contents of poll_dict.

Finally, if kwargs contains a key named "publish" we publish the poll.

Page 77: Python for Programmers- A Project-Based Tutorial

Now for edit_poll...

This is a big one!

Page 78: Python for Programmers- A Project-Based Tutorial

def edit_poll(**kwargs): choices_arr = [] poll = get_poll(str(kwargs.get('slug'))) poll_dict = {} poll_dict['question'] = kwargs.get('question') for k,v in kwargs.items(): if 'choice' not in k: continue this_choice = [ c for c in poll.get('choices') if int(k.strip('choice')) == c.get('id') ] if not len(this_choice): return False else: this_choice = this_choice[0] choice_dict = { 'id': this_choice.get('id'), 'text': v, 'value': this_choice.get('value'), } choices_arr.append(choice_dict)

Page 79: Python for Programmers- A Project-Based Tutorial

edit_poll (continued) slug = str(kwargs.get('slug')) poll_dict['slug'] = slug poll_dict['choices'] = choices_arr set_value(slug,poll_dict) if kwargs.get('publish'): publish_poll(slug) else: unpublish_poll(slug) return poll_dict

The edit_poll function and add_poll functions have some similarities. They start to differ with the list comprehension used to assign this_choice.

We build out this_choice using a conditional to validate that the id for the choice passed in is equivalent to the choice id on the poll object from memcache.From there, we build out the choice_dict , get slug from **kwargs , add slug and choices to the poll_dict , and set the value in memcache.

Page 80: Python for Programmers- A Project-Based Tutorial

What did we just do?

We created a models file with two functions that allow us to: create and edit poll objects, as well as publish them if they are publishable (according to the editor).

Remember, poll objects will be stored in memcache.

We used our ORM functions from db_tools.py, and our slugify function from utils.py to help with this.

Page 81: Python for Programmers- A Project-Based Tutorial

Any questions about slugs?

We're about to dive into views.

There are a lot of views, and I'll explain each as we go along.

Page 82: Python for Programmers- A Project-Based Tutorial

views.py: Presenting our model datafrom db_tools import get_value, set_value, del_valuefrom utils import slugify

def cast_vote(poll_key,choice): poll = get_value(poll_key) for c in poll['choices']: if c['id'] == int(choice): c['value'] += 1 set_value(poll_key,poll) return poll

This is the view that allows visitors to vote on a poll. When executed, it accepts the poll_key (its unique identifier), and the choice that the user selected.

The poll is retrieved from memcached, validated, and it's vote count is incremented in memcache.

Page 83: Python for Programmers- A Project-Based Tutorial

views.py (continued)

def get_global_links(): return [{ 'name': 'Add a poll', 'url': '/polls/add', }, { 'name': 'Home', 'url': '/' }, { 'name': 'Poll list', 'url': '/polls', }]

This is essentially our site navigation. We return a list of dictionaries, each one containing a pretty name for the view, along with the url path where the view is located within our application.

The view to "Add a poll" is mapped to the "/polls/add" url.

Page 84: Python for Programmers- A Project-Based Tutorial

views.py (continued)

def get_poll(key): poll = get_value(key) return poll

Retrieve a specific poll from memcache, based on the key-- the slug-- for the poll.

Page 85: Python for Programmers- A Project-Based Tutorial

views.py (continued)def get_polls(): poll_list = [] published_polls = get_value('published_polls') if not published_polls: set_value('published_polls',[]) for p in published_polls: poll = get_value(p) poll_list.append(poll) return poll_list

Return the value from memcache with the key 'published_polls', loop through each poll id (slug), and retrieve the poll object for that id. Build a list of poll objects, and return them.

Page 86: Python for Programmers- A Project-Based Tutorial

views.py (continued)def publish_poll(key): published_polls = get_value('published_polls') if not published_polls: set_value('published_polls',[]) if key not in published_polls: published_polls.append(key) set_value('published_polls',published_polls)

For a given poll id (key), add it to the list of published_polls in memcache if it is not already there.

Notice how we're using if-statements to perform validation on published_polls and the individual key we're attempting to add to the published_polls list.

Page 87: Python for Programmers- A Project-Based Tutorial

views.py (continued)def unpublish_polls(key): published_polls = get_value('published_polls') if not published_polls: set_value('published_polls',[]) if key in published_polls: published_polls.remove(key) set_value('published_polls',published_polls)

For a given poll id (key), remove it from list of published_polls in memcache, if it is currently published.

Note again how we perform validation.

Page 88: Python for Programmers- A Project-Based Tutorial

Whew! That was a lot of coding...

So, who has questions?

...I know someone must have a question!

... c'mon...

Page 89: Python for Programmers- A Project-Based Tutorial

app.conf: Important app instructionsThese configuration settings tell our app how and where we want it to run its server at.

[global]server.socket_host = "127.0.0.1"server.socket_port = 8888server.thread_pool = 10

When our app is complete and we fire it up, we'll be running on http://127.0.0.1:8888 with 10 worker threads.

Page 90: Python for Programmers- A Project-Based Tutorial

app.py: Our main application codeThis is another big file. Let's start with imports and environment setup, these go at the top:

import cherrypyfrom jinja2 import Environment,FileSystemLoaderfrom db_tools import get_value,set_value,del_valuefrom views import get_global_links,get_polls,get_poll,\ add_poll,edit_poll,cast_voteimport os.path

conf = os.path.join(os.path.dirname(__file__), 'app.conf')env = Environment(loader=FileSystemLoader('templates'))

env tells our app where to find templates at, and conf tells our app where to find the app.conf file.

Page 91: Python for Programmers- A Project-Based Tutorial

app.py class 1: the PollViews classclass PollViews:

@cherrypy.expose def index(self): data_dict = { 'title': 'Poll List', 'links': get_global_links(), 'polls': get_polls(), } templ = env.get_template('polls.html') return templ.render(data_dict)

First, we define a class named PollViews. Then we write our index function, the default page for our app.

There are some interesting things here, @cherrypy.expose, env.get_template, and templ.render. Everything else is pretty standard.

Page 92: Python for Programmers- A Project-Based Tutorial

What are these [email protected]

The "@" indicates that this is a decorator. Decorators are commonly-used "syntactic sugar" in Python. They allow you to execute the function that @cherrypy.expose is decorating-- in this case, index--, to be passed into the cherrypy.expose function, where additional work is done. This decorator function is a standard part of the CherryPy microframework, and it allows objects passed into index to be traversable by the URL mapping routine.

env.get_template('polls.html'),We get the polls.html template from our environment. Remember, we set up env at the top of our app.py file?

templ.render(data_dict). We pass our data dictionary-- the attributes of our poll model that will be exposed in our view-- into the render method of our template object, templ.

You will observe these same patterns in other methods within the PollViews class.

Page 93: Python for Programmers- A Project-Based Tutorial

app.py class 1 cont: the poll method @cherrypy.expose def poll(self,key,choice=None): method = cherrypy.request.method.upper() poll = False data_dict = { 'title': 'Poll', 'links': get_global_links(), } if method == 'POST': data_dict['poll'] = cast_vote(key,choice) data_dict['success'] = True else: data_dict['poll'] = get_poll(key) templ = env.get_template('poll.html') return templ.render(data_dict)

A little more CherryPy internal magic. Then, if the request is POST, we submit a vote to cast_vote. otherwise, we retrieve the poll to display for the user, get_poll. We then render the data_dict to our poll.html template.

Page 94: Python for Programmers- A Project-Based Tutorial

app.py class 1 cont: the add method @cherrypy.expose def add(self,**kwargs): method = cherrypy.request.method.upper() poll = False data_dict = { 'title': 'Add a poll', 'links': get_global_links(), } if method == 'POST': add_poll(**kwargs) data_dict['success'] = True templ = env.get_template('add_poll.html') return templ.render(data_dict)

This lets us add a poll to our datastore. If method is POST, we pass in **kwargs to add_poll, then we render the contents of our data_dict out to the add_poll.html template.

Page 95: Python for Programmers- A Project-Based Tutorial

app.py: class 1 cont: the edit method @cherrypy.expose def edit(self,key,**kwargs): method = cherrypy.request.method.upper() poll = False data_dict = { 'title': 'Edit your poll', 'links': get_global_links(), } if method == 'POST': poll = edit_poll(**kwargs) data_dict['poll'] = poll data_dict['success'] = True else: data_dict['poll'] = get_poll(key) templ = env.get_template('edit_poll.html') return templ.render(data_dict)

If method is POST, we pass **kwargs into edit_poll, then we add poll to our data_dict.

Otherwise, we call get_poll with the key passed in to edit, and add that to our data_dict.

Finally, we render our data_dict to edit_poll.html.

Page 96: Python for Programmers- A Project-Based Tutorial

app.py class 2: the PollApp classclass PollApp: polls = PollViews()

@cherrypy.expose def index(self): data_dict = { 'title': 'Welcome to the poll application!', 'links': get_global_links(), } templ = env.get_template('index.html') return templ.render(data_dict)

polls is a member variable of the PollApp class, and it is an instance of the PollViews class.

The PollApp class has one method, index. This method has a simple data_dict, which is rendered out to index.html.

Page 97: Python for Programmers- A Project-Based Tutorial

app.py: The last bit! Let's make our app "go"!

This goes at the very bottom of app.py:

if __name__ == '__main__': cherrypy.quickstart(PollApp(), config=conf)else: cherrypy.tree.mount(PollApp(), config=conf)

When we run our CherryPy app, Python sees this as the "__main__" function.

Congratulations! You've completed writing your Python code for this app!

Page 98: Python for Programmers- A Project-Based Tutorial

App Homework!

● Raise an error if a user enters bad data into the form

● Use the unpublish_polls function in the application to properly unpublish polls

● Use CherryPy Sessions to validate that each user can only vote once http://docs.cherrypy.org/dev/refman/lib/sessions.html

● Allow users to add more than 4 choices to a poll using a more dynamically generated form

Page 99: Python for Programmers- A Project-Based Tutorial

Don't forget the templates!Within your my-cherrypy-poll directory, create another directory named templates.

In the interest of time, we won't be typing out all of the HTML, however, we will go over the highlights and important template syntax.

Remember, we're using the jinja2 library to drive templates in our app.

Page 100: Python for Programmers- A Project-Based Tutorial

add_poll.html<html>

<head>

</head>

<body>

<h1>{{title}}</h1>

<h2>Add a poll</h2>

<form action="/polls/add" method='POST'>Question: <input type="text" name="question" value=""><br>

Choice 1: <input type="text" name="choice1" value=""><br>

Choice 2: <input type="text" name="choice2" value=""><br>

Choice 3: <input type="text" name="choice3" value=""><br>

Choice 4: <input type="text" name="choice4" value=""><br>

Publish? <input type="checkbox" name="publish" value="checked"><br>

<input type="submit" value="Add poll">

</form>

{% for l in links %}<span><a href="{{l.url}}">{{l.name}}</a></span> |{% endfor %}

</body>

</html>

Page 101: Python for Programmers- A Project-Based Tutorial

Some things to noteWe use pairs of double braces-- {{ }} -- to surround a variable passed into the template from our view.

When calling a method that posts data to our server, we specify POST as the method inside our form.

We are able to use for-loops, and reference attributes on our objects (each link in links, has both a url and a name attribute):

{% for l in links %}<span><a href="{{l.url}}">{{l.name}}</a></span> |{% endfor %}

Page 102: Python for Programmers- A Project-Based Tutorial

edit_poll.html<html>

<head></head>

<body>

<h1>{{title}}</h1><h2>Edit your poll</h2>

<form action="/polls/edit/{{poll.slug}}" method='POST'>Question: <input type="text" name="question" value=" {{poll.question}}"><br>

{% for c in poll.choices %}Choice {{c.id}}: <input type="text" name="choice {{c.id}}" value="{{c.text}}"><br>{% endfor %}Publish? <input type="checkbox" name="publish" checked=" {% if poll.published %}checked{% endif %}"><br><input type="hidden" name="slug" value=" {{poll.slug}}"><input type="submit" value="Edit poll">

</form>

{% for l in links %}<span><a href="{{l.url}}">{{l.name}}</a></span> |{% endfor %}</body>

</html>

Page 103: Python for Programmers- A Project-Based Tutorial

index.html<html>

<head>

</head>

<body>

<h1>{{title}}</h1>

<p>Welcome to the polls app. Feel free to add a poll or answer some questions! :)</p>

{% for l in links %}<span><a href="{{l.url}}">{{l.name}}</a></span> |{% endfor %}

</body>

</html>

Page 104: Python for Programmers- A Project-Based Tutorial

poll.html<html>

<head></head>

<body>

<h1>{{title}}</h1><h2>Poll: {{poll.question}}</h2>{% if success %}<p>Thanks for voting!</p>

<h3>Results</h3>

{% for c in poll.choices %}<p>{{c.text}}: {{c.value}}</p>{% endfor %}{% else %}<form action="/polls/poll/{{poll.slug}}" method='POST'>{% for c in poll.choices %}<input type="radio" name="choice" value=" {{c.id}}">{{c.text}}<br>{% endfor %}<input type="submit" value="Vote">

</form>

{% endif %}{% for l in links %}<span><a href="{{l.url}}">{{l.name}}</a></span> |{% endfor %} </body></html>

Page 105: Python for Programmers- A Project-Based Tutorial

polls.html<html><head></head><body><h1>{{title}}</h1>

<h2>Polls</h2><ul>{% for p in polls %}<li><a href="/polls/poll/{{p.slug}}">{{p.question}}</a></li>{% endfor %}</ul>

{% for l in links %}<span><a href="{{l.url}}">{{l.name}}</a></span> |{% endfor %}</body></html>

Page 106: Python for Programmers- A Project-Based Tutorial

What we've learned about templates:

jinja2 allows us to do some powerful things:

● Reference variables passed into our template with double-braces: Ex. {{ title }}

● Reference attributes on variables passed in to the template: Ex. poll.choices

● Use for-loops: for ... endfor● Use control flow structures: if ... endif

Pretty cool, right?

Page 107: Python for Programmers- A Project-Based Tutorial

Now, bring in the templates!

Copy the contents of the templates directory from the cherrypy-polls code you downloaded earlier, into the templates directory that you just created in your own version of the app, my-cherrypy-polls.

Page 108: Python for Programmers- A Project-Based Tutorial

Fire up the app and play with it

To start your polls app, execute the following command in your terminal, from within the my-cherrypy-polls directory:

python app.py

NOTE: We are running a development server. This method is not suitable for a production environment.

Page 109: Python for Programmers- A Project-Based Tutorial

Testing(/me facepalms)

Page 110: Python for Programmers- A Project-Based Tutorial

Testing: What it is and why we do it"Code not tested is broken by design" - JKM

We test our code to determine whether or not it is fit for use.

Why, specifically, do we do it?● Saves money● Saves development time● Happier developers● Saves on QA time● Confidence

Page 111: Python for Programmers- A Project-Based Tutorial

Writing Tests

● Unit Tests● Mocking Request/Response● Mocking Objects

Page 112: Python for Programmers- A Project-Based Tutorial

Unit Tests vs. Mock Tests

Units:A method by which individual units of source code are tested. A unit is the smallest testable part of an application, like an individual function.

Mocks:You can mock the creation, updating, and deletion of request and response objects-- and much more. This type of testing is good for making sure your app behaves in a sane fashion for your users. The mock library is a great tool for doing this.

Page 113: Python for Programmers- A Project-Based Tutorial

Unfortunately...

... we don't have time to go into testing in-depth during this tutorial.

We want you guys to come away with:

● Understanding of why writing tests is so important● A basic understanding of how tests might be written● Resources that inspire you to write your own tests!

Page 114: Python for Programmers- A Project-Based Tutorial

Using the Python unittest libraryimport randomimport unittest

class TestSequenceFunctions(unittest.TestCase):

def setUp(self): self.sequence = range(10)

def test_choice(self): element = random.choice(self.sequence) self.assertTrue(element in self.sequence)

if __name__ == '__main__': unittest.main()

Page 115: Python for Programmers- A Project-Based Tutorial

Using the mock library

But first, some sample application code...

Page 116: Python for Programmers- A Project-Based Tutorial

class BlogLogin(object): def __init__(self, login, password): self._blogin = Blog(login, password) def my_subscriptions(self): return self._subs(self._blogin.subs(self._blogin.login) def _subs(self, call): crs = -1 results = [] while crs != 0: page = call(crs) subscriptions = page['subscriptions'] crs = page['next_cursor'] results.extend(subscriptions) return results

Page 117: Python for Programmers- A Project-Based Tutorial

Mock class to test the sample code...import BlogLoginimport mock

class BlogLoginTest(unittest.TestCase): def test_all_subs(self): """Verify that my subscriptions load""" bl = BlogLogin('username', 'password') bl._blogin = Mock() bl._subs.blogin.subs.return_value = \ {subs:[{'a':1}], 'next_cursor':0} result = bl.my_subscriptions() bl._blogin.subs.my_subscriptions.assert_called_with( cursor = -1) self.assertEquals([{'a':1}], result)

Page 118: Python for Programmers- A Project-Based Tutorial

Testing homework:

Your app should be well-tested!

After PyCon, go home and write a test suite using unittest and mock, you can find great documentation here:

○ Python unittest Documentation○ Mock Library Documentation

Page 119: Python for Programmers- A Project-Based Tutorial

Errors(FML)

Page 120: Python for Programmers- A Project-Based Tutorial

Python Errors and Exception class

● Syntax errors○ whitespace violations or other parsing errors

● Exceptions: ○ Python has built-in exceptions that can be caught or

raised○ Alternatively, you can write your own exception

classes specific to your application

Page 121: Python for Programmers- A Project-Based Tutorial

Catching exceptionstry: # Try to get "my-first-poll" from memcache return get_value('my-first-poll')except KeyError: # If the key is not found in memcache, # return an empty dictionary instead return {}

Catching exceptions is useful when you can anticipate what exception might occur, and have the application do something else instead of raising the exception.

In this case, if our key is not found, we want our app to return an empty dictionary.

Page 122: Python for Programmers- A Project-Based Tutorial

Raising exceptions

If you do not attempt to catch an exception, then Python will raise it automatically.

There are many cases where you can catch an exception and mitigate the negative impact for users.

However, there are some exceptions that you can never programmatically predict-- and those bubble up into your application error log.

Page 123: Python for Programmers- A Project-Based Tutorial

Exception homework:

Go through your completed my-cherrypy-poll app and find places that are missing exceptions.

Put in your own try/catch blocks, and then try writing your own custom exception classes:

○ Python Built-In Exceptions○ Creating User-Defined Exception Classes

Page 124: Python for Programmers- A Project-Based Tutorial

While we're talking about errors...I want to introduce you to a valuable debugging tool that's part of Python's standard library, pdb. Let's try it out really quickly in IPython:

In [8]: def my_fnc(val1, val2, val3): ...: import pdb; pdb.set_trace() ...: if val1 > val2: ...: print val1 ...: elif val3 < val2: ...: print val2 ...: else: ...: print val3

In [10]: my_fnc(1, 2, 3)> <ipython-input-8-1f89e51121c6>(3)my_fnc()-> if val1 > val2:(Pdb) n

Page 125: Python for Programmers- A Project-Based Tutorial

Basic pdb commands

A few options to enter at the pdb prompt:

○ l = print out lines above/below your cursor○ n = continue to next line○ s = step into a function

You can find the full pdb documentation here.

Have fun, and happy bug hunting!

Page 126: Python for Programmers- A Project-Based Tutorial

Briefly, on server errors

● Server Errors: What are they?○ http://en.wikipedia.org/wiki/List_of_HTTP_status_codes○ Use them to handle exceptions or proper situation

handling (sorry, I can't find what you're looking for: 404. sorry, you can't see that: 403)

● Throwing Server Errors○ Each framework has their own way of throwing

server errors○ http://docs.cherrypy.org/stable/refman/_cperror.html#classes

Page 127: Python for Programmers- A Project-Based Tutorial

A few more goodies from Python's stdlibDate object creation and manipulation

import datetimeHashing algorithm toolbox

import hashlibEncoding and decoding JSON objects

import jsonMore advanced math

import mathWeb requests made easy (check out the Missing Manual)

import urllib2Accepting user input to a script

import sys

Page 128: Python for Programmers- A Project-Based Tutorial

Conclusions(what did we learn?)

Page 129: Python for Programmers- A Project-Based Tutorial

Whew!

Today we gave you a whirlwind tour of basic Python, and we're hoping it's enough to get you guys hooked!

We covered data types, loops, control flow statements, conditionals, functions, classes, testing, error handling, debugging, and more!

Page 130: Python for Programmers- A Project-Based Tutorial

A few more resources for devs:

Docs:○ Official Python Documentation○ Stack Overflow

IRC:○ Freenode #python channel

Cool stuff:○ Github

Page 131: Python for Programmers- A Project-Based Tutorial

Questions?

Thank you so much for attending our tutorial, we hope you enjoyed it!