General

What is Albatross

Albatross is a small and flexible toolkit for developing highly stateful web applications. The toolkit provides the following:

A primary design goal of Albatross is that it be small and easy to use and extend. Most of the toolkit is constructed from a collection of mixin classes. You are encouraged to look at the code and to think of new ways to combine the Albatross mixin classes with your own classes.

Object Craft developed Albatross because there was nothing available with the same capabilities which they could use for consulting work. For this reason the toolkit is important to Object Craft and so is actively maintained and developed.

What language is Albatross written in?

Albatross is written in Python, a powerful, but easy to learn, object-oriented scripting language. For more information on Python, go to http://www.python.org/

What license does Albatross use?

Albatross uses a Open Source BSD license that permits most uses. The Albatross 1.00 license is below:

Copyright (C) 2002, Object Craft P/L, Melbourne, Australia.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright notice,
      this list of conditions and the following disclaimer.

    * Redistributions in binary form must reproduce the above copyright notice,
      this list of conditions and the following disclaimer in the documentation
      and/or other materials provided with the distribution.

    * Neither the name of Object Craft nor the names of its contributors may be
      used to endorse or promote products derived from this software without
      specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

I can't find an answer to my question here?

Ask us - send your question to albatross@object-craft.com.au.

Alternatively, you can join the Albatross-Users mailing list. For instructions on subscribing, go HERE.

Is commercial support available?

Definitely! Contact enquiries@object-craft.com.au for more information.

Can you write my application for me?

Object Craft would be happy to talk to you about your requirements. Please contact enquiries@object-craft.com.au.

It is worth noting, however, that because Albatross is Open Source and written with ease of understanding in mind, any skilled python developer will also be able to help you.

What projects has Albatross been used in?

Among other things, Albatross has been used to:

Does Albatross have a mailing list or discussion group?

Yes - to subscribe, go to https://www.object-craft.com.au/cgi-bin/mailman/listinfo/albatross-users


Installation

Does Albatross work under Unix?

Of course!

Does Albatross work under Windows?

Yes.

What version of Python is required to run Albatross?

Albatross works with all versions of Python from version 2.3 onwards. It has not yet been ported to Python 3.

What other packages do I need to have before I can install Albatross?

You will probably want a web server, although it's possible to use the templating functionality outside a web server, and Albatross includes a stand-alone deployment mode that is suitable for limited use without a web server.

To rebuild the documentation, you will need: LATEX latex2html Python source - mkhowto and LATEX styles dia

We recommend you use the pre-formatted manuals available from http://www.object-craft.com.au/projects/albatross/.


Templates

Can I nest macros?

Yes, although you will need to explicitly pass any arguments through. For example:

   1     <al-macro name="sub">
   2         This is the submacro, its argument is:
   3         <al-usearg name="subarg" />
   4     </al-macro>
   5     <al-macro name="main">
   6         This is the main macro.
   7         <al-expand name="sub">
   8             <al-setarg name="subarg">
   9                 <al-usearg name="mainarg" />
  10             </al-setarg>
  11         </al-expand>
  12     </al-macro>
  13     <al-expand name="main">
  14         <al-setarg name="mainarg">
  15             Whatever
  16         </al-setarg>
  17     </al-expand>

What happens to attributes on Albatross tags that Albatross doesn't recognise?

Attributes that Albatross doesn't recognise are passed through to the generated HTML unchanged.

I need to internationalise my templates?

Dave suggested the following (untested) idea:

   1     class MyCtx(SimpleAppContext):
   2         def language_vary(self, name):
   3             language = self.request.get_header('Accept-Language')
   4             if language:
   5                 return os.path.join(string.split(language, ',')[0], name)
   6             return name
   7         def load_template(self, name):
   8             name = self.language_vary(name)
   9             return SimpleAppContext.load_template(name)
  10         def load_template_once(self, name):
  11             name = self.language_vary(name)
  12             return SimpleAppContext.load_template_once(name)


Application model

How do I capture the output from processing my templates?

You may want your template output to go somewhere other than stdout. If so, use ctx.push_content_trap() and ctx.pop_content_trap():

   1     ctx.push_content_trap()
   2     tmpl = ctx.load_template(filename)
   3     tmpl.to_html(ctx)
   4     ctx.flush_content()
   5     results = ctx.pop_content_trap()

Technically, calling push_content_trap() saves the current list off fragments on a stack and then clears the list. When you call pop_content_trap() the contents of the list and joined and returned and the previous fragment list is restored from the stack. This is used internally for arguments to macro expansion and sometimes for options to the select tag.

Can Albatross look after my logging?

Albatross doesn't help you with logging (yet), although traditionally stderr goes to the web server error log.

The easiest way to add logging support yourself would be to make your own subclass of one of the Context objects, and add your logging methods to this.

A simple example:

   1     class LogMixin:
   2         def log(self, s):
   3             sys.stderr.write('%s\n' % s)
   4             sys.stderr.flush()
   5     class AppContext(albatross.SimpleAppContext, LogMixin):
   6             ...
   7     ctx.log('log message')

