/
swapon.py
173 lines (132 loc) · 4.09 KB
/
swapon.py
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
r"""jc - JSON Convert `swapon` command output parser
Usage (cli):
$ swapon | jc --swapon
or
$ jc swapon
Usage (module):
import jc
result = jc.parse('swapon', swapon_command_output)
Schema:
[
{
"name": string,
"type": string,
"size": integer,
"used": integer,
"priority": integer
}
]
Example:
$ swapon | jc --swapon
[
{
"name": "/swapfile",
"type": "file",
"size": 1073741824,
"used": 0,
"priority": -2
}
]
"""
from enum import Enum
from jc.exceptions import ParseError
import jc.utils
from typing import List, Dict, Union
class info:
"""Provides parser metadata (version, author, etc.)"""
version = "1.0"
description = "`swapon` command parser"
author = "Roey Darwish Dror"
author_email = "roey.ghost@gmail.com"
compatible = ["linux", "freebsd"]
magic_commands = ["swapon"]
tags = ["command"]
__version__ = info.version
_Value = Union[str, int]
_Entry = Dict[str, _Value]
class _Column(Enum):
NAME = "name"
TYPE = "type"
SIZE = "size"
USED = "used"
PRIO = "priority"
LABEL = "label"
UUID = "uuid"
@classmethod
def from_header(cls, header: str) -> "_Column":
if (header == "NAME") or (header == "Filename"):
return cls.NAME
elif (header == "TYPE") or (header == "Type"):
return cls.TYPE
elif (header == "SIZE") or (header == "Size"):
return cls.SIZE
elif (header == "USED") or (header == "Used"):
return cls.USED
elif (header == "PRIO") or (header == "Priority"):
return cls.PRIO
elif header == "LABEL":
return cls.LABEL
elif header == "UUID":
return cls.UUID
else:
raise ParseError(f"Unknown header: {header}")
def _parse_size(size: str) -> int:
power = None
if size[-1] == "B":
power = 0
if size[-1] == "K":
power = 1
elif size[-1] == "M":
power = 2
elif size[-1] == "G":
power = 3
elif size[-1] == "T":
power = 4
multiplier = 1024**power if power is not None else 1024
return (int(size[:-1]) if power is not None else int(size)) * multiplier
def _value(value: str, column: _Column) -> _Value:
if column == _Column.SIZE or column == _Column.USED:
return _parse_size(value)
elif column == _Column.PRIO:
return int(value)
else:
return value
def _process(proc_data: List[Dict]) -> List[Dict]:
"""
Final processing to conform to the schema.
Parameters:
proc_data: (List of Dictionaries) raw structured data to process
Returns:
List of Dictionaries. Structured to conform to the schema.
"""
return proc_data
def parse(data: str, raw: bool = False, quiet: bool = False) -> List[_Entry]:
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output: List[dict] = []
if jc.utils.has_data(data):
lines = iter(data.splitlines())
headers = next(lines)
columns = headers.split()
for each_line in lines:
line = each_line.split()
diff = len(columns) - len(line)
if not 0 <= diff <= 2:
raise ParseError(
f"Number of columns ({len(line)}) in line does not match number of headers ({len(columns)})"
)
document: _Entry = {}
for each_column, value in zip(columns, line):
column = _Column.from_header(each_column)
document[column.value] = _value(value, column)
raw_output.append(document)
return raw_output if raw else _process(raw_output)