proj-flow 0.9.4__py3-none-any.whl → 0.11.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 (36) hide show
  1. proj_flow/__init__.py +6 -1
  2. proj_flow/api/arg.py +47 -24
  3. proj_flow/api/ctx.py +43 -23
  4. proj_flow/api/env.py +65 -2
  5. proj_flow/api/step.py +3 -5
  6. proj_flow/{ext/cplusplus/cmake/__version__.py → base/__cmake_version__.py} +5 -0
  7. proj_flow/base/name_list.py +19 -0
  8. proj_flow/base/registry.py +23 -3
  9. proj_flow/cli/__init__.py +1 -1
  10. proj_flow/cli/argument.py +2 -2
  11. proj_flow/{flow/dependency.py → dependency.py} +1 -1
  12. proj_flow/ext/cplusplus/cmake/__init__.py +2 -2
  13. proj_flow/ext/cplusplus/cmake/steps.py +1 -1
  14. proj_flow/ext/cplusplus/conan/__init__.py +2 -4
  15. proj_flow/ext/cplusplus/conan/_conan.py +1 -1
  16. proj_flow/ext/github/cli.py +2 -11
  17. proj_flow/ext/github/switches.py +2 -2
  18. proj_flow/flow/__init__.py +2 -2
  19. proj_flow/minimal/base.py +1 -0
  20. proj_flow/minimal/init.py +43 -10
  21. proj_flow/minimal/run.py +1 -2
  22. proj_flow/project/__init__.py +11 -0
  23. proj_flow/project/api.py +51 -0
  24. proj_flow/project/cplusplus/__init__.py +10 -0
  25. proj_flow/{ext/cplusplus/cmake/context.py → project/cplusplus/cmake_context.py} +10 -7
  26. proj_flow/project/cplusplus/conan_context.py +12 -0
  27. proj_flow/project/cplusplus/project.py +16 -0
  28. proj_flow/project/data.py +14 -0
  29. proj_flow/project/interact.py +255 -0
  30. {proj_flow-0.9.4.dist-info → proj_flow-0.11.0.dist-info}/METADATA +2 -2
  31. {proj_flow-0.9.4.dist-info → proj_flow-0.11.0.dist-info}/RECORD +34 -28
  32. proj_flow/flow/init.py +0 -65
  33. proj_flow/flow/interact.py +0 -134
  34. {proj_flow-0.9.4.dist-info → proj_flow-0.11.0.dist-info}/WHEEL +0 -0
  35. {proj_flow-0.9.4.dist-info → proj_flow-0.11.0.dist-info}/entry_points.txt +0 -0
  36. {proj_flow-0.9.4.dist-info → proj_flow-0.11.0.dist-info}/licenses/LICENSE +0 -0
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.11.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/env.py CHANGED
@@ -23,6 +23,7 @@ from dataclasses import dataclass
23
23
  from enum import Enum
24
24
  from typing import Any, Callable, Dict, List, Optional, Union, cast
25
25
 
26
+ from proj_flow.api import ctx
26
27
  from proj_flow.base import plugins, uname
27
28
 
28
29
  platform = uname.uname()[0]
@@ -115,6 +116,59 @@ class RunAlias:
115
116
  return RunAlias(name, doc, steps)
116
117
 
117
118
 
119
+ def _merge_dicts(dst: dict, src: dict):
120
+ for key in src:
121
+ if key not in dst:
122
+ dst[key] = src[key]
123
+ continue
124
+
125
+ src_val = src[key]
126
+ dst_val = dst[key]
127
+
128
+ if isinstance(src_val, dict):
129
+ if isinstance(dst_val, dict):
130
+ _merge_dicts(dst_val, src_val)
131
+ continue
132
+
133
+ dst[key] = src_val
134
+
135
+
136
+ def _flatten_dict(dst: ctx.SettingsType, defaults: Dict[str, Any], prefix=""):
137
+ for key, val in defaults.items():
138
+ this_key = f"{prefix}{key}"
139
+
140
+ if isinstance(val, dict):
141
+ _flatten_dict(dst, val, f"{this_key}.")
142
+ continue
143
+
144
+ if val is None:
145
+ dst[this_key] = ""
146
+ continue
147
+
148
+ if isinstance(val, bool):
149
+ dst[this_key] = val
150
+ continue
151
+
152
+ dst[this_key] = str(val)
153
+
154
+
155
+ def _merge(cfg: dict, defaults: ctx.SettingsType, path: str):
156
+ config = plugins.load_data(path)
157
+ if not isinstance(config, dict):
158
+ return
159
+
160
+ stored_defaults = config.get("defaults", {})
161
+ try:
162
+ del config["defaults"]
163
+ except KeyError:
164
+ pass
165
+
166
+ _merge_dicts(cfg, config)
167
+
168
+ if isinstance(stored_defaults, dict):
169
+ _flatten_dict(defaults, stored_defaults)
170
+
171
+
118
172
  class FlowConfig:
