{"id":16,"date":"2007-09-03T16:50:25","date_gmt":"2007-09-03T23:50:25","guid":{"rendered":"http:\/\/codefork.com\/blog\/index.php\/2007\/09\/03\/decorators-cherrypy-tools-and-other-python-adventures\/"},"modified":"2007-10-03T10:37:44","modified_gmt":"2007-10-03T17:37:44","slug":"decorators-cherrypy-tools-and-other-python-adventures","status":"publish","type":"post","link":"https:\/\/codefork.com\/blog\/index.php\/2007\/09\/03\/decorators-cherrypy-tools-and-other-python-adventures\/","title":{"rendered":"Decorators, CherryPy Tools, and Other Python Adventures"},"content":{"rendered":"<p>In my free time I&#8217;ve been working on my own interesting side project using CherryPy. This is my first major foray into Python: I&#8217;ve admired it for a long time, but haven&#8217;t used it much except for the occasional small script. So it&#8217;s pretty awesome to be really digging in. And I&#8217;m finding the more I learn about Python, the more I love it.<\/p>\n<p>CherryPy, like Python, is extremely easy to start developing with, but it also has a ton of mind-blowing stuff available when you&#8217;re ready to do more. One of these more advanced features is what they call &#8220;Tools,&#8221; which (among other things!) let you write callbacks into various points of the HTTP request-response cycle. The <a href=\"http:\/\/cherrypy.org\/wiki\/CustomTools\">documentation<\/a> explains tools in detail, but a good practical example is <a href=\"http:\/\/tools.cherrypy.org\/wiki\/DirectToDiskFileUpload\">here<\/a>. I&#8217;ll condense it to relevant bits:<\/p>\n<pre>\r\ndef noBodyProcess():\r\n    \"\"\"Sets cherrypy.request.process_request_body = False, giving\r\n    us direct control of the file upload destination. By default\r\n    cherrypy loads it to memory, we are directing it to disk.\"\"\"\r\n    cherrypy.request.process_request_body = False\r\n\r\ncherrypy.tools.noBodyProcess = cherrypy.Tool('before_request_body', noBodyProcess)\r\n\r\nclass fileUpload:\r\n    \"\"\"fileUpload cherrypy application\"\"\"\r\n\r\n    \"\"\" [bunch of code cut out] \"\"\"    \r\n\r\n    @cherrypy.expose\r\n    @cherrypy.tools.noBodyProcess()\r\n    def upload(self, theFile=None):\r\n        \"\"\"upload action\r\n        \"\"\" [more code ... ] \"\"\"\r\n<\/pre>\n<p>The example shows how to set cherrypy.request.process_request_body to False, at the &#8220;before_request_body&#8221; hook; this overrides the default behavior, allowing you to deal directly with the request body contents.<\/p>\n<p>The nice thing is you don&#8217;t need to understand a whole lot about the Tools architecture to make them work, although some things puzzled me initially (more below). Since I really wanted to know why and how the above did what it did, I spent some time poking around. Some things I discovered:<\/p>\n<p>1) Decorators (the lines with the @ symbol) are executed when the class definition is executed. It&#8217;s a bit of shortcut syntax for modifying method definitions. I was confused about this for a while, thinking that decorators are just simple wrappers, called each time the function is. Nope!<\/p>\n<p>2) The Tool decorator above modifies an attribute called &#8220;_cp_config&#8221; of the index() callable. (Not only do objects have attributes, but functions do too in Python&#8211;in fact, functions are actually objects! Wacky.) This is how CherryPy stores info about the Tools that should apply to specific handlers.<\/p>\n<p>3) When Request.run() executes, it looks at the relevant Tools, and calls into them as appropriate. In this example, the specific Tool created says noBodyProcess() should be executed at the &#8220;process_request_body&#8221; point in the request cycle. So it does.<\/p>\n<p>4) cherrypy.request is a strange thing. I was wondering why it&#8217;s accessed everywhere directly, as opposed to being passed as request instances into the handler (as it is, say, in Java Servlets). Doesn&#8217;t that mean every thread is handling the same request?! Nope. Turns out cherrypy.request is able to store per-thread data, even though the name is accessed globally. (See the <a href=\"http:\/\/docs.python.org\/lib\/module-threading.html\">threading.local<\/a> class.)<\/p>\n<p>The convenience in CherryPy comes at the cost of some transparency and intuitiveness: not a high cost, mind you, but a cost nevertheless. Don&#8217;t get me wrong, I think CherryPy is pretty excellent. Still&#8230; it really tripped me up that Request.run() examines the handler&#8217;s attributes for Tool callbacks, instead of storing that information separately (there may well be good reasons for doing it the way it&#8217;s done). The fact that cherrypy.request is thread-local also prompted a &#8220;Huh?!?!&#8221; at first.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my free time I&#8217;ve been working on my own interesting side project using CherryPy. This is my first major foray into Python: I&#8217;ve admired it for a long time, but haven&#8217;t used it much except for the occasional small script. So it&#8217;s pretty awesome to be really digging in. And I&#8217;m finding the more &hellip; <a href=\"https:\/\/codefork.com\/blog\/index.php\/2007\/09\/03\/decorators-cherrypy-tools-and-other-python-adventures\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Decorators, CherryPy Tools, and Other Python Adventures&#8221;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-16","post","type-post","status-publish","format-standard","hentry","category-python"],"_links":{"self":[{"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/16","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/comments?post=16"}],"version-history":[{"count":0,"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/posts\/16\/revisions"}],"wp:attachment":[{"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/media?parent=16"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/categories?post=16"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codefork.com\/blog\/index.php\/wp-json\/wp\/v2\/tags?post=16"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}