mvc

New in version 1.1.0.

Model-View-Controller-based sub-framework.

This module and it’s sub-modules constitutes the most common way of using Smisk, mapping URLs to the control tree – an actual class tree, growing from smisk.mvc.control.Controller.

Key members

  • main() is a helper function which facilitates the most common use case: Setting up an application, configuring it, running it and logging uncaught exceptions.
  • The Application class is the type of application. Normally you do not subclass Application, but rather configure it and its different components.
  • The smisk.mvc.console module is an interactive console, aiding in development and management.
  • The smisk.mvc.control module contains many useful functions for inspecting the control tree.

Example:

from smisk.mvc import *
class root(Controller):
  def __call__(self, *args, **params):
   return {'message': 'Hello World!'}

main()

Configuration parameters

smisk.mvc.response.serializer

Name of a default response serializer.

The value should be the name of a serializer extension (the value should be a valid key in the smisk.serialization.serializers.extensions dictionary).

If not set, the Response.fallback_serializer will be used for clients not requesting any particular content type.

Default:None
Type:str
smisk.mvc.etag

Enables adding an ETag header to all buffered responses.

The value needs to be either the name of a valid hash function in the hashlib module (i.e. “md5”), or a something respoding in the same way as the hash functions in hashlib. i.e. need to return a hexadecimal string rep when:

h = etag(data)
h.update(more_data)
etag_value = h.hexdigest()

Enabling this is generally not recommended as it introduces a small to moderate performance hit, because a checksum need to be calculated for each response, and because of the nature of the data Smisk does not know about all stakes in a transaction, thus constructing a valid ETag might somethimes be impossible.

Default:None
Type:str
smisk.mvc.strict_tcn

Controls whether or not this application is strict about transparent content negotiation.

For example, if this is True and a client accepts a character encoding which is not available, a 206 Not Acceptable response is sent. If the value would have been False, the response would be sent using a data encoder default character set.

This affects Accept* request headers which demands can not be met.

As HTTP 1.1 (RFC 2616) allows fallback to defaults (though not recommended) we provide the option of turning off the 206 response. Setting this to false will cause Smisk to encode text responses using a best-guess character encoding.

Default:True
Type:bool

Leaf filters

A leaf filter is basically code that is run prior to and after calling a leaf on the controller tree, allowing manipulation of input, output and the states of things.

Let’s have a look at an example filter which gives the ability to say “this leaf requires a logged in user”:

from smisk.mvc.decorators import leaf_filter

@leaf_filter
def require_login(leaf, *va, **kw):
  if not request.session or not request.session.get('userid'):
    redirect_to(root.login)
  return leaf(*va, **kw)

This filter requires a valid session and the key userid to be set in the session (in this case things are simplified for educational reasons — in reality we would probably check the type of the session object also).

And here is how you would use the filter above:

class root(Controller):
  @require_login
  def protected(self):
    return {'secret launch codes': [u'abc', 123]}

  def login(self):
    pass # (actual auth code here)

# curl localhost:8080/protected
# 302 Found -> http://localhost:8080/login

Note that the from smisk.mvc.decorators import leaf_filter is not needed if you are importing everything from smisk.mvc since leaf_filter is also exported from smisk.mvc.

For more information and examples see smisk.mvc.filters and smisk.mvc.decorators.leaf_filter().

Functions

smisk.mvc.environment()

Name of the current environment.

Returns the value of SMISK_ENVIRONMENT environment value and defaults to “stable“.

smisk.mvc.main(application=None, appdir=None, bind=None, forks=None, handle_errors=True, cli=True, *args, **kwargs) → object

Helper for setting up and running an application.

This function is not a true function, but rather an instance of Main.

See documentation of smisk.util.main.main() for more information.

smisk.mvc.run(bind=None, application=None, forks=None, handle_errors=False) → object

Helper for running an application.

Note that because of the nature of libfcgi an application can not be started, stopped and then started again. That said, you can only start your application once per process. (Details: OS_ShutdownPending sets a process-wide flag causing any call to accept to bail out)

Environment variables

SMISK_BIND
If set and not empty, a call to smisk.core.bind will occur, passing the value to bind, effectively starting a stand-alone process.
Parameters:
  • bind – Bind to address (and port). Note that this overrides SMISK_BIND.
  • application – An application type or instance.
  • forks – Number of child processes to spawn.
  • handle_errors – Handle any errors by wrapping calls in smisk.util.main.handle_errors_wrapper()
Returns:

Anything returned by Application.run()

Return type:

object

See:

setup(), main()

smisk.mvc.setup(application=None, appdir=None, *args, **kwargs) → Application
Deprecated:You should use smisk.util.main.Main.setup() instead.

