diff --git a/lix/code-generation/__init__.py b/lix/code-generation/__init__.py
new file mode 100644
index 000000000..9a071b07b
--- /dev/null
+++ b/lix/code-generation/__init__.py
@@ -0,0 +1 @@
+# noqa: N999 # consistency with rest of the codebase
diff --git a/lix/code-generation/build_builtin_constants.py b/lix/code-generation/build_builtin_constants.py
index beb7b6bfa..bdba163c5 100644
--- a/lix/code-generation/build_builtin_constants.py
+++ b/lix/code-generation/build_builtin_constants.py
@@ -1,15 +1,12 @@
-from typing import List, NamedTuple
+from typing import NamedTuple
+
+from frontmatter import Post
from common import cxx_literal, generate_file, load_data
+import argparse
+
+KNOWN_KEYS = {"name", "type", "constructorArgs", "implementation", "impure", "renameInGlobalScope"}
-KNOWN_KEYS = set([
- 'name',
- 'type',
- 'constructorArgs',
- 'implementation',
- 'impure',
- 'renameInGlobalScope',
-])
class BuiltinConstant(NamedTuple):
name: str
@@ -19,75 +16,97 @@ class BuiltinConstant(NamedTuple):
rename_in_global_scope: bool
documentation: str
- def parse(datum):
+ @classmethod
+ def parse(cls, datum: Post) -> "BuiltinConstant":
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
- raise Exception('unknown keys', unknown_keys)
+ msg = f"unknown keys: {unknown_keys!r}"
+ raise ValueError(msg)
+ if (constructor_args := datum.get("constructorArgs")) is not None:
+ args = [f"NewValueAs::{datum['type']}"] + constructor_args # type: ignore
+ impl = f"{{{','.join(args)}}}"
+ else:
+ impl = datum["implementation"]
+
return BuiltinConstant(
- name = datum['name'],
- type = datum['type'],
- implementation = ('{' + ', '.join([f'NewValueAs::{datum["type"]}', *datum['constructorArgs']]) + '}') if 'constructorArgs' in datum else datum['implementation'],
- impure = datum.get('impure', False),
- rename_in_global_scope = datum.get('renameInGlobalScope', True),
- documentation = datum.content,
+ name=datum["name"], # type: ignore
+ type=datum["type"], # type: ignore
+ implementation=impl,
+ impure=datum.get("impure", False), # type: ignore
+ rename_in_global_scope=datum.get("renameInGlobalScope", True), # type: ignore
+ documentation=datum.content,
)
+
VALUE_TYPES = {
- 'attrs': 'nAttrs',
- 'boolean': 'nBool',
- 'integer': 'nInt',
- 'list': 'nList',
- 'null': 'nNull',
- 'string': 'nString',
+ "attrs": "nAttrs",
+ "boolean": "nBool",
+ "integer": "nInt",
+ "list": "nList",
+ "null": "nNull",
+ "string": "nString",
}
HUMAN_TYPES = {
- 'attrs': 'set',
- 'boolean': 'Boolean',
- 'integer': 'integer',
- 'list': 'list',
- 'null': 'null',
- 'string': 'string',
+ "attrs": "set",
+ "boolean": "Boolean",
+ "integer": "integer",
+ "list": "list",
+ "null": "null",
+ "string": "string",
}
-def main():
- import argparse
+def main():
ap = argparse.ArgumentParser()
- ap.add_argument('--header', help='Path of the header to generate')
- ap.add_argument('--docs', help='Path of the documentation file to generate')
- ap.add_argument('defs', help='Builtin definition files', nargs='+')
+ ap.add_argument("--header", help="Path of the header to generate")
+ ap.add_argument("--docs", help="Path of the documentation file to generate")
+ ap.add_argument("defs", help="Builtin definition files", nargs="+")
args = ap.parse_args()
builtin_constants = load_data(args.defs, BuiltinConstant.parse)
- generate_file(args.header, builtin_constants, lambda constant:
+ generate_file(
+ args.header,
+ builtin_constants,
+ lambda constant:
# `builtins` is magic and must come first
- '' if constant.name == 'builtins' else constant.name,
- lambda constant:
- f'''{'if (!evalSettings.pureEval) ' if constant.impure else ''}{{
- addConstant({cxx_literal(('__' if constant.rename_in_global_scope else '') + constant.name)}, {constant.implementation}, {{
+ "" if constant.name == "builtins" else constant.name,
+ lambda constant: f"""{"if (!evalSettings.pureEval) " if constant.impure else ""}{{
+ addConstant({cxx_literal(("__" if constant.rename_in_global_scope else "") + constant.name)}, {constant.implementation}, {{
.type = {VALUE_TYPES[constant.type]},
.doc = {cxx_literal(constant.documentation)},
.impureOnly = {cxx_literal(constant.impure)},
}});
}}
-''')
- generate_file(args.docs, builtin_constants, lambda constant: constant.name, lambda constant:
- f'''
+""",
+ )
+ generate_file(
+ args.docs,
+ builtin_constants,
+ lambda constant: constant.name,
+ lambda constant: f"""
{constant.name} ({HUMAN_TYPES[constant.type]})
{constant.documentation}
-''' + ('''> **Note**
+"""
+ + (
+ """> **Note**
>
> Not available in [pure evaluation mode](@docroot@/command-ref/conf-file.md#conf-pure-eval).
-''' if constant.impure else '') + '''
+"""
+ if constant.impure
+ else ""
+ )
+ + """
-''')
+""",
+ )
-if __name__ == '__main__':
+
+if __name__ == "__main__":
main()
diff --git a/lix/code-generation/build_builtins.py b/lix/code-generation/build_builtins.py
index 64021b4d3..ee9c9f3a2 100644
--- a/lix/code-generation/build_builtins.py
+++ b/lix/code-generation/build_builtins.py
@@ -1,61 +1,71 @@
-from typing import List, NamedTuple, Optional
+from typing import NamedTuple
+
+from frontmatter import Post
from build_experimental_features import ExperimentalFeature
from common import cxx_literal, generate_file, load_data
+import argparse
+
+KNOWN_KEYS = {"name", "implementation", "renameInGlobalScope", "args", "experimentalFeature"}
-KNOWN_KEYS = set([
- 'name',
- 'implementation',
- 'renameInGlobalScope',
- 'args',
- 'experimentalFeature',
-])
class Builtin(NamedTuple):
name: str
implementation: str
rename_in_global_scope: bool
- args: List[str]
- experimental_feature: Optional[str]
+ args: list[str]
+ experimental_feature: str | None
documentation: str
- def parse(datum):
+ @classmethod
+ def parse(cls, datum: Post) -> "Builtin":
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
- raise Exception('unknown keys', unknown_keys)
+ msg = f"unknown keys: {unknown_keys!r}"
+ raise ValueError(msg)
return Builtin(
- name = datum['name'],
- implementation = datum['implementation'] if 'implementation' in datum else f'prim_{datum["name"]}',
- rename_in_global_scope = datum.get('renameInGlobalScope', True),
- args = datum['args'],
- experimental_feature = datum.get('experimentalFeature', None),
- documentation = datum.content,
+ name=datum["name"], # type: ignore
+ implementation=datum["implementation"]
+ if "implementation" in datum
+ else f"prim_{datum['name']}",
+ rename_in_global_scope=datum.get("renameInGlobalScope", True), # type: ignore
+ args=datum["args"], # type: ignore
+ experimental_feature=datum.get("experimentalFeature", None), # type: ignore
+ documentation=datum.content,
)
-def main():
- import argparse
+def main():
ap = argparse.ArgumentParser()
- ap.add_argument('--header', help='Path of the header to generate')
- ap.add_argument('--docs', help='Path of the documentation file to generate')
- ap.add_argument('--experimental-features', help='Directory containing the experimental feature definitions')
- ap.add_argument('defs', help='Builtin definition files', nargs='+')
+ ap.add_argument("--header", help="Path of the header to generate")
+ ap.add_argument("--docs", help="Path of the documentation file to generate")
+ ap.add_argument(
+ "--experimental-features", help="Directory containing the experimental feature definitions"
+ )
+ ap.add_argument("defs", help="Builtin definition files", nargs="+")
args = ap.parse_args()
builtins = load_data(args.defs, Builtin.parse)
- experimental_feature_names = set([builtin.experimental_feature for (_, builtin) in builtins])
+ experimental_feature_names = {builtin.experimental_feature for (_, builtin) in builtins}
experimental_feature_names.discard(None)
- experimental_feature_files = [f'{args.experimental_features}/{name}.md' for name in experimental_feature_names]
+ experimental_feature_files = [
+ f"{args.experimental_features}/{name}.md" for name in experimental_feature_names
+ ]
experimental_features = load_data(experimental_feature_files, ExperimentalFeature.parse)
- experimental_features = dict(map(lambda path_and_feature:
- (path_and_feature[1].name, f'Xp::{path_and_feature[1].internal_name}'), experimental_features))
- experimental_features[None] = 'std::nullopt'
+ experimental_features = {
+ path_and_feature[1].name: f"Xp::{path_and_feature[1].internal_name}"
+ for path_and_feature in experimental_features
+ }
+ experimental_features[None] = "std::nullopt"
- generate_file(args.header, builtins, lambda builtin: builtin.name, lambda builtin:
- f'''{'' if builtin.experimental_feature is None else f'if (experimentalFeatureSettings.isEnabled({experimental_features[builtin.experimental_feature]})) '}{{
+ generate_file(
+ args.header,
+ builtins,
+ lambda builtin: builtin.name,
+ lambda builtin: f"""{"" if builtin.experimental_feature is None else f"if (experimentalFeatureSettings.isEnabled({experimental_features[builtin.experimental_feature]})) "}{{
addPrimOp({{
- .name = {cxx_literal(('__' if builtin.rename_in_global_scope else '') + builtin.name)},
+ .name = {cxx_literal(("__" if builtin.rename_in_global_scope else "") + builtin.name)},
.args = {cxx_literal(builtin.args)},
.arity = {len(builtin.args)},
.doc = {cxx_literal(builtin.documentation)},
@@ -63,20 +73,32 @@ def main():
.experimentalFeature = {experimental_features[builtin.experimental_feature]},
}});
}}
-''')
- generate_file(args.docs, builtins, lambda builtin: builtin.name, lambda builtin:
- f'''
- {builtin.name} {' '.join([f'{arg}' for arg in builtin.args])}
+""",
+ )
+ generate_file(
+ args.docs,
+ builtins,
+ lambda builtin: builtin.name,
+ lambda builtin: f"""
+ {builtin.name} {" ".join([f"{arg}" for arg in builtin.args])}
{builtin.documentation}
-''' + (f'''This function is only available if the [{builtin.experimental_feature}](@docroot@/contributing/experimental-features.md#xp-feature-{builtin.experimental_feature}) experimental feature is enabled.
+"""
+ + (
+ f"""This function is only available if the [{builtin.experimental_feature}](@docroot@/contributing/experimental-features.md#xp-feature-{builtin.experimental_feature}) experimental feature is enabled.
-''' if builtin.experimental_feature is not None else '') + '''
+"""
+ if builtin.experimental_feature is not None
+ else ""
+ )
+ + """
-''')
+""",
+ )
-if __name__ == '__main__':
+
+if __name__ == "__main__":
main()
diff --git a/lix/code-generation/build_experimental_features.py b/lix/code-generation/build_experimental_features.py
index 8768d826e..73dedb2fd 100644
--- a/lix/code-generation/build_experimental_features.py
+++ b/lix/code-generation/build_experimental_features.py
@@ -1,58 +1,77 @@
from typing import NamedTuple
+from frontmatter import Post
+
from common import cxx_literal, generate_file, load_data
+import argparse
+
+KNOWN_KEYS = {"name", "internalName"}
-KNOWN_KEYS = set([
- 'name',
- 'internalName',
-])
class ExperimentalFeature(NamedTuple):
name: str
internal_name: str
description: str
- def parse(datum):
+ @classmethod
+ def parse(cls, datum: Post) -> "ExperimentalFeature":
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
- raise ValueError('unknown keys', unknown_keys)
+ msg = f"unknown keys: {unknown_keys!r}"
+ raise ValueError(msg)
return ExperimentalFeature(
- name = datum['name'],
- internal_name = datum['internalName'],
- description = datum.content,
+ name=datum["name"], # type: ignore
+ internal_name=datum["internalName"], # type: ignore
+ description=datum.content,
)
-def main():
- import argparse
+def main():
ap = argparse.ArgumentParser()
- ap.add_argument('--deprecated', action='store_true', help='Generate deprecated features')
- ap.add_argument('--header', help='Path of the declaration header to generate')
- ap.add_argument('--impl-header', help='Path of the implementation header to generate')
- ap.add_argument('--descriptions', help='Path of the description file to generate')
- ap.add_argument('--shortlist', help='Path of the shortlist file to generate')
- ap.add_argument('defs', help='Experimental feature definition files', nargs='+')
+ ap.add_argument("--deprecated", action="store_true", help="Generate deprecated features")
+ ap.add_argument("--header", help="Path of the declaration header to generate")
+ ap.add_argument("--impl-header", help="Path of the implementation header to generate")
+ ap.add_argument("--descriptions", help="Path of the description file to generate")
+ ap.add_argument("--shortlist", help="Path of the shortlist file to generate")
+ ap.add_argument("defs", help="Experimental feature definition files", nargs="+")
args = ap.parse_args()
features = load_data(args.defs, ExperimentalFeature.parse)
- generate_file(args.header, features, lambda feature: feature.name, lambda feature:
- f' {feature.internal_name},\n')
- generate_file(args.impl_header, features, lambda feature: feature.name, lambda feature:
- f''' {{
+ generate_file(
+ args.header,
+ features,
+ lambda feature: feature.name,
+ lambda feature: f" {feature.internal_name},\n",
+ )
+ generate_file(
+ args.impl_header,
+ features,
+ lambda feature: feature.name,
+ lambda feature: f""" {{
.tag = {"Dep" if args.deprecated else "Xp"}::{feature.internal_name},
.name = {cxx_literal(feature.name)},
.description = {cxx_literal(feature.description)},
}},
-''')
- generate_file(args.descriptions, features, lambda feature: feature.name, lambda feature:
- f'''## [`{feature.name}`]{{#{"dp" if args.deprecated else "xp"}-feature-{feature.name}}}
+""",
+ )
+ generate_file(
+ args.descriptions,
+ features,
+ lambda feature: feature.name,
+ lambda feature: f"""## [`{feature.name}`]{{#{"dp" if args.deprecated else "xp"}-feature-{feature.name}}}
{feature.description}
-''')
- generate_file(args.shortlist, features, lambda feature: feature.name, lambda feature:
- f' - [`{feature.name}`](@docroot@/contributing/{"deprecated" if args.deprecated else "experimental"}-features.md#{"dp" if args.deprecated else "xp"}-feature-{feature.name})\n')
+""",
+ )
+ generate_file(
+ args.shortlist,
+ features,
+ lambda feature: feature.name,
+ lambda feature: f" - [`{feature.name}`](@docroot@/contributing/{'deprecated' if args.deprecated else 'experimental'}-features.md#{'dp' if args.deprecated else 'xp'}-feature-{feature.name})\n",
+ )
-if __name__ == '__main__':
+
+if __name__ == "__main__":
main()
diff --git a/lix/code-generation/build_settings.py b/lix/code-generation/build_settings.py
index f9e993795..35e746f3b 100644
--- a/lix/code-generation/build_settings.py
+++ b/lix/code-generation/build_settings.py
@@ -1,101 +1,121 @@
-from typing import List, NamedTuple, Optional
+from typing import NamedTuple, Any
+
+from frontmatter import Post
from build_experimental_features import ExperimentalFeature
from common import cxx_literal, generate_file, load_data
+import argparse
+
+KNOWN_KEYS = {
+ "name",
+ "internalName",
+ "platforms",
+ "type",
+ "settingType",
+ "default",
+ "defaultExpr",
+ "defaultText",
+ "aliases",
+ "experimentalFeature",
+ "deprecated",
+}
-KNOWN_KEYS = set([
- 'name',
- 'internalName',
- 'platforms',
- 'type',
- 'settingType',
- 'default',
- 'defaultExpr',
- 'defaultText',
- 'aliases',
- 'experimentalFeature',
- 'deprecated',
-])
class Setting(NamedTuple):
name: str
internal_name: str
description: str
- platforms: Optional[List[str]]
+ platforms: list[str] | None
setting_type: str
default_expr: str
default_text: str
- aliases: List[str]
- experimental_feature: Optional[str]
+ aliases: list[str]
+ experimental_feature: str | None
deprecated: bool
- def parse(datum):
+ @classmethod
+ def parse(cls, datum: Post) -> "Setting":
unknown_keys = set(datum.keys()) - KNOWN_KEYS
if unknown_keys:
- raise ValueError('unknown keys', unknown_keys)
- default_text = f'`{nix_conf_literal(datum["default"])}`' if 'default' in datum else datum['defaultText']
- if default_text == '``':
- default_text = '*empty*'
+ msg = f"unknown keys: {unknown_keys!r}"
+ raise ValueError(msg)
+ default_text = (
+ f"`{nix_conf_literal(datum['default'])}`"
+ if "default" in datum
+ else datum["defaultText"]
+ )
+ if default_text == "``":
+ default_text = "*empty*"
return Setting(
- name = datum['name'],
- internal_name = datum['internalName'],
- description = datum.content,
- platforms = datum.get('platforms', None),
- setting_type = f'Setting<{datum["type"]}>' if 'type' in datum else datum['settingType'],
- default_expr = cxx_literal(datum['default']) if 'default' in datum else datum['defaultExpr'],
- default_text = default_text,
- aliases = datum.get('aliases', []),
- experimental_feature = datum.get('experimentalFeature', None),
- deprecated = datum.get('deprecated', False),
+ name=datum["name"], # type: ignore
+ internal_name=datum["internalName"], # type: ignore
+ description=datum.content,
+ platforms=datum.get("platforms", None), # type: ignore
+ setting_type=f"Setting<{datum['type']}>" if "type" in datum else datum["settingType"],
+ default_expr=cxx_literal(datum["default"])
+ if "default" in datum
+ else datum["defaultExpr"],
+ default_text=default_text,
+ aliases=datum.get("aliases", []), # type: ignore
+ experimental_feature=datum.get("experimentalFeature", None), # type: ignore
+ deprecated=datum.get("deprecated", False), # type: ignore
)
-platform_names = {
- 'darwin': 'Darwin',
- 'linux': 'Linux',
-}
-def nix_conf_literal(v):
+platform_names = {"darwin": "Darwin", "linux": "Linux"}
+
+
+def nix_conf_literal(v: Any) -> str:
if v is None:
- return ''
- elif isinstance(v, bool) and v == False: # 0 == False
- return 'false'
- elif isinstance(v, bool) and v == True: # 1 == True
- return 'true'
- elif isinstance(v, int):
+ return ""
+ if v is False:
+ return "false"
+ if v is True:
+ return "true"
+ if isinstance(v, int):
return str(v)
- elif isinstance(v, str):
+ if isinstance(v, str):
return v
- elif isinstance(v, list):
- return ' '.join([nix_conf_literal(item) for item in v])
- else:
- raise NotImplementedError(f'Cannot represent {repr(v)} in nix.conf')
+ if isinstance(v, list):
+ return " ".join([nix_conf_literal(item) for item in v])
+ msg = f"Cannot represent {v!r} in nix.conf"
+ raise NotImplementedError(msg)
+
+
+def indent(prefix: str, body: str) -> str:
+ return "".join(["\n" if not line else f"{prefix}{line}\n" for line in body.split("\n")])
-def indent(prefix, body):
- return ''.join(['\n' if line == '' else f'{prefix}{line}\n' for line in body.split('\n')])
def main():
- import argparse
-
ap = argparse.ArgumentParser()
- ap.add_argument('--kernel', help='Name of the kernel Lix will run on')
- ap.add_argument('--header', help='Path of the header to generate')
- ap.add_argument('--docs', help='Path of the documentation file to generate')
- ap.add_argument('--experimental-features', help='Directory containing the experimental feature definitions')
- ap.add_argument('defs', help='Setting definition files', nargs='+')
+ ap.add_argument("--kernel", help="Name of the kernel Lix will run on")
+ ap.add_argument("--header", help="Path of the header to generate")
+ ap.add_argument("--docs", help="Path of the documentation file to generate")
+ ap.add_argument(
+ "--experimental-features", help="Directory containing the experimental feature definitions"
+ )
+ ap.add_argument("defs", help="Setting definition files", nargs="+")
args = ap.parse_args()
settings = load_data(args.defs, Setting.parse)
- experimental_feature_names = set([setting.experimental_feature for (_, setting) in settings])
+ experimental_feature_names = {setting.experimental_feature for (_, setting) in settings}
experimental_feature_names.discard(None)
- experimental_feature_files = [f'{args.experimental_features}/{name}.md' for name in experimental_feature_names]
+ experimental_feature_files = [
+ f"{args.experimental_features}/{name}.md" for name in experimental_feature_names
+ ]
experimental_features = load_data(experimental_feature_files, ExperimentalFeature.parse)
- experimental_features = dict(map(lambda path_and_feature:
- (path_and_feature[1].name, f'Xp::{path_and_feature[1].internal_name}'), experimental_features))
- experimental_features[None] = 'std::nullopt'
+ experimental_features = {
+ path_and_feature[1].name: f"Xp::{path_and_feature[1].internal_name}"
+ for path_and_feature in experimental_features
+ }
+ experimental_features[None] = "std::nullopt"
- generate_file(args.header, settings, lambda setting: setting.name, lambda setting:
- f'''{setting.setting_type} {setting.internal_name} {{
+ generate_file(
+ args.header,
+ settings,
+ lambda setting: setting.name,
+ lambda setting: f"""{setting.setting_type} {setting.internal_name} {{
this,
{setting.default_expr},
{cxx_literal(setting.name)},
@@ -106,15 +126,28 @@ def main():
{cxx_literal(setting.deprecated)}
}};
-''' if setting.platforms is None or args.kernel in setting.platforms else '')
- generate_file(args.docs, settings, lambda setting: setting.name, lambda setting:
- f'''- [`{setting.name}`](#conf-{setting.name})
+"""
+ if setting.platforms is None or args.kernel in setting.platforms
+ else "",
+ )
+ generate_file(
+ args.docs,
+ settings,
+ lambda setting: setting.name,
+ lambda setting: f"""- [`{setting.name}`](#conf-{setting.name})
{indent(" ", setting.description)}
-''' + (f''' > **Note**
- > This setting is only available on {', '.join([platform_names[platform] for platform in setting.platforms])} systems.
+"""
+ + (
+ f""" > **Note**
+ > This setting is only available on {", ".join([platform_names[platform] for platform in setting.platforms])} systems.
-''' if setting.platforms is not None else '') + (f''' > **Warning**
+"""
+ if setting.platforms is not None
+ else ""
+ )
+ + (
+ f""" > **Warning**
> This setting is part of an
> [experimental feature](@docroot@/contributing/experimental-features.md).
@@ -128,14 +161,30 @@ def main():
{setting.name} = ...
```
-''' if setting.experimental_feature is not None else '') + (''' > **Warning**
+"""
+ if setting.experimental_feature is not None
+ else ""
+ )
+ + (
+ """ > **Warning**
> This setting is deprecated and will be removed in a future version of Lix.
-''' if setting.deprecated else '') + f''' **Default:** {setting.default_text}
+"""
+ if setting.deprecated
+ else ""
+ )
+ + f""" **Default:** {setting.default_text}
-''' + (f''' **Deprecated alias:** {', '.join([f'`{item}`' for item in setting.aliases])}
+"""
+ + (
+ f""" **Deprecated alias:** {", ".join([f"`{item}`" for item in setting.aliases])}
-''' if setting.aliases != [] else ''))
+"""
+ if setting.aliases != []
+ else ""
+ ),
+ )
-if __name__ == '__main__':
+
+if __name__ == "__main__":
main()
diff --git a/lix/code-generation/capnpc_wrapper.py b/lix/code-generation/capnpc_wrapper.py
index 8bcd1b36b..5ecaeb79f 100755
--- a/lix/code-generation/capnpc_wrapper.py
+++ b/lix/code-generation/capnpc_wrapper.py
@@ -1,4 +1,5 @@
#!@python@
+# ruff: noqa: SIM112 # ignore lowercase env variable names for capnpc as we have them in lower case as arguments
import argparse
import capnp
@@ -7,58 +8,61 @@ import os
import subprocess
import sys
-if lang := os.environ.get('lix_capnp_lang'):
- outputs = os.environ['lix_capnp_outputs'].split()
- old_cwd = os.environ['lix_capnp_old_cwd']
- schema = capnp.load('@capnp_include@/capnp/schema.capnp', imports=['@capnp_include@'])
+if lang := os.environ.get("lix_capnp_lang"):
+ outputs = os.environ["lix_capnp_outputs"].split()
+ old_cwd = os.environ["lix_capnp_old_cwd"]
+ schema = capnp.load("@capnp_include@/capnp/schema.capnp", imports=["@capnp_include@"])
request = schema.CodeGeneratorRequest.read(sys.stdin)
subprocess.run([lang], input=request.as_builder().to_bytes()).check_returncode()
- base_dir = os.getcwd()
+ base_dir = Path.cwd()
os.chdir(old_cwd)
- include = [ str(Path(p).resolve()) for p in os.environ['lix_capnp_include'].split(':') ]
+ include = [str(Path(p).resolve()) for p in os.environ["lix_capnp_include"].split(":")]
- if depfile := os.environ['lix_capnp_depfile']:
+ if depfile := os.environ["lix_capnp_depfile"]:
deps = ""
- for input in request.requestedFiles:
- deps += " ".join(f"{input.filename}.{o}" for o in outputs)
+ for input_file in request.requestedFiles:
+ deps += " ".join(f"{input_file.filename}.{o}" for o in outputs)
deps += ":"
- for dep in input.imports:
+ for dep in input_file.imports:
if dep.name.startswith("/"):
for candidate in (Path(i + dep.name) for i in include):
if candidate.exists():
deps += " " + str(candidate)
break
else:
- raise RuntimeError("not handling relative includes")
+ msg = "not handling relative includes"
+ raise RuntimeError(msg)
deps += "\n\n"
Path(depfile).write_text(deps)
else:
parser = argparse.ArgumentParser()
- parser.add_argument('--language')
- parser.add_argument('--outdir')
- parser.add_argument('--src-prefix')
- parser.add_argument('--depfile', default="")
- parser.add_argument('-I', '--include', action='append', default=['@capnp_include@'])
- parser.add_argument('inputs', nargs='+')
+ parser.add_argument("--language")
+ parser.add_argument("--outdir")
+ parser.add_argument("--src-prefix")
+ parser.add_argument("--depfile", default="")
+ parser.add_argument("-I", "--include", action="append", default=["@capnp_include@"])
+ parser.add_argument("inputs", nargs="+")
args = parser.parse_args()
for infile in args.inputs:
- os.environ['lix_capnp_lang'] = f"capnpc-{args.language}"
- os.environ['lix_capnp_include'] = ':'.join(args.include)
- os.environ['lix_capnp_depfile'] = args.depfile
- os.environ['lix_capnp_old_cwd'] = os.getcwd()
+ os.environ["lix_capnp_lang"] = f"capnpc-{args.language}"
+ os.environ["lix_capnp_include"] = ":".join(args.include)
+ os.environ["lix_capnp_depfile"] = args.depfile
+ os.environ["lix_capnp_old_cwd"] = str(Path.cwd())
if args.language == "c++":
- os.environ['lix_capnp_outputs'] = "c++ h"
+ os.environ["lix_capnp_outputs"] = "c++ h"
else:
raise RuntimeError("unknown language " + args.language)
- subprocess.run([
- '@capnp@',
- 'compile',
- f'-o{sys.argv[0]}:{args.outdir}',
- f'--src-prefix={args.src_prefix}',
- *(f"-I{i}" for i in args.include),
- infile
- ]).check_returncode()
+ subprocess.run(
+ [
+ "@capnp@",
+ "compile",
+ f"-o{sys.argv[0]}:{args.outdir}",
+ f"--src-prefix={args.src_prefix}",
+ *(f"-I{i}" for i in args.include),
+ infile,
+ ]
+ ).check_returncode()
diff --git a/lix/code-generation/common.py b/lix/code-generation/common.py
index 38e8122a0..3e4d8124a 100644
--- a/lix/code-generation/common.py
+++ b/lix/code-generation/common.py
@@ -1,60 +1,72 @@
+from collections.abc import Callable
+from pathlib import Path
+from typing import Any
+
import frontmatter
-import pathlib
-from collections import defaultdict
-def cxx_escape_character(c):
- if ord(c) >= 0x20 and ord(c) < 0x7f and c != '"' and c != '?' and c != '\\':
+
+def cxx_escape_character(c: str) -> str:
+ if 0x20 <= ord(c) < 0x7F and c != '"' and c != "?" and c != "\\":
return c
- elif c == '\t':
- return r'\t'
- elif c == '\n':
- return r'\n'
- elif c == '\r':
- return r'\r'
- elif c == '"':
- return r'\"'
- elif c == '?':
- return r'\?'
- elif c == '\\':
- return r'\\'
- elif ord(c) <= 0xffff:
- return str.format(r'\u{:04x}', ord(c))
- else:
- return str.format(r'\U{:08x}', ord(c))
+ if c == "\t":
+ return r"\t"
+ if c == "\n":
+ return r"\n"
+ if c == "\r":
+ return r"\r"
+ if c == '"':
+ return r"\""
+ if c == "?":
+ return r"\?"
+ if c == "\\":
+ return r"\\"
+ if ord(c) <= 0xFFFF:
+ return str.format(r"\u{:04x}", ord(c))
+ return str.format(r"\U{:08x}", ord(c))
-def cxx_literal(v):
+
+def cxx_literal(v: Any) -> str:
if v is None:
- return 'std::nullopt'
- elif isinstance(v, bool) and v == False: # 0 == False
- return 'false'
- elif isinstance(v, bool) and v == True: # 1 == True
- return 'true'
- elif isinstance(v, int):
+ return "std::nullopt"
+ if v is False:
+ return "false"
+ if v is True:
+ return "true"
+ if isinstance(v, int):
return str(v)
- elif isinstance(v, str):
- return ''.join(['"', *(cxx_escape_character(c) for c in v), '"'])
- elif isinstance(v, list):
- return f'{{{", ".join([cxx_literal(item) for item in v])}}}'
- else:
- raise NotImplementedError(f'cannot represent {repr(v)} in C++')
+ if isinstance(v, str):
+ return "".join(['"', *(cxx_escape_character(c) for c in v), '"'])
+ if isinstance(v, list):
+ return f"{{{', '.join([cxx_literal(item) for item in v])}}}"
+ msg = f"cannot represent {v!r} in C++"
+ raise NotImplementedError(msg)
-def load_data(defs, parse_function):
+
+def load_data[T](defs: list[str], parse_function: Callable[[frontmatter.Post], T]) -> list[T]:
data = []
for path in defs:
try:
datum = frontmatter.load(path)
data.append((path, parse_function(datum)))
except Exception as e:
- e.add_note(f'in {path}')
+ e.add_note(f"in {path}")
raise
return data
-def generate_file(path, data, sort_key_function, generate_function):
+
+def generate_file(
+ path: str | None,
+ data: list[Any],
+ sort_key_function: Callable[[Any], str],
+ generate_function: Callable[[Any], str],
+):
if path is not None:
- with open(path, 'w') as out:
- for path, datum in sorted(data, key=lambda pathAndDatum: sort_key_function(pathAndDatum[1])):
+ with Path(path).open("w") as out:
+ for path, datum in sorted(
+ data, key=lambda path_and_datum: sort_key_function(path_and_datum[1])
+ ):
try:
out.write(generate_function(datum))
except Exception as e:
- e.add_note(f'in {path}')
+ e.add_note(f"in {path}")
raise
diff --git a/treefmt.toml b/treefmt.toml
index aa39993f2..508801778 100644
--- a/treefmt.toml
+++ b/treefmt.toml
@@ -12,5 +12,5 @@ priority = 0
[formatter.ruff]
command = "ruff"
options = ["check"]
-includes = ["tests/functional2/**/*.py", "meson/clang-tidy/*.py", "maintainers/*.py"]
+includes = ["tests/functional2/**/*.py", "meson/clang-tidy/*.py", "maintainers/*.py", "lix/code-generation/*.py"]
priority = 1