proj-flow 0.9.4__py3-none-any.whl → 0.10.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.
proj_flow/__init__.py CHANGED
@@ -1,4 +1,9 @@
1
1
  # Copyright (c) 2025 Marcin Zdun
2
2
  # This code is licensed under MIT license (see LICENSE for details)
3
3
 
4
- __version__ = "0.9.4"
4
+ """
5
+ The **proj_flow** contains only ``__version__`` to be updated, nothing more.
6
+ This is in an attempt to make this module easy to load initially.
7
+ """
8
+
9
+ __version__ = "0.10.0"
proj_flow/api/arg.py CHANGED
@@ -12,48 +12,71 @@ from dataclasses import dataclass, field
12
12
 
13
13
  from proj_flow.base import inspect as _inspect
14
14
 
15
+ T = typing.TypeVar("T")
16
+ LazyArgument = typing.Union[T, typing.Callable[[], T]]
17
+
18
+
19
+ def _eval(arg: LazyArgument[T]) -> T:
20
+ if callable(arg):
21
+ return typing.cast(T, arg())
22
+ return arg
23
+
24
+
25
+ class _Completable(typing.Protocol):
26
+ completer: _inspect.Function
27
+
15
28
 
16
29
  @dataclass
17
30
  class Argument:
18
- help: str = ""
31
+ help: LazyArgument[str] = ""
19
32
  pos: bool = False
20
- names: typing.List[str] = field(default_factory=list)
21
- nargs: typing.Union[str, int, None] = None
22
- opt: typing.Optional[bool] = None
23
- meta: typing.Optional[str] = None
24
- action: typing.Union[str, argparse.Action, None] = None
25
- default: typing.Optional[typing.Any] = None
26
- choices: typing.Optional[typing.List[str]] = None
33
+ names: LazyArgument[typing.List[str]] = field(default_factory=list)
34
+ nargs: LazyArgument[typing.Union[str, int, None]] = None
35
+ opt: LazyArgument[typing.Optional[bool]] = None
36
+ meta: LazyArgument[typing.Optional[str]] = None
37
+ action: LazyArgument[typing.Union[str, argparse.Action, None]] = None
38
+ default: LazyArgument[typing.Optional[typing.Any]] = None
39
+ choices: LazyArgument[typing.Optional[typing.List[str]]] = None
27
40
  completer: typing.Optional[_inspect.Function] = None
28
41
 
29
42
  def visit(self, parser: argparse.ArgumentParser, name: str):
30
43
  kwargs = {}
31
- if self.help is not None:
32
- kwargs["help"] = self.help
33
- if self.nargs is not None:
34
- kwargs["nargs"] = self.nargs
35
- if self.meta is not None:
36
- kwargs["metavar"] = self.meta
37
- if self.default is not None:
38
- kwargs["default"] = self.default
39
- if self.action is not None:
40
- kwargs["action"] = self.action
41
- if self.choices is not None:
42
- kwargs["choices"] = self.choices
44
+
45
+ self_help = _eval(self.help)
46
+ self_names = _eval(self.names)
47
+ self_nargs = _eval(self.nargs)
48
+ self_opt = _eval(self.opt)
49
+ self_meta = _eval(self.meta)
50
+ self_action = _eval(self.action)
51
+ self_default = _eval(self.default)
52
+ self_choices = _eval(self.choices)
53
+
54
+ if self_help is not None:
55
+ kwargs["help"] = self_help
56
+ if self_nargs is not None:
57
+ kwargs["nargs"] = self_nargs
58
+ if self_meta is not None:
59
+ kwargs["metavar"] = self_meta
60
+ if self_default is not None:
61
+ kwargs["default"] = self_default
62
+ if self_action is not None:
63
+ kwargs["action"] = self_action
64
+ if self_choices is not None:
65
+ kwargs["choices"] = self_choices
43
66
 
44
67
  names = (
45
- [name] if self.pos else self.names if len(self.names) > 0 else [f"--{name}"]
68
+ [name] if self.pos else self_names if len(self_names) > 0 else [f"--{name}"]
46
69
  )
47
70
 
48
71
  if self.pos:
49
- kwargs["nargs"] = "?" if self.opt else 1
72
+ kwargs["nargs"] = "?" if self_opt else 1
50
73
  else:
51
74
  kwargs["dest"] = name
52
- kwargs["required"] = not self.opt
75
+ kwargs["required"] = not self_opt
53
76
 
54
77
  action = parser.add_argument(*names, **kwargs)
55
78
  if self.completer:
56
- action.completer = self.completer # type: ignore
79
+ typing.cast(_Completable, action).completer = self.completer
57
80
 
58
81
  return action
59
82
 
proj_flow/api/ctx.py CHANGED
@@ -9,9 +9,10 @@ import datetime
9
9
  import inspect
10
10
  import os
11
11
  from dataclasses import dataclass
12
- from typing import Callable, Dict, Iterable, List, Optional, Union
12
+ from typing import Any, Callable, Dict, Iterable, List, Optional, Union, cast
13
13
 
14
14
  from proj_flow.base import cmd
15
+ from proj_flow.base import inspect as _inspect
15
16
 
16
17
  package_root = os.path.dirname(os.path.dirname(__file__))
17
18
  template_dir = "template"
@@ -26,32 +27,44 @@ SettingsType = Dict[str, StrOrBool]
26
27
  class Setting:
27
28
  json_key: str
28
29
  prompt: str = ""
29
- value: Union[Values, Callable[[], Values]] = ""
30
+ value: Union[Values, Callable[[], Values], Callable[[SettingsType], Values]] = ""
30
31
  fix: Optional[str] = None
31
32
  force_fix: bool = False
33
+ project: Optional[str] = None
32
34
 
33
35
  def calc_value(self, previous: SettingsType):
34
- if isinstance(self.value, Callable):
36
+ if callable(self.value):
35
37
  kwargs = {}
36
38
 
37
39
  params = inspect.signature(self.value).parameters
38
40
  if "settings" in params:
39
41
  kwargs["settings"] = previous
40
42
 
41
- return self.value(**kwargs)
43
+ return cast(_inspect.Function, self.value)(**kwargs)
42
44
 
43
45
  return self.value
44
46
 
45
47
 
46
- def register_init_setting(*setting: Setting, is_hidden=False):
48
+ def register_init_setting(*settings: Setting, is_hidden=False, project: Optional[str]):
49
+ if project is not None:
50
+ for setting in settings:
51
+ setting.project = project
52
+ (hidden if is_hidden else defaults).extend(settings)
53
+
54
+
55
+ def register_common_init_setting(*setting: Setting, is_hidden=False):
47
56
  (hidden if is_hidden else defaults).extend(setting)
48
57
 
49
58
 
50
- def register_switch(key: str, prompt: str, enabled: bool):
59
+ def register_switch(key: str, prompt: str, enabled: bool, project: Optional[str]):
60
+ switches.append(Setting(key, prompt, value=enabled, project=project))
61
+
62
+
63
+ def register_common_switch(key: str, prompt: str, enabled: bool):
51
64
  switches.append(Setting(key, prompt, value=enabled))
