proj-flow 0.17.2__py3-none-any.whl → 0.19.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 (30) hide show
  1. proj_flow/__init__.py +1 -1
  2. proj_flow/api/step.py +13 -3
  3. proj_flow/ext/webidl/__init__.py +12 -0
  4. proj_flow/ext/webidl/base/__init__.py +2 -0
  5. proj_flow/ext/webidl/base/config.py +425 -0
  6. proj_flow/ext/webidl/cli/__init__.py +10 -0
  7. proj_flow/ext/webidl/cli/cmake.py +82 -0
  8. proj_flow/ext/webidl/cli/depfile.py +83 -0
  9. proj_flow/ext/webidl/cli/gen.py +157 -0
  10. proj_flow/ext/webidl/cli/init.py +26 -0
  11. proj_flow/ext/webidl/cli/root.py +12 -0
  12. proj_flow/ext/webidl/cli/updater.py +20 -0
  13. proj_flow/ext/webidl/data/init/flow_webidl.cmake +26 -0
  14. proj_flow/ext/webidl/data/templates/cmake.mustache +45 -0
  15. proj_flow/ext/webidl/data/templates/depfile.mustache +6 -0
  16. proj_flow/ext/webidl/data/templates/partials/cxx/attribute-decl.mustache +1 -0
  17. proj_flow/ext/webidl/data/templates/partials/cxx/in-out.mustache +2 -0
  18. proj_flow/ext/webidl/data/templates/partials/cxx/includes.mustache +6 -0
  19. proj_flow/ext/webidl/data/templates/partials/cxx/operation-decl.mustache +12 -0
  20. proj_flow/ext/webidl/data/templates/partials/cxx/type.mustache +6 -0
  21. proj_flow/ext/webidl/data/types/cxx.json +47 -0
  22. proj_flow/ext/webidl/model/__init__.py +2 -0
  23. proj_flow/ext/webidl/model/ast.py +586 -0
  24. proj_flow/ext/webidl/model/builders.py +230 -0
  25. proj_flow/ext/webidl/registry.py +23 -0
  26. {proj_flow-0.17.2.dist-info → proj_flow-0.19.0.dist-info}/METADATA +2 -1
  27. {proj_flow-0.17.2.dist-info → proj_flow-0.19.0.dist-info}/RECORD +30 -7
  28. {proj_flow-0.17.2.dist-info → proj_flow-0.19.0.dist-info}/WHEEL +0 -0
  29. {proj_flow-0.17.2.dist-info → proj_flow-0.19.0.dist-info}/entry_points.txt +0 -0
  30. {proj_flow-0.17.2.dist-info → proj_flow-0.19.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,157 @@
1
+ # Copyright (c) 2026 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ import json
5
+ import sys
6
+ import typing
7
+ from dataclasses import asdict
8
+ from pathlib import Path
9
+ from pprint import pprint
10
+
11
+ import chevron
12
+
13
+ from proj_flow.api import arg, env
14
+ from proj_flow.ext.webidl.base.config import (
15
+ CMakeDirs,
16
+ Output,
17
+ TemplateConfig,
18
+ templates_dir,
19
+ )
20
+ from proj_flow.ext.webidl.cli.updater import update_file_if_needed
21
+ from proj_flow.ext.webidl.model.ast import Definitions
22
+ from proj_flow.ext.webidl.registry import webidl_visitors
23
+
24
+
25
+ def aslist(items: list):
26
+ return [asdict(item) for item in items]
27
+
28
+
29
+ T = typing.TypeVar("T")
30
+ P = typing.TypeVar("P")
31
+
32
+
33
+ def conv(conv: typing.Callable[[T], P], maybe: T | None) -> P | None:
34
+ if maybe is None:
35
+ return None
36
+ return conv(maybe)
37
+
38
+
39
+ @arg.command("webidl", "gen")
40
+ def gen(
41
+ config_path: typing.Annotated[
42
+ str, arg.Argument(help="Input configuration", names=["--cfg"], meta="json-file")
43
+ ],
44
+ binary_dir: typing.Annotated[
45
+ str,
46
+ arg.Argument(
47
+ help="Project binary dir", names=["--binary-dir"], meta="project-binary-dir"
48
+ ),
49
+ ],
50
+ rt: env.Runtime,
51
+ ):
52
+ """Run mustache codegen on WebIDL definitions"""
53
+
54
+ config_filename = Path(config_path)
55
+ cmake_dirs = CMakeDirs.from_config_path(
56
+ config_filename, project_binary_dir=Path(binary_dir)
57
+ )
58
+
59
+ global_ctx = cmake_dirs.my_defines()
60
+ config_filename = Path(config_path).absolute()
61
+ config = TemplateConfig.load_config(
62
+ config_filename, global_ctx, cmake_dirs.project_source_dir
63
+ )
64
+
65
+ global_ctx["version"] = str(config.version)
66
+
67
+ pkg_partials: dict[str, str] = {}
68
+ partials_dir = templates_dir / "partials"
69
+ ext = ".mustache"
70
+ for root, _dirs, files in partials_dir.walk():
71
+ for path in [
72
+ root / filename for filename in files if filename[-len(ext) :] == ext
73
+ ]:
74
+ path_id = path.relative_to(partials_dir).with_name(path.stem)
75
+ lang_id = str(path_id.parents[0])
76
+ partial = path_id.as_posix()[len(lang_id) + 1 :]
77
+ partial = f"pkg-{lang_id}:{partial}"
78
+ pkg_partials[partial] = path.read_text(encoding="UTF-8")
79
+
80
+ if rt.verbose:
81
+ print(f"-- WebIDL: adding `{partial}' package partial")
82
+
83
+ for rule in config.rules:
84
+ names = list(map(lambda path: path.as_posix(), rule.inputs))
85
+ context = {
86
+ **global_ctx,
87
+ "input": Output.filename_context(names[0], cmake_dirs.project_source_dir),
88
+ }
89
+
90
+ try:
91
+ idl = Definitions.parse_and_merge(names, config.ext_attrs)
92
+ except FileNotFoundError as e:
93
+ p = Path(e.filename)
94
+ print(f"{e.strerror}: {p.as_posix()}")
95
+ sys.exit(1)
96
+ if isinstance(idl, list):
97
+ for error in idl:
98
+ print(f"{error.path}:{error.error}")
99
+ sys.exit(1)
100
+
101
+ for visitor in webidl_visitors.get():
102
+ visitor.on_definitions(idl)
103
+
104
+ for output in rule.outputs:
105
+ if not output.mustache_template:
106
+ continue
107
+ write_message: str | None = None
108
+ if not rt.silent:
109
+ fname = (
110
+ output.output.relative_to(cmake_dirs.project_source_dir).as_posix()
111
+ if output.output.is_relative_to(cmake_dirs.project_source_dir)
112
+ else output.output.as_posix()
113
+ )
114
+ write_message = f"writing {fname}"
115
+
116
+ types = output.get_type_replacements()
117
+ interfaces, modules_or_includes = idl.order(types)
118
+ output_context = {
119
+ **output.initial_context,
120
+ **context,
121
+ "modules_or_includes": list(sorted(modules_or_includes)),
122
+ "interfaces": aslist(interfaces),
123
+ "enums": aslist(idl.enum),
124
+ "has_modules_or_includes": not not modules_or_includes,
125
+ "has_interfaces": not not interfaces,
126
+ "has_enums": not not idl.enum,
127
+ "output": Output.filename_context(
128
+ str(output.output), cmake_dirs.project_binary_dir
129
+ ),
130
+ }
131
+
132
+ kwargs = {}
133
+ if output.partials:
134
+ kwargs["partials_path"] = output.partials.as_posix()
135
+ text = output.mustache_template.read_text(encoding="UTF-8")
136
+ try:
137
+ text = chevron.render(
138
+ text, output_context, partials_dict=pkg_partials, **kwargs
139
+ )
140
+ except chevron.ChevronError as e:
141
+ filename = e.filename or output.mustache_template.as_posix()
142
+ line = conv(lambda num: f":{num}", e.lineno) or ""
143
+ col = (conv(lambda num: f":{num}", e.offset) or "") if e.lineno else ""
144
+ print(f"{filename}{line}{col}: {e.msg}")
145
+ if e.text:
146
+ print(e.text)
147
+ if e.offset:
148
+ spaces = "".join(
149
+ ["\t" if c == "\t" else " " for c in e.text[: e.offset]]
150
+ )
151
+ print(f"{spaces}^")
152
+ sys.exit(1)
153
+
154
+ update_file_if_needed(output.output, text, write_message)
155
+
156
+ dst = output.output.with_name(output.output.name + ".json")
157
+ update_file_if_needed(dst, json.dumps(output_context, indent=2))
@@ -0,0 +1,26 @@
1
+ # Copyright (c) 2026 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ import typing
5
+ from pathlib import Path
6
+
7
+ from proj_flow.api import arg, env
8
+ from proj_flow.ext.webidl.cli.updater import update_file_if_needed
9
+
10
+
11
+ @arg.command("webidl", "init")
12
+ def init(rt: env.Runtime):
13
+ """Copy the main WebIDL CMake config to .flow/cmake"""
14
+
15
+ out_dir_name = Path(".flow") / "cmake"
16
+ in_dir_name = Path(__file__).parent.parent / "data" / "init"
17
+
18
+ for root, _dirs, files in in_dir_name.walk():
19
+ for filename in files:
20
+ in_path = root / filename
21
+ out_path = out_dir_name / in_path.relative_to(in_dir_name)
22
+ update_file_if_needed(
23
+ out_path,
24
+ in_path.read_bytes().decode("utf-8"),
25
+ f"adding {out_path.as_posix()}",
26
+ )
@@ -0,0 +1,12 @@
1
+ # Copyright (c) 2026 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)
3
+
4
+ from typing import cast
5
+
6
+ from proj_flow.api import arg
7
+ from proj_flow.base import inspect as _inspect
8
+
9
+
10
+ @arg.command("webidl")
11
+ def webidl():
12
+ """Configure and generate WebIDL-based interfaces"""
@@ -0,0 +1,20 @@
1
+ # Copyright (c) 2026 Marcin Zdun
2
+ # This file is licensed under MIT license (see LICENSE for details)
3
+
4
+ from pathlib import Path
5
+
6
+
7
+ def update_file_if_needed(dst: Path, contents: str, message: str | None = None):
8
+ prev = ""
9
+ if dst.exists():
10
+ prev = dst.read_bytes().decode("utf-8")
11
+ if prev == contents:
12
+ return False
13
+
14
+ if message:
15
+ print("-- WebIDL:", "\n-- WebIDL: ".join(message.split("\n")))
16
+
17
+ dst.parent.mkdir(parents=True, exist_ok=True)
18
+ dst.write_bytes(contents.encode("utf-8"))
19
+
20
+ return True
@@ -0,0 +1,26 @@
1
+ macro(__webidl_exec CONFIG EXT COMMAND)
2
+ cmake_path(ABSOLUTE_PATH CONFIG OUTPUT_VARIABLE __ABS_CONFIG)
3
+ cmake_path(RELATIVE_PATH __ABS_CONFIG OUTPUT_VARIABLE __CONFIG)
4
+
5
+ execute_process(
6
+ ECHO_OUTPUT_VARIABLE
7
+ ECHO_ERROR_VARIABLE
8
+ COMMAND_ERROR_IS_FATAL LAST
9
+ COMMAND "${Python3_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/.flow/flow.py" webidl ${COMMAND}
10
+ --cfg "${__ABS_CONFIG}"
11
+ --binary-dir "${PROJECT_BINARY_DIR}"
12
+ ${ARGN}
13
+ )
14
+ endmacro()
15
+
16
+ function(add_webidl_generation TARGET CONFIG)
17
+ __webidl_exec("${CONFIG}" cmake cmake --target "${TARGET}")
18
+ __webidl_exec("${CONFIG}" deps depfile)
19
+
20
+ cmake_path(ABSOLUTE_PATH CONFIG)
21
+ cmake_path(RELATIVE_PATH CONFIG)
22
+
23
+ if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}.cmake")
24
+ include("${CMAKE_CURRENT_BINARY_DIR}/${CONFIG}.cmake")
25
+ endif()
26
+ endfunction()
@@ -0,0 +1,45 @@
1
+ macro(__webidl_config EXT COMMAND)
2
+ add_custom_command(
3
+ OUTPUT "{{{config.binary}}}.${EXT}"
4
+ MAIN_DEPENDENCY "{{{config.source}}}"
5
+ COMMAND "${Python3_EXECUTABLE}"
6
+ "${PROJECT_SOURCE_DIR}/.flow/flow.py" webidl ${COMMAND}
7
+ --cfg "{{{config.source}}}"
8
+ --binary-dir "${PROJECT_BINARY_DIR}"
9
+ ${ARGN}
10
+ )
11
+ target_sources(__webidl_cmake PRIVATE "{{{config.binary}}}.${EXT}")
12
+ endmacro()
13
+
14
+ if(NOT TARGET __webidl_cmake)
15
+ add_custom_target(__webidl_cmake ALL)
16
+ endif()
17
+
18
+ __webidl_config(deps depfile)
19
+ {{# target}}
20
+ __webidl_config(cmake cmake --target "{{{.}}}")
21
+ {{/ target}}
22
+ {{^ target}}
23
+ __webidl_config(cmake cmake)
24
+ {{/ target}}
25
+
26
+ SET(GEN_SRCS
27
+ {{# output}}
28
+ "{{{.}}}"
29
+ {{/ output}}
30
+ )
31
+ {{# target}}
32
+
33
+ source_group(TREE ${CMAKE_CURRENT_BINARY_DIR} PREFIX gen FILES ${GEN_SRCS})
34
+ target_sources("{{{.}}}" PRIVATE ${GEN_SRCS})
35
+ {{/target}}
36
+
37
+ add_custom_command(
38
+ OUTPUT ${GEN_SRCS}
39
+ DEPFILE "{{{depfile}}}"
40
+ COMMAND "${Python3_EXECUTABLE}"
41
+ "${PROJECT_SOURCE_DIR}/.flow/flow.py" webidl gen
42
+ --cfg "{{{config.source}}}"
43
+ --binary-dir "${PROJECT_BINARY_DIR}"
44
+ COMMENT "Generating code for {{{config.basename}}}"
45
+ )
@@ -0,0 +1,6 @@
1
+ {{# rule}}
2
+ {{#target}}{{sep}}{{pathname}}{{/target}}:{{#dependency}} \
3
+ {{.}}{{/dependency}}
4
+
5
+
6
+ {{/rule}}
@@ -0,0 +1 @@
1
+ {{# static}}static {{/static}}{{# readonly}}const {{/readonly}}{{#type}}{{> pkg-cxx:type}}{{/type}} {{name}}{{default}};
@@ -0,0 +1,2 @@
1
+ {{#ext_attrs.out}}&{{/ext_attrs.out}}{{!
2
+ }}{{^ext_attrs.out}}{{#ext_attrs.in}} const&{{/ext_attrs.in}}{{/ext_attrs.out}}
@@ -0,0 +1,6 @@
1
+ {{# modules_or_includes}}
2
+ #include {{{.}}}
3
+ {{/ modules_or_includes}}
4
+ {{# has_modules_or_includes}}
5
+
6
+ {{/ has_modules_or_includes}}
@@ -0,0 +1,12 @@
1
+ {{# static}}static {{/static}}{{#type}}{{> pkg-cxx:type}}{{/type}}{{> pkg-cxx:in-out}} {{!
2
+ }}{{name}}({{#arguments}}{{!
3
+ }}{{^ first}}, {{/first}}{{#type}}{{> pkg-cxx:type}}{{/type}}{{> pkg-cxx:in-out}}{{!
4
+ }} {{name}}{{#ext_attrs}}{{!
5
+ }}{{#default}} = {{{.}}}{{/default}}{{!
6
+ }}{{^default}}{{#defaulted}} = {}{{/defaulted}}{{/default}}{{!
7
+ }}{{/ext_attrs}}{{!
8
+ }}{{/arguments}}){{!
9
+ }}{{^ext_attrs.mutable}}{{^ static}} const{{/static}}{{/ext_attrs.mutable}}{{!
10
+ }}{{^ext_attrs.throws}} noexcept{{/ext_attrs.throws}}{{!
11
+ }}{{#ext_attrs.defaulted}} = default{{/ext_attrs.defaulted}}{{!
12
+ }};
@@ -0,0 +1,6 @@
1
+ {{# nullable}}std::optional<{{/nullable}}{{!
2
+ }}{{{name}}}{{!
3
+ }}{{# generic}}<{{#arguments}}{{!
4
+ }}{{^first}}, {{/first}}{{#arg_type}}{{> pkg-cxx:type}}{{/arg_type}}{{!
5
+ }}{{/arguments}}>{{/generic}}{{!
6
+ }}{{# nullable}}>{{/nullable}}
@@ -0,0 +1,47 @@
1
+ {
2
+ "void": null,
3
+ "int": null,
4
+ "signed": null,
5
+ "unsigned": null,
6
+ "uint": "unsigned",
7
+ "short": null,
8
+ "signed short": null,
9
+ "unsigned short": null,
10
+ "long": null,
11
+ "signed long": null,
12
+ "unsigned long": null,
13
+ "ulong": "unsigned long",
14
+ "long long": null,
15
+ "signed long long": null,
16
+ "unsigned long long": null,
17
+ "char": null,
18
+ "wchar_t": null,
19
+ "char8_t": null,
20
+ "char16_t": null,
21
+ "char32_t": null,
22
+ "bool": null,
23
+ "float": null,
24
+ "double": null,
25
+ "long double": null,
26
+ "record": [ "<map>", "std::map" ],
27
+ "optional": [ "<optional>", "std::optional" ],
28
+ "array": [ "<vector>", "std::vector" ],
29
+ "sequence": [ "<vector>", "std::vector" ],
30
+ "span": [ "<span>", "std::span" ],
31
+ "set": [ "<set>", "std::set" ],
32
+ "union": [ "<variant>", "std::variant" ],
33
+ "DOMString": [ "<string>", "std::string" ],
34
+ "string": [ "<string>", "std::string" ],
35
+ "string_view": [ "<string_view>", "std::string_view" ],
36
+ "path": [ "<filesystem>", "std::filesystem::path" ],
37
+ "size_t": [ "<cstddef>", "std::size_t" ],
38
+ "nullptr_t": [ "<cstddef>", "std::nullptr_t" ],
39
+ "int8_t": [ "<cstdint>", "std::int8_t" ],
40
+ "int16_t": [ "<cstdint>", "std::int16_t" ],
41
+ "int32_t": [ "<cstdint>", "std::int32_t" ],
42
+ "int64_t": [ "<cstdint>", "std::int64_t" ],
43
+ "uint8_t": [ "<cstdint>", "std::uint8_t" ],
44
+ "uint16_t": [ "<cstdint>", "std::uint16_t" ],
45
+ "uint32_t": [ "<cstdint>", "std::uint32_t" ],
46
+ "uint64_t": [ "<cstdint>", "std::uint64_t" ]
47
+ }
@@ -0,0 +1,2 @@
1
+ # Copyright (c) 2026 Marcin Zdun
2
+ # This code is licensed under MIT license (see LICENSE for details)