proj-flow 0.20.3__py3-none-any.whl → 0.22.0__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.
Files changed (37) hide show
  1. proj_flow/__init__.py +1 -1
  2. proj_flow/api/arg.py +2 -0
  3. proj_flow/api/completers.py +1 -1
  4. proj_flow/api/env.py +45 -15
  5. proj_flow/api/release.py +1 -1
  6. proj_flow/api/step.py +20 -5
  7. proj_flow/{ext/cplusplus/cmake/presets.py → base/cmake_presets.py} +40 -16
  8. proj_flow/base/plugins.py +12 -7
  9. proj_flow/cli/finder.py +4 -3
  10. proj_flow/dependency.py +6 -2
  11. proj_flow/ext/cplusplus/cmake/__init__.py +2 -2
  12. proj_flow/ext/cplusplus/cmake/parser.py +4 -3
  13. proj_flow/ext/cplusplus/cmake/steps.py +9 -9
  14. proj_flow/ext/cplusplus/conan/__init__.py +8 -4
  15. proj_flow/ext/github/cli.py +1 -3
  16. proj_flow/ext/github/publishing.py +1 -0
  17. proj_flow/ext/python/rtdocs.py +37 -8
  18. proj_flow/ext/python/steps.py +5 -4
  19. proj_flow/ext/python/version.py +12 -12
  20. proj_flow/ext/sign/__init__.py +2 -2
  21. proj_flow/ext/test_runner/__init__.py +6 -0
  22. proj_flow/ext/test_runner/cli.py +416 -0
  23. proj_flow/ext/test_runner/driver/__init__.py +2 -0
  24. proj_flow/ext/test_runner/driver/commands.py +74 -0
  25. proj_flow/ext/test_runner/driver/test.py +610 -0
  26. proj_flow/ext/test_runner/driver/testbed.py +141 -0
  27. proj_flow/ext/test_runner/utils/__init__.py +2 -0
  28. proj_flow/ext/test_runner/utils/archives.py +56 -0
  29. proj_flow/log/rich_text/api.py +3 -4
  30. proj_flow/minimal/list.py +126 -10
  31. proj_flow/minimal/run.py +3 -2
  32. proj_flow/project/api.py +1 -0
  33. {proj_flow-0.20.3.dist-info → proj_flow-0.22.0.dist-info}/METADATA +17 -7
  34. {proj_flow-0.20.3.dist-info → proj_flow-0.22.0.dist-info}/RECORD +37 -29
  35. {proj_flow-0.20.3.dist-info → proj_flow-0.22.0.dist-info}/WHEEL +0 -0
  36. {proj_flow-0.20.3.dist-info → proj_flow-0.22.0.dist-info}/entry_points.txt +0 -0
  37. {proj_flow-0.20.3.dist-info → proj_flow-0.22.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,141 @@
1
+ # Copyright (c) 2026 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ import os
5
+ import pprint
6
+ import random
7
+ import string
8
+ import sys
9
+ from dataclasses import dataclass, field, replace
10
+ from pathlib import Path
11
+ from typing import cast
12
+
13
+ from proj_flow.ext.test_runner.driver.test import Env, Test, fix_file_write, to_lines
14
+
15
+
16
+ class color:
17
+ reset = "\033[m"
18
+ counter = "\033[2;49;92m"
19
+ name = "\033[0;49;90m"
20
+ failed = "\033[0;49;91m"
21
+ passed = "\033[2;49;92m"
22
+ skipped = "\033[0;49;34m"
23
+
24
+
25
+ class TaskResult:
26
+ OK = 0
27
+ SKIPPED = 1
28
+ SAVED = 2
29
+ FAILED = 3
30
+ CLIP_FAILED = 4
31
+
32
+
33
+ @dataclass
34
+ class Counters:
35
+ error_counter: int = 0
36
+ skip_counter: int = 0
37
+ save_counter: int = 0
38
+ echo: list[str] = field(default_factory=list)
39
+
40
+ def report(self, outcome: int, test_id: str, message: str | None):
41
+ if outcome == TaskResult.SKIPPED:
42
+ print(f"{test_id} {color.skipped}SKIPPED{color.reset}")
43
+ self.skip_counter += 1
44
+ return
45
+
46
+ if outcome == TaskResult.SAVED:
47
+ print(f"{test_id} {color.skipped}saved{color.reset}")
48
+ self.skip_counter += 1
49
+ self.save_counter += 1
50
+ return
51
+
52
+ if outcome == TaskResult.CLIP_FAILED:
53
+ msg = f"{test_id} {color.failed}FAILED (unknown check '{message}'){color.reset}"
54
+ print(msg)
55
+ self.echo.append(msg)
56
+ self.error_counter += 1
57
+ return
58
+
59
+ if outcome == TaskResult.OK:
60
+ print(f"{test_id} {color.passed}PASSED{color.reset}")
61
+ return
62
+
63
+ if message is not None:
64
+ print(message)
65
+ msg = f"{test_id} {color.failed}FAILED{color.reset}"
66
+ print(msg)
67
+ self.echo.append(msg)
68
+ self.error_counter += 1
69
+
70
+ def summary(self, counter: int):
71
+ print(f"Failed {self.error_counter}/{counter}")
72
+ if self.skip_counter > 0:
73
+ skip_test = "test" if self.skip_counter == 1 else "tests"
74
+ if self.save_counter > 0:
75
+ print(
76
+ f"Skipped {self.skip_counter} {skip_test} (including {self.save_counter} due to saving)"
77
+ )
78
+ else:
79
+ print(f"Skipped {self.skip_counter} {skip_test}")
80
+
81
+ if len(self.echo):
82
+ print()
83
+ for echo in self.echo:
84
+ print(echo)
85
+
86
+ return self.error_counter == 0
87
+
88
+
89
+ def task(
90
+ env1: Env, tested: Test, current_counter: int
91
+ ) -> tuple[int, str, str | None, str]:
92
+ temp_instance = "".join(random.choice(string.ascii_letters) for _ in range(16))
93
+ tempdir = f"{env1.tempdir}/{temp_instance}"
94
+ tempdir_alt = None
95
+
96
+ if env1.tempdir_alt is not None:
97
+ tempdir_alt = f"{env1.tempdir_alt}{os.sep}{temp_instance}"
98
+
99
+ env2 = replace(env1, tempdir=tempdir, tempdir_alt=tempdir_alt)
100
+
101
+ test_counter = f"{color.counter}[{current_counter:>{env2.counter_digits}}/{env2.counter_total}]{color.reset}"
102
+ test_name = f"{color.name}{tested.name}{color.reset}"
103
+ test_id = f"{test_counter} {test_name}"
104
+
105
+ print(test_id)
106
+ os.makedirs(tempdir, exist_ok=True)
107
+
108
+ actual = tested.run(env2)
109
+ if actual is None:
110
+ return (TaskResult.SKIPPED, test_id, None, tempdir)
111
+
112
+ if tested.expected is None:
113
+ is_json = tested.filename.suffix == ".json"
114
+ tested.data["expected"] = [
115
+ actual[0],
116
+ *[to_lines(stream, is_json) for stream in actual[1:3]],
117
+ ]
118
+ tested.store()
119
+ return (TaskResult.SAVED, test_id, None, tempdir)
120
+
121
+ clipped = tested.clip(actual[:3])
122
+
123
+ if isinstance(clipped, str):
124
+ return (TaskResult.CLIP_FAILED, test_id, clipped, tempdir)
125
+
126
+ reports: list[str] = []
127
+
128
+ files = actual[3]
129
+ for file in files:
130
+ fixed = fix_file_write(file, env2, tested.cwd, tested.patches)
131
+ if fixed.generated.content != fixed.template.content:
132
+ reports.append(tested.report_file(fixed))
133
+
134
+ if actual[:3] != tested.expected and clipped != tested.expected:
135
+ reports.append(tested.report_io(actual[:3]))
136
+
137
+ if reports:
138
+ reports.append(tested.test_footer(env2, tempdir))
139
+ return (TaskResult.FAILED, test_id, "\n".join(reports), tempdir)
140
+
141
+ return (TaskResult.OK, test_id, None, tempdir)
@@ -0,0 +1,2 @@
1
+ # Copyright (c) 2026 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
@@ -0,0 +1,56 @@
1
+ # Copyright (c) 2026 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ import os
5
+ import tarfile
6
+ import zipfile
7
+ from typing import Callable
8
+
9
+
10
+ def _untar(src, dst):
11
+ with tarfile.open(src) as TAR:
12
+
13
+ def is_within_directory(directory, target):
14
+ abs_directory = os.path.abspath(directory)
15
+ abs_target = os.path.abspath(target)
16
+
17
+ prefix = os.path.commonprefix([abs_directory, abs_target])
18
+
19
+ return prefix == abs_directory
20
+
21
+ for member in TAR.getmembers():
22
+ member_path = os.path.join(dst, member.name)
23
+ if not is_within_directory(dst, member_path):
24
+ raise Exception(f"Attempted path traversal in Tar file: {member.name}")
25
+
26
+ TAR.extractall(dst)
27
+
28
+
29
+ def _unzip(src, dst):
30
+ with zipfile.ZipFile(src) as ZIP:
31
+ ZIP.extractall(dst)
32
+
33
+
34
+ _tar = (_untar, ["tar", "-xf"])
35
+
36
+ Unpacker = Callable[[str, str], None]
37
+ UnpackInfo = tuple[Unpacker, list[str]]
38
+
39
+ ARCHIVES: dict[str, UnpackInfo] = {
40
+ ".tar": _tar,
41
+ ".tar.gz": _tar,
42
+ ".zip": (_unzip, ["unzip"]),
43
+ }
44
+
45
+
46
+ def locate_unpack(archive: str) -> UnpackInfo:
47
+ reminder, ext = os.path.splitext(archive)
48
+ _, mid = os.path.splitext(reminder)
49
+ if mid == ".tar":
50
+ ext = ".tar"
51
+ return ARCHIVES[ext]
52
+
53
+
54
+ del _tar
55
+ del _unzip
56
+ del _untar
@@ -91,7 +91,7 @@ class ChangelogGenerator(abc.ABC):
91
91
 
92
92
  entire_log.append(self.intro())
93
93
 
94
- filename = os.path.join(rt.root, f"CHANGELOG{self.ext}")
94
+ filename = rt.root / f"CHANGELOG{self.ext}"
95
95
  with open(filename, "wb") as f:
96
96
  for text in reversed(entire_log):
97
97
  f.write(text.encode("UTF-8"))
@@ -106,8 +106,7 @@ class ChangelogGenerator(abc.ABC):
106
106
  setup, commit.read_tag_date(setup.curr_tag or "HEAD", rt)
107
107
  )