52
65
 
53
66
 
54
- def register_internal(key: str, value: any):
67
+ def register_internal(key: str, value: Any):
55
68
  internals[key] = value
56
69
 
57
70
 
@@ -59,17 +72,20 @@ def _git_config(name: str):
59
72
  def wrap():
60
73
  proc = cmd.run("git", "config", name, capture_output=True)
61
74
  if proc is None or proc.returncode != 0:
62
- return None
75
+ return ""
63
76
  return proc.stdout.strip()
64
77
 
65
78
  return wrap
66
79
 
67
80
 
68
- def move_to_front(preferred: str, values: Iterable[str]):
81
+ def move_to_front(preferred: str, values: Iterable[Optional[str]]):
69
82
  result: List[str] = []
70
83
 
71
84
  has_preferred = False
72
85
  for value in values:
86
+ if value is None:
87
+ continue
88
+
73
89
  if value == preferred:
74
90
  has_preferred = True
75
91
  else:
@@ -94,7 +110,7 @@ def _enum_licenses():
94
110
  root = os.path.abspath(os.path.join(package_root, template_dir, "licenses"))
95
111
  for _, dirnames, filenames in os.walk(root):
96
112
  dirnames[:] = []
97
- iter = filter(lambda x: x is not None, map(_as_mustache, filenames))
113
+ iter = map(_as_mustache, filenames)
98
114
  return move_to_front("MIT", iter)
99
115
  return []
100
116
 
@@ -114,11 +130,11 @@ def _get_nothing(_: SettingsType) -> StrOrBool:
114
130
 
115
131
 
116
132
  def _map(internal_key: str):
117
- def impl(key: str):
133
+ def impl(key: StrOrBool) -> StrOrBool:
118
134
  mapped = internals.get(internal_key)
119
135
  if not isinstance(mapped, dict):
120
- return None
121
- return mapped.get(key)
136
+ return ""
137
+ return mapped.get(key, "")
122
138
 
123
139
  return impl
124
140
 
@@ -152,12 +168,12 @@ def _build_fixup(settings: SettingsType, fixup: str):
152
168
  value = code(settings)
153
169
 
154
170
  if result:
155
- result += value
171
+ result = f"{result}{value}"
156
172
  else:
157
173
  result = value
158
174
 
159
175
  if verbose:
160
- result += verbose
176
+ result = f"{result}{verbose}"
161
177
 
162
178
  return result
163
179
 
@@ -169,7 +185,7 @@ def _fixed(fixup: str):
169
185
  return wrap
170
186
 
171
187
 
172
- internals = {}
188
+ internals: Dict[str, Any] = {}
173
189
 
174
190
  switches: List[Setting] = []
175
191
 
@@ -200,18 +216,21 @@ defaults: List[Setting] = [
200
216
  "INCLUDE_PREFIX",
201
217
  'Prefix for includes (as in #include "{PREFIX}/version.hpp")',
202
218
  _fixed("{PROJECT.NAME}"),
219
+ project="cxx",
203
220
  ),
204
221
  Setting(
205
222
  "NAME_PREFIX",
206
223
  "CMake variable name prefix",
207
224
  _fixed("{PROJECT.NAME$safe$upper}"),
225
+ project="cxx",
208
226
  ),
209
227
  Setting(
210
228
  "NAMESPACE",
211
229
  "C++ namespace for the project",
212
230
  _fixed("{PROJECT.NAME$safe}"),
231
+ project="cxx",
213
232
  ),
214
- Setting("EXT", "Extension for code files", _list_ext),
233
+ Setting("EXT", "Extension for code files", _list_ext, project="cxx"),
215
234
  Setting("SRCDIR", "Directory for code files", "src"),
216
235
  Setting(
217
236
  "INCLUDEDIR",
@@ -219,20 +238,21 @@ defaults: List[Setting] = [
219
238
  "include",
220
239
  "{INCLUDEDIR}/{INCLUDE_PREFIX}",
221
240
  force_fix=True,
241
+ project="cxx",
222
242
  ),
223
243
  ]
224
244
 
225
245
  hidden: List[Setting] = [
226
- Setting("EXT.cxx", fix="{EXT}"),
227
- Setting("EXT.hxx", fix="{EXT.cxx$header}"),
246
+ Setting("EXT.cxx", fix="{EXT}", project="cxx"),
247
+ Setting("EXT.hxx", fix="{EXT.cxx$header}", project="cxx"),
228
248
  ]
229
249
 
230
250
  _fileext = {".cc": ".hh", ".cxx": ".hxx", ".cpp": ".hpp"}
231
251
 
232
252
 
233
253
  _filters: Dict[str, Callable[[StrOrBool], StrOrBool]] = {
234
- "safe": lambda value: value.replace("-", "_"),
235
- "upper": lambda value: value.upper(),
236
- "lower": lambda value: value.lower(),
237
- "header": lambda cxx_ext: _fileext.get(cxx_ext, ".hpp"),
254
+ "safe": lambda value: str(value).replace("-", "_"),
255
+ "upper": lambda value: str(value).upper(),
256
+ "lower": lambda value: str(value).lower(),
257
+ "header": lambda cxx_ext: _fileext.get(str(cxx_ext), ".hpp"),
238
258
  }
proj_flow/api/step.py CHANGED
@@ -12,6 +12,7 @@ from typing import List, cast
12
12
  from proj_flow.api.env import Config, Runtime
13
13
  from proj_flow.base import inspect as _inspect
14
14
  from proj_flow.base import matrix
15
+ from proj_flow.base.name_list import name_list
15
16
 
16
17
 
17
18
  class Step(ABC):
@@ -111,11 +112,8 @@ def _name_list(label: str, names: List[str], template="`{}`") -> str:
111
112
  if len(names) == 0:
112
113
  return ""
113
114
 
114
- em = [template.format(name) for name in names]
115
- prefix = ", ".join(em[:-1])
116
- if prefix:
117
- prefix += " and "
118
- return f"\n:{label}: {prefix}{em[-1]}"
115
+ joined = name_list([template.format(name) for name in names])
116
+ return f"\n:{label}: {joined}"
119
117
 
120
118
 
121
119
  def _make_private(f: _inspect.Function):
