mirror of
ssh://git.janware.com/srv/git/janware/proj/jw-devtest
synced 2026-01-15 10:23:32 +01:00
ListCmd.py: Make Rows sortable
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
parent
3fba1c2a89
commit
dc40decb96
1 changed files with 111 additions and 26 deletions
|
|
@ -3,7 +3,10 @@
|
|||
import re
|
||||
import os
|
||||
import asyncio
|
||||
import shlex
|
||||
import traceback
|
||||
from operator import itemgetter
|
||||
from functools import total_ordering
|
||||
from jwutils.log import *
|
||||
from jwutils import Options
|
||||
from devtest.os import *
|
||||
|
|
@ -14,21 +17,30 @@ class ListCmd(TestCase): # export
|
|||
|
||||
# ------------------------------------- class Row
|
||||
|
||||
@total_ordering
|
||||
class Row:
|
||||
|
||||
def field(self, key, default=None):
|
||||
def field(self, key, default=None, throw=True):
|
||||
if key in self.__fields.keys():
|
||||
return self.__fields[key]
|
||||
if default is not None:
|
||||
if default is not None or not throw:
|
||||
return default
|
||||
raise KeyError('No field "{}" in row "{}"'.format(key, self))
|
||||
raise KeyError('No field "{}" in row "{}"'.format(key, self.__fields))
|
||||
|
||||
def attrib(self, key, default=None):
|
||||
def attrib(self, key, default=None, throw=True):
|
||||
if self.__attribs is not None:
|
||||
return self.__attribs.get(key, default)
|
||||
if default is not None:
|
||||
if default is not None or not throw:
|
||||
return default
|
||||
raise KeyError('No attrib "{}" in row "{}"'.format(key, self))
|
||||
raise KeyError('No attrib "{}" in row "{}"'.format(key, self.__fields))
|
||||
|
||||
@property
|
||||
def fields(self):
|
||||
return self.__fields
|
||||
|
||||
@property
|
||||
def attribs(self):
|
||||
return self.__attribs
|
||||
|
||||
# "needed": [ "dummyd", "v3.23" ]
|
||||
# "key_": [ feature ]
|
||||
|
|
@ -61,18 +73,26 @@ class ListCmd(TestCase): # export
|
|||
def cmp(self, other):
|
||||
decisive = self.parent.decisive
|
||||
for field in decisive:
|
||||
ret = self.field(field) < other.field(field)
|
||||
if ret:
|
||||
return ret
|
||||
s, o = self.field(field, throw=False), other.field(field, throw=False)
|
||||
if (s and not o) or (o and not s):
|
||||
if s: # certainly not o
|
||||
return 1
|
||||
return -1 # certainly not s
|
||||
if s == o:
|
||||
continue
|
||||
if s > o:
|
||||
return 1
|
||||
if o > s:
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def to_str(self, only_values=False, quotes=None, fields=['fields']):
|
||||
use_fields = None
|
||||
for f_set_name in fields:
|
||||
use_fields = self.parent.row_info(f_set_name, default=False)
|
||||
if use_fields != False:
|
||||
use_fields = self.parent.row_info(f_set_name, throw=False)
|
||||
if use_fields is not None:
|
||||
break
|
||||
if use_fields == False:
|
||||
if use_fields is None:
|
||||
raise Exception("None of the fields wanted for formatting are available: {}".format(fields))
|
||||
q = '"' if quotes == True else ('' if quotes is None and only_values else '')
|
||||
if only_values:
|
||||
|
|
@ -83,10 +103,37 @@ class ListCmd(TestCase): # export
|
|||
r += " | " + str(self.__attribs)
|
||||
return r
|
||||
|
||||
def dump(self, prio, msg=None, **kwargs):
|
||||
if 'caller' not in kwargs:
|
||||
caller = get_caller_pos(1)
|
||||
else:
|
||||
caller = kwargs['caller']
|
||||
del kwargs['caller']
|
||||
if msg is not None:
|
||||
slog(prio, ',----------------------- {}'.format(msg), caller=caller)
|
||||
slog(prio, '| line="{}"'.format(self.__line), caller=caller)
|
||||
slog(prio, '| ---------- types', caller=caller)
|
||||
for t in self.__types:
|
||||
slog(prio, '| {}'.format(t), caller=caller)
|
||||
if len(self.__fields):
|
||||
slog(prio, '| ---------- fields', caller=caller)
|
||||
for key, val in self.__fields.items():
|
||||
slog(prio, '| {}="{}"'.format(key, val), caller=caller)
|
||||
if self.__attribs is not None and len(self.__attribs):
|
||||
slog(prio, '| ---------- attribs', caller=caller)
|
||||
for key, val in self.__attribs.items():
|
||||
slog(prio, '| {}="{}"'.format(key, val), caller=caller)
|
||||
if msg is not None:
|
||||
slog(prio, '`----------------------- {}'.format(msg), caller=caller)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return '(' + self.to_str(fields=['name-fields', 'cmp-fields'], only_values=True) + ')'
|
||||
|
||||
@property
|
||||
def line(self):
|
||||
return self.__line
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.cmp(other) < 0
|
||||
def __le__(self, other):
|
||||
|
|
@ -103,7 +150,7 @@ class ListCmd(TestCase): # export
|
|||
def __str__(self):
|
||||
return self.to_str()
|
||||
|
||||
def _repr__(self):
|
||||
def __repr__(self):
|
||||
return self.to_str()
|
||||
|
||||
def __format__(self, fmt):
|
||||
|
|
@ -120,7 +167,7 @@ class ListCmd(TestCase): # export
|
|||
|
||||
def __hash__(self):
|
||||
decisive = self.parent.decisive
|
||||
return hash(tuple([self.field(field, '') for field in decisive]))
|
||||
return hash(tuple([str(self.field(field, '')) for field in decisive]))
|
||||
|
||||
# ------------------------------------- class ListCmd methods
|
||||
|
||||
|
|
@ -130,6 +177,7 @@ class ListCmd(TestCase): # export
|
|||
self.total_timeout = total_timeout
|
||||
self.__decisive = None
|
||||
self.__row_info = None
|
||||
self.__write_raw_response = True
|
||||
if write_response is not None:
|
||||
self.__write_response = write_response
|
||||
else:
|
||||
|
|
@ -161,7 +209,10 @@ class ListCmd(TestCase): # export
|
|||
def _filter(self, output):
|
||||
return output
|
||||
|
||||
def row_info(self, key, default=None):
|
||||
def _row_name(self, row):
|
||||
return '(' + row.to_str(fields=['name-fields', 'cmp-fields'], only_values=True) + ')'
|
||||
|
||||
def row_info(self, key, default=None, throw=False):
|
||||
if self.__row_info == None:
|
||||
info = self._row_info()
|
||||
if type(info) == dict:
|
||||
|
|
@ -172,7 +223,7 @@ class ListCmd(TestCase): # export
|
|||
for i in range(0, len(info)):
|
||||
self.__row_info[keys[i]] = info[i]
|
||||
if not key in self.__row_info.keys():
|
||||
if default is not None:
|
||||
if default is not None or not throw:
|
||||
return default
|
||||
raise Exception('Required row info "{}" missing'.format(key))
|
||||
return self.__row_info[key]
|
||||
|
|
@ -208,31 +259,45 @@ class ListCmd(TestCase): # export
|
|||
def _eval(self, output, features, header=None):
|
||||
|
||||
def format_rows(rows, quotes=False):
|
||||
def cmp(r1, r2):
|
||||
for k in sort_keys:
|
||||
if r1[k] < r2[k]:
|
||||
return True
|
||||
return False
|
||||
|
||||
#def cmp(r1, r2):
|
||||
# for k in sort_keys:
|
||||
# if r1[k] < r2[k]:
|
||||
# return True
|
||||
# return False
|
||||
sort_keys = []
|
||||
key_sets = ['name-fields', 'cmp-fields']
|
||||
for s in key_sets:
|
||||
for k in self.row_info(s, []):
|
||||
if k not in sort_keys:
|
||||
sort_keys.append(k)
|
||||
|
||||
#return sorted([ row.to_str(fields=['cmp-fields', 'fields'], only_values=True, quotes=quotes) for row in rows], key=cmp)
|
||||
return [ row.to_str(fields=['cmp-fields', 'fields'], only_values=True, quotes=quotes) for row in sorted(rows, key=itemgetter(*sort_keys))]
|
||||
#return [row.to_str(fields=['cmp-fields', 'fields'], only_values=True, quotes=quotes) for row in sorted(rows, key=itemgetter(*sort_keys))]
|
||||
return [row.to_str(fields=['cmp-fields', 'fields'], only_values=True, quotes=quotes) for row in sorted(rows)]
|
||||
|
||||
if self.__write_response and not os.path.exists(self.refpath):
|
||||
ref_lines = []
|
||||
else:
|
||||
slog(INFO, 'Reading reference from "{}"'.format(self.refpath))
|
||||
with open(self.refpath, "r") as f:
|
||||
ref_lines = f.readlines()
|
||||
|
||||
if self.__write_raw_response:
|
||||
raw_response_path = self.refpath + '.raw'
|
||||
with open(raw_response_path, "w") as f:
|
||||
slog(INFO, 'Writing raw response to "{}"'.format(raw_response_path))
|
||||
if header:
|
||||
f.write(header)
|
||||
f.write('\n'.join(output))
|
||||
output = self._filter(output)
|
||||
last_features = set()
|
||||
if self.__write_response:
|
||||
response_path = self.refpath + '.last'
|
||||
if os.path.exists(response_path):
|
||||
with open(response_path, "r") as f:
|
||||
for line in f:
|
||||
payload, matches = re.subn('^ *# *features: *', '', line)
|
||||
if matches > 0:
|
||||
last_features = set(shlex.split(payload))
|
||||
break
|
||||
with open(response_path, "w") as f:
|
||||
slog(INFO, 'Writing response to "{}"'.format(response_path))
|
||||
if header:
|
||||
|
|
@ -275,6 +340,22 @@ class ListCmd(TestCase): # export
|
|||
if not len(r):
|
||||
return None
|
||||
|
||||
feature_diff = set(features) - last_features
|
||||
if self.__write_response and len(feature_diff):
|
||||
response_path = self.refpath + '.bad'
|
||||
feature_diff_str = ', '.join(['"{}"'.format(f) for f in feature_diff])
|
||||
with open(response_path, "w") as f:
|
||||
slog(INFO, 'Writing feature diff to "{}"'.format(response_path))
|
||||
if header:
|
||||
f.write(header)
|
||||
if len(missing):
|
||||
f.write("# --- missing {}\n".format(feature_diff))
|
||||
for row in missing:
|
||||
f.write(row.line + ' # "needed": [{}], "bad": ["default"]\n'.format(feature_diff_str))
|
||||
if len(too_many):
|
||||
f.write("# --- too many {}\n".format(feature_diff))
|
||||
for row in too_many:
|
||||
f.write(row.line + ' # "bad" [{}]\n'.format(feature_diff_str))
|
||||
return ' and '.join(r)
|
||||
|
||||
async def _run(self, env, machine, phase):
|
||||
|
|
@ -284,7 +365,11 @@ class ListCmd(TestCase): # export
|
|||
total_timeout=self.total_timeout, echo_cmd=False)
|
||||
if output is None:
|
||||
return "Failed to run command: " + cmd
|
||||
header = '# ' + cmd + '\n' if self.__write_response else None
|
||||
if not self.__write_response:
|
||||
header = None
|
||||
else:
|
||||
header = '# ' + cmd + '\n'
|
||||
header += '# features: {}\n'.format(' '.join(env.features))
|
||||
return self._eval(output, env.features, header=header)
|
||||
|
||||
def dump(self, prio, *args, **kwargs):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue