grass.app.runtime: Do not add the same paths again on repeated setup - #7840
Pranav-error wants to merge 2 commits into
Conversation
nilason
left a comment
There was a problem hiding this comment.
Looks good to me, I have only a minor suggestion. I'll leave the final approval and merge to a colleague for another pair of eyes on this.
| # Assigning rather than appending to an empty value avoids a leading | ||
| # separator, which would be an empty entry meaning the current directory. |
There was a problem hiding this comment.
| # Assigning rather than appending to an empty value avoids a leading | |
| # separator, which would be an empty entry meaning the current directory. |
Not worth the comment in code, but please add to commit description.
No need to explain every line of code, I think.
set_executable_paths(), set_dynamic_library_path() and set_python_path_variable() appended unconditionally, so calling them more than once on the same environment grew PATH, LD_LIBRARY_PATH and PYTHONPATH with duplicate entries. Each now checks whether the path is already present and returns early if it is. Paths from a different installation are still added in front of the existing ones. In set_dynamic_library_path() the value is assigned rather than appended when the variable is empty. Appending to an empty value would leave a leading separator, and an empty entry in a path list means the current directory. Signed-off-by: sai pranav <rajasaipranav0@gmail.com>
49471da to
c2dbc6b
Compare
|
Done — comment removed and the explanation moved into the commit message:
Also rebased onto current Fair point on not explaining every line. That one was load-bearing enough that losing it would invite someone to "simplify" the assignment back into an append, which is why I had written it down — but the commit message is the better place for it, since that is where someone doing the simplifying would look. |
| if library_path in existing.split(os.pathsep): | ||
| return | ||
| env[variable_name] = ( | ||
| existing + os.pathsep + library_path if existing else library_path |
There was a problem hiding this comment.
That reads weird to me. Is there missing parentheses for me to understand well?
I read it like this:
existing + os.pathsep + (library_path if existing else library_path)Which is a useless if.
If it resolves to
(existing + os.pathsep + library_path) if existing else library_pathThen it makes a bit more sense
| def set_executable_paths(install_path, grass_config_dir, env): | ||
| """Add paths with executables to PATH in _env_""" | ||
| """Add paths with executables to PATH in _env_ | ||
|
|
||
| Paths already present are not added again, so calling this repeatedly on | ||
| the same environment does not grow the variable. Paths from another | ||
| installation are still added in front of the existing ones. | ||
| """ | ||
| paths = collections.deque() | ||
| # Addons | ||
| append_left_addon_paths(paths, grass_config_dir, env=env) | ||
| # Standard installation | ||
| append_left_main_executable_paths(paths, install_path=install_path) | ||
|
|
||
| paths.append(env.get("PATH")) | ||
| existing = env.get("PATH") or "" | ||
| present = existing.split(os.pathsep) if existing else [] | ||
| paths = [path for path in paths if path not in present] | ||
| if not paths: | ||
| return | ||
| if existing: | ||
| paths.append(existing) |
There was a problem hiding this comment.
Is the deduplication keeping the paths in the same order, keeping the left-most instance? And is it only affecting what we are adding, not pre-existing paths set by the user or software outside of us?
My comment might mean contradictory instructions though. But not keeping a path in front because it it found later on in the list might not be equivalent, if a binary of the same name exists in one of these other locations
|
Both are worth answering precisely rather than from memory, so I went back to the code for each. Line 357 ( env[variable_name] = (
existing + os.pathsep + library_path if existing else library_path
)parses as Line 291 ( existing = env.get("PATH") or ""
present = existing.split(os.pathsep) if existing else []
paths = [path for path in paths if path not in present]Yes to both parts of your question. And it's scoped to only what's being added: Will push the explicit parens from the first point as a follow-up commit. |
…ry_path explicit Precedence was already correct (a bare conditional expression binds looser than +, so the whole preceding + chain is the true-branch), but it read as ambiguous. Add explicit parens so it doesn't need a precedence check to read correctly. Requested by echoix on OSGeo#7840.
Fixes #7738.
set_executable_paths(),set_dynamic_library_path()andset_python_path_variable()added their entries unconditionally, so calling the setup repeatedly on one environment grewPATH,PYTHONPATHand the library path variable without bound. That happens in a pytest run and in a Jupyter kernel, wheregs.setup.init()is called many times onos.environ.All three now skip an entry that is already present, which is the fix suggested in the issue. First call behaviour is unchanged, and paths from another installation are still added in front of the existing ones, so switching installations keeps working. I did not route
init()throughensure_runtime_env(), sinceruntime_env_is_active()only checks that GISBASE appears inPATHand would skip a re-setup that is actually needed.set_dynamic_library_path()also assigned""before appending when the variable was unset, leaving a leading separator. An empty entry in a library path means the current directory, so it now assigns the path directly in that case.Tests
Seven tests in
python/grass/app/tests/grass_app_runtime_test.py, covering, for each setter, that a second call changes nothing, and that an existing value is preserved. Two more cover the empty-entry case and that a second installation's paths still come first.Checked against the unfixed module first: the four idempotency and empty-entry tests fail there and pass with the change. The 41 existing tests in that file still pass.
I used an AI assistant while preparing this. I understand the change and can explain it.