feat: initial stub generator package
						commit
						65d135a6d1
					
				| @ -0,0 +1,49 @@ | ||||
| """Types definitions that are common in capnproto schemas.""" | ||||
| from types import ModuleType | ||||
| from typing import Dict, Tuple | ||||
| 
 | ||||
| CAPNP_TYPE_TO_PYTHON = { | ||||
|     "void": "None", | ||||
|     "bool": "bool", | ||||
|     "int8": "int", | ||||
|     "int16": "int", | ||||
|     "int32": "int", | ||||
|     "int64": "int", | ||||
|     "uint8": "int", | ||||
|     "uint16": "int", | ||||
|     "uint32": "int", | ||||
|     "uint64": "int", | ||||
|     "float32": "float", | ||||
|     "float64": "float", | ||||
|     "text": "str", | ||||
|     "data": "bytes", | ||||
| } | ||||
| 
 | ||||
| class CapnpFieldType: | ||||
|     """Types of capnproto fields.""" | ||||
| 
 | ||||
|     GROUP = "group" | ||||
|     SLOT = "slot" | ||||
| 
 | ||||
| 
 | ||||
| class CapnpSlotType: | ||||
|     """Types of capnproto slots. | ||||
| 
 | ||||
|     If CapnpFieldType is 'slot', this defines the type of that slot. | ||||
|     """ | ||||
| 
 | ||||
|     ANY_POINTER = "anyPointer" | ||||
|     STRUCT = "struct" | ||||
|     ENUM = "enum" | ||||
|     LIST = "list" | ||||
| 
 | ||||
| 
 | ||||
| class CapnpElementType: | ||||
|     """Types of capnproto elements.""" | ||||
| 
 | ||||
|     ENUM = "enum" | ||||
|     STRUCT = "struct" | ||||
|     CONST = "const" | ||||
| 
 | ||||
| 
 | ||||
| ModuleRegistryType = Dict[int, Tuple[str, ModuleType]] | ||||
| @ -0,0 +1,112 @@ | ||||
| """Command-line interface for generating type hints for *.capnp schemas. | ||||
| 
 | ||||
| Notes: | ||||
|     - This generator requires pycapnp >= 1.0.0. | ||||
|     - Capnp interfaces (RPC) are not yet supported. | ||||
| """ | ||||
| 
 | ||||
| from __future__ import annotations | ||||
| 
 | ||||
| import argparse | ||||
| import logging | ||||
| import os.path | ||||
| from typing import Sequence | ||||
| 
 | ||||