@@ -0,0 +1,19 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ """
5
+ The **proj_flow.base.name_list** provides name list helper for arguments with
6
+ choices
7
+ """
8
+
9
+ from typing import List
10
+
11
+
12
+ def name_list(names: List[str]) -> str:
13
+ if len(names) == 0:
14
+ return ""
15
+
16
+ prefix = ", ".join(names[:-1])
17
+ if prefix:
18
+ prefix += " and "
19
+ return f"{prefix}{names[-1]}"
@@ -7,6 +7,7 @@ to register the plugins with a decorator.
7
7
  """
8
8
 
9
9
  import typing
10
+ from collections import OrderedDict
10
11
 
11
12
  T = typing.TypeVar("T")
12
13
  K = typing.TypeVar("K")
@@ -101,6 +102,19 @@ _debug_copies: typing.List[Registry] = []
101
102
  def verbose_info():
102
103
  for registry in _debug_copies:
103
104
  for item in registry.container:
104
- print(
105
- f"-- {registry.name}: adding `{item.__module__}.{item.__class__.__name__}`"
106
- )
105
+ full_name = f"{item.__module__}.{item.__class__.__name__}"
106
+
107
+ kw = OrderedDict()
108
+
109
+ if hasattr(item, "name"):
110
+ kw["name"] = getattr(item, "name")
111
+ elif hasattr(item, "__name__"):
112
+ kw["name"] = getattr(item, "__name__")
113
+
114
+ if hasattr(item, "id"):
115
+ kw["id"] = getattr(item, "id")
116
+
117
+ items = ", ".join([f"{key}={value}" for key, value in kw.items()])
118
+ if len(items) > 0:
119
+ items = f" ({items})"
120
+ print(f"-- {registry.name}: adding `{full_name}`{items}")
proj_flow/cli/__init__.py CHANGED
@@ -29,7 +29,7 @@ def main():
29
29
  def _change_dir():
30
30
  root = argparse.ArgumentParser(
31
31
  prog="proj-flow",
32
- usage="proj-flow [-h] [--version] [-C [dir]] {command} ...",
32
+ usage="proj-flow [-h] [--version] [-C [dir]] command ...",
33
33
  add_help=False,
34
34
  )
35
35
  root.add_argument("-C", dest="cd", nargs="?")
proj_flow/cli/argument.py CHANGED
@@ -192,7 +192,7 @@ class Command:
192
192
  if len(self.children):
193
193
  subparsers = parser.add_subparsers(
194
194
  dest=f"command_{level}",
195
- metavar="{command}",
195
+ metavar="command",
196
196
  help="Known command name, see below",
197
197
  )
198
198
  subparsers.parent = parser # type: ignore
@@ -254,7 +254,7 @@ def _argparse_visit_all(
254
254
  parser.shortcuts = shortcut_configs
255
255
 
256
256
  subparsers = parser.add_subparsers(
257
- dest="command", metavar="{command}", help="Known command name, see below"
257
+ dest="command", metavar="command", help="Known command name, see below"
258
258
  )
259
259
 
260
260
  subparsers.parent = parser # type: ignore
@@ -2,7 +2,7 @@
2
2
  # This code is licensed under MIT license (see LICENSE for details)
3
3
 
4
4
  """
5
- The **proj_flow.flow.dependency** verifies availabilty of Step's external tools.
5
+ The **proj_flow.dependency** verifies availabilty of Step's external tools.
6
6
  """
7
7
 
8
8
  import re
@@ -1,5 +1,10 @@
1
1
  # Copyright (c) 2025 Marcin Zdun
2
2
  # This code is licensed under MIT license (see LICENSE for details)
3
3
 
4
+ """
5
+ The **proj_flow.ext.cplusplus.cmake.__version__** contains only the value of
6
+ expected version of CMake.
7
+ """
8
+
4
9
 
5
10
  CMAKE_VERSION = "3.28"
@@ -11,7 +11,8 @@ import uuid
11
11
 
12
12
  import chevron
13
13
 
14
- from proj_flow import api, flow
14
+ from proj_flow import api, project
15
+ from proj_flow.project import cplusplus
15
16
 
16
17
  from .__version__ import CMAKE_VERSION
17
18
 
@@ -49,22 +50,23 @@ class CMakeInit(api.init.InitStep):
49
50
  def _list_cmake_types():
50
51
  return api.ctx.move_to_front(
51
52
  "console-application",
52
- sorted(key for key in flow.init.get_internal("cmake").keys() if key),
53
+ sorted(key for key in project.data.get_internal("cmake").keys() if key),
53
54
  )
54
55
 
55
56
 
56
57
  api.init.register_init_step(CMakeInit())
57
- api.ctx.register_init_setting(
58
+
59
+ cplusplus.project.register_init_setting(
58
60
  api.ctx.Setting("PROJECT.TYPE", "CMake project type", _list_cmake_types)
59
61
  )
60
- api.ctx.register_init_setting(
62
+ cplusplus.project.register_init_setting(
61
63
  api.ctx.Setting("cmake", fix="{PROJECT.TYPE$map:cmake}"),
62
64
  api.ctx.Setting("CMAKE_VERSION", value=CMAKE_VERSION),
63
65
  api.ctx.Setting("PROJECT.WIX.UPGRADE_GUID", value=lambda: str(uuid.uuid4())),
64
66
  is_hidden=True,
65
67
  )
66
- api.ctx.register_switch("with_cmake", "Use CMake", True)
67
- api.ctx.register_internal(
68
+ cplusplus.project.register_switch("with_cmake", "Use CMake", True)
69
+ cplusplus.project.register_internal(
68
70
  "cmake",
69
71
  {
70
72
  "": {"cmd": "add_executable", "type": ""},
@@ -9,7 +9,8 @@ import os
9
9
  import textwrap
10
10
  from typing import List
11
11
 
12
- from proj_flow.api import ctx, env, step
12
+ from proj_flow.api import env, step
13
+ from proj_flow.project import cplusplus
13
14
 
14
15
  from ._conan import conan_api
15
16
 
@@ -64,4 +65,6 @@ class ConanConfig:
64
65
  return 0
65
66
 
66
67
 
67
- ctx.register_switch("with_conan", "Use Conan for dependency manager", True)
68
+ cplusplus.project.register_switch(
69
+ "with_conan", "Use Conan for dependency manager", True
70
+ )
@@ -13,7 +13,7 @@ from typing import Callable, List, cast
13
13
 
14
14
  from proj_flow.api.env import Config, Runtime
15
15
  from proj_flow.base import cmd
16
- from proj_flow.flow.dependency import VER_REGEX
16
+ from proj_flow.dependency import VER_REGEX
17
17
 
18
18
 
19
19
  class conan(ABC):
@@ -16,22 +16,13 @@ import typing
16
16
 
17
17
  from proj_flow import log
18
18
  from proj_flow.api import arg, env
19
+ from proj_flow.base.name_list import name_list
19
20
  from proj_flow.flow.configs import Configs
20
21
  from proj_flow.log import commit, hosting, rich_text
21
22
 
22
23
  FORCED_LEVEL_CHOICES = list(commit.FORCED_LEVEL.keys())
23
24
 
24
25
 
25
- def _name_list(names: typing.List[str]) -> str:
26
- if len(names) == 0:
27
- return ""
28
-
29
- prefix = ", ".join(names[:-1])
30
- if prefix:
31
- prefix += " and "
32
- return f"{prefix}{names[-1]}"
33
-
34
-
35
26
  @arg.command("github")
36
27
  def github():
37
28
  """Interact with GitHub workflows and releases"""
@@ -81,7 +72,7 @@ def release(
81
72
  typing.Optional[str],
82
73
  arg.Argument(
83
74
  help="Ignore the version change from changelog and instead use this value. "
84
- f"Allowed values are: {_name_list(FORCED_LEVEL_CHOICES)}",
75
+ f"Allowed values are: {name_list(FORCED_LEVEL_CHOICES)}",
85
76
  meta="level",
86
77
  choices=FORCED_LEVEL_CHOICES,
87
78
  ),
@@ -7,7 +7,7 @@ projects.
7
7
 
8
8
  from proj_flow.api import ctx
9
9
 
10
- ctx.register_switch("with_github_actions", "Use Github Actions", True)
11
- ctx.register_switch(
10
+ ctx.register_common_switch("with_github_actions", "Use Github Actions", True)
11
+ ctx.register_common_switch(
12
12
  "with_github_social", "Use Github ISSUE_TEMPLATE, CONTRIBUTING.md, etc.", True
13
13
  )
@@ -6,6 +6,6 @@ The **proj_flow.flow** contains the inner workings of various *Project Flow*
6
6
  components.
7
7
  """
