From c61e7de1227d329c1eaa46c6b5966bbc76284ed7 Mon Sep 17 00:00:00 2001 From: Alon Grinberg Dana Date: Fri, 14 Aug 2026 05:56:48 +0300 Subject: [PATCH] Use a paired RMG-database worktree when one exists With several RMG-Py worktrees checked out at once, every one of them defaulted to the same ../RMG-database/input, so they all read whichever branch that single shared checkout happened to be on -- silently, and with nothing in the settings report to say so. Now a worktree named RMG-Py- will use RMG-database- beside it, if that directory exists. Everything else is unchanged: no pair on disk, or a plain RMG-Py checkout name, and the old default applies. An explicit rmgrc still wins over both. --- rmgpy/__init__.py | 36 ++++++++++++++++- test/rmgpy/settingsTest.py | 81 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 test/rmgpy/settingsTest.py diff --git a/rmgpy/__init__.py b/rmgpy/__init__.py index 5e58c69b55..43f6dbc6ea 100644 --- a/rmgpy/__init__.py +++ b/rmgpy/__init__.py @@ -139,14 +139,46 @@ def load(self, path=None): self["test_data.directory"] = value self.sources["test_data.directory"] = "from {0}".format(self.filename) + @staticmethod + def default_database_directory(rmg_py_dir): + """ + Return the default database directory for the RMG-Py checkout at + `rmg_py_dir`, along with a string saying where it came from. + + If the checkout is a worktree named ``RMG-Py-`` and a matching + ``RMG-database-`` sits beside it, that paired database is used. + This lets several RMG-Py worktrees each use their own database + worktree, rather than all of them sharing the single ``RMG-database`` + next door. + + Otherwise the historical default is returned unchanged: the + ``RMG-database`` directory alongside the RMG-Py checkout. + """ + parent_dir, checkout_name = os.path.split(rmg_py_dir) + prefix = "RMG-Py-" + if checkout_name.startswith(prefix): + paired = os.path.realpath( + os.path.join(parent_dir, "RMG-database-" + checkout_name[len(prefix):], "input") + ) + if os.path.isdir(paired): + return paired, "Default, paired RMG-database worktree beside this RMG-Py worktree" + + return ( + os.path.realpath(os.path.join(rmg_py_dir, "..", "RMG-database", "input")), + "Default, relative to RMG-Py source code", + ) + def reset(self): """ Reset all settings to their default values. """ self.filename = None rmgpy_module_dir = os.path.abspath(os.path.dirname(__file__)) - self["database.directory"] = os.path.realpath(os.path.join(rmgpy_module_dir, "..", "..", "RMG-database", "input")) - self.sources["database.directory"] = "Default, relative to RMG-Py source code" + database_dir, database_source = self.default_database_directory( + os.path.abspath(os.path.join(rmgpy_module_dir, "..")) + ) + self["database.directory"] = database_dir + self.sources["database.directory"] = database_source self["test_data.directory"] = os.path.realpath(os.path.join(rmgpy_module_dir, "..", "test", "rmgpy", "test_data")) self.sources["test_data.directory"] = "Default, relative to RMG-Py source code" diff --git a/test/rmgpy/settingsTest.py b/test/rmgpy/settingsTest.py new file mode 100644 index 0000000000..d2fae5f08f --- /dev/null +++ b/test/rmgpy/settingsTest.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 + +############################################################################### +# # +# RMG - Reaction Mechanism Generator # +# # +# Copyright (c) 2002-2026 Prof. William H. Green (whgreen@mit.edu), # +# Prof. Richard H. West (r.west@neu.edu) and the RMG Team (rmg_dev@mit.edu) # +# # +# Permission is hereby granted, free of charge, to any person obtaining a # +# copy of this software and associated documentation files (the 'Software'), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, sublicense, # +# and/or sell copies of the Software, and to permit persons to whom the # +# Software is furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # +# DEALINGS IN THE SOFTWARE. # +# # +############################################################################### + +""" +This script contains unit tests of the :mod:`rmgpy` settings object. +""" + +import os + +from rmgpy import Settings + + +class TestDefaultDatabaseDirectory: + """ + Tests for how the default database directory is chosen when no rmgrc + supplies one. + """ + + def test_paired_database_worktree_is_used(self, tmp_path): + """ + A checkout named RMG-Py- uses RMG-database- beside it. + """ + (tmp_path / "RMG-Py-feature").mkdir() + (tmp_path / "RMG-database-feature" / "input").mkdir(parents=True) + + directory, source = Settings.default_database_directory(str(tmp_path / "RMG-Py-feature")) + + assert directory == os.path.realpath(str(tmp_path / "RMG-database-feature" / "input")) + assert "paired" in source + + def test_falls_back_when_the_paired_worktree_is_absent(self, tmp_path): + """ + The suffix alone is not enough: with no RMG-database- on disk, + the historical default is used, so an unpaired worktree is unaffected. + """ + (tmp_path / "RMG-Py-feature").mkdir() + (tmp_path / "RMG-database" / "input").mkdir(parents=True) + + directory, source = Settings.default_database_directory(str(tmp_path / "RMG-Py-feature")) + + assert directory == os.path.realpath(str(tmp_path / "RMG-database" / "input")) + assert source == "Default, relative to RMG-Py source code" + + def test_plain_checkout_name_is_unaffected(self, tmp_path): + """ + An ordinary RMG-Py checkout keeps the behaviour it has always had, + even when a similarly named database directory happens to be present. + """ + (tmp_path / "RMG-Py").mkdir() + (tmp_path / "RMG-database" / "input").mkdir(parents=True) + + directory, source = Settings.default_database_directory(str(tmp_path / "RMG-Py")) + + assert directory == os.path.realpath(str(tmp_path / "RMG-database" / "input")) + assert source == "Default, relative to RMG-Py source code"