pyDiffTools 0.1.8__tar.gz → 0.1.11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. {pydifftools-0.1.8/pyDiffTools.egg-info → pydifftools-0.1.11}/PKG-INFO +1 -1
  2. {pydifftools-0.1.8 → pydifftools-0.1.11/pyDiffTools.egg-info}/PKG-INFO +1 -1
  3. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/command_line.py +68 -1
  4. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/flowchart/graph.py +66 -2
  5. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/flowchart/watch_graph.py +91 -24
  6. {pydifftools-0.1.8 → pydifftools-0.1.11}/pyproject.toml +1 -1
  7. {pydifftools-0.1.8 → pydifftools-0.1.11}/LICENSE.md +0 -0
  8. {pydifftools-0.1.8 → pydifftools-0.1.11}/MANIFEST.in +0 -0
  9. {pydifftools-0.1.8 → pydifftools-0.1.11}/README.rst +0 -0
  10. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/_quarto.yml +0 -0
  11. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/__version__.txt +0 -0
  12. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/1a724af72b16f5a9e607e12b1c721645/base.ipynb +0 -0
  13. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/1b28fc9daac9081847e5161b2c546f8a/base.ipynb +0 -0
  14. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/231f64eee282fa225d1104935cf80a24/base.ipynb +0 -0
  15. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/26e56f6b0ff54851a45145157f2f0dc4/base.ipynb +0 -0
  16. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/311fabd7029ffd050d056e2f316eb50f/base.ipynb +0 -0
  17. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/57da2021e5b156ac3adf01398201c723/base.ipynb +0 -0
  18. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/62b24ea7da75011d92b0f8924faa208d/base.ipynb +0 -0
  19. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/7f1b20d69d889514ab5d1cc92e3cb14f/base.ipynb +0 -0
  20. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/86f74c8c54a87ff892d9b15dd714e8f0/base.ipynb +0 -0
  21. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/88893b4234eac2945d9d6cb2e277f186/base.ipynb +0 -0
  22. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/9a40046ada6f582ee34af00fbdbfb417/base.ipynb +0 -0
  23. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/a1bf4d270d0641ff41faf1d7cce3439a/base.ipynb +0 -0
  24. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/a3789f7d9585a781f2a1c60ce95ff10d/base.ipynb +0 -0
  25. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/be46ecba858d39ad5f0c46902ddf1c02/base.ipynb +0 -0
  26. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/d0cbc57a12f2ccce710a5afd04cc05e7/base.ipynb +0 -0
  27. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/d3e12d320b14228f701231ae32ddd7dd/base.ipynb +0 -0
  28. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/eb434f61555438d020a6970a5dbf9ee8/base.ipynb +0 -0
  29. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/f6b0e73aa7fa029134665d4dde57e096/base.ipynb +0 -0
  30. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/f719a6e4ff09873cb0ffb06ec9d232f9/base.ipynb +0 -0
  31. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/executed/feeee244a7ce3d60e1a227eb604df823/base.ipynb +0 -0
  32. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/.jupyter_cache/global.db +0 -0
  33. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/example.qmd +0 -0
  34. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/example.tex +0 -0
  35. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/index.qmd +0 -0
  36. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/.jupyter_cache/__version__.txt +0 -0
  37. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/.jupyter_cache/executed/ca90e4df5f4f0583df6554156a68dc7f/base.ipynb +0 -0
  38. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/.jupyter_cache/global.db +0 -0
  39. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/end_vs_he_sketch.jpg +0 -0
  40. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/independent.qmd +0 -0
  41. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/index.qmd +0 -0
  42. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/tasks.qmd +0 -0
  43. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/test_include.qmd +0 -0
  44. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/subproject1/tryforerror.qmd +0 -0
  45. {pydifftools-0.1.8 → pydifftools-0.1.11}/example_notebook/project1/test_include.qmd +0 -0
  46. {pydifftools-0.1.8 → pydifftools-0.1.11}/pyDiffTools.egg-info/SOURCES.txt +0 -0
  47. {pydifftools-0.1.8 → pydifftools-0.1.11}/pyDiffTools.egg-info/dependency_links.txt +0 -0
  48. {pydifftools-0.1.8 → pydifftools-0.1.11}/pyDiffTools.egg-info/entry_points.txt +0 -0
  49. {pydifftools-0.1.8 → pydifftools-0.1.11}/pyDiffTools.egg-info/requires.txt +0 -0
  50. {pydifftools-0.1.8 → pydifftools-0.1.11}/pyDiffTools.egg-info/top_level.txt +0 -0
  51. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/__init__.py +0 -0
  52. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/check_numbers.py +0 -0
  53. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/command_registry.py +0 -0
  54. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/comment_functions.py +0 -0
  55. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/continuous.py +0 -0
  56. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/copy_files.py +0 -0
  57. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/diff-doc.js +0 -0
  58. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/doc_contents.py +0 -0
  59. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/flowchart/__init__.py +0 -0
  60. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/flowchart/dot_to_yaml.py +0 -0
  61. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/html_comments.py +0 -0
  62. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/html_uncomments.py +0 -0
  63. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/match_spaces.py +0 -0
  64. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/notebook/__init__.py +0 -0
  65. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/notebook/fast_build.py +0 -0
  66. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/notebook/tex_to_qmd.py +0 -0
  67. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/onewordify.py +0 -0
  68. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/onewordify_undo.py +0 -0
  69. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/outline.py +0 -0
  70. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/rearrange_tex.py +0 -0
  71. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/searchacro.py +0 -0
  72. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/separate_comments.py +0 -0
  73. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/split_conflict.py +0 -0
  74. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/unseparate_comments.py +0 -0
  75. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/update_check.py +0 -0
  76. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/wrap_sentences.py +0 -0
  77. {pydifftools-0.1.8 → pydifftools-0.1.11}/pydifftools/xml2xlsx.vbs +0 -0
  78. {pydifftools-0.1.8 → pydifftools-0.1.11}/setup.cfg +0 -0
  79. {pydifftools-0.1.8 → pydifftools-0.1.11}/tests/test_rrng.py +0 -0
  80. {pydifftools-0.1.8 → pydifftools-0.1.11}/tests/test_tex_to_qmd.py +0 -0
  81. {pydifftools-0.1.8 → pydifftools-0.1.11}/tests/test_update_check.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyDiffTools
