1 #!/usr/bin/env python 2 3 """ 4 Generic Web framework interfaces. 5 The WebStack architecture consists of the following layers: 6 7 * Framework - The underlying Web framework implementation. 8 * Adapter - Code operating under the particular framework which creates 9 WebStack abstractions and issues them to the application. 10 * Resources - Units of functionality operating within the hosted Web 11 application. 12 13 Resources can act as both content producers within an application and as request 14 dispatchers to other resources; in the latter role, they may be referred to as 15 directors. 16 """ 17 18 class ContentType: 19 20 "A container for content type information." 21 22 def __init__(self, content_type, charset=None): 23 self.content_type = content_type 24 self.charset = charset 25 26 class Transaction: 27 28 """ 29 A generic transaction interface containing framework-specific methods to be 30 overridden. 31 """ 32 33 def commit(self): 34 35 """ 36 A special method, synchronising the transaction with framework-specific 37 objects. 38 """ 39 40 pass 41 42 # Utility methods. 43 44 def parse_content_type(self, content_type_field): 45 46 """ 47 Determine the content type and charset from the supplied 48 'content_type_field' string. 49 """ 50 51 if content_type_field is None: 52 return ContentType(None, "iso-8859-1") 53 54 t = content_type_field.split("; charset=") 55 if len(t) == 1: 56 return ContentType(t[0], "iso-8859-1") 57 else: 58 return ContentType(t[0], t[1]) 59 60 def format_content_type(self, content_type): 61 62 """ 63 Format the given 'content_type' object, producing a string suitable for 64 the response header field. 65 """ 66 67 if content_type.charset: 68 field = "%s; charset=%s" % (content_type.content_type, content_type.charset) 69 else: 70 field = content_type.content_type 71 72 # Make sure that only ASCII is used in the header. 73 74 return field.encode("US-ASCII") 75 76 def parse_content_preferences(self, accept_preference): 77 78 """ 79 Returns the preferences as requested by the user agent. The preferences are 80 returned as a list of codes in the same order as they appeared in the 81 appropriate environment variable. In other words, the explicit weighting 82 criteria are ignored. 83 84 As the 'accept_preference' parameter, values for language and charset 85 preferences are appropriate. 86 """ 87 88 accept_defs = accept_preference.split(",") 89 accept_prefs = [] 90 for accept_def in accept_defs: 91 t = accept_def.split(";") 92 if len(t) >= 1: 93 accept_prefs.append(t[0].strip()) 94 return accept_prefs 95 96 # Request-related methods. 97 98 def get_request_stream(self): 99 100 """ 101 A framework-specific method which returns the request stream for 102 the transaction. 103 """ 104 105 raise NotImplementedError, "get_request_stream" 106 107 def get_request_method(self): 108 109 """ 110 A framework-specific method which gets the request method. 111 """ 112 113 raise NotImplementedError, "get_request_method" 114 115 def get_headers(self): 116 117 """ 118 A framework-specific method which returns the request headers. 119 NOTE: Experimental, since framework support varies somewhat. 120 """ 121 122 raise NotImplementedError, "get_headers" 123 124 def get_content_type(self): 125 126 """ 127 A framework-specific method which gets the content type specified on the 128 request, along with the charset employed. 129 """ 130 131 raise NotImplementedError, "get_content_type" 132 133 def get_content_charsets(self): 134 135 """ 136 Returns the character set preferences. 137 """ 138 139 raise NotImplementedError, "get_content_charsets" 140 141 def get_content_languages(self): 142 143 """ 144 A framework-specific method which extracts language information from 145 the transaction. 146 """ 147 148 raise NotImplementedError, "get_content_languages" 149 150 def get_path(self): 151 152 """ 153 A framework-specific method which gets the entire path from the request. 154 """ 155 156 raise NotImplementedError, "get_path" 157 158 def get_path_info(self): 159 160 """ 161 A framework-specific method which gets the "path info" (the part of the 162 URL after the resource name handling the current request) from the 163 request. 164 """ 165 166 raise NotImplementedError, "get_path_info" 167 168 def get_fields(self): 169 170 """ 171 A framework-specific method which extracts the form fields from the 172 transaction. 173 """ 174 175 raise NotImplementedError, "get_fields" 176 177 def get_agent_information(self): 178 179 """ 180 A framework-specific method which extracts agent information from 181 the transaction. 182 """ 183 184 raise NotImplementedError, "get_agent_information" 185 186 # Response-related methods. 187 188 def get_response_stream(self): 189 190 """ 191 A framework-specific method which returns the response stream for 192 the transaction. 193 """ 194 195 raise NotImplementedError, "get_response_stream" 196 197 def get_response_code(self): 198 199 """ 200 Get the response code associated with the transaction. If no response 201 code is defined, None is returned. 202 """ 203 204 raise NotImplementedError, "get_response_code" 205 206 def set_response_code(self, response_code): 207 208 """ 209 Set the 'response_code' using a numeric constant defined in the HTTP 210 specification. 211 """ 212 213 raise NotImplementedError, "set_response_code" 214 215 def set_content_type(self, content_type): 216 217 """ 218 A framework-specific method which sets the 'content_type' for the 219 response. 220 """ 221 222 raise NotImplementedError, "set_content_type" 223 224 class Resource: 225 226 "A generic resource interface." 227 228 def respond(self, trans): 229 230 """ 231 An application-specific method which performs activities on the basis of 232 the transaction object 'trans'. 233 """ 234 235 raise NotImplementedError, "respond" 236 237 # vim: tabstop=4 expandtab shiftwidth=4