From 4f1535c7686fe128b135795123b934b2f28263cd Mon Sep 17 00:00:00 2001 From: Caleb Maclennan Date: Thu, 16 Apr 2020 17:54:08 +0300 Subject: [PATCH 01/15] Refactor exclusive argument check using argparse's builtin functionality --- lib/fdiff/__main__.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/fdiff/__main__.py b/lib/fdiff/__main__.py index f413148..f7517f7 100644 --- a/lib/fdiff/__main__.py +++ b/lib/fdiff/__main__.py @@ -45,13 +45,14 @@ def run(argv): parser.add_argument( "-l", "--lines", type=int, default=3, help="Number of context lines (default 3)" ) - parser.add_argument( + filters = parser.add_mutually_exclusive_group() + filters.add_argument( "--include", type=str, default=None, help="Comma separated list of tables to include", ) - parser.add_argument( + filters.add_argument( "--exclude", type=str, default=None, @@ -74,17 +75,6 @@ def run(argv): # # ///////////////////////////////////////////////////////// - # ---------------------------------- - # Incompatible argument validations - # ---------------------------------- - # --include and --exclude are mutually exclusive options - if args.include and args.exclude: - sys.stderr.write( - f"[*] Error: --include and --exclude are mutually exclusive options. " - f"Please use ONLY one of these options in your command.{os.linesep}" - ) - sys.exit(1) - # ------------------------------- # File path argument validations # ------------------------------- From fda7c1986ffba833f8bb118671392ceca05bf9c2 Mon Sep 17 00:00:00 2001 From: Caleb Maclennan Date: Thu, 16 Apr 2020 18:18:50 +0300 Subject: [PATCH 02/15] Fix bug, wrong variable being tested --- lib/fdiff/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fdiff/__main__.py b/lib/fdiff/__main__.py index f7517f7..645d840 100644 --- a/lib/fdiff/__main__.py +++ b/lib/fdiff/__main__.py @@ -84,7 +84,7 @@ def run(argv): f"[*] ERROR: The file path '{args.PREFILE}' can not be found.{os.linesep}" ) sys.exit(1) - if not args.PREFILE.startswith("http") and not file_exists(args.POSTFILE): + if not args.POSTFILE.startswith("http") and not file_exists(args.POSTFILE): sys.stderr.write( f"[*] ERROR: The file path '{args.POSTFILE}' can not be found.{os.linesep}" ) From ce9ab9859b3666cc0c58c7d5f3445160635ff7d4 Mon Sep 17 00:00:00 2001 From: Caleb Maclennan Date: Thu, 16 Apr 2020 01:02:29 +0300 Subject: [PATCH 03/15] Add --git argument with 7 inputs --- lib/fdiff/__main__.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/fdiff/__main__.py b/lib/fdiff/__main__.py index 645d840..7112ae3 100644 --- a/lib/fdiff/__main__.py +++ b/lib/fdiff/__main__.py @@ -35,6 +35,7 @@ def run(argv): description="An OpenType table diff tool for fonts." ) parser.add_argument("--version", action="version", version=f"fdiff v{__version__}") + parser.add_argument("--git", type=str, nargs=7, action="git", help="Act as a diff driver for git (takes 7 parameters)") parser.add_argument( "-c", "--color", @@ -107,6 +108,11 @@ def run(argv): # optimization use use_mp = not args.nomp + if args.git: + pass + # TODO: catch 2nd & 5th parameters and stuff them in args.PREFILE and + # args.POSTFILE respectively + if args.external: # ------------------------------ # External executable tool diff From ac39bd8749a5561f0070072260848b02bcc60c49 Mon Sep 17 00:00:00 2001 From: Caleb Maclennan Date: Thu, 16 Apr 2020 17:21:46 +0300 Subject: [PATCH 04/15] Catch parameters from --git and stuff them in as pre/post file args --- lib/fdiff/__main__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/fdiff/__main__.py b/lib/fdiff/__main__.py index 7112ae3..d796d10 100644 --- a/lib/fdiff/__main__.py +++ b/lib/fdiff/__main__.py @@ -35,7 +35,7 @@ def run(argv): description="An OpenType table diff tool for fonts." ) parser.add_argument("--version", action="version", version=f"fdiff v{__version__}") - parser.add_argument("--git", type=str, nargs=7, action="git", help="Act as a diff driver for git (takes 7 parameters)") + parser.add_argument("--git", type=str, nargs=7, help="Act as a diff driver for git (takes 7 parameters)") parser.add_argument( "-c", "--color", @@ -65,11 +65,16 @@ def run(argv): "--nomp", action="store_true", help="Do not use multi process optimizations" ) parser.add_argument("--external", type=str, help="Run external diff tool command") - parser.add_argument("PREFILE", help="Font file path/URL 1") - parser.add_argument("POSTFILE", help="Font file path/URL 2") + # parser.add_argument("PREFILE", help="Font file path/URL 1") + # parser.add_argument("POSTFILE", help="Font file path/URL 2") args = parser.parse_args(argv) + if args.git: + print(args.git) + args.PREFILE = args.git[1] + args.POSTFILE = args.git[4] + # ///////////////////////////////////////////////////////// # # Validations @@ -108,11 +113,6 @@ def run(argv): # optimization use use_mp = not args.nomp - if args.git: - pass - # TODO: catch 2nd & 5th parameters and stuff them in args.PREFILE and - # args.POSTFILE respectively - if args.external: # ------------------------------ # External executable tool diff From 6f67b99cd27b0ee59b89d462e017d5d18ab4d001 Mon Sep 17 00:00:00 2001 From: Caleb Maclennan Date: Thu, 16 Apr 2020 17:54:35 +0300 Subject: [PATCH 05/15] Split argument processing into two steps --- lib/fdiff/__main__.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/lib/fdiff/__main__.py b/lib/fdiff/__main__.py index d796d10..75187a5 100644 --- a/lib/fdiff/__main__.py +++ b/lib/fdiff/__main__.py @@ -35,7 +35,6 @@ def run(argv): description="An OpenType table diff tool for fonts." ) parser.add_argument("--version", action="version", version=f"fdiff v{__version__}") - parser.add_argument("--git", type=str, nargs=7, help="Act as a diff driver for git (takes 7 parameters)") parser.add_argument( "-c", "--color", @@ -65,15 +64,23 @@ def run(argv): "--nomp", action="store_true", help="Do not use multi process optimizations" ) parser.add_argument("--external", type=str, help="Run external diff tool command") + + parser.add_argument("--git", type=str, nargs=7, help="Act as a diff driver for git (takes 7 parameters)") # parser.add_argument("PREFILE", help="Font file path/URL 1") # parser.add_argument("POSTFILE", help="Font file path/URL 2") - args = parser.parse_args(argv) + args, positionals = parser.parse_known_args(argv) + inputs = argparse.Namespace() if args.git: - print(args.git) - args.PREFILE = args.git[1] - args.POSTFILE = args.git[4] + inputs.PREFILE = args.git[1] + inputs.POSTFILE = args.git[4] + else: + inputparser = argparse.ArgumentParser() + inputparser.add_argument("PREFILE", help="Font file path/URL 1") + inputparser.add_argument("POSTFILE", help="Font file path/URL 2") + inputparser.parse_args(positionals, namespace=inputs) + # ///////////////////////////////////////////////////////// # @@ -85,14 +92,14 @@ def run(argv): # File path argument validations # ------------------------------- - if not args.PREFILE.startswith("http") and not file_exists(args.PREFILE): + if not inputs.PREFILE.startswith("http") and not file_exists(inputs.PREFILE): sys.stderr.write( - f"[*] ERROR: The file path '{args.PREFILE}' can not be found.{os.linesep}" + f"[*] ERROR: The file path '{inputs.PREFILE}' can not be found.{os.linesep}" ) sys.exit(1) - if not args.POSTFILE.startswith("http") and not file_exists(args.POSTFILE): + if not inputs.POSTFILE.startswith("http") and not file_exists(inputs.POSTFILE): sys.stderr.write( - f"[*] ERROR: The file path '{args.POSTFILE}' can not be found.{os.linesep}" + f"[*] ERROR: The file path '{inputs.POSTFILE}' can not be found.{os.linesep}" ) sys.exit(1) @@ -134,8 +141,8 @@ def run(argv): try: diff = external_diff( args.external, - args.PREFILE, - args.POSTFILE, + inputs.PREFILE, + inputs.POSTFILE, include_tables=include_list, exclude_tables=exclude_list, use_multiprocess=use_mp, @@ -160,8 +167,8 @@ def run(argv): # perform the unified diff analysis try: diff = u_diff( - args.PREFILE, - args.POSTFILE, + inputs.PREFILE, + inputs.POSTFILE, context_lines=args.lines, include_tables=include_list, exclude_tables=exclude_list, From efbbeafebc64f67487b702dd77a54c9b91f2c2d1 Mon Sep 17 00:00:00 2001 From: Caleb Maclennan Date: Thu, 16 Apr 2020 18:35:26 +0300 Subject: [PATCH 06/15] Allow usage on named pipes, not just ordinary files --- lib/fdiff/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fdiff/utils.py b/lib/fdiff/utils.py index 3b2aec6..62b90ed 100644 --- a/lib/fdiff/utils.py +++ b/lib/fdiff/utils.py @@ -7,7 +7,7 @@ def file_exists(path): """Validates file path as existing local file""" - return os.path.isfile(path) + return os.path.exists(path) def get_file_modtime(path): From e69f9ecc6f3cf8f06c86bd5e34fa03dbeafe3c23 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 19 Apr 2020 18:51:04 -0400 Subject: [PATCH 07/15] [main] formatting --- lib/fdiff/__main__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/fdiff/__main__.py b/lib/fdiff/__main__.py index 75187a5..f1bc778 100644 --- a/lib/fdiff/__main__.py +++ b/lib/fdiff/__main__.py @@ -65,7 +65,12 @@ def run(argv): ) parser.add_argument("--external", type=str, help="Run external diff tool command") - parser.add_argument("--git", type=str, nargs=7, help="Act as a diff driver for git (takes 7 parameters)") + parser.add_argument( + "--git", + type=str, + nargs=7, + help="Act as a diff driver for git (takes 7 parameters)", + ) # parser.add_argument("PREFILE", help="Font file path/URL 1") # parser.add_argument("POSTFILE", help="Font file path/URL 2") @@ -81,7 +86,6 @@ def run(argv): inputparser.add_argument("POSTFILE", help="Font file path/URL 2") inputparser.parse_args(positionals, namespace=inputs) - # ///////////////////////////////////////////////////////// # # Validations From c7ce54f3e3860212deb89f00034b48d9ef815c4d Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 19 Apr 2020 19:17:30 -0400 Subject: [PATCH 08/15] [main] update help string to indicate seven arg positions for --git flag --- lib/fdiff/__main__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/fdiff/__main__.py b/lib/fdiff/__main__.py index f1bc778..6150a37 100644 --- a/lib/fdiff/__main__.py +++ b/lib/fdiff/__main__.py @@ -70,6 +70,15 @@ def run(argv): type=str, nargs=7, help="Act as a diff driver for git (takes 7 parameters)", + metavar=( + "PATH", + "OLD-FILE", + "OLD-HEX", + "OLD-MODE", + "NEW-FILE", + "NEW-HEX", + "NEW-MODE", + ), ) # parser.add_argument("PREFILE", help="Font file path/URL 1") # parser.add_argument("POSTFILE", help="Font file path/URL 2") From c5514988e39e8c51839f80135e5204d3f3f90ea2 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 19 Apr 2020 19:55:25 -0400 Subject: [PATCH 09/15] fix stderr unit test for message that changed with transition to new approach for exclusive args --- tests/test_main.py | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/tests/test_main.py b/tests/test_main.py index 999d0dc..bb24709 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -9,12 +9,24 @@ ROBOTO_BEFORE_PATH = os.path.join("tests", "testfiles", "Roboto-Regular.subset1.ttf") ROBOTO_AFTER_PATH = os.path.join("tests", "testfiles", "Roboto-Regular.subset2.ttf") -ROBOTO_UDIFF_EXPECTED_PATH = os.path.join("tests", "testfiles", "roboto_udiff_expected.txt") -ROBOTO_UDIFF_COLOR_EXPECTED_PATH = os.path.join("tests", "testfiles", "roboto_udiff_color_expected.txt") -ROBOTO_UDIFF_1CONTEXT_EXPECTED_PATH = os.path.join("tests", "testfiles", "roboto_udiff_1context_expected.txt") -ROBOTO_UDIFF_HEADONLY_EXPECTED_PATH = os.path.join("tests", "testfiles", "roboto_udiff_headonly_expected.txt") -ROBOTO_UDIFF_HEADPOSTONLY_EXPECTED_PATH = os.path.join("tests", "testfiles", "roboto_udiff_headpostonly_expected.txt") -ROBOTO_UDIFF_EXCLUDE_HEADPOST_EXPECTED_PATH = os.path.join("tests", "testfiles", "roboto_udiff_ex_headpost_expected.txt") +ROBOTO_UDIFF_EXPECTED_PATH = os.path.join( + "tests", "testfiles", "roboto_udiff_expected.txt" +) +ROBOTO_UDIFF_COLOR_EXPECTED_PATH = os.path.join( + "tests", "testfiles", "roboto_udiff_color_expected.txt" +) +ROBOTO_UDIFF_1CONTEXT_EXPECTED_PATH = os.path.join( + "tests", "testfiles", "roboto_udiff_1context_expected.txt" +) +ROBOTO_UDIFF_HEADONLY_EXPECTED_PATH = os.path.join( + "tests", "testfiles", "roboto_udiff_headonly_expected.txt" +) +ROBOTO_UDIFF_HEADPOSTONLY_EXPECTED_PATH = os.path.join( + "tests", "testfiles", "roboto_udiff_headpostonly_expected.txt" +) +ROBOTO_UDIFF_EXCLUDE_HEADPOST_EXPECTED_PATH = os.path.join( + "tests", "testfiles", "roboto_udiff_ex_headpost_expected.txt" +) ROBOTO_BEFORE_URL = "https://github.com/source-foundry/fdiff/raw/master/tests/testfiles/Roboto-Regular.subset1.ttf" ROBOTO_AFTER_URL = "https://github.com/source-foundry/fdiff/raw/master/tests/testfiles/Roboto-Regular.subset2.ttf" @@ -80,21 +92,32 @@ def test_main_filepath_validations_false_secondfont(capsys): # Mutually exclusive argument tests # + def test_main_include_exclude_defined_simultaneously(capsys): - args = ["--include", "head", "--exclude", "head", ROBOTO_BEFORE_PATH, ROBOTO_AFTER_PATH] + args = [ + "--include", + "head", + "--exclude", + "head", + ROBOTO_BEFORE_PATH, + ROBOTO_AFTER_PATH, + ] with pytest.raises(SystemExit) as exit_info: run(args) captured = capsys.readouterr() - assert captured.err.startswith("[*] Error: --include and --exclude are mutually exclusive options") - assert exit_info.value.code == 1 + assert captured.err.endswith( + "error: argument --exclude: not allowed with argument --include\n" + ) + assert exit_info.value.code == 2 # # Unified diff integration tests # + def test_main_run_unified_default_local_files_no_diff(capsys): """Test default behavior when there is no difference in font files under evaluation""" args = [ROBOTO_BEFORE_PATH, ROBOTO_BEFORE_PATH] From ac242bd156d4253bcca8854e7c8df1e8a7bcb44e Mon Sep 17 00:00:00 2001 From: Caleb Maclennan Date: Mon, 20 Apr 2020 18:12:04 +0300 Subject: [PATCH 10/15] Document usage as a git-driver in README Co-Authored-By: Chris Simpkins --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index 03ef84d..f066d73 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Looking for a high-level overview of OpenType table differences rather than low- - Display the first n lines of the diff output with the `--head` option - Display the last n lines of the diff output with the `--tail` option - Execute the diff with an external diff tool using the `--external` option +- Use in Git as a diff-driver to be the default diff tool for fonts Run `fdiff --help` to view all available options. @@ -103,6 +104,30 @@ $ fdiff [OPTIONS] [PRE-FONT FILE URL] [POST-FONT FILE FILE PATH] ⭐ **Tip**: Remote git repository hosting services (like Github) support access to files on different git branches by URL. Use these repository branch URL to compare fonts across git branches in your repository. +#### As Git's diff driver for fonts + +Git can be configured to automatically use a specific tool to diff specific file types. These are the steps to use `fdiff` as the default diff output for font files. + +1. Tell Git how to run the tool. As with most git configuration options this may be set for a repository (`--local`), for your user (`--global`), or system (`--system`). We recommend setting this at the user level. Assuming `fdiff` is available in your path, this setting should do the trick: + + git config --global diff.fdiff.command 'fdiff -c --git' + + This will write it to your `$HOME/.gitconfig` file looking like this: + + ```gitconfig + [diff "fdiff"] + command = fdiff -c --git + ``` + + Of course you may also edit it there. + +2. Tell Git to use that specific tool for supported file types. This may also be done at multiple places. Each repository may have it's own `.gitattributes` file, a user may have one setting global defaults in `$XDG_HOME/git/attributes`, or there may be a system wide default file. Wherever you choose to place this, the lines are the same: + + ```gitattributes + .otf diff=fdiff + .ttf diff=fdiff + ``` + ### Options #### Color diffs From e44e90dacc89081f622c570e7f8d1c1f22c040eb Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 3 May 2020 18:41:39 -0400 Subject: [PATCH 11/15] add support for os.path.exists when new --git flag is used We need to be able to accept args such as /dev/null and /dev/stdin and these do not meet truth test with os.path.isfile. See https://github.com/source-foundry/fdiff/pull/48#discussion_r410430929 for discussion --- lib/fdiff/__main__.py | 17 ++++++++++++++--- lib/fdiff/utils.py | 10 +++++++--- tests/test_utils.py | 33 ++++++++++++++++++++++++++++----- tests/test_utils_unix_only.py | 13 +++++++++++++ 4 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 tests/test_utils_unix_only.py diff --git a/lib/fdiff/__main__.py b/lib/fdiff/__main__.py index 6150a37..60de656 100644 --- a/lib/fdiff/__main__.py +++ b/lib/fdiff/__main__.py @@ -8,7 +8,7 @@ from fdiff.color import color_unified_diff_line from fdiff.diff import external_diff, u_diff from fdiff.textiter import head, tail -from fdiff.utils import file_exists, get_tables_argument_list +from fdiff.utils import path_exists, get_tables_argument_list def main(): # pragma: no cover @@ -86,9 +86,16 @@ def run(argv): args, positionals = parser.parse_known_args(argv) inputs = argparse.Namespace() + include_dir_paths = False if args.git: inputs.PREFILE = args.git[1] inputs.POSTFILE = args.git[4] + # If the --git flag is used, we need to accept arguments + # that do not meet the definition of a file path using + # os.path.exists instead of os.path.isfile + # See https://github.com/source-foundry/fdiff/pull/48#discussion_r410424497 + # for additional details + include_dir_paths = True else: inputparser = argparse.ArgumentParser() inputparser.add_argument("PREFILE", help="Font file path/URL 1") @@ -105,12 +112,16 @@ def run(argv): # File path argument validations # ------------------------------- - if not inputs.PREFILE.startswith("http") and not file_exists(inputs.PREFILE): + if not inputs.PREFILE.startswith("http") and not path_exists( + inputs.PREFILE, include_dir_paths=include_dir_paths + ): sys.stderr.write( f"[*] ERROR: The file path '{inputs.PREFILE}' can not be found.{os.linesep}" ) sys.exit(1) - if not inputs.POSTFILE.startswith("http") and not file_exists(inputs.POSTFILE): + if not inputs.POSTFILE.startswith("http") and not path_exists( + inputs.POSTFILE, include_dir_paths=include_dir_paths + ): sys.stderr.write( f"[*] ERROR: The file path '{inputs.POSTFILE}' can not be found.{os.linesep}" ) diff --git a/lib/fdiff/utils.py b/lib/fdiff/utils.py index 62b90ed..6edfbaa 100644 --- a/lib/fdiff/utils.py +++ b/lib/fdiff/utils.py @@ -5,9 +5,13 @@ from datetime import datetime, timezone -def file_exists(path): - """Validates file path as existing local file""" - return os.path.exists(path) +def path_exists(path, include_dir_paths=False): + """Validates existing paths. The include_dir_paths parameter + toggles acceptance of dir paths in addition to file paths.""" + if include_dir_paths: + return os.path.exists(path) + else: + return os.path.isfile(path) def get_file_modtime(path): diff --git a/tests/test_utils.py b/tests/test_utils.py index 8927172..690a307 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,17 +1,40 @@ import os import re -from fdiff.utils import get_file_modtime, get_tables_argument_list, file_exists +from fdiff.utils import get_file_modtime, get_tables_argument_list, path_exists import pytest -def test_file_exists_true(): - assert file_exists(os.path.join("tests", "testfiles", "test.txt")) is True +def test_path_exists_default_true(): + assert ( + path_exists( + os.path.join("tests", "testfiles", "test.txt"), include_dir_paths=False + ) + is True + ) -def test_file_exists_false(): - assert file_exists(os.path.join("tests", "testfiles", "bogus.jpg")) is False +def test_path_exists_default_false(): + assert ( + path_exists( + os.path.join("tests", "testfiles", "bogus.jpg"), include_dir_paths=False + ) + is False + ) + + +def test_path_exists_default_dirpath_fails(): + assert ( + path_exists(os.path.join("tests", "testfiles"), include_dir_paths=False) + is False + ) + + +def test_path_exists_default_dirpath_toggle_succeeds(): + assert ( + path_exists(os.path.join("tests", "testfiles"), include_dir_paths=True) is True + ) def test_get_file_modtime(): diff --git a/tests/test_utils_unix_only.py b/tests/test_utils_unix_only.py new file mode 100644 index 0000000..2304172 --- /dev/null +++ b/tests/test_utils_unix_only.py @@ -0,0 +1,13 @@ +import os +import sys + +import pytest + +from fdiff.utils import path_exists + +if sys.platform.startswith("win"): + pytest.skip("skipping Unix only tests", allow_module_level=True) + + +def test_path_exists_default_dirpath_toggle_succeeds(): + assert path_exists("/dev/null", include_dir_paths=True) is True From 3a1f283055f0665d5eb017953f25c0a2e7b2dd0a Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 3 May 2020 19:46:08 -0400 Subject: [PATCH 12/15] [README.md] use wildcards in gitattribute configuration with `--git` option --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f066d73..3eda7e6 100644 --- a/README.md +++ b/README.md @@ -124,8 +124,8 @@ Git can be configured to automatically use a specific tool to diff specific file 2. Tell Git to use that specific tool for supported file types. This may also be done at multiple places. Each repository may have it's own `.gitattributes` file, a user may have one setting global defaults in `$XDG_HOME/git/attributes`, or there may be a system wide default file. Wherever you choose to place this, the lines are the same: ```gitattributes - .otf diff=fdiff - .ttf diff=fdiff + *.otf diff=fdiff + *.ttf diff=fdiff ``` ### Options From 534eef629f9ac3ef049e924ccf2c90d01feeac22 Mon Sep 17 00:00:00 2001 From: Caleb Maclennan Date: Mon, 20 Apr 2020 18:12:04 +0300 Subject: [PATCH 13/15] Document usage as a git-driver in README Co-Authored-By: Chris Simpkins --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index 03ef84d..036b978 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ Looking for a high-level overview of OpenType table differences rather than low- - Display the first n lines of the diff output with the `--head` option - Display the last n lines of the diff output with the `--tail` option - Execute the diff with an external diff tool using the `--external` option +- Use in Git as a diff-driver to be the default diff tool for fonts Run `fdiff --help` to view all available options. @@ -103,6 +104,34 @@ $ fdiff [OPTIONS] [PRE-FONT FILE URL] [POST-FONT FILE FILE PATH] ⭐ **Tip**: Remote git repository hosting services (like Github) support access to files on different git branches by URL. Use these repository branch URL to compare fonts across git branches in your repository. +#### As Git's diff driver for fonts + +Git can be configured to automatically use a specific tool to diff specific file types. These are the steps to use `fdiff` as the default diff output for font files. + +1. Tell Git that the tool is available and how to run it. As with most git configuration options this may be set for a repository (`--local`), for your user (`--global`), or system (`--system`). We recommend setting this at the repository or user level. Assuming `fdiff` is available in your path, this setting should do the trick: + + # Repository level + git config --local diff.fdiff.command 'fdiff -c --git' + + # User level + git config --global diff.fdiff.command 'fdiff -c --git' + + This will write something like the following to either the current repository's `.git/config` or your user's `$HOME/.gitconfig` file looking like this: + + ```gitconfig + [diff "fdiff"] + command = fdiff -c --git + ``` + + Of course you may also edit the appropriate file and place that configuration manually. + +2. Tell Git to actually use that specific tool for supported file types. This may also be done at multiple places. Each repository may have it's own `.gitattributes` file, a user may have one setting global defaults in `$XDG_HOME/git/attributes`, or there may be a system wide default file. Wherever you choose to place this, the lines are the same: + + ```gitattributes + .otf diff=fdiff + .ttf diff=fdiff + ``` + ### Options #### Color diffs From 534844780bebbdb8839574bccf27b364b672424d Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 3 May 2020 18:41:39 -0400 Subject: [PATCH 14/15] add support for os.path.exists when new --git flag is used We need to be able to accept args such as /dev/null and /dev/stdin and these do not meet truth test with os.path.isfile. See https://github.com/source-foundry/fdiff/pull/48#discussion_r410430929 for discussion --- lib/fdiff/__main__.py | 17 ++++++++++++++--- lib/fdiff/utils.py | 10 +++++++--- tests/test_utils.py | 33 ++++++++++++++++++++++++++++----- tests/test_utils_unix_only.py | 13 +++++++++++++ 4 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 tests/test_utils_unix_only.py diff --git a/lib/fdiff/__main__.py b/lib/fdiff/__main__.py index 6150a37..60de656 100644 --- a/lib/fdiff/__main__.py +++ b/lib/fdiff/__main__.py @@ -8,7 +8,7 @@ from fdiff.color import color_unified_diff_line from fdiff.diff import external_diff, u_diff from fdiff.textiter import head, tail -from fdiff.utils import file_exists, get_tables_argument_list +from fdiff.utils import path_exists, get_tables_argument_list def main(): # pragma: no cover @@ -86,9 +86,16 @@ def run(argv): args, positionals = parser.parse_known_args(argv) inputs = argparse.Namespace() + include_dir_paths = False if args.git: inputs.PREFILE = args.git[1] inputs.POSTFILE = args.git[4] + # If the --git flag is used, we need to accept arguments + # that do not meet the definition of a file path using + # os.path.exists instead of os.path.isfile + # See https://github.com/source-foundry/fdiff/pull/48#discussion_r410424497 + # for additional details + include_dir_paths = True else: inputparser = argparse.ArgumentParser() inputparser.add_argument("PREFILE", help="Font file path/URL 1") @@ -105,12 +112,16 @@ def run(argv): # File path argument validations # ------------------------------- - if not inputs.PREFILE.startswith("http") and not file_exists(inputs.PREFILE): + if not inputs.PREFILE.startswith("http") and not path_exists( + inputs.PREFILE, include_dir_paths=include_dir_paths + ): sys.stderr.write( f"[*] ERROR: The file path '{inputs.PREFILE}' can not be found.{os.linesep}" ) sys.exit(1) - if not inputs.POSTFILE.startswith("http") and not file_exists(inputs.POSTFILE): + if not inputs.POSTFILE.startswith("http") and not path_exists( + inputs.POSTFILE, include_dir_paths=include_dir_paths + ): sys.stderr.write( f"[*] ERROR: The file path '{inputs.POSTFILE}' can not be found.{os.linesep}" ) diff --git a/lib/fdiff/utils.py b/lib/fdiff/utils.py index 62b90ed..6edfbaa 100644 --- a/lib/fdiff/utils.py +++ b/lib/fdiff/utils.py @@ -5,9 +5,13 @@ from datetime import datetime, timezone -def file_exists(path): - """Validates file path as existing local file""" - return os.path.exists(path) +def path_exists(path, include_dir_paths=False): + """Validates existing paths. The include_dir_paths parameter + toggles acceptance of dir paths in addition to file paths.""" + if include_dir_paths: + return os.path.exists(path) + else: + return os.path.isfile(path) def get_file_modtime(path): diff --git a/tests/test_utils.py b/tests/test_utils.py index 8927172..690a307 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,17 +1,40 @@ import os import re -from fdiff.utils import get_file_modtime, get_tables_argument_list, file_exists +from fdiff.utils import get_file_modtime, get_tables_argument_list, path_exists import pytest -def test_file_exists_true(): - assert file_exists(os.path.join("tests", "testfiles", "test.txt")) is True +def test_path_exists_default_true(): + assert ( + path_exists( + os.path.join("tests", "testfiles", "test.txt"), include_dir_paths=False + ) + is True + ) -def test_file_exists_false(): - assert file_exists(os.path.join("tests", "testfiles", "bogus.jpg")) is False +def test_path_exists_default_false(): + assert ( + path_exists( + os.path.join("tests", "testfiles", "bogus.jpg"), include_dir_paths=False + ) + is False + ) + + +def test_path_exists_default_dirpath_fails(): + assert ( + path_exists(os.path.join("tests", "testfiles"), include_dir_paths=False) + is False + ) + + +def test_path_exists_default_dirpath_toggle_succeeds(): + assert ( + path_exists(os.path.join("tests", "testfiles"), include_dir_paths=True) is True + ) def test_get_file_modtime(): diff --git a/tests/test_utils_unix_only.py b/tests/test_utils_unix_only.py new file mode 100644 index 0000000..2304172 --- /dev/null +++ b/tests/test_utils_unix_only.py @@ -0,0 +1,13 @@ +import os +import sys + +import pytest + +from fdiff.utils import path_exists + +if sys.platform.startswith("win"): + pytest.skip("skipping Unix only tests", allow_module_level=True) + + +def test_path_exists_default_dirpath_toggle_succeeds(): + assert path_exists("/dev/null", include_dir_paths=True) is True From 462454439216ba0ea94954db99198c3f1477a49f Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 3 May 2020 19:46:08 -0400 Subject: [PATCH 15/15] [README.md] use wildcards in gitattribute configuration with `--git` option --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 036b978..a927cd1 100644 --- a/README.md +++ b/README.md @@ -128,8 +128,8 @@ Git can be configured to automatically use a specific tool to diff specific file 2. Tell Git to actually use that specific tool for supported file types. This may also be done at multiple places. Each repository may have it's own `.gitattributes` file, a user may have one setting global defaults in `$XDG_HOME/git/attributes`, or there may be a system wide default file. Wherever you choose to place this, the lines are the same: ```gitattributes - .otf diff=fdiff - .ttf diff=fdiff + *.otf diff=fdiff + *.ttf diff=fdiff ``` ### Options