Completed
Push — master ( 31b814...90c146 )
by Stephan
32s
created

XInitError   A

Complexity

Total Complexity 1

Size/Duplication

Total Lines 8
Duplicated Lines 0 %

Importance

Changes 3
Bugs 2 Features 0
Metric Value
c 3
b 2
f 0
dl 0
loc 8
rs 10
wmc 1
1
"""Helper functions and classes"""
2
3
__author__ = "Stephan Sokolow (deitarion/SSokolow)"
4
__license__ = "GNU GPL 2.0 or later"
5
6
from itertools import chain, combinations
7
from UserDict import DictMixin
8
9
# Allow MyPy to work without depending on the `typing` package
10
# (And silence complaints from only using the imported types in comments)
11
try:
12
    # pylint: disable=unused-import
13
    from typing import (Any, Callable, Dict, Iterable, Iterator, List,  # NOQA
14
                        Optional, Sequence, Tuple, TYPE_CHECKING)
15
    from mypy_extensions import VarArg, KwArg  # NOQA
16
17
    if TYPE_CHECKING:
18
        from .wm import WindowManager  # NOQA
19
20
    # pylint: disable=C0103
21
    PercentRect = Tuple[float, float, float, float]
22
    Strut = Tuple[int, int, int, int, int, int, int, int, int, int, int, int]
23
24
    # FIXME: Replace */** with a dict so I can be strict here
25
    CommandCB = Callable[..., Any]
26
except:  # pylint: disable=bare-except
27
    pass
0 ignored issues
show
Unused Code introduced by
This except handler seems to be unused and could be removed.

Except handlers which only contain pass and do not have an else clause can usually simply be removed:

try:
    raises_exception()
except:  # Could be removed
    pass
Loading history...
28
29
def clamp_idx(idx, stop, wrap=True):
30
    # type: (int, int, bool) -> int
31
    """Ensure a 0-based index is within a given range [0, stop).
32
33
    Uses the same half-open range convention as Python slices.
34
35
    @param wrap: If C{True}, wrap around rather than saturating.
36
    """
37
    if wrap:
38
        return idx % stop
39
    else:
40
        return max(min(idx, stop - 1), 0)
41
42
def powerset(iterable):  # type: (Iterable[Any]) -> Iterator[Sequence[Any]]
43
    """C{powerset([1,2,3])} --> C{() (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)}
44
45
    @rtype: iterable
46
    """
47
    i = list(iterable)
48
    return chain.from_iterable(combinations(i, j) for j in range(len(i) + 1))
49
50
def fmt_table(rows,          # type: Any
51
              headers,       # type: Sequence
52
              group_by=None  # type: Optional[int]
53
              ):  # type: (...) -> str
54
    """Format a collection as a textual table.
55
56
    @param headers: Header labels for the columns
57
    @param group_by: Index of the column to group results by.
58
    @type rows: C{dict} or iterable of iterables
59
    @type headers: C{list(str)}
60
    @type group_by: C{int}
61
62
    @attention: This uses C{zip()} to combine things. The number of columns
63
        displayed will be defined by the narrowest of all rows.
64
65
    @rtype: C{str}
66
    """
67
    output = []  # type: List[str]
68
69
    if isinstance(rows, dict):
70
        rows = list(sorted(rows.items()))
71
72
    groups = {}  # type: Dict[str, List[str]]
73
    if group_by is not None:
74
        headers = list(headers)
75
        headers.pop(group_by)
76
        rows = [list(row) for row in rows]
77
        for row in rows:
78
            group = row.pop(group_by)
79
            groups.setdefault(group, []).append(row)
80
    else:
81
        groups[''] = rows
82
83
    # Identify how much space needs to be allocated for each column
84
    col_maxlens = []
85
    for pos, header in enumerate(headers):
86
        maxlen = max(len(x[pos]) for x in rows if len(x) > pos)
87
        col_maxlens.append(max(maxlen, len(header)))
88
89
    def fmt_row(row, pad=' ', indent=0, min_width=0):
90
        """Format a fmt_table row"""
91
        result = []
92
        for width, label in zip(col_maxlens, row):
93
            result.append('%s%s ' % (' ' * indent, label.ljust(width, pad)))
94
95
        _width = sum(len(x) for x in result)
96
        if _width < min_width:
97
            result[-1] = result[-1][:-1]
98
            result.append(pad * (min_width - _width + 1))
99
100
        result.append('\n')
101
        return result
102
103
    # Print the headers and divider
104
    group_width = max(len(x) for x in groups)
105
    output.extend(fmt_row(headers))
106
    output.extend(fmt_row([''] * len(headers), '-', min_width=group_width + 1))
107
108
    for group in sorted(groups):
109
        if group:
110
            output.append("\n%s\n" % group)
111
        for row in groups[group]:
112
            output.extend(fmt_row(row, indent=1))
113
114
    return ''.join(output)
115
116
class EnumSafeDict(DictMixin):
117
    """A dict-like object which avoids comparing objects of different types
118
    to avoid triggering spurious Glib "comparing different enum types"
119
    warnings.
120
    """
121
122
    def __init__(self, *args):
123
        self._contents = {}
124
125
        for inDict in args:
0 ignored issues
show
Coding Style Naming introduced by
The name inDict does not conform to the variable naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
126
            for key, val in inDict.items():
127
                self[key] = val
128
129
    def __contains__(self, key):
130
        ktype = type(key)
131
        return ktype in self._contents and key in self._contents[ktype]
132
133
    def __delitem__(self, key):  # type: (Any) -> None
134
        if key in self:
135
            ktype = type(key)
136
            section = self._contents[ktype]
137
            del section[key]
138
            if not section:
139
                del self._contents[ktype]
140
        else:
141
            raise KeyError(key)
142
143
    def __getitem__(self, key):  # type: (Any) -> Any
144
        if key in self:
145
            return self._contents[type(key)][key]
146
        else:
147
            raise KeyError(key)
148
149
    def __iter__(self):
150
        for section in self._contents.values():
151
            for key in section.keys():
152
                yield key
153
154
    def __len__(self):
155
        return len(self._contents)
156
157
    def __repr__(self):
158
        return "%s(%s)" % (self.__class__.__name__,
159
            ', '.join(repr(x) for x in self._contents.values()))
160
161
    def __setitem__(self, key, value):  # type: (Any, Any) -> None
162
        ktype = type(key)
163
        self._contents.setdefault(ktype, {})[key] = value
164
165
    def iteritems(self):
166
        return [(key, self[key]) for key in self]
167
168
    def keys(self):  # type: () -> List[Any]
169
        """D.keys() -> list of D's keys"""
170
        return list(self)
171
172
class XInitError(Exception):
173
    """Raised when something outside our control causes the X11 connection to
174
       fail.
175
    """
176
177
    def __str__(self):
178
        return ("%s\n\t(The cause of this error lies outside of QuickTile)" %
179
                Exception.__str__(self))
180
181
# vim: set sw=4 sts=4 expandtab :
182