Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Definitive Introduction to Falcon

The Definitive Introduction to Falcon

This presentation refreshes you on HTTP and Python/WSGI, then proceeds to walk you through the process of creating a subset of MultiBlog - the multi-author blogging platform - using the Falcon WSGI framework.

You should come out of this knowing: 1) what Falcon is, 2) how to design an API using Falcon, and 3) how to automate testing of APIs implemented with Falcon.

Alejandro Cabrera

November 13, 2013
Tweet

More Decks by Alejandro Cabrera

Other Decks in Programming

Transcript

  1. from pprint import pprint if __name__ == ’__main__’: meta =

    { ’author’ : ’Alejandro Cabrera’, ’email’ : ’[email protected]’, ’objective’ : ’Introduce Falcon!’ } pprint(meta) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  2. About Me Software developer @ Rackspace Likes: Haskell, Python, Linux,

    open-source, automation, quality, cordiality Dislikes: Proprietary, manual processes, hostility Github: cabrera Twitter: @cppcabrera IRC: alcabrera @ freenode Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  3. Falcon Is. . . Falcon is a high-performance Python framework

    for building cloud APIs. It encourages the REST architectural style, and tries to do as little as possible while remaining highly effective. Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  4. Falcon Is. . . A simple web framework WSGI/Python Alejandro

    Cabrera ([email protected]) The Definitive Introduction to Falcon
  5. Falcon Is. . . A simple web framework WSGI/Python Fast

    Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  6. HTTP: In Short HTTP/1.1 200 OK Connection: close Content-Length: 119

    Content-Location: /v1/queues Content-Type: application/json; charset=utf-8 Date: Tue, 12 Nov 2013 19:45:55 GMT Server: gunicorn/18.0 { "queues": [ { "href": "/v1/queues/taco", "name": "taco" } ] } Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  7. HTTP: Status Codes Five classes: 100, 200, 300, 400, 500

    Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  8. HTTP: Status Codes Five classes: 100, 200, 300, 400, 500

    100: Informational Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  9. HTTP: Status Codes Five classes: 100, 200, 300, 400, 500

    100: Informational 200: Good - it worked Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  10. HTTP: Status Codes Five classes: 100, 200, 300, 400, 500

    100: Informational 200: Good - it worked 300: Redirections Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  11. HTTP: Status Codes Five classes: 100, 200, 300, 400, 500

    100: Informational 200: Good - it worked 300: Redirections 400: Client-side oops Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  12. HTTP: Status Codes Five classes: 100, 200, 300, 400, 500

    100: Informational 200: Good - it worked 300: Redirections 400: Client-side oops 500: Server side error Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  13. HTTP: Headers Like a dictionary Many standard-defined, some used by

    convention Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  14. HTTP: Headers Connection: close Content-Length: 119 Content-Location: /v1/queues Content-Type: application/json;

    charset=utf-8 Date: Tue, 12 Nov 2013 19:45:55 GMT Server: gunicorn/18.0 Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  15. HTTP: Body Actual data Can be anything Good idea -

    set Content-Type to be descriptive Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  16. HTTP: Body Actual data Can be anything Good idea -

    set Content-Type to be descriptive json -> application/json Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  17. HTTP: Body Actual data Can be anything Good idea -

    set Content-Type to be descriptive json -> application/json html -> text/html Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  18. HTTP: Body Actual data Can be anything Good idea -

    set Content-Type to be descriptive json -> application/json html -> text/html msgpack -> application/x-msgpack Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  19. HTTP: Body Actual data Can be anything Good idea -

    set Content-Type to be descriptive json -> application/json html -> text/html msgpack -> application/x-msgpack binary -> application/octet-stream Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  20. HTTP: Body { "links": [ { "href": "/v1/queues?marker=taco", "rel": "next"

    } ], "queues": [ { "href": "/v1/queues/taco", "name": "taco" } ] } Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  21. HTTP: Request A client makes a request The request carries

    information that a server uses to reply Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  22. HTTP: Request A client makes a request The request carries

    information that a server uses to reply What kind of request is it? (HTTP verb) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  23. HTTP: Request A client makes a request The request carries

    information that a server uses to reply What kind of request is it? (HTTP verb) How large is my payload? (Content-Length) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  24. HTTP: Request A client makes a request The request carries

    information that a server uses to reply What kind of request is it? (HTTP verb) How large is my payload? (Content-Length) What type of data am I sending? (Content-Type) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  25. HTTP: Request A client makes a request The request carries

    information that a server uses to reply What kind of request is it? (HTTP verb) How large is my payload? (Content-Length) What type of data am I sending? (Content-Type) What type of data can I receive? (Accept) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  26. HTTP: Response A server makes a response The response also

    carries metadata Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  27. HTTP: Response A server makes a response The response also

    carries metadata Was it successful? (status code) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  28. HTTP: Response A server makes a response The response also

    carries metadata Was it successful? (status code) What type of data am I replying with? (Content-Type) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  29. HTTP: Verbs This request/response cycle is further controlled by HTTP

    verbs: Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  30. HTTP: Verbs This request/response cycle is further controlled by HTTP

    verbs: HEAD Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  31. HTTP: Verbs This request/response cycle is further controlled by HTTP

    verbs: HEAD GET Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  32. HTTP: Verbs This request/response cycle is further controlled by HTTP

    verbs: HEAD GET PUT Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  33. HTTP: Verbs This request/response cycle is further controlled by HTTP

    verbs: HEAD GET PUT POST Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  34. HTTP: Verbs This request/response cycle is further controlled by HTTP

    verbs: HEAD GET PUT POST DELETE Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  35. HTTP: Verbs This request/response cycle is further controlled by HTTP

    verbs: HEAD GET PUT POST DELETE PATCH Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  36. HTTP: Routing Finally, the server needs to know what you

    want to access to respond Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  37. HTTP: Routing Finally, the server needs to know what you

    want to access to respond A route looks like: Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  38. HTTP: Routing The Host tells us which server to access

    Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  39. HTTP: Routing The Host tells us which server to access

    The URI tells us what resource to access Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  40. HTTP: Routing The Host tells us which server to access

    The URI tells us what resource to access The URI can also have a query string (more on this later) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  41. An Aside: REST REST is a style of writing web

    applications Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  42. An Aside: REST REST is a style of writing web

    applications In short, it means - follow the HTTP standard Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  43. An Aside: REST REST is a style of writing web

    applications In short, it means - follow the HTTP standard Key points: Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  44. An Aside: REST REST is a style of writing web

    applications In short, it means - follow the HTTP standard Key points: HEAD|GET are read-only, pure, safe operations Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  45. An Aside: REST REST is a style of writing web

    applications In short, it means - follow the HTTP standard Key points: HEAD|GET are read-only, pure, safe operations PUT|DELETE modify a resource, but should be idempotent Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  46. An Aside: REST REST is a style of writing web

    applications In short, it means - follow the HTTP standard Key points: HEAD|GET are read-only, pure, safe operations PUT|DELETE modify a resource, but should be idempotent POST creates an entity subordinate to the given URI Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  47. An Aside: REST REST is a style of writing web

    applications In short, it means - follow the HTTP standard Key points: HEAD|GET are read-only, pure, safe operations PUT|DELETE modify a resource, but should be idempotent POST creates an entity subordinate to the given URI PATCH modifies a portion of an entity Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  48. WSGI: What? A Pythonic approach to HTTP application development Takes

    HTTP headers and metadata and stuffs them in dictionaries Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  49. WSGI: What? A Pythonic approach to HTTP application development Takes

    HTTP headers and metadata and stuffs them in dictionaries Takes body content and stores it as a stream Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  50. WSGI: What? A Pythonic approach to HTTP application development Takes

    HTTP headers and metadata and stuffs them in dictionaries Takes body content and stores it as a stream Think of it as: HTTP made easy Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  51. WSGI: Hello World def hello(env, start_response): print(env[’PATH_INFO’]) status = ’200

    OK’ headers = [(’Content-Type’, ’text/plain’)] body = b’hello’ start_response(status, headers) return [body] Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  52. Falcon: WSGI Made Easy Falcon wraps WSGI and gives us:

    Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  53. Falcon: WSGI Made Easy Falcon wraps WSGI and gives us:

    Easy routing + method not allowed Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  54. Falcon: WSGI Made Easy Falcon wraps WSGI and gives us:

    Easy routing + method not allowed Request/response objects Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  55. Falcon: WSGI Made Easy Falcon wraps WSGI and gives us:

    Easy routing + method not allowed Request/response objects Automatic header setting Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  56. Falcon: WSGI Made Easy Falcon wraps WSGI and gives us:

    Easy routing + method not allowed Request/response objects Automatic header setting Clean, consistent exceptions (400s, 500s) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  57. Falcon: WSGI Made Easy Falcon wraps WSGI and gives us:

    Easy routing + method not allowed Request/response objects Automatic header setting Clean, consistent exceptions (400s, 500s) Classes to express responders Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  58. Falcon: Hello World import falcon class Hello(object): def on_get(self, request,

    response): response.body = b’hello’ response.status = falcon.HTTP_200 app = falcon.API() app.add_route(’/’, Hello()) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  59. Falcon: Request Objects Contain at least the following information: path

    - /v1/queues Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  60. Falcon: Request Objects Contain at least the following information: path

    - /v1/queues query string - detailed=True Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  61. Falcon: Request Objects Contain at least the following information: path

    - /v1/queues query string - detailed=True content length - how long is the body? Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  62. Falcon: Request Objects Contain at least the following information: path

    - /v1/queues query string - detailed=True content length - how long is the body? content type Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  63. Falcon: Request Objects Contain at least the following information: path

    - /v1/queues query string - detailed=True content length - how long is the body? content type uri - localhost:8000/v1/queues?detailed=True Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  64. Falcon: Request Objects Contain at least the following information: path

    - /v1/queues query string - detailed=True content length - how long is the body? content type uri - localhost:8000/v1/queues?detailed=True relative uri - /v1/queues?detailed=True Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  65. Falcon: Request Objects Contain at least the following information: path

    - /v1/queues query string - detailed=True content length - how long is the body? content type uri - localhost:8000/v1/queues?detailed=True relative uri - /v1/queues?detailed=True More! Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  66. Falcon: Request Objects Contain at least the following information: path

    - /v1/queues query string - detailed=True content length - how long is the body? content type uri - localhost:8000/v1/queues?detailed=True relative uri - /v1/queues?detailed=True More! Also has methods for handling query params! Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  67. Falcon: Response Objects Responses have: body - set this to

    return content to the client Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  68. Falcon: Response Objects Responses have: body - set this to

    return content to the client location - sets Location header Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  69. Falcon: Response Objects Responses have: body - set this to

    return content to the client location - sets Location header content type Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  70. Falcon: Response Objects Responses have: body - set this to

    return content to the client location - sets Location header content type etag Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  71. Falcon: Response Objects Responses have: body - set this to

    return content to the client location - sets Location header content type etag set header(s) - custom header handling Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  72. Falcon: Response Objects Responses have: body - set this to

    return content to the client location - sets Location header content type etag set header(s) - custom header handling More! Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  73. Crafting an API API == zip(routes, resources) Route: a URI

    associated with one or more HTTP verbs Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  74. Crafting an API API == zip(routes, resources) Route: a URI

    associated with one or more HTTP verbs Resource: a responder that contains application logic Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  75. Crafting an API API == zip(routes, resources) Route: a URI

    associated with one or more HTTP verbs Resource: a responder that contains application logic Put them together! Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  76. Crafting an API API == zip(routes, resources) Route: a URI

    associated with one or more HTTP verbs Resource: a responder that contains application logic Put them together! One resource per route (usually) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  77. Crafting an API API == zip(routes, resources) Route: a URI

    associated with one or more HTTP verbs Resource: a responder that contains application logic Put them together! One resource per route (usually) Let’s practice with. . . Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  78. Crafting an API: MultiBlog It helps to layout the routes

    and resources before writing even a single line of code Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  79. Crafting an API: MultiBlog It helps to layout the routes

    and resources before writing even a single line of code Let’s do that now Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  80. Crafting an API: MultiBlog GET /v1 GET /v1/health GET /v1/stats

    Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  81. Crafting an API: MultiBlog GET /v1 GET /v1/health GET /v1/stats

    GET /v1/authors POST /v1/authors Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  82. Crafting an API: MultiBlog GET /v1 GET /v1/health GET /v1/stats

    GET /v1/authors POST /v1/authors GET /v1/authors/{author} PATCH /v1/authors/{author} DELETE /v1/authors/{author} Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  83. Crafting an API: MultiBlog GET /v1 GET /v1/health GET /v1/stats

    GET /v1/authors POST /v1/authors GET /v1/authors/{author} PATCH /v1/authors/{author} DELETE /v1/authors/{author} GET /v1/blog/{author} POST /v1/blog/{author} Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  84. Crafting an API: MultiBlog GET /v1/authors POST /v1/authors GET /v1/authors/{author}

    PATCH /v1/authors/{author} DELETE /v1/authors/{author} GET /v1/blog/{author} POST /v1/blog/{author} GET /v1/blog/{author}/{post} PATCH /v1/blog/{author}/{post} DELETE /v1/blog/{author}/{post} Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  85. Crafting an API: MultiBlog # Management routes GET /v1 GET

    /v1/health GET /v1/stats Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  86. Crafting an API: MultiBlog # Listing and creating authors GET

    /v1/authors POST /v1/authors Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  87. Crafting an API: MultiBlog # Working with specific authors GET

    /v1/authors/{author} PATCH /v1/authors/{author} DELETE /v1/authors/{author} Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  88. Crafting an API: MultiBlog # Reading blog details, creating an

    entry GET /v1/blog/{author} POST /v1/blog/{author} Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  89. Crafting an API: MultiBlog # Working with existing posts GET

    /v1/blog/{author}/{post} PATCH /v1/blog/{author}/{post} DELETE /v1/blog/{author}/{post} Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  90. Falcon: Crafting a Resource Resources are classes that serve as

    responders Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  91. Falcon: Crafting a Resource Resources are classes that serve as

    responders Example follows: we’ll create a BlogEntry resource! Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  92. Falcon: Crafting a Resource Resources are classes that serve as

    responders Example follows: we’ll create a BlogEntry resource! Expresses part of the “existing posts” routes Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  93. Falcon: Crafting a Resource class BlogEntry(object): def __init__(self, db_client): self._post_ctrl

    = db_client def on_get(self, request, response, author, post): content = self._post_ctrl.get(author, post) if not content: raise falcon.exceptions.HTTPNotFound() response.content_type = ’application/json’ response.body = content[’body’] Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  94. Falcon: Crafting a Resource class BlogEntry(object): def __init__(self, db_client): self._post_ctrl

    = db_client def on_get(self, request, response, author, post): content = self._post_ctrl.get(author, post) if not content: raise falcon.exceptions.HTTPNotFound() response.content_type = ’application/json’ response.body = content[’body’] def on_delete(self, request, response, author, post): self._post_ctrl.delete(author, post) response.status = falcon.HTTP_204 Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  95. Falcon: Crafting a Resource class BlogEntry(object): # ||| responds to

    GET requests def on_get(self, request, response, author, post): content = self._post_ctrl.get(author, post) if not content: raise falcon.exceptions.HTTPNotFound() response.content_type = ’application/json’ response.body = content[’body’] Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  96. Falcon: Crafting a Resource class BlogEntry(object): def on_get(self, request, response,

    author, post): ... # ||| Returning a 404 if not content: raise falcon.exceptions.HTTPNotFound() ... Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  97. Falcon: Crafting a Resource class BlogEntry(object): def on_get(self, request, response,

    author, post): ... # ||| user-visible response: 200 OK by default response.content_type = ’application/json’ response.body = content[’body’] Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  98. Falcon: Crafting a Resource class BlogEntry(object): # ||| Responds to

    DELETEs def on_delete(self, request, response, author, post): self._post_ctrl.delete(author, post) response.status = falcon.HTTP_204 Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  99. Falcon: Crafting a Resource class BlogEntry(object): # ||| Request object

    def on_get(self, request, response, author, post): ... Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  100. Falcon: Crafting a Resource class BlogEntry(object): # ||| Response object

    def on_get(self, request, response, author, post): ... Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  101. Falcon: Crafting a Resource class BlogEntry(object): # ||| URI template

    args def on_get(self, ..., author, post): ... Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  102. Falcon: Crafting a Resource class BlogEntry(object): def on_patch(...): pass #

    Left as an exercise for the reader! Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  103. Falcon: Exposing the API Here, we come back to routes

    Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  104. Falcon: Exposing the API Here, we come back to routes

    To connect a resource to a route. . . Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  105. Falcon: Exposing the API # blog.py app = falcon.API() app.add_route(’/v1/blog/{author}/{post}’,

    BlogEntry(db_client)) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  106. Falcon: Exposing the API # ||| Falcon’s API object app

    = falcon.API() Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  107. Falcon: Exposing the API # ||| URI templates # |||

    passed as param to responder app.add_route(’/v1/blog/{author}/{post}’, ...) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  108. Falcon: Making it Fly Now we have to run it

    For this demo, we’ll use gunicorn Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  109. Falcon: Make it Fly $ pip install gunicorn ... Alejandro

    Cabrera ([email protected]) The Definitive Introduction to Falcon
  110. Falcon: Make it Fly $ pip install gunicorn ... Successfully

    installed gunicorn Cleaning up... Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  111. Falcon: Make it Fly $ gunicorn blog:app [INFO] Starting gunicorn

    18.0 [INFO] Listening at: http://127.0.0.1:8000 (6194) [INFO] Using worker: sync [INFO] Booting worker with pid: 6199 Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  112. Falcon: Manual Testing Allow me to recommend httpie: It’s built

    on python-requests Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  113. Falcon: Manual Testing Allow me to recommend httpie: It’s built

    on python-requests It’s human-friendly Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  114. Falcon: Manual Testing Allow me to recommend httpie: It’s built

    on python-requests It’s human-friendly It’s easy to install Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  115. Falcon: Manual Testing Allow me to recommend httpie: It’s built

    on python-requests It’s human-friendly It’s easy to install It’s well-behaved and handles HTTP really well Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  116. Falcon: Manual Testing $ pip install httpie ... Successfully installed

    httpie Cleaning up... Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  117. Falcon: Manual Testing $ http get localhost:8000/v1/blog/alej/1 HTTP/1.1 200 OK

    Connection: close Content-Type: application/text Date: Tue, 12 Nov 2013 20:13:58 GMT Server: gunicorn/18.0 Transfer-Encoding: chunked Cool story, bro. Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  118. Falcon: Manual Testing $ http get localhost:8000/v1/alej/1 HTTP/1.1 404 Not

    Found Connection: close Content-Length: 0 Date: Tue, 12 Nov 2013 20:49:17 GMT Server: gunicorn/18.0 Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  119. Falcon: Manual Testing $ http post localhost:8000/v1/blog/alej/1 HTTP/1.1 405 Method

    Not Allowed Allow: GET Connection: close Content-Length: 0 Date: Tue, 12 Nov 2013 20:50:02 GMT Server: gunicorn/18.0 Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  120. Falcon: Manual Testing $ http delete localhost:8000/v1/blog/alej/1 HTTP/1.1 204 No

    Content Connection: close Date: Tue, 12 Nov 2013 20:50:35 GMT Server: gunicorn/18.0 Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  121. Falcon: Automated Testing Manual testing is kinda cool. . .

    Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  122. Falcon: Automated Testing Manual testing is kinda cool. . .

    . . . but not ideal for long-term maintenance Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  123. Falcon: Automated Testing Manual testing is kinda cool. . .

    . . . but not ideal for long-term maintenance Unit testing with Falcon is pretty easy Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  124. Falcon: Automated Testing Manual testing is kinda cool. . .

    . . . but not ideal for long-term maintenance Unit testing with Falcon is pretty easy Functional testing with Falcon + python-requests is doable, as well Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  125. Falcon: Unit Testing (The Base Class) First, let’s start with

    a base class to help us simulate requests Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  126. Falcon: Unit Testing (The Base Class) class TestBase(unittest.TestCase): def setUp(self):

    self.app = blog.app self.srmock = falcon.testing.StartResponseMock() Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  127. Falcon: Unit Testing (The Base Class) class TestBase(unittest.TestCase): def setUp(self):

    self.app = blog.app self.srmock = falcon.testing.StartResponseMock() def simulate_request(self, path, ...): env = falcon.testing.create_environ( path=path, **kwargs ) return self.app(env, self.srmock) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  128. Falcon: Unit Testing (The Base Class) class TestBase(unittest.TestCase): def setUp(self):

    self.app = blog.app self.srmock = falcon.testing.StartResponseMock() def simulate_request(self, path, ...): env = falcon.testing.create_environ( path=path, **kwargs ) return self.app(env, self.srmock) def simulate_get(self, *args, **kwargs): kwargs[’method’] = ’GET’ return self.simulate_request(*args, **kwargs) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  129. Falcon: Unit Testing (The Base Class) # ||| unittest as

    foundation class TestBase(unittest.TestCase): Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  130. Falcon: Unit Testing (The Base Class) class TestBase(unittest.TestCase): def setUp(self):

    # ||| your blog API self.app = blog.app self.srmock = falcon.testing.StartResponseMock() Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  131. Falcon: Unit Testing (The Base Class) class TestBase(unittest.TestCase): # |||

    generic request handler def simulate_request(self, path, ...): env = falcon.testing.create_environ( path=path, **kwargs ) return self.app(env, self.srmock) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  132. Falcon: Unit Testing (The Base Class) class TestBase(unittest.TestCase): def simulate_request(self,

    path, ...): # ||| create a mock WSGI environment env = falcon.testing.create_environ( path=path, **kwargs ) return self.app(env, self.srmock) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  133. Falcon: Unit Testing (The Base Class) class TestBase(unittest.TestCase): # |||

    a method for simulating GETs def simulate_get(self, *args, **kwargs): kwargs[’method’] = ’GET’ return self.simulate_request(*args, **kwargs) ... Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  134. Falcon: Unit Testing (The Base Class) class TestBase(unittest.TestCase): # more

    methods, more methods! (just like GET) def simulate_head(self, *args, **kwargs): ... def simulate_put(self, *args, **kwargs): ... def simulate_delete(self, *args, **kwargs): ... def simulate_patch(self, *args, **kwargs): ... def simulate_post(self, *args, **kwargs): ... Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  135. Falcon: Unit Testing (The Test) With a handy base class

    in hand, let’s actually test this thing! Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  136. Falcon: Unit Testing (The Test) @ddt.ddt class TestBlogEntry(base.TestBase): def setUp(self):

    super(TestBlogEntry, self).setUp() # create a blog post self.entry_path = ’/v1/blog/alej/1’ self.simulate_post(’/v1/blog/alej’) def tearDown(self): self.simulate_delete(’/v1/blog/alej/1’) super(TestBlogEntry, self).tearDown() Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  137. Falcon: Unit Testing (The Test) @ddt.ddt class TestBlogEntry(base.TestBase): ... def

    test_get_returns_200_when_entry_exists(self): self.simulate_get(self.entry_path) self.assertEqual(self.srmock.status, falcon.HTTP_200) def test_get_returns_404_when_entry_not_found(self): self.simulate_get(’/v1/blog/alej/notfound’) self.assertEqual(self.srmock.status, falcon.HTTP_404) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  138. Falcon: Unit Testing (The Test) @ddt.ddt class TestBlogEntry(base.TestBase): ... @ddt.data([self.entry_path,

    ’/v1/blog/alej/notfound’]) def test_delete_always_returns_204(self, path): self.simulate_delete(path) self.assertEqual(self.srmock.status, falcon.HTTP_204) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  139. Falcon: Unit Testing (The Test) @ddt.ddt class TestBlogEntry(base.TestBase): ... @ddt.data(’post’,

    ’put’, ’patch’, ’head’) def test_method_not_allowed_respected(self, method): getattr(self, ’on_’ + method)(self.entry_path) self.assertEqual(self.srmock.status, falcon.HTTP_405) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  140. Falcon: Run the Tests $ nosetests -v tests/test_blog.py ... Ran

    8 tests in 0.008s OK Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  141. Recap With a basic understanding of HTTP. . . Alejandro

    Cabrera ([email protected]) The Definitive Introduction to Falcon
  142. Recap With a basic understanding of HTTP. . . .

    . . adding a smidgen of WSGI. . . Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  143. Recap With a basic understanding of HTTP. . . .

    . . adding a smidgen of WSGI. . . . . . a couple of routes. . . Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  144. Recap With a basic understanding of HTTP. . . .

    . . adding a smidgen of WSGI. . . . . . a couple of routes. . . . . . and a dash of resources . . . Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  145. Recap With a basic understanding of HTTP. . . .

    . . adding a smidgen of WSGI. . . . . . a couple of routes. . . . . . and a dash of resources . . . . . . allowed to bake in a test oven for a few minutes . . . Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  146. Recap With a basic understanding of HTTP. . . .

    . . adding a smidgen of WSGI. . . . . . a couple of routes. . . . . . and a dash of resources . . . . . . allowed to bake in a test oven for a few minutes . . . . . . you, too, can have a Falcon-powered web application! Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  147. Quick Takes How do I read the request contents? Alejandro

    Cabrera ([email protected]) The Definitive Introduction to Falcon
  148. Quick Takes How do I read the request contents? request.stream.read()

    Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  149. Quick Takes How do I set a header for the

    response? Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  150. Quick Takes How do I set a header for the

    response? response.set_header(’X-Awesomeness’, ’You’) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  151. Quick Takes Where can I find all the errors falcon

    can raise? (400s, 500s) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  152. Quick Takes Where can I find all the errors falcon

    can raise? (400s, 500s) from falcon import exceptions dir(exceptions) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  153. Quick Takes How can I generate an etag? import hashlib

    md5 = hashlib.md5 response.etag = md5(request.stream.read()).hexdigest() Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  154. Quick Takes How can I retrieve URI query parameters? limit

    = request.get_param_as_int(’limit’) detailed = request.get_param_as_bool(’detailed’) marker = request.get_param(’marker’) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  155. Falcon: Next Steps Write your own application Add storage Learn

    about middleware Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  156. Falcon: Next Steps Write your own application Add storage Learn

    about middleware Leveling up your tests: tox, mock, httpretty Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  157. Falcon: Next Steps Write your own application Add storage Learn

    about middleware Leveling up your tests: tox, mock, httpretty Contribute! Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon
  158. Falcon: Thanks To These Falconians! Kurt Griffiths (kgriffs) created Falcon

    and is its current maintainer. Many thanks to all the mighty fine contributors to the project, listed below by date of contribution: Alejandro Cabrera (cabrera) Chad Lung (chadlung) Josh Brand (joshbrand) Jamie Painter (painterjd) Flavio Percoco (flaper87) Randall Burt (rs-randallburt) Zhihao Yuan (lichray) Ashutosh Das (pyprism) Emanuele Rocca (ema) Flavio Percoco (flaper87) Soren Hansen (sorenh) Alejandro Cabrera ([email protected]) The Definitive Introduction to Falcon