The PEAK Developers' Center   IntroToPeak/LessonOne UserPreferences
 
HelpContents Search Diffs Info Edit Subscribe XML Print View Up
The following 731 words could not be found in the dictionary of 50 words (including 50 LocalSpellingWords) and are highlighted below:
Abstract   Although   An   And   Anyway   App   Application   Arguments   As   At   Attribute   Before   Binding   Bootstrap   But   By   Can   Class   Command   Commands   Config   Configurable   Configuration   Contents   Error   Even   Executable   File   Files   First   For   Hello   Hellow   Here   Hmm   How   If   In   Indeed   Ini   Instead   Intro   Introduction   Invocation   It   Lesson   Let   Looks   Making   My   Name   Next   Nice   None   Note   Notice   Now   Object   Obtain   Of   Oh   Okay   One   Oops   Or   Other   Package   Parser   Peak   Points   Properties   Property   Python   Python22   Pythonistas   Remember   Running   Scripts   See   Simple   Simply   So   Specifically   String   Table   Take   The   This   Thus   To   Traceback   Try   Two   Under   Unfortunately   Unix   Unixes   Up   Usage   We   Well   What   When   With   World   You   about   above   abstract   access   accessed   across   acts   actual   actually   adding   addition   advantage   again   all   allows   alluded   almost   alongside   already   also   although   among   an   and   any   api   app   appear   application   applications   appropriate   are   aren   argument   arguments   argv   around   arrange   as   asks   assignment   associated   assume   at   attribute   attributes   automatically   available   away   barely   base   based   be   because   become   becomes   before   begin   big   bin   bind   binding   bindings   bootstraps   both   brackets   building   built   business   but   by   call   callable   called   calls   can   cannot   care   case   central   certain   certainly   change   changed   chapter   chapters   cheat   checking   chmod   choose   class   clear   cmd   code   comes   command   commands   communicate   complete   complex   component   composed   computed   computing   concepts   concrete   config   configration   configurability   configurable   configuration   configured   conflict   connection   console   constructor   consuming   context   convenience   cookie   copy   cost   could   couple   coupled   course   covered   covering   create   creates   creating   current   cygwin   data   database   deal   def   defaults   define   defined   definitions   deployment   described   describes   descriptor   descriptors   details   developers   did   different   directly   directory   dirname   do   does   don   done   dot   dumb   each   earlier   easily   easy   echoed   edit   effect   either   elegantly   else   enabling   end   enough   ensures   entered   entire   env   environ   environment   environments   equivalent   evaluated   evaluation   even   everywhere   example   except   executable   execute   executed   exit   experienced   export   expression   expressions   extension   extremely   facilities   far   few   file   files   finally   find   first   fix   followed   following   follows   for   format   formats   found   framework   frameworks   from   fully   function   gave   generally   get   getting   giving   goal   going   good   got   greeted   grouped   harder   has   have   haven   headings   hellonewworld   helloworld   helpful   here   home   how   however   idea   ideas   identifiers   if   implement   implementations   import   imported   in   information   inherit   ini   inside   install   installed   instance   instances   instead   integer   interested   interesting   interfaces   interpret   intimidating   into   introducing   invoke   involve   is   ish   issues   it   its   itself   just   keeping   keys   know   larger   last   later   lazily   lazy   learned   left   lesson   let   lib   like   line   lines   list   little   ll   load   loaded   loading   loads   local   located   logging   long   longer   look   looked   looking   looks   made   magic   main   majority   make   making   manner   many   mean   means   mechanism   mechanisms   meet   memory   message   messing   method   methods   might   minimal   module   modules   moment   more   most   move   moving   much   multiple   must   name   named   names   namespace   naming   need   needed   needs   never   new   next   no   non   normally   not   note   nothing   noticing   now   object   objects   obtain   of   off   offers   often   on   one   only   opening   operating   operation   opposed   optionally   options   or   order   ordinary   os   other   our   out   output   over   overridden   override   package   packages   page   parent   parse   parses   part   participate   pass   path   pay   peak   perhaps   pesky   pipelines   pje   place   placed   point   portable   position   possibilities   powerful   pretty   print   probably   process   program   programs   prompt   properties   property   prove   provide   provided   provides   pull   put   python   qualified   quickly   raise   re   read   real   reason   recap   receive   recent   recurring   redirection   remaining   replace   requirements   rest   restrictions   results   return   returns   reuse   reused   revised   right   roughly   rule   rules   run   runnable   running   runs   said   same   say   says   scratch   script   scripts   searching   section   sections   see   self   semantics   sense   separately   service   services   set   setting   several   share   shared   shell   shells   shifted   short   shortcut   shortcuts   should   show   shown   side   significance   similar   simple   simplest   simply   since   single   site   so   socket   some   someone   something   special   specific   specifics   specified   square   src   standalone   start   starting   state   statement   statements   stderr   stdin   stdout   step   stick   still   string   stuff   style   subclass   subclassed   subsequent   such   suffice   suitable   supplied   supply   supported   supports   surface   syntactically   syntax   sys   system   systems   take   takes   telling   terminal   text   than   that   the   their   them   theme   themes   then   there   they   things   this   those   though   through   thus   time   to   told   toolkit   treat   trivial   try   turn   two   type   ultimately   under   unix   unoriginal   up   us   usage   use   used   useful   users   uses   using   usr   usually   utility   valid   value   values   variable   variables   various   vast   ve   version   very   via   wait   want   was   way   ways   we   were   what   whatever   when   where   which   whole   whose   wide   will   wish   with   within   without   won   work   works   world   would   write   writing   wrong   wrote   yet   you   your  

