 For the sake of convenience, Web2py defines a type which it calls storage. The storage class is defined in a module called storage inside a package called gluon. I don't know where the name gluon comes from, but it's the name that Web2py gives to its library that contains all the core elements. In any case, what a storage object is is basically a more convenient dictionary, more convenient in the sense that you can use the attribute operator, the dot operator, to access key value pairs, rather than having to use the subscript operator, the square brackets. So here we create a new storage object and assign it to the reference s. The storage starts out empty. It doesn't have any key value pairs, but then we write s.foo equals 3 and this creates a key value pair with a key of the string reading foo and a value of 3. If we then write s.foo equals none, this is actually the same as removing the key value pair. So we're not changing the value of the string key foo to the value none. We're just actually getting rid of the whole key value pair and in the next line we write s.bar and that returns none. So the expression s.bar, assuming there is no key of that name, will return none rather than what a dictionary does, which is throw an exception. Again, the idea of storage objects is simply that they're like a dictionary and just more convenient. As we'll see later, a number of important types in Web2Py are derived from storage, so with those objects you can assign to them and retrieve from them attributes of any name, whether the object has an attribute already of that name or not. Unlike with a dictionary, you don't have to worry too much about accessing a name that isn't in the collection because that will just return none rather than throw an exception. Now, when it comes to the modules which we create in our applications, whether we're talking about the controller modules or the model modules or the view modules, which we haven't discussed yet, in all those cases Web2Py does not simply import them as one normally does when one wants to run a Python module. Instead, what Web2Py will do is it uses an exec statement, which is a kind of statement that existed in Python 2 but actually has been removed from Python 3, and also it uses a built-in function called exec file. And both exec statements and the exec file function, what they do is quite similar with slight variance, but the gist of what they both do is take some Python code, whether it's in the form of a string or the form of some file, and it executes that code, which sounds quite similar to what import does except in this case it doesn't create a module object. And unlike what happens when importing modules, the code will be run no matter what, whereas with modules, if the module has previously already been run, it won't run again. So that right there partly explains why Web2Py uses exec and exec file rather than import statements. The other reason being that Web2Py wants to magically inject into the namespace of the module certain variables. For example, in our controller modules, we want to have available certain objects, but we don't want to have to import anything. We want Web2Py to put those names into our module namespace automatically. And Web2Py can only do this using exec and exec file rather than import. Two of the most important objects which Web2Py automatically makes available in your controller modules is request and response. Request being an instance of gluon.globals.request and response being an instance of gluon.globals.response. Notice that these are classes defined in a module called globals, because these are names which are globally made available throughout your application by the means we just discussed. As these names suggest, the request object encapsulates the currently processing request and the response object encapsulates the response to that request. And both request type and response type are descendants of storage, so we can access and create attributes for these objects simply using the attribute operator, the dot operator. A third important object is session, which is an instance of the class session found also in the module gluon.globals. Like request and response, session is a descendant of storage, and the purpose of the session object is that it can retain values between different requests. Remember that the HTTP protocol itself is perfectly stateless, so various different requests all coming from the same user agent, nothing inherent in HTTP tells the server that hey, these are all coming from the same browser. The workaround for this is to use a cookie. When the user makes a first request to our site, our site sends back a cookie for their browser to store with a unique identifier number, and then subsequently in any request back to our server, the browser will automatically include that cookie value, so we can identify that hey, this is another request from that same person. And so part of the convenience of Web2Py is that it's managing this cookie for us, and with each unique user it is associating a session object, so we can store values in the session object, and the next time we get a request from the same user, then those values are all retained in the session object. So for example, if in the course of processing a request, we set the attribute foo of the session object to the value 2, well in any subsequent request from the same user, the same browser, the session object will have that value for its attribute foo, unless of course we choose to remove it by assigning none to that attribute. So the session object is a convenient way for retaining values between different requests from the same user. Be clear though that session objects aren't really intended for long-term storage. If you have a lot of data you want to keep about a user, you don't store that in the session object, you store it in the database. You create a table with your users and you have the user log in, and that's where you keep their user data. The session object is intended more for transient data. Looking at the request object, one of its most important attributes is called end short for environment. As in the environment which is so called because in CGI at least the environment is the means by which the headers are passed from the web server to the script. So this end attribute, this environment attribute is itself another storage object containing all the so called environment variables passed from the web server to web2py. So for example, request.env.pathinfo will contain the path of the URL and request.env.request method will specify whether this is a get request or a post request. You'll also see a good number of attributes beginning with HTTP underscore and these are all the raw headers in the request. So HTTP underscore host here, that's the host header and it had the value 127.0.0.1 colon 8000. Now the important thing to understand about request.env is that its contents are not entirely up to web2py. It depends upon what the web server chooses to send to the script and not all web servers send all the same information. In a couple cases they might send the same information as another web server but call it something different. So what you get exactly in request.env depends upon the web server. Although for the most part there is uniformity especially when it comes to the most important things you'll find in an event like say HTTP host that should be included no matter what web server you're using. Just be mindful that some things in here may be web server specific. So you can think of request.env as in a sense being the raw unparced information coming from the web server. Most of the other attributes of the request object are just more convenient forms of accessing that same information. So for example request.cookies contains the cookies sent with a request but here they are presented as cookie objects. In the Python library there is a module called cookie with a class called simple cookie which despite the name actually can represent multiple cookies rather than just one. So request.cookies is a simple cookie object. The attributes request.now and request.utc now they both contain Python datetime objects representing the time, the time and date of the request the only difference being that now is localized for your time zone and utc now is expressed in terms of utc time which in English is known as coordinated universal time which is basically Greenwich mean time though not exactly the same to my understanding. The attribute request.user agent actually contains a function which takes the user agent header in the request and parses it and returns a dictionary representing information about the user agent like say what operating system what's the name of the browser engine that sort of stuff and the reason this comes in the form of a function rather than a dictionary is because in most requests you're not going to want to check the user agent and parsing that user agent string can be slightly costly so it kind of makes sense for the sake of efficiency to not bother so when you do need it you have to call the function and that gets you the dictionary. The attribute request.ajax is simply a boolean indicating whether or not this is an Ajax request or not whether something is an Ajax request can't always be determined but usually when the browser makes an Ajax request it will include a request header called x-requested-with and the value xml-http request so assuming that's done we can tell whether a request was made as an Ajax request. The request attributes application, controller function and extension unsurprisingly contain the application, controller function and extension respectively. To get this information Web2Py itself of course has to parse the request URL though actually as we discussed the URL may leave out some or all of this information and it may just simply be inferred like for example if we don't specify any extension then the extension.html is assumed. Also note here the oddity that the proper term action is not being used instead for some reason they called it function I assume that's just a mistake and they simply haven't corrected it so just try and remember that it's request.function rather than request.action as you would expect. Lastly probably the most important request attributes are these getVars as in get variables contains the name value pairs passed as get parameters and postVars as in post variables contains the name value pairs parsed from the post body assuming this was a post request otherwise it'll be blank and both of these are storage objects so say if the URL contains a get parameter named foo with a value of 3 you'll find an attribute foo in the getVars object with the value 3 the vars attribute again short for variables is another storage object but this is an amalgamation of the getVars and the postVars together I'm not sure exactly what happens when there's a name collision the post variables clobber the get variables so if you have a post variable named foo and a get variable also named foo in vars it'll contain just the post variable foo not the get variable I'm not certain about that I wouldn't say it's a huge issue because name collisions between post variables and get variables isn't really very common in any case the vars attribute isn't strictly necessary it's just more convenient to use in some circumstances the args attribute short for arguments is a list of zero or more strings what web2py calls an argument is a slash separated token appended to the end of the URL after the action so given these three examples the first one has a single argument sam the second one has two arguments first sam and alice and the third one has three arguments sam, alice and then bob and again note that after the arguments you can optionally put a slash there or not it doesn't matter to web2py so any so-called args in a URL web2py puts them in a list in order which is assigned to request.args and understand that for any URL we can tack on to the end get parameters which web2py somewhat confusingly calls vars even though these are just the get vars not the post vars but in any case we have three more examples here the first with a single get parameter a single var with the name Edgar and the value Sharon the second example has two vars to get vars first name Edgar with the value Sharon again and then separated by an ampersand of course is the name Aubrey with the value Lisa and finally this last example here includes both args and vars there's a single arg bob and then two vars again Edgar with the value Sharon and Aubrey with the value Lisa and be clear that vars unlike args don't have any concept of order so if you write Aubrey before Edgar it would still be the same URL now if you're like me you'll have trouble remembering which are args and which are vars because frankly it's kind of arbitrary without any context these terms are really almost synonyms so it's kind of arbitrary which is called which the way I finally remembered is that A alphabetically it comes before V so args come before vars the last attribute we'll discuss in request is called body and it contains a read-only file which represents the content of the request body which of course only exists if this is a post request and while normally we don't have much cause to use request.body because Web2py itself will parse the content as name value pairs and place those in the post vars attribute well sometimes the request body doesn't come in the form of name value pairs sometimes the body is just say a bunch of XML or a bunch of binary data and in those cases the post vars attribute is going to be empty so you have to read the body yourself now as for the response object we have a different set of attributes starting with response.headers which is a dictionary simply containing all the headers included in the response and of course many of these are automatically set by Web2py but then if you want to add your own headers or modify one of the headers you can do so via this attribute and then the attribute response.cookies contains a simple cookie object which represents the cookies being set in the response the attribute status contains the HTTP response code being sent back which of course by default will be 200 with the message okay if you want to send a different response code rather than set this attribute directly you should raise an HTTP exception which is an exception included in Web2py we'll talk about later the view attribute contains the view that will be used to render the response once the action returns if you modify this before the action returns then that will actually change which view gets used otherwise this is set by default to the view of the same name as the action as we discussed earlier and last here the render attribute is actually a function which takes two arguments the path to a view file parameter vars expects a dictionary and what the render function does is it manually invokes a view and whatever that view generates is not immediately returned as the response but rather just returned by this function as a string so whereas most commonly views are invoked automatically when an action returns in this case we're just manually invoking a view and getting this generated content and the role of the dictionary path to vars is the same as the dictionary returned by an action the names in the dictionary become variables accessible in the view lastly the most important attribute of the response object is write which contains a method that actually writes data to the response body write normally takes a string argument which is the data that gets written to the response body and it optionally takes a second argument, a named argument escape with a boolean value whether true or false when escape is false the content of the string is written verbatim but when escape is true the text is xml escaped as we say meaning the text is treated as if it's supposed to be the content of an xml or html tag and so characters like the less than sign the greater than sign ampersands those have to be converted into what xml and html call character entities for example the less than sign is escaped as ampersand ltsemicolon these character entities are then of course rendered in the browser as the characters which they represent but this way the browser doesn't confuse them as the angle brackets of a tag so here we have two examples of invoking response.write with the same string argument but in one escape is false and in the other escape is true as you can see when escape is false the text is written into the response body verbatim whereas when escape is true the angle brackets all become character entities