119
173
  _cfg: dict
120
174
  steps: list = []
@@ -129,9 +183,18 @@ class FlowConfig:
129
183
  self.root = cfg.root
130
184
  else:
131
185
  self.root = os.path.abspath(root)
132
- self._cfg = plugins.load_data(
133
- os.path.join(self.root, ".flow", "config.json")
186
+ defaults: ctx.SettingsType = {}
187
+ dest: dict = {}
188
+
189
+ _merge(
190
+ dest,
191
+ defaults,
192
+ os.path.join(os.path.expanduser("~"), ".config", "proj-flow.json"),
134
193
  )
194
+ _merge(dest, defaults, os.path.join(self.root, ".flow", "config.json"))
195
+
196
+ self._cfg = dest
197
+ self._cfg["defaults"] = defaults
135
198
 
136
199
  self._propagate_compilers()
137
200
  self._load_extensions()
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):
@@ -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"
@@ -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")
@@ -97,10 +98,29 @@ class Registry(typing.Generic[T]):
97
98
 
98
99
  _debug_copies: typing.List[Registry] = []
99
100
 
101
+ def quoted(s: str) -> str:
102
+ if '"' in s:
103
+ return "'{}'".format(s.replace("\\", r"\\").replace("'", r"\'"))
104
+ if "'" in s or " " in s:
105
+ return '"{}"'.format(s)
106
+ return s
100
107
 
101
108
  def verbose_info():
102
109
  for registry in _debug_copies:
103
110
  for item in registry.container:
104
- print(
105
- f"-- {registry.name}: adding `{item.__module__}.{item.__class__.__name__}`"
106
- )
111
+ full_name = f"{item.__module__}.{item.__class__.__name__}"
112
+
113
+ kw = OrderedDict()
114
+
115
+ if hasattr(item, "name"):
116
+ kw["name"] = quoted(getattr(item, "name"))
117
+ elif hasattr(item, "__name__"):
118
+ kw["name"] = quoted(getattr(item, "__name__"))
119
+
120
+ if hasattr(item, "id"):
121
+ kw["id"] = quoted(getattr(item, "id"))
122
+
123
+ items = ", ".join([f"{key}={value}" for key, value in kw.items()])
124
+ if len(items) > 0:
125
+ items = f" ({items})"
126
+ 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
@@ -7,6 +7,6 @@ The **proj_flow.ext.cplusplus.cmake** provides ``"CMake"``, ``"Build"``,
7
7
  context.
8
8
  """
9
9
 
10
- from . import context, parser, steps, version
10
+ from . import parser, steps, version
11
11
 
12
- __all__ = ["context", "parser", "steps", "version"]
12
+ __all__ = ["parser", "steps", "version"]
@@ -12,7 +12,7 @@ from typing import Dict, List, cast
12
12
  from proj_flow import api
13
13
  from proj_flow.api import env, step
14
14
 
15
- from .__version__ import CMAKE_VERSION
15
+ from proj_flow.base.__cmake_version__ import CMAKE_VERSION
16
16
 
17
17
 
18
18
  class CMakeBase(api.step.Step):
@@ -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
 
@@ -62,6 +63,3 @@ class ConanConfig:
62
63
  if not rt.dry_run and os.path.exists("CMakeUserPresets.json"):
63
64
  os.remove("CMakeUserPresets.json")
64
65
  return 0
65
-
66
-
67
- ctx.register_switch("with_conan", "Use Conan for dependency manager", True)
@@ -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
  )