8
8
 
9
- from . import configs, dependency, init, interact, layer, steps
9
+ from . import configs, layer, steps
10
10
 
11
- __all__ = ["configs", "dependency", "init", "interact", "layer", "steps"]
11
+ __all__ = ["configs", "layer", "steps"]
proj_flow/minimal/base.py CHANGED
@@ -26,5 +26,6 @@ api.init.register_init_step(GitInit())
26
26
  api.ctx.register_init_setting(
27
27
  api.ctx.Setting("__flow_version__", value=__version__),
28
28
  api.ctx.Setting("${", value="${"),
29
+ project=None,
29
30
  is_hidden=True,
30
31
  )
proj_flow/minimal/init.py CHANGED
@@ -10,12 +10,36 @@ import os
10
10
  import sys
11
11
  from typing import Annotated, Optional
12
12
 
13
- from proj_flow import flow
13
+ import yaml
14
+
15
+ from proj_flow import dependency, flow
14
16
  from proj_flow.api import arg, ctx, env, init
17
+ from proj_flow.base.name_list import name_list
18
+ from proj_flow.project import api, interact
19
+
20
+
21
+ def _project_types():
22
+ return list(map(lambda proj: proj.id, api.project_type.get()))
23
+
24
+
25
+ def _project_help():
26
+ return (
27
+ "Type of project to create. "
28
+ f"Allowed values are: {name_list(_project_types())}"
29
+ )
15
30
 
16
31
 
17
32
  @arg.command("init")
