parent
1451b4261f
commit
917dfed842
|
@ -67,11 +67,82 @@ Powerline script has a number of options controlling powerline behavior. Here
|
|||
performed by powerline script itself, but ``-p ~/.powerline`` will likely be
|
||||
expanded by the shell to something like ``-p /home/user/.powerline``.
|
||||
|
||||
Environment variables overrides
|
||||
===============================
|
||||
|
||||
All bindings that use ``POWERLINE_COMMAND`` environment variable support taking
|
||||
overrides from environment variables. In this case overrides should look like
|
||||
the following::
|
||||
|
||||
OVERRIDE='key1.key2.key3=value;key4.key5={"value":1};key6=true;key1.key7=10'
|
||||
|
||||
. This will be parsed into
|
||||
|
||||
.. code-block:: Python
|
||||
|
||||
{
|
||||
"key1": {
|
||||
"key2": {
|
||||
"key3": "value"
|
||||
},
|
||||
"key7": 10,
|
||||
},
|
||||
"key4": {
|
||||
"key5": {
|
||||
"value": 1,
|
||||
},
|
||||
},
|
||||
"key6": True,
|
||||
}
|
||||
|
||||
. Rules:
|
||||
|
||||
#. Environment variable must form a semicolon-separated list of key-value pairs:
|
||||
``key=value;key2=value2``.
|
||||
#. Keys are always dot-separated strings that must not contain equals sign (as
|
||||
well as semicolon) or start with an underscore. They are interpreted
|
||||
literally and create a nested set of dictionaries: ``k1.k2.k3`` creates
|
||||
``{"k1":{"k2":{}}}`` and inside the innermost dictionary last key (``k3`` in
|
||||
the example) is contained with its value.
|
||||
#. Value may be empty in which case they are interpreted as an order to remove
|
||||
some value: ``k1.k2=`` will form ``{"k1":{"k2":REMOVE_THIS_KEY}}`` nested
|
||||
dictionary where ``k2`` value is a special value that tells
|
||||
dictionary-merging function to remove ``k2`` rather then replace it with
|
||||
something.
|
||||
#. Value may be a JSON strings like ``{"a":1}`` (JSON dictionary), ``["a",1]``
|
||||
(JSON list), ``1`` or ``-1`` (JSON number), ``"abc"`` (JSON string) or
|
||||
``true``, ``false`` and ``null`` (JSON boolean objects and ``Null`` object
|
||||
from JSON). General rule is that anything starting with a digit (U+0030 till
|
||||
U+0039, inclusive), a hyphenminus (U+002D), a quotation mark (U+0022), a left
|
||||
curly bracket (U+007B) or a left square bracket (U+005B) is considered to be
|
||||
some JSON object, same for *exact* values ``true``, ``false`` and ``null``.
|
||||
#. Any other value is considered to be literal string: ``k1=foo:bar`` parses to
|
||||
``{"k1": "foo:bar"}``.
|
||||
|
||||
The following environment variables may be used for overrides according to the
|
||||
above rules:
|
||||
|
||||
``POWERLINE_CONFIG_OVERRIDES``
|
||||
Overrides values from :file:`powerline/config.json`.
|
||||
|
||||
``POWERLINE_THEME_OVERRIDES``
|
||||
Overrides values from :file:`powerline/themes/{ext}/{key}.json`. Top-level
|
||||
key is treated as a name of the theme for which overrides are used: e.g. to
|
||||
disable cwd segment defined in :file:`powerline/themes/shell/default.json`
|
||||
one needs to use::
|
||||
|
||||
POWERLINE_THEME_OVERRIDES=default.segment_data.cwd.display=false
|
||||
|
||||
Additionally one environment variable is a usual *colon*-separated list of
|
||||
directories: ``POWERLINE_CONFIG_PATHS``. This one defines paths which will be
|
||||
searched for configuration.
|
||||
|
||||
Zsh/zpython overrides
|
||||
=====================
|
||||
|
||||
Here overrides are controlled by similarly to the powerline script, but values
|
||||
are taken from zsh variables.
|
||||
are taken from zsh variables. :ref:`Environment variable overrides` are also
|
||||
supported: if variable is a string this variant is used.
|
||||
|
||||
``POWERLINE_CONFIG_OVERRIDES``
|
||||
Overrides options from :file:`powerline/config.json`. Should be a zsh
|
||||
|
|
|
@ -8,7 +8,7 @@ from weakref import WeakValueDictionary, ref
|
|||
import zsh
|
||||
|
||||
from powerline.shell import ShellPowerline
|
||||
from powerline.lib import parsedotval
|
||||
from powerline.lib.overrides import parsedotval
|
||||
from powerline.lib.unicode import unicode, u
|
||||
from powerline.lib.encoding import (get_preferred_output_encoding,
|
||||
get_preferred_environment_encoding)
|
||||
|
|
|
@ -5,7 +5,9 @@ from __future__ import (division, absolute_import, print_function)
|
|||
import argparse
|
||||
import sys
|
||||
|
||||
from powerline.lib import parsedotval
|
||||
from itertools import chain
|
||||
|
||||
from powerline.lib.overrides import parsedotval, parse_override_var
|
||||
from powerline.lib.dict import mergeargs
|
||||
from powerline.lib.encoding import get_preferred_arguments_encoding
|
||||
|
||||
|
@ -14,21 +16,23 @@ if sys.version_info < (3,):
|
|||
encoding = get_preferred_arguments_encoding()
|
||||
|
||||
def arg_to_unicode(s):
|
||||
return unicode(s, encoding, 'replace') if not isinstance(s, unicode) else s
|
||||
return unicode(s, encoding, 'replace') if not isinstance(s, unicode) else s # NOQA
|
||||
else:
|
||||
def arg_to_unicode(s):
|
||||
return s
|
||||
|
||||
|
||||
def finish_args(args):
|
||||
if args.config_override:
|
||||
args.config_override = mergeargs((parsedotval(v) for v in args.config_override))
|
||||
if args.theme_override:
|
||||
args.theme_override = mergeargs((parsedotval(v) for v in args.theme_override))
|
||||
else:
|
||||
args.theme_override = {}
|
||||
def finish_args(environ, args):
|
||||
args.config_override = mergeargs(chain(
|
||||
(parsedotval(v) for v in args.config_override or ()),
|
||||
parse_override_var(environ.get('POWERLINE_CONFIG_OVERRIDES', '')),
|
||||
))
|
||||
args.theme_override = mergeargs(chain(
|
||||
(parsedotval(v) for v in args.theme_override or ()),
|
||||
parse_override_var(environ.get('POWERLINE_THEME_OVERRIDES', '')),
|
||||
))
|
||||
if args.renderer_arg:
|
||||
args.renderer_arg = mergeargs((parsedotval(v) for v in args.renderer_arg))
|
||||
args.renderer_arg = mergeargs((parsedotval(v) for v in args.renderer_arg), remove=True)
|
||||
|
||||
|
||||
def get_argparser(ArgumentParser=argparse.ArgumentParser):
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
# vim:fileencoding=utf-8:noet
|
||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||
|
||||
import json
|
||||
|
||||
from functools import wraps
|
||||
|
||||
from powerline.lib.dict import REMOVE_THIS_KEY
|
||||
|
||||
|
||||
def wraps_saveargs(wrapped):
|
||||
def dec(wrapper):
|
||||
|
@ -30,59 +26,3 @@ def add_divider_highlight_group(highlight_group):
|
|||
return None
|
||||
return f
|
||||
return dec
|
||||
|
||||
|
||||
def parse_value(s):
|
||||
'''Convert string to Python object
|
||||
|
||||
Rules:
|
||||
|
||||
* Empty string means that corresponding key should be removed from the
|
||||
dictionary.
|
||||
* Strings that start with a minus, digit or with some character that starts
|
||||
JSON collection or string object are parsed as JSON.
|
||||
* JSON special values ``null``, ``true``, ``false`` (case matters) are
|
||||
parsed as JSON.
|
||||
* All other values are considered to be raw strings.
|
||||
|
||||
:param str s: Parsed string.
|
||||
|
||||
:return: Python object.
|
||||
'''
|
||||
if not s:
|
||||
return REMOVE_THIS_KEY
|
||||
elif s[0] in '"{[0193456789-' or s in ('null', 'true', 'false'):
|
||||
return json.loads(s)
|
||||
else:
|
||||
return s
|
||||
|
||||
|
||||
def keyvaluesplit(s):
|
||||
if '=' not in s:
|
||||
raise TypeError('Option must look like option=json_value')
|
||||
if s[0] == '_':
|
||||
raise ValueError('Option names must not start with `_\'')
|
||||
idx = s.index('=')
|
||||
o = s[:idx]
|
||||
val = parse_value(s[idx + 1:])
|
||||
return (o, val)
|
||||
|
||||
|
||||
def parsedotval(s):
|
||||
if type(s) is tuple:
|
||||
o, val = s
|
||||
val = parse_value(val)
|
||||
else:
|
||||
o, val = keyvaluesplit(s)
|
||||
|
||||
keys = o.split('.')
|
||||
if len(keys) > 1:
|
||||
r = (keys[0], {})
|
||||
rcur = r[1]
|
||||
for key in keys[1:-1]:
|
||||
rcur[key] = {}
|
||||
rcur = rcur[key]
|
||||
rcur[keys[-1]] = val
|
||||
return r
|
||||
else:
|
||||
return (o, val)
|
||||
|
|
|
@ -5,26 +5,44 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct
|
|||
REMOVE_THIS_KEY = object()
|
||||
|
||||
|
||||
def mergeargs(argvalue):
|
||||
def mergeargs(argvalue, remove=False):
|
||||
if not argvalue:
|
||||
return None
|
||||
r = {}
|
||||
for subval in argvalue:
|
||||
mergedicts(r, dict([subval]))
|
||||
mergedicts(r, dict([subval]), remove=remove)
|
||||
return r
|
||||
|
||||
|
||||
def mergedicts(d1, d2):
|
||||
def _clear_special_values(d):
|
||||
'''Remove REMOVE_THIS_KEY values from dictionary
|
||||
'''
|
||||
l = [d]
|
||||
while l:
|
||||
i = l.pop()
|
||||
pops = []
|
||||
for k, v in i.items():
|
||||
if v is REMOVE_THIS_KEY:
|
||||
pops.append(k)
|
||||
elif isinstance(v, dict):
|
||||
l.append(v)
|
||||
for k in pops:
|
||||
i.pop(k)
|
||||
|
||||
|
||||
def mergedicts(d1, d2, remove=True):
|
||||
'''Recursively merge two dictionaries
|
||||
|
||||
First dictionary is modified in-place.
|
||||
'''
|
||||
for k in d2:
|
||||
if k in d1 and isinstance(d1[k], dict) and isinstance(d2[k], dict):
|
||||
mergedicts(d1[k], d2[k])
|
||||
elif d2[k] is REMOVE_THIS_KEY:
|
||||
mergedicts(d1[k], d2[k], remove)
|
||||
elif remove and d2[k] is REMOVE_THIS_KEY:
|
||||
d1.pop(k, None)
|
||||
else:
|
||||
if remove and isinstance(d2[k], dict):
|
||||
_clear_special_values(d2[k])
|
||||
d1[k] = d2[k]
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
# vim:fileencoding=utf-8:noet
|
||||
from __future__ import (unicode_literals, division, absolute_import, print_function)
|
||||
|
||||
import json
|
||||
|
||||
from powerline.lib.dict import REMOVE_THIS_KEY
|
||||
|
||||
|
||||
def parse_value(s):
|
||||
'''Convert string to Python object
|
||||
|
||||
Rules:
|
||||
|
||||
* Empty string means that corresponding key should be removed from the
|
||||
dictionary.
|
||||
* Strings that start with a minus, digit or with some character that starts
|
||||
JSON collection or string object are parsed as JSON.
|
||||
* JSON special values ``null``, ``true``, ``false`` (case matters) are
|
||||
parsed as JSON.
|
||||
* All other values are considered to be raw strings.
|
||||
|
||||
:param str s: Parsed string.
|
||||
|
||||
:return: Python object.
|
||||
'''
|
||||
if not s:
|
||||
return REMOVE_THIS_KEY
|
||||
elif s[0] in '"{[0193456789-' or s in ('null', 'true', 'false'):
|
||||
return json.loads(s)
|
||||
else:
|
||||
return s
|
||||
|
||||
|
||||
def keyvaluesplit(s):
|
||||
'''Split K1.K2=VAL into K1.K2 and parsed VAL
|
||||
'''
|
||||
if '=' not in s:
|
||||
raise TypeError('Option must look like option=json_value')
|
||||
if s[0] == '_':
|
||||
raise ValueError('Option names must not start with `_\'')
|
||||
idx = s.index('=')
|
||||
o = s[:idx]
|
||||
val = parse_value(s[idx + 1:])
|
||||
return (o, val)
|
||||
|
||||
|
||||
def parsedotval(s):
|
||||
'''Parse K1.K2=VAL into {"K1":{"K2":VAL}}
|
||||
|
||||
``VAL`` is processed according to rules defined in :py:func:`parse_value`.
|
||||
'''
|
||||
if type(s) is tuple:
|
||||
o, val = s
|
||||
val = parse_value(val)
|
||||
else:
|
||||
o, val = keyvaluesplit(s)
|
||||
|
||||
keys = o.split('.')
|
||||
if len(keys) > 1:
|
||||
r = (keys[0], {})
|
||||
rcur = r[1]
|
||||
for key in keys[1:-1]:
|
||||
rcur[key] = {}
|
||||
rcur = rcur[key]
|
||||
rcur[keys[-1]] = val
|
||||
return r
|
||||
else:
|
||||
return (o, val)
|
||||
|
||||
|
||||
def parse_override_var(s):
|
||||
'''Parse a semicolon-separated list of strings into a sequence of values
|
||||
|
||||
Emits the same items in sequence as :py:func:`parsedotval` does.
|
||||
'''
|
||||
return (
|
||||
parsedotval(item)
|
||||
for item in s.split(';')
|
||||
if item
|
||||
)
|
|
@ -78,8 +78,11 @@ def render(args, environ, cwd):
|
|||
tuple(args.config_override) if args.config_override else None,
|
||||
tuple(args.theme_override) if args.theme_override else None,
|
||||
tuple(args.config_path) if args.config_path else None,
|
||||
environ.get('POWERLINE_THEME_OVERRIDES', ''),
|
||||
environ.get('POWERLINE_CONFIG_OVERRIDES', ''),
|
||||
environ.get('POWERLINE_CONFIG_PATHS', ''),
|
||||
)
|
||||
finish_args(args)
|
||||
finish_args(environ, args)
|
||||
powerline = None
|
||||
try:
|
||||
powerline = powerlines[key]
|
||||
|
|
|
@ -24,7 +24,7 @@ else:
|
|||
|
||||
if __name__ == '__main__':
|
||||
args = get_argparser().parse_args()
|
||||
finish_args(args)
|
||||
finish_args(os.environ, args)
|
||||
powerline = ShellPowerline(args, run_once=True)
|
||||
segment_info = {'args': args, 'environ': os.environ}
|
||||
write_output(args, powerline, segment_info, write, get_preferred_output_encoding())
|
||||
|
|
|
@ -114,7 +114,7 @@ class TestParser(TestCase):
|
|||
(['shell', '-c', 'common={ }'], {'ext': ['shell'], 'config_override': {'common': {}}}),
|
||||
]:
|
||||
args = parser.parse_args(argv)
|
||||
finish_args(args)
|
||||
finish_args({}, args)
|
||||
for key, val in expargs.items():
|
||||
self.assertEqual(getattr(args, key), val)
|
||||
for key, val in args.__dict__.items():
|
||||
|
|
Loading…
Reference in New Issue