From 64a4f827245e55cdc8ae907417c5ddcb2324c1ca Mon Sep 17 00:00:00 2001 From: poikilos <7557867+poikilos@users.noreply.github.com> Date: Mon, 26 Feb 2024 22:49:12 -0500 Subject: [PATCH] Fix: Check if file is binary and use appropriate diff option(s). Fix: Add "B" (ignore changes where all lines are blank) in mtpatches. --- pyenliven/mtpatches.py | 123 ++++++++++++++++++++++++++++++----------- pyproject.toml | 3 + 2 files changed, 93 insertions(+), 33 deletions(-) diff --git a/pyenliven/mtpatches.py b/pyenliven/mtpatches.py index e373140..11ed97e 100755 --- a/pyenliven/mtpatches.py +++ b/pyenliven/mtpatches.py @@ -17,6 +17,7 @@ import shlex import shutil import sys import subprocess +from binaryornot.check import is_binary class Fore: @@ -106,6 +107,65 @@ def diff_only_head(base, head, more_1char_args=None, log_level=0): ) +def validate_diff_args(more_1char_args): + """Check the given arguments format for diff or fc + and convert to a list if has spaces. + + Args: + more_1char_args (Union[str,list[str],tuple[str]]): Arguments for + the diff tool. + + Raises: + TypeError: Not list, tuple, nor str. + ValueError: is blank str + ValueError: is just a '-' or '/' + ValueError: starts with '/' when using diff, or '-' when not + + Returns: + list(str): Arguments (usually only one, such as "-wbB" for diff, + or for FC, "/B" for binary and "/W" to ignore whitespace). + In other words, return the original more_1char_args or + split it into a list. + """ + diff_bin_name = DIFF_CMD_PARTS[0].lower() + opener_for_1char = "-" if (diff_bin_name == "fc") else "/" + # ^ even on Windows, diff would take "-" (fc takes "/" before options) + + if not isinstance(more_1char_args, (list, tuple)): + if not isinstance(more_1char_args, str): + raise TypeError("more_1char_args must be list, tuple, or str") + more_1char_args = more_1char_args.strip() + if len(more_1char_args) == 0: + raise ValueError("more_1char_args for \"{}\" was empty str" + " but should be None or an args string" + " starting with '{}'" + "".format(diff_bin_name, opener_for_1char)) + elif len(more_1char_args) == 1: + if more_1char_args in ("-", "/"): + raise ValueError("more_1char_args was only '{}' with no args" + "".format(more_1char_args)) + # Allow string too (split if has space, otherwise convert + # to 1-long list still) + # Split into tuple and recurse: + return validate_diff_args(more_1char_args.strip().split()) + + for arg in more_1char_args: + if not arg.startswith(opener_for_1char): + bin_msg = "diff" + if diff_bin_name != "diff": + bin_msg = ( + "diff wasn't in your PATH so {} is being used, but it" + "".format(diff_bin_name) + ) + raise ValueError( + "{} only accepts options" + " starting with '{}'" + " (got \"{}\")".format(bin_msg, opener_for_1char, + arg) + ) + return more_1char_args # It is a list now. + + def _diff_only_head(base, head, rel=None, more_1char_args=None, depth=0, log_level=0): """Compare two directories or files. @@ -127,39 +187,6 @@ def _diff_only_head(base, head, rel=None, more_1char_args=None, depth=0, raise FileNotFoundError("There is no {} in your PATH." "".format(ok_commands)) args_1char = None - is_binary = False - if more_1char_args is None: - if platform.system() == "Windows": - if not is_binary: - args_1char = ["/W"] # ignore whitespace - else: - args_1char = ["/B"] # binary comparison - else: - if not is_binary: - args_1char = ["-wb"] - # -b: ignore changes in the amount of whitespace - # -w: ignore all white space (better for code since - # "a=1" is same as "a == 1", otherwise tokenizing - # the specific language would be necessary) - # -a: --text treat all files as text and - # --strip-trailing-cr strip trailing carriage - # return - # -E, --ignore-tab-expansion - else: - if more_1char_args.startswith("-"): - if DIFF_CMD_PARTS[0].lower() == "fc": - raise ValueError( - "diff wasn't in your PATH so fc is being used," - " but it only accepts options" - " starting with '/'" - " (got \"{}\")".format(more_1char_args) - ) - if isinstance(more_1char_args, (list, tuple)): - args_1char = more_1char_args - else: - # allow string too (split if has space, otherwise convert - # to 1-long list still) - args_1char = more_1char_args.strip().split() whats = [None, None] if rel: @@ -169,6 +196,7 @@ def _diff_only_head(base, head, rel=None, more_1char_args=None, depth=0, base_path = base head_path = head paths = (base_path, head_path) + names = ("base", "head") for i, path in enumerate(paths): if not os.path.exists(path): @@ -223,6 +251,35 @@ def _diff_only_head(base, head, rel=None, more_1char_args=None, depth=0, 'rel': rel, 'new': True, }] + + + head_is_binary = is_binary(head_path) + + if more_1char_args is None: + if platform.system() == "Windows": + if not head_is_binary: + args_1char = ["/W"] # ignore whitespace + else: + args_1char = ["/B"] # binary comparison + else: + if not head_is_binary: + args_1char = ["-wbB"] + # -b: ignore changes in the amount of whitespace + # -w: ignore all white space (better for code since + # "a=1" is same as "a == 1", otherwise tokenizing + # the specific language would be necessary) + # -a: --text treat all files as text and + # --strip-trailing-cr strip trailing carriage + # return + # [doesn't do enough if has extra blank line, + # still says different, so use -B instead] + # -B, ignore changes where all lines are blank + # -E, --ignore-tab-expansion + # else None (just use diff without any options) + else: + args_1char = validate_diff_args(more_1char_args) + del more_1char_args + # echo0("^ in base") cmd_parts = DIFF_CMD_PARTS.copy() # echo0("args_1char={}".format(args_1char)) diff --git a/pyproject.toml b/pyproject.toml index 2e20b9c..3111684 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,9 @@ version = "0.2.0" authors = [ { name="Jake Gustafson", email="7557867+poikilos@users.noreply.github.com" }, ] +dependencies = [ + "binaryornot", # only for mtpatches +] # requires-python = ">=3.3" # TODO: (shutil.which requires 3.3 but) may need newer version, # & other code may run on older version