pyDiffTools 0.1.8__py3-none-any.whl

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.
@@ -0,0 +1,147 @@
1
+ from collections import OrderedDict
2
+ from fuzzywuzzy import process
3
+
4
+
5
+ class doc_contents_class(object):
6
+ def __init__(self, format_type="latex"):
7
+ self.contents = OrderedDict()
8
+ self.contents["header"] = ""
9
+ self.types = {}
10
+ self.types["header"] = "header"
11
+ self._reordering_started = False
12
+ self._aliases = {}
13
+ self._processed_titles = []
14
+ self.set_format(format_type)
15
+
16
+ def set_format(self, format_type):
17
+ if format_type == "markdown":
18
+ # markdown levels go deeper, so include subparagraph mapping
19
+ self.level_numbers = {
20
+ "section": 1,
21
+ "subsection": 2,
22
+ "subsubsection": 3,
23
+ "paragraph": 4,
24
+ "subparagraph": 5,
25
+ }
26
+ else:
27
+ # default to latex behavior
28
+ self.level_numbers = {
29
+ "section": 1,
30
+ "subsection": 2,
31
+ "subsubsection": 3,
32
+ "paragraph": 4,
33
+ "subparagraph": 5,
34
+ }
35
+ # map indentation back to section type for outline parsing
36
+ self.inv_prefix = {
37
+ (level - 1) * "\t": section
38
+ for section, level in self.level_numbers.items()
39
+ }
40
+ self.format_type = format_type
41
+
42
+ def start_sec(self, thistype, thistitle):
43
+ assert thistitle not in self.contents.keys(), (
44
+ "more than one section with the name:\n" + thistitle
45
+ )
46
+ self.contents[thistitle] = ""
47
+ self.types[thistitle] = thistype
48
+ print("added", thistitle)
49
+
50
+ def __setstate__(self, d):
51
+ "set the info from a pickle"
52
+ self.contents = d["contents"]
53
+ self.types = d["types"]
54
+ self._aliases = {} # doesn't exist, but still needed
55
+ self._reordering_started = False
56
+ self._processed_titles = []
57
+ if "format_type" in d:
58
+ self.set_format(d["format_type"])
59
+ else:
60
+ self.set_format("latex")
61
+ return
62
+
63
+ def __getstate__(self):
64
+ "return info for a pickle"
65
+ return {
66
+ "contents": self.contents,
67
+ "types": self.types,
68
+ "format_type": self.format_type,
69
+ }
70
+
71
+ def __iadd__(self, value):
72
+ self.contents[next(reversed(self.contents))] += value
73
+ return self
74
+
75
+ def __str__(self):
76
+ if len(self._processed_titles) > 0:
77
+ raise ValueError(
78
+ "the following section"
79
+ " titles were not utilized -- this program is"
80
+ " for reordering, not dropping!:\n"
81
+ + str(self._processed_titles)
82
+ )
83
+ retval = ""
84
+ for j in self.contents.keys():
85
+ if self.types[j] != "header":
86
+ new_name = j
87
+ if j in self._aliases.keys():
88
+ new_name = self._aliases[j]
89
+ if self.format_type == "markdown":
90
+ retval += "#" * self.level_numbers[self.types[j]]
91
+ retval += f" {new_name}\n\n"
92
+ else:
93
+ retval += f"\\{self.types[j]}{{{new_name}}}"
94
+ retval += f"{self.contents[j]}"
95
+ return retval
96
+
97
+ @property
98
+ def outline(self):
99
+ retval = []
100
+ for j in self.contents.keys():
101
+ if self.types[j] != "header":
102
+ indent = (self.level_numbers[self.types[j]] - 1) * "\t"
103
+ thistitle = (indent + "\t").join(j.split("\n"))
104
+ retval.append(indent + "*\t" + thistitle)
105
+ self._reordering_started = False
106
+ return "\n".join(retval)
107
+
108
+ def outline_in_order(self, thisline):
109
+ if not self._reordering_started:
110
+ self._processed_titles = [
111
+ j for j in self.contents.keys() if self.types[j] != "header"
112
+ ]
113
+ self._reordering_started = True
114
+ ilevel = 0
115
+ spacelevel = 0
116
+ hitmarker = False
117
+ for j, thischar in enumerate(thisline):
118
+ if not hitmarker:
119
+ if thischar == " ":
120
+ spacelevel += 1
121
+ if spacelevel == 4 or thischar == "\t":
122
+ ilevel += 1
123
+ spacelevel = 0
124
+ elif thischar == "*":
125
+ hitmarker = True
126
+ else:
127
+ assert thischar in [" ", "\t"]
128
+ title = thisline[j + 1 :]
129
+ break
130
+ if not hitmarker:
131
+ raise ValueError("somehow, there wasn't a * marker!")
132
+ if title not in self.contents.keys():
133
+ best_match, match_quality = process.extractOne(
134
+ title, self.contents.keys()
135
+ )
136
+ yesorno = input(
137
+ f"didn't find\n\t{title}\nin keys, maybe you"
138
+ f" want\n\t{best_match}\nsay y or n"
139
+ )
140
+ if yesorno == "y":
141
+ self._aliases[best_match] = title # will be replaced later
142
+ title = best_match
143
+ else:
144
+ raise ValueError("problem with replacement")
145
+ self.contents.move_to_end(title)
146
+ self._processed_titles.remove(title)
147
+ self.types[title] = self.inv_prefix[ilevel * "\t"]
@@ -0,0 +1,15 @@
1
+ """Flowchart helpers for dot/yaml conversion and watching.
2
+
3
+ Heavy dependencies (like PyYAML) are imported lazily so unrelated CLI commands
4
+ can start up even when optional packages are absent.
5
+ """
6
+
7
+ __all__ = [
8
+ "IndentDumper",
9
+ "load_graph_yaml",
10
+ "write_dot_from_yaml",
11
+ "dot_to_yaml",
12
+ "watch_graph",
13
+ ]
14
+
15
+
@@ -0,0 +1,114 @@
1
+ import sys
2
+ import re
3
+ import yaml
4
+ import pydot
5
+
6
+ from .graph import IndentDumper
7
+
8
+
9
+ def _label_to_text(label: str) -> str:
10
+ if label is None:
11
+ return None
12
+ stripped = label.strip()
13
+ if stripped.startswith('<') and stripped.endswith('>'):
14
+ stripped = stripped[1:-1]
15
+ stripped = stripped.replace('<br align="left"/>', '\n')
16
+ stripped = stripped.replace('<br/>', '\n')
17
+ lines = [l.strip() for l in stripped.splitlines()]
18
+ out_lines = []
19
+ current: list[str] = []
20
+
21
+ def flush():
22
+ nonlocal current
23
+ if current:
24
+ out_lines.append(' '.join(current))
25
+ current = []
26
+
27
+ for s in lines:
28
+ if not s:
29
+ continue
30
+ if s.startswith('<font'):
31
+ flush()
32
+ if out_lines and out_lines[-1] != '':
33
+ out_lines.append('')
34
+ out_lines.append(s)
35
+ elif s.startswith('</font'):
36
+ flush()
37
+ out_lines.append(s)
38
+ elif s.startswith('•'):
39
+ flush()
40
+ current = ['* ' + s[1:].lstrip()]
41
+ elif re.match(r'\d+[.)]\s', s):
42
+ flush()
43
+ current = [s]
44
+ else:
45
+ if current:
46
+ current.append(s)
47
+ else:
48
+ current = [s]
49
+ flush()
50
+ return '\n'.join(out_lines)
51
+
52
+
53
+ def dot_to_yaml(dot_path, yaml_path):
54
+ graphs = pydot.graph_from_dot_file(dot_path)
55
+ if not graphs:
56
+ raise ValueError("No graph found in dot file")
57
+ graph = graphs[0]
58
+ nodes = {}
59
+ for node in graph.get_nodes():
60
+ name = node.get_name().strip('"')
61
+ if name in ('graph', 'node', 'edge'):
62
+ continue
63
+ label = node.get('label')
64
+ if label is not None:
65
+ label = label.strip('"')
66
+ label = _label_to_text(label)
67
+ nodes[name] = {
68
+ 'text': label,
69
+ 'children': [],
70
+ 'parents': [],
71
+ }
72
+ styles = {}
73
+ for sub in graph.get_subgraphs():
74
+ sub_name = sub.get_name().strip('"')
75
+ attrs = {}
76
+ if sub.get_node_defaults():
77
+ attrs['node'] = sub.get_node_defaults()
78
+ styles[sub_name] = {'attrs': attrs}
79
+ for n in sub.get_nodes():
80
+ nname = n.get_name().strip('"')
81
+ if nname == 'node':
82
+ continue
83
+ if nname not in nodes:
84
+ label = n.get('label')
85
+ if label is not None:
86
+ label = label.strip('"')
87
+ label = _label_to_text(label)
88
+ nodes[nname] = {'text': label, 'children': [], 'parents': []}
89
+ nodes[nname]['style'] = sub_name
90
+ for edge in graph.get_edges():
91
+ src = edge.get_source().strip('"')
92
+ dst = edge.get_destination().strip('"')
93
+ nodes.setdefault(src, {'text': None, 'children': [], 'parents': []})
94
+ nodes.setdefault(dst, {'text': None, 'children': [], 'parents': []})
95
+ nodes[src]['children'].append(dst)
96
+ nodes[dst]['parents'].append(src)
97
+ data = {'styles': styles, 'nodes': nodes}
98
+ with open(yaml_path, 'w') as f:
99
+ yaml.dump(
100
+ data,
101
+ f,
102
+ Dumper=IndentDumper,
103
+ default_flow_style=False,
104
+ sort_keys=True,
105
+ allow_unicode=True,
106
+ indent=2,
107
+ )
108
+
109
+
110
+ if __name__ == '__main__':
111
+ if len(sys.argv) != 3:
112
+ print('Usage: dot_to_yaml.py <input.dot> <output.yaml>')
113
+ sys.exit(1)
114
+ dot_to_yaml(sys.argv[1], sys.argv[2])