[TransWarp] PEAK Tutorial

Phillip J. Eby pje at telecommunity.com
Wed Nov 13 22:19:15 EST 2002

At 11:20 PM 11/13/02 +0100, Ulrich Eck wrote:
>Hi Phillip,
>>Understood.  I'd like to have some useful example applications, but until
>>we finish the rest of the 0.5 release plans, there aren't many generally
>>useful programs we can write that show off the framework.  The binding,
>>config, and naming packages are extremely useful as *part* of an
>>application, and could be used to develop sysadmin-ish scripts and tools,
>>but "real" apps want a domain model and storage capabilities.
>at least config and naming are really new to us (didn't have such a thing
>with Transwarp) and some (complete) snippets would help alot to
>get into it without having to dig through all the code guessing how
>it could work.

The main usage for the naming system is to have a way to decouple "what" 
you want, from how it must be accessed.  Thus, one may reference a name 
like "myApplicationDB", and have that translated to an actual database 
connection.  Logfiles, lockfiles, and various other kinds of services are 
useful to look up by name as well.  In theory, if you have a distributed 
object naming service, you could implement a peak.naming "context" class 
which could be used to retrieve objects from the naming service.

At a more detailed level, the naming package also provides support for 
"addresses", which are generally more specific than names.  Instead of 
"myApplicationDB", an address might be something like 
"pgsql://user:pass@server/dbname", to specify a particular PostgreSQL 
database.  Addresses are also handled by naming contexts, but for many 
kinds of addresses, it's sufficient to use the "GenericURLContext" and 
simply register a URL scheme class that parses the address and provides a 
default implementation for retrieval.

So far, the naming package only has two contexts existing: AbstractContext 
and GenericURLContext.  Before 0.5, I'd like to have at least one "flat" 
namespace context (e.g. a context that looks up configuration properties) 
and one "hierarchical" namespace context (e.g. a filesystem context that 
gives you a namespace into a file system).  Currently, the two contexts Ty 
and I have built outside of PEAK proper are simple flat contexts that run 
off of configuration files that are of interest or use only in our 
employer's runtime environment, that allow us to refer to existing abstract 
names (e.g. "app1_db") for database connections in that environment.

A big advantage of the peak.naming framework is simply that it defines a 
standardized interface for dealing with, well, namespaces.  An application 
written to use the peak.naming interfaces doesn't need to be changed much 
to deal with different back ends or changing low-level details.  One gives 
the system names, and gets back objects, or gives the system an object and 
tells it what name to store it under.  If you take a look at the JNDI 
tutorials for Java, you'll get an idea of the motivation behind 
peak.naming, but of course peak.naming is *much* simpler to deal with both 
as a user and as a framework extender.  (Although there are still some 
minor bits of Java cruftiness remaining in peak.naming that I haven't yet 
figured out how to purge.)

The configuration package is designed to do two things: 1) provide a 
framework for "placeful" and "lazily immutable" lookups of data by 
interface or property name, and 2) establish specific "root" components 
outside of one's explicit component hierarchy which can supply a place for 
"placeless" lookups.

Both utilities (keyed by Interface objects) and properties (keyed by 
PropertyName strings) are looked up using objects' _getConfigData() 
methods, if present.  If something isn't found, it's looked for in the 
object's parent, and so on until the "top" of the explicit hierarchy is 
reached.  At that point, the system continues the search at the 
"LocalConfig" which corresponds to that root component, and then finally to 
the system-wide "GlobalConfig" object.

This is the basic infrastructure of peak.config.  In addition, peak.config 
offers a variety of components and functions to make the actual 
configuration process easier.  For example, it can parse configuration 
files with a fairly sophisticated, Python-and-PEAK-oriented format, that 
supports inclusions of data in arbitrary user-defined formats.  (See the 
"[peak.config.loaders]" section of the peak.ini file, and the 
config.ISettingLoader interface.)

peak.binding, peak.config, and peak.naming all interconnect with each other 
in fairly intimate ways.  Binding components define _getConfigData() 
methods, for example, and attribute bindings like bindToUtilities and 
bindToProperty call back into the configuration system to look up 
values.  The naming system relies on configuration properties and utilities 
in order to find handlers for different name and address schemes, and to 
control its operation in other ways.  Attribute bindings of course can also 
call into the naming system to look up other components, and the naming 
system components are implemented in terms of binding components.

Where things get really interesting, and it can make your head spin when 
you think about it, is that since the naming system is controlled by the 
configuration system, which in turn is completely contextual, it means that 
you could in principle have two components with a completely different idea 
of what say, "http:" means.  Which is a very good thing, because in the 
same application you might have different components that *need* different 
things from an HTTP connection.  All you need to do is supply appropriate 
configuration "near" the affected objects.

And you can do this either at runtime via the config API's, or you can do 
it using things like binding.Constant() at code-writing time.  Or you can 
put it in a config file...  Or...

Well, guess I should save something for the tutorial, huh?

>We have experience in using the binding package and i can probably
>guess how the storage parts work.

The easiest way to jump into a PEAK package, at least if it's relatively 
mature, is to look at its "interfaces" module.  For example, in the naming 
package you'll see IBasicContext for naming lookups and address spaces, 
IReadContext and IWriteContext for namespaces that can iterate over their 
contents or store things for you, etc.

You needn't even use the source for this, as the online API docs render the 
interfaces almost as thoroughly (and more pleasantly formatted) as the 
source code.

>you have a note about a small script from ty .. would it be possible
>to give this as an example (all needed files) on how config and naming
>works ??

Afraid not, since as I mentioned before they're being used for proprietary 
things and wouldn't be of much use or interest outside anyway.  But the 
"peak.ini" file is a pretty good example of some of what the config system 
can do, and is also a good entry point to seeing how parts of the naming 
system work.  Interestingly, it was Ty's first reading of the "peak.ini" 
file that prompted him to say "This is something I could almost *use*!"

>that would save us a lot of time ..
>and like i said before, we're willing to help writing howto's
>and examples like i did it for Transwarp 0.2
>what do you think ??

I'd love to have some help.  But I don't really have any non-proprietary 
uses of PEAK to pass out to anybody right now, and am unlikely to until 
after the model and storage packages are done for 0.5.  So if there's 
anything that you can do to document, based on the code and docs that are 
out there right now, then great.

I tell you what, if you have a naming service that you use right now for 
distributed object stuff, how about you create a naming context based on 
it?  I'll "walk you through" the process of creating one via e-mail.  And 
then it can either get included in PEAK or supplied on the side as an 
example of a naming context.

The first steps for creating a context are:

1. Decide if it will be a Basic, Read, or Write context.  A "basic" context 
can really only look things up, or check if the name exists -- effectively 
__getitem__ and has_key() operations.  A "read" context can also iterate 
(think keys(), values(), items(), etc.), and a "write" context can have 
objects bound or unbound in it (think __setitem__, __delitem__, and rename).

2. Subclass AbstractContext (found in peak.naming.contexts).  Define a 
"_get()" method, based on the instructions found in the source of the base 
class.  This will get you a "basic" context.  Add an __iter__ method if you 
want a read context, and so on.

If you'll take a look at these steps and bring me your questions, I'll help 
you through it.

More information about the PEAK mailing list