<!--  vim: set sw=2 sts=2 et ft=docbk:

  Part of the A-A-P recipe executive: Using Python

  Copyright (C) 2002-2003 Stichting NLnet Labs
  Permission to copy and use this file is specified in the file COPYING.
  If this file is missing you can find it here: http://www.a-a-p.org/COPYING

-->

<para>
        Python commands can be used where &Aap; commands are not sufficient.
        This includes flow control, selecting the commands to be executed and 
        repeating commands.
</para>


<bridgehead>Using Python Lines</bridgehead>

<para>
Single lines of Python code can be included in the recipe by prepending "@".
This is most often used for flow control:
</para>

<programlisting>
        @if os.path.isdir("/usr/local/bin"):
            :copy $File /usr/local/bin
</programlisting>

<para>
You can write multiple Python commands, just prefix a "@" to every line.
Do remember that the amount of indent is used to form command blocks.
The indent that is used excludes the "@" character.
When there is a non-white character after the "@", the "@" is removed.
When there is white space after the "@" it is replaced with a space.
Generally you don't need to worry about this, if the indenting looks right it
probably is.
</para>

<para>
  The main advantage of using single Python lines is that they can be mixed
  freely with &Aap; recipe command lines.  You can use Python lines both at
  the recipe level and in build commands.  Again, just make sure the indent
  indicates command blocks.
</para>

<para>
To learn using Python start at the Python web site:
<ulink url="http://www.python.org/doc/">http://www.python.org/doc/</ulink>
</para>


<bridgehead>Using Aap variables in Python</bridgehead>

<para>
  You can use all the Aap variables in Python code.  You don't use the "$",
  Python syntax does not use a dollar for variables.
</para>
<para>
  When using a scope name, as in "_recipe.var", the variable otherwise looks
  the same as in Aap commands.  But when omitting a scope name it works
  differently.  While "$var" in Aap searches for "var" in the "_no" scope,
  this does not happen for Python.  Python only looks in the current scope.
  Thus you must explicitly specify "_no.var" in Python code to get the same
  effect as "$var" in an Aap command.
</para>

<para>
  In the following example a local variable "system" is used:
</para>

<programlisting>
        CFLAGS = -DFOO
        all:
            system = $OSTYPE
            @if system == "posix":
            @   flags = _recipe.CFLAGS
</programlisting>

<para>
  The same thing could be done by accessing $OSTYPE directly from Python:
</para>

<programlisting>
        CFLAGS = -DFOO
        all:
            @if _no.OSTYPE == "posix":
            @   flags = _recipe.CFLAGS
</programlisting>


<bridgehead id="python-expression">Using Python Expressions</bridgehead>

<para>
In an assignment, command arguments and most other places a Python expression
can be used in backticks.  Expanding this is done before expanding $VAR items,
because this allows the possibility to use the Python expression to result in
the name of a variable.  Example:
</para>

<programlisting>
        foovaridx = 5
        Foo = $Src`foovaridx`
</programlisting>
<para>
Is equivalent to:
</para>
<programlisting>
        Foo = $Src5
</programlisting>

<para>
The result of the Python expression in backticks should be a string or a list
of strings.  A list is automatically converted to a white-separated string of
all list items.
</para>

<para>
A Python expression cannot be used for the variable name in an assignment.
This doesn't work:
</para>
<programlisting>
        `varname` = this does not work
</programlisting>

<para>
If you really need this, use a Python command instead:
</para>
<programlisting>
        @eval(varname + ' = "value"')
</programlisting>

<para>
When using a function from a module, it must be imported first.  Example:
</para>
<programlisting>
        @from httplib import HTTP
        Connection = `HTTP('www.microsoft.com')`
</programlisting>

<para>
For your convenience these things are imported for you already:
</para>
<programlisting>
        from glob import glob
        from RecPython import *
</programlisting>

<para>
The RecPython module defines the Python functions listed in <xref
  linkend="ref-python"/>.
</para>