3
- Version: 0.1.8
3
+ Version: 0.1.11
4
4
  Summary: Diff tools
5
5
  Author: J M Franck
6
6
  License: Copyright (c) 2015, jmfranck
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyDiffTools
3
- Version: 0.1.8
3
+ Version: 0.1.11
4
4
  Summary: Diff tools
5
5
  Author: J M Franck
6
6
  License: Copyright (c) 2015, jmfranck
@@ -10,6 +10,7 @@ import re
10
10
  import nbformat
11
11
  import difflib
12
12
  import shutil
13
+ import importlib.util
13
14
  from pathlib import Path
14
15
  from . import (
15
16
  match_spaces,
@@ -26,11 +27,31 @@ from .copy_files import copy_image_files
26
27
  from .searchacro import replace_acros
27
28
  from .rearrange_tex import run as rearrange_tex_run
28
29
  from .flowchart.watch_graph import wgrph
30
+ from .flowchart.graph import load_graph_yaml
29
31
  from .notebook.tex_to_qmd import tex2qmd
30
32
  from .notebook.fast_build import qmdb, qmdinit
31
33
 
32
34
  from .command_registry import _COMMAND_SPECS, register_command
33
35
 
36
+ _ARGCOMPLETE_SPEC = importlib.util.find_spec("argcomplete")
37
+ if (
38
+ _ARGCOMPLETE_SPEC is not None
39
+ and _ARGCOMPLETE_SPEC.submodule_search_locations is not None
40
+ ):
41
+ _ARGCOMPLETE_COMPLETERS_SPEC = importlib.util.find_spec(
42
+ "argcomplete.completers"
43
+ )
44
+ else:
45
+ _ARGCOMPLETE_COMPLETERS_SPEC = None
46
+ if _ARGCOMPLETE_SPEC is not None:
47
+ import argcomplete
48
+ else:
49
+ argcomplete = None
50
+ if _ARGCOMPLETE_COMPLETERS_SPEC is not None:
51
+ from argcomplete.completers import FilesCompleter
52
+ else:
53
+ FilesCompleter = None
54
+
34
55
 
35
56
  def printed_exec(cmd):
36
57
  print("about to execute:\n", cmd)
@@ -57,6 +78,37 @@ def get_data(path):
57
78
  return os.path.join(_ROOT, path)
58
79
 
59
80
 
81
+ def wgrph_task_completer(prefix, parsed_args, **kwargs):
82
+ # Provide case-insensitive task name completions for wgrph -t.
83
+ if parsed_args is None or not hasattr(parsed_args, "yaml"):
84
+ return []
85
+ yaml_path = Path(parsed_args.yaml)
86
+ if not yaml_path.exists():
87
+ return []
88
+ try:
89
+ data = load_graph_yaml(str(yaml_path))
90
+ except Exception:
91
+ return []
92
+ if "nodes" not in data:
93
+ return []
94
+ prefix_lower = prefix.lower()
95
+ matches = []
96
+ for name in data["nodes"]:
97
+ if (
98
+ "style" in data["nodes"][name]
99
+ and data["nodes"][name]["style"] == "completed"
100
+ ):
101
+ continue
102
+ if name.lower().startswith(prefix_lower):
103
+ # Preserve case-insensitive matches even when the typed prefix
104
+ # doesn't match case so argcomplete still accepts the suggestion.
105
+ if name.startswith(prefix):
106
+ matches.append(name)
107
+ else:
108
+ matches.append(prefix + name[len(prefix) :])
109
+ return matches
110
+
111
+
60
112
  def recursive_include_search(directory, basename, does_it_input):
61
113
  with open(
62
114
  os.path.join(directory, basename + ".tex"), "r", encoding="utf-8"
@@ -704,7 +756,19 @@ def build_parser():
704
756
  for argument in arguments:
705
757
  flags = argument["flags"]
706
758
  kwargs = dict(argument["kwargs"])
707
- subparser.add_argument(*flags, **kwargs)
759
+ action = subparser.add_argument(*flags, **kwargs)
760
+ if (
761
+ FilesCompleter is not None
762
+ and name == "wgrph"
763
+ and action.dest == "yaml"
764
+ ):
765
+ # Provide YAML-only completions for the flowchart watcher.
766
+ action.completer = FilesCompleter(
767
+ allowednames=["*.yaml", "*.yml"]
768
+ )
769
+ if name == "wgrph" and action.dest == "t":
770
+ # Offer case-insensitive completions for incomplete task names.
771
+ action.completer = wgrph_task_completer
708
772
  subparser.set_defaults(_handler=spec["handler"])
709
773
  return parser
710
774
 
@@ -732,6 +796,9 @@ def main(argv=None):
732
796
  file=sys.stderr,
733
797
  )
734
798
  parser = build_parser()
799
+ if argcomplete is not None:
800
+ # Enable argcomplete integration when the dependency is available.
801
+ argcomplete.autocomplete(parser)
735
802
  if not argv:
736
803
  parser.print_help()
737
804
  return
@@ -324,7 +324,7 @@ def _node_text_with_due(node):
324
324
  # same visual emphasis used for overdue dates.
325
325
  if is_completed:
326
326
  due_color = "green"
327
- elif due_date > today_date and (due_date - today_date).days <= 7:
327
+ elif (due_date - today_date).days <= 7:
328
328
  due_color = "red"
329
329
  else:
330
330
  due_color = "orange"
@@ -565,6 +565,7 @@ def write_dot_from_yaml(
565
565
  order_by_date=False,
566
566
  old_data=None,
567
567
  validate_due_dates=False,
568
+ filter_task=None,
568
569
  ):
569
570
  data = load_graph_yaml(str(yaml_path), old_data=old_data)
570
571
  _normalize_graph_dates(data)
@@ -611,8 +612,71 @@ def write_dot_from_yaml(
611
612
  parents_to_check.append(
612
613
  (grandparent, path + [grandparent])
613
614
  )
615
+ data_for_dot = data
616
+ if filter_task is not None:
617
+ # Limit the rendered graph to incomplete ancestors of the target task.
618
+ if "nodes" not in data or filter_task not in data["nodes"]:
619
+ # Allow case-insensitive task lookup to align with CLI completion.
620
+ matches = [
621
+ name
622
+ for name in data["nodes"]
623
+ if name.lower() == filter_task.lower()
624
+ ]
625
+ if len(matches) == 1:
626
+ filter_task = matches[0]
627
+ elif len(matches) > 1:
628
+ raise ValueError(
629
+ "Task name is ambiguous when compared case-insensitively: "
630
+ f"'{filter_task}' matches {matches}."
631
+ )
632
+ else:
633
+ raise ValueError(
634
+ f"Task '{filter_task}' not found in flowchart YAML."
635
+ )
636
+ # Include the target task alongside its ancestors in the filtered view.
637
+ ancestors = set([filter_task])
638
+ parents_to_check = list(data["nodes"][filter_task]["parents"])
639
+ while parents_to_check:
640
+ parent = parents_to_check.pop()
641
+ if parent in ancestors:
642
+ continue
643
+ ancestors.add(parent)
644
+ if (
645
+ parent in data["nodes"]
646
+ and "parents" in data["nodes"][parent]
647
+ ):
648
+ for grandparent in data["nodes"][parent]["parents"]:
649
+ parents_to_check.append(grandparent)
650
+ incomplete_ancestors = set()
651
+ for name in ancestors:
652
+ if name not in data["nodes"]:
653
+ continue
654
+ if (
655
+ "style" in data["nodes"][name]
656
+ and data["nodes"][name]["style"] == "completed"
657
+ ):
658
+ continue
659
+ incomplete_ancestors.add(name)
660
+ data_for_dot = {"nodes": {}, "styles": {}}
661
+ if "styles" in data:
662
+ data_for_dot["styles"] = data["styles"]
663
+ for name in incomplete_ancestors:
664
+ data_for_dot["nodes"][name] = dict(data["nodes"][name])
665
+ for name in data_for_dot["nodes"]:
666
+ if "children" in data_for_dot["nodes"][name]:
667
+ data_for_dot["nodes"][name]["children"] = [
668
+ child
669
+ for child in data_for_dot["nodes"][name]["children"]
670
+ if child in incomplete_ancestors
671
+ ]
672
+ if "parents" in data_for_dot["nodes"][name]:
673
+ data_for_dot["nodes"][name]["parents"] = [
674
+ parent
675
+ for parent in data_for_dot["nodes"][name]["parents"]
676
+ if parent in incomplete_ancestors
677
+ ]
614
678
  dot_str = yaml_to_dot(
615
- data, wrap_width=wrap_width, order_by_date=order_by_date
679
+ data_for_dot, wrap_width=wrap_width, order_by_date=order_by_date
616
680
  )
617
681
  Path(dot_path).write_text(dot_str)
618
682
  if update_yaml:
@@ -26,6 +26,23 @@ def _reload_svg(driver, svg_file: Path) -> None:
26
26
  )
27
27
 
28
28
 
29
+ def start_chrome(webdriver, options, html_file):
30
+ # Launch Chrome and display the local SVG preview HTML file.
31
+ driver = webdriver.Chrome(options=options)
32
+ driver.get(html_file.resolve().as_uri())
33
+ return driver
34
+
35
+
36
+ def close_chrome(driver):
37
+ # Close the Chrome window if it is still running.
38
+ if driver is None:
39
+ return
40
+ try:
41
+ driver.quit()
42
+ except Exception:
43
+ pass
44
+
45
+
29
46
  def build_graph(
30
47
  yaml_file,
31
48
  dot_file,
@@ -33,6 +50,7 @@ def build_graph(
33
50
  wrap_width,
34
51
  order_by_date=False,
35
52
  prev_data=None,
53
+ target_task=None,
36
54
  ):
37
55
  # Graphviz is required for dot -> svg rendering.
38
56
  if shutil.which("dot") is None:
@@ -47,6 +65,7 @@ def build_graph(
47
65
  order_by_date=order_by_date,
48
66
  old_data=prev_data,
49
67
  validate_due_dates=True,
68
+ filter_task=target_task,
50
69
  )
51
70
  subprocess.run(
52
71
  ["dot", "-Tsvg", str(dot_file), "-o", str(svg_file)],
@@ -61,19 +80,27 @@ class GraphEventHandler(FileSystemEventHandler):
61
80
  yaml_file,
62
81
  dot_file,
63
82
  svg_file,
64
- driver,
65
- wrap_width,
66
- data,
83
+ html_file=None,
84
+ driver=None,
85
+ options=None,
86
+ webdriver=None,
87
+ wrap_width=55,
88
+ data=None,
67
89
  order_by_date=False,
90
+ target_task=None,
68
91
  debounce=0.25,
69
92
  ):
70
93
  self.yaml_file = Path(yaml_file)
71
94
  self.dot_file = Path(dot_file)
72
95
  self.svg_file = Path(svg_file)
96
+ self.html_file = None if html_file is None else Path(html_file)
73
97
  self.driver = driver
98
+ self.options = options
99
+ self.webdriver = webdriver
74
100
  self.wrap_width = wrap_width
75
101
  self.data = data
76
102
  self.order_by_date = order_by_date
103
+ self.target_task = target_task
77
104
  self.debounce = debounce
78
105
  self._last_handled = 0.0
79
106
  self._last_mtime = None
@@ -87,15 +114,40 @@ class GraphEventHandler(FileSystemEventHandler):
87
114
  if now - self._last_handled < self.debounce:
88
115
  return
89
116
  self._last_handled = now
90
- self.data = build_graph(
91
- self.yaml_file,
92
- self.dot_file,
93
- self.svg_file,
94
- self.wrap_width,
95
- self.order_by_date,
96
- self.data,
97
- )
98
- _reload_svg(self.driver, self.svg_file)
117
+ try:
118
+ self.data = build_graph(
119
+ self.yaml_file,
120
+ self.dot_file,
121
+ self.svg_file,
122
+ self.wrap_width,
123
+ self.order_by_date,
124
+ self.data,
125
+ self.target_task,
126
+ )
127
+ except Exception:
128
+ # If the graph fails to build (e.g. invalid date), close the
129
+ # preview window until a clean rebuild occurs.
130
+ close_chrome(self.driver)
131
+ self.driver = None
132
+ self._last_mtime = self.yaml_file.stat().st_mtime
133
+ return
134
+ if self.driver is None:
135
+ # Restart the preview once the SVG successfully builds again.
136
+ if (
137
+ self.webdriver is not None
138
+ and self.options is not None
139
+ and self.html_file is not None
140
+ ):
141
+ self.driver = start_chrome(
142
+ self.webdriver, self.options, self.html_file
143
+ )
144
+ else:
145
+ # Allow legacy/test usage without a live driver.
146
+ _reload_svg(self.driver, self.svg_file)
147
+ self._last_mtime = self.yaml_file.stat().st_mtime
148
+ return
149
+ else:
150
+ _reload_svg(self.driver, self.svg_file)
99
151
  self._last_mtime = self.yaml_file.stat().st_mtime
100
152
 
101
153
 
@@ -106,9 +158,12 @@ class GraphEventHandler(FileSystemEventHandler):
106
158
  "yaml": "Path to the flowchart YAML file",
107
159
  "wrap_width": "Line wrap width used when generating node labels",
108
160
  "d": "Render nodes by date without showing connections",
161
+ "t": (
162
+ "Task name to focus on (show incomplete ancestor tasks only)"
163
+ ),
109
164
  },
110
165
  )
