diff --git a/How-to-create-a-GRASS-GIS-addon.md b/How-to-create-a-GRASS-GIS-addon.md index 8a10a2e..74457e4 100644 --- a/How-to-create-a-GRASS-GIS-addon.md +++ b/How-to-create-a-GRASS-GIS-addon.md @@ -33,7 +33,7 @@ A GRASS python module consists of - either a python script (most easy with one script as whole addon) - or C code -#### Reuse & Recycle: +### Reuse & Recycle... and refactor! - it helps if GRASS GIS source code is there to look at - best to look at existing GRASS addons @@ -43,60 +43,64 @@ A GRASS python module consists of - inside the GRASS GIS source code all python modules are in folder 'scripts' - or see https://grasswiki.osgeo.org/wiki/Category:Python, especially https://grasswiki.osgeo.org/wiki/GRASS_Python_Scripting_Library - use `r.blend --script` and it will generate how it would look like as addon (like a template, not a copy of source code!) (will always create generic long version) -- use of predefinded functions `import grass.script as gscript` (e.g. gscript.run_command, gscript.message, gscript.fatal, gscript.warning, gscript.read_command) +- use of predefinded functions `import grass.script as grass` (e.g. grass.run_command, grass.message, grass.fatal, grass.warning, grass.read_command) + - See [script documentation](https://grass.osgeo.org/grass83/manuals/libpython/script.html) for more usage examples - located in 'python/script', please read to avoid to reinvent the wheel - TODO add docs (but better to read source code because it might be more up-to-date) - use of [grass-gis-helpers](https://github.com/mundialis/grass-gis-helpers) library + - also beware of copying multiple code mutiple times, if there are only small changes. Consider adding methods to [grass-gis-helpers](https://github.com/mundialis/grass-gis-helpers) library instead and reuse. - use of [github-workflows](https://github.com/mundialis/github-workflows) - add linting workflow - add workflow to run tests - Submitting rules: - https://github.com/OSGeo/grass/blob/master/CONTRIBUTING.md -#### Structure (here `r.blend` as example) +### Structure (here `r.blend` as example) 1. shebang (first line) - -1. header (author, purpose, license) - -1. `# % ` comments are important (ignored by python but important for parser) +2. header (author, purpose, license) +3. `# % ` comments are important (ignored by python but important for parser) - See https://grass.osgeo.org/grass-devel/manuals/g.parser.html - ``` + ```shell r.blend -c first=aspect second=elevation output=elev_shade_blend ``` ###### `# % module` + - including `keyword` to make it appear in keyword searches and lists + ###### `# % options` - (e.g. 'input', 'output', 'first'), some are predefined (predefined or custom, predefined is more convenient but also needs more knowledge to use) - key is key in command line (e.g. 'first') - answer is default values - access them in main function like `options['first']` + - there are also [standard options]("https://grass.osgeo.org/grass84/manuals/parser_standard_options.html) which can be extended ###### `# % flag` -1. `def main():` reads in all variables (`options['first']`) + ###### `# % rules` - - a main function is required - -1. indefinite additional functions are possible + - define dependencies between options, required options and more. See official [docs](https://grass.osgeo.org/grass84/manuals/g.parser.html#conditional-parameters) -1. include parser at the end before calling main function: +4. `def main():` reads in all variables (`options['first']`) + - a main function is required +5. indefinite additional functions are possible +6. include parser at the end before calling main function: - ``` + ```python if __name__ == "__main__": - options, flags = gscript.parser() + options, flags = grass.parser() main() ``` or optionally clean temporary stuff in 'cleanup' function and call it on exit - ``` + ```python if __name__ == "__main__": - options, flags = gscript.parser() + options, flags = grass.parser() atexit.register(cleanup) main() ``` @@ -104,7 +108,7 @@ A GRASS python module consists of ## Best practises - python style guide - - [PEP 8 – Style Guide for Python Code](https://peps.python.org/pep-0008/%5D) + - [PEP 8 – Style Guide for Python Code](https://peps.python.org/pep-0008/]) - https://trac.osgeo.org/grass/wiki/Submitting - https://trac.osgeo.org/grass/wiki/Submitting/Python - html documentation (no full html please, parts are auto-generated at compile time) @@ -121,18 +125,24 @@ A GRASS python module consists of ``` - for this to work use message standardisation (look at locale) -- how to name it - - start with 'v.' if it is a vector module - - start with 'r.' if it is a raster module - - start with 'db.' if it is a database module - - try to stick to existing convention but no strickt rules - - do not name it too long + +### how to name it Choose a name depending on the "family": +- start with `v.` if it is a vector module +- start with `r.` if it is a raster module +- start with `i.` if it is a imagery module +- start with `db.` if it is a database module +- try to stick to existing convention but no strickt rules +- do not name it too long +- existing families are d, db, g, i, m, ps, r, r3, t, test and v + +### mundialis / actinia specific + - How to handle dependencies (for installation within actinia) - use `requirements.txt` for python packages -- How can I log to actinia output? (with `gscript.message`) +- How can I log to actinia output? (with `grass.message`) - GRASS GIS addons need to be installed globally (see `$HOME`) - Which things are tricky - `db.login`, `db.connect -d` @@ -145,9 +155,8 @@ Choose a name depending on the "family": #### General steps -- Decide whether it should be a single addon or __multi-module / toolbox__. Example for multi-module is here: [t.sentinel](https://github.com/mundialis/t.sentinel) - -- Check for __sensitive information__. If included, remove them and publish without git history (or rewrite if needed). +- Decide whether it should be a single addon or **multi-module / toolbox**. Example for multi-module is here: [t.sentinel](https://github.com/mundialis/t.sentinel) +- Check for **sensitive information**. If included, remove them and publish without git history (or rewrite if needed). #### License + Copyright @@ -155,10 +164,6 @@ Choose a name depending on the "family": - Copyright: no single person (only as author), `mundialis -> mundialis GmbH & Co. KG` - Add license information to `*.py` file header: -- License is GPL3. If no LICENSE.md exists, create it later directly with GitHub (see section below). -- Copyright: no single person (only as author), `mundialis -> mundialis GmbH & Co. KG` -- Add license information to `*.py` file header: - ```shell # COPYRIGHT: (C) 2021-2024 by mundialis GmbH & Co. KG and the GRASS Development Team # @@ -178,7 +183,8 @@ Choose a name depending on the "family": #### Linting - Add reusable linting workflow as described [here](https://github.com/mundialis/github-workflows?tab=readme-ov-file#python-linting) - ``` + + ```yaml name: Python Flake8, black and pylint code quality check on: [push] @@ -187,6 +193,7 @@ Choose a name depending on the "family": lint: uses: mundialis/github-workflows/.github/workflows/linting.yml@main ``` + - Run locally. Mind that there might be differences due to version mismatches. To overcome this, pre-commit hooks from the same repository can be used @@ -195,6 +202,7 @@ Choose a name depending on the "family": flake8 --config=.flake8 --count --statistics --show-source . pylint . ``` + - Fix lint errors which black couldn't fix or add exception to `.flake8` file - Black does not fix GRASS GIS parameter `#%`. This can be batch changed with `sed -i 's|^#%|# %|g' foo.py` - There may not be any linting issues left as the pipeline would fail @@ -234,9 +242,4 @@ For more information on standardized messages see [here](https://trac.osgeo.org/ - Delete "old" code from internal repository - Add hint to internal repository README that the addon was moved and where to find it - Adjust README.md in actinia-assets/grass-gis-addons -- Adjust deployments which use the addon, if applicable - -- Delete "old" code from internal repository -- Add hint to internal repository README that the addon was moved and where to find it -- Adjust README.md in actinia-assets/grass-gis-addons -- Adjust deployments which use the addon, if applicable +- Adjust deployments which use the addon, if applicable \ No newline at end of file