On Jul 19, 2008, at 12:01 PM, Glynn Clements wrote:


Michael Barton wrote:


On Jul 18, 2008, at 11:03 PM, <[EMAIL PROTECTED]> wrote:

Date: Fri, 18 Jul 2008 18:20:46 +0100
From: Glynn Clements <[EMAIL PROTECTED]>
Subject: Re: [GRASS-dev] Python Scripting
To: "Dan D'Alimonte" <[EMAIL PROTECTED]>, [email protected]
Message-ID: <[EMAIL PROTECTED]>
Content-Type: text/plain; charset="us-ascii"


Glynn Clements wrote:

As to existing modules, what about a helper function to access then?

module.executeModule( name="r.stats", options={ "input":
"elevation.dem,slope,aspect", "fs": ",", "output": "elev.csv"},
flags=["q", "1", "n", "g"] )

This idea has occurred to me. Some comments:

Pass argument values as Python values, e.g. passing multiple values
as
lists, passing numeric types directly, etc, and have the interface
convert them to strings. Pass the flags as a single string.

module.execute( "r.stats",
options = { "input": ["elevation.dem", "slope", "aspect"],
                          "fs": ",",
                          "output": "elev.csv" },
              flags = "q1ng" )

Provide a lower-level function which simply generates the command to pass to Popen(), for cases where you want to interact with the child
process.

I have attached a first draft of such a module, which also includes a
wrapper function for g.parser (for which an example script is also
attached).

Do you still run a GRASS command as subprocess.call([command...])? Or
is there another syntax with your wrapper script (e.g., as in the
module.execute() above)?

Yes, although it's actually called grass.run_command().

Specifically, this:

def make_command(prog, options = [], flags = "", overwrite = False, quiet = False, verbose = False):
                ...

constructs a list suitable for use as the "args" argument to the
Popen() constructor or to call(). E.g.:

        >>> import grass
        >>> grass.make_command( "r.stats",
options = { "input": ["elevation.dem", "slope", "aspect"],
                                    "fs": ",",
                                    "output": "elev.csv" },
                        flags = "1ng" )
['r.stats', '-1ng', 'input=elevation.dem,slope,aspect', 'fs=,', 'output=elev.csv']

This:

def start_command(prog, options = [], flags = "", overwrite = False, quiet = False, verbose = False, **kwargs): args = make_command(prog, options, flags, overwrite, quiet, verbose)
            return subprocess.Popen(args, **kwargs)

does just that: constructs the argument list then passes it to the
Popen() constructor, along with any additional keyword arguments (so
you can set stdin, stdout, etc), and returns the Popen() object. E.g.:

        >>> import sys
        >>> import subprocess
        >>> import grass
>>> p = grass.start_command( "g.list", options = { "type": "rast" }, stdout = subprocess.PIPE )
        >>> txt = p.communicate()[0]
        >>> sys.stdout.write(txt)
        ----------------------------------------------
        raster files available in mapset <PERMANENT>:
[snip]

Finally, this:

        def run_command(*args, **kwargs):
            ps = start_command(*args, **kwargs)
            return ps.wait()

is analogous to call(), but with the GRASS-oriented interface of
make_command and start_command, e.g.:

        >>> import grass
        >>> grass.run_command( "g.list", options = { "type": "rast" } )
        ----------------------------------------------
        raster files available in mapset <PERMANENT>:
[snip]

The option values can be strings, numbers, tuples, or lists, and are
converted appropriately; numbers (well, anything except for strings,
tuples and lists) are converted with str(), strings are taken
literally (i.e. they aren't quoted), tuples and lists have their
components converted and separated by commas, e.g.:

        >>> import grass
>>> grass.make_command( "prog", options = { "arg": [(1,10), (2,20)] } )
        ['prog', 'arg=1,10,2,20']

It has just occurred to me that it might be better to take the options
as keyword arguments, rather than an explicit dictionary, e.g.:

        grass.make_command( "r.stats",
                flags = "1ng",
                input = ["elevation.dem", "slope", "aspect"],
                fs = ",",
                output = "elev.csv" )

You could still pass a dictionary using the ** syntax:

        opts = {"input": ["elevation.dem", "slope", "aspect"],
                "fs": ",",
                "output": "elev.csv" }
        grass.make_command( "r.stats", flags = "1ng", **opts)

This would be trivial to implement:

-def make_command(prog, options = [], flags = "", overwrite = False, quiet = False, verbose = False): +def make_command(prog, flags = "", overwrite = False, quiet = False, verbose = False, **options):

but there is the (remote) possibility that a module option could
conflict with one of the predefined arguments (prog, flags, overwrite,
quiet, or verbose), or with one of Popen()'s arguments (which
run_command and start_command would have to handle explicitly, rather
than using **kwargs).


Glynn,

I am probably missing something. But I guess I don't understand the advantage of using the modules available from this grass library over using normal Python functions to run a GRASS command. That is, the syntax for running a command here doesn't seem any easier--and maybe a shade more complicated than simply using subprocess.call for simple one-shot commands and subprocess.Popen for commands where you need to return stdout or stderr.

Are there other benefits to using a grass library that I'm not understanding?

Michael

_______________________________________________
grass-dev mailing list
[email protected]
http://lists.osgeo.org/mailman/listinfo/grass-dev

Reply via email to