Classes

class smisk.mvc.Main(smisk.util.main.Main)
See:Documentation of smisk.util.main.Main
class smisk.mvc.Application(smisk.core.Application)

MVC application

See:smisk.core.Application
templates

Templates handler.

If this evaluates to false, templates are disabled.

See:__init__()
Type:Templates
routes

Router.

Type:Router
serializer

Used during runtime. Here because we want to use it in error()

Type:Serializer
destination

Used during runtime. Available in actions, serializers and templates.

Type:routing.Destination
template

Used during runtime.

Type:mako.template.Template
unicode_errors

How to handle unicode conversions.

Possible values: strict, ignore, replace, xmlcharrefreplace, backslashreplace

Type:string
autoclear_model_session

Automatically clear the model session cache before each request is handled.

You should not disable this unless any of the following statements apply:

  • You do not use smisk.model or SQLAlchemy at all (no database).
  • You run only one process (safe to cache).
  • Your application only reads a set of data that never changes.
  • Your model does not involve entity relations.

Disabling this means entities stay in the local memory cache between sessions. (Relations and their content etc are cached, but not the actual data in the entities)

Consider this model:

class Department(Entity):
  people = OneToMany('Person')

class Person(Entity):
  department = ManyToOne('Department')

Now, imagine we have two processes running; process A and process B. Process A gets a request to add a new person to an existing department. Afterwards, process B is requested to list all people in the same department. Now, if autoclear_model_session where set to False, the previously added person would no show up in the listing of people in the department which process B queried. This is because which people is “contained within” what department has been implicitly cached by SQLAlchemy.

Type:bool
Default:True
leaf_filters

Application-global leaf filters, applied to all leafs in the controller tree.

The filter are applied in the order they appear in this list.

from smisk.mvc import *

@leaf_filter
def ensure_page_title(leaf, *va, **kw):
  rsp = leaf(*va, **kw)
  rsp.setdefault('title', u'The Title')
  return rsp

class App(Application):
  def application_will_start(self):
    self.leaf_filters.append(ensure_page_title)
    return Application.application_will_start(self)
Type:list

New in version 1.1.3.

__init__(router=None, templates=None, show_traceback=False, *args, **kwargs)
TODO
run()
TODO
application_did_stop()
TODO
application_will_start()
TODO
apply_leaf_restrictions()
Applies any restrictions set by the current action.
autoload_configuration(config_mod_name='config')

Automatically load configuration from application sub-module named config_mod_name.

Parameters:
  • config_mod_name – Name of the application configuration sub-module
call_action(args, params) → dict

Resolves and calls the appropriate action, passing args and params to it.

Returns:Response structure or None
encode_response(rsp) → str

Encode the response object rsp

Parameters:
  • rsp – Must be a string, a dict or None
Returns:

rsp encoded as a series of bytes

See:

send_response()

error(typ, val, tb)

Handle an error and produce an appropriate response.

Parameters:
  • typ – Exception type
  • val – Exception value
  • tb – Traceback
parse_request()

Parses the request, involving appropriate serializer if needed.

Returns:(list arguments, dict parameters)
Return type:tuple
response_serializer(no_http_exc=False) → Serializer

Return the most appropriate serializer for handling response encoding.

Parameters:
  • no_http_exc – If true, HTTP statuses are never rised when no acceptable serializer is found. Instead a fallback serializer will be returned: First we try to return a serializer for format html, if that fails we return the first registered serializer. If that also fails there is nothing more left to do but return None. Primarily used by error().
Returns:

The most appropriate serializer

Return type:

smisk.serialization.Serializer

send_response(rsp)

Send the response to the current client, finalizing the current HTTP transaction.

Parameters:
  • rsp – Response body
See:

encode_response()

service()

Manages the life of a HTTP transaction.

