Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Environment Variable Expansion in settings.ini #225

Merged
merged 3 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions src/fprime/fbuild/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,37 @@
from typing import Any, Callable, Dict, List, Union


class EnvironmentVariableInterpolation(configparser.BasicInterpolation):
"""Interpolation for environment variables embedded in setting.ini

settings.ini will extrapolate $/${} as an environment variable. This will allow basic environment variable
replacement. It is illegal to supply an environment variable with a $ or %( as part of the value because that might
trigger unanticipated recursive parsing.

Note: environment variable substitution is only performed in the environment section and nowhere else.

Based off: https://stackoverflow.com/questions/26586801/configparser-and-string-interpolation-with-env-variable
"""

def before_get(self, parser, section, option, value, defaults):
"""Pre-process sections to replace environment variables

Runs before the value is gotten to replace environment variables. It will use os.path.expandvars to do the
actual substitution on any value in the "environment" section. Other pre-processing is done first.

Args:
parser: unused
section: environment substitution will be done only when set to "environment"
option: unused
value: will be searched for and replace environment variables
defaults: unused
Returns:
the value post substitution
"""
value = super().before_get(parser, section, option, value, defaults)
return os.path.expandvars(value) if section == "environment" else value


class SettingType(Enum):
"""Designates the type of the setting"""

Expand Down Expand Up @@ -171,7 +202,9 @@ def load(settings_file: Path, platform: str = "native", is_ut: bool = False):
# Setup a config parser, or none if the settings file does not exist
confparse = None
if settings_file.exists():
confparse = configparser.ConfigParser()
confparse = configparser.ConfigParser(
interpolation=EnvironmentVariableInterpolation()
)
confparse.read(settings_file)
else:
print(f"[WARNING] {settings_file} does not exist", file=sys.stderr)
Expand Down Expand Up @@ -235,7 +268,9 @@ def load_environment(env_file):
:param env_file: load environment from this file
:return: environment dictionary
"""
parser = configparser.ConfigParser()
parser = configparser.ConfigParser(
interpolation=EnvironmentVariableInterpolation()
)
parser.optionxform = str
parser.read(env_file)
env_dict = {}
Expand Down
6 changes: 6 additions & 0 deletions test/fprime/fbuild/settings-data/settings-environment.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[fprime]
framework_path: ../..

[environment]
MY_VARIABLE: my value
MY_VARIABLE_2: ${TEST_SETTING_1}:$TEST_SETTING_2
24 changes: 22 additions & 2 deletions test/fprime/fbuild/test_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Tests the F prime settings module.
@author joshuaa
"""

import os
from pathlib import Path

from fprime.fbuild.settings import IniSettings
Expand Down Expand Up @@ -126,8 +126,28 @@ def test_settings():
"default_cmake_options": "OPTION1=ABC\nOPTION2=123\nOPTION3=Something",
},
},
{
"file": "settings-environment.ini",
"expected": {
"settings_file": full_path("settings-data/settings-environment.ini"),
"default_toolchain": "native",
"default_ut_toolchain": "native",
"framework_path": full_path(".."),
"install_destination": full_path("settings-data/build-artifacts"),
"library_locations": [],
"environment_file": full_path("settings-data/settings-environment.ini"),
"environment": {"MY_VARIABLE": "my value", "MY_VARIABLE_2": "abc:123"},
"component_cookiecutter": "default",
"deployment_cookiecutter": "default",
"project_root": full_path(".."),
"config_directory": full_path("..") / "config",
"default_cmake_options": "",
},
},
]

# Prep for substitution
os.environ["TEST_SETTING_1"] = "abc"
os.environ["TEST_SETTING_2"] = "123"
for case in test_cases:
fp = full_path("settings-data/" + case["file"])
results = IniSettings.load(fp)
Expand Down
Loading