From 35278447c3d44e077cc9d13abac4bc77e5835180 Mon Sep 17 00:00:00 2001 From: jhalbauer Date: Wed, 20 Mar 2024 12:47:50 +0100 Subject: [PATCH] fixed md linting issue --- How-to-create-a-GRASS-GIS-addon.md | 241 +++++++++++++++-------------- 1 file changed, 128 insertions(+), 113 deletions(-) diff --git a/How-to-create-a-GRASS-GIS-addon.md b/How-to-create-a-GRASS-GIS-addon.md index b898534..780b2cf 100644 --- a/How-to-create-a-GRASS-GIS-addon.md +++ b/How-to-create-a-GRASS-GIS-addon.md @@ -26,82 +26,92 @@ are needed as a minimum. ## How to create a GRASS GIS addon A GRASS python module consists of -* Makefile -* html documentation file -* either a python script (most easy with one script as whole addon) -* or C code -#### Reuse & Recycle: -* it helps if GRASS GIS source code is there to look at -* best to look at existing GRASS addons - * [r.mapcalc.simple](https://github.com/OSGeo/grass/tree/master/scripts/r.mapcalc.simple) - * [v.example](https://github.com/mundialis/v.example) - * ... -* 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) - * 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 -* 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 +- Makefile +- html documentation file +- either a python script (most easy with one script as whole addon) +- or C code +#### Reuse & Recycle: +- it helps if GRASS GIS source code is there to look at +- best to look at existing GRASS addons + - [r.mapcalc.simple](https://github.com/OSGeo/grass/tree/master/scripts/r.mapcalc.simple) + - [v.example](https://github.com/mundialis/v.example) + - ... +- 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) + - 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 +- 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) + 1. shebang (first line) -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 - - ``` - r.blend -c first=aspect second=elevation output=elev_shade_blend - ``` - ###### ```# % module``` - ###### ```# % 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']``` - - ###### ```# % flag``` - -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: - - ``` - if __name__ == "__main__": - options, flags = gscript.parser() - main() - ``` - or optionally clean temporary stuff in 'cleanup' function and call it on exit - ``` - if __name__ == "__main__": - options, flags = gscript.parser() - atexit.register(cleanup) - main() - ``` +1. header (author, purpose, license) + +1. `# % ` comments are important (ignored by python but important for parser) + + - See https://grass.osgeo.org/grass-devel/manuals/g.parser.html + + ``` + r.blend -c first=aspect second=elevation output=elev_shade_blend + ``` + + ###### `# % module` + + ###### `# % 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']` + + ###### `# % flag` + +1. `def main():` reads in all variables (`options['first']`) + + - a main function is required + +1. indefinite additional functions are possible +1. include parser at the end before calling main function: + + ``` + if __name__ == "__main__": + options, flags = gscript.parser() + main() + ``` + + or optionally clean temporary stuff in 'cleanup' function and call it on exit + + ``` + if __name__ == "__main__": + options, flags = gscript.parser() + atexit.register(cleanup) + main() + ``` ## Best practises -* python style guide - * [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) - * https://trac.osgeo.org/grass/wiki/Submitting/Docs -* to support i18n, import following module and use macro '_' before strings. The text after will be replaced with existing lookup tables for other languages -* use existing functions: esp. from PyGRASS - * API description: https://grass.osgeo.org/grass-devel/manuals/libpython/pygrass_index.html - * PyGRASS paper: An Object Oriented Python Application Programming Interface (API) for GRASS: https://www.mdpi.com/2220-9964/2/1/201/htm +- python style guide + - [PEP 8 – Style Guide for Python Code](https://peps.python.org/pep-0008/%5D) + - 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) + - https://trac.osgeo.org/grass/wiki/Submitting/Docs +- to support i18n, import following module and use macro '\_' before strings. The text after will be replaced with existing lookup tables for other languages +- use existing functions: esp. from PyGRASS + - API description: https://grass.osgeo.org/grass-devel/manuals/libpython/pygrass_index.html + - PyGRASS paper: An Object Oriented Python Application Programming Interface (API) for GRASS: https://www.mdpi.com/2220-9964/2/1/201/htm ``` # i18N @@ -109,26 +119,24 @@ A GRASS python module consists of 52 gettext.install('grassmods', os.path.join(os.getenv("GISBASE"), 'locale')) ``` -* 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 - +- 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 #### 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`) -* GRASS GIS addons need to be installed globally (see `$HOME`) -* Which things are tricky - * `db.login`, `db.connect -d` - * $HOME directory might not be what you think (`/root`) - * ... - +- How to handle dependencies (for installation within actinia) + - use `requirements.txt` for python packages +- How can I log to actinia output? (with `gscript.message`) +- GRASS GIS addons need to be installed globally (see `$HOME`) +- Which things are tricky + - `db.login`, `db.connect -d` + - $HOME directory might not be what you think (`/root`) + - ... ## Steps to make the GRASS GIS addon public + open source @@ -136,14 +144,15 @@ A GRASS python module consists of #### 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) +- 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). +- Check for __sensitive information__. If included, remove them and publish without git history (or rewrite if needed). #### License + Copyright -* 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: + +- 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: ``` # COPYRIGHT: (C) 2021-2024 by mundialis GmbH & Co. KG and the GRASS Development Team @@ -158,10 +167,12 @@ A GRASS python module consists of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. ``` -* Adjust year + +- Adjust year #### Linting -* Add reusable linting workflow as described [here](https://github.com/mundialis/github-workflows?tab=readme-ov-file#python-linting) + +- Add reusable linting workflow as described [here](https://github.com/mundialis/github-workflows?tab=readme-ov-file#python-linting) ``` name: Python Flake8, black and pylint code quality check @@ -171,48 +182,52 @@ A GRASS python module consists of lint: uses: mundialis/github-workflows/.github/workflows/linting.yml@main ``` -* Run locally. Mind that there might be differences due to version mismatches. +- 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 ``` black --check --diff --line-length 79 . 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 +- 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 #### General steps Part 2 -* For GRASS GIS parameters, pay attention to label and description, so that the first word after the `:` is written in capital letters -* For GRASS GIS module description the sentence should be ended with a point. No point for parameter description or any other parameter value -* for GRASS messages like `grass.message()`, `grass.fatal()` and `grass.warning()`, use the format with underscore (in order to support [message translations](https://grasswiki.osgeo.org/wiki/GRASS_messages_translation)), e.g.: `grass.message(_("Message text"))`. - * NOTE that underscores in `grass.debug()` messages are not desired. - * NOTE that variables should not go into the macro, e.g. `grass.message(_("Created output group <%s>") % output)` instead of `grass.message(_("Created output group <%s>" % output))` -* There should be no space between the text and suspension points, e.g. `Reading raster map...` instead of `Reading raster map ...` +- For GRASS GIS parameters, pay attention to label and description, so that the first word after the `:` is written in capital letters +- For GRASS GIS module description the sentence should be ended with a point. No point for parameter description or any other parameter value +- for GRASS messages like `grass.message()`, `grass.fatal()` and `grass.warning()`, use the format with underscore (in order to support [message translations](https://grasswiki.osgeo.org/wiki/GRASS_messages_translation)), e.g.: `grass.message(_("Message text"))`. + - NOTE that underscores in `grass.debug()` messages are not desired. + - NOTE that variables should not go into the macro, e.g. `grass.message(_("Created output group <%s>") % output)` instead of `grass.message(_("Created output group <%s>" % output))` +- There should be no space between the text and suspension points, e.g. `Reading raster map...` instead of `Reading raster map ...` For more information on standardized messages see [here](https://trac.osgeo.org/grass/wiki/MessageStandardization). #### Tests -* If tests exist, the header should look like in the actual module. -* If tests exist, the test workflow should be included. [See here](https://github.com/mundialis/github-workflows?tab=readme-ov-file#grass-gis-addon-tests) for instructions. + +- If tests exist, the header should look like in the actual module. +- If tests exist, the test workflow should be included. [See here](https://github.com/mundialis/github-workflows?tab=readme-ov-file#grass-gis-addon-tests) for instructions. #### In the end -* Create a merge request in the old repository with your changes, so it can be reviewed there and moved to GitHub when cleanup is done + +- Create a merge request in the old repository with your changes, so it can be reviewed there and moved to GitHub when cleanup is done ### Steps on GitHub -* Every addon gets its own GitHub repo - * default location is at https://github.com/mundialis. If needed they can be integrated somewhere else as submodule. -* Choose "GPL3" as license -* Copy code to new repository (not via `git remote` if sensitive information are included) -* Release new addon in GitHub with 1.0.0 -* Add project label if applicable (e.g. `vale`, `hermosa-earth`, `incora`) -* Add / update README.md in project repositories which use this addon. + +- Every addon gets its own GitHub repo + - default location is at https://github.com/mundialis. If needed they can be integrated somewhere else as submodule. +- Choose "GPL3" as license +- Copy code to new repository (not via `git remote` if sensitive information are included) +- Release new addon in GitHub with 1.0.0 +- Add project label if applicable (e.g. `vale`, `hermosa-earth`, `incora`) +- Add / update README.md in project repositories which use this addon. ### Cleanup -* 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 ​