grass icon indicating copy to clipboard operation
grass copied to clipboard

grass.experimental: Add name prefixes to Tools

Open wenzeslaus opened this issue 6 months ago • 1 comments

Name prefixes allows a Tools object to represent only a subset of tools with a common prefix. This provides functionality which is similar to grass.pygrass.modules.shortcuts, although the PR as it is implemented now adds really only the functionality to have things running that way and doesn't provide the actual objects to import. It may provide an alternative interface appealing to some users (grass.pygrass.modules.shortcuts is used in several addon tools and I know that @pesekon2 is using it). However, this would add another way of using it, making documentation and examples more challenging. In any case, this is an optional functionality which we can, but don't have to, add to tools. We can also add the functionality (what is in the PR now, but don't add any of the library objects like grass.pygrass.modules.shortcuts has).

The PR provides code which not only makes the individual tools usable this way, but also implements support for prefixes in attribute access and error messages when the tools are not found (showing the short method and the theoretical underlying tool).

I would be interested in opinions on whether to include this, how much to promote it, and whether to go further, and include "shortcut"/"prefix" objects into the library.

The PR currently build on top of #2923 (not merged yet), so the diff is large.

Examples

Basic example of use code. Creating a variable like this is totally a user choice, so it does not complicate the documentation of tools themselves. Unlike grass.pygrass.modules.shortcuts, this is actually more aligned with the basic case because only piece which is different is using raster.mapcalc(...) instead of tools.r_mapcalc(...) (while instantiating Module from grass.pygrass is different than importing from grass.pygrass.modules.shortcuts).

raster = Tools(prefix="r")
raster.mapcalc(expression="streams = if(row() > 1, 1, null())")
raster.buffer(input="streams", output="buffer", distance=1)
assert raster.info(map="streams", format="json")["datatype"] == "CELL"

The variable name is again up to the user, similarly to using aliases for imports in grass.pygrass.modules.shortcuts, so using the short, original prefix is possible (I find this hard to understand actually because it breaks the Python expectations about what is a name and what is a operator - the Python operator . becomes part of the tool name). Anyway, this common way of using grass.pygrass.modules.shortcuts is possible:

r = Tools(prefix="r")
r.mapcalc(expression="streams = if(row() > 1, 1, null())")
r.buffer(input="streams", output="buffer", distance=1)
assert r.info(map="streams", format="json")["datatype"] == "CELL"

Just to make the point, that user can choose the variable name to fit whatever they need, here is another example:

raster_tools = Tools(prefix="r")
raster_tools.mapcalc(expression="streams = if(row() > 1, 1, null())")
raster_tools.buffer(input="streams", output="buffer", distance=1)
assert raster_tools.info(map="streams", format="json")["datatype"] == "CELL"

What I find somewhat interesting, although without seeing any potential big of it, is using prefixes for sets of tools such as r.sim or r.stream, possibly tailoring the syntax to a specific use case:

simwe = Tools(prefix="r.sim")
simwe.water(...)
simwe.sediment(...)

Just like grass.pygrass.modules.shortcuts, shorter names suffer from a greater Python keyword conflict potential, so with prefixes active, the implementation allows trailing underscore for tool names so that things like v.import work:

vector = Tools(prefix="v")
assert vector.import_(...)

As already mentioned above, the proposed functionality could be used in the library to create the tools to be used later which would make it really similar to grass.pygrass.modules.shortcuts):

# grass/experimental/tools/shortcuts.py
raster = Tools(prefix="r")

# my_script.py
from grass.experimental.tools.shortcuts import raster as r

r.mapcalc(expression="streams = if(row() > 1, 1, null())")
r.buffer(input="streams", output="buffer", distance=1)
assert r.info(map="streams", format="json")["datatype"] == "CELL"

However, a session would then need to be provided through an env parameter like now (while using a Tools object directly avoids that by Tools accepting a session parameter):

from grass.experimental.tools.shortcuts import raster as r

# ...some special session setup

r.mapcalc(expression="streams = if(row() > 1, 1, null())", env=session.env)
r.buffer(input="streams", output="buffer", distance=1, env=session.env)
assert r.info(map="streams", format="json", env=session.env)["datatype"] == "CELL"

wenzeslaus avatar Jun 13 '25 18:06 wenzeslaus

I like it and I'm up for that. However, if I would be the only user, I can live without that in order to make code cleaner and without unused features.

pesekon2 avatar Jun 15 '25 00:06 pesekon2