| from capnp_stub_generator.generator import run | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| def _add_output_argument(parser: argparse.ArgumentParser): | ||||
|     """Add an output argument to a parser. | ||||
| 
 | ||||
|     Args: | ||||
|         parser (argparse.ArgumentParser): The parser to add the argument to. | ||||
|     """ | ||||
|     parser.add_argument( | ||||
|         "-o", | ||||
|         "--output-dir", | ||||
|         dest="output_dir", | ||||
|         default=None, | ||||
|         help="override directory where to write the .pyi file", | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| def _add_recursive_argument(parser: argparse.ArgumentParser): | ||||
|     """Add a recursive argument to a parser. | ||||
| 
 | ||||
|     Args: | ||||
|         parser (argparse.ArgumentParser): The parser to add the argument to. | ||||
|     """ | ||||
|     parser.add_argument( | ||||
|         "-r", | ||||
|         "--recursive", | ||||
|         dest="recursive", | ||||
|         default=False, | ||||
|         action="store_true", | ||||
|         help="recursively search for *.capnp files with a given glob expression.", | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| def setup_parser() -> argparse.ArgumentParser: | ||||
|     """Setup for the parser. | ||||
| 
 | ||||
|     Returns: | ||||
|         argparse.ArgumentParser: The parser after setup. | ||||
|     """ | ||||
|     parser = argparse.ArgumentParser(description="Generate type hints for capnp schema files.") | ||||
| 
 | ||||
|     parser.add_argument( | ||||
|         "-p", | ||||
|         "--paths", | ||||
|         type=str, | ||||
|         nargs="+", | ||||
|         default=["**/*.capnp"], | ||||
|         help="path or glob expressions that match *.capnp files.", | ||||
|     ) | ||||
| 
 | ||||
|     parser.add_argument( | ||||
|         "-e", | ||||
|         "--excludes", | ||||
|         type=str, | ||||
|         nargs="+", | ||||
|         default=["**/c-capnproto/**/*.capnp"], | ||||
|         help="path or glob expressions to exclude from matches.", | ||||
|     ) | ||||
| 
 | ||||
|     _add_output_argument(parser) | ||||
|     _add_recursive_argument(parser) | ||||
| 
 | ||||
|     return parser | ||||
| 
 | ||||
| 
 | ||||
| def main(argv: Sequence[str] | None = None) -> int: | ||||
|     """Entry point of the stub generator. | ||||
| 
 | ||||
|     Args: | ||||
|         argv (Sequence[str] | None, optional): Run arguments. Defaults to None. | ||||
| 
 | ||||
|     Returns: | ||||
|         int: Error code. | ||||
|     """ | ||||
|     logging.basicConfig(level=logging.INFO) | ||||
| 
 | ||||
|     root_directory = os.getcwd() | ||||
|     logging.info("Working from root directory: %s", root_directory) | ||||
| 
 | ||||
|     parser = setup_parser() | ||||
|     args = parser.parse_args(argv) | ||||
| 
 | ||||
|     run(args, root_directory) | ||||
| 
 | ||||
|     return 0 | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     main( | ||||
|         [ | ||||
|             "-p", | ||||
|             "python/libraries/mars/mars/interfaces/**/*.capnp", | ||||
|             "-r", | ||||
|         ] | ||||
|     ) | ||||
| @ -0,0 +1,85 @@ | ||||
| """Top-level module for stub generation.""" | ||||
| 
 | ||||
| from __future__ import annotations | ||||
| 
 | ||||
| import argparse | ||||
| import glob | ||||
| import logging | ||||
| import os.path | ||||
| from types import ModuleType | ||||
| from typing import Set | ||||
| 
 | ||||
| import black | ||||
| import capnp | ||||
| import isort | ||||
| from capnp_stub_generator.capnp_types import ModuleRegistryType | ||||
| from capnp_stub_generator.helper import replace_capnp_suffix | ||||
| from capnp_stub_generator.writer import Writer | ||||
| 
 | ||||
| capnp.remove_import_hook() | ||||
| 
 | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| PYI_SUFFIX = ".pyi" | ||||
| PY_SUFFIX = ".py" | ||||
| 
 | ||||
| 
 | ||||
| def generate_stubs(module: ModuleType, module_registry: ModuleRegistryType, output_file_path: str): | ||||
|     """Entry-point for generating *.pyi stubs from a module definition. | ||||
| 
 | ||||
|     Args: | ||||
|         module (ModuleType): The module to generate stubs for. | ||||
|         module_registry (ModuleRegistryType): A registry of all detected modules. | ||||
|         output_file_path (str): The name of the output stub files, without file extension. | ||||
|     """ | ||||
|     writer = Writer(module, module_registry) | ||||
|     writer.generate_recursive() | ||||
| 
 | ||||
|     for outputs, suffix in zip((writer.dumps_pyi(), writer.dumps_py()), (PYI_SUFFIX, PY_SUFFIX)): | ||||
|         sorted_imports = isort.code(outputs, config=isort.Config(profile="black")) | ||||
|         formatted_output = black.format_str(sorted_imports, mode=black.Mode(is_pyi=True, line_length=79)) | ||||
| 
 | ||||
|         with open(output_file_path + suffix, "w", encoding="utf8") as output_file: | ||||
|             output_file.write(formatted_output) | ||||
| 
 | ||||
|     logger.info("Wrote stubs to '%s(%s/%s)'.", output_file_path, PYI_SUFFIX, PY_SUFFIX) | ||||
| 
 | ||||
| 
 | ||||
| def run(args: argparse.Namespace, root_directory: str): | ||||
|     """Run the stub generator on a set of paths that point to *.capnp schemas. | ||||
| 
 | ||||
|     Uses `generate_stubs` on each input file. | ||||
| 
 | ||||
|     Args: | ||||
|         args (argparse.Namespace): The arguments that were passed when calling the stub generator. | ||||
|         root_directory (str): The directory, from which the generator is executed. | ||||
|     """ | ||||
|     paths: str = args.paths | ||||
|     excludes: str = args.excludes | ||||
| 
 | ||||
|     excluded_paths: Set[str] = set() | ||||
|     for exclude in excludes: | ||||
|         exclude_directory = os.path.join(root_directory, exclude) | ||||
|         excluded_paths = excluded_paths.union(glob.glob(exclude_directory, recursive=args.recursive)) | ||||
| 
 | ||||
|     search_paths: Set[str] = set() | ||||
|     for path in paths: | ||||
|         search_directory = os.path.join(root_directory, path) | ||||
|         search_paths = search_paths.union(glob.glob(search_directory, recursive=args.recursive)) | ||||
| 
 | ||||
|     # The `valid_paths` contain the automatically detected search paths, except for specifically excluded paths. | ||||
|     valid_paths = search_paths - excluded_paths | ||||
| 
 | ||||
|     parser = capnp.SchemaParser() | ||||
|     module_registry: ModuleRegistryType = {} | ||||
| 
 | ||||
|     for path in valid_paths: | ||||
|         module = parser.load(path) | ||||
|         module_registry[module.schema.node.id] = (path, module) | ||||
| 
 | ||||
|     for path, module in module_registry.values(): | ||||
|         output_directory = os.path.dirname(path) | ||||
|         output_file_name = replace_capnp_suffix(os.path.basename(path)) | ||||
| 
 | ||||
|         generate_stubs(module, module_registry, os.path.join(output_directory, output_file_name)) | ||||
| @ -0,0 +1,19 @@ | ||||
| """Helper functionality that is used in other modules of this package.""" | ||||
| 
 | ||||
| 
 | ||||
| def replace_capnp_suffix(original: str) -> str: | ||||
|     """If found, replaces the .capnp suffix in a string with _capnp. | ||||
| 
 | ||||
|     For example, `some_module.capnp` becomes `some_module_capnp`. | ||||
| 
 | ||||
|     Args: | ||||
|         original (str): The string to replace the suffix in. | ||||
| 
 | ||||
|     Returns: | ||||
|         str: The string with the replaced suffix. | ||||
|     """ | ||||
|     if original.endswith(".capnp"): | ||||
|         return original.replace(".capnp", "_capnp") | ||||
| 
 | ||||
|     else: | ||||
|         return original | ||||
| @ -0,0 +1,810 @@ | ||||
| """Generate type hints for *.capnp schemas. | ||||
| 
 | ||||
| Note: This generator requires pycapnp >= 1.0.0. | ||||
| 
 | ||||
| Note: capnp interfaces (RPC) are not yet supported. | ||||
| """ | ||||
| 
 | ||||
| from __future__ import annotations | ||||
| 
 | ||||
| import dataclasses | ||||
| import keyword | ||||
| import logging | ||||
| import os.path | ||||
| import pathlib | ||||
| from types import ModuleType | ||||
| from typing import Any, Literal, Set | ||||
| 
 | ||||
| import capnp | ||||
| from capnp_stub_generator.capnp_types import ( | ||||
|     CAPNP_TYPE_TO_PYTHON, | ||||
|     CapnpElementType, | ||||
|     CapnpFieldType, | ||||
|     CapnpSlotType, | ||||
|     ModuleRegistryType, | ||||
| ) | ||||
| from capnp_stub_generator.helper import replace_capnp_suffix | ||||
| 
 | ||||
| capnp.remove_import_hook() | ||||
| 
 | ||||
| 
 | ||||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| INDENT_SPACES = 4 | ||||
| 
 | ||||
| 
 | ||||
| class NoParentError(Exception): | ||||
|     """Raised, when the parent of a scope is not available.""" | ||||
| 
 | ||||
| 
 | ||||
| @dataclasses.dataclass | ||||
| class Scope: | ||||
|     """A scope within the output .pyi file. | ||||
| 
 | ||||
|     Scopes contain text and are indented by a certain amount. They often have parents, within which they are located. | ||||
| 
 | ||||
|     Args: | ||||
|         name (str): The name of the scope. Use an empty name for the root scope (""). | ||||
|         id (int): A numerical identifier of the scope. | ||||
|         parent (Scope | None): The direct parent scope of this scope, if there is any. | ||||
|         return scope (Scope | None): The scope to which to return, when closing this one. | ||||
|         lines (list[str]): The list of text lines in this scope. | ||||
|     """ | ||||
| 
 | ||||
|     name: str | ||||
|     id: int | ||||
|     parent: Scope | None | ||||
|     return_scope: Scope | None | ||||
|     lines: list[str] = dataclasses.field(default_factory=list) | ||||
| 
 | ||||
|     def __post_init__(self): | ||||
|         """Assures that, if this is the root scope, its name is empty.""" | ||||
|         assert (self.is_root) == (self.name == "") | ||||
| 
 | ||||
|     @property | ||||
|     def parents(self) -> list[Scope]: | ||||
|         """A list of all parent scopes of this scope, starting from the first parent. | ||||
| 
 | ||||
|         If the returned list is empty, this scope has no parents. The first parent in the list has no further | ||||
|         parents, it is the root scope. | ||||
|         """ | ||||
|         parents: list[Scope] = [] | ||||
|         scope: Scope | None = self.parent | ||||
| 
 | ||||
|         while scope is not None: | ||||
|             parents.append(scope) | ||||
|             scope = scope.parent | ||||
| 
 | ||||
|         parents.reverse() | ||||
| 
 | ||||
|         return parents | ||||
| 
 | ||||
|     @property | ||||
|     def trace(self) -> list[Scope]: | ||||
|         """A list of all scopes that lead to this scope, starting from the first parent. | ||||
| 
 | ||||
|         The first parent has no further parents. | ||||
|         """ | ||||
|         return self.parents + [self] | ||||
| 
 | ||||
|     @property | ||||
|     def root(self) -> Scope: | ||||
|         """Get the root scope that has no further parents.""" | ||||
|         if not self.parents: | ||||
|             return self | ||||
| 
 | ||||
|         else: | ||||
|             return self.parents[0] | ||||
| 
 | ||||
|     @property | ||||
|     def is_root(self) -> bool: | ||||
|         """Determine, whether this is the root scope.""" | ||||
|         return self.root == self | ||||
| 
 | ||||
|     @property | ||||
|     def indent_spaces(self) -> int: | ||||
|         """The number of spaces by which this scope is indented.""" | ||||
|         return len(self.parents) * INDENT_SPACES | ||||
| 
 | ||||
|     def add_line(self, line: str = ""): | ||||
|         """Add a line to this scope, taking into account the current indent spaces. | ||||
| 
 | ||||
|         Args: | ||||
|             line (str): The line to add. Optional, defaults to "". | ||||
|         """ | ||||
|         if not line: | ||||
|             self.lines.append("") | ||||
| 
 | ||||
|         else: | ||||
|             self.lines.append(" " * self.indent_spaces + line) | ||||
| 
 | ||||
|     def trace_as_str(self, delimiter: Literal[".", "_"] = ".") -> str: | ||||
|         """A string representation of this scope's relative trace. | ||||
| 
 | ||||
|         Follow the trace of the scope, and connect parent scopes with a delimiter. | ||||
|         The root scope is not included in this trace string. | ||||
| 
 | ||||
|         Args: | ||||
|             delimiter (Literal[".", "_"]): The delimiter to join the scope names with. | ||||
|         """ | ||||
|         return delimiter.join((scope.name for scope in self.trace if not scope.is_root)) | ||||
| 
 | ||||
|     def __repr__(self) -> str: | ||||
|         """A string representation of this scope. | ||||
| 
 | ||||
|         Follow the path of scopes, and connect parent scopes with '.'. | ||||
|         """ | ||||
|         return self.trace_as_str(".") | ||||
| 
 | ||||
| 
 | ||||
| @dataclasses.dataclass | ||||
| class CapnpType: | ||||
|     """Represents a type that is extracted from a .capnp schema. | ||||
| 
 | ||||
|     Args: | ||||
|         schema (Any): | ||||
|         name (str): | ||||
|         scope (Scope): | ||||
|         generic_params (list[str]): | ||||
|     """ | ||||
| 
 | ||||
|     schema: Any | ||||
|     name: str | ||||
|     scope: Scope | ||||
|     generic_params: list[str] = dataclasses.field(default_factory=list) | ||||
| 
 | ||||
| 
 | ||||
| class Writer: | ||||
|     """A class that handles writing the stub file, based on a provided module definition.""" | ||||
| 
 | ||||
|     VALID_TYPING_IMPORTS = Literal["Generic", "TypeVar", "List", "Literal", "Union", "overload"] | ||||
| 
 | ||||
|     def __init__(self, module: ModuleType, module_registry: ModuleRegistryType): | ||||
|         """Initialize the stub writer with a module definition. | ||||
| 
 | ||||
|         Args: | ||||
|             module (ModuleType): The module definition to parse and write a stub for. | ||||
|             module_registry (ModuleRegistryType): The module registry, for finding dependencies between loaded modules. | ||||
|         """ | ||||
|         self.scope = Scope(name="", id=module.schema.node.id, parent=None, return_scope=None) | ||||
|         self.scopes_by_id: dict[int, Scope] = {self.scope.id: self.scope} | ||||
| 
 | ||||
|         self._module = module | ||||
|         self._module_registry = module_registry | ||||
| 
 | ||||
|         if self._module.__file__: | ||||
|             self._module_path = pathlib.Path(self._module.__file__) | ||||
| 
 | ||||
|         else: | ||||
|             raise ValueError("The module has no file path attached to it.") | ||||
| 
 | ||||
|         self._imports: Set[str] = set() | ||||
|         self._add_import("from __future__ import annotations") | ||||
| 
 | ||||
|         self._typing_imports: Set[Writer.VALID_TYPING_IMPORTS] = set() | ||||
| 
 | ||||
|         self.type_vars: set[str] = set() | ||||
|         self.type_map: dict[int, CapnpType] = {} | ||||
| 
 | ||||
|         self.docstring = f'"""This is an automatically generated stub for `{self._module_path.name}`."""' | ||||
| 
 | ||||
|     def _add_typing_import(self, module_name: Writer.VALID_TYPING_IMPORTS): | ||||
|         """Add an import for a module from the 'typing' package. | ||||
| 
 | ||||
|         E.g., when using | ||||
|         add_typing_import("List") | ||||
|         add_typing_import("Union") | ||||
| 
 | ||||
|         this generates an import line `from typing import List, Union`. | ||||
| 
 | ||||
|         Args: | ||||
|             module_name (Writer.VALID_TYPING_IMPORTS): The module to import from `typing`. | ||||
|         """ | ||||
|         self._typing_imports.add(module_name) | ||||
| 
 | ||||
|     def _add_import(self, import_line: str): | ||||
|         """Add a full import line. | ||||
| 
 | ||||
|         E.g. 'import numpy as np'. | ||||
| 
 | ||||
|         Args: | ||||
|             import_line (str): The import line to add. | ||||
|         """ | ||||
|         self._imports.add(import_line) | ||||
| 
 | ||||
|     def _add_enum_import(self): | ||||
|         """Adds an import for the `Enum` class.""" | ||||
|         self._add_import("from enum import Enum") | ||||
| 
 | ||||
|     @property | ||||
|     def base_module_name(self) -> str: | ||||
|         """The base name of this writer's target module.""" | ||||
|         return self._module.schema.node.displayName | ||||
| 
 | ||||
|     @property | ||||
|     def imports(self) -> list[str]: | ||||
|         """Get the full list of import strings that were added to the writer, including typing imports. | ||||
| 
 | ||||
|         Returns: | ||||
|             list[str]: The list of imports that were previously added. | ||||
|         """ | ||||
|         import_lines: list[str] = [] | ||||
| 
 | ||||
|         for imp in self._imports: | ||||
|             import_lines.append(imp) | ||||
| 
 | ||||
|         if self._typing_imports: | ||||
|             import_lines.append("from typing import " + ", ".join(sorted(self._typing_imports))) | ||||
| 
 | ||||
|         return import_lines | ||||
| 
 | ||||
|     @staticmethod | ||||
|     def get_display_name(schema: Any) -> str: | ||||
|         """Extract the display name from the schema. | ||||
| 
 | ||||
|         Args: | ||||
|             schema (Any): The schema to get the display name from. | ||||
| 
 | ||||
|         Returns: | ||||
|             str: The display name of the schema. | ||||
|         """ | ||||
|         return schema.node.displayName[schema.node.displayNamePrefixLength :] | ||||
| 
 | ||||
|     def gen_const(self, schema: Any) -> None: | ||||
|         """Generate a `const` object. | ||||
| 
 | ||||
|         Args: | ||||
|             schema (Any): The schema to generate the `const` object out of. | ||||
|         """ | ||||
|         assert schema.node.which() == CapnpElementType.CONST | ||||
| 
 | ||||
|         name = self.get_display_name(schema) | ||||
|         python_type = CAPNP_TYPE_TO_PYTHON[schema.node.const.type.which()] | ||||
|         self.scope.add_line(f"{name}: {python_type}") | ||||
| 
 | ||||
|     def gen_enum(self, schema: Any) -> CapnpType | None: | ||||
|         """Generate an `enum` object. | ||||
| 
 | ||||
|         Args: | ||||
|             schema (Any): The schema to generate the `enum` object out of. | ||||
|         """ | ||||
|         assert schema.node.which() == CapnpElementType.ENUM | ||||
| 
 | ||||
|         imported = self.register_import(schema) | ||||
| 
 | ||||
|         if imported is not None: | ||||
|             return imported | ||||
| 
 | ||||
|         name = self.get_display_name(schema) | ||||
|         self._add_enum_import() | ||||
| 
 | ||||
|         self.new_scope(name, schema.node, f"class {name}(str, Enum):") | ||||
|         self.register_type(schema.node.id, schema, name) | ||||
| 
 | ||||
|         for enumerant in schema.node.enum.enumerants: | ||||
|             value = enumerant.name | ||||
|             name = enumerant.name | ||||
| 
 | ||||
|             if enumerant.name in keyword.kwlist: | ||||
|                 # Avoid naming collisions with Python keywords. | ||||
|                 name += "_" | ||||
| 
 | ||||
|             self.scope.add_line(f'{name}: str = "{value}"') | ||||
| 
 | ||||
|         self.return_from_scope() | ||||
| 
 | ||||
|         return None | ||||
| 
 | ||||
|     def gen_generic(self, schema: Any) -> list[str]: | ||||
|         """Generate a `generic` type variable. | ||||
| 
 | ||||
|         Args: | ||||
|             schema (Any): The schema to generate the `generic` object out of. | ||||
| 
 | ||||
|         Returns: | ||||
|             list[str]: The list of registered generic type variables. | ||||
|         """ | ||||
|         self._add_typing_import("TypeVar") | ||||
|         self._add_typing_import("Generic") | ||||
| 
 | ||||
|         generic_params: list[str] = [param.name for param in schema.node.parameters] | ||||
|         referenced_params: list[str] = [] | ||||
| 
 | ||||
|         for field, _ in zip(schema.node.struct.fields, schema.as_struct().fields_list): | ||||
|             if field.slot.type.which() == "anyPointer" and field.slot.type.anyPointer.which() == "parameter": | ||||
|                 param = field.slot.type.anyPointer.parameter | ||||
| 
 | ||||
|                 t = self.get_type_by_id(param.scopeId) | ||||
| 
 | ||||
|                 if t is not None: | ||||
|                     param_source = t.schema | ||||
|                     source_params: list[str] = [param.name for param in param_source.node.parameters] | ||||
|                     referenced_params.append(source_params[param.parameterIndex]) | ||||
| 
 | ||||
|         return [self.register_type_var(param) for param in generic_params + referenced_params] | ||||
| 
 | ||||
|     def gen_slot( | ||||
|         self, | ||||
|         schema: Any, | ||||
|         field: Any, | ||||
|         raw_field: Any, | ||||
|         registered_type: CapnpType, | ||||
|         contructor_kwargs: list[str], | ||||
|         init_choices: list[tuple[str, str]], | ||||
|     ) -> None: | ||||
|         """Generate a slot of a type that is yet to be determined. | ||||
| 
 | ||||
|         Args: | ||||
|             schema (Any): The schema to extract the slot from. | ||||
|             field (Any): FIXME | ||||
|             raw_field (Any): FIXME | ||||
|             registered_type (Type): FIXME | ||||
|             contructor_kwargs (list[str]): FIXME | ||||
|             init_choices (list[tuple[str, str]]): FIXME | ||||
|         """ | ||||
| 
 | ||||
|         def gen_list_slot(): | ||||
|             """Generate a slot, which contains a `list`.""" | ||||
|             list_slot_type: CapnpElementType = field.slot.type.list.elementType.which() | ||||
| 
 | ||||
|             if list_slot_type == CapnpElementType.STRUCT: | ||||
|                 if not self.is_type_id_known(field.slot.type.list.elementType.struct.typeId): | ||||
|                     self.generate_nested(raw_field.schema.elementType) | ||||
| 
 | ||||
|             elif list_slot_type == CapnpElementType.ENUM: | ||||
|                 if not self.is_type_id_known(field.slot.type.list.elementType.enum.typeId): | ||||
|                     self.generate_nested(raw_field.schema.elementType) | ||||
| 
 | ||||
|             type_name = self.get_type_name(field.slot.type.list.elementType) | ||||
|             field_py_code = f"{field.name}: List[{type_name}]" | ||||
|             self.scope.add_line(field_py_code) | ||||
|             contructor_kwargs.append(field_py_code) | ||||
|             self._add_typing_import("List") | ||||
| 
 | ||||
|         def gen_python_type_slot(): | ||||
|             """Generate a slot, which contains a regular Python type.""" | ||||
|             python_type_name: str = CAPNP_TYPE_TO_PYTHON[field_slot_type] | ||||
| 
 | ||||
|             field_py_code = f"{field.name}: {python_type_name}" | ||||
|             self.scope.add_line(field_py_code) | ||||
|             contructor_kwargs.append(field_py_code) | ||||
| 
 | ||||
|         def gen_enum_slot(): | ||||
|             """Generate a slot, which contains an `enum`.""" | ||||
|             if not self.is_type_id_known(field.slot.type.enum.typeId): | ||||
|                 try: | ||||
|                     self.generate_nested(raw_field.schema) | ||||
| 
 | ||||
|                 except NoParentError: | ||||
|                     pass | ||||
| 
 | ||||
|             type_name = self.get_type_name(field.slot.type) | ||||
|             field_py_code = f"{field.name}: {type_name}" | ||||
|             self.scope.add_line(field_py_code) | ||||
|             contructor_kwargs.append(field_py_code) | ||||
| 
 | ||||
|         def gen_struct_slot(): | ||||
|             """Generate a slot, which contains a `struct`.""" | ||||
|             elem_type = raw_field.schema | ||||
| 
 | ||||
|             if not self.is_type_id_known(elem_type.node.id): | ||||
|                 self.gen_struct(elem_type) | ||||
| 
 | ||||
|             type_name = self.get_type_name(field.slot.type) | ||||
|             field_py_code = f"{field.name}: {type_name}" | ||||
|             self.scope.add_line(field_py_code) | ||||
|             contructor_kwargs.append(field_py_code) | ||||
|             init_choices.append((field.name, type_name)) | ||||
| 
 | ||||
|         def gen_any_pointer_slot(): | ||||
|             """Generate a slot, which contains an `any_pointer` object.""" | ||||
|             param = field.slot.type.anyPointer.parameter | ||||
|             type_name = registered_type.generic_params[param.parameterIndex] | ||||
|             field_py_code = f"{field.name}: {type_name}" | ||||
|             self.scope.add_line(field_py_code) | ||||
|             contructor_kwargs.append(field_py_code) | ||||
| 
 | ||||
|         field_slot_type = field.slot.type.which() | ||||
| 
 | ||||
|         if field_slot_type == CapnpSlotType.LIST: | ||||
|             gen_list_slot() | ||||
| 
 | ||||
|         elif field_slot_type in CAPNP_TYPE_TO_PYTHON: | ||||
|             gen_python_type_slot() | ||||
| 
 | ||||
|         elif field_slot_type == CapnpSlotType.ENUM: | ||||
|             gen_enum_slot() | ||||
| 
 | ||||
|         elif field_slot_type == CapnpSlotType.STRUCT: | ||||
|             gen_struct_slot() | ||||
| 
 | ||||
|         elif field_slot_type == CapnpSlotType.ANY_POINTER: | ||||
|             gen_any_pointer_slot() | ||||
| 
 | ||||
|         else: | ||||
|             raise AssertionError(f"{schema.node.displayName}: {field.name}: " f"{field_slot_type}") | ||||
| 
 | ||||
|     def gen_struct(self, schema: Any, type_name: str = "") -> CapnpType: | ||||
|         """Generate a `struct` object. | ||||
| 
 | ||||
|         Args: | ||||
|             schema (Any): The schema to generate the `struct` object out of. | ||||
|             type_name (str, optional): A type name to override the display name of the struct. Defaults to "". | ||||
| 
 | ||||
|         Returns: | ||||
|             Type: The `struct`-type module that was generated. | ||||
|         """ | ||||
|         assert schema.node.which() == CapnpElementType.STRUCT | ||||
| 
 | ||||
|         imported = self.register_import(schema) | ||||
| 
 | ||||
|         if imported is not None: | ||||
|             return imported | ||||
| 
 | ||||
|         if not type_name: | ||||
|             type_name = self.get_display_name(schema) | ||||
| 
 | ||||
|         registered_params: list[str] = [] | ||||
|         if schema.node.isGeneric: | ||||
|             registered_params = self.gen_generic() | ||||
| 
 | ||||
|         if registered_params: | ||||
|             scope_decl_line = f"class {type_name}(Generic[{', '.join(registered_params)}]):" | ||||
| 
 | ||||
|         else: | ||||
|             scope_decl_line = f"class {type_name}:" | ||||
| 
 | ||||
|         self.new_scope(type_name, schema.node, scope_decl_line) | ||||
| 
 | ||||
|         registered_type: CapnpType = self.register_type(schema.node.id, schema, name=type_name) | ||||
|         registered_type.generic_params = registered_params | ||||
|         type_name = registered_type.name | ||||
|         definition_has_body = False | ||||
| 
 | ||||
|         init_choices: list[tuple[str, str]] = [] | ||||
|         contructor_kwargs: list[str] = [] | ||||
| 
 | ||||
|         for field, raw_field in zip(schema.node.struct.fields, schema.as_struct().fields_list): | ||||
|             field_type = field.which() | ||||
| 
 | ||||
|             if field_type == CapnpFieldType.SLOT: | ||||
|                 definition_has_body = True | ||||
|                 self.gen_slot(schema, field, raw_field, registered_type, contructor_kwargs, init_choices) | ||||
| 
 | ||||
|             elif field_type == CapnpFieldType.GROUP: | ||||
|                 group_name = field.name[0].upper() + field.name[1:] | ||||
| 
 | ||||
|                 assert group_name != field.name | ||||
| 
 | ||||
|                 raw_schema = raw_field.schema | ||||
|                 group_name = self.gen_struct(raw_schema, type_name=group_name).name | ||||
|                 field_py_code = f"{field.name}: {group_name}" | ||||
|                 self.scope.add_line(field_py_code) | ||||
|                 contructor_kwargs.append(field_py_code) | ||||
|                 definition_has_body = True | ||||
|                 init_choices.append((field.name, group_name)) | ||||
| 
 | ||||
|             else: | ||||
|                 raise AssertionError(f"{schema.node.displayName}: {field.name}: " f"{field.which()}") | ||||
| 
 | ||||
|         if not registered_type.scope.is_root: | ||||
|             scoped_name = f"{registered_type.scope}.{type_name}" | ||||
| 
 | ||||
|         else: | ||||
|             scoped_name = type_name | ||||
| 
 | ||||
|         self.scope.add_line("@staticmethod") | ||||
|         self.scope.add_line(f"def from_bytes(data: bytes) -> {scoped_name}: ...") | ||||
|         self.scope.add_line("def to_bytes(self) -> bytes: ...") | ||||
|         definition_has_body = True | ||||
| 
 | ||||
|         if schema.node.struct.discriminantCount: | ||||
|             literals = ", ".join( | ||||
|                 f'Literal["{field.name}"]' for field in schema.node.struct.fields if field.discriminantValue != 65535 | ||||
|             ) | ||||
|             self._add_typing_import("Literal") | ||||
|             self._add_typing_import("Union") | ||||
|             self.scope.add_line(f"def which(self) -> Union[{literals}]: ...") | ||||
|             definition_has_body = True | ||||
| 
 | ||||
|         if contructor_kwargs: | ||||
|             kwargs = ", ".join(f"{kwarg} = ..." for kwarg in contructor_kwargs) | ||||
|             self.scope.add_line(f"def __init__(self, *, {kwargs}) -> None: ...") | ||||
|             definition_has_body = True | ||||
| 
 | ||||
|         if len(init_choices) > 1: | ||||
|             self._add_typing_import("Literal") | ||||
|             self._add_typing_import("overload") | ||||
| 
 | ||||
|             for field_name, field_type in init_choices: | ||||
| 
 | ||||
|                 self.scope.add_line("@overload") | ||||
|                 self.scope.add_line(f'def init(self, name: Literal["{field_name}"])' f" -> {field_type}: ...") | ||||
| 
 | ||||
|         elif len(init_choices) == 1: | ||||
|             self._add_typing_import("Literal") | ||||
|             field_name, field_type = init_choices[0] | ||||
|             self.scope.add_line(f'def init(self, name: Literal["{field_name}"])' f" -> {field_type}: ...") | ||||
| 
 | ||||
|         if not definition_has_body: | ||||
|             self.scope.add_line("pass") | ||||
| 
 | ||||
|         self.return_from_scope() | ||||
| 
 | ||||
|         return registered_type | ||||
| 
 | ||||
|     def generate_nested(self, schema: Any) -> None: | ||||
|         """Generate the type for a nested schema. | ||||
| 
 | ||||
|         Args: | ||||
|             schema (Any): The schema to generate types for. | ||||
| 
 | ||||
|         Raises: | ||||
|             AssertionError: If the schema belongs to an unknown type. | ||||
|         """ | ||||
|         if schema.node.id in self.type_map: | ||||
|             return  # already generated type hints for this type | ||||
| 
 | ||||
|         node_type = schema.node.which() | ||||
| 
 | ||||
|         if node_type == "const": | ||||
|             self.gen_const(schema) | ||||
| 
 | ||||
|         elif node_type == "struct": | ||||
|             self.gen_struct(schema) | ||||
| 
 | ||||
|         elif node_type == "enum": | ||||
|             self.gen_enum(schema) | ||||
| 
 | ||||
|         elif node_type == "interface": | ||||
|             logger.warning("Skipping interface: not implemented") | ||||
| 
 | ||||
|         else: | ||||
|             raise AssertionError(node_type) | ||||
| 
 | ||||
|     def generate_recursive(self): | ||||
|         """Generate types for all nested nodes, recursively.""" | ||||
|         for node in self._module.schema.node.nestedNodes: | ||||
|             self.generate_nested(self._module.schema.get_nested(node.name)) | ||||
| 
 | ||||
|     def register_import(self, schema) -> CapnpType | None: | ||||
|         """Determine, whether a schema is imported from the base module. | ||||
| 
 | ||||
|         If so, the type definition that the schema contains, is added to the type registry. | ||||
| 
 | ||||
|         Args: | ||||
|             schema (Any): The schema to check. | ||||
| 
 | ||||
|         Returns: | ||||
|             Type | None: The type of the import, if the schema is imported, | ||||
|                 or None if the schema defines the base module itself. | ||||
|         """ | ||||
|         module_name, definition_name = schema.node.displayName.split(":") | ||||
| 
 | ||||
|         if module_name == self.base_module_name: | ||||
|             # This is the base module, not an import. | ||||
|             return None | ||||
| 
 | ||||
|         common_path: str | ||||
|         matching_path: pathlib.Path | None = None | ||||
| 
 | ||||
|         # Find the path of the parent module, from which this schema is imported. | ||||
|         for path, module in self._module_registry.values(): | ||||
|             for node in module.schema.node.nestedNodes: | ||||
|                 if node.id == schema.node.id: | ||||
|                     matching_path = pathlib.Path(path) | ||||
|                     break | ||||
| 
 | ||||
|         # Since this is an import, there must be a parent module. | ||||
|         assert matching_path is not None | ||||
| 
 | ||||
|         # Find the relative path to go from the parent module, to this imported module. | ||||
|         common_path = os.path.commonpath([self._module_path, matching_path]) | ||||
| 
 | ||||
|         relative_module_path = self._module_path.relative_to(common_path) | ||||
|         relative_import_path = matching_path.relative_to(common_path) | ||||
| 
 | ||||
|         # Shape the relative path to a relative Python import statement. | ||||
|         python_import_path = "." * len(relative_module_path.parents) + replace_capnp_suffix( | ||||
|             ".".join(relative_import_path.parts) | ||||
|         ) | ||||
|         self._add_import(f"from {python_import_path} import {definition_name}") | ||||
| 
 | ||||
|         return self.register_type(schema.node.id, schema, name=definition_name, scope=self.scope.root) | ||||
| 
 | ||||
|     def register_type_var(self, name: str) -> str: | ||||
|         """Find and register the full name of a type variable, which includes its scopes. | ||||
| 
 | ||||
|         Args: | ||||
|             name (str): The type name to register. | ||||
| 
 | ||||
|         Returns: | ||||
|             str: The full name in the format scope0_scope1_..._scopeN_name, including the type name to register. | ||||
|         """ | ||||
|         full_name: str = self.scope.trace_as_str("_") + f"_{name}" | ||||
| 
 | ||||
|         self.type_vars.add(full_name) | ||||
|         return full_name | ||||
| 
 | ||||
|     def register_type(self, type_id: int, schema: Any, name: str = "", scope: Scope | None = None) -> CapnpType: | ||||
|         """Register a new type in the writer's registry of types. | ||||
| 
 | ||||
|         Args: | ||||
|             type_id (int): The identification number of the type. | ||||
|             schema (Any): The schema that defines the type. | ||||
|             name (str, optional): An name to specify, if overriding the type name. Defaults to "". | ||||
|             scope (Scope | None, optional): The scope in which the type is defined. Defaults to None. | ||||
| 
 | ||||
|         Returns: | ||||
|             Type: The registered type. | ||||
|         """ | ||||
|         if not name: | ||||
|             name = self.get_display_name(schema) | ||||
| 
 | ||||
|         if scope is None: | ||||
|             scope = self.scope.parent | ||||
| 
 | ||||
|         if scope is None: | ||||
|             raise ValueError(f"No valid scope was found for registering the type '{name}'.") | ||||
| 
 | ||||
|         self.type_map[type_id] = retval = CapnpType(schema=schema, name=name, scope=scope) | ||||
|         return retval | ||||
| 
 | ||||
|     def is_type_id_known(self, type_id: int) -> bool: | ||||
|         """Check, whether a type ID was previously registered. | ||||
| 
 | ||||
|         Args: | ||||
|             type_id (int): The type ID to check. | ||||
| 
 | ||||
|         Returns: | ||||
|             bool: True, if the type ID is known, False otherwise. | ||||
|         """ | ||||
|         return type_id in self.type_map | ||||
| 
 | ||||
|     def get_type_by_id(self, type_id: int) -> CapnpType: | ||||
|         """Look up a type in the type registry, by means of its ID. | ||||
| 
 | ||||
|         Args: | ||||
|             type_id (int): The identification number of the type. | ||||
| 
 | ||||
|         Raises: | ||||
|             KeyError: If the type ID was not found in the registry. | ||||
| 
 | ||||
|         Returns: | ||||
|             Type: The type, if it exists. | ||||
|         """ | ||||
|         if self.is_type_id_known(type_id): | ||||
|             return self.type_map[type_id] | ||||
| 
 | ||||
|         else: | ||||
|             raise KeyError(f"The type ID '{type_id} was not found in the type registry.'") | ||||
| 
 | ||||
|     def new_scope(self, name: str, node: Any, scope_heading: str) -> None: | ||||
|         """Creates a new scope below the scope of the provided node. | ||||
| 
 | ||||
|         Args: | ||||
|             name (str): The name of the new scope. | ||||
|             node (Any): The node whose scope is the parent scope of the new scope. | ||||
|             scope_heading (str): The line of code that starts this new scope. | ||||
|         """ | ||||
|         try: | ||||
|             parent_scope = self.scopes_by_id[node.scopeId] | ||||
| 
 | ||||
|         except KeyError as e: | ||||
|             raise NoParentError(f"The scope with name '{name}' has no parent.") from e | ||||
| 
 | ||||
|         # Add the heading of the scope to the parent scope. | ||||
|         parent_scope.add_line(scope_heading) | ||||
| 
 | ||||
|         # Then, make a new scope that is one indent level deeper. | ||||
|         child_scope = Scope(name=name, id=node.id, parent=parent_scope, return_scope=self.scope) | ||||
| 
 | ||||
|         self.scope = child_scope | ||||
|         self.scopes_by_id[node.id] = child_scope | ||||
| 
 | ||||
|     def return_from_scope(self): | ||||
|         """Return from the current scope.""" | ||||
|         # Cannot return from the root scope, as it is the highest of all scopes. | ||||
|         assert not self.scope.is_root | ||||
| 
 | ||||
|         scope = self.scope | ||||
|         scope.parent.lines += scope.lines | ||||
|         self.scope = scope.return_scope | ||||
| 
 | ||||
|     def get_type_name(self, type_reader: capnp._DynamicStructReader) -> str: | ||||
|         """Extract the type name from a type reader. | ||||
| 
 | ||||
|         The output type name is prepended by the scope name, if there is a parent scope. | ||||
| 
 | ||||
|         Args: | ||||
|             type_reader (capnp._DynamicStructReader): The type reader to get the type name from. | ||||
| 
 | ||||
|         Returns: | ||||
|             str: The extracted type name. | ||||
|         """ | ||||
|         try: | ||||
|             return CAPNP_TYPE_TO_PYTHON[type_reader.which()] | ||||
| 
 | ||||
|         except KeyError: | ||||
|             pass | ||||
| 
 | ||||
|         type_reader_type = type_reader.which() | ||||
| 
 | ||||
|         if type_reader_type == "struct": | ||||
|             element_type = self.get_type_by_id(type_reader.struct.typeId) | ||||
|             type_name = element_type.name | ||||
|             generic_params = [] | ||||
| 
 | ||||
|             for brand_scope in type_reader.struct.brand.scopes: | ||||
|                 brand_scope_type = brand_scope.which() | ||||
| 
 | ||||
|                 if brand_scope_type == "inherit": | ||||
|                     parent_scope = self.get_type_by_id(brand_scope.scopeId) | ||||
|                     generic_params.extend(parent_scope.generic_params) | ||||
| 
 | ||||
|                 elif brand_scope_type == "bind": | ||||
|                     for bind in brand_scope.bind: | ||||
|                         generic_params.append(self.get_type_name(bind.type)) | ||||
| 
 | ||||
|                 else: | ||||
|                     raise TypeError(f"Unknown brand scope '{brand_scope_type}'.") | ||||
| 
 | ||||
|             if generic_params: | ||||
|                 type_name += f"[{', '.join(generic_params)}]" | ||||
| 
 | ||||
|         elif type_reader_type == "enum": | ||||
|             element_type = self.get_type_by_id(type_reader.enum.typeId) | ||||
|             type_name = element_type.name | ||||
| 
 | ||||
|         else: | ||||
|             raise TypeError(f"Unknown type reader type '{type_reader_type}'.") | ||||
| 
 | ||||
|         if not element_type.scope.is_root: | ||||
|             return f"{element_type.scope}.{type_name}" | ||||
| 
 | ||||
|         else: | ||||
|             return type_name | ||||
| 
 | ||||
|     def dumps_pyi(self) -> str: | ||||
|         """Generates string output for the *.pyi stub file that provides type hinting. | ||||
| 
 | ||||
|         Returns: | ||||
|             str: The output string. | ||||
|         """ | ||||
|         assert self.scope.is_root | ||||
| 
 | ||||
|         out = [] | ||||
|         out.append(self.docstring) | ||||
|         out.extend(self.imports) | ||||
|         out.append("") | ||||
| 
 | ||||
|         if self.type_vars: | ||||
|             for name in sorted(self.type_vars): | ||||
|                 out.append(f'{name} = TypeVar("{name}")') | ||||
|             out.append("") | ||||
| 
 | ||||
|         out.extend(self.scope.lines) | ||||
|         return "\n".join(out) | ||||
| 
 | ||||
|     def dumps_py(self) -> str: | ||||
|         """Generates string output for the *.py stub file that handles the import of capnproto schemas. | ||||
| 
 | ||||
|         Returns: | ||||
|             str: The output string. | ||||
|         """ | ||||
|         assert self.scope.is_root | ||||
| 
 | ||||
|         out = [] | ||||
|         out.append(self.docstring) | ||||
|         out.append("import os") | ||||
|         out.append("import capnp") | ||||
|         out.append("capnp.remove_import_hook()") | ||||
|         out.append("here = os.path.dirname(os.path.abspath(__file__))") | ||||
| 
 | ||||
|         out.append(f'module_file = os.path.abspath(os.path.join(here, "{self.base_module_name}"))') | ||||
| 
 | ||||
|         for scope in self.scopes_by_id.values(): | ||||
|             if scope.parent is not None and scope.parent.is_root: | ||||
|                 out.append(f"{scope.name} = capnp.load(module_file).{scope.name}") | ||||
| 
 | ||||
|         return "\n".join(out) | ||||
| @ -0,0 +1,330 @@ | ||||
| [[package]] | ||||
| name = "atomicwrites" | ||||
| version = "1.4.0" | ||||
| description = "Atomic file writes." | ||||
| category = "dev" | ||||
| optional = false | ||||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "attrs" | ||||
| version = "21.4.0" | ||||
| description = "Classes Without Boilerplate" | ||||
| category = "dev" | ||||
| optional = false | ||||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" | ||||
| 
 | ||||
| [package.extras] | ||||
| dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] | ||||
| docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] | ||||
| tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] | ||||
| tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "black" | ||||
| version = "22.6.0" | ||||
| description = "The uncompromising code formatter." | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=3.6.2" | ||||
| 
 | ||||