<para>
A backtick in the Python expression has to be escaped to avoid it being
recognized as the end of the expression:
</para>
<programlisting>
        CMD = `my_func("$(`)grep -l foo *.c$(`)")`
</programlisting>

<para>
contains the Python expression:
</para>
<programlisting>
        my_func("`grep -l foo *.c`")
</programlisting>

<para>
In the result of the Python expression $ characters are doubled, to avoid them
being interpreted as the start of a variable reference.  Otherwise Python
expressions with arbitrary results would always have to be filtered explicitly.
When the resulting string is used the $$ will be reduced to a single $ again.
</para>

<para>
  The result of the expression is used as a string in place of the expression
  and the backticks.  Example:
</para>

<programlisting>
        Foo = foo/`glob("*.tmp")`
</programlisting>

<para>
Would be equivalent to:
</para>

<programlisting>
        Foo = foo/one.tmp two.tmp
</programlisting>

<para>
Note that "foo/" is only prepended to the whole result, not each
white-separated item.  If you do want rc-style expansion, use two commands:
</para>
<programlisting>
        TT = `glob("*.tmp")`
        Foo = foo/$*TT
</programlisting>

<para>
Equivalent to:
</para>
<programlisting>
        Foo = foo/one.tmp foo/two.tmp
</programlisting>

<para>
Note that a following attribute is only attached to the last item resulting
from the Python expression.
</para>
<programlisting>
        Source = `glob('*.c')` {check = md5}
</programlisting>

<para>
Can be equivalent to:
</para>

<programlisting>
        Source = foo.c bar.c {check = md5}
</programlisting>

<para>
To apply it to all items, use the <link linkend="cmd-attr">:attr</link>
command:
</para>

<programlisting>
        Source = `glob('*.c')`
        :attr {check = md5} $Source
</programlisting>


<para>
Watch out for unexpected results when rc-style expansion is done for $*VAR.
Example:
</para>

<programlisting>
        VAR = one two
        Foo = $*VAR/`glob("*.tmp")`
</programlisting>

<para>
Would result in:
</para>

<programlisting>
        Foo = one/one.tmp two/one.tmp two.tmp
</programlisting>

<para>
because the `` part is expanded first, thus the assignment is executed like:
</para>

<programlisting>
        Foo = $*VAR/one.tmp two.tmp
</programlisting>

<para>
The backticks for a Python expression are also recognized inside quotes.
Thus you need to escape the special meaning there:
</para>

<programlisting>
        Foo = "this$(`)file" that$(`)file
</programlisting>

<para>
Backtick expressions can be used inside a string if you really need this:
</para>

<programlisting>
        DIR = /home/foo /home/bar
        :print "`DIR + "/fi le"`"
</programlisting>
<para>
results in:
</para>
<literallayout>        "/home/foo /home/bar/fi le"
</literallayout>

<para>
Compare this to:
</para>
<programlisting>
        :print "$*DIR/fi le"
</programlisting>
<para>
which results in:
</para>

<literallayout>        "/home/foo/fi le" "/home/bar/fi le"
</literallayout>


<bridgehead>Python Block</bridgehead>

<para>
  A block of Python commands is started with a
  <link linkend="cmd-python">:python</link> command.  If no
terminator string is specified the python code ends where the indent is equal
to or less than the
<link linkend="cmd-python">:python</link> command:
</para>

<programlisting>
    Source = foo.c bar.c
    :python
        for i in items:
            Source = Source + " " + i
    Target = foo
</programlisting>

<para>
Optionally a terminator string may be specified.  The indent of the Python
code may then drop below the indent of the
<link linkend="cmd-python">:python</link> command.
</para>


<para>
The terminator cannot contain white space.  A comment may follow.  The Python
block continues until the terminator string is found in a line by itself.  It
may be preceded and followed by white space and a comment may follow.
Example:
</para>
<programlisting>
    @if ok:
        :print finding include files
        :python EOF         # start of the Python block
    include = glob("include/*.c")
                EOF         # end of the Python block
</programlisting>

<bridgehead>Useful Python Items</bridgehead>

<para>
A list of Python functions defined by &Aap; can be found in the reference
manual, <xref linkend="ref-python"/>.
</para>

<programlisting>
        VAR = `os.environ.get('VAR', 'default')`
</programlisting>

<para>
Obtain environment variable $VAR.  If it is not set use "default".
</para>

<programlisting>
        @os.environ["PATH"] = mypath
</programlisting>

<para>
Set an environment variable.
</para>

<programlisting>
        files = `glob("*.ext")`
</programlisting>

<para>
Obtain a list of files matching "*.ext".  Aap will take care of turning the
list that glob() returns into a string, using quotes where needed.
</para>
<para>
The difference with using "*.ext" directly is that the expansion is done right
here, not later when $files is used.  The catch with using glob() here is that
when a file name contains a wildcard character it may be expanded again.  So
long as that expansion fails or matches the same file name it is still OK, but
it becomes rather unpredictable.  Use <link
linkend="python-wildescape">wildescape()</link> when needed.
</para>

<programlisting>
        choice = `raw_input("Enter the value: ")`
</programlisting>

<para>
Prompt the user to enter a value.
</para>

<programlisting>
        tempfile = `tempfname()`
</programlisting>

<para>
  Get a file name to use for temporary files.  The file will not exist.
  See <link linkend="python-tempfname">tempfname()</link>.
</para>
<para>
  If you create it you need to make sure it is deleted afterwards.
  Example:
</para>

<programlisting>
        tempfile = `tempfname()`
        @try:
          :print >$tempfile  start of file
          :print >>$tempfile $this variable may not exist and cause an error
          :cat $tempfile
        @finally:
          # this is executed whether there is an error or not
          :del $tempfile
</programlisting>

<para>
Exception handling can be useful to handle situations where you know something
might fail.  Example:
</para>
<programlisting>
     @try:
         :sys links -dump exec.html >exec.txt
     @except:
         :sys lynx -dump -nolist exec.html >exec.txt
</programlisting>
<para>
  This will execute the "links" command.  If this fails for some reason (e.g.,
  because a new version of "links" does not support the "-dump" argument) then
  the "lynx" command will be used.
</para>
