Skip to content

Commit

Permalink
Merge pull request #25 from mundialis/add_test_memory_func
Browse files Browse the repository at this point in the history
added test_memory()
  • Loading branch information
JHalbauer authored Mar 20, 2024
2 parents 0f1cbfa + 5d90764 commit 8128d05
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 42 deletions.
79 changes: 38 additions & 41 deletions How-to-create-a-GRASS-GIS-addon.md
Original file line number Diff line number Diff line change
Expand Up @@ -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... and refactor!
#### Reuse & Recycle:

- it helps if GRASS GIS source code is there to look at
- best to look at existing GRASS addons
Expand All @@ -43,72 +43,68 @@ 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 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
- 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
- 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)
2. header (author, purpose, license)
3. `# % ` comments are important (ignored by python but important for parser)

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

```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`

###### `# % rules`
1. `def main():` reads in all variables (`options['first']`)

- define dependencies between options, required options and more. See official [docs](https://grass.osgeo.org/grass84/manuals/g.parser.html#conditional-parameters)
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
1. indefinite additional functions are possible

1. include parser at the end before calling main function:

```
if __name__ == "__main__":
options, flags = grass.parser()
options, flags = gscript.parser()
main()
```

or optionally clean temporary stuff in 'cleanup' function and call it on exit

```python
```
if __name__ == "__main__":
options, flags = grass.parser()
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/])
- [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)
Expand All @@ -125,24 +121,18 @@ A GRASS python module consists of
```

- for this to work use message standardisation (look at locale)
### how to name it
- 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

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 `grass.message`)
- 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`
Expand All @@ -155,15 +145,20 @@ 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

- 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:

```shell
# COPYRIGHT: (C) 2021-2024 by mundialis GmbH & Co. KG and the GRASS Development Team
#
Expand All @@ -183,8 +178,7 @@ 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]
Expand All @@ -193,7 +187,6 @@ 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

Expand All @@ -202,7 +195,6 @@ 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
Expand Down Expand Up @@ -243,3 +235,8 @@ For more information on standardized messages see [here](https://trac.osgeo.org/
- 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
24 changes: 23 additions & 1 deletion src/grass_gis_helpers/general.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def log_memory(grassenv=None):
)


def free_ram(unit, percent=100):
def get_free_ram(unit, percent=100):
"""The function gives the amount of the percentages of the available
RAM memory and free swap space.
Args:
Expand Down Expand Up @@ -111,3 +111,25 @@ def free_ram(unit, percent=100):
return int(round(memory_gb_percent))
else:
grass.fatal(f"Memory unit {unit} not supported")


def test_memory(memory_string):
"""Test if desired memory is available. In case RAM is smaller than
desired memory, use free RAM instead of desired memory value.
Args:
memory_string(string): string from standard memory input option
Returns:
free_ram(int): free RAM to use
memory(int): available memory to use
"""
# check memory
memory = int(memory_string)
free_ram = get_free_ram("MB", 100)
if free_ram < memory:
grass.warning(
_(f"Using {memory} MB but only {free_ram} MB RAM available.")
)
grass.warning(_(f"Set used memory to {free_ram} MB."))
return free_ram
else:
return memory

0 comments on commit 8128d05

Please sign in to comment.