Skip to content

Commit 4e2d9ea

Browse files
committed
Parse environment= using shlex in posix mode if possible
1 parent bb1a276 commit 4e2d9ea

File tree

4 files changed

+46
-2
lines changed

4 files changed

+46
-2
lines changed

CHANGES.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
4.4.0.dev0 (Next Release)
22
-------------------------
33

4+
- Parsing ``environment=`` in the config file now uses ``shlex`` in POSIX
5+
mode instead of legacy mode to allow for escaped quotes in the values.
6+
However, on Python 2 before 2.7.13 and Python 3 before 3.5.3, POSIX mode
7+
can't be used because of a `bug <https://bugs.python.org/issue21999>`_
8+
in ``shlex``. If ``supervisord`` is run on a Python version with the bug,
9+
it will fall back to legacy mode. Patch by Stefan Friesel.
10+
411
- ``supervisorctl`` now reads extra files included via the ``[include]``
512
section in ``supervisord.conf`` like ``supervisord`` does. This allows
613
the ``[supervisorctl]`` section or ``[ctlplugin:x]`` sections to be in

supervisor/compat.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,26 @@ def is_text_stream(stream):
150150
except ImportError: # pragma: no cover
151151
from HTMLParser import HTMLParser
152152

153+
# Begin check for working shlex posix mode
154+
155+
# https://github.com/Supervisor/supervisor/issues/328
156+
# https://github.com/Supervisor/supervisor/issues/873
157+
# https://bugs.python.org/issue21999
158+
159+
from shlex import shlex as _shlex
160+
161+
_shlex_posix_expectations = {
162+
'foo="",bar=a': ['foo', '=', '', ',', 'bar', '=', 'a'],
163+
"'')abc": ['', ')', 'abc']
164+
}
165+
166+
shlex_posix_works = all(
167+
list(_shlex(_input, posix=True)) == _expected
168+
for _input, _expected in _shlex_posix_expectations.items()
169+
)
170+
171+
# End check for working shlex posix mode
172+
153173
# Begin importlib/setuptools compatibility code
154174

155175
# Supervisor used pkg_resources (a part of setuptools) to load package

supervisor/datatypes.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import socket
66
import shlex
77

8+
from supervisor.compat import shlex_posix_works
89
from supervisor.compat import urlparse
910
from supervisor.compat import long
1011
from supervisor.loggers import getLevelNumByDescription
@@ -68,7 +69,7 @@ def dict_of_key_value_pairs(arg):
6869
""" parse KEY=val,KEY2=val2 into {'KEY':'val', 'KEY2':'val2'}
6970
Quotes can be used to allow commas in the value
7071
"""
71-
lexer = shlex.shlex(str(arg))
72+
lexer = shlex.shlex(str(arg), posix=shlex_posix_works)
7273
lexer.wordchars += '/.+-():'
7374

7475
tokens = list(lexer)
@@ -81,7 +82,12 @@ def dict_of_key_value_pairs(arg):
8182
if len(k_eq_v) != 3 or k_eq_v[1] != '=':
8283
raise ValueError(
8384
"Unexpected end of key/value pairs in value '%s'" % arg)
84-
D[k_eq_v[0]] = k_eq_v[2].strip('\'"')
85+
86+
k, v = k_eq_v[0], k_eq_v[2]
87+
if not shlex_posix_works:
88+
v = v.strip('\'"')
89+
90+
D[k] = v
8591
i += 4
8692
return D
8793

supervisor/tests/test_datatypes.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from supervisor.tests.base import Mock, patch, sentinel
1010
from supervisor.compat import maxint
11+
from supervisor.compat import shlex_posix_works
1112

1213
from supervisor import datatypes
1314

@@ -164,6 +165,16 @@ def test_handles_newlines_inside_quotes(self):
164165
expected = {'foo': 'a\nb\nc'}
165166
self.assertEqual(actual, expected)
166167

168+
def test_handles_quotes_inside_quotes(self):
169+
func = lambda: datatypes.dict_of_key_value_pairs('foo="\'\\""')
170+
171+
if shlex_posix_works:
172+
actual = func()
173+
expected = {'foo': '\'"'}
174+
self.assertEqual(actual, expected)
175+
else:
176+
self.assertRaises(ValueError, func)
177+
167178
def test_handles_empty_inside_quotes(self):
168179
actual = datatypes.dict_of_key_value_pairs('foo=""')
169180
expected = {'foo': ''}

0 commit comments

Comments
 (0)