Skip to content
Open
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
23 changes: 16 additions & 7 deletions supervisor/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,19 @@ def list_of_exitcodes(arg):
except:
raise ValueError("not a valid list of exit codes: " + repr(arg))

def dict_of_key_value_pairs(arg):
""" parse KEY=val,KEY2=val2 into {'KEY':'val', 'KEY2':'val2'}
Quotes can be used to allow commas in the value
def list_of_key_value_pairs(arg):
"""Parse KEY=val,KEY2=val2 into a list of (KEY, val) tuples.

Quotes can be used to allow commas in the value. The original order is
preserved so callers can expand values incrementally.
"""
lexer = shlex.shlex(str(arg), posix=shlex_posix_works)
lexer.wordchars += '/.+-():'
lexer.wordchars += '/.+-():%'

tokens = list(lexer)
tokens_len = len(tokens)

D = {}
pairs = []
i = 0
while i < tokens_len:
k_eq_v = tokens[i:i+3]
Expand All @@ -87,9 +89,16 @@ def dict_of_key_value_pairs(arg):
if not shlex_posix_works:
v = v.strip('\'"')

D[k] = v
pairs.append((k, v))
i += 4
return D
return pairs


def dict_of_key_value_pairs(arg):
""" parse KEY=val,KEY2=val2 into {'KEY':'val', 'KEY2':'val2'}
Quotes can be used to allow commas in the value
"""
return dict(list_of_key_value_pairs(arg))

class Automatic:
pass
Expand Down
24 changes: 15 additions & 9 deletions supervisor/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from supervisor.datatypes import byte_size
from supervisor.datatypes import signal_number
from supervisor.datatypes import list_of_exitcodes
from supervisor.datatypes import dict_of_key_value_pairs
from supervisor.datatypes import list_of_key_value_pairs
from supervisor.datatypes import logfile_name
from supervisor.datatypes import list_of_strings
from supervisor.datatypes import octal_type
Expand Down Expand Up @@ -647,8 +647,8 @@ def get(opt, default, **kwargs):
section.strip_ansi = boolean(get('strip_ansi', 'false'))

environ_str = get('environment', '', do_expand=False)
environ_str = expand(environ_str, expansions, 'environment')
section.environment = dict_of_key_value_pairs(environ_str)
section.environment = expand_key_value_pairs(
environ_str, expansions, 'environment')

# extend expansions for global from [supervisord] environment definition
for k, v in section.environment.items():
Expand Down Expand Up @@ -958,12 +958,8 @@ def get(section, opt, *args, **kwargs):
expansions.update({'process_num': process_num, 'numprocs': numprocs})
expansions.update(self.environ_expansions)

environment = dict_of_key_value_pairs(
expand(environment_str, expansions, 'environment'))

# extend expansions for process from [program:x] environment definition
for k, v in environment.items():
expansions['ENV_%s' % k] = v
environment = expand_key_value_pairs(
environment_str, expansions, 'environment')

directory = get(section, 'directory', None)

Expand Down Expand Up @@ -2213,6 +2209,16 @@ def expand(s, expansions, name):
(s, name, str(ex))
)


def expand_key_value_pairs(arg, expansions, name):
"""Parse and expand KEY=val pairs in order."""
pairs = []
for k, v in list_of_key_value_pairs(arg):
v = expand(v, expansions, name)
pairs.append((k, v))
expansions['ENV_%s' % k] = v
return dict(pairs)

def make_namespec(group_name, process_name):
# we want to refer to the process by its "short name" (a process named
# process1 in the group process1 has a name "process1"). This is for
Expand Down
26 changes: 26 additions & 0 deletions supervisor/tests/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -1796,6 +1796,20 @@ def test_processes_from_section_expands_env_in_environment(self):
expected = "/foo/bar:%s" % os.environ['PATH']
self.assertEqual(pconfigs[0].environment['PATH'], expected)

def test_processes_from_section_environment_can_reuse_earlier_vars(self):
instance = self._makeOne()
text = lstrip("""\
[program:foo]
command = /bin/foo --value=%(ENV_B)s
environment = A="1",B="%(ENV_A)s"
""")
from supervisor.options import UnhosedConfigParser
config = UnhosedConfigParser()
config.read_string(text)
pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
self.assertEqual(pconfigs[0].environment, {'A': '1', 'B': '1'})
self.assertEqual(pconfigs[0].command, '/bin/foo --value=1')

def test_processes_from_section_redirect_stderr_with_filename(self):
instance = self._makeOne()
text = lstrip("""\
Expand Down Expand Up @@ -3414,6 +3428,18 @@ def test_options_environment_of_supervisord_with_escaped_chars(self):
options = instance.configroot.supervisord
self.assertEqual(options.environment, dict(VAR_WITH_P="some_value_%_end"))

def test_options_environment_of_supervisord_can_reuse_earlier_vars(self):
text = lstrip("""
[supervisord]
environment=A="1",B="%(ENV_A)s"
""")

instance = self._makeOne()
instance.configfile = StringIO(text)
instance.realize(args=[])
options = instance.configroot.supervisord
self.assertEqual(options.environment, dict(A="1", B="1"))


class ProcessConfigTests(unittest.TestCase):
def _getTargetClass(self):
Expand Down
Loading