108
108
  text = formatter.format_changelog(log)
109
- filename = self.filename
110
- path = os.path.join(rt.root, filename)
109
+ path = rt.root / self.filename
111
110
 
112
111
  try:
113
112
  with open(path, encoding="UTF-8") as f:
@@ -125,7 +124,7 @@ class ChangelogGenerator(abc.ABC):
125
124
  if len(split) > 1:
126
125
  new_text += "".join(split[1:])
127
126
 
128
- with open(filename, "wb") as f:
127
+ with open(path, "wb") as f:
129
128
  f.write(new_text.encode("UTF-8"))
130
129
 
131
130
 
proj_flow/minimal/list.py CHANGED
@@ -6,12 +6,20 @@ The **proj_flow.minimal.list** implements ``./flow list`` command.
6
6
  """
7
7
 
8
8
  import os
9
- from typing import Annotated, Dict, List, Set, cast
9
+ import re
10
+ import sys
11
+ from typing import Annotated, Dict, Iterable, List, Set, cast
10
12
 
11
13
  from proj_flow import cli
12
14
  from proj_flow.api import arg, env, step
13
15
  from proj_flow.base import matrix
14
16
 
17
+ if sys.platform == "win32":
18
+ import ctypes
19
+ import ctypes.wintypes
20
+ else:
21
+ import termios
22
+
15
23
 
16
24
  @arg.command("list")
17
25
  def main(
@@ -39,10 +47,7 @@ def main(
39
47
  configs = True
40
48
 
41
49
  if builtin:
42
- root = menu
43
- while root.parent is not None:
44
- root = root.parent
45
- builtin_entries = list(sorted((cmd.name, cmd.doc) for cmd in root.children))
50
+ builtin_entries = list(sorted(_walk_menu(menu)))
46
51
  if not pipe and len(builtin_entries) > 0:
47
52
  print("Builtin commands")
48
53
  print("----------------")
@@ -54,7 +59,10 @@ def main(
54
59
 
55
60
  name = f"{bold}{entry_name}{reset}"
56
61
  if entry_doc:
57
- print(f"- {name}: {entry_doc}")
62
+ print(f"- {name}:", end=" ")
63
+ _write_console_para(
64
+ " ".join(para.split("\n")) for para in entry_doc.split("\n\n")
65
+ )
58
66
  else:
59
67
  print(f"- {name}")
60
68
 
@@ -76,7 +84,8 @@ def main(
76
84
  continue
77
85
 
78
86
  name = f"{bold}{run_alias.name}{reset}"
79
- print(f"- {name}: {', '.join(run_alias.steps)}")
87
+ print(f"- {name}:", end=" ")
88
+ _write_console_para([", ".join(run_alias.steps)])
80
89
 
81
90
  printed_something = True
82
91
 
@@ -109,8 +118,10 @@ def main(
109
118
  print(f"- {name}*")
110
119
 
111
120
  if some_unused:
112
- print(
113
- f"*step can only be run by explicitly calling through {bold}run{reset}."
121
+ _write_console_para(
122
+ [
123
+ f"*step can only be run by explicitly calling through {bold}run{reset}."
124
+ ]
114
125
  )
115
126
 
116
127
  printed_something = True
@@ -147,7 +158,8 @@ def main(
147
158
  value = ", ".join(values.get(key, empty))
148
159
  name = f"{bold}{key}{reset}"
149
160
  if value:
150
- print(f"- {name}: {value}")
161
+ print(f"- {name}:", end=" ")
162
+ _write_console_para([value])
151
163
  else:
152
164
  print(f"- {name}")
153
165
 
@@ -157,6 +169,110 @@ def main(
157
169
  print(f"Use {bold}--help{reset} to see, which listings are available")
158
170
 
159
171
 
172
+ def _iterate_levels(menu: cli.argument.Command, prefix: str):
173
+ yield [(f"{prefix}{cmd.name}", cmd.doc) for cmd in menu.children]
174
+ for cmd in menu.children:
175
+ child_prefix = f"{prefix}{cmd.name} "
176
+ for layer in _iterate_levels(cmd, child_prefix):
177
+ yield layer
178
+
179
+
180
+ def _walk_menu(menu: cli.argument.Command):
181
+ root = menu
182
+ while root.parent is not None:
183
+ root = root.parent
184
+
185
+ items: list[tuple[str, str]] = []
186
+ for layer in _iterate_levels(root, ""):
187
+ items.extend(layer)
188
+
189
+ return items
190
+
191
+
192
+ def _cursor_pos():
193
+ if sys.platform == "win32":
194
+ old_stdin_mode = ctypes.wintypes.DWORD()
195
+ old_stdout_mode = ctypes.wintypes.DWORD()
196
+ kernel32 = ctypes.windll.kernel32
197
+ kernel32.GetConsoleMode(
198
+ kernel32.GetStdHandle(-10), ctypes.byref(old_stdin_mode)
199
+ )
200
+ kernel32.SetConsoleMode(kernel32.GetStdHandle(-10), 0)
201
+ kernel32.GetConsoleMode(
202
+ kernel32.GetStdHandle(-11), ctypes.byref(old_stdout_mode)
203
+ )
204
+ kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
205
+ else:
206
+ old_stdin_mode = termios.tcgetattr(sys.stdin)
207
+ _ = termios.tcgetattr(sys.stdin)
208
+ _[3] = _[3] & ~(termios.ECHO | termios.ICANON)
209
+ termios.tcsetattr(sys.stdin, termios.TCSAFLUSH, _)
210
+ try:
211
+ _ = ""
212
+ sys.stdout.write("\x1b[6n")
213
+ sys.stdout.flush()
214
+ while not (_ := _ + sys.stdin.read(1)).endswith("R"):
215
+ pass
216
+ res = re.match(r".*\[(?P<y>\d*);(?P<x>\d*)R", _)
217
+ finally:
218
+ if sys.platform == "win32":
219
+ kernel32.SetConsoleMode(kernel32.GetStdHandle(-10), old_stdin_mode)
220
+ kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), old_stdout_mode)
221
+ else:
222
+ termios.tcsetattr(sys.stdin, termios.TCSAFLUSH, old_stdin_mode)
223
+ if res:
224
+ return (int(res.group("x")), int(res.group("y")))
225
+ return (1, 1)
226
+
227
+
228
+ def _write_console_para(text: Iterable[str]):
229
+ term_width = os.get_terminal_size().columns
230
+ margin = min(_cursor_pos()[0], term_width // 2) - 1
231
+ width = term_width - 1
232
+ pos = margin
233
+ for para in text:
234
+ for word in para.split():
235
+ if not word:
236
+ continue
237
+ word_len = len(word)
238
+ next_pos = pos + word_len + 1
239
+
240
+ if next_pos >= width:
241
+ orig = pos
242
+ if orig == margin:
243
+ pos = margin - word_len
244
+ print(word, end="")
245
+
246
+ print()
247
+ print(" " * margin, end="")
248
+
249
+ if orig != margin:
250
+ pos = margin
251
+ print(word, end=" ")
252
+ else:
253
+ print(word, end=" ")
254
+
255
+ pos += word_len + 1
256
+
257
+ continue
258
+ if (word_len + 1) > term_width:
259
+ first_word = pos == margin
260
+ if first_word:
261
+ print(word, end="")
262
+
263
+ print(f"\n{' ' * margin}", end="")
264
+ pos = margin
265
+
266
+ if not first_word:
267
+ print(word, "", end="")
268
+ pos += word_len + 1
269
+ continue
270
+
271
+ print(word, "", end="")
272
+ pos += word_len + 1
273
+ print()
274
+
275
+
160
276
  def _load_flow_data(rt: env.Runtime):
161
277
  paths = [os.path.join(".flow", "matrix.yml")]
162
278
  m, keys = matrix.load_matrix(*paths)
proj_flow/minimal/run.py CHANGED
@@ -9,6 +9,7 @@ import os
9
9
  import shutil
10
10
  import sys
11
11
  from contextlib import contextmanager
12
+ from pathlib import Path
12
13
  from typing import Annotated, List, Optional, Set, cast
13
14
 
14
15
  from proj_flow import api, dependency
@@ -69,7 +70,7 @@ def gather_dependencies_for_all_configs(
69
70
  def refresh_directories(
70
71
  configs: Configs, rt: api.env.Runtime, steps: List[api.step.Step]
71
72
  ):
72
- directories_to_refresh: Set[str] = set()
73
+ directories_to_refresh: Set[Path] = set()
73
74
  for config in configs.usable:
74
75
  for step in steps:
75
76
  if step.is_active(config, rt):
@@ -80,7 +81,7 @@ def refresh_directories(
80
81
  for dirname in directories_to_refresh:
81
82
  if not rt.silent:
82
83
  printed = True
83
- print(f"[-] {dirname}", file=sys.stderr)
84
+ print(f"[-] {dirname.as_posix()}", file=sys.stderr)
84
85
  if not rt.dry_run:
85
86
  shutil.rmtree(dirname, ignore_errors=True)
86
87
 
proj_flow/project/api.py CHANGED
@@ -67,5 +67,6 @@ def get_project_type(id: str):
67
67
  raise ProjectNotFound(id)
68
68
  return result
69
69
 
70
+
70
71
  def load_common_init_setting_extensions():
71
72
  env.load_extensions(["proj_flow.ext.github.switches"])
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proj-flow
3
- Version: 0.20.3
3
+ Version: 0.22.0
4
4
  Summary: C++ project maintenance, automated
5
5
  Project-URL: Changelog, https://github.com/mzdun/proj-flow/blob/main/CHANGELOG.rst
6
6
  Project-URL: Documentation, https://proj-flow.readthedocs.io/en/latest/
7
7
  Project-URL: Homepage, https://pypi.org/project/proj-flow/
8
8
  Project-URL: Source Code, https://github.com/mzdun/proj-flow
9
9
  Author-email: Marcin Zdun <marcin.zdun@gmail.com>
10
+ License-Expression: MIT
10
11
  License-File: LICENSE
11
12
  Keywords: C/C++,build-tool,c++,ci-cd,continuous-integration,cpp,dependencies,dependency-manager,developer,developer-tools,development,meta-build-tool,pipeline,tools-and-automation
12
13
  Classifier: Development Status :: 4 - Beta
@@ -17,13 +18,22 @@ Classifier: Programming Language :: Python :: 3
17
18
  Classifier: Programming Language :: Python :: 3.10
18
19
  Classifier: Topic :: Software Development :: Build Tools
19
20
  Requires-Python: >=3.10
20
- Requires-Dist: argcomplete
21
+ Requires-Dist: argcomplete~=3.5
21
22
  Requires-Dist: chevron2021
22
- Requires-Dist: prompt-toolkit
23
- Requires-Dist: pywebidl2
24
- Requires-Dist: pyyaml
25
- Requires-Dist: requests-cache
26
- Requires-Dist: toml
23
+ Requires-Dist: prompt-toolkit~=3.0
24
+ Requires-Dist: pywebidl2~=0.1
25
+ Requires-Dist: pyyaml~=6.0
26
+ Requires-Dist: requests-cache~=1.3
27
+ Requires-Dist: toml~=0.10
28
+ Provides-Extra: dev
29
+ Requires-Dist: black~=25.0; extra == 'dev'
30
+ Requires-Dist: build~=1.4; extra == 'dev'
31
+ Requires-Dist: isort~=6.0; extra == 'dev'
32
+ Requires-Dist: sphinx~=9.1; extra == 'dev'
33
+ Requires-Dist: twine<=6.0.1; extra == 'dev'
34
+ Requires-Dist: types-chevron~=0.14; extra == 'dev'
35
+ Requires-Dist: types-pyyaml~=6.0; extra == 'dev'
36
+ Requires-Dist: types-toml~=0.10; extra == 'dev'
27
37
  Description-Content-Type: text/markdown
28
38
 
29
39
  # Project Flow
@@ -1,51 +1,59 @@
1
- proj_flow/__init__.py,sha256=ngosVXJXC6NPzy7Jrvy_GEmyLLNcMeHljXRL0gpxsKc,277
1
+ proj_flow/__init__.py,sha256=q44bLIhvwlNd00XujabnofmCmqvXybHCZSg3Fg3FFRE,277
2
2
  proj_flow/__main__.py,sha256=HUar_qQ9Ndmchmryegtzu__5wukwCLrFN_SGRl5Ol_M,233
3
- proj_flow/dependency.py,sha256=CpcnR6El8AO9hlLc9lQtYQADYlkx3GMHlkLYbEAtdMI,4639
3
+ proj_flow/dependency.py,sha256=f2wjr_F7Cy_3I4R5AfT6R9Q28F3G3WrLKbqyEld_CF0,4765
4
4
  proj_flow/api/__init__.py,sha256=gV2f6kll_5JXtvkGASvnx7CbOWr34PHOdck-4ce-qEk,378
5
- proj_flow/api/arg.py,sha256=3XqPAucon0k3yyYsqzSp7X9HGMZ_sNJNo8Z4hf-0erY,5275
6
- proj_flow/api/completers.py,sha256=NapNVu6QAQ_iF6dqcAzOV5kDHKD9MAMVX209Bklq-Mw,2464
5
+ proj_flow/api/arg.py,sha256=0sVVEqdIOCqWmZtST_-u4rP6JkrResGJsKERQp3WYgc,5392
6
+ proj_flow/api/completers.py,sha256=gZ50toLTWstKQbSV24gaNUzVjmrS0IjuYujzyMbp41A,2452
7
7
  proj_flow/api/ctx.py,sha256=IJu0q0Chivo6b2M4MKkAlV09oi7Cn9VxtDFeAeL_tnc,6646
8
- proj_flow/api/env.py,sha256=Ns0XBklOOEmMfo6Bt45XpfG3kqxA1W0rF28-Qu8SADE,12787
8
+ proj_flow/api/env.py,sha256=8XN3N91iVzcgx2urYDy2IjqCk5CIzeCvFqN6FTK6YLo,13582
9
9
  proj_flow/api/init.py,sha256=p4ZDGfq6fw4bXbJu2iq0vEmVxbS7nALIhZfe-XnEs44,565
10
10
  proj_flow/api/makefile.py,sha256=q0fBSsWTWfR5YwunwiRjWJKtiLeHdSKUgUTEgo5I7dE,3863
11
- proj_flow/api/release.py,sha256=IM4axJ6dfyilCmpwL_Z8q43XGKsfHaPoMAdqSziwT7s,2810
12
- proj_flow/api/step.py,sha256=C6LfjAN5edYjlpkhd6k9eMO9SLfLtyS2ZG1Rg_dLcjg,4926
11
+ proj_flow/api/release.py,sha256=JOddtYNH7J-GfUsvc1hOgKLzf56C6qvwF748n87MQFE,2821
12
+ proj_flow/api/step.py,sha256=npUREdBIJb6s4CPA7snVd83YvfhsfMx6mPYKiHFhVF4,5326
13
13
  proj_flow/base/__cmake_version__.py,sha256=imja0GnhpBvS8Crz-64eOUKhc4i6FeRrjBGRB68x_p0,239
14
14
  proj_flow/base/__init__.py,sha256=V6IFRRtwxzvHuvtog6LIG9_6oivNpdcR5UmGobypvTo,930
15
+ proj_flow/base/cmake_presets.py,sha256=tBgzKk8BU1Lz9oA7o3PdB2Z4nK9ftuLb2TgeUzVhABc,5618
15
16
  proj_flow/base/cmd.py,sha256=XJk_r4Nlq_2XGgD_w92Us4WKwItmQAB8QWdT1pKxUFA,1746
16
17
  proj_flow/base/inspect.py,sha256=lt5P19rvSZ-wMCTrCYAaQFCt2S9fUjEQXlrKK-Tmvwc,2786
17
18
  proj_flow/base/matrix.py,sha256=kH2BB6JRrLYdR71VJiJp58zfQSazwEOeiRXOPrayAcI,8446
18
19
  proj_flow/base/name_list.py,sha256=KiHSnbDgYplJc25O3EehYhFAhD7Z3mHVAK6UYOdg5PQ,416
19
- proj_flow/base/plugins.py,sha256=evn2Dym_NeoBaIZAu2YUtRd--15PCFpHD0h5zSsWkQE,978
20
+ proj_flow/base/plugins.py,sha256=8mQfkrdF7UncJaWz6623oa4g9XiZjJWvTgU1pCEBkr0,1078
20
21
  proj_flow/base/registry.py,sha256=zbkB9KNfHnyPtzOurdvjwt714jrFpGHyOqeZL5sMvzI,3745
21
22
  proj_flow/base/uname.py,sha256=7Awb3Es0jTAKMpyRawdrC16xc5X9M97BlPqEfQibqIk,2295
22
23
  proj_flow/cli/__init__.py,sha256=cMsZpECkXeSzY4Hv_ela3Ou-FhwE5w1A3ypMSnZZikM,1196
23
24
  proj_flow/cli/argument.py,sha256=V242x0ziuvqqXl56TDXxVhN1llCRZRT3bUnw7GfGRIE,14341
24
- proj_flow/cli/finder.py,sha256=5x7H1nH0k63DetDauhB_wABel_f0RQpsZ5YnhPfbkRc,1402
25
+ proj_flow/cli/finder.py,sha256=IHnX2tkD0Hq7N6hVDArGJIK8NFzD5isMZYugkArlS6Y,1439
25
26
  proj_flow/ext/__init__.py,sha256=XD52rUFTPz3GnyRq6KZUNeWdMce7e0bB19iTx-zU6DE,169
26
27
  proj_flow/ext/markdown_changelog.py,sha256=fRGL09jojnv2B-8vAX2prvgNp8e7uyq5NxboSZjFCJ8,436
27
28
  proj_flow/ext/re_structured_changelog.py,sha256=UF23W9eu_YgPO42MiaoDbEKu8In_48mQg6rH9--mI30,459
28
29
  proj_flow/ext/store.py,sha256=zc9yh9M042V5OSLUZjWe9KazhdZ35h1JJsWvKughM0Y,3385
29
30
  proj_flow/ext/cplusplus/__init__.py,sha256=dAmLMyGVQq586jJM_jiAuo5Ecw9U8agpvSRbzzPgh3g,245
30
- proj_flow/ext/cplusplus/cmake/__init__.py,sha256=uQPclC2Bs5qVR_VnoYYPGUbzkzR2gU4RLThcTumYXUE,366
31
- proj_flow/ext/cplusplus/cmake/parser.py,sha256=ZqQRZqS_VU5VtC8uwax-dknh7sfuLEvtazG8ChSqHDQ,3814
32
- proj_flow/ext/cplusplus/cmake/presets.py,sha256=iYtSlkIoY3fEa6GL95zJugJbmleV5NPPB9UhME8RbSA,4922
31
+ proj_flow/ext/cplusplus/cmake/__init__.py,sha256=XQ8l1WsEZAgcNX_A0KgY535VhF6s7glS0TW2L0EmdVc,346
32
+ proj_flow/ext/cplusplus/cmake/parser.py,sha256=GsySyYC3DES-3teKPvs5PFwl3hpQkvKkNhWGbpLa-Ww,3828
33
33
  proj_flow/ext/cplusplus/cmake/project.py,sha256=Cp-5HwEsrQW4RjDThjMBQmaVJiRHo9QvYbw7IvjHKNQ,929
34
- proj_flow/ext/cplusplus/cmake/steps.py,sha256=MnxPTR9tD2O8_DaRSe-fkvkpoM-DZc2zqt-J84xA_fI,4353
35
- proj_flow/ext/cplusplus/conan/__init__.py,sha256=Fv839SWsKPWMZs9ox9T-bofZ4xDJXOI5UfWKQkm0Vtg,1924
34
+ proj_flow/ext/cplusplus/cmake/steps.py,sha256=Xp7d-79wi759p4OqpRPfhNplUNbNa1Oi-_18_K5-Vs4,4256
35
+ proj_flow/ext/cplusplus/conan/__init__.py,sha256=hHxf9-7W5KGKOv2e1EBQj6sPDD-R7LMCXx5BlLETT8w,2052
36
36
  proj_flow/ext/cplusplus/conan/_conan.py,sha256=9xnji-f8uN7huXLqavVBUDC33CgnjBIyZX6wVcGm2RA,3352
37
37
  proj_flow/ext/github/__init__.py,sha256=Mgx19YS6SYBXYB66_pOgIgwuB2WKRxqp5UGutq0B9Xk,282
38
- proj_flow/ext/github/cli.py,sha256=zUkRb38_hHj165oxkFdxOX-NJfhg1UN_GFPQPo_lmWc,6455
38
+ proj_flow/ext/github/cli.py,sha256=whK_VEUAG48tODyUYGfh3ZzZEa6vQgsNxY8yEfyDTzs,6441
39
39
  proj_flow/ext/github/hosting.py,sha256=3iW8QjeJk7MyqKNbv92nB-5a_Yn_B5_eEIlw_cdgUT0,519
40
- proj_flow/ext/github/publishing.py,sha256=5dUNFq47X_g9vo25R3lQRkSjV2IiAm2zkIhNe8gLDKs,1921
40
+ proj_flow/ext/github/publishing.py,sha256=IXiQ5O3dA5ctGa46rCynZofF4Ixg85QhMHBaPZ-Uk10,1947
41
41
  proj_flow/ext/github/switches.py,sha256=Y3pqJdiHYLoveCQtqZqELR84Phb2YF4u5y78TU7n2CQ,752
42
42
  proj_flow/ext/python/__init__.py,sha256=GbEKEJJZ3PJ4sRHEykAWjGIR6yyyrYdlUFulldvsAGI,252
43
- proj_flow/ext/python/rtdocs.py,sha256=mICD6EAFgAkWG4wcQZG2_7Ito4lxo7Vl_GdM6-4K9hY,6249
44
- proj_flow/ext/python/steps.py,sha256=pDHGAe_CDzzdRFAzM1AIBvkbc14KB3SNUunusKZAaaY,1815
45
- proj_flow/ext/python/version.py,sha256=pnyuKATyZwBh1p0gf9KmqbRSZx8hJ5285CiFK_tHEaY,3159
46
- proj_flow/ext/sign/__init__.py,sha256=b9AN1_BalPtVy7YLBjvGhLamTujHEKcZP7npgDDDuSI,4296
43
+ proj_flow/ext/python/rtdocs.py,sha256=kNPlrnmFLXIbtWB-WuEAgwpymNnI-qXF0P1cHJXfgiM,7116
44
+ proj_flow/ext/python/steps.py,sha256=c2ZXaS6F0p1euQrsZMLuSjisUTmZd-CFKWhbk7RnOQI,1865
45
+ proj_flow/ext/python/version.py,sha256=wgrhWCYSnJsAg52rijvX7WxAyKQwgh-oSRIp5uRk5hs,3216
46
+ proj_flow/ext/sign/__init__.py,sha256=IMnFqRdl9e-EejXsIiifHs6dF4jkujQHbvbNXWjDP50,4298
47
47
  proj_flow/ext/sign/api.py,sha256=l5SO5RHiHTwxg0aexkGOfApRdojWDcIBY_cfbKSKsC0,2286
48
48
  proj_flow/ext/sign/win32.py,sha256=yMAmO-DdIWZdOi_NxycRym8XM9WIsrWKtFANdIwthJ4,4968
49
+ proj_flow/ext/test_runner/__init__.py,sha256=dgPOf6b4AnoHVtPvhHrfC46m_4I-CCXvhQUsI-rXD-E,163
50
+ proj_flow/ext/test_runner/cli.py,sha256=qxDx5GJwbxK7ULlbgWpNKY9m_bMrHlRe7JdIuabU0T4,11880
51
+ proj_flow/ext/test_runner/driver/__init__.py,sha256=VJs_ODnF7-8DE0MbYKX-Bs-JDNqHgGdMkk2w142bcYQ,101
52
+ proj_flow/ext/test_runner/driver/commands.py,sha256=TzG9aqdUZLNa7yI2DajK7M7Ehhi_t8D_nEY6m1uICLo,2217
53
+ proj_flow/ext/test_runner/driver/test.py,sha256=xcZcBLNMkIN-jIQjtd7ke6Ngs4laTTd0TtMG_x_GlvE,19213
54
+ proj_flow/ext/test_runner/driver/testbed.py,sha256=jhxRvOXT1iEFIut9LIgJbixk7mUta9nKrcBEicaGhZA,4256
55
+ proj_flow/ext/test_runner/utils/__init__.py,sha256=VJs_ODnF7-8DE0MbYKX-Bs-JDNqHgGdMkk2w142bcYQ,101
56
+ proj_flow/ext/test_runner/utils/archives.py,sha256=f7whwVUeh5YtRwXxGCDs7CCRLd38aWPjwfb4bHBXyvI,1325
49
57
  proj_flow/ext/tools/__init__.py,sha256=m9iJeCkdmrqLjBWNx9hp4o1H2kgzIXpgypHiSf3SzKE,373
50
58
  proj_flow/ext/tools/pragma_once.py,sha256=BiNvX5X5pX_bGPZwBaKISlL6KkxJJGWFeZGSO-UDAgo,1218
51
59
  proj_flow/ext/tools/run_linter.py,sha256=_EdmTfITc_hwI5hgMc_mKFkbw4unnDGkWaFohs0RDr4,6145
@@ -86,19 +94,19 @@ proj_flow/log/release.py,sha256=tm25MACkcn7DpneR5z7vvnU24cGGvuH9tj-R7NXuRrE,4463
86
94
  proj_flow/log/hosting/__init__.py,sha256=9Teyw8jJcxeWH2MegqYEgW0n5OmSAWC7FFJj2u_UcrM,278
87
95
  proj_flow/log/hosting/github.py,sha256=O2BdB50vzVSKKIu3qNEYBiBdEUIPqj6C2xVvGAKjTZ4,9123
88
96
  proj_flow/log/rich_text/__init__.py,sha256=D3Y2jy9xlGgnQZdNC_ekoLzQtwkF_NTgLqDTWPvSRUk,279
89
- proj_flow/log/rich_text/api.py,sha256=PCSAGwkmDUMoVlpN7BDsgIA1AiMZEC0H6TUZXpr_Mg8,3571
97
+ proj_flow/log/rich_text/api.py,sha256=dJq2QMPbO4XczK4qux7q2Sb3vkGD_khUdn_N1FI8fBY,3513
90
98
  proj_flow/log/rich_text/markdown.py,sha256=jBnNxxhBHzyIZ3Y4HXDfqpl7zlRbbKbKdwdnZwkmNAI,1623
91
99
  proj_flow/log/rich_text/re_structured_text.py,sha256=DEl9KjBUF6cxfNWpQ7GVnHi7wKeuFnPGJwxQxjbCsnM,1823
92
100
  proj_flow/minimal/__init__.py,sha256=Yv32uwmS5a9SXSjaMVK0xKla9sWtcA8QkJHt15ffhiU,354
93
101
  proj_flow/minimal/base.py,sha256=jFAiJICAD6izCBqsNgt7syZ_lynpC5goNuEsaQv1a44,1227
94
102
  proj_flow/minimal/bootstrap.py,sha256=PcZfBsUmj8uDPGBC55iUgD5O7W4VSkpCQb6r9GEyAaQ,556
95
103
  proj_flow/minimal/init.py,sha256=-ZNzhPFqAgZFAuN5hYOJFuFy_wHkPzjeSk90G2GUQfk,4365
96
- proj_flow/minimal/list.py,sha256=RlOqammE8olNKXsnbv1enF5uriu0MZ2wFbht37Z2ETw,4810
97
- proj_flow/minimal/run.py,sha256=4qvGLqz2ayCZDvVBrq4tG094fjfcmDPon-xcGPQkM_U,4665
104
+ proj_flow/minimal/list.py,sha256=FIp49D0BW2UcOggibP_-5Ar1Ao_UmERMqHMDxNO7fRQ,8338
105
+ proj_flow/minimal/run.py,sha256=ASYisLyhPGVuDcaRIiZ5K7dtuaHXVihDskRSuveLccM,4702
98
106
  proj_flow/minimal/system.py,sha256=9FliH5TD103JYSAe2O5EU7hkOHDgVzTqu0Exxk-WrXE,1579
99
107
  proj_flow/minimal/ext/bug_report.py,sha256=dKy2FzVanoF3LISN5fsoaj-0TatGvVnahKuhy9HE4os,2311
100
108
  proj_flow/project/__init__.py,sha256=AROrwhbuMR5rJE-HC769eL4IXrMLQYpQb3HgpkOAYqg,293
101
- proj_flow/project/api.py,sha256=sWGrNisx6VwWxq5kwzBLkETkWshYrQOlKMBjZANMu2Y,2102
109
+ proj_flow/project/api.py,sha256=j0j3UBWi8vwGLIScSL7t2fWUr2-ezAilYkc1FM-EfYM,2103
102
110
  proj_flow/project/data.py,sha256=TluhBDoJEYL4dnyTpInmhQ49Uvf8mkWmpU-YMLQPNhE,317
103
111
  proj_flow/project/interact.py,sha256=t9brVfLC0HR3LUWs2RSBjMi7NgCBQc9_Ip1ut5oYVa4,9522
104
112
  proj_flow/project/cplusplus/__init__.py,sha256=cBjTOL8unMiPBWx9QkY4-vahzlHXNVNAxdaUTtVBjZM,302
@@ -160,8 +168,8 @@ proj_flow/template/licenses/MIT.mustache,sha256=NncPoQaNsuy-WmRmboik3fyhJJ8m5pc2
160
168
  proj_flow/template/licenses/Unlicense.mustache,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
161
169
  proj_flow/template/licenses/WTFPL.mustache,sha256=lvF4V_PrKKfZPa2TC8CZo8tlqaKvs3Bpv9G6XsWWQ4k,483
162
170
  proj_flow/template/licenses/Zlib.mustache,sha256=uIj-mhSjes2HJ3rRapyy2ALflKRz4xQgS4mVM9827C0,868
163
- proj_flow-0.20.3.dist-info/METADATA,sha256=j0vuyq16Bh2qoW4ECd-sPR3xQbrIK6aBZM9UHBHw3io,3035
164
- proj_flow-0.20.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
165
- proj_flow-0.20.3.dist-info/entry_points.txt,sha256=d_OmGKZzpY7FCWz0sZ4wnBAPZC75oMEzTgJZWtpDELo,49
166
- proj_flow-0.20.3.dist-info/licenses/LICENSE,sha256=vpOQJ5QlrTedF3coEWvA4wJzVJH304f66ZitR7Od4iU,1068
167
- proj_flow-0.20.3.dist-info/RECORD,,
171
+ proj_flow-0.22.0.dist-info/METADATA,sha256=PR4boGwINN-tDoh2CE4VgcX4tH-AjYVcVT_EHR_LYb8,3472
172
+ proj_flow-0.22.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
173
+ proj_flow-0.22.0.dist-info/entry_points.txt,sha256=d_OmGKZzpY7FCWz0sZ4wnBAPZC75oMEzTgJZWtpDELo,49
174
+ proj_flow-0.22.0.dist-info/licenses/LICENSE,sha256=vpOQJ5QlrTedF3coEWvA4wJzVJH304f66ZitR7Od4iU,1068
175
+ proj_flow-0.22.0.dist-info/RECORD,,