Can I use the cgitb module with Albatross?

"cgitb" is a module that aids in the reporting of uncaught exceptions from within CGI scripts. It is distributed as a standard part of Python as of version 2.2. Because Albatross handles it's own exceptions, however, cgitb never gets a chance to report them.

Dave suggests overriding the handle_exception() method would work, although this is untested:

   1     import cgitb
   2     from albatross import SimpleApp
   3     class MyApp(SimpleApp):
   4         def handle_exception(self, req):
   5             cgitb.handler()

When are the page methods page_process, page_enter, page_leave, and page_display called?

For a page class called "blah", blah.page_enter() is called once after the previous page class calls ctx.set_page('blah'), and blah.page_display() is then called (and called each time the page is redisplayed - on reload, for example).

When the user submits some data, blah.page_process() is called. page_process() can act on the data, save it, or request the application to move to a new page (ctx.set_page()).

If the application remains on the "blah" page, blah.page_display() is called. If set_page() was called, blah.page_leave() is called, and process starts again with the new page class.

Note that you don't need to provide all four methods - if not provided, they default to sensible actions in each case.

In page_enter(), you would put one time initialisation (setting default values for form fields for example).

   1     def page_enter(ctx):
   2         ctx.locals.diagnostic = ''
   3         ctx.locals.whatever = load_whatever()
   4         ctx.add_session_vars('whatever')

In page_display(), you might initialise more form fields (ones derived from external data, maybe), for example:

   1     def page_display(ctx):
   2         ctx.locals.load_setting_name = ctx.locals.settings.get_current_name()
   3         return ctx.run_template('change_parameters.html')

In page_process(), you act on the user's form submission:

   1     def page_process(ctx):
   2         if ctx.req_equals('submit'):
   3             if ctx.locals.username == '':
   4                 ctx.locals.diagnostic = 'You must enter a username'
   5         else:
   6             ctx.locals.diagnostic = ''

In page_leave(), you might perform cleanup:

   1     def page_leave(ctx):
   2         ctx.remove_session_vars('whatever')


Sessions

What are the differences between the various session mixins?

The simplest are the sessions that are not persistent - no state is recorded between submissions from the user. The most common example of this class of application is the SimpleContext class.

The true session context classes include SimpleAppContext, SessionAppContext, and SessionFileAppContext.

SimpleAppContext uses the HiddenFieldSessionMixin to record session variables in a hidden text field within the form sent to the user's browser. This field is MD5 signed with a server key to make it very difficult for a hostile user to manipulate their session in unauthorised ways.

SessionAppContext (and SimpleSessionApp) use a TCP based session server to store session variables. This has the advantage that your application can be spread over multiple web servers all talking to the one session server. Cookies are used to correlate users to sessions saved with the session server.

SessionFileAppContext (and SimpleSessionFileApp) store session variables within files on the server. As with the session server classes, cookies are used to correlate users with sessions.

My application is spread across multiple web servers. How can I use server side-sessions?

The TCP-based session server is ideal for applications such as this. One session server would be shared between all web servers.

When the session server is restarted, all sessions are lost?

Currently the session server does not record sessions on disk. This will change (particularly if someone submits code!).

An alternative may be for you to use the SessionFile classes - these record the sessions on the server disk (this is only useful if all server processes reside on one server).

How and where do I create a new session?

If the request from the browser contains information about an existing session (cookie or hidden field), then that session is used (provided it can be unpacked), otherwise a new one is created.

A new session (context) is created by calling the create_context() method on the Application object. The prebuilt application objects all provide a create_context() method that provides an appropriate context object, however you may wish to supply your own context object (maybe a sub-class of one of the built-ins) or perform some initialisation at that time. Simply override the create_context() method - for example:

   1     class App(albatross.SimpleSessionFileApp):
   2         def create_context(self):
   3             ctx = albatross.SessionFileAppContext(self)
   4             # Load some macro templates here, if you like
   5             # ctx.run_template_once('macrostuff.html')
   6             return ctx
   7     if __name__ == '__main__':
   8         app = App()
   9         app.run(Request())

How and where do I delete a session?

If your application contains a "logout" button, you could do it in the page_process function like this:

   1     if ctx.req_equals('logout'):
   2         ctx.remove_session()
   3         ctx.set_page('login')

If you have a page from which the application can go no further, you could remove the session on entering the page (the user will never leave the page - but reloading the page will take them back to the begining).

Note that you will need something to clean up abandoned sessions (the client closes their browser window without signing off, etc) - the simplest is a cron job (if you are using unix) or script that is run once a day that deletes any files older than a day from the session directory. This isn't necessary with other types of session application (the session server discards old sessions itself, and the hidden field sessions are saved by the client).


Extending

Can I add new tags to the templating system?

Yes - section 6 of the Albatross Manual, "Developing Custom Tags", describes this in detail.

None: FAQ (last edited 2011-02-15 06:05:17 by localhost)