111
- def wgrph(yaml, wrap_width=55, d=False):
166
+ def wgrph(yaml, wrap_width=55, d=False, t=None):
112
167
  # Selenium is only required when actually launching the watcher, so it is
113
168
  # imported here to avoid breaking the command-line tools when the optional
114
169
  # dependency is not installed.
@@ -134,35 +189,47 @@ def wgrph(yaml, wrap_width=55, d=False):
134
189
  html_file = yaml_file.with_suffix(".html")
135
190
 
136
191
  # Use date ordering when requested so boxes appear in calendar order.
137
- data = build_graph(yaml_file, dot_file, svg_file, wrap_width, d)
192
+ # Render the initial graph, optionally restricting to incomplete ancestors
193
+ # of a target task.
194
+ data = build_graph(yaml_file, dot_file, svg_file, wrap_width, d, None, t)
138
195
  html_file.write_text(
139
196
  "<html><body style='margin:0'><embed id='svg-view'"
140
197
  " type='image/svg+xml'"
141
198
  f" src='{svg_file.name}?t={time.time()}'/></body></html>"
142
199
  )
143
200
  options = Options()
144
- driver = webdriver.Chrome(options=options)
145
- driver.get(html_file.resolve().as_uri())
201
+ driver = start_chrome(webdriver, options, html_file)
146
202
  event_handler = GraphEventHandler(
147
- yaml_file, dot_file, svg_file, driver, wrap_width, data, d
203
+ yaml_file,
204
+ dot_file,
205
+ svg_file,
206
+ html_file,
207
+ driver,
208
+ options,
209
+ webdriver,
210
+ wrap_width,
211
+ data,
212
+ d,
213
+ t,
148
214
  )
149
215
  observer = Observer()
150
216
  observer.schedule(event_handler, yaml_file.parent, recursive=False)
151
217
  observer.start()
152
218
  try:
153
219
  while True:
220
+ if event_handler.driver is None:
221
+ time.sleep(1)
222
+ continue
154
223
  try:
155
- _ = driver.window_handles
156
- driver.execute_script("return 1")
224
+ _ = event_handler.driver.window_handles
225
+ event_handler.driver.execute_script("return 1")
157
226
  except (NoSuchWindowException, WebDriverException):
158
- break
227
+ close_chrome(event_handler.driver)
228
+ event_handler.driver = None
159
229
  time.sleep(1)
160
230
  except KeyboardInterrupt:
161
231
  pass
162
232
  finally:
163
233
  observer.stop()
164
234
  observer.join()
165
- try:
166
- driver.quit()
167
- except Exception:
168
- pass
235
+ close_chrome(event_handler.driver)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyDiffTools"
7
- version = "0.1.8"
7
+ version = "0.1.11"
8
8
  authors = [{ name = "J M Franck" }]
9
9
  description = "Diff tools"
10
10
  readme = "README.rst"
File without changes
File without changes
File without changes
File without changes