Clear message


Up: IntroToPeak Next: IntroToPeak/LessonTwo

Lesson One: Commands and Configuration

Contents

  1. Lesson One: Commands and Configuration
    1. Introduction
    2. Running helloworld under PEAK
    3. ".ini" Files and the "runIni" Command
    4. Executable Configuration Files
    5. Making the Application Object Configurable
    6. Binding attributes to configuration
    7. Points to Remember

Introduction

At base, a PEAK program is a Python program. PEAK is a toolkit and framework, which means it provides stuff you can use in your Python programs to get things done more easily and more quickly, and usually more elegantly.

So, the simplest PEAK "Hello, world!" program is also, in a trivial sense, the Python "Hello, world!" program:

    1 print "Hello, World!"
This, however, is not very interesting.

One of the things PEAK provides is a framework for giving application programs easy access to their associated configuration data. The goal of this first chapter will be to show how to turn the output of the simple "Hello, world!" program into a configured string. I'm going to get us there step by step, keeping the program in a runnable state at each step (except the first!)

Running helloworld under PEAK

Let's start by introducing the peak command. For convenience in starting PEAK applications, the peak.running package provides a peak command-line script, that is installed alongside Python when you install PEAK. We'll use the peak script to invoke our application. First, just type:

peak 
at your shell prompt, and see what comes up. You should receive a usage message, something like:
 
Usage: peak NAME_OR_URL arguments... 
 
The 'peak' script bootstraps and runs a specified command object or command 
class.  The NAME_OR_URL argument may be a shortcut name defined in the 
'peak.running.shortcuts' property namespace, or a URL of a type 
supported by 'peak.naming'.  For example, if you have a class 'MyAppClass' 
defined in 'MyPackage', you can use: 
 
    peak import:MyPackage.MyAppClass 
 
to invoke it.  Arguments to the found object are shifted left one position, 
so in the example above it will see 'import:MyPackage.MyAppClass' as its 
'argv[0]'. 
 
The named object must implement one of the 'peak.running' command interfaces, 
or be callable.  See the 'Bootstrap' class in 'peak.running.commands' for 
more details on creating command objects for use with 'peak'.  For the 
list of available shortcut names, see '/usr/lib/python/site-packages/peak/peak.ini'. 
 

(The last line will list the actual path to peak.ini on your system.)

Okay, that looks helpful, although the stuff about command interfaces and shortcuts is perhaps a little intimidating. For now, we'll stick to the easy part, where it said we could use a callable! Let's make a helloworld module, with a HelloWorld function, in helloworld.py:

    1 def HelloWorld():
    2     print "Hello, world!"

