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 expansion feature #37

Merged
merged 10 commits into from
Dec 3, 2021
21 changes: 21 additions & 0 deletions pystarport/cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
import tomlkit
import yaml
from dateutil.parser import isoparse
from dotenv import dotenv_values, load_dotenv
from supervisor import xmlrpc
from supervisor.compat import xmlrpclib

from . import ports
from .app import CHAIN, IMAGE, SUPERVISOR_CONFIG_FILE
from .cosmoscli import ChainCommand, CosmosCLI, ModuleAccount, module_address
from .expansion import expand_posix_vars
from .ledger import ZEMU_BUTTON_PORT, ZEMU_HOST
from .utils import format_doc_string, interact, write_ini

Expand Down Expand Up @@ -906,6 +908,20 @@ def init_cluster(
):
config = yaml.safe_load(open(config_path))

if "dotenv" in config:
dotenv = config.pop("dotenv", {})
if not isinstance(dotenv, str):
raise ValueError(f"Invalid value passed to dotenv: {dotenv}")
config_vars = load_system_envvars()
env_path = Path(config_path).parent.joinpath(dotenv)
yihuang marked this conversation as resolved.
Show resolved Hide resolved
if not env_path.is_file():
raise ValueError(
f"Dotenv specified in config but not found at path: {env_path}"
)
config_vars.update(dotenv_values(dotenv_path=env_path)) # type: ignore
load_dotenv(dotenv_path=env_path)
config = expand_posix_vars(config, config_vars)

relayer_config = config.pop("relayer", {})
for chain_id, cfg in config.items():
cfg["path"] = str(config_path)
Expand Down Expand Up @@ -1101,3 +1117,8 @@ def format_value(v, ctx):
supervisord.wait()
t.stop()
t.join()


def load_system_envvars():
config_vars = dict(os.environ)
return config_vars
58 changes: 58 additions & 0 deletions pystarport/expansion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import re
from typing import Any, Mapping, Optional, Text

from dotenv.variables import parse_variables


def expand_posix_vars(obj: Any, variables: Mapping[Text, Optional[Any]]) -> Any:
"""expand_posix_vars recursively expands POSIX values in an object.

Args:
obj (any): object in which to interpolate variables.
variables (dict): dictionary that maps variable names to their value
"""
if isinstance(obj, (dict,)):
for key, val in obj.items():
obj[key] = expand_posix_vars(val, variables)
elif isinstance(obj, (list,)):
for index in range(len(obj)):
obj[index] = expand_posix_vars(obj[index], variables)
elif isinstance(obj, (str,)):
obj = _str_to_python_value(_expand(obj, variables))
yihuang marked this conversation as resolved.
Show resolved Hide resolved
return obj


def _expand(value, variables={}):
yihuang marked this conversation as resolved.
Show resolved Hide resolved
"""_expand does POSIX-style variable expansion

This is adapted from python-dotenv, specifically here:

https://github.com/theskumar/python-dotenv/commit/17dba65244c1d4d10f591fe37c924bd2c6fd1cfc

We need this layer here so we can explicitly pass in variables;
python-dotenv assumes you want to use os.environ.
"""

if not isinstance(value, (str,)):
return value
atoms = parse_variables(value)
return "".join([str(atom.resolve(variables)) for atom in atoms])


INT_REGEX = re.compile(r"^[-+]?[0-9]+$")


def _str_to_python_value(val):
"""_str_to_python_value infers the data type from a string.

This could eventually use PyYAML's parsing logic.
"""
if not isinstance(val, (str,)):
return val
elif val == "true" or val == "True" or val == "on":
return True
elif val == "false" or val == "False" or val == "off":
return False
# elif INT_REGEX.match(val):
# return int(val)
return val