The PEAK Developers' Center   PeakWebHowTo UserPreferences
 
HelpContents Search Diffs Info Edit Subscribe XML Print View
The following 495 words could not be found in the dictionary of 50 words (including 50 LocalSpellingWords) and are highlighted below:
Adding   Again   And   Anybody   At   Attribute   Children   Command   Component   Content   Contents   Create   Current   Document   Element   Episode   Factory   Finally   First   Following   For   Here   How   If   In   Infinite   Insertion   Item   Larch   Length   List   Location   Ls   Make   Method   Mlet   Mlets   Names   Namespaces   Next   No   None   Normally   Note   Notice   Now   Of   Our   Provide   Provider   Python   Real   Render   Rerunnable   Return   Root   Server   Show   Simple   Sitemaps   Slide   Slides   Spec   Suppose   Tag   Template   Text   The   There   This   To   True   Type   Using   Value   We   When   You   above   accepts   access   accessible   actions   adapted   address   advise   after   alex   alias   all   allow   allowing   also   altogether   an   and   ank   api   application   applications   are   argv   as   assertion   asserts   at   attribute   attributes   available   away   base   be   because   being   below   binding   body   box   build   but   by   can   cgi   change   child   class   close   cmd   code   collection   com   come   command   common   complex   component   components   config   configuration   container   contains   content   contents   controlled   convenient   copy   could   course   create   ctx   current   currently   custom   data   declaring   def   default   develop   development   did   different   directory   disk   display   displaying   do   document   doing   dynamic   each   element   elements   empty   enable   end   ends   environ   escaping   evaluated   everyone   example   examples   exercises   existing   expects   export   expression   file   files   first   fixed   flag   focus   following   footer   for   from   functionality   generator   given   globals   good   h1   handled   handler   handlers   have   header   headers   holding   host   id   if   ii   image   img   import   importable   imports   in   index   infinite   informal   ini   init   inserted   inside   instances   instead   intentional   interfaces   into   introduction   is   it   iterable   itself   jpg   just   knew   known   lambda   larch   launch   launches   learn   len   li   life   like   list   listen   listitem   local   localhost   located   location   long   looks   magic   main   make   making   many   means   metadata   method   mkdir   modifications   module   more   most   much   must   my   name   named   names   namespace   namespaces   naming   near   need   negotiator   new   next   no   not   now   object   of   often   on   one   only   open   operating   optimized   or   original   other   our   out   output   over   own   package   packages   page   parameter   parameters   parent   part   paste   path   peak   pkgfile   place   plain   point   port   previous   process   processed   processing   producer   producing   program   property   protocol   protocols   provides   publishes   publishing   put   pwt   python   quite   quote   real   recognise   ref   refers   register   registered   render   rendered   renderer   rendering   replace   replaced   request   resource   resources   respectively   result   return   reusable   root   rule   rules   run   running   same   script   section   sections   security   see   seems   self   sequence   series   serve   server   set   sets   setting   setup   shown   shows   sia   similar   simple   simplify   single   site   sitemap   sitemaps   skip   slide   slideno   slides   sm   so   socket   some   something   special   specifications   specifier   specifies   specify   specifying   src   start   starting   starts   state   static   status   stderr   stdin   stdout   str   such   sys   take   taken   target   tcp   td   template   templates   text   than   that   the   them   there   they   thing   this   those   time   title   to   too   tool   tools   touch   traversal   traversals   traverse   tree   trees   ts   tuple   two   type   types   under   unless   unused   urllib   use   used   users   uses   using   usually   valid   value   values   verbs   view   viewed   views   want   was   way   we   web   webserver   when   where   which   while   whole   wich   will   with   without   word   works   would   wouldn   wrapper   write   written   xrange   yield   you   your   zero  

Clear message


This is an informal introduction to peak.web written after exercises taken to learn peak.web functionality.

Content producer

Suppose we have an existing application component which we want to make accessible over the web.

This shows a simple component producing an infinite series of text slides. Create the file slides.py and paste this content:

#!python
import sys
from urllib import quote

import protocols
import peak.web.templates
from peak.api import binding, config, naming, security, web
from peak.running.interfaces import IRerunnable
from peak.tools.local_server import WSGIServer

class ISlides(IRerunnable):

    """Infinite sequence of slides"""

    binding.metadata(
        next=security.Anybody,
        current=security.Anybody,
    )

    next = protocols.Attribute("""Next slide.

    This is a generator property, making new contents
    each time it is evaluated.

    """)

    current = protocols.Attribute("""Current slide.""")

