From 430ed6e34d84604142fa896b194a2422df104777 Mon Sep 17 00:00:00 2001 From: Martin Sekera Date: Sun, 31 Mar 2019 00:13:55 +0100 Subject: [PATCH 1/5] hexdump: skip lines if the content is the same --- over/misc.py | 40 +++++++++++++++++++++++++++++++--------- over/version.py | 4 ++-- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/over/misc.py b/over/misc.py index af8e526..1273090 100644 --- a/over/misc.py +++ b/over/misc.py @@ -110,7 +110,7 @@ def debugger(): # -------------------------------------------------- -def hexdump(data, indent=0, width=16, show_header=True, show_offsets=True, show_ascii=True, use_colors=True, initial_offset=0, output=sys.stdout): +def hexdump(data, indent=0, width=16, show_header=True, show_offsets=True, show_ascii=True, use_colors=True, initial_offset=0, output=sys.stdout, skip_same=True): """ Writes a hex dump of `data` to `output`. @@ -120,6 +120,7 @@ def hexdump(data, indent=0, width=16, show_header=True, show_offsets=True, show_ If `show_ascii` is True, each line is suffixed with its ASCII representation. Unprintable characters are replaced with a dot. The `output` must implement a .write(str x) method. If `output` is None, the string is returned instead. + If `skip_same` is True, lines with identical content are abbreviated (omitted). @while creating a hex dump """ @@ -153,13 +154,10 @@ def hexdump(data, indent=0, width=16, show_header=True, show_offsets=True, show_ output_io.write(" ".join(line) + "\n") + skip_cnt = 0 + last_hex_bytes = None + while data[ptr:]: - if indent: - output_io.write(" " * indent) - - if show_offsets: - output_io.write(format_str %(initial_offset + ptr)) - hex_bytes = [] ascii_bytes = [] @@ -175,6 +173,29 @@ def hexdump(data, indent=0, width=16, show_header=True, show_offsets=True, show_ elif i == len(data): hex_bytes.extend([" "] * (width - local_i)) + ptr += width + + # the rest is just rendering + if skip_same: + if hex_bytes == last_hex_bytes: + skip_cnt += 1 + else: + if skip_cnt: + output_io.write("(skipped %d lines, %d B)\n" %(skip_cnt, skip_cnt * width)) + + skip_cnt = 0 + + last_hex_bytes = hex_bytes + + if skip_cnt: + continue + + if indent: + output_io.write(" " * indent) + + if show_offsets: + output_io.write(format_str %(initial_offset + ptr)) + if use_colors: output_io.write(text.render("")) output_io.write(" ".join(hex_bytes)) if use_colors: output_io.write(text.render("<.>")) @@ -185,8 +206,9 @@ def hexdump(data, indent=0, width=16, show_header=True, show_offsets=True, show_ output_io.write(text.render("<.>|") if use_colors else "|") output_io.write("\n") - - ptr += width + + if skip_cnt: + output_io.write("(skipped %d lines, %d B)\n" %(skip_cnt, skip_cnt * width)) if not output: output_io.seek(0) diff --git a/over/version.py b/over/version.py index e60543e..c9378ec 100644 --- a/over/version.py +++ b/over/version.py @@ -4,5 +4,5 @@ major = 2 # VERSION_MAJOR_IDENTIFIER minor = 1 # VERSION_MINOR_IDENTIFIER # VERSION_LAST_MM 2.1 -patch = 0 # VERSION_PATCH_IDENTIFIER -str = "2.1.0" # VERSION_STRING_IDENTIFIER +patch = 1 # VERSION_PATCH_IDENTIFIER +str = "2.1.1" # VERSION_STRING_IDENTIFIER From 1d5090b33e195dac9b62af7a6bf3d37903cdf6d8 Mon Sep 17 00:00:00 2001 From: Martin Sekera Date: Thu, 18 Apr 2019 01:57:58 +0200 Subject: [PATCH 2/5] generalized over.callbacks.directory -> over.callbacks.path --- over/callback.py | 37 +++++++++++++++++++++++++------------ over/version.py | 4 ++-- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/over/callback.py b/over/callback.py index 591af05..43c0146 100644 --- a/over/callback.py +++ b/over/callback.py @@ -53,28 +53,41 @@ def strings(*args): return out -def directory(exists=False, writable=False, gio=False): +def path(exists=False, writable=False, validators=[]): """ - Returns a directory callback that raises hell if: - - the supplied directory doesn't exist and `exists` is True - - isn't writable and `writable` is True - - isn't a valid Gio path and `gio` is True + Returns a path callback that takes a path (str) and verifies if it: + - exists iff `exists` is True + - is writable iff `writable` is True + - goes through each (function, message) pair in `validators`, in order - @while generating a callback + A validator is a function that takes a str argument and returns True or False. + If False is returned, the matching message is raised along with a RuntimeError. + The message may contain %s to be replaced by the supplied path. Example: + + validators=[ + (os.path.isdir, "%s is not a directory"), + (os.path.isabs, "%s is a directory, but is not an absolute path.") + ] + + @while generating a path callback """ - if gio: - raise NotImplementedError("Gio support is not yet here") - def cb(arg): path = os.path.abspath(os.path.expanduser(arg)) - if not os.path.isdir(path) and exists: - raise FileNotFoundError("%s (%s) does not exist" %(arg, path)) + if exists and not os.path.exists(path): + raise FileNotFoundError("%s does not exist" %(arg)) - if writable and not os.access(path, os.W_OK | os.X_OK): + if writable and not os.access(path, os.W_OK): raise PermissionError("%s exists but is not writable" %(arg)) + if validators: + for fn, msg_tpl in validators: + if not fn(path): + msg = msg_tpl %(arg) if "%s" in msg_tpl else msg_tpl + + raise RuntimeError(msg) + return path return cb diff --git a/over/version.py b/over/version.py index c9378ec..26e8c61 100644 --- a/over/version.py +++ b/over/version.py @@ -4,5 +4,5 @@ major = 2 # VERSION_MAJOR_IDENTIFIER minor = 1 # VERSION_MINOR_IDENTIFIER # VERSION_LAST_MM 2.1 -patch = 1 # VERSION_PATCH_IDENTIFIER -str = "2.1.1" # VERSION_STRING_IDENTIFIER +patch = 2 # VERSION_PATCH_IDENTIFIER +str = "2.1.2" # VERSION_STRING_IDENTIFIER From cf9fda165fa5e2734db201edf88333bf9a765e62 Mon Sep 17 00:00:00 2001 From: Martin Sekera Date: Thu, 18 Apr 2019 02:08:20 +0200 Subject: [PATCH 3/5] changed the over.callback.path API a bit --- over/callback.py | 13 +++++++++---- over/version.py | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/over/callback.py b/over/callback.py index 43c0146..252dd8b 100644 --- a/over/callback.py +++ b/over/callback.py @@ -53,11 +53,11 @@ def strings(*args): return out -def path(exists=False, writable=False, validators=[]): +def path(exists=False, permissions=None, validators=None): """ Returns a path callback that takes a path (str) and verifies if it: - exists iff `exists` is True - - is writable iff `writable` is True + - its permissions match those in `permissions` ("rwx" or any subset, e.g. "r--", "-w-", dashes are optional) - goes through each (function, message) pair in `validators`, in order A validator is a function that takes a str argument and returns True or False. @@ -78,8 +78,13 @@ def path(exists=False, writable=False, validators=[]): if exists and not os.path.exists(path): raise FileNotFoundError("%s does not exist" %(arg)) - if writable and not os.access(path, os.W_OK): - raise PermissionError("%s exists but is not writable" %(arg)) + if permissions: + if "r" in permissions and not os.access(path, os.R_OK): + raise PermissionError("%s is not readable" %(arg)) + if "w" in permissions and not os.access(path, os.W_OK): + raise PermissionError("%s is not writable" %(arg)) + if "x" in permissions and not os.access(path, os.X_OK): + raise PermissionError("%s is not executable" %(arg)) if validators: for fn, msg_tpl in validators: diff --git a/over/version.py b/over/version.py index 26e8c61..14cbadc 100644 --- a/over/version.py +++ b/over/version.py @@ -4,5 +4,5 @@ major = 2 # VERSION_MAJOR_IDENTIFIER minor = 1 # VERSION_MINOR_IDENTIFIER # VERSION_LAST_MM 2.1 -patch = 2 # VERSION_PATCH_IDENTIFIER -str = "2.1.2" # VERSION_STRING_IDENTIFIER +patch = 3 # VERSION_PATCH_IDENTIFIER +str = "2.1.3" # VERSION_STRING_IDENTIFIER From 908857a31700feda751d0ad41146466b2a721650 Mon Sep 17 00:00:00 2001 From: Martin Sekera Date: Thu, 18 Apr 2019 02:18:15 +0200 Subject: [PATCH 4/5] changed the over.callback.path API some more --- over/callback.py | 13 ++++++++++++- over/version.py | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/over/callback.py b/over/callback.py index 252dd8b..40f077f 100644 --- a/over/callback.py +++ b/over/callback.py @@ -87,7 +87,18 @@ def path(exists=False, permissions=None, validators=None): raise PermissionError("%s is not executable" %(arg)) if validators: - for fn, msg_tpl in validators: + if hasattr(validators, "__iter__"): + src = validators + else: + src = [validators] + + for v in src: + if hasattr(v, "__iter__"): + fn, msg_tpl = v + else: + fn = v + msg_tpl = "%%s failed validation by '%s'" %(fn.__name__) + if not fn(path): msg = msg_tpl %(arg) if "%s" in msg_tpl else msg_tpl diff --git a/over/version.py b/over/version.py index 14cbadc..87de0f3 100644 --- a/over/version.py +++ b/over/version.py @@ -4,5 +4,5 @@ major = 2 # VERSION_MAJOR_IDENTIFIER minor = 1 # VERSION_MINOR_IDENTIFIER # VERSION_LAST_MM 2.1 -patch = 3 # VERSION_PATCH_IDENTIFIER -str = "2.1.3" # VERSION_STRING_IDENTIFIER +patch = 4 # VERSION_PATCH_IDENTIFIER +str = "2.1.4" # VERSION_STRING_IDENTIFIER From 7634b3e35d3b580ce701fc4a673650c83735c903 Mon Sep 17 00:00:00 2001 From: Martin Sekera Date: Tue, 23 Apr 2019 01:51:51 +0200 Subject: [PATCH 5/5] add over.cmd.Command.__str__ --- over/cmd.py | 3 +++ over/version.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/over/cmd.py b/over/cmd.py index 0b12703..311b25d 100644 --- a/over/cmd.py +++ b/over/cmd.py @@ -61,6 +61,9 @@ class Command: self.__dict__["sequence_original"] = list(sequence) self.reset() + def __str__(self): + return " ".join(self.dump(pretty=True)) + def __setattr__(self, name, value): found = False diff --git a/over/version.py b/over/version.py index 87de0f3..e0f48db 100644 --- a/over/version.py +++ b/over/version.py @@ -4,5 +4,5 @@ major = 2 # VERSION_MAJOR_IDENTIFIER minor = 1 # VERSION_MINOR_IDENTIFIER # VERSION_LAST_MM 2.1 -patch = 4 # VERSION_PATCH_IDENTIFIER -str = "2.1.4" # VERSION_STRING_IDENTIFIER +patch = 5 # VERSION_PATCH_IDENTIFIER +str = "2.1.5" # VERSION_STRING_IDENTIFIER