Completed
Push — master ( 645052...f7441f )
by Satoru
01:00
created

_parseline()   B

Complexity

Conditions 4

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 1
Metric Value
cc 4
c 2
b 1
f 1
dl 0
loc 30
rs 8.5806
1
#
2
# Copyright (C) 2016 Satoru SATOH <ssato @ redhat.com>
3
# License: MIT
4
#
5
"""
6
Parser for simple Shell vars' definitions.
7
8
.. versionadded:: 0.6.99
9
   Added an experimental parser for simple shelll vars' definitions w/o shell
10
   variable expansions nor complex shell statements like conditionals.
11
12
- Format to support: Simple shell variables' definitions w/o any shell variable
13
  expansions nor complex shell statements such as conditionals, etc.
14
- Requirements: None (built-in)
15
- Limitations: Currently, it only supports a varialbe defined in a line.
16
- Special options: None
17
"""
18
from __future__ import absolute_import
19
20
import logging
21
import itertools
22
import re
23
24
import anyconfig.backend.base
25
26
27
LOGGER = logging.getLogger(__name__)
28
29
30
def _parseline(line):
31
    """
32
    Parse a line contains shell variable definition.
33
34
    :param line: A string to parse, must not start with '#' (comment)
35
    :return: A tuple of (key, value), both key and value may be None
36
37
    >>> _parseline("aaa=")
38
    ('aaa', '')
39
    >>> _parseline("aaa=bbb")
40
    ('aaa', 'bbb')
41
    >>> _parseline("aaa='bb b'")
42
    ('aaa', 'bb b')
43
    >>> _parseline('aaa="bb#b"')
44
    ('aaa', 'bb#b')
45
    >>> _parseline('aaa="bb\\"b"')
46
    ('aaa', 'bb"b')
47
    >>> _parseline("aaa=bbb   # ccc")
48
    ('aaa', 'bbb')
49
    """
50
    match = re.match(r"^\s*(\S+)=(?:(?:"
51
                     r"(?:\"(.*[^\\])\")|(?:'(.*[^\\])')|"
52
                     r"(?:([^\"'#\s]+)))?)\s*#*", line)
53
    if not match:
54
        LOGGER.warning("Invalid line found: %s", line)
55
        return (None, None)
56
57
    tpl = match.groups()
58
    vals = list(itertools.dropwhile(lambda x: x is None, tpl[1:]))
59
    return (tpl[0], vals[0] if vals else '')
60
61
62
def load(stream, to_container=dict):
63
    """
64
    Load and parse a file or file-like object `stream` provides simple shell
65
    variables' definitions.
66
67
    :param stream: A file or file like object
68
    :param to_container:
69
        Factory function to create a dict-like object to store properties
70
    :return: Dict-like object holding shell variables' definitions
71
72
    >>> from anyconfig.compat import StringIO as to_strm
73
    >>> load(to_strm(''))
74
    {}
75
    >>> load(to_strm("# "))
76
    {}
77
    >>> load(to_strm("aaa="))
78
    {'aaa': ''}
79
    >>> load(to_strm("aaa=bbb"))
80
    {'aaa': 'bbb'}
81
    >>> load(to_strm("aaa=bbb # ..."))
82
    {'aaa': 'bbb'}
83
    """
84
    ret = to_container()
85
86
    for line in stream.readlines():
87
        line = line.rstrip()
88
        if line is None or not line:
89
            continue
90
91
        (key, val) = _parseline(line)
92
        if key is None:
93
            LOGGER.warning("Empty val in the line: %s", line)
94
            continue
95
96
        ret[key] = val
97
98
    return ret
99
100
101
class Parser(anyconfig.backend.base.FromStreamLoader,
102
             anyconfig.backend.base.ToStreamDumper):
103
    """
104
    Parser for Java properties files.
105
    """
106
    _type = "shellvars"
107
108
    def load_from_stream(self, stream, to_container, **kwargs):
109
        """
110
        Load config from given file like object `stream`.
111
112
        :param stream: A file or file like object of Java properties files
113
        :param to_container: callble to make a container object
114
        :param kwargs: optional keyword parameters (ignored)
115
116
        :return: Dict-like object holding config parameters
117
        """
118
        return load(stream, to_container=to_container)
119
120
    def dump_to_stream(self, cnf, stream, **kwargs):
121
        """
122
        Dump config `cnf` to a file or file-like object `stream`.
123
124
        :param cnf: Java properties config data to dump
125
        :param stream: Java properties file or file like object
126
        :param kwargs: backend-specific optional keyword parameters :: dict
127
        """
128
        for key, val in anyconfig.compat.iteritems(cnf):
129
            stream.write("%s='%s'\n" % (key, val))
130
131
# vim:sw=4:ts=4:et:
132