class Slides(binding.Component):

    protocols.advise(instancesProvide=[ISlides])

    def slide(self):
        yield "Episode 12B"
        yield "How to recognise different types of trees" \
            " from quite a long way away"
        slideno = 0
        while True:
            slideno += 1
            yield "No %i" % slideno
            yield "The Larch"
            yield "And Now..."
    slide = binding.Make(slide)

    current = binding.Make(str)

    def next(self):
        self.current = self.slide.next()
        return self.current
    next = property(next)

    def run(self, stdin, stdout, stderr=None, environ={}, argv=[]):
        stdout.write("%s\n" % self.next)

def main():
    root = config.makeRoot()
    cmd = Slides(root)
    for ii in xrange(10):
        cmd.run(None, sys.stdout)

if __name__ == "__main__":
    main()

Note that there are many unused imports in this script; that's intentional, because examples in the following sections are modifications of this script, and i want to skip the common part in those examples. (Of course, you wouldn't copy reusable code from script to script in the real life!)

Simple WSGI handler

We start with simple wrapper component, itself doing most of the request processing. peak.web.Location seems to be a good base for such wrapper: request path traversal and security rules will be processed by PEAK, so we can focus on our application.

When HTTP request path ends at our location, PEAK looks for default request handler which name is given in config setting peak.web.defaultMethod and usually is index_html (i.e., for example http://my.host/ works like an alias for http://my.host/index_html). At the same location, we could have other handlers with other names too; they would be accessible by URLs with handler name put instead of index_html.

We must have our handler accessible by web users. We do this by declaring that access to index_html is controlled by security rule Anybody, i.e. this method is accessible for everyone.

We use fixed address and port for our HTTP server. Contents can be viewed at http://localhost:8917/.

#!python

 ...

class SlideShow(web.Location):

    binding.metadata(index_html=security.Anybody)

    slides = binding.Make(Slides)

    def index_html(self, ctx):
        text = self.slides.next
        # Return '(status,headers,output_iterable)' tuple
        return ('200 OK', [('Content-Type', 'text/plain'),
            ('Content-Length', str(len(text)))], [text])

def main():
    root = config.makeRoot()
    server = WSGIServer(root, socketURL="tcp://localhost:8917/")
    server.cgiCommand=SlideShow(server)
    server.run()

if __name__ == "__main__":
    main()

Using page templates

In previous example, our index_html was rendered by custom python code. Following script uses PEAK web templates to render dynamic content:

#!python

 ...

class SlideShow(web.Location):

    binding.metadata(
        index_html = security.Anybody,
        slides = security.Anybody,
    )

    slides = binding.Make(Slides)

    TEMPLATE = """<html this:is="page" with:content-type="text/html">
 <body>
  <h1 content:replace="slides/next" />
 </body>
</html>
"""

    index_html = binding.Make(lambda self: config.processXML(
        web.TEMPLATE_SCHEMA(self.slides),
        "data:," + quote(self.TEMPLATE),
        pwt_document=web.TemplateDocument(self.slides)
    ))

def main():
    root = config.makeRoot()
    server = WSGIServer(root, socketURL="tcp://localhost:8917/")
    server.cgiCommand=SlideShow(server)
    server.run()

if __name__ == "__main__":
    main()

In real applications, templates usually come from disk files; in this example, we put template text into the program body to simplify example setup.

Insertion of dynamic content is controlled by "magic" element attributes in namespaces this, content and with. this means we want to do something with the element holding the attribute, content specifies actions on the contents of the element, and with provides parameters for the element rendering.

Namespaces this and content out-of-the box allow following attribute names:

  • is - asserts that contents are known under the name set by attribute value. E.g. <li this:is="listItem" content:replace="title" /> sets listItem template for list renderer (see below).
  • replace - contents (whole element for this or element contents for contents) is replaced by result of path traversal; attribute value is path.
  • xml - contents are replaced by result of path traversal without escaping XML markup.
  • list - render a sequence of values. This DOMlet accepts parameters listItem, header, emptyList and footer. listitem is rendered for each element of the list, emptyList is rendered when there are no elements in the list, and header and footer are inserted at start and end of the list, respectively.
  • uses - render child elements with target data, or skip element altogether. Attribute value is traversal expression specifying the starting point for path traversals in child elements.
  • unless - contents are rendered only if target data is not available.
  • expects - an assertion as to the type or protocol of the thing currently being handled. Attribute value is protocol import specifier; DOMlet value will be adapted to that protocol at the time of rendering.

Names of attributes in namespace with are names of parameters for operating DOMlet. In the above example, content-type is a parameter of HTML page renderer.

Adding your own DOMlets

You may develop custom content handlers and register them for use in this: and content: attributes of template elements. In the following example, we display the tree image when our slide refers to the larch:

#!python

 ...


class SlideShow(web.Location):

    binding.metadata(
        index_html = security.Anybody,
        slides = security.Anybody,
    )

    slides = binding.Make(Slides)

    TEMPLATE = """<html this:is="page" with:content-type="text/html">
 <body this:uses="slides/next" >
  <h1 content:replace="." />
  <p this:when-larch=".">
   <img src="http://www.ank-sia.com/~alex/larch.jpg" />
  </p>
 </body>
</html>
"""

    index_html = binding.Make(lambda self: config.processXML(
        web.TEMPLATE_SCHEMA(self.slides),
        "data:," + quote(self.TEMPLATE),
        pwt_document=web.TemplateDocument(self.slides)
    ))

class WhenLarch(peak.web.templates.Element):

    """Render only if traversal result contains word 'Larch'"""

    # enable dynamic contents
    staticText = None

    def renderFor(self, data, state):
        if self.dataSpec:
            (td, ts) = self._traverse(data, state)
            if "Larch" in td.current:
                state.write(self._openTag)
                for child in self.optimizedChildren:
                    child.renderFor(data,state)
                state.write(self._closeTag)

def main():
    root = config.makeRoot()
    root.registerProvider("peak.web.verbs.when-larch",
        config.Value(peak.web.templates.negotiatorFactory(WhenLarch)))
    server = WSGIServer(root, socketURL="tcp://localhost:8917/")
    server.cgiCommand=SlideShow(server)
    server.run()

if __name__ == "__main__":
    main()

Normally, DOMlet handlers are registered in section peak.web.verbs of PEAK configuration file like this:

[peak.web.verbs]
when-larch = pwt.negotiatorFactory(my.module.WhenLarch)

Again, we did that in program code to make example setup more simple.

Sitemaps

Real applications are often much more complex than just a single web page, like one shown in above examples. Here come sitemaps, allowing you to build a site out of collection of components, templates and resources.

Our setup in this example is also more complex. First, we make python package directory for web resources (templates and static files):

$ mkdir templates
$ touch templates/__init__.py

In this directory, we create template file slide.pwt:

<html this:is="page" with:content-type="text/html">
 <body>
  <h1 content:replace="next" />
 </body>
</html>

Next, we must allow access to that package from peak.web. We create file named resources.ini (in the current directory) with the following contents:

[peak.web.resource_packages]
templates = True

Finally, we make sitemap file (named sitemap.xml) itself:

<location id="root" config="resources.ini">
    <container object="Slides()" />
    <content type="ISlides">
    <view name="index_html" resource="templates/slide"/>
        <view name="larch" resource="templates/larch.jpg"/>
    </content>
</location>

In this example, we have only one (root) location with two views: index_html, displaying our application component, and larch, publishing a static file from resource package.

Finally we need to have the current directory in an importable path:

$ export PYTHONPATH=.

And now... the script to run all this:

#!python

 ...

def main():
    root = config.makeRoot()
    server = WSGIServer(root, socketURL="tcp://localhost:8917/")
    server.cgiCommand=config.processXML(web.SITEMAP_SCHEMA(server),
        "sitemap.xml", parent=server, sm_globals=globals())
    server.run()

if __name__ == "__main__":
    main()

There is near to zero web code in this script! If our sitemap knew where to take the component it publishes (i.e. if we used valid import specifications in container object and content type), we wouldn't need our launch script at all. To do so we first need to change our sitemap.xml:

<location id="root" config="resources.ini">
    <import module="slides" />
    <container object="slides.Slides()" />
    <content type="slides.ISlides">
    <view name="index_html" resource="templates/slide"/>
        <view name="larch" resource="templates/larch.jpg"/>
    </content>
</location>

Notice the import of the module slides, wich is our original slides.py. Next we need to start the server using the sitemap.xml, we use the command serve to do it. There is a special flag -p <PORT> to specify the port we want our webserver to listen to. Finally we run peak as the following:

$ peak serve -p 8917 ref:sitemap@file:sitemap.xml

If the sitemap was located inside a Python package directory we could specify it using a pkgfile: URL in place of a file: URL. For example, if the sitemap file was in the package directory for a package named some.package, we could launch the web browser displaying our application:

$ peak launch ref:sitemap@pkgfile:some.package/sitemap.xml

(The peak serve command just starts a web server. The peak launch command is similar, but also launches a web browser to display the root of the application -- a convenient development tool.)


PythonPowered
EditText of this page (last modified 2007-03-26 19:17:30)
FindPage by browsing, title search , text search or an index
Or try one of these actions: AttachFile, DeletePage, LikePages, LocalSiteMap, SpellCheck