forked from stratis-storage/stratis-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path_formatting.py
More file actions
186 lines (151 loc) · 5.99 KB
/
_formatting.py
File metadata and controls
186 lines (151 loc) · 5.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# Copyright 2016 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Formatting for tables.
"""
# isort: STDLIB
import sys
from functools import wraps
from typing import Any, Callable, List, Optional
from uuid import UUID
# isort: THIRDPARTY
from dbus import Struct
from wcwidth import wcswidth
# isort: FIRSTPARTY
from dbus_client_gen import DbusClientMissingPropertyError
# placeholder for tables where a desired value was not obtained from stratisd
# when the value should be supported.
TABLE_FAILURE_STRING = "FAILURE"
# placeholder for tables where a desired value is returned on the D-Bus but
# stratis-cli is unable to interpret the value.
TABLE_UNKNOWN_STRING = "???"
TOTAL_USED_FREE = "Total / Used / Free"
def get_property(prop: Struct, to_repr: Callable, default: Optional[Any]):
"""
Get a representation of an optional D-Bus property. An optional
D-Bus property is one that may be unknown to stratisd.
:param prop: the property value
:type prop: a pair of bool * T
:param to_repr: conversion function
:type to_repr: object -> object
:param object default: default for display
:returns: object produced by to_repr or default
:rtype: object
"""
(valid, value) = prop
return to_repr(value) if valid else default
def _get_column_len(column_width: int, entry_len: int, entry_width: int) -> int:
"""
From the desired column width in cells and the item to be printed,
calculate the required number of characters to pass to the format method.
In order to get the correct width in chars it is necessary to subtract
the number of cells above 1 (or add the number of cells below 1) that
an individual character occupies.
:param int column_width: the column width, in cells
:param int entry_len: the entry len, in characters
:param int entry_width: the entry width, in cells
:returns: the column width in characters
Note that if wcswidth has defaulted to len,
entry_width == entry_len, so the result is always column_width.
Precondition: entry_width != -1
(equivalently, entry has no unprintable characters)
"""
return column_width - (entry_width - entry_len)
def _print_row(
file: Any,
row: Any,
row_widths: List[int],
column_widths: List[int],
column_alignments: List[str],
):
"""
Print a single row in a table. The row might be the header row, or
a row of data items.
:param file: file to print to
:param list row: the list of items to print
:param list row_widths: the list of wcswidth for the row
:param list column_widths: corresponding list of column widths
:param list column_alignments: corresponding list of column alignment specs
Precondition: len(row) == len(column_widths) == len(alignment)
Precondition: no elements of row have unprintable characters
"""
entries = []
for index, entry in enumerate(row):
column_len = _get_column_len(
column_widths[index], len(entry), row_widths[index]
)
entries.append(f"{entry:{column_alignments[index]}{column_len}}")
print(" ".join(entries), end="", file=file)
def print_table(
column_headings: List[str],
row_entries: List[Any],
alignment: List[str],
file=sys.stdout,
):
"""
Given the column headings and the row_entries, print a table.
Align according to alignment specification and always pad with 2 spaces.
:param column_headings: the column headings
:type column_headings: list of str
:param row_entries: a list of the row entries
:type row_entries: list of list of str
:param alignment: the alignment indicator for each key, '<', '>', '^', '='
:type alignment: list of str
:param file: file to print too
:type file: writeable stream
Precondition: len(column_headings) == len(alignment) == len of each entry
in row_entries.
Precondition: all(wcswidth(h) != -1 for h in column_headings)
all(wcswidth(i) != -1 for row in rows for item in row)
(i.e., no items to be printed contain unprintable characters)
"""
column_widths = [0] * len(column_headings)
cell_widths = []
# Column header isn't different than any other row, insert into rows.
row_entries.insert(0, column_headings)
for row_index, row in enumerate(row_entries):
cell_widths.append([])
for column_index, cell in enumerate(row):
cell_width = wcswidth(cell)
cell_widths[row_index].append(cell_width)
column_widths[column_index] = max(column_widths[column_index], cell_width)
for row, row_widths in zip(row_entries, cell_widths):
_print_row(file, row, row_widths, column_widths, alignment)
print(file=file)
def get_uuid_formatter(unhyphenated: bool) -> Callable:
"""
Get a function to format UUIDs.
:param bool unhyphenated: whether the UUIDs are to be unhyphenated
:returns: function to format UUIDs
:rtype: str or UUID -> str
"""
return (
(lambda u: UUID(str(u)).hex) if unhyphenated else (lambda u: str(UUID(str(u))))
)
def catch_missing_property(
prop_to_str: Callable[[Any], Any], default: Any
) -> Callable[[Any], Any]:
"""
Return a function to just return a default if a property is missing.
"""
@wraps(prop_to_str)
def inner(mo: Any) -> str:
"""
Catch the exception and return a default
"""
try:
return prop_to_str(mo)
except DbusClientMissingPropertyError:
return default
return inner