diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9b32c14..7685d09 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,6 +9,7 @@ This document records the main changes to the sdss_access code. 3.0.4 (unreleased) ------------------ - Fix issue `52` - rsync failure when remote file is compressed compared to template +- Issue `48` - Add support for adding temporary paths for use in local sdss_access 3.0.3 (11-29-2023) ------------------ diff --git a/python/sdss_access/path/path.py b/python/sdss_access/path/path.py index dcdd562..9b951d8 100644 --- a/python/sdss_access/path/path.py +++ b/python/sdss_access/path/path.py @@ -1047,6 +1047,60 @@ def url(self, filetype, base_dir=None, sasdir='sas', **kwargs): url = re.sub(r'(/v?[0-9._]+/)', r'/tags\1', url, count=1) return url + def add_temp_path(self, name: str, path: str, envvar_path: str = None): + """ Add a temporary path template in sdss_access + + Add a path template temporarily into the local os environment + for use in sdss_access. Define a template name and path. + The path must start with an environment variable definition. + + This is useful for development of new paths before adding them to the + tree and tagging a new version. This allows sdss_access to still + be used in the interim. This is an alternative to checking out + the tree git repo and modifying paths there. The recommended way + of adding new paths is through a PR on the tree product. + + Parameters + ---------- + name : str + the temporary file species name + path : str + the temporary template directory path + envvar_path : str, optional + the definition path of the environment variable, by default None + + Raises + ------ + ValueError + when the name does not match the correct syntax + ValueError + when the path does not start with an environment variable + ValueError + when the environment variable is not defined + """ + + # check name syntax + if not re.match(r"^[a-zA-Z_0-9\-]+$", name): + raise ValueError('Name can only consist of letters, numbers, dashes or underscores.') + + # check if template path starts with an environment variable + envvar = path.split("/", 1)[0] + if not envvar.startswith("$"): + raise ValueError('Template path must start with an environment variable, $ENVVAR_NAME.') + + # check envvar is in the local environment + if envvar[1:] not in os.environ: + if not envvar_path: + raise ValueError('Template path envvar not defined in local ' + 'environment. Please specify an envvar_path.') + + # add the envvar + envvar_path = envvar_path.rstrip("/") + os.environ[envvar[1:]] = envvar_path + + # add the temporary path template + self.templates[name] = path + def _expandvars(template): ''' Recursively run os.path.expandvars diff --git a/tests/path/test_path.py b/tests/path/test_path.py index d17e7fb..de1a0c2 100644 --- a/tests/path/test_path.py +++ b/tests/path/test_path.py @@ -361,6 +361,28 @@ def test_fzcomp(self, exist, path, monkeypatch, tmp_path): else: assert pp.endswith('test.txt.fz') + @pytest.mark.parametrize('name, temp, envvar, exp', + [('testFile', '$LVM_DATA_S/test_file_{ver}.fits', None, 'sdsswork/data/lvm/lco'), + ('testFile', '$TEST_DIR/test_file_{ver}.fits', '/tmp/test/path/', 'tmp/test/path')], + ids=['withev', 'newev']) + def test_add_temp_path(self, path, name, temp, envvar, exp): + path.add_temp_path(name, temp, envvar_path=envvar) + + full = path.full(name, ver='1.0') + assert full.endswith('test_file_1.0.fits') + assert exp in full + + + @pytest.mark.parametrize('name, temp, msg', + [("testFile!", "/path/to/testfile.fits", 'Name can only consist of letters, numbers, dashes'), + ("testFile", "/path/to/testfile.fits", 'Template path must start with an environment variable'), + ("testFile", "$TEST_DI/testfile.fits", 'Template path envvar not defined in local environment')], + ids=['badname', 'badpath', 'noenvvar']) + def test_add_temp_fails(self, path, name, temp, msg): + with pytest.raises(ValueError, match=msg): + path.add_temp_path(name, temp) + + @pytest.fixture() def monkeyoos(monkeypatch, mocker): ''' monkeypatch the original os environ from tree '''