[GRASS-git] [OSGeo/grass] 45c1e7: grass.jupyter: Return session from init (#1834)

Vaclav Petras noreply at github.com
Tue Jan 25 09:32:31 PST 2022


  Branch: refs/heads/main
  Home:   https://github.com/OSGeo/grass
  Commit: 45c1e7f47abe9cbf6103168dbdcb73b228f5a801
      https://github.com/OSGeo/grass/commit/45c1e7f47abe9cbf6103168dbdcb73b228f5a801
  Author: Vaclav Petras <wenzeslaus at gmail.com>
  Date:   2022-01-25 (Tue, 25 Jan 2022)

  Changed paths:
    M doc/notebooks/basic_example_grass_jupyter.ipynb
    M doc/notebooks/grass_jupyter.ipynb
    M doc/notebooks/hydrology.ipynb
    M doc/notebooks/solar_potential.ipynb
    M doc/notebooks/viewshed_analysis.ipynb
    M python/grass/jupyter/setup.py

  Log Message:
  -----------
  grass.jupyter: Return session from init (#1834)

Calling grass.script.setup.init needs to paired with a call to finish. However, given the iterative nature of working in notebook, an explicit call to finish would have to be commented out in most notebooks with notes about nothing working once this is called, but with an explanation of the need to call it at some point (and that would be confusing).

Several common solution don't work well with notebooks. Context manager would have to be limited to one call which is exactly what we don't want for a session which should be available in the whole notebook. atexit does not or may not work in Jupyter (IPython kernel). It didn't work not for me locally and the Internet is full of various notes about not working in certain cases or versions.

Therefore, the new session object returned from grass.jupyter.setup.init uses the weakref.finalize technique (aka finalizer) to call the finish procedure. This technique is used, e.g., in the Python standard library temporary directory object. The finalize technique guarantees that the "cleanup" method will be called (unlike the `__del__` method is which is not guaranteed to be called). This works together with keeping reference to one instance of global session in the module (keeping the session reference by the user is optional). The finish function is called at kernel end or restart unless called manually before that (multiple finish (finalizer) calls are okay and ignored).

The documentation does not discuss the finalizer and garbage collection issues in detail, because it works in a expected and straightforward way, although the reasons for it are complex.

The new class represents a global GRASS session and the init function keeps a module-level private global reference to one session only. The session class is used as a singleton, although not strictly (new objects are sometimes created) and without any enforcement (nothing in the code prevents from creating more objects). However, the init function, documentation, and the class name starting with underscore supports the right use of the class.

The session object is always created only by the init function. If the session object does not exist yet, it is created. If the session was finished (not active), a new session object is created. If the session exists and is active, the init function switches the mapset.

In other words, the init function can be called more than once and the user doesn't need to keep the returned reference. However, the returned reference to the session object can be used to handle the session. The same could be done with standalone functions, but this may be good for consistency with (future version of) the grass.script.setup.init function which needs a similar session object to provide context manager functionality.

The session object takes care of switching the mapset although there is nothing in the object needed to do that since the state is global anyway. The session object can be also used to finish (terminate) the session explicitly (mapset part only, runtime stays the same). User needs can hold result of init, e.g., `session = init(...)`, but does not have to. Notebooks were updated to use `session = init(...)` but the session is mostly not used further.

Not assigning the reference to a variable results in printing of `<grass.jupyter.setup._JupyterGlobalSession at ...>` after the cell because the call to init is usually the last item in the cell. This is not pretty, but it is common enough not to create confusion. Notebooks can always assign to variable to avoid that (although the variable is then unused).

The `switch_mapset` method of the session object takes the same parameters as init, i.e., can do the mapset path versus database path, location name, mapset name resolution. Additionally, it also accepts just mapset name to change mapset within the current location. It uses g.gisenv (and not g.mapset) to switch mapset because grass.script.setup.init doesn't lock the mapset (while g.mapset, so that would create inconsistency).




More information about the grass-commit mailing list