Can we run it with the peak script now? Let's try it:

 
% peak import:helloworld.HelloWorld 
Traceback (most recent call last): 
  File "C:\Python22\Scripts\peak", line 7, in ? 
    sys.exit( 
  File "C:\cygwin\home\pje\PEAK\src\peak\running\commands.py", line 303, in __call__ 
    return cmd.interpret(cmd.argv[1]) 
  File "C:\cygwin\home\pje\PEAK\src\peak\running\commands.py", line 624, in interpret 
    raise InvocationError("Name not found: %s" % name) 
peak.running.commands.InvocationError: Name not found: import:helloworld:HelloWorld 
 

Oops. What's wrong? Oh, wait, since helloworld is a module, not a script, it needs to be on the PYTHONPATH. Let's fix that:

 
% export PYTHONPATH=. 
% peak import:helloworld.HelloWorld 
Hello, world! 
 

Nice! But we said we were going to make the message configurable. To do that, we're going to use the peak runIni command.

".ini" Files and the "runIni" Command

runIni is one of the built-in "shortcut" commands that were alluded to in the peak script's usage message. It takes the argument following runIni, and loads it as a configuration file. Take a moment now to type peak runIni, and look at the usage message:

 
Usage: peak runIni CONFIG_FILE arguments... 
 
CONFIG_FILE should be a file in the format used by 'peak.ini'.  (Note that 
it does not have to be named with an '.ini' extension.)  The file should 
define a 'running.IExecutable' for the value of its 'peak.running.app' 
property.  The specified 'IExecutable' will then be run with the remaining 
command-line arguments. 
 

Okay, there's that business about executable command interfaces again, but we won't let that put us off because we know we can get away with using a function for now.

So, now we know we need an .ini-format file, with a peak.running.app property. But it doesn't have to actually be named with .ini, so let's just call our configuration file helloworld:

 
[peak.running] 
app = importString("helloworld.HelloWorld") 
 

(Note to experienced Pythonistas: PEAK does not use the Python ConfigParser module to parse .ini files, so don't assume that you can use ConfigParser syntax or semantics here. To meet PEAK's configuration requirements, both the syntax and semantics needed to be different from those supplied by ConfigParser.)

How did we know to format the file this way? Well, we looked at the peak.ini file, of course, and learned by example. Here we've defined a configuration rule for the property named peak.running.app. The rule says (in effect), "when someone asks for the peak.running.app property, call the PEAK importString function on the string "helloworld.HelloWorld", and return the value.

The importString() function acts like a Python import statement, such that importString("x.y.z") is equivalent to from x.y import z. Thus, the importString() line of the config file shown above is roughly equivalent to:

 
    from helloworld import HelloWorld as app 
 

The reason we say "roughly" is that PEAK configuration files aren't executed the way Python code is. Properties in the configuration file are not Python assignment statements: they're rules that supply a Python expression whose value is computed only when the property is looked up. When the configuration file is loaded, the expressions are read, but not executed. Indeed, if the property is never looked up, the expression is never evaluated at all.

This can be a big advantage over using a Python script for configuration, since you can create a shared .ini file that describes properties used by several applications. Even if some of those properties might involve a time-consuming operation such as opening a database connection, only the applications that actually use the property will pay the cost of computing it.

Indeed, this allows you to use a single site-wide configuration file to provide configuration for all of your PEAK-based applications, using the PEAK_CONFIG environment variable to point to the file. Of course, the applications can't use property names that conflict with other applications' property names. But, there are many configuration properties used by PEAK that you may wish to share across multiple applications, such as logging rules, naming services, and command shortcuts, to name just a few. Property definitions supplied by application-specific configuration files will of course override any definitions in the site-wide configuration defaults.

Anyway, we now have a configuration file for our application, trivial as it is so far. Let's try it out:

 
% peak runIni helloworld 
Hello, world! 
 

Looks like it still works.

By the way, just as a side note, if an application object like our HelloWorld function returns a value, that value has significance. If the value is an integer (or None), it will be used as the return code from the peak script, and of our command line as a whole. If it is not an integer, it will be echoed to the terminal, and 1 will be used as the return code from the peak script. If you want, try this out now by adding various return statements to your helloworld.py and checking the exit code from the peak script in your shell.

Executable Configuration Files

The configuration file for a complex application is often central to that application. Under Unix-like command shells, PEAK allows configuration files to be the "executable" file of the application, the file whose name is entered at the console to invoke the program. This is done using the "magic cookie" of the unix shell:

 
#!/usr/bin/env peak runIni 
 
[peak.running] 
app = importString('helloworld:HellowWorld') 
 
This uses the Unix utility env to find the peak command in the current PATH, and calls it. peak then parses the file just like it does when called via peak runIni [file] from the command line. (Note the configuration file must also be made executable with chmod +x helloworld if you're using a Unix-like OS, as opposed to just a Unix-like shell.)

(Unfortunately, the format of #! lines is extremely non-portable. The example we gave above will work on BSD-ish Unixes, which pass all of the arguments on a #! line separately. For other operating systems, you may need to use the invoke program, as described on the IntroToPeak main page.)

Python still needs imported modules to be on the PYTHONPATH, though, so you have to call this helloworld file like this:

 
% PYTHONPATH=. ./helloworld 
 
(Or else set the PYTHONPATH separately, as we did earlier. In that case, you can just run ./helloworld. Try it now.)

In a real application, you could of course install your modules in a directory on the system PYTHONPATH (such as in the Python site-packages directory), thus enabling your command to simply be called by name, without setting any pesky environment variables.

If you can't arrange for the package to be on the system PYTHONPATH, or you don't want it to be there, you have a couple of different options. For example, you might change the "magic cookie" line to read:

#!/usr/bin/env PYTHONPATH=/where/my/modules/are peak runIni 

But this doesn't work on operating systems that treat the entire #! line as a single command argument. For those operating systems, you might write a short shell script instead. In this case you might call the config file helloworld.ini, since the shell script will be called helloworld. If helloworld.ini and helloworld.py are in the same directory as the helloworld script, the script might look like this:

 
#!/bin/sh 
here=`dirname $0` 
PYTHONPATH=$here peak runIni $here/helloworld.ini 
 
Of course, in a real app you'd probably put the script in your /usr/local/bin directory or equivalent and the module files in some special directory, and replace $here with the actual path to those module files.

We're going to assume from here on out that you've got your system configured to run the helloworld example when you type ./helloworld. You can do that by making helloworld an executable in one of the ways we've described, or you can just cheat and type peak runIni helloworld everywhere we say to type ./helloworld.

Making the Application Object Configurable

Okay, enough messing around with application deployment issues, and on to configuration. Let's move the "Hello world" message into the configuration file.

The configration file follows .ini format, which means you have "sections" in square brackets followed by lines that set values for various names within the section. PEAK doesn't place many restrictions on what sections or property names can appear in an .ini file, so we can pretty much choose whatever section name and property names we want, as long as they're valid Python identifiers.

We'll be very unoriginal and call our configuration section helloworld. (It's generally a good idea to have an application's properties grouped under names that begin with the application name, so they won't conflict with those of other applications.) The text of our message will be placed in the message configuration property:

 
#!/usr/bin/env peak runIni 
 
[peak.running] 
app = importString('helloworld:HelloWorld') 
 
[helloworld] 
message = "Hello, world!" 
 
At this point, you can run helloworld again, and nothing will have changed. PEAK will have loaded the new configuration information we provided, but our program doesn't yet actually use that information.

To use the configuration information we need to obtain it via the PEAK configuration framework. At this point we are actually starting to use PEAK facilities directly in our Python code, so we'll need to import them from the PEAK package. PEAK provides a simple way to access the vast majority of the PEAK facilities through its api module:

 
from peak.api import * 
 
This will load into the local namespace a minimal set of names through which all of the PEAK frameworks can be accessed. It uses a lazy import mechanism, so ultimately your program will only end up loading into memory those PEAK facilities it actually uses. (As you might be noticing, "lazy loading" and "lazy evaluation" are recurring themes in PEAK. We'll be running into them over and over again, in this and subsequent chapters.)

In addition, we can no longer use a simple, dumb function object as our application. In order to access the configuration information, the application object must participate in the appropriate PEAK frameworks.

In this case, the framework for a simple command line command such as our helloworld is provided by the AbstractCommand class of the peak.running.commands module (lazily imported for us by peak.api):

 
class HelloWorld(commands.AbstractCommand): 
 
An "abstract" class is a base class that cannot be used by itself. To be used, it must be subclassed and certain abstract methods overridden with "real" code in the concrete subclass.

In the case of AbstractCommand, there is only one method we need to override: the _run method, which will do the actual work of our application (and optionally return an exit code, just like our function could). So, without getting into the configurability yet, our complete helloworld.py file would now look like this:

    1 from peak.api import *
    2 
    3 class HelloWorld(commands.AbstractCommand):
    4 
    5     def _run(self):
    6         print >>self.stdout, "Hello, world!"
Hmm, what's that output redirection? Doesn't print normally write to stdout?

Indeed, it does. And self.stdout for an AbstractCommand is normally the same as Python's sys.stdout; certainly this is so for our application here. But an AbstractCommand can be run in many environments, not just from a shell script. For example, in a later lesson we'll run this very same HelloWorld application as a service over a TCP/IP socket. If we wrote a simple print statement, it would be harder to reuse our command object.

So, when writing a command object, we use self.stdin, self.stdout, self.stderr, self.environ, and self.argv to obtain the files and values that a standalone program would obtain via the sys and os modules. This allows our command objects to be composed into larger commands within a Python program, almost as though we were building Unix pipelines within a single process.

Anyway, we can run our program again at this point, and get the same results as before. But we still haven't used the configuration information. To do that, we need the binding package, to "bind" an attribute of our application to a configuration variable.

Binding attributes to configuration

Specifically, we will use the Obtain class from the peak.binding package. Obtain, coupled with the PropertyName class, allows us to pull values out of the configuration file:

 
    message = binding.Obtain(PropertyName('helloworld.message')) 
 

Note that this code does not mean, "set message to the configuration property helloworld.message. Instead it means, "when the message attribute of an instance of this class is accessed, obtain the helloworld.message property, and use that as the attribute's value from then on." (See? We told you lazy loading was a recurring theme in PEAK!)

Notice that this means bindings like Obtain are Python "descriptors", similar to the Python built-in type property. So, in order for a binding to be used, it must be placed in a class, and then used from an instance of the class.

Obtain can find things using mechanisms other than PropertyName, but PropertyName is the one we are interested in here. PropertyName is a string subclass, and its constructor takes as its argument the fully qualified property name. By "fully qualified", I mean the name of the section the property is found in, followed by a dot, followed by the rest of the property name. So the message property from our helloworld section becomes the PropertyName helloworld.message.

The PropertyName class ensures that its instances are of valid syntax for use in a configuration file, but that's not the only reason we need to use it. If we gave Obtain an ordinary string, it would not look it up in our configuration file. Instead, it would try to use the string as a URL or a component path -- two ideas we don't need or want to get into right now.

Anyway, when we access our HelloWorld object's message attribute in the _run() method, the binding.Obtain descriptor will look up the configuration property for us. It does this by searching the context in which the instance is located. The peak.running.commands framework will provide us with with such a context automatically, when it creates an instance of our HelloWorld class. (We won't deal with the specifics of this mechanism here; suffice to say that some code we inherit from AbstractCommand will take care of the details for us.)

Let's look at the revised program:

    1 from peak.api import *
    2 
    3 class HelloWorld(commands.AbstractCommand):
    4 
    5     message = binding.Obtain(PropertyName('helloworld.message'))
    6 
    7     def _run(self):
    8         print >>self.stdout, self.message
With this version of helloworld.py we are finally using the configuration information provided in the helloworld file. To prove this, first run the helloworld command:
 
% ./helloworld 
Hello, world! 
 
Now copy helloworld to hellonewworld and edit it to look like this:
 
#!/usr/bin/env peak runIni 
 
[peak.running] 
app = importString('helloworld.HelloWorld') 
 
[helloworld] 
message = "Hello, new world!" 
 
If you now run this new command:
 
% ./hellonewworld 
 
you'll be greeted by our new message:
 
Hello, new world! 
 
Although this is a very simple example, it should already be clear that PEAK provides an easy to use and powerful configuration mechanism. Simple use of PropertyName and values provided in config files barely even scratch the surface of what PEAK can do, but even if you used nothing else PEAK would still be useful just for making it easy to write configurable python shell scripts!

Points to Remember

Before moving on to the next lesson, let's recap the concepts we've covered so far:

Up: IntroToPeak Next: IntroToPeak/LessonTwo


PythonPowered
EditText of this page (last modified 2010-01-12 21:21:04)
FindPage by browsing, title search , text search or an index
Or try one of these actions: AttachFile, DeletePage, LikePages, LocalSiteMap, SpellCheck