proj-flow 0.21.0__py3-none-any.whl → 0.22.1__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 (36) hide show
  1. proj_flow/__init__.py +1 -1
  2. proj_flow/api/completers.py +1 -1
  3. proj_flow/api/env.py +37 -13
  4. proj_flow/api/release.py +1 -1
  5. proj_flow/api/step.py +7 -3
  6. proj_flow/{ext/cplusplus/cmake/presets.py → base/cmake_presets.py} +44 -24
  7. proj_flow/base/plugins.py +12 -7
  8. proj_flow/cli/finder.py +4 -3
  9. proj_flow/dependency.py +6 -2
  10. proj_flow/ext/cplusplus/cmake/__init__.py +2 -2
  11. proj_flow/ext/cplusplus/cmake/parser.py +4 -3
  12. proj_flow/ext/cplusplus/cmake/steps.py +3 -9
  13. proj_flow/ext/cplusplus/conan/__init__.py +6 -4
  14. proj_flow/ext/github/publishing.py +1 -0
  15. proj_flow/ext/python/rtdocs.py +2 -2
  16. proj_flow/ext/python/steps.py +5 -4
  17. proj_flow/ext/python/version.py +12 -12
  18. proj_flow/ext/sign/__init__.py +2 -2
  19. proj_flow/ext/test_runner/__init__.py +6 -0
  20. proj_flow/ext/test_runner/cli.py +416 -0
  21. proj_flow/ext/test_runner/driver/__init__.py +2 -0
  22. proj_flow/ext/test_runner/driver/commands.py +74 -0
  23. proj_flow/ext/test_runner/driver/test.py +610 -0
  24. proj_flow/ext/test_runner/driver/testbed.py +141 -0
  25. proj_flow/ext/test_runner/utils/__init__.py +2 -0
  26. proj_flow/ext/test_runner/utils/archives.py +56 -0
  27. proj_flow/log/release.py +7 -4
  28. proj_flow/log/rich_text/api.py +3 -4
  29. proj_flow/minimal/ext/bug_report.py +6 -4
  30. proj_flow/minimal/ext/versions.py +48 -0
  31. proj_flow/minimal/run.py +3 -2
  32. {proj_flow-0.21.0.dist-info → proj_flow-0.22.1.dist-info}/METADATA +1 -1
  33. {proj_flow-0.21.0.dist-info → proj_flow-0.22.1.dist-info}/RECORD +36 -27
  34. {proj_flow-0.21.0.dist-info → proj_flow-0.22.1.dist-info}/WHEEL +0 -0
  35. {proj_flow-0.21.0.dist-info → proj_flow-0.22.1.dist-info}/entry_points.txt +0 -0
  36. {proj_flow-0.21.0.dist-info → proj_flow-0.22.1.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
proj_flow/log/release.py CHANGED
@@ -18,13 +18,16 @@ OneOrMoreStrings = Union[str, Iterable[str]]
18
18
 
19
19
 
20
20
  class VersionUpdater:
21
- def on_version_change(self, new_version: str) -> Optional[OneOrMoreStrings]:
21
+
22
+ def on_version_change(
23
+ self, rt: env.Runtime, new_version: str
24
+ ) -> Optional[OneOrMoreStrings]:
22
25
  return None
23
26
 
24
27
  def on_version_change_tags(
25
- self, new_version: str, tags: list[str]
28
+ self, rt: env.Runtime, new_version: str, tags: list[str]
26
29
  ) -> Optional[OneOrMoreStrings]:
27
- return self.on_version_change(new_version)
30
+ return self.on_version_change(rt, new_version)
28
31
 
29
32
 
30
33
  version_updaters = registry.Registry[VersionUpdater]("VersionUpdater")
@@ -118,7 +121,7 @@ def add_release(
118
121
  files_to_commit.append(version_path)
119
122
 
120
123
  for updater in version_updaters.get():
121
- modified = updater.on_version_change_tags(next_version, tags)
124
+ modified = updater.on_version_change_tags(rt, next_version, tags)
122
125
  if modified is None:
123
126
  continue
124
127
  elif isinstance(modified, str):
@@ -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
 
@@ -8,14 +8,14 @@ next to CHANGELOG.rst.
8
8
 
9
9
  import re
10
10
  import sys
11
- from typing import List, Tuple
12
11
 
12
+ from proj_flow.api import env
13
13
  from proj_flow.log import release
14
14
 
15
15
  YAML_PATH = ".github/ISSUE_TEMPLATE/bug_report.yaml"
16
16
 
17
17
 
18
- def _version(ver: str) -> Tuple[int, int, int, str]:
18
+ def _version(ver: str) -> tuple[int, int, int, str]:
19
19
  if ver[:1] == "v":
20
20
  ver = ver[1:]
21
21
 
@@ -26,7 +26,7 @@ def _version(ver: str) -> Tuple[int, int, int, str]:
26
26
  return (int(m.group(1)), int(m.group(2)), int(m.group(3)), ver)
27
27
 
28
28
 
29
- def _prev_version(new_version: str, tags: List[str]):
29
+ def _prev_version(new_version: str, tags: list[str]):
30
30
  current = _version(new_version)
31
31
  versions = [_version(tag) for tag in reversed(tags)]
32
32
  index = 0
@@ -44,7 +44,9 @@ def _prev_version(new_version: str, tags: List[str]):
44
44
 
45
45
  @release.version_updaters.add
46
46
  class VersionUpdater(release.VersionUpdater):
47
- def on_version_change_tags(self, new_version: str, tags: List[str]):
47
+ def on_version_change_tags(
48
+ self, rt: env.Runtime, new_version: str, tags: list[str]
49
+ ):
48
50
  old_version = _prev_version(new_version, tags)
49
51
 
50
52
  range = [f" - Current (v{new_version})\n"]
@@ -0,0 +1,48 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ """
5
+ The **proj_flow.minimal.ext.versions** update version schema files from version-updates
6
+ """
7
+
8
+ from pathlib import Path
9
+ from typing import cast
10
+
11
+ from proj_flow.api import env
12
+ from proj_flow.log import release
13
+
14
+
15
+ @release.version_updaters.add
16
+ class VersionUpdater(release.VersionUpdater):
17
+ def on_version_change_tags(
18
+ self, rt: env.Runtime, new_version: str, tags: list[str]
19
+ ) -> release.OneOrMoreStrings | None:
20
+ if not tags:
21
+ return None
22
+ prev_tag = tags[-1]
23
+ old_version = prev_tag[1:] if prev_tag.startswith("v") else prev_tag
24
+
25
+ files = cast(dict[str, str], rt._cfg.get("version-updates", {}))
26
+ changed_files: list[str] = []
27
+
28
+ rt.message("Config")
29
+ for key, value in files.items():
30
+ if "$VERSION" not in value:
31
+ continue
32
+
33
+ path = Path(key)
34
+ previous = value.replace("$VERSION", old_version)
35
+ next = value.replace("$VERSION", new_version)
36
+ rt.message(f" >> {key} :: {previous} -> {next}")
37
+
38
+ try:
39
+ content = path.read_text(encoding="UTF-8")
40
+ except FileNotFoundError:
41
+ rt.fatal(f"File not found: {key}")
42
+
43
+ new_content = content.replace(previous, next)
44
+ if new_content != content:
45
+ path.write_text(new_content, encoding="UTF-8")
46
+ changed_files.append(key)
47
+
48
+ return changed_files
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proj-flow
3
- Version: 0.21.0
3
+ Version: 0.22.1
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/
@@ -1,51 +1,59 @@
1
- proj_flow/__init__.py,sha256=Ef26YQuXsAeNUbAaqbrbiJborj7G6r-Xkd-gLQdaSLQ,277
1
+ proj_flow/__init__.py,sha256=7tP0EcGLKC5fwA3PTFj4xAYTAJtwRuR1CO_ZEs1F_Sg,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
5
  proj_flow/api/arg.py,sha256=0sVVEqdIOCqWmZtST_-u4rP6JkrResGJsKERQp3WYgc,5392
6
- proj_flow/api/completers.py,sha256=NapNVu6QAQ_iF6dqcAzOV5kDHKD9MAMVX209Bklq-Mw,2464
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=Tjgz73TPF6SotyxsZy1L7oid2FSnBQwJu8r1qDyC7WU,12994
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=3yxWWCRqWB-2iro7oA_247xqykiMCrI4hKQV5uemJak,5205
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=hrAMgFj9TP8mguNsGgRKB4rDOMHIrC77sJKn2SzEAOM,5059
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=t4izW8CByc-kgzJEDoqGcKNh3c77e3GcwbsnCVSBkw4,4569
35
- proj_flow/ext/cplusplus/conan/__init__.py,sha256=3PdwAeNthjg6rK4EaNDphMVVHvcG4Dx0vtYzSmBIgJY,1968
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
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=tbWYKVg8BL2aGAjCDGPms-7EOth9PsB5B5E1N3tuCeE,7146
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
@@ -82,11 +90,11 @@ proj_flow/log/error.py,sha256=65Nvhfs_d1xSY4EB-ISdWgjotvg-on3iKjhAWHpsBYM,841
82
90
  proj_flow/log/fmt.py,sha256=o14aO3iEt5_KKp9SqcfkscqbMKuTI83NBoSXHcrb7Kg,330
83
91
  proj_flow/log/format.py,sha256=gp1kUoW0nYj5e7Ysu1c29Fh2ssfE1KBSDIYeUbhzN9g,333
84
92
  proj_flow/log/msg.py,sha256=zARmRZHFV3yG-fBnx00wal4Y0O5aGnL-6XcGwNBNKA4,6758
85
- proj_flow/log/release.py,sha256=tm25MACkcn7DpneR5z7vvnU24cGGvuH9tj-R7NXuRrE,4463
93
+ proj_flow/log/release.py,sha256=h3DI7yqTjrnVIlbyfVyrsxQ4GDO9OXiAJKpn3esCL-Y,4520
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
@@ -94,9 +102,10 @@ proj_flow/minimal/base.py,sha256=jFAiJICAD6izCBqsNgt7syZ_lynpC5goNuEsaQv1a44,122
94
102
  proj_flow/minimal/bootstrap.py,sha256=PcZfBsUmj8uDPGBC55iUgD5O7W4VSkpCQb6r9GEyAaQ,556
95
103
  proj_flow/minimal/init.py,sha256=-ZNzhPFqAgZFAuN5hYOJFuFy_wHkPzjeSk90G2GUQfk,4365
96
104
  proj_flow/minimal/list.py,sha256=FIp49D0BW2UcOggibP_-5Ar1Ao_UmERMqHMDxNO7fRQ,8338
97
- proj_flow/minimal/run.py,sha256=4qvGLqz2ayCZDvVBrq4tG094fjfcmDPon-xcGPQkM_U,4665
105
+ proj_flow/minimal/run.py,sha256=ASYisLyhPGVuDcaRIiZ5K7dtuaHXVihDskRSuveLccM,4702
98
106
  proj_flow/minimal/system.py,sha256=9FliH5TD103JYSAe2O5EU7hkOHDgVzTqu0Exxk-WrXE,1579
99
- proj_flow/minimal/ext/bug_report.py,sha256=dKy2FzVanoF3LISN5fsoaj-0TatGvVnahKuhy9HE4os,2311
107
+ proj_flow/minimal/ext/bug_report.py,sha256=YxaS0LjSPaDQARuhQUVSCqe4URIOa4VIhKfzDJqCcYA,2341
108
+ proj_flow/minimal/ext/versions.py,sha256=nj_AMnDKbDnHF55UtPc0aX0F8MGQOM12DkCgNGjJb-4,1539
100
109
  proj_flow/project/__init__.py,sha256=AROrwhbuMR5rJE-HC769eL4IXrMLQYpQb3HgpkOAYqg,293
101
110
  proj_flow/project/api.py,sha256=j0j3UBWi8vwGLIScSL7t2fWUr2-ezAilYkc1FM-EfYM,2103
102
111
  proj_flow/project/data.py,sha256=TluhBDoJEYL4dnyTpInmhQ49Uvf8mkWmpU-YMLQPNhE,317
@@ -160,8 +169,8 @@ proj_flow/template/licenses/MIT.mustache,sha256=NncPoQaNsuy-WmRmboik3fyhJJ8m5pc2
160
169
  proj_flow/template/licenses/Unlicense.mustache,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
161
170
  proj_flow/template/licenses/WTFPL.mustache,sha256=lvF4V_PrKKfZPa2TC8CZo8tlqaKvs3Bpv9G6XsWWQ4k,483
162
171
  proj_flow/template/licenses/Zlib.mustache,sha256=uIj-mhSjes2HJ3rRapyy2ALflKRz4xQgS4mVM9827C0,868
163
- proj_flow-0.21.0.dist-info/METADATA,sha256=sTxFh9SPVTvanRQf1ccSU9Yo4_YbElioA_G5pI0YIW8,3472
164
- proj_flow-0.21.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
165
- proj_flow-0.21.0.dist-info/entry_points.txt,sha256=d_OmGKZzpY7FCWz0sZ4wnBAPZC75oMEzTgJZWtpDELo,49
166
- proj_flow-0.21.0.dist-info/licenses/LICENSE,sha256=vpOQJ5QlrTedF3coEWvA4wJzVJH304f66ZitR7Od4iU,1068
167
- proj_flow-0.21.0.dist-info/RECORD,,
172
+ proj_flow-0.22.1.dist-info/METADATA,sha256=dWZixZZDgrrewFkhT7yvGMRoYUIdGtVKdtDkek58UbM,3472
173
+ proj_flow-0.22.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
174
+ proj_flow-0.22.1.dist-info/entry_points.txt,sha256=d_OmGKZzpY7FCWz0sZ4wnBAPZC75oMEzTgJZWtpDELo,49
175
+ proj_flow-0.22.1.dist-info/licenses/LICENSE,sha256=vpOQJ5QlrTedF3coEWvA4wJzVJH304f66ZitR7Od4iU,1068
176
+ proj_flow-0.22.1.dist-info/RECORD,,