Summary

  1. Reset current shared request, response and self.
  2. Aquire response serializer from response_serializer().
    1. Try looking at response.format, if set.
    2. Try looking at any explicitly set Content-Type in response.
    3. Try looking at request filename extension, derived from request.url.path.
    4. Try looking at media types in request Accept header.
    5. Use Response.fallback_serializer or raise http.MultipleChoices, depending on value of no_http_exc method argument.
  3. Parse request using parse_request().
    1. Update request parameters with any query string parameters.
    2. Register for the client acceptable character encodings by looking at any Accept-Charset header.
    3. Update request parameters and arguments with any POST body, possibly by using a serializer to decode the request body.
  4. Resolve controller leaf by calling routes.
    1. Apply any route filters.
    2. Resolve leaf on the controller tree.
  5. Apply any format restrictions defined by the current controller leaf.
  6. Append Vary header to response, with the value negotiate, accept, accept-charset.
  7. Call the controller leaf which will return a response object.
    1. Applies any “before” filters.
    2. Calls the controller leaf
    3. Applies any “after” filters.
  8. Flush the model/database session, if started or modified, committing any modifications.
  9. If a templates are used, and the current controller leaf is associated with a template – aquire the template object for later use in encode_response().
  10. Encode the response object using encode_response(), resulting in a string of opaque bytes which constitutes the response body, or payload.
    1. If the response object is None, either render the current template (if any) without any parameters or fall back to encode_response() returning None.
    2. If the response object is a string, encode it if needed and simply return the string, resulting in the input to encode_response() compares equally to the output.
    3. If a template object has been deduced from previous algorithms, serialize the response object using that template object.
    4. Otherwise, if no template is used, serialize the response object using the previously deduced response serializer.
  11. Complete (or commit) the current HTTP transaction by sending the response by calling send_response().
    1. Set Content-Length and other response headers, unless the response has already begun.
    2. Calculate ETag if enabled through the etag attribute.
    3. Write the response body.
setup()

Setup application state.

Can be called multiple times and is automatically called, just after calling autoload_configuration(), by setup() and application_will_start().

Outline

  1. If etag is enabled and is a string, replaces etag with the named hashing algorithm from hashlib.
  2. If templates are enabled but templates.directories evaluates to false, set templates.directories to the default [SMISK_APP_DIR + "templates"].
  3. Make sure Response.fallback_serializer has a valid serializer as it’s value.
  4. Setup any models.
template_for_path(path) → Template

Aquire template URI for path.

Parameters:
  • path – A relative path
Return type:

template.Template

template_for_uri(uri) → Template

Aquire template for uri.

Parameters:
  • uri – Path
Return type:

template.Template

template_uri_for_path(path) → string

Get template URI for path.

Parameters:
  • path – A relative path
Return type:

string

class smisk.mvc.Request(smisk.core.Request)
serializer

Serializer used for decoding request payload.

Available during a HTTP transaction.

Type:smisk.serialization.Serializer
cn_url

New in version 1.1.3.

URL with any filename extension removed, for use with Content Negotiation.

If the request did not contain a filename extension, this is the same object as smisk.core.Request.url.

Type:smisk.core.URL
class smisk.mvc.Response(smisk.core.Response)
format

Any value which is a valid key of the smisk.serializers.extensions dict.

Type:string
serializer

Serializer to use for encoding the response.

The class attribute value serves as the application default serializer, used in cases where we need to encode the response, but the client is not specific about which serializer to use.

If None, strict TCN applies.

See:fallback_serializer
Type:smisk.serialization.Serializer
fallback_serializer

Last-resort serializer, used for error responses and etc.

If None when Application.application_will_start is called, this will be set to a HTML-serializer, and if none is available, simply the first registered serializer will be used.

The class property is the only one used, the instance property has no meaning and no effect, thus if you want to modify this during runtime, you should do this Response.fallback_serializer = my_serializer instead of this app.response.fallback_serializer = my_serializer.

Type:smisk.serialization.Serializer
charset

Character encoding used to encode the response body.

The value of Response.charset (class property value) serves as the application default charset.

Type:string
send_file(path)

Send a file to the client by using the host server sendfile-header technique.

Automatically sets Content-Type header, using mimetypes.guess_type

Calling this method implicitly commits the current HTTP transaction, sending the response immedately.

Parameters:
  • path (string) – If this is a relative path, the host server defines the behaviour.
Raises EnvironmentError:
 

If smisk does not know how to perform sendfile through the current host server.

Raises EnvironmentError:
 

If response has already started.

Raises IOError:
adjust_status(has_content)

New in version 1.1.1.

Make sure appropriate status is set for the response.

remove_header(name)

New in version 1.1.1.

Remove any instance of header named or prefixed name.

remove_headers(*names)

New in version 1.1.1.

Remove any instance of headers named or prefixed *names.

replace_header(header)

New in version 1.1.1.

Replace any instances of the same header type with header.

>>> response.headers
[]
>>> response.headers.append('Content-Type: text/humbug')
>>> response.headers.append('X-Name: Fuffens')
>>> response.headers
['Content-Type: text/humbug', 'X-Name: Fuffens']
>>> response.replace_header('Content-type: text/plain')
>>> response.headers
['X-Name: Fuffens', 'Content-type: text/plain']
class smisk.mvc.Main(smisk.util.main.Main)
default_app_type
Defaults to Application
setup(application=None, appdir=None, *args, **kwargs)
TODO