| [package.dependencies] | ||||
| click = ">=8.0.0" | ||||
| mypy-extensions = ">=0.4.3" | ||||
| pathspec = ">=0.9.0" | ||||
| platformdirs = ">=2" | ||||
| tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} | ||||
| typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} | ||||
| 
 | ||||
| [package.extras] | ||||
| colorama = ["colorama (>=0.4.3)"] | ||||
| d = ["aiohttp (>=3.7.4)"] | ||||
| jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] | ||||
| uvloop = ["uvloop (>=0.15.2)"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "click" | ||||
| version = "8.1.3" | ||||
| description = "Composable command line interface toolkit" | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=3.7" | ||||
| 
 | ||||
| [package.dependencies] | ||||
| colorama = {version = "*", markers = "platform_system == \"Windows\""} | ||||
| 
 | ||||
| [[package]] | ||||
| name = "colorama" | ||||
| version = "0.4.5" | ||||
| description = "Cross-platform colored terminal text." | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "isort" | ||||
| version = "5.10.1" | ||||
| description = "A Python utility / library to sort Python imports." | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=3.6.1,<4.0" | ||||
| 
 | ||||
| [package.extras] | ||||
| pipfile_deprecated_finder = ["pipreqs", "requirementslib"] | ||||
| requirements_deprecated_finder = ["pipreqs", "pip-api"] | ||||
| colors = ["colorama (>=0.4.3,<0.5.0)"] | ||||
| plugins = ["setuptools"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "more-itertools" | ||||
| version = "8.13.0" | ||||
| description = "More routines for operating on iterables, beyond itertools" | ||||
| category = "dev" | ||||
| optional = false | ||||
| python-versions = ">=3.5" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "mypy-extensions" | ||||
| version = "0.4.3" | ||||
| description = "Experimental type system extensions for programs checked with the mypy typechecker." | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = "*" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "packaging" | ||||
| version = "21.3" | ||||
| description = "Core utilities for Python packages" | ||||
| category = "dev" | ||||
| optional = false | ||||
| python-versions = ">=3.6" | ||||
| 
 | ||||
| [package.dependencies] | ||||
| pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pathspec" | ||||
| version = "0.9.0" | ||||
| description = "Utility library for gitignore style pattern matching of file paths." | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "platformdirs" | ||||
| version = "2.5.2" | ||||
| description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=3.7" | ||||
| 
 | ||||
| [package.extras] | ||||
| docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] | ||||
| test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pluggy" | ||||
| version = "0.13.1" | ||||
| description = "plugin and hook calling mechanisms for python" | ||||
| category = "dev" | ||||
| optional = false | ||||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" | ||||
| 
 | ||||
| [package.extras] | ||||
| dev = ["pre-commit", "tox"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "py" | ||||
| version = "1.11.0" | ||||
| description = "library with cross-python path, ini-parsing, io, code, log facilities" | ||||
| category = "dev" | ||||
| optional = false | ||||
| python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pycapnp" | ||||
| version = "1.1.1" | ||||
| description = "A cython wrapping of the C++ Cap'n Proto library" | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = "*" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pyparsing" | ||||
| version = "3.0.9" | ||||
| description = "pyparsing module - Classes and methods to define and execute parsing grammars" | ||||
| category = "dev" | ||||
| optional = false | ||||
| python-versions = ">=3.6.8" | ||||
| 
 | ||||
| [package.extras] | ||||
| diagrams = ["railroad-diagrams", "jinja2"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "pytest" | ||||
| version = "5.4.3" | ||||
| description = "pytest: simple powerful testing with Python" | ||||
| category = "dev" | ||||
| optional = false | ||||
| python-versions = ">=3.5" | ||||
| 
 | ||||
| [package.dependencies] | ||||
| atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} | ||||
| attrs = ">=17.4.0" | ||||
| colorama = {version = "*", markers = "sys_platform == \"win32\""} | ||||
| more-itertools = ">=4.0.0" | ||||
| packaging = "*" | ||||
| pluggy = ">=0.12,<1.0" | ||||
| py = ">=1.5.0" | ||||
| wcwidth = "*" | ||||
| 
 | ||||
| [package.extras] | ||||
| checkqa-mypy = ["mypy (==v0.761)"] | ||||
| testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "tomli" | ||||
| version = "2.0.1" | ||||
| description = "A lil' TOML parser" | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=3.7" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "typing-extensions" | ||||
| version = "4.2.0" | ||||
| description = "Backported and Experimental Type Hints for Python 3.7+" | ||||
| category = "main" | ||||
| optional = false | ||||
| python-versions = ">=3.7" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "wcwidth" | ||||
| version = "0.2.5" | ||||
| description = "Measures the displayed width of unicode strings in a terminal" | ||||
| category = "dev" | ||||
| optional = false | ||||
| python-versions = "*" | ||||
| 
 | ||||
| [metadata] | ||||
| lock-version = "1.1" | ||||
| python-versions = "^3.8" | ||||
| content-hash = "cb72b821776054307543e305bf211ea56ad6e97d83b9b443ec5087ddef10fa98" | ||||
| 
 | ||||
| [metadata.files] | ||||
| atomicwrites = [ | ||||
|     {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, | ||||
|     {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, | ||||
| ] | ||||
| attrs = [ | ||||
|     {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, | ||||
|     {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, | ||||
| ] | ||||
| black = [ | ||||
|     {file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"}, | ||||
|     {file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"}, | ||||
|     {file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"}, | ||||
|     {file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"}, | ||||
|     {file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"}, | ||||
|     {file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"}, | ||||
|     {file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"}, | ||||
|     {file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"}, | ||||
|     {file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"}, | ||||
|     {file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"}, | ||||
|     {file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"}, | ||||
|     {file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"}, | ||||
|     {file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"}, | ||||
|     {file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"}, | ||||
|     {file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"}, | ||||
|     {file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"}, | ||||
|     {file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"}, | ||||
|     {file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"}, | ||||
|     {file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"}, | ||||
|     {file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"}, | ||||
|     {file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"}, | ||||
|     {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"}, | ||||
|     {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"}, | ||||
| ] | ||||
| click = [ | ||||
|     {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, | ||||
|     {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, | ||||
| ] | ||||
| colorama = [ | ||||
|     {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, | ||||
|     {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, | ||||
| ] | ||||
| isort = [ | ||||
|     {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, | ||||
|     {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, | ||||
| ] | ||||
| more-itertools = [ | ||||
|     {file = "more-itertools-8.13.0.tar.gz", hash = "sha256:a42901a0a5b169d925f6f217cd5a190e32ef54360905b9c39ee7db5313bfec0f"}, | ||||
|     {file = "more_itertools-8.13.0-py3-none-any.whl", hash = "sha256:c5122bffc5f104d37c1626b8615b511f3427aa5389b94d61e5ef8236bfbc3ddb"}, | ||||
| ] | ||||
| mypy-extensions = [ | ||||
|     {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, | ||||
|     {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, | ||||
| ] | ||||
| packaging = [ | ||||
|     {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, | ||||
|     {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, | ||||
| ] | ||||
| pathspec = [ | ||||
|     {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, | ||||
|     {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, | ||||
| ] | ||||
| platformdirs = [ | ||||
|     {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, | ||||
|     {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, | ||||
| ] | ||||
| pluggy = [ | ||||
|     {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, | ||||
|     {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, | ||||
| ] | ||||
| py = [ | ||||
|     {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, | ||||
|     {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, | ||||
| ] | ||||
| pycapnp = [ | ||||
|     {file = "pycapnp-1.1.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:48169ebf71b032c83348320160abef3992a9b17e7a588b372bb256426cad7564"}, | ||||
|     {file = "pycapnp-1.1.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1138be597238ca1a5837fd9c81fe10743c8826750e6d2b112d43d3c551bc474f"}, | ||||
|     {file = "pycapnp-1.1.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:313dbdf28d0f93e22df5db1ba1f0b2bf568e66f55487794ab431175fa2235d57"}, | ||||
|     {file = "pycapnp-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0627a8f981c7ad509197e4f8b4be613cc36f1c287e3f8917b77c04c50ac8b31a"}, | ||||
|     {file = "pycapnp-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:97bbd1106fa8eb46a0a6acb1ab81c8dff2ab67b0e1cb679465f368784a85d894"}, | ||||
|     {file = "pycapnp-1.1.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:e4808d524f252d8883db0f0a72f00512bdc137159272ae9436a222b7f3266ef0"}, | ||||
|     {file = "pycapnp-1.1.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:99903494567d1bec4e3eed45f8ddb67c46552ea10dc148ab12f489b02a605c2a"}, | ||||
|     {file = "pycapnp-1.1.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2dc3f390f4fd46ad84d1dee6f135770fa538b4964cd2d5430b96806958e86f15"}, | ||||
|     {file = "pycapnp-1.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:295f9c364d662a603f3454dc9a8f294a55cf7cf0f1d527032e4d933fcacb0574"}, | ||||
|     {file = "pycapnp-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f5d97c4e0ab7c84308153ee23200d75b9253b2e8617b7b9f838a1c114d448ce3"}, | ||||
|     {file = "pycapnp-1.1.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:97257ab9e33c223e2c04541c5066c09f91575c5ba7205af94c518461f7222e1a"}, | ||||
|     {file = "pycapnp-1.1.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a5ce83e1c1e492a58031f55a86f21402e067a12b7ffeb4747b3503eff87fb16e"}, | ||||
|     {file = "pycapnp-1.1.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4d824455d6e624ced4af5eaea37d88b731de078bf250f3158728ed4f8435e1bb"}, | ||||
|     {file = "pycapnp-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afbd0705d22d3179d8c3ae0bc37c4fe1c9e084f5266a1930fba6ee690cfe6703"}, | ||||
|     {file = "pycapnp-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:d4664c5b982a89ffc936e55963c333b68919d6a008f96f6989fff17c379a047c"}, | ||||
|     {file = "pycapnp-1.1.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:72d5259060f54698ca2cd1c72f57ec301a7505191c14efa3a58b536935afc011"}, | ||||
|     {file = "pycapnp-1.1.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:78b5b23c796ec956e438f04b787e31b4b059ce65ec225f4476ba7ff662d9aea8"}, | ||||
|     {file = "pycapnp-1.1.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0bac4b4af672229dcf40aa16108f6363e4dfc38ef424954a19be797eccd6da25"}, | ||||
|     {file = "pycapnp-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33165fa2bafe1916b182b123e1edb1d5288a2f4872e070bf3cfe8965c87add6d"}, | ||||
|     {file = "pycapnp-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:5652f9730f7fa695df20b7f5613a31d27bbb7827392a93b8ef32ab60504ef007"}, | ||||
|     {file = "pycapnp-1.1.1.tar.gz", hash = "sha256:45e77810624b9d2b37cbdc4d1854ff9984b3dea20d1b3f7dd4a65403263a5aea"}, | ||||
| ] | ||||
| pyparsing = [ | ||||
|     {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, | ||||
|     {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, | ||||
| ] | ||||
| pytest = [ | ||||
|     {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, | ||||
|     {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, | ||||
| ] | ||||
| tomli = [ | ||||
|     {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, | ||||
|     {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, | ||||
| ] | ||||
| typing-extensions = [ | ||||
|     {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, | ||||
|     {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, | ||||
| ] | ||||
| wcwidth = [ | ||||
|     {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, | ||||
|     {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, | ||||
| ] | ||||
| @ -0,0 +1,21 @@ | ||||
| [tool.poetry] | ||||
| name = "capnp-stub-generator" | ||||
| version = "1.0.0" | ||||
| description = "" | ||||
| authors = ["Adrian Figueroa <adrian.figueroa@metirionic.com>"] | ||||
| 
 | ||||
| [tool.poetry.dependencies] | ||||
| python = "^3.8" | ||||
| black = "^22.6.0" | ||||
| isort = "^5.10.1" | ||||
| pycapnp = "^1.1.1" | ||||
| 
 | ||||
| [tool.poetry.dev-dependencies] | ||||
| pytest = "^5.2" | ||||
| 
 | ||||
| [tool.poetry.scripts] | ||||
| capnp-stub-generator = "capnp_stub_generator.cli:main" | ||||
| 
 | ||||
| [build-system] | ||||
| requires = ["poetry-core>=1.0.0"] | ||||
| build-backend = "poetry.core.masonry.api" | ||||
					Loading…
					
					
				
		Reference in New Issue