Repository navigation
tests: Cover the current grass.lib loading behavior - #8093
Merged
Merged
Conversation
Pin the current behavior of grass.lib in a session set up by init in an environment without the dynamic library search path variable: the modules can be imported after init and the libraries see the session (except on Windows, where the C libraries, built with a different C runtime than Python, do not see variables set in the running process). Also pin that a session does not load the C libraries into the process, so sessions using only tools never pay for the GRASS C stack and its dependencies. The tests construct the environment of their subprocess themselves, so they are independent of how the test process was started. The subprocess fixtures are in a new python/conftest.py, shared with the grass.app tests, and not in python/grass/conftest.py: python/grass is a package, so pytest would import a conftest.py there as grass.conftest and with it the grass package from the source tree instead of the installed one. Standalone runs with only the test files as arguments still pick up the shared fixtures.
Pin the manual setup used by scripts, test instructions, and container images: with the platform's dynamic library search path variable set before the process starts, grass.lib works without any session and in any layout, including an installation moved away from the prefix it was configured with. The test takes the variable name from the runtime (RuntimePaths) and sets DYLD_LIBRARY_PATH additionally on macOS, where the variable known to GRASS (LD_RUN_PATH) is read by the ctypes loader but ignored by the dynamic linker.
Run the new tests in steps which do not export the library search path variable, unlike the other pytest steps, so that the steps pin what works without that setup. On Linux and macOS, run the manual-setup test also against an installation moved away from the prefix it was configured with, where the search path recorded in the libraries at build time cannot help; only the test which provides the variable itself can pass there today. On Windows, the library directory is deliberately not added to PATH in the new step.
The docstring claimed since 2015 that the libraries used through ctypes are not usable after init. They are, for an installation which is at the prefix it was configured for: the libraries find each other through the search path recorded at build time, and the ctypes loader reads the variables set by init from the environment of the running process. The remaining limitation is an installation moved away from its prefix, where the variable must be set before the process starts.
Make the clean subprocess environment free of all GRASS library search paths: also remove LD_RUN_PATH (the variable GRASS uses on macOS) and, on Windows, the PATH directories containing a GRASS core library, so that running the tests from a GRASS session shell does not make them pass for the wrong reason. Make the Windows xfail of the usable-after-init test strict, so the test reports when the C libraries start seeing the session on Windows instead of passing silently.
The fixture removes the dynamic library search paths in general, not paths specific to GRASS (only on Windows it removes just the PATH directories with the GRASS libraries, as the comment there says).
The C libraries read the session from the environment of the process, so a session set up with a custom env is not visible to grass.lib. This is the current behavior, so document it together with the rest of the corrected init docstring.
…tring State first that both importing grass.lib and using it depend on init, and then when the import works, without describing how grass.lib finds its libraries.
Add the copyright lines required by the style guide to the files added in this series: the author and the GRASS Development Team before the license identifier.
The manual-setup test prepended the library directory to the variable GRASS knows but replaced DYLD_LIBRARY_PATH on macOS. Prepend to both, so the two are handled the same way and an existing value is kept.
… on Windows On Windows, the C libraries do not see environment variables set in the running process (the strict xfail of the usable-after-init test), so a session set up by init is invisible to grass.lib calls there. Say so in a separate paragraph of the init docstring, which stays the same while the import behavior described above it changes.
Say that grass.lib functions see a session set up by init only in processes started afterwards, such as subprocesses, instead of the ending about a session set up before the process starts.
A process started after init has the session in its environment from its start, so the C libraries see it on all platforms, including Windows, where they do not see a session set up in their own process. This is the subprocess that the Windows skip in lib/gis/tests/ lib_gis_env_test.py refers to.
petrasovaa
approved these changes
Oct 27, 2026
This was referenced Oct 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This adds tests which pin the current behavior of making the GRASS C libraries (grass.lib) available, without changing any behavior. grass.lib works with a session set up by init even in an environment without the dynamic library search path variable. A session does not load the C libraries into the process, so sessions using only tools never pay for the GRASS C stack. Setting the platform's search path variable before the process starts makes grass.lib work without a session in any layout, including an installation moved away from the prefix it was configured with.
The init function docstring is corrected accordingly. It claimed since 2015 that ctypes-based libraries are not usable after init, and now says that both importing and using grass.lib depend on init and when the import works. The whole situation is complex with things working differently on different platforms even across Linux distros.
The new CI steps run these tests as standalone pytests on all platforms and, unlike the other pytest steps, do not export the search path variable or add the library directory to PATH, so the tests see only the environment they construct themselves. I have two other PRs in prep which will modify the behavior and these tests.
Details (AI generated)
Two mechanisms behind the import
Importing a grass.lib module in a running process needs two things from two different mechanisms.
For an installation at its prefix, init provides the first and the build-time search path the second, so init looks sufficient. For a moved installation, init still provides the first, but nothing in the running process can provide the second. Setting the variable before the process starts provides both, which is why that setup, used by CI and the test instructions, works in every layout and hid the difference.
What is pinned and why it holds
On macOS, the variable GRASS knows (LD_RUN_PATH) is read by the ctypes loader but ignored by dyld, so the manual-setup test additionally sets DYLD_LIBRARY_PATH, documenting in executable form what the real manual escape hatch per platform is.
Why the new CI steps do not export the variable
Every existing pytest step exports LD_LIBRARY_PATH (or adds the library directory to PATH on Windows) before running, and the documented test setup instructs developers to do the same. That makes grass.lib always work in front of developers and in CI, which also means the layouts in which it fails never show up there. The new steps keep the environment clean and let the tests state their own requirements, so later changes to the loading have an honest baseline to diff against. The relocated-installation steps (Linux, macOS) run only the manual-setup test for now, because nothing else can pass there today. The clean-environment fixture removes the dynamic library search path variables and, on Windows, the PATH directories containing a GRASS core library, so running the tests from a GRASS session shell does not make them pass for the wrong reason.
Test placement
Session behavior is tested in python/grass/script/tests (init lives in grass.script.setup), plain loading without a session in python/grass/app/tests (grass.app.runtime is the home of the runtime setup machinery). Test module basenames follow the unit-name convention.
The subprocess fixtures are shared through a new python/conftest.py rather than duplicated in both test directories. The obvious place, python/grass/conftest.py, is not usable: python/grass is a package, so pytest imports a conftest.py there as grass.conftest, which imports the grass package from the source tree and shadows the installed one for the whole test process (verified:
grass.__file__then points to the source tree and grass.app.resource_paths holds the unconfigured template values). The python directory is not a package, so the conftest there is imported on its own and the installed grass package stays in use. pytest collects conftest files from the directory of each test file up to the root directory, so the standalone runs with only the two test files as arguments still get the fixtures, from the repository root as well as from any other working directory with absolute paths. Moving the tests themselves to a common directory is not needed for that.Suggested squash-merge commit message
The changes and this description were prepared with help of Claude Code (Claude Fable 5 and Claude Opus 5.5) and reviewed by me.