18
33
  def main(
34
+ project: Annotated[
35
+ str,
36
+ arg.Argument(
37
+ help=_project_help,
38
+ meta="project",
39
+ pos=True,
40
+ choices=_project_types,
41
+ ),
42
+ ],
19
43
  path: Annotated[
20
44
  Optional[str],
21
45
  arg.Argument(
@@ -38,26 +62,35 @@ def main(
38
62
  ):
39
63
  """Initialize new project"""
40
64
 
65
+ try:
66
+ current_project = api.get_project_type(project)
67
+ except api.ProjectNotFound:
68
+ print(f"proj-flow init: error: project type `{project}` is not known")
69
+ return 1
70
+
41
71
  if path is not None:
42
72
  os.makedirs(path, exist_ok=True)
43
73
  os.chdir(path)
44
74
 
45
- errors = flow.dependency.verify(flow.dependency.gather(init.__steps))
75
+ errors = dependency.verify(dependency.gather(init.__steps))
46
76
  if len(errors) > 0:
47
77
  if not rt.silent:
48
78
  for error in errors:
49
79
  print(f"proj-flow: {error}", file=sys.stderr)
50
80
  return 1
51
81
 
52
- context = flow.init.fixup(
53
- flow.init.all_default() if non_interactive else flow.interact.prompt()
54
- )
82
+ context = current_project.get_context(not non_interactive)
55
83
  if not non_interactive and not rt.silent:
56
84
  print()
57
85
 
58
- if save_context:
59
- with open(".context.json", "w", encoding="UTF-8") as jsonf:
60
- json.dump(context, jsonf, ensure_ascii=False, indent=4)
86
+ if save_context and rt.verbose:
87
+ lines = yaml.dump(context, indent=4).rstrip().split("\n")
88
+ for line in lines:
89
+ rt.message("[CONTEXT]", line)
90
+
91
+ if save_context and not rt.dry_run:
92
+ with open(".context.yaml", "w", encoding="UTF-8") as jsonf:
93
+ yaml.dump(context, jsonf, indent=4)
61
94
 
62
95
  flow.layer.copy_license(rt, context)
63
96
  if not rt.silent:
@@ -67,9 +100,9 @@ def main(
67
100
  for fs_layer in layers:
68
101
  fs_layer.run(rt, context)
69
102
 
70
- if save_context:
103
+ if save_context and not rt.dry_run:
71
104
  with open(".gitignore", "ab") as ignoref:
72
- ignoref.write("\n/.context.json\n".encode("UTF-8"))
105
+ ignoref.write("\n/.context.yaml\n".encode("UTF-8"))
73
106
 
74
107
  for step in init.__steps:
75
108
  step.postprocess(rt, context)
proj_flow/minimal/run.py CHANGED
@@ -11,9 +11,8 @@ import sys
11
11
  from contextlib import contextmanager
12
12
  from typing import Annotated, List, Optional, Set, cast
13
13
 
14
- from proj_flow import api
14
+ from proj_flow import api, dependency
15
15
  from proj_flow.base import matrix
16
- from proj_flow.flow import dependency
17
16
  from proj_flow.flow.configs import Configs
18
17
 
19
18
 
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ """
5
+ The **proj_flow.project** contains the inner workings of ``proj-flow init``
6
+ command.
7
+ """
8
+
9
+ from . import api, cplusplus, data, interact
10
+
11
+ __all__ = ["api", "cplusplus", "data", "interact"]
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ """
5
+ The **proj_flow.project.api** defines an extension point for project suites.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from typing import Any, List, NamedTuple, Optional
10
+
11
+ from proj_flow import base
12
+ from proj_flow.api import ctx
13
+ from proj_flow.project import interact
14
+
15
+
16
+ class ProjectType(ABC):
17
+ name: str
18
+ id: str
19
+
20
+ def __init__(self, name: str, id: str):
21
+ self.name = name
22
+ self.id = id
23
+
24
+ def register_switch(self, key: str, prompt: str, enabled: bool):
25
+ ctx.register_switch(key, prompt, enabled, self.id)
26
+
27
+ def register_internal(self, key: str, value: Any):
28
+ ctx.register_internal(key, value)
29
+
30
+ def register_init_setting(self, *settings: ctx.Setting, is_hidden=False):
31
+ ctx.register_init_setting(*settings, is_hidden=is_hidden, project=self.id)
32
+
33
+ def get_context(self, interactive: bool):
34
+ return interact.get_context(interactive, self.id)
35
+
36
+
37
+ project_type = base.registry.Registry[ProjectType]("ProjectType")
38
+
39
+
40
+ class ProjectNotFound(Exception):
41
+ name: str
42
+
43
+ def __init__(self, name: str):
44
+ self.name = name
45
+
46
+
47
+ def get_project_type(id: str):
48
+ result, _ = project_type.find(lambda proj: proj.id == id)
49
+ if result is None:
50
+ raise ProjectNotFound(id)
51
+ return result
@@ -0,0 +1,17 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ """
5
+ The **proj_flow.project.cplusplus** registers a ``"C++"`` projects support.
6
+ """
7
+
8
+ from proj_flow.project import api
9
+
10
+
11
+ @api.project_type.add
12
+ class CPlusPlus(api.ProjectType):
13
+ def __init__(self):
14
+ super().__init__("C++ + CMake + Conan", "cxx")
15
+
16
+
17
+ project = api.get_project_type("cxx")
@@ -0,0 +1,14 @@
1
+ # Copyright (c) 2025 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ """
5
+ The **proj_flow.project.data** supports the ``init`` command.
6
+ """
7
+
8
+ from typing import Any
9
+
10
+ from proj_flow.api import ctx
11
+
12
+
13
+ def get_internal(key: str, value: Any = None):
14
+ return ctx.internals.get(key, value)
@@ -2,15 +2,16 @@
2
2
  # This code is licensed under MIT license (see LICENSE for details)
3
3
 
4
4
  """
5
- The **proj_flow.flow.interact** provides initialization context through user
6
- prompts.
5
+ The **proj_flow.project.interact** provides initialization context through
6
+ user prompts.
7
7
  """
8
8
 
9
9
  from dataclasses import dataclass
10
- from typing import List, Union
10
+ from typing import Callable, List, Optional, Union
11
11
 
12
12
  from prompt_toolkit import prompt as tk_prompt
13
13
  from prompt_toolkit.completion import WordCompleter
14
+ from prompt_toolkit.formatted_text.base import AnyFormattedText
14
15
  from prompt_toolkit.shortcuts import CompleteStyle
15
16
  from prompt_toolkit.validation import Validator
16
17
 
@@ -39,7 +40,7 @@ class _Question:
39
40
  def ps(self):
40
41
  return self.prompt or f'"{self.key}"'
41
42
 
42
- def _ps(self, default: ctx.Values, counter: int, size: int):
43
+ def _ps(self, default: ctx.Values, counter: int, size: int) -> AnyFormattedText:
43
44
  if default:
44
45
  if isinstance(default, str):
45
46
  return [
@@ -89,7 +90,7 @@ class _Question:
89
90
 
90
91
  def _tk_prompt(
91
92
  self,
92
- defaults: Union[bool | List[str]],
93
+ defaults: Union[bool, List[str]],
93
94
  words: List[str],
94
95
  counter: int,
95
96
  size: int,
@@ -108,27 +109,127 @@ class _Question:
108
109
  )
109
110
 
110
111
 
111
- def prompt() -> ctx.SettingsType:
112
- """
113
- Prompts user to provide details of newly-crated project.
112
+ def _project_filter(project: Optional[str]):
113
+ if project is None:
114
114
 
115
- :returns: Dictionary with answers to all interactive settings and switches.
116
- """
115
+ def impl(setting: ctx.Setting):
116
+ return setting.project is None
117
+
118
+ return impl
119
+
120
+ def impl(setting: ctx.Setting):
121
+ return setting.project is None or setting.project == project
122
+
123
+ return impl
124
+
125
+
126
+ def _prompt(wanted: Callable[[ctx.Setting], bool]) -> ctx.SettingsType:
117
127
  settings: ctx.SettingsType = {}
118
128
 
119
- size = len(ctx.defaults) + len(ctx.switches)
129
+ defaults = [setting for setting in ctx.defaults if wanted(setting)]
130
+ switches = [setting for setting in ctx.switches if wanted(setting)]
131
+
132
+ size = len(defaults) + len(switches)
120
133
  counter = 1
121
134
 
122
- for setting in ctx.defaults:
135
+ for setting in defaults:
123
136
  loaded = _Question.load_default(setting, settings)
124
137
  value = loaded.interact(counter, size)
125
138
  settings[loaded.key] = value
126
139
  counter += 1
127
140
 
128
- for setting in ctx.switches:
141
+ for setting in switches:
129
142
  loaded = _Question.load_default(setting, settings)
130
143
  value = loaded.interact(counter, size)
131
144
  settings[loaded.key] = value
132
145
  counter += 1
133
146
 
134
147
  return settings
148
+
149
+
150
+ def _all_default(wanted: Callable[[ctx.Setting], bool]):
151
+ """
152
+ Chooses default answers for all details of newly-crated project.
153
+
154
+ :returns: Dictionary with default values of all interactive settings
155
+ and switches.
156
+ """
157
+
158
+ settings: ctx.SettingsType = {}
159
+
160
+ defaults = [setting for setting in ctx.defaults if wanted(setting)]
161
+ switches = [setting for setting in ctx.switches if wanted(setting)]
162
+
163
+ for setting in defaults:
164
+ value = _get_default(setting, settings)
165
+ settings[setting.json_key] = value
166
+
167
+ for setting in switches:
168
+ value = _get_default(setting, settings)
169
+ settings[setting.json_key] = value
170
+
171
+ return settings
172
+
173
+
174
+ def _fixup(settings: ctx.SettingsType, key: str, fixup: str, force=False):
175
+ value = settings.get(key, "")
176
+ if value != "" and not force:
177
+ return
178
+
179
+ value = ctx._build_fixup(settings, fixup)
180
+ settings[key] = value
181
+
182
+
183
+ def _get_default(setting: ctx.Setting, settings: ctx.SettingsType):
184
+ value = setting.calc_value(settings)
185
+ if isinstance(value, list):
186
+ return value[0]
187
+ return value
188
+
189
+
190
+ def _fixup_context(settings: ctx.SettingsType, wanted: Callable[[ctx.Setting], bool]):
191
+ defaults = [setting for setting in ctx.defaults if wanted(setting)]
192
+ hidden = [setting for setting in ctx.hidden if wanted(setting)]
193
+
194
+ for setting in hidden:
195
+ value = _get_default(setting, settings)
196
+ if isinstance(value, bool) or value != "":
197
+ settings[setting.json_key] = value
198
+
199
+ for coll in [defaults, hidden]:
200
+ for setting in coll:
201
+ _fixup(settings, setting.json_key, setting.fix or "", setting.force_fix)
202
+
203
+ try:
204
+ del settings["EXT"]
205
+ except KeyError:
206
+ pass
207
+
208
+ result = {}
209
+ for key in settings:
210
+ path = key.split(".")
211
+ path_ctx = result
212
+ for step in path[:-1]:
213
+ if step not in path_ctx or not isinstance(path_ctx[step], dict):
214
+ path_ctx[step] = {}
215
+ path_ctx = path_ctx[step]
216
+ path_ctx[path[-1]] = settings[key]
217
+ return result
218
+
219
+
220
+ def get_context(interactive: bool, project: Optional[str]):
221
+ """
222
+ Prompts user to provide details of newly-crated project. If `interactive`
223
+ is true, however, this functions skips the prompts and chooses all the
224
+ default answers.
225
+
226
+ :param interactive: Selects, if the initialization process is done through
227
+ prompts, or not
228
+
229
+ :returns: Dictionary with answers to all interactive settings and switches.
230
+ """
231
+
232
+ wanted = _project_filter(project)
233
+ return _fixup_context(
234
+ _all_default(wanted) if not interactive else _prompt(wanted), wanted
235
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proj-flow
3
- Version: 0.9.4
3
+ Version: 0.10.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/
@@ -76,7 +76,7 @@ From PowerShell with:
76
76
  A fresh C++ project can be created with a
77
77
 
78
78
  ```sh
79
- proj-flow init
79
+ proj-flow init cxx
80
80
  ```
81
81
 
82
82
  This command will ask multiple questions to build Mustache context for the
@@ -1,23 +1,25 @@
1
- proj_flow/__init__.py,sha256=mb_RaqBK8YLjzInbWdxN7mBDJZ3gqbzxfnHEXvzXbTw,124
1
+ proj_flow/__init__.py,sha256=cd3hxTcDnlv9Qd6qQYRRa3g7bs5iuMrPJtfF_ENzMTY,277
2
2
  proj_flow/__main__.py,sha256=HUar_qQ9Ndmchmryegtzu__5wukwCLrFN_SGRl5Ol_M,233
3
+ proj_flow/dependency.py,sha256=CpcnR6El8AO9hlLc9lQtYQADYlkx3GMHlkLYbEAtdMI,4639
3
4
  proj_flow/api/__init__.py,sha256=gV2f6kll_5JXtvkGASvnx7CbOWr34PHOdck-4ce-qEk,378
4
- proj_flow/api/arg.py,sha256=J7_53noW7t8VJ9sfJiX4qaPs7KqWfMHNcrWYe1jIq-g,4097
5
+ proj_flow/api/arg.py,sha256=id08mLFVDWIvsrhEaPuJfng27f92Vf8pIA5VajyjGVo,4802
5
6
  proj_flow/api/completers.py,sha256=NapNVu6QAQ_iF6dqcAzOV5kDHKD9MAMVX209Bklq-Mw,2464
6
- proj_flow/api/ctx.py,sha256=AXZSnfLO9w7Uiqa8VtrTzLnNegVZru2sn1-WON3RIyg,5847
7
+ proj_flow/api/ctx.py,sha256=IJu0q0Chivo6b2M4MKkAlV09oi7Cn9VxtDFeAeL_tnc,6646
7
8
  proj_flow/api/env.py,sha256=4VvSkfA2k6OPrtvkPtEPhb24dDyaqbUoqSznIgvAXNg,11082
8
9
  proj_flow/api/init.py,sha256=an0czDiPCDqW4Bp0I8sGQgaAlDAozLO0ZYnP149lWqk,521
9
10
  proj_flow/api/makefile.py,sha256=AxtGOvmypBtSvoyMEDJq1bGoaVD5yW9YYRZSdunUEeg,3856
10
11
  proj_flow/api/release.py,sha256=A-t4qcjeaptEgfBCZs2Q9cOjsGACrNM0CzmCorms0U4,2401
11
- proj_flow/api/step.py,sha256=PAqLBkQkUVQETrBvHjI6_-PAkh4Q7KLPMBUOWqW_jbk,4580
12
+ proj_flow/api/step.py,sha256=AOqe1wXYnU_O3pD5KlzfyyOm_D9fcF_4vyhhr1w-NrI,4561
12
13
  proj_flow/base/__init__.py,sha256=RRmqiYjdVlC4i8QijXHNiRh9yzNH8305WXezrSaPjKk,311
13
14
  proj_flow/base/cmd.py,sha256=Vo3e8kd4CHRzbsUkO-MnbhQxjLXTczv7YQRUkFlcBQE,1560
14
15
  proj_flow/base/inspect.py,sha256=lt5P19rvSZ-wMCTrCYAaQFCt2S9fUjEQXlrKK-Tmvwc,2786
15
16
  proj_flow/base/matrix.py,sha256=8XBFGYOwW6Myt_4h3WNk36V2bJ5xeqUv6DvzF4B3q_g,7767
17
+ proj_flow/base/name_list.py,sha256=KiHSnbDgYplJc25O3EehYhFAhD7Z3mHVAK6UYOdg5PQ,416
16
18
  proj_flow/base/plugins.py,sha256=evn2Dym_NeoBaIZAu2YUtRd--15PCFpHD0h5zSsWkQE,978
17
- proj_flow/base/registry.py,sha256=oRLGY1QLY306KLEXJU-p5kmTKQPM7xgRpaajdCFg0OQ,3022
19
+ proj_flow/base/registry.py,sha256=C04Imxux_BO7DffZZth28iFLPnM4Yw9K7IGt06VokE0,3528
18
20
  proj_flow/base/uname.py,sha256=7Awb3Es0jTAKMpyRawdrC16xc5X9M97BlPqEfQibqIk,2295
19
- proj_flow/cli/__init__.py,sha256=__5sdeicDv3HBEREPwY2FAbLNcI5x8bUDJqmkhH6HJ0,1198
20
- proj_flow/cli/argument.py,sha256=KnBRmnHfBQHF_rDxtE8HEa01QMIagBxxImq-jGClynE,13690
21
+ proj_flow/cli/__init__.py,sha256=cMsZpECkXeSzY4Hv_ela3Ou-FhwE5w1A3ypMSnZZikM,1196
22
+ proj_flow/cli/argument.py,sha256=OCx_Z0NVm4bmHI30WFdtPdqVMdDnvkqqluhAgP7Ya0w,13686
21
23
  proj_flow/cli/finder.py,sha256=5x7H1nH0k63DetDauhB_wABel_f0RQpsZ5YnhPfbkRc,1402
22
24
  proj_flow/ext/__init__.py,sha256=XD52rUFTPz3GnyRq6KZUNeWdMce7e0bB19iTx-zU6DE,169
23
25
  proj_flow/ext/markdown_changelog.py,sha256=fRGL09jojnv2B-8vAX2prvgNp8e7uyq5NxboSZjFCJ8,436
@@ -25,17 +27,17 @@ proj_flow/ext/re_structured_changelog.py,sha256=UF23W9eu_YgPO42MiaoDbEKu8In_48mQ
25
27
  proj_flow/ext/store.py,sha256=yfyIb2G7UhoIkPmVDnp1RPx2fwFZK8FyLZzrMvPlEUM,3681
26
28
  proj_flow/ext/cplusplus/__init__.py,sha256=dAmLMyGVQq586jJM_jiAuo5Ecw9U8agpvSRbzzPgh3g,245
27
29
  proj_flow/ext/cplusplus/cmake/__init__.py,sha256=f-_gTY_XpIfcKrAj0jhT57DBGeifkW2s7NlSxjpHTMg,366
28
- proj_flow/ext/cplusplus/cmake/__version__.py,sha256=tmRnT4aUpx4_h-vyWsGyVEeahW49VlLqlsd-o7irmKY,126
29
- proj_flow/ext/cplusplus/cmake/context.py,sha256=5RPpc0tquh7xDO1_ifgnIdxCbabuZeW-Yw7QjpeazUo,3085
30
+ proj_flow/ext/cplusplus/cmake/__version__.py,sha256=imja0GnhpBvS8Crz-64eOUKhc4i6FeRrjBGRB68x_p0,239
31
+ proj_flow/ext/cplusplus/cmake/context.py,sha256=BJDMRuIvCEXR577yWuYSw-wzQ9PQudpXjnIxo1gKHBU,3172
30
32
  proj_flow/ext/cplusplus/cmake/parser.py,sha256=ZqQRZqS_VU5VtC8uwax-dknh7sfuLEvtazG8ChSqHDQ,3814
31
33
  proj_flow/ext/cplusplus/cmake/steps.py,sha256=Q7HcVlMbKOwrRj-Sms0W7FS027M_shM7s2FJx6rX4KQ,4100
32
34
  proj_flow/ext/cplusplus/cmake/version.py,sha256=E0PAUdO9Wg02pxtU4LWYTNoTB-9Oer3Y9zr1lR2zvgw,962
33
- proj_flow/ext/cplusplus/conan/__init__.py,sha256=Ohcx5DpvI9X3uEhkzcb85otqZPRGqeWw8Mc5HeVvjgs,1967
34
- proj_flow/ext/cplusplus/conan/_conan.py,sha256=c3rVi4WdmhsO8eqxG2xG4vzgagnOx1HDUOPl3bWxkD0,3357
35
+ proj_flow/ext/cplusplus/conan/__init__.py,sha256=1WmGMY1hhvJdHsXfHg5MjA8g6qL3H2FABsK0TLBigCI,2022
36
+ proj_flow/ext/cplusplus/conan/_conan.py,sha256=9xnji-f8uN7huXLqavVBUDC33CgnjBIyZX6wVcGm2RA,3352
35
37
  proj_flow/ext/github/__init__.py,sha256=Mgx19YS6SYBXYB66_pOgIgwuB2WKRxqp5UGutq0B9Xk,282
36
- proj_flow/ext/github/cli.py,sha256=tOo65gZR1JRuitQdbs4i9lG0pxlWRXy2bmsaIbfOyUo,4091
38
+ proj_flow/ext/github/cli.py,sha256=zQS2TB7fDeY0VwR1bOw065Trz2NPe07JKcFQaJbwKGg,3934
37
39
  proj_flow/ext/github/hosting.py,sha256=3iW8QjeJk7MyqKNbv92nB-5a_Yn_B5_eEIlw_cdgUT0,519
38
- proj_flow/ext/github/switches.py,sha256=Sv5NxTOzwatrhCanKM3siKI-NcH9Cwloe-ohWLnIuWg,407
40
+ proj_flow/ext/github/switches.py,sha256=g7O2hvrg4mHm3WSHYsRBhEDU0bIkEJgp4Qclhqxk0uI,421
39
41
  proj_flow/ext/python/__init__.py,sha256=GbEKEJJZ3PJ4sRHEykAWjGIR6yyyrYdlUFulldvsAGI,252
40
42
  proj_flow/ext/python/rtdocs.py,sha256=idm6DTUnbA18L-EpxQiFVXCz9x8AIRSb52ZPqIXzrf8,6354
41
43
  proj_flow/ext/python/steps.py,sha256=pDHGAe_CDzzdRFAzM1AIBvkbc14KB3SNUunusKZAaaY,1815
@@ -43,11 +45,8 @@ proj_flow/ext/python/version.py,sha256=pnyuKATyZwBh1p0gf9KmqbRSZx8hJ5285CiFK_tHE
43
45
  proj_flow/ext/sign/__init__.py,sha256=yvXpqLdvBwkB0GDBl4yWw--iZ2tFxhx-97EP9OAzx2g,4345
44
46
  proj_flow/ext/sign/api.py,sha256=dlnXYYoBDYXx_WWGBQ8ThKmEMYmw2kt6NNZA8j-MXuM,2288
45
47
  proj_flow/ext/sign/win32.py,sha256=fV8_Z42KaeDBEf3_5qCzlALb0dy1zN0Pqy8Hr-ZMAQ4,4874
46
- proj_flow/flow/__init__.py,sha256=Y1MfQsrbk_dIT6pn7c9YIlqsEUZj4Q9n_oIGtbnhIP4,339
48
+ proj_flow/flow/__init__.py,sha256=5Zo97zJsR7HMbl64jeMB9PbUuxCxpOlNuLmo3apWSVU,277
47
49
  proj_flow/flow/configs.py,sha256=l-pIotrXFm4oMqqpuvKP-RdtlTmDDnK_izhTJfsWbhk,5260
48
- proj_flow/flow/dependency.py,sha256=5o_-5Soc2e1tOu5mwvkXa6jqDFplZi_chN8edkR6vVY,4644
49
- proj_flow/flow/init.py,sha256=9k3cgVmmeOKtH6mJ0nc4IOmI48oXvtYXFYWx2rj60-M,1777
50
- proj_flow/flow/interact.py,sha256=AJknpCxk3C39GhsR6BjH5QDk33KouWCZbHEUFLjvfGc,4166
51
50
  proj_flow/flow/layer.py,sha256=6mvbhiOy9KcMP69Q9zew_7jGhf5Wqr7v-veS3YPGnmc,5720
52
51
  proj_flow/flow/steps.py,sha256=PN_C_B6vNvqOsjpDpa5ESvH30Sc6RM1fSSqWqXgqg-4,2804
53
52
  proj_flow/log/__init__.py,sha256=02EIgasE-K7mmbbNiIdX0IebWQMp2Co_D6H4ZBhJgcs,365
@@ -64,12 +63,17 @@ proj_flow/log/rich_text/api.py,sha256=PCSAGwkmDUMoVlpN7BDsgIA1AiMZEC0H6TUZXpr_Mg
64
63
  proj_flow/log/rich_text/markdown.py,sha256=jBnNxxhBHzyIZ3Y4HXDfqpl7zlRbbKbKdwdnZwkmNAI,1623
65
64
  proj_flow/log/rich_text/re_structured_text.py,sha256=DEl9KjBUF6cxfNWpQ7GVnHi7wKeuFnPGJwxQxjbCsnM,1823
66
65
  proj_flow/minimal/__init__.py,sha256=NglaSdKiMebrOqfsqF9ctqi0ZwiiBHOQcUnp3DS8lP0,340
67
- proj_flow/minimal/base.py,sha256=-J9aEU1R7tJY7hgcODB2o37CYi7v3iWUztuZ2IZF5cQ,740
66
+ proj_flow/minimal/base.py,sha256=yJR3FAigR_x8krTQ1UeifBb4AnLUZAk6LfVVqB_RFO4,758
68
67
  proj_flow/minimal/bootstrap.py,sha256=PcZfBsUmj8uDPGBC55iUgD5O7W4VSkpCQb6r9GEyAaQ,556
69
- proj_flow/minimal/init.py,sha256=3AhICGKG8ZScMG1aVlNOXXM1pGOXayqW9AOmgsYbNAs,2081
68
+ proj_flow/minimal/init.py,sha256=YFAsD_wGypGxweEFrbAbCdGptV1jQ2OdxUseUPi-3C8,2945
70
69
  proj_flow/minimal/list.py,sha256=RlOqammE8olNKXsnbv1enF5uriu0MZ2wFbht37Z2ETw,4810
71
- proj_flow/minimal/run.py,sha256=fkirdYeWb36SFXqIq5NQEflIv0HdjNhaTpmsutzWwXA,4691
70
+ proj_flow/minimal/run.py,sha256=4qvGLqz2ayCZDvVBrq4tG094fjfcmDPon-xcGPQkM_U,4665
72
71
  proj_flow/minimal/system.py,sha256=9FliH5TD103JYSAe2O5EU7hkOHDgVzTqu0Exxk-WrXE,1579
72
+ proj_flow/project/__init__.py,sha256=AROrwhbuMR5rJE-HC769eL4IXrMLQYpQb3HgpkOAYqg,293
73
+ proj_flow/project/api.py,sha256=xQ3eFcxgLfi6ZAGs2q_1V0uHTpM7v9gsb7Ie2o2z-v8,1358
74
+ proj_flow/project/cplusplus.py,sha256=GXHXI4cdrSm0jWTr2g9dCBgeFS1G4g2fC9WCv6ILn0c,398
75
+ proj_flow/project/data.py,sha256=TluhBDoJEYL4dnyTpInmhQ49Uvf8mkWmpU-YMLQPNhE,317
76
+ proj_flow/project/interact.py,sha256=DgclUNeY6K146QQe510tdHcYnZmWmQZ0xH9w4HifLTU,7235
73
77
  proj_flow/template/layers/base.json,sha256=jrlby8FUUwkx4V_EGMix_pkQlWcUCVUgmeoapZaZnt4,3
74
78
  proj_flow/template/layers/cmake.json,sha256=KJe9uqTDoGm0ppdNOtniKEv30iNV2K4Yk8hZQetEZ7Y,385
75
79
  proj_flow/template/layers/conan.json,sha256=mAhDrxCtDjI_7Rbtr2hlNW5_jZkLdWLiwgfuhRsRuuw,29
@@ -124,8 +128,8 @@ proj_flow/template/licenses/MIT.mustache,sha256=NncPoQaNsuy-WmRmboik3fyhJJ8m5pc2
124
128
  proj_flow/template/licenses/Unlicense.mustache,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
125
129
  proj_flow/template/licenses/WTFPL.mustache,sha256=lvF4V_PrKKfZPa2TC8CZo8tlqaKvs3Bpv9G6XsWWQ4k,483
126
130
  proj_flow/template/licenses/Zlib.mustache,sha256=uIj-mhSjes2HJ3rRapyy2ALflKRz4xQgS4mVM9827C0,868
127
- proj_flow-0.9.4.dist-info/METADATA,sha256=xubfKNSwQpIkuu3Mugzwed5kdO3XhraoEIctZs0ppOI,2863
128
- proj_flow-0.9.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
129
- proj_flow-0.9.4.dist-info/entry_points.txt,sha256=d_OmGKZzpY7FCWz0sZ4wnBAPZC75oMEzTgJZWtpDELo,49
130
- proj_flow-0.9.4.dist-info/licenses/LICENSE,sha256=vpOQJ5QlrTedF3coEWvA4wJzVJH304f66ZitR7Od4iU,1068
131
- proj_flow-0.9.4.dist-info/RECORD,,
131
+ proj_flow-0.10.0.dist-info/METADATA,sha256=bQUtOSHQevlBQB43X1xui_BfX4KBey_TwIYx_tUNx1Q,2868
132
+ proj_flow-0.10.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
133
+ proj_flow-0.10.0.dist-info/entry_points.txt,sha256=d_OmGKZzpY7FCWz0sZ4wnBAPZC75oMEzTgJZWtpDELo,49
134
+ proj_flow-0.10.0.dist-info/licenses/LICENSE,sha256=vpOQJ5QlrTedF3coEWvA4wJzVJH304f66ZitR7Od4iU,1068
135
+ proj_flow-0.10.0.dist-info/RECORD,,
proj_flow/flow/init.py DELETED
@@ -1,65 +0,0 @@
1
- # Copyright (c) 2025 Marcin Zdun
2
- # This code is licensed under MIT license (see LICENSE for details)
3
-
4
- """
5
- The **proj_flow.flow.init** supports the ``init`` command.
6
- """
7
-
8
- from proj_flow.api import ctx
9
-
10
-
11
- def _fixup(settings: ctx.SettingsType, key: str, fixup: str, force=False):
12
- value = settings.get(key, "")
13
- if value != "" and not force:
14
- return
15
-
16
- value = ctx._build_fixup(settings, fixup)
17
- settings[key] = value
18
-
19
-
20
- def _get_default(setting: ctx.Setting, settings: ctx.SettingsType):
21
- value = setting.calc_value(settings)
22
- if isinstance(value, list):
23
- return value[0]
24
- return value
25
-
26
-
27
- def all_default():
28
- settings: ctx.SettingsType = {}
29
-
30
- for setting in ctx.defaults:
31
- value = _get_default(setting, settings)
32
- settings[setting.json_key] = value
33
-
34
- for setting in ctx.switches:
35
- value = _get_default(setting, settings)
36
- settings[setting.json_key] = value
37
-
38
- return settings
39
-
40
-
41
- def fixup(settings: ctx.SettingsType):
42
- for setting in ctx.hidden:
43
- value = _get_default(setting, settings)
44
- if isinstance(value, bool) or value != "":
45
- settings[setting.json_key] = value
46
-
47
- for coll in [ctx.defaults, ctx.hidden]:
48
- for setting in coll:
49
- _fixup(settings, setting.json_key, setting.fix or "", setting.force_fix)
50
- del settings["EXT"]
51
-
52
- result = {}
53
- for key in settings:
54
- path = key.split(".")
55
- path_ctx = result
56
- for step in path[:-1]:
57
- if step not in path_ctx or not isinstance(path_ctx[step], dict):
58
- path_ctx[step] = {}
59
- path_ctx = path_ctx[step]
60
- path_ctx[path[-1]] = settings[key]
61
- return result
62
-
63
-
64
- def get_internal(key: str, value: any = None):
65
- return ctx.internals.get(key, value)