From 921ecee9b0b5fb90cd3dc472098abccf8973fa49 Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Fri, 27 Sep 2024 07:10:13 +0000 Subject: [PATCH 1/6] #4 mv input info --- cppwg/generators.py | 2 +- cppwg/{input => info}/__init__.py | 0 cppwg/{input => info}/base_info.py | 0 cppwg/{input => info}/class_info.py | 2 +- cppwg/{input => info}/cpp_type_info.py | 2 +- cppwg/{input => info}/free_function_info.py | 2 +- cppwg/{input => info}/method_info.py | 2 +- cppwg/{input => info}/module_info.py | 6 +++--- cppwg/{input => info}/package_info.py | 2 +- cppwg/{input => info}/variable_info.py | 2 +- cppwg/parsers/package_info_parser.py | 10 +++++----- cppwg/writers/free_function_writer.py | 2 +- cppwg/writers/header_collection_writer.py | 6 +++--- 13 files changed, 19 insertions(+), 19 deletions(-) rename cppwg/{input => info}/__init__.py (100%) rename cppwg/{input => info}/base_info.py (100%) rename cppwg/{input => info}/class_info.py (99%) rename cppwg/{input => info}/cpp_type_info.py (97%) rename cppwg/{input => info}/free_function_info.py (95%) rename cppwg/{input => info}/method_info.py (92%) rename cppwg/{input => info}/module_info.py (98%) rename cppwg/{input => info}/package_info.py (99%) rename cppwg/{input => info}/variable_info.py (86%) diff --git a/cppwg/generators.py b/cppwg/generators.py index 3a1b421..a7a380b 100644 --- a/cppwg/generators.py +++ b/cppwg/generators.py @@ -10,7 +10,7 @@ import pygccxml -from cppwg.input.package_info import PackageInfo +from cppwg.info.package_info import PackageInfo from cppwg.parsers.package_info_parser import PackageInfoParser from cppwg.parsers.source_parser import CppSourceParser from cppwg.templates import pybind11_default as wrapper_templates diff --git a/cppwg/input/__init__.py b/cppwg/info/__init__.py similarity index 100% rename from cppwg/input/__init__.py rename to cppwg/info/__init__.py diff --git a/cppwg/input/base_info.py b/cppwg/info/base_info.py similarity index 100% rename from cppwg/input/base_info.py rename to cppwg/info/base_info.py diff --git a/cppwg/input/class_info.py b/cppwg/info/class_info.py similarity index 99% rename from cppwg/input/class_info.py rename to cppwg/info/class_info.py index c2edd6e..2f9adfa 100644 --- a/cppwg/input/class_info.py +++ b/cppwg/info/class_info.py @@ -8,7 +8,7 @@ from pygccxml.declarations.matchers import access_type_matcher_t from pygccxml.declarations.runtime_errors import declaration_not_found_t -from cppwg.input.cpp_type_info import CppTypeInfo +from cppwg.info.cpp_type_info import CppTypeInfo from cppwg.utils import utils diff --git a/cppwg/input/cpp_type_info.py b/cppwg/info/cpp_type_info.py similarity index 97% rename from cppwg/input/cpp_type_info.py rename to cppwg/info/cpp_type_info.py index dd846eb..3881d79 100644 --- a/cppwg/input/cpp_type_info.py +++ b/cppwg/info/cpp_type_info.py @@ -2,7 +2,7 @@ from typing import Any, Dict, List, Optional -from cppwg.input.base_info import BaseInfo +from cppwg.info.base_info import BaseInfo class CppTypeInfo(BaseInfo): diff --git a/cppwg/input/free_function_info.py b/cppwg/info/free_function_info.py similarity index 95% rename from cppwg/input/free_function_info.py rename to cppwg/info/free_function_info.py index 5f69134..d4ef490 100644 --- a/cppwg/input/free_function_info.py +++ b/cppwg/info/free_function_info.py @@ -2,7 +2,7 @@ from typing import Any, Dict, Optional -from cppwg.input.cpp_type_info import CppTypeInfo +from cppwg.info.cpp_type_info import CppTypeInfo class CppFreeFunctionInfo(CppTypeInfo): diff --git a/cppwg/input/method_info.py b/cppwg/info/method_info.py similarity index 92% rename from cppwg/input/method_info.py rename to cppwg/info/method_info.py index 2077d3b..9d066fc 100644 --- a/cppwg/input/method_info.py +++ b/cppwg/info/method_info.py @@ -2,7 +2,7 @@ from typing import Optional -from cppwg.input.cpp_type_info import CppTypeInfo +from cppwg.info.cpp_type_info import CppTypeInfo class CppMethodInfo(CppTypeInfo): diff --git a/cppwg/input/module_info.py b/cppwg/info/module_info.py similarity index 98% rename from cppwg/input/module_info.py rename to cppwg/info/module_info.py index 7a48e50..b3dcb0a 100644 --- a/cppwg/input/module_info.py +++ b/cppwg/info/module_info.py @@ -4,9 +4,9 @@ from pathlib import Path from typing import Any, Dict, List, Optional -from cppwg.input.base_info import BaseInfo -from cppwg.input.class_info import CppClassInfo -from cppwg.input.free_function_info import CppFreeFunctionInfo +from cppwg.info.base_info import BaseInfo +from cppwg.info.class_info import CppClassInfo +from cppwg.info.free_function_info import CppFreeFunctionInfo class ModuleInfo(BaseInfo): diff --git a/cppwg/input/package_info.py b/cppwg/info/package_info.py similarity index 99% rename from cppwg/input/package_info.py rename to cppwg/info/package_info.py index b31ca76..91fa52c 100644 --- a/cppwg/input/package_info.py +++ b/cppwg/info/package_info.py @@ -6,7 +6,7 @@ from pathlib import Path from typing import Any, Dict, List, Optional -from cppwg.input.base_info import BaseInfo +from cppwg.info.base_info import BaseInfo from cppwg.utils.constants import CPPWG_EXT diff --git a/cppwg/input/variable_info.py b/cppwg/info/variable_info.py similarity index 86% rename from cppwg/input/variable_info.py rename to cppwg/info/variable_info.py index 541c246..bdaea78 100644 --- a/cppwg/input/variable_info.py +++ b/cppwg/info/variable_info.py @@ -2,7 +2,7 @@ from typing import Any, Dict, Optional -from cppwg.input.cpp_type_info import CppTypeInfo +from cppwg.info.cpp_type_info import CppTypeInfo class CppVariableInfo(CppTypeInfo): diff --git a/cppwg/parsers/package_info_parser.py b/cppwg/parsers/package_info_parser.py index e2b62f7..2a28cbe 100644 --- a/cppwg/parsers/package_info_parser.py +++ b/cppwg/parsers/package_info_parser.py @@ -9,11 +9,11 @@ import yaml import cppwg.templates.custom -from cppwg.input.base_info import BaseInfo -from cppwg.input.class_info import CppClassInfo -from cppwg.input.free_function_info import CppFreeFunctionInfo -from cppwg.input.module_info import ModuleInfo -from cppwg.input.package_info import PackageInfo +from cppwg.info.base_info import BaseInfo +from cppwg.info.class_info import CppClassInfo +from cppwg.info.free_function_info import CppFreeFunctionInfo +from cppwg.info.module_info import ModuleInfo +from cppwg.info.package_info import PackageInfo from cppwg.utils import utils from cppwg.utils.constants import CPPWG_SOURCEROOT_STRING diff --git a/cppwg/writers/free_function_writer.py b/cppwg/writers/free_function_writer.py index 2c87ef4..de02532 100644 --- a/cppwg/writers/free_function_writer.py +++ b/cppwg/writers/free_function_writer.py @@ -2,7 +2,7 @@ from typing import Dict, List -from cppwg.input.free_function_info import CppFreeFunctionInfo +from cppwg.info.free_function_info import CppFreeFunctionInfo from cppwg.writers.base_writer import CppBaseWrapperWriter diff --git a/cppwg/writers/header_collection_writer.py b/cppwg/writers/header_collection_writer.py index 6ddc5c3..baab7ea 100644 --- a/cppwg/writers/header_collection_writer.py +++ b/cppwg/writers/header_collection_writer.py @@ -3,9 +3,9 @@ import os from typing import Dict -from cppwg.input.class_info import CppClassInfo -from cppwg.input.free_function_info import CppFreeFunctionInfo -from cppwg.input.package_info import PackageInfo +from cppwg.info.class_info import CppClassInfo +from cppwg.info.free_function_info import CppFreeFunctionInfo +from cppwg.info.package_info import PackageInfo class CppHeaderCollectionWriter: From f3c7aada2d167a3db5a0a9852656d17da1f153c8 Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Fri, 27 Sep 2024 07:46:57 +0000 Subject: [PATCH 2/6] #4 Add package_info.init() --- cppwg/generators.py | 7 ++----- cppwg/info/package_info.py | 14 +++++++++++++- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/cppwg/generators.py b/cppwg/generators.py index a7a380b..c2696ec 100644 --- a/cppwg/generators.py +++ b/cppwg/generators.py @@ -259,11 +259,8 @@ def generate(self) -> None: # Parse the input yaml for package, module, and class information self.parse_package_info() - # Collect header files, skipping wrappers to avoid pollution - self.package_info.collect_source_headers(restricted_paths=[self.wrapper_root]) - - # Update info objects with data from the source headers - self.package_info.update_from_source() + # Collect header files (skip wrappers), and update info + self.package_info.init(restricted_paths=[self.wrapper_root]) # Write the header collection file self.write_header_collection() diff --git a/cppwg/info/package_info.py b/cppwg/info/package_info.py index 91fa52c..06d866c 100644 --- a/cppwg/info/package_info.py +++ b/cppwg/info/package_info.py @@ -75,6 +75,18 @@ def parent(self) -> None: """Returns None as this is the top level object in the hierarchy.""" return None + def init(self, restricted_paths: List[str]) -> None: + """ + Initialise - collect header files and update info. + + Parameters + ---------- + restricted_paths : List[str] + A list of restricted paths to skip when collecting header files. + """ + self.collect_source_headers(restricted_paths) + self.update_from_source() + def collect_source_headers(self, restricted_paths: List[str]) -> None: """ Collect header files from the source root. @@ -116,7 +128,7 @@ def collect_source_headers(self, restricted_paths: List[str]) -> None: def update_from_source(self) -> None: """ - Update modules with information from the source headers. + Update with data from the source headers. """ for module_info in self.module_info_collection: module_info.update_from_source(self.source_hpp_files) From 132519b3b69d7ba05fd1bb3bcb3e6beb80f9b13c Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Fri, 27 Sep 2024 12:55:17 +0000 Subject: [PATCH 3/6] #4 Simplify parsers --- cppwg/info/base_info.py | 134 +++++++++++++++++------- cppwg/info/class_info.py | 7 +- cppwg/info/cpp_type_info.py | 34 +++--- cppwg/info/module_info.py | 64 ++++++++++-- cppwg/info/package_info.py | 26 ++--- cppwg/parsers/package_info_parser.py | 149 +++++++-------------------- cppwg/parsers/source_parser.py | 31 +++--- 7 files changed, 242 insertions(+), 203 deletions(-) diff --git a/cppwg/info/base_info.py b/cppwg/info/base_info.py index 81f39af..9e4ebba 100644 --- a/cppwg/info/base_info.py +++ b/cppwg/info/base_info.py @@ -1,7 +1,14 @@ """Generic information structure.""" +import importlib.util +import logging +import os +import sys from typing import Any, Dict, List, Optional +import cppwg.templates.custom as cppwg_custom +from cppwg.utils.constants import CPPWG_SOURCEROOT_STRING + class BaseInfo: """ @@ -14,26 +21,14 @@ class BaseInfo: Attributes ---------- - name : str - The feature name, as it appears in its definition. - source_includes : List[str] - A list of source files to be included with the feature. + arg_type_excludes : List[str] + List of exclude patterns for arg types in methods. calldef_excludes : List[str] Do not include calldefs matching these patterns. - smart_ptr_type : str, optional - Handle classes with this smart pointer type. - template_substitutions : Dict[str, List[Any]] - A list of template substitution sequences. - pointer_call_policy : str, optional - The default pointer call policy. - reference_call_policy : str, optional - The default reference call policy. - extra_code : List[str] - Any extra wrapper code for the feature. - prefix_code : List[str] - Any wrapper code that precedes the feature. - prefix_text : str, optional - Text to add at the top of all wrappers. + constructor_arg_type_excludes : List[str] + List of exclude patterns for arg types in constructors. + constructor_signature_excludes : List[List[str]] + List of exclude patterns for constructor signatures. custom_generator : str, optional A custom generator for the feature. excluded: bool @@ -42,37 +37,59 @@ class BaseInfo: Do not include these methods. excluded_variables : List[str] Do not include these variables. - arg_type_excludes : List[str] - List of exclude patterns for arg types in methods. - constructor_arg_type_excludes : List[str] - List of exclude patterns for arg types in constructors. - constructor_signature_excludes : List[List[str]] - List of exclude patterns for constructor signatures. + extra_code : List[str] + Any extra wrapper code for the feature. + name : str + The name of the package, module, class etc. represented by this object. + name_replacements : Dict[str, str] + A dictionary of name replacements e.g. {"double":"Double"} + pointer_call_policy : str, optional + The default pointer call policy. + prefix_code : List[str] + Any wrapper code that precedes the feature. + prefix_text : str, optional + Text to add at the top of all wrappers. + reference_call_policy : str, optional + The default reference call policy. return_type_excludes : List[str] List of exclude patterns for return types. - name_replacements : Dict[str, str] - A dictionary of name replacements e.g. {"double":"Double", "unsigned - int":"Unsigned"} + smart_ptr_type : str, optional + Handle classes with this smart pointer type. + source_includes : List[str] + A list of source files to be included with the feature. + template_substitutions : Dict[str, List[Any]] + A list of template substitution sequences. """ def __init__(self, name): self.name: str = name + + # Paths self.source_includes: List[str] = [] + self.source_root: str = None + + # Exclusions + self.arg_type_excludes: List[str] = [] self.calldef_excludes: List[str] = [] - self.smart_ptr_type: Optional[str] = None - self.template_substitutions: Dict[str, List[Any]] = [] + self.constructor_arg_type_excludes: List[str] = [] + self.constructor_signature_excludes: List[List[str]] = [] + self.excluded: bool = False + self.excluded_methods: List[str] = [] + self.excluded_variables: List[str] = [] + self.return_type_excludes: List[str] = [] + + # Pointers self.pointer_call_policy: Optional[str] = None self.reference_call_policy: Optional[str] = None + self.smart_ptr_type: Optional[str] = None + + # Custom Code self.extra_code: List[str] = [] self.prefix_code: List[str] = [] self.custom_generator: Optional[str] = None - self.excluded = False - self.excluded_methods: List[str] = [] - self.excluded_variables: List[str] = [] - self.arg_type_excludes: List[str] = [] - self.constructor_arg_type_excludes: List[str] = [] - self.constructor_signature_excludes: List[List[str]] = [] - self.return_type_excludes: List[str] = [] + + # Substitutions + self.template_substitutions: Dict[str, List[Any]] = [] self.name_replacements: Dict[str, str] = { "double": "Double", "unsigned int": "Unsigned", @@ -89,6 +106,8 @@ def __init__(self, name): "std::set": "Set", } + self.load_custom_generator() + @property def parent(self) -> Optional["BaseInfo"]: """ @@ -105,6 +124,49 @@ def parent(self) -> Optional["BaseInfo"]: """ return None + def load_custom_generator(self) -> None: + """ + Check if a custom generator is specified and load it. + """ + if not self.custom_generator: + return + + logger = logging.getLogger() + + # Replace the `CPPWG_SOURCEROOT` placeholder in the custom generator + # string if needed. For example, a custom generator might be specified + # as `custom_generator: CPPWG_SOURCEROOT/path/to/CustomGenerator.py` + filepath = self.custom_generator.replace( + CPPWG_SOURCEROOT_STRING, self.source_root + ) + filepath = os.path.abspath(filepath) + + # Verify that the custom generator file exists + if not os.path.isfile(filepath): + logger.error( + f"Could not find specified custom generator for {self.name}: {filepath}" + ) + raise FileNotFoundError() + + logger.info(f"Custom generator for {self.name}: {filepath}") + + # Load the custom generator as a module + module_name = os.path.splitext(filepath)[0] # /path/to/CustomGenerator + class_name = os.path.basename(module_name) # CustomGenerator + + spec = importlib.util.spec_from_file_location(module_name, filepath) + module = importlib.util.module_from_spec(spec) + sys.modules[module_name] = module + spec.loader.exec_module(module) + + # Get the custom generator class from the loaded module. + # Note: The custom generator class name must match the filename. + CustomGeneratorClass: cppwg_custom.Custom = getattr(module, class_name) + + # Replace the `info.custom_generator` string with a new object created + # from the provided custom generator class + self.custom_generator = CustomGeneratorClass() + def hierarchy_attribute(self, attribute_name: str) -> Any: """ Get the attribute value from this object or one of its parents. diff --git a/cppwg/info/class_info.py b/cppwg/info/class_info.py index 2f9adfa..54fab4f 100644 --- a/cppwg/info/class_info.py +++ b/cppwg/info/class_info.py @@ -18,21 +18,22 @@ class CppClassInfo(CppTypeInfo): Attributes ---------- + base_decls : pygccxml.declarations.declaration_t + Declarations for this type's base classes, one per template instantiation cpp_names : List[str] The C++ names of the class e.g. ["Foo<2,2>", "Foo<3,3>"] py_names : List[str] The Python names of the class e.g. ["Foo_2_2", "Foo_3_3"] - decls : pygccxml.declarations.declaration_t - Declarations for this type's base class, one per template instantiation """ def __init__(self, name: str, class_config: Optional[Dict[str, Any]] = None): super().__init__(name, class_config) + self.base_decls: Optional[List["declaration_t"]] = None # noqa: F821 + self.cpp_names: List[str] = None self.py_names: List[str] = None - self.base_decls: Optional[List["declaration_t"]] = None # noqa: F821 def extract_templates_from_source(self) -> None: """ diff --git a/cppwg/info/cpp_type_info.py b/cppwg/info/cpp_type_info.py index 3881d79..aaf5303 100644 --- a/cppwg/info/cpp_type_info.py +++ b/cppwg/info/cpp_type_info.py @@ -11,22 +11,22 @@ class CppTypeInfo(BaseInfo): Attributes ---------- + decls : pygccxml.declarations.declaration_t + The pygccxml declarations associated with this type, one per template arg if templated module_info : ModuleInfo The module info parent object associated with this type + name_override : str + The name override specified in config e.g. "CustomFoo" -> "Foo" source_file : str The source file containing the type source_file_full_path : str The full path to the source file containing the type - name_override : str - The name override specified in config e.g. "CustomFoo" -> "Foo" - template_signature : str - The template signature of the type e.g. "" - template_params : List[str] - List of template parameters e.g. ["DIM_A", "DIM_B"] template_arg_lists : List[List[Any]] List of template replacement arguments e.g. [[2, 2], [3, 3]] - decls : pygccxml.declarations.declaration_t - The pygccxml declarations associated with this type, one per template arg if templated + template_params : List[str] + List of template parameters e.g. ["DIM_A", "DIM_B"] + template_signature : str + The template signature of the type e.g. "" """ def __init__(self, name: str, type_config: Optional[Dict[str, Any]] = None): @@ -34,14 +34,24 @@ def __init__(self, name: str, type_config: Optional[Dict[str, Any]] = None): super().__init__(name) self.module_info: Optional["ModuleInfo"] = None # noqa: F821 - self.source_file_full_path: str = "" - self.source_file: str = "" + self.name_override: Optional[str] = None - self.template_signature: Optional[str] = None - self.template_params: Optional[List[str]] = None + + self.source_file: str = "" + self.source_file_full_path: str = "" + self.template_arg_lists: Optional[List[List[Any]]] = None + self.template_params: Optional[List[str]] = None + self.template_signature: Optional[str] = None + self.decls: Optional[List["declaration_t"]] = None # noqa: F821 if type_config: for key, value in type_config.items(): setattr(self, key, value) + + def set_module(self, module_info: "ModuleInfo") -> None: # noqa: F821 + """ + Set the associated module info object. + """ + self.module_info = module_info diff --git a/cppwg/info/module_info.py b/cppwg/info/module_info.py index b3dcb0a..0b9174a 100644 --- a/cppwg/info/module_info.py +++ b/cppwg/info/module_info.py @@ -15,33 +15,47 @@ class ModuleInfo(BaseInfo): Attributes ---------- - package_info : PackageInfo - The package info parent object associated with this module - source_locations : List[str] - A list of source locations for this module class_info_collection : List[CppClassInfo] A list of class info objects associated with this module free_function_info_collection : List[CppFreeFunctionInfo] A list of free function info objects associated with this module - variable_info_collection : List[CppFreeFunctionInfo] - A list of variable info objects associated with this module + package_info : PackageInfo + The package info parent object associated with this module + source_locations : List[str] + A list of source locations for this module use_all_classes : bool Use all classes in the module use_all_free_functions : bool Use all free functions in the module use_all_variables : bool Use all variables in the module + variable_info_collection : List[CppFreeFunctionInfo] + A list of variable info objects associated with this module """ - def __init__(self, name: str, module_config: Optional[Dict[str, Any]] = None): + def __init__( + self, name: str, module_config: Optional[Dict[str, Any]] = None + ) -> None: + """ + Create a module info object from a module_config dict. + Parameters + ---------- + name : str + The name of the module + module_config : Dict[str, Any] + A dictionary of module configuration settings + """ super().__init__(name) self.package_info: Optional["PackageInfo"] = None # noqa: F821 + self.source_locations: List[str] = None - self.class_info_collection: List["CppClassInfo"] = [] # noqa: F821 - self.free_function_info_collection: List["CppFreeFunctionInfo"] = [] # fmt: skip # noqa: F821 - self.variable_info_collection: List["CppFreeFunctionInfo"] = [] # noqa: F821 + + self.class_info_collection: List[CppClassInfo] = [] + self.free_function_info_collection: List[CppFreeFunctionInfo] = [] + self.variable_info_collection: List["CppVariableInfo"] = [] # noqa: F821 + self.use_all_classes: bool = False self.use_all_free_functions: bool = False self.use_all_variables: bool = False @@ -57,6 +71,27 @@ def parent(self) -> "PackageInfo": # noqa: F821 """ return self.package_info + def add_class(self, class_info: CppClassInfo) -> None: + """ + Add a class info object to the module. + """ + self.class_info_collection.append(class_info) + class_info.set_module(self) + + def add_free_function(self, free_function_info: CppFreeFunctionInfo) -> None: + """ + Add a free function info object to the module. + """ + self.free_function_info_collection.append(free_function_info) + free_function_info.set_module(self) + + def add_variable(self, variable_info: "CppVariableInfo") -> None: # noqa: F821 + """ + Add a variable info object to the module. + """ + self.variable_info_collection.append(variable_info) + variable_info.set_module(self) + def is_decl_in_source_path(self, decl: "declaration_t") -> bool: # noqa: F821 """ Check if the declaration is associated with a file in the specified source paths. @@ -81,6 +116,12 @@ def is_decl_in_source_path(self, decl: "declaration_t") -> bool: # noqa: F821 return False + def set_package(self, package_info: "PackageInfo") -> None: # noqa: F821 + """ + Set the package info object associated with this module. + """ + self.package_info = package_info + def sort_classes(self) -> None: """ Sort the class info collection in order of dependence. @@ -215,3 +256,6 @@ def update_from_source(self, source_file_paths: List[str]) -> None: """ for class_info in self.class_info_collection: class_info.update_from_source(source_file_paths) + + self.class_info_collection.sort(key=lambda x: x.name) + self.free_function_info_collection.sort(key=lambda x: x.name) diff --git a/cppwg/info/package_info.py b/cppwg/info/package_info.py index 06d866c..58dc22d 100644 --- a/cppwg/info/package_info.py +++ b/cppwg/info/package_info.py @@ -35,32 +35,22 @@ class PackageInfo(BaseInfo): """ def __init__( - self, - name: str, - source_root: str, - package_config: Optional[Dict[str, Any]] = None, + self, name: str, package_config: Optional[Dict[str, Any]] = None ) -> None: """ - Create a package info object from a package_config. - - The package_config is a dictionary of package configuration settings - extracted from a yaml input file. + Create a package info object from a package_config dict. Parameters ---------- name : str The name of the package - source_root : str - The root directory of the C++ source code package_config : Dict[str, Any] A dictionary of package configuration settings """ super().__init__(name) - self.name: str = name self.source_locations: List[str] = None self.module_info_collection: List["ModuleInfo"] = [] # noqa: F821 - self.source_root: str = source_root self.source_hpp_patterns: List[str] = ["*.hpp"] self.source_hpp_files: List[str] = [] self.common_include_file: bool = False @@ -75,6 +65,18 @@ def parent(self) -> None: """Returns None as this is the top level object in the hierarchy.""" return None + def add_module(self, module_info: "ModuleInfo") -> None: # noqa: F821 + """ + Add a module info object to the package. + + Parameters + ---------- + module_info : ModuleInfo + The module info object to add + """ + self.module_info_collection.append(module_info) + module_info.set_package(self) + def init(self, restricted_paths: List[str]) -> None: """ Initialise - collect header files and update info. diff --git a/cppwg/parsers/package_info_parser.py b/cppwg/parsers/package_info_parser.py index 2a28cbe..c7cf630 100644 --- a/cppwg/parsers/package_info_parser.py +++ b/cppwg/parsers/package_info_parser.py @@ -1,21 +1,16 @@ """Parser for input yaml.""" -import importlib.util import logging -import os -import sys -from typing import Any, Dict, Optional +from typing import Any, Dict import yaml -import cppwg.templates.custom -from cppwg.info.base_info import BaseInfo from cppwg.info.class_info import CppClassInfo from cppwg.info.free_function_info import CppFreeFunctionInfo from cppwg.info.module_info import ModuleInfo from cppwg.info.package_info import PackageInfo +from cppwg.info.variable_info import CppVariableInfo from cppwg.utils import utils -from cppwg.utils.constants import CPPWG_SOURCEROOT_STRING class PackageInfoParser: @@ -24,76 +19,15 @@ class PackageInfoParser: Attributes ---------- - input_filepath : str - The path to the package info yaml file + config_file : str + The path to the package info yaml config file source_root : str The root directory of the C++ source code - raw_package_info : Dict[str, Any] - Raw info from the yaml file - package_info : Optional[PackageInfo] - The parsed package info """ - def __init__(self, input_filepath: str, source_root: str): - self.input_filepath: str = input_filepath - self.source_root: str = source_root - - # For holding raw info from the yaml file - self.raw_package_info: Dict[str, Any] = {} - - # The parsed package info - self.package_info: Optional[PackageInfo] = None - - def check_for_custom_generators(self, info: BaseInfo) -> None: - """ - Check if a custom generator is specified and load it into a module. - - Parameters - ---------- - info : BaseInfo - The info object to check for a custom generator - might be info - about a package, module, class, or free function. - """ - logger = logging.getLogger() - - if not info.custom_generator: - return - - # Replace the `CPPWG_SOURCEROOT` placeholder in the custom generator - # string if needed. For example, a custom generator might be specified - # as `custom_generator: CPPWG_SOURCEROOT/path/to/CustomGenerator.py` - filepath: str = info.custom_generator.replace( - CPPWG_SOURCEROOT_STRING, self.source_root - ) - filepath = os.path.abspath(filepath) - - # Verify that the custom generator file exists - if not os.path.isfile(filepath): - logger.error( - f"Could not find specified custom generator for {info.name}: {filepath}" - ) - raise FileNotFoundError() - - logger.info(f"Custom generator for {info.name}: {filepath}") - - # Load the custom generator as a module - module_name: str = os.path.splitext(filepath)[0] # /path/to/CustomGenerator - class_name: str = os.path.basename(module_name) # CustomGenerator - - spec = importlib.util.spec_from_file_location(module_name, filepath) - module = importlib.util.module_from_spec(spec) - sys.modules[module_name] = module - spec.loader.exec_module(module) - - # Get the custom generator class from the loaded module. - # Note: The custom generator class name must match the filename. - CustomGeneratorClass: cppwg.templates.custom.Custom = getattr( - module, class_name - ) - - # Replace the `info.custom_generator` string with a new object created - # from the provided custom generator class - info.custom_generator = CustomGeneratorClass() + def __init__(self, config_file: str, source_root: str): + self.config_file = config_file + self.source_root = source_root def parse(self) -> PackageInfo: """ @@ -110,24 +44,28 @@ def parse(self) -> PackageInfo: logger = logging.getLogger() logger.info("Parsing package info file.") - with open(self.input_filepath, "r") as input_filepath: - self.raw_package_info = yaml.safe_load(input_filepath) + # Load raw info from the yaml file + raw_package_info: Dict[str, Any] = {} - # Default config options that apply to the package, modules, classes, and free functions + with open(self.config_file, "r") as config_file: + raw_package_info = yaml.safe_load(config_file) + + # Default config options that apply to package, modules, classes, etc. global_config: Dict[str, Any] = { - "source_includes": [], - "smart_ptr_type": None, "calldef_excludes": None, - "return_type_excludes": None, - "template_substitutions": [], - "pointer_call_policy": None, - "reference_call_policy": None, "constructor_arg_type_excludes": None, "constructor_signature_excludes": None, + "custom_generator": None, "excluded": False, "excluded_methods": [], "excluded_variables": [], - "custom_generator": None, + "pointer_call_policy": None, + "reference_call_policy": None, + "return_type_excludes": None, + "smart_ptr_type": None, + "source_includes": [], + "source_root": self.source_root, + "template_substitutions": [], "prefix_code": [], "prefix_text": "", } @@ -142,21 +80,18 @@ def parse(self) -> PackageInfo: package_config.update(global_config) for key in package_config.keys(): - if key in self.raw_package_info: - package_config[key] = self.raw_package_info[key] + if key in raw_package_info: + package_config[key] = raw_package_info[key] # Replace boolean strings with booleans utils.substitute_bool_for_string(package_config, "common_include_file") utils.substitute_bool_for_string(package_config, "exclude_default_args") # Create the PackageInfo object from the package config dict - self.package_info = PackageInfo( - package_config["name"], self.source_root, package_config - ) - self.check_for_custom_generators(self.package_info) + package_info = PackageInfo(package_config["name"], package_config) # Parse the module data - for raw_module_info in self.raw_package_info["modules"]: + for raw_module_info in raw_package_info["modules"]: # Get module config from the raw module info module_config = { "name": "cppwg_module", @@ -187,11 +122,9 @@ def parse(self) -> PackageInfo: # Create the ModuleInfo object from the module config dict module_info = ModuleInfo(module_config["name"], module_config) - self.check_for_custom_generators(module_info) - # Connect the module to the package - module_info.package_info = self.package_info - self.package_info.module_info_collection.append(module_info) + # Add the module to the package + package_info.add_module(module_info) # Parse the class data and create class info objects. # Note: if module_config["use_all_classes"] == True, class info @@ -209,13 +142,9 @@ def parse(self) -> PackageInfo: # Create the CppClassInfo object from the class config dict class_info = CppClassInfo(raw_class_info["name"], class_config) - self.check_for_custom_generators(class_info) - - # Connect the class to the module - class_info.module_info = module_info - module_info.class_info_collection.append(class_info) - module_info.class_info_collection.sort(key=lambda x: x.name) + # Add the class to the module + module_info.add_class(class_info) # Parse the free function data and create free function info objects. # Note: if module_config["use_all_free_functions"] == True, free function @@ -239,13 +168,8 @@ def parse(self) -> PackageInfo: free_function_config["name"], free_function_config ) - # Connect the free function to the module - free_function_info.module_info = module_info - module_info.free_function_info_collection.append( - free_function_info - ) - - module_info.free_function_info_collection.sort(key=lambda x: x.name) + # Add the free function to the module + module_info.add_free_function(free_function_info) # Parse the variable data if not module_config["use_all_variables"]: @@ -258,13 +182,12 @@ def parse(self) -> PackageInfo: if key in raw_variable_info: variable_config[key] = raw_variable_info[key] - # Create the CppFreeFunctionInfo object from the variable config dict - variable_info = CppFreeFunctionInfo( + # Create the CppVariableInfo object from the variable config dict + variable_info = CppVariableInfo( variable_config["name"], variable_config ) - # Connect the variable to the module - variable_info.module_info = module_info - module_info.variable_info_collection.append(variable_info) + # Add the variable to the module + module_info.add_variable(variable_info) - return self.package_info + return package_info diff --git a/cppwg/parsers/source_parser.py b/cppwg/parsers/source_parser.py index ff4a73c..211b797 100644 --- a/cppwg/parsers/source_parser.py +++ b/cppwg/parsers/source_parser.py @@ -2,7 +2,7 @@ import logging from pathlib import Path -from typing import List, Optional +from typing import List from pygccxml import declarations, parser from pygccxml.declarations import declaration_t @@ -28,20 +28,20 @@ class CppSourceParser: Attributes ---------- - source_root : str - The root directory of the source code - wrapper_header_collection : str - The path to the header collection file - castxml_binary : str - The path to the CastXML binary - source_includes : List[str] - The list of source include paths castxml_cflags : str Optional cflags to be passed to CastXML e.g. "-std=c++17" + castxml_binary : str + The path to the CastXML binary global_ns : namespace_t The namespace containing all parsed C++ declarations + source_includes : List[str] + The list of source include paths source_ns : namespace_t The namespace containing C++ declarations from the source tree + source_root : str + The root directory of the source code + wrapper_header_collection : str + The path to the header collection file """ def __init__( @@ -58,9 +58,6 @@ def __init__( self.source_includes: List[str] = source_includes self.castxml_cflags: str = castxml_cflags - self.source_ns: Optional[namespace_t] = None - self.global_ns: Optional[namespace_t] = None - def parse(self) -> namespace_t: """ Parse the C++ source code from the header collection using CastXML and pygccxml. @@ -89,12 +86,12 @@ def parse(self) -> namespace_t: ) # Get access to the global namespace - self.global_ns: namespace_t = declarations.get_global_namespace(decls) + global_ns: namespace_t = declarations.get_global_namespace(decls) # Filter declarations for which files exist logger.info("Filtering source declarations.") query = declarations.custom_matcher_t(lambda decl: decl.location is not None) - filtered_decls: mdecl_wrapper_t = self.global_ns.decls(function=query) + filtered_decls: mdecl_wrapper_t = global_ns.decls(function=query) # Filter declarations in our source tree; include declarations from the # wrapper_header_collection file for explicit instantiations, typedefs etc. @@ -106,10 +103,10 @@ def parse(self) -> namespace_t: ] # Create a source namespace module for the filtered declarations - self.source_ns = namespace_t(name="source", declarations=source_decls) + source_ns = namespace_t(name="source", declarations=source_decls) # Initialise the source namespace's internal type hash tables for faster queries logger.info("Optimizing source declaration queries.") - self.source_ns.init_optimizer() + source_ns.init_optimizer() - return self.source_ns + return source_ns From 0d90170ac47d1c96ca53afe6b9002374673b4096 Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Fri, 27 Sep 2024 17:54:05 +0000 Subject: [PATCH 4/6] #4 Simplify info attributes --- cppwg/generators.py | 4 +- cppwg/info/base_info.py | 152 ++++++++++++++++------ cppwg/info/class_info.py | 54 ++++---- cppwg/info/cpp_type_info.py | 37 +++--- cppwg/info/method_info.py | 2 +- cppwg/info/module_info.py | 91 +++++++------ cppwg/info/package_info.py | 44 ++++--- cppwg/parsers/package_info_parser.py | 88 +++++++------ cppwg/writers/class_writer.py | 30 +++-- cppwg/writers/constructor_writer.py | 24 +++- cppwg/writers/header_collection_writer.py | 28 ++-- cppwg/writers/module_writer.py | 24 ++-- cppwg/writers/package_writer.py | 2 +- 13 files changed, 354 insertions(+), 226 deletions(-) diff --git a/cppwg/generators.py b/cppwg/generators.py index c2696ec..c6aef3d 100644 --- a/cppwg/generators.py +++ b/cppwg/generators.py @@ -174,8 +174,8 @@ def log_unknown_classes(self) -> None: all_class_decls = self.source_ns.classes(allow_empty=True) seen_class_names = set() - for module_info in self.package_info.module_info_collection: - for class_info in module_info.class_info_collection: + for module_info in self.package_info.module_collection: + for class_info in module_info.class_collection: seen_class_names.add(class_info.name) if class_info.decls: seen_class_names.update(decl.name for decl in class_info.decls) diff --git a/cppwg/info/base_info.py b/cppwg/info/base_info.py index 9e4ebba..65f14e6 100644 --- a/cppwg/info/base_info.py +++ b/cppwg/info/base_info.py @@ -29,7 +29,7 @@ class BaseInfo: List of exclude patterns for arg types in constructors. constructor_signature_excludes : List[List[str]] List of exclude patterns for constructor signatures. - custom_generator : str, optional + custom_generator : str A custom generator for the feature. excluded: bool Exclude this feature. @@ -43,30 +43,45 @@ class BaseInfo: The name of the package, module, class etc. represented by this object. name_replacements : Dict[str, str] A dictionary of name replacements e.g. {"double":"Double"} - pointer_call_policy : str, optional + pointer_call_policy : str The default pointer call policy. prefix_code : List[str] Any wrapper code that precedes the feature. - prefix_text : str, optional + prefix_text : str Text to add at the top of all wrappers. - reference_call_policy : str, optional + reference_call_policy : str The default reference call policy. return_type_excludes : List[str] List of exclude patterns for return types. - smart_ptr_type : str, optional + smart_ptr_type : str Handle classes with this smart pointer type. source_includes : List[str] A list of source files to be included with the feature. + source_root : str + The root directory of the C++ source code. template_substitutions : Dict[str, List[Any]] A list of template substitution sequences. + + custom_generator_instance : cppwg_custom.Custom + An instance of the custom generator class. """ - def __init__(self, name): + def __init__(self, name: str, info_config: Optional[Dict[str, Any]] = None) -> None: + """ + Create a base info object from a config dict. + + Parameters + ---------- + name : str + The name of the package, module, class, etc. represented by this object. + info_config : Dict[str, Any] + A dictionary of configuration settings + """ self.name: str = name # Paths self.source_includes: List[str] = [] - self.source_root: str = None + self.source_root: str = "" # Exclusions self.arg_type_excludes: List[str] = [] @@ -79,14 +94,9 @@ def __init__(self, name): self.return_type_excludes: List[str] = [] # Pointers - self.pointer_call_policy: Optional[str] = None - self.reference_call_policy: Optional[str] = None - self.smart_ptr_type: Optional[str] = None - - # Custom Code - self.extra_code: List[str] = [] - self.prefix_code: List[str] = [] - self.custom_generator: Optional[str] = None + self.pointer_call_policy: str = "" + self.reference_call_policy: str = "" + self.smart_ptr_type: str = "" # Substitutions self.template_substitutions: Dict[str, List[Any]] = [] @@ -106,6 +116,70 @@ def __init__(self, name): "std::set": "Set", } + # Custom Code + self.extra_code: List[str] = [] + self.prefix_code: List[str] = [] + self.prefix_text: str = "" + self.custom_generator: str = "" + + self.custom_generator_instance: cppwg_custom.Custom = None + + if info_config: + # Paths + self.source_includes = info_config.get( + "source_includes", self.source_includes + ) + self.source_root = info_config.get("source_root", self.source_root) + + # Exclusions + self.arg_type_excludes = info_config.get( + "arg_type_excludes", self.arg_type_excludes + ) + self.calldef_excludes = info_config.get( + "calldef_excludes", self.calldef_excludes + ) + self.constructor_arg_type_excludes = info_config.get( + "constructor_arg_type_excludes", self.constructor_arg_type_excludes + ) + self.constructor_signature_excludes = info_config.get( + "constructor_signature_excludes", self.constructor_signature_excludes + ) + self.excluded = info_config.get("excluded", self.excluded) + self.excluded_methods = info_config.get( + "excluded_methods", self.excluded_methods + ) + self.excluded_variables = info_config.get( + "excluded_variables", self.excluded_variables + ) + self.return_type_excludes = info_config.get( + "return_type_excludes", self.return_type_excludes + ) + + # Pointers + self.pointer_call_policy = info_config.get( + "pointer_call_policy", self.pointer_call_policy + ) + self.reference_call_policy = info_config.get( + "reference_call_policy", self.reference_call_policy + ) + self.smart_ptr_type = info_config.get("smart_ptr_type", self.smart_ptr_type) + + # Substitutions + self.template_substitutions = info_config.get( + "template_substitutions", self.template_substitutions + ) + self.name_replacements = info_config.get( + "name_replacements", self.name_replacements + ) + + # Custom Code + self.extra_code = info_config.get("extra_code", self.extra_code) + self.prefix_code = info_config.get("prefix_code", self.prefix_code) + self.prefix_text = info_config.get("prefix_text", self.prefix_text) + self.custom_generator = info_config.get( + "custom_generator", self.custom_generator + ) + self.load_custom_generator() @property @@ -134,7 +208,7 @@ def load_custom_generator(self) -> None: logger = logging.getLogger() # Replace the `CPPWG_SOURCEROOT` placeholder in the custom generator - # string if needed. For example, a custom generator might be specified + # path if needed. For example, a custom generator might be specified # as `custom_generator: CPPWG_SOURCEROOT/path/to/CustomGenerator.py` filepath = self.custom_generator.replace( CPPWG_SOURCEROOT_STRING, self.source_root @@ -163,17 +237,15 @@ def load_custom_generator(self) -> None: # Note: The custom generator class name must match the filename. CustomGeneratorClass: cppwg_custom.Custom = getattr(module, class_name) - # Replace the `info.custom_generator` string with a new object created - # from the provided custom generator class - self.custom_generator = CustomGeneratorClass() + # Instantiate the custom generator from the provided class + self.custom_generator_instance = CustomGeneratorClass() def hierarchy_attribute(self, attribute_name: str) -> Any: """ - Get the attribute value from this object or one of its parents. + Get the attribute value from this object or the one that owns this. - For the supplied attribute, iterate through parent objects until a non-None - value is found. If the top level parent (i.e. package) attribute is - None, return None. + Search higher level objects recursively and return the first + value found for the attribute. Parameters ---------- @@ -183,21 +255,24 @@ def hierarchy_attribute(self, attribute_name: str) -> Any: Returns ------- Any - The attribute value. + The attribute value, or None if not found. """ - if hasattr(self, attribute_name) and getattr(self, attribute_name) is not None: - return getattr(self, attribute_name) + value = getattr(self, attribute_name, None) + if value: + return value - if hasattr(self, "parent") and self.parent is not None: - return self.parent.hierarchy_attribute(attribute_name) + if self.parent is None: + # Reached the top of the hierarchy (i.e. PackageInfo) + return None - return None + return self.parent.hierarchy_attribute(attribute_name) def hierarchy_attribute_gather(self, attribute_name: str) -> List[Any]: """ - Get a list of attribute values from this object and its parents. + Get a list of attribute values from this object or the one that owns it. - For the supplied attribute, iterate through parent objects gathering list entries. + Search higher level objects recursively, gathering attribute values + into a list wherever the attribute is found. Parameters ---------- @@ -209,12 +284,15 @@ def hierarchy_attribute_gather(self, attribute_name: str) -> List[Any]: List[Any] The list of attribute values. """ - att_list: List[Any] = [] + value_list: List[Any] = [] - if hasattr(self, attribute_name) and getattr(self, attribute_name) is not None: - att_list.extend(getattr(self, attribute_name)) + value = getattr(self, attribute_name, None) + if value: + value_list.append(value) - if hasattr(self, "parent") and self.parent is not None: - att_list.extend(self.parent.hierarchy_attribute_gather(attribute_name)) + if self.parent is None: + # Reached the top of the hierarchy (i.e. PackageInfo) + return value_list - return att_list + value_list.extend(self.parent.hierarchy_attribute_gather(attribute_name)) + return value_list diff --git a/cppwg/info/class_info.py b/cppwg/info/class_info.py index 54fab4f..6cd76cb 100644 --- a/cppwg/info/class_info.py +++ b/cppwg/info/class_info.py @@ -30,10 +30,9 @@ def __init__(self, name: str, class_config: Optional[Dict[str, Any]] = None): super().__init__(name, class_config) - self.base_decls: Optional[List["declaration_t"]] = None # noqa: F821 - - self.cpp_names: List[str] = None - self.py_names: List[str] = None + self.base_decls: List["declaration_t"] = [] # noqa: F821 + self.cpp_names: List[str] = [] + self.py_names: List[str] = [] def extract_templates_from_source(self) -> None: """ @@ -48,13 +47,19 @@ def extract_templates_from_source(self) -> None: return # Skip if there is no source file - source_path = self.source_file_full_path + source_path = self.source_file_path if not source_path: return # Get list of template substitutions applicable to this class # e.g. [ {"signature":"", "replacement":[[2,2], [3,3]]} ] - substitutions = self.hierarchy_attribute_gather("template_substitutions") + substitutions = [ + ts_dict + for ts_dict_list in self.hierarchy_attribute_gather( + "template_substitutions" + ) + for ts_dict in ts_dict_list + ] # Skip if there are no applicable template substitutions if not substitutions: @@ -85,7 +90,6 @@ def extract_templates_from_source(self) -> None: self.template_arg_lists = substitution["replacement"] # Extract parameters ["A", "B"] from "" - self.template_params = [] for part in signature.split(","): param = ( part.strip() @@ -169,8 +173,6 @@ def update_from_ns(self, source_ns: "namespace_t") -> None: # noqa: F821 if self.excluded: return - self.decls = [] - for class_cpp_name in self.cpp_names: class_name = class_cpp_name.replace(" ", "") # e.g. Foo<2,2> @@ -178,10 +180,7 @@ def update_from_ns(self, source_ns: "namespace_t") -> None: # noqa: F821 class_decl = source_ns.class_(class_name) except declaration_not_found_t as e1: - if ( - self.template_signature is None - or "=" not in self.template_signature - ): + if "=" not in self.template_signature: logger.error(f"Could not find declaration for class {class_name}") raise e1 @@ -213,9 +212,9 @@ def update_from_ns(self, source_ns: "namespace_t") -> None: # noqa: F821 self.decls.append(class_decl) # Update the class source file if not already set - if not self.source_file_full_path: - self.source_file_full_path = self.decls[0].location.file_name - self.source_file = os.path.basename(self.source_file_full_path) + if not self.source_file_path: + self.source_file_path = self.decls[0].location.file_name + self.source_file = os.path.basename(self.source_file_path) # Update the base class declarations self.base_decls = [ @@ -236,18 +235,18 @@ def update_from_source(self, source_file_paths: List[str]) -> None: return # Attempt to map class to a source file - if self.source_file_full_path: - self.source_file = os.path.basename(self.source_file_full_path) + if self.source_file_path: + self.source_file = os.path.basename(self.source_file_path) else: for file_path in source_file_paths: file_name = os.path.basename(file_path) # Match file name if set if self.source_file == file_name: - self.source_file_full_path = file_path + self.source_file_path = file_path # Match class name, assuming the file name is the class name elif self.name == os.path.splitext(file_name)[0]: self.source_file = file_name - self.source_file_full_path = file_path + self.source_file_path = file_path # Extract template args from the source file self.extract_templates_from_source() @@ -267,22 +266,20 @@ def update_py_names(self) -> None: ["Foo_2_2", "Foo_3_3"]. """ # Handles untemplated classes - if self.template_arg_lists is None: + if not self.template_arg_lists: if self.name_override: - self.py_names = [self.name_override] + self.py_names.append(self.name_override) else: - self.py_names = [self.name] + self.py_names.append(self.name) return - self.py_names = [] - # Table of special characters for removal rm_chars = {"<": None, ">": None, ",": None, " ": None} rm_table = str.maketrans(rm_chars) # Clean the type name type_name = self.name - if self.name_override is not None: + if self.name_override: type_name = self.name_override # Do standard name replacements e.g. "unsigned int" -> "Unsigned" @@ -333,11 +330,10 @@ def update_cpp_names(self) -> None: ["Foo<2, 2>", "Foo<3, 3>"]. """ # Handles untemplated classes - if self.template_arg_lists is None: - self.cpp_names = [self.name] + if not self.template_arg_lists: + self.cpp_names.append(self.name) return - self.cpp_names = [] for template_arg_list in self.template_arg_lists: # Create template string from arg list e.g. [2, 2] -> "<2, 2>" template_string = ", ".join([str(arg) for arg in template_arg_list]) diff --git a/cppwg/info/cpp_type_info.py b/cppwg/info/cpp_type_info.py index aaf5303..9bdbd6f 100644 --- a/cppwg/info/cpp_type_info.py +++ b/cppwg/info/cpp_type_info.py @@ -11,16 +11,18 @@ class CppTypeInfo(BaseInfo): Attributes ---------- - decls : pygccxml.declarations.declaration_t - The pygccxml declarations associated with this type, one per template arg if templated - module_info : ModuleInfo - The module info parent object associated with this type name_override : str The name override specified in config e.g. "CustomFoo" -> "Foo" source_file : str The source file containing the type - source_file_full_path : str + source_file_path : str The full path to the source file containing the type + + module_info : ModuleInfo + The module info parent object associated with this type + + decls : pygccxml.declarations.declaration_t + The pygccxml declarations associated with this type, one per template arg if templated template_arg_lists : List[List[Any]] List of template replacement arguments e.g. [[2, 2], [3, 3]] template_params : List[str] @@ -31,24 +33,25 @@ class CppTypeInfo(BaseInfo): def __init__(self, name: str, type_config: Optional[Dict[str, Any]] = None): - super().__init__(name) - - self.module_info: Optional["ModuleInfo"] = None # noqa: F821 - - self.name_override: Optional[str] = None + super().__init__(name, type_config) + self.name_override: str = "" self.source_file: str = "" - self.source_file_full_path: str = "" + self.source_file_path: str = "" - self.template_arg_lists: Optional[List[List[Any]]] = None - self.template_params: Optional[List[str]] = None - self.template_signature: Optional[str] = None + self.module_info: Optional["ModuleInfo"] = None # noqa: F821 - self.decls: Optional[List["declaration_t"]] = None # noqa: F821 + self.decls: List["declaration_t"] = [] # noqa: F821 + self.template_arg_lists: List[List[Any]] = [] + self.template_params: List[str] = [] + self.template_signature: str = "" if type_config: - for key, value in type_config.items(): - setattr(self, key, value) + self.name_override = type_config.get("name_override", self.name_override) + self.source_file = type_config.get("source_file", self.source_file) + self.source_file_path = type_config.get( + "source_file_path", self.source_file_path + ) def set_module(self, module_info: "ModuleInfo") -> None: # noqa: F821 """ diff --git a/cppwg/info/method_info.py b/cppwg/info/method_info.py index 9d066fc..aa55a82 100644 --- a/cppwg/info/method_info.py +++ b/cppwg/info/method_info.py @@ -15,7 +15,7 @@ class CppMethodInfo(CppTypeInfo): The class info parent object associated with this method """ - def __init__(self, name: str, _): + def __init__(self, name: str, _) -> None: super().__init__(name) diff --git a/cppwg/info/module_info.py b/cppwg/info/module_info.py index 0b9174a..c77b151 100644 --- a/cppwg/info/module_info.py +++ b/cppwg/info/module_info.py @@ -15,12 +15,6 @@ class ModuleInfo(BaseInfo): Attributes ---------- - class_info_collection : List[CppClassInfo] - A list of class info objects associated with this module - free_function_info_collection : List[CppFreeFunctionInfo] - A list of free function info objects associated with this module - package_info : PackageInfo - The package info parent object associated with this module source_locations : List[str] A list of source locations for this module use_all_classes : bool @@ -29,8 +23,16 @@ class ModuleInfo(BaseInfo): Use all free functions in the module use_all_variables : bool Use all variables in the module - variable_info_collection : List[CppFreeFunctionInfo] - A list of variable info objects associated with this module + + package_info : PackageInfo + The package info object this module belongs to + + class_collection : List[CppClassInfo] + A list of class info objects that belong to this module + free_function_collection : List[CppFreeFunctionInfo] + A list of free function info objects that belong to this module + variable_collection : List[CppFreeFunctionInfo] + A list of variable info objects that belong to this module """ def __init__( @@ -46,28 +48,37 @@ def __init__( module_config : Dict[str, Any] A dictionary of module configuration settings """ - super().__init__(name) - - self.package_info: Optional["PackageInfo"] = None # noqa: F821 - - self.source_locations: List[str] = None - - self.class_info_collection: List[CppClassInfo] = [] - self.free_function_info_collection: List[CppFreeFunctionInfo] = [] - self.variable_info_collection: List["CppVariableInfo"] = [] # noqa: F821 + super().__init__(name, module_config) + self.source_locations: List[str] = [] self.use_all_classes: bool = False self.use_all_free_functions: bool = False self.use_all_variables: bool = False + self.package_info: Optional["PackageInfo"] = None # noqa: F821 + + self.class_collection: List[CppClassInfo] = [] + self.free_function_collection: List[CppFreeFunctionInfo] = [] + self.variable_collection: List["CppVariableInfo"] = [] # noqa: F821 + if module_config: - for key, value in module_config.items(): - setattr(self, key, value) + self.source_locations = module_config.get( + "source_locations", self.source_locations + ) + self.use_all_classes = module_config.get( + "use_all_classes", self.use_all_classes + ) + self.use_all_free_functions = module_config.get( + "use_all_free_functions", self.use_all_free_functions + ) + self.use_all_variables = module_config.get( + "use_all_variables", self.use_all_variables + ) @property def parent(self) -> "PackageInfo": # noqa: F821 """ - Returns the associated package info object. + Returns the associated package info object that this module belongs to. """ return self.package_info @@ -75,21 +86,21 @@ def add_class(self, class_info: CppClassInfo) -> None: """ Add a class info object to the module. """ - self.class_info_collection.append(class_info) + self.class_collection.append(class_info) class_info.set_module(self) def add_free_function(self, free_function_info: CppFreeFunctionInfo) -> None: """ Add a free function info object to the module. """ - self.free_function_info_collection.append(free_function_info) + self.free_function_collection.append(free_function_info) free_function_info.set_module(self) def add_variable(self, variable_info: "CppVariableInfo") -> None: # noqa: F821 """ Add a variable info object to the module. """ - self.variable_info_collection.append(variable_info) + self.variable_collection.append(variable_info) variable_info.set_module(self) def is_decl_in_source_path(self, decl: "declaration_t") -> bool: # noqa: F821 @@ -106,7 +117,7 @@ def is_decl_in_source_path(self, decl: "declaration_t") -> bool: # noqa: F821 bool True if the declaration is associated with a file in a specified source path """ - if self.source_locations is None: + if not self.source_locations: return True for source_location in self.source_locations: @@ -166,19 +177,19 @@ def compare(a: CppClassInfo, b: CppClassInfo) -> int: cache[(b, a)] = 0 return 0 - self.class_info_collection.sort( - key=lambda x: (os.path.dirname(x.source_file_full_path), x.name) + self.class_collection.sort( + key=lambda x: (os.path.dirname(x.source_file_path), x.name) ) i = 0 - n = len(self.class_info_collection) + n = len(self.class_collection) while i < n - 1: - cls_i = self.class_info_collection[i] + cls_i = self.class_collection[i] ii = i # Tracks destination of cls_i j_pos = [] # Tracks positions of cls_i's dependents for j in range(i + 1, n): - cls_j = self.class_info_collection[j] + cls_j = self.class_collection[j] order = compare(cls_i, cls_j) if order == 1: # Position cls_i after all classes it depends on @@ -192,15 +203,15 @@ def compare(a: CppClassInfo, b: CppClassInfo) -> int: continue # No change in position # Move cls_i into new position ii - cls_i = self.class_info_collection.pop(i) - self.class_info_collection.insert(ii, cls_i) + cls_i = self.class_collection.pop(i) + self.class_collection.insert(ii, cls_i) # Move dependents into positions after ii for idx, j in enumerate(j_pos): if j > ii: break # Rest of dependents are already positioned after ii - cls_j = self.class_info_collection.pop(j - 1 - idx) - self.class_info_collection.insert(ii + idx, cls_j) + cls_j = self.class_collection.pop(j - 1 - idx) + self.class_collection.insert(ii + idx, cls_j) def update_from_ns(self, source_ns: "namespace_t") -> None: # noqa: F821 """ @@ -221,10 +232,10 @@ def update_from_ns(self, source_ns: "namespace_t") -> None: # noqa: F821 class_info = CppClassInfo(class_decl.name) class_info.update_names() class_info.module_info = self - self.class_info_collection.append(class_info) + self.class_collection.append(class_info) # Update classes with information from source namespace. - for class_info in self.class_info_collection: + for class_info in self.class_collection: class_info.update_from_ns(source_ns) # Sort classes by dependence @@ -239,10 +250,10 @@ def update_from_ns(self, source_ns: "namespace_t") -> None: # noqa: F821 if self.is_decl_in_source_path(free_function): ff_info = CppFreeFunctionInfo(free_function.name) ff_info.module_info = self - self.free_function_info_collection.append(ff_info) + self.free_function_collection.append(ff_info) # Update free functions with information from source namespace. - for ff_info in self.free_function_info_collection: + for ff_info in self.free_function_collection: ff_info.update_from_ns(source_ns) def update_from_source(self, source_file_paths: List[str]) -> None: @@ -254,8 +265,8 @@ def update_from_source(self, source_file_paths: List[str]) -> None: source_files : List[str] A list of source file paths. """ - for class_info in self.class_info_collection: + for class_info in self.class_collection: class_info.update_from_source(source_file_paths) - self.class_info_collection.sort(key=lambda x: x.name) - self.free_function_info_collection.sort(key=lambda x: x.name) + self.class_collection.sort(key=lambda x: x.name) + self.free_function_collection.sort(key=lambda x: x.name) diff --git a/cppwg/info/package_info.py b/cppwg/info/package_info.py index 58dc22d..b23549a 100644 --- a/cppwg/info/package_info.py +++ b/cppwg/info/package_info.py @@ -16,22 +16,19 @@ class PackageInfo(BaseInfo): Attributes ---------- + common_include_file : bool + Use a common include file for all source files + exclude_default_args : bool + Exclude default arguments from method wrappers. name : str The name of the package - source_locations : List[str] - A list of source locations for this package - module_info_collection : List[ModuleInfo] - A list of module info objects associated with this package - source_root : str - The root directory of the C++ source code source_hpp_patterns : List[str] A list of source file patterns to include + + module_collection : List[ModuleInfo] + A list of module info objects associated with this package source_hpp_files : List[str] A list of source file names to include - common_include_file : bool - Use a common include file for all source files - exclude_default_args : bool - Exclude default arguments from method wrappers. """ def __init__( @@ -47,18 +44,25 @@ def __init__( package_config : Dict[str, Any] A dictionary of package configuration settings """ - super().__init__(name) + super().__init__(name, package_config) - self.source_locations: List[str] = None - self.module_info_collection: List["ModuleInfo"] = [] # noqa: F821 - self.source_hpp_patterns: List[str] = ["*.hpp"] - self.source_hpp_files: List[str] = [] self.common_include_file: bool = False self.exclude_default_args: bool = False + self.source_hpp_patterns: List[str] = ["*.hpp"] + + self.module_collection: List["ModuleInfo"] = [] # noqa: F821 + self.source_hpp_files: List[str] = [] if package_config: - for key, value in package_config.items(): - setattr(self, key, value) + self.common_include_file = package_config.get( + "common_include_file", self.common_include_file + ) + self.exclude_default_args = package_config.get( + "exclude_default_args", self.exclude_default_args + ) + self.source_hpp_patterns = package_config.get( + "source_hpp_patterns", self.source_hpp_patterns + ) @property def parent(self) -> None: @@ -74,7 +78,7 @@ def add_module(self, module_info: "ModuleInfo") -> None: # noqa: F821 module_info : ModuleInfo The module info object to add """ - self.module_info_collection.append(module_info) + self.module_collection.append(module_info) module_info.set_package(self) def init(self, restricted_paths: List[str]) -> None: @@ -132,7 +136,7 @@ def update_from_source(self) -> None: """ Update with data from the source headers. """ - for module_info in self.module_info_collection: + for module_info in self.module_collection: module_info.update_from_source(self.source_hpp_files) def update_from_ns(self, source_ns: "namespace_t") -> None: # noqa: F821 @@ -144,5 +148,5 @@ def update_from_ns(self, source_ns: "namespace_t") -> None: # noqa: F821 source_ns : pygccxml.declarations.namespace_t The source namespace """ - for module_info in self.module_info_collection: + for module_info in self.module_collection: module_info.update_from_ns(source_ns) diff --git a/cppwg/parsers/package_info_parser.py b/cppwg/parsers/package_info_parser.py index c7cf630..38b6dfc 100644 --- a/cppwg/parsers/package_info_parser.py +++ b/cppwg/parsers/package_info_parser.py @@ -50,24 +50,25 @@ def parse(self) -> PackageInfo: with open(self.config_file, "r") as config_file: raw_package_info = yaml.safe_load(config_file) - # Default config options that apply to package, modules, classes, etc. - global_config: Dict[str, Any] = { - "calldef_excludes": None, - "constructor_arg_type_excludes": None, - "constructor_signature_excludes": None, - "custom_generator": None, + # Base config options that apply to package, modules, classes, etc. + base_config: Dict[str, Any] = { + "calldef_excludes": "", + "constructor_arg_type_excludes": "", + "constructor_signature_excludes": "", + "custom_generator": "", "excluded": False, "excluded_methods": [], "excluded_variables": [], - "pointer_call_policy": None, - "reference_call_policy": None, - "return_type_excludes": None, - "smart_ptr_type": None, + "extra_code": [], + "pointer_call_policy": "", + "prefix_code": [], + "prefix_text": "", + "reference_call_policy": "", + "return_type_excludes": "", + "smart_ptr_type": "", "source_includes": [], "source_root": self.source_root, "template_substitutions": [], - "prefix_code": [], - "prefix_text": "", } # Get package config from the raw package info @@ -77,7 +78,7 @@ def parse(self) -> PackageInfo: "exclude_default_args": False, "source_hpp_patterns": ["*.hpp"], } - package_config.update(global_config) + package_config.update(base_config) for key in package_config.keys(): if key in raw_package_info: @@ -95,14 +96,15 @@ def parse(self) -> PackageInfo: # Get module config from the raw module info module_config = { "name": "cppwg_module", - "source_locations": None, + "source_locations": "", + "use_all_classes": False, + "use_all_free_functions": False, + "use_all_variables": False, "classes": [], "free_functions": [], "variables": [], - "use_all_classes": False, - "use_all_free_functions": False, } - module_config.update(global_config) + module_config.update(base_config) for key in module_config.keys(): if key in raw_module_info: @@ -133,8 +135,12 @@ def parse(self) -> PackageInfo: if module_config["classes"]: for raw_class_info in module_config["classes"]: # Get class config from the raw class info - class_config = {"name_override": None, "source_file": None} - class_config.update(global_config) + class_config = { + "name_override": "", + "source_file": "", + "source_file_path": "", + } + class_config.update(base_config) for key in class_config.keys(): if key in raw_class_info: @@ -154,10 +160,11 @@ def parse(self) -> PackageInfo: for raw_free_function_info in module_config["free_functions"]: # Get free function config from the raw free function info free_function_config = { - "name_override": None, - "source_file": None, + "name_override": "", + "source_file": "", + "source_file_path": "", } - free_function_config.update(global_config) + free_function_config.update(base_config) for key in free_function_config.keys(): if key in raw_free_function_info: @@ -173,21 +180,26 @@ def parse(self) -> PackageInfo: # Parse the variable data if not module_config["use_all_variables"]: - for raw_variable_info in module_config["variables"]: - # Get variable config from the raw variable info - variable_config = {"name_override": None, "source_file": None} - variable_config.update(global_config) - - for key in variable_config.keys(): - if key in raw_variable_info: - variable_config[key] = raw_variable_info[key] - - # Create the CppVariableInfo object from the variable config dict - variable_info = CppVariableInfo( - variable_config["name"], variable_config - ) - - # Add the variable to the module - module_info.add_variable(variable_info) + if module_config["variables"]: + for raw_variable_info in module_config["variables"]: + # Get variable config from the raw variable info + variable_config = { + "name_override": "", + "source_file": "", + "source_file_path": "", + } + variable_config.update(base_config) + + for key in variable_config.keys(): + if key in raw_variable_info: + variable_config[key] = raw_variable_info[key] + + # Create the CppVariableInfo object from the variable config dict + variable_info = CppVariableInfo( + variable_config["name"], variable_config + ) + + # Add the variable to the module + module_info.add_variable(variable_info) return package_info diff --git a/cppwg/writers/class_writer.py b/cppwg/writers/class_writer.py index 8ca5310..5782159 100644 --- a/cppwg/writers/class_writer.py +++ b/cppwg/writers/class_writer.py @@ -70,7 +70,9 @@ def add_hpp(self, class_py_name: str) -> None: The Python name of the class e.g. Foo_2_2 """ # Add the top prefix text - self.hpp_string += self.class_info.module_info.package_info.prefix_text + "\n" + prefix_text = self.class_info.hierarchy_attribute("prefix_text") + if prefix_text: + self.hpp_string += prefix_text + "\n" # Add the header guard, includes and declarations class_hpp_dict = {"class_py_name": class_py_name} @@ -91,7 +93,9 @@ def add_cpp_header(self, class_cpp_name: str, class_py_name: str) -> None: The Python name of the class e.g. Foo_2_2 """ # Add the top prefix text - self.cpp_string += self.class_info.module_info.package_info.prefix_text + "\n" + prefix_text = self.class_info.hierarchy_attribute("prefix_text") + if prefix_text: + self.cpp_string += prefix_text + "\n" # Add the includes for this class includes = "" @@ -100,9 +104,13 @@ def add_cpp_header(self, class_cpp_name: str, class_py_name: str) -> None: includes += f'#include "{CPPWG_HEADER_COLLECTION_FILENAME}"\n' else: - source_includes = self.class_info.hierarchy_attribute_gather( - "source_includes" - ) + source_includes = [ + inc + for inc_list in self.class_info.hierarchy_attribute_gather( + "source_includes" + ) + for inc in inc_list + ] for source_include in source_includes: if source_include[0] == "<": @@ -146,9 +154,11 @@ def add_cpp_header(self, class_cpp_name: str, class_py_name: str) -> None: self.cpp_string += code_line + "\n" # Run any custom generators to add additional prefix code - if self.class_info.custom_generator: - self.cpp_string += self.class_info.custom_generator.get_class_cpp_pre_code( - class_py_name + if self.class_info.custom_generator_instance: + self.cpp_string += ( + self.class_info.custom_generator_instance.get_class_cpp_pre_code( + class_py_name + ) ) def add_virtual_overrides( @@ -353,9 +363,9 @@ def write(self, work_dir: str) -> None: self.cpp_string += method_writer.generate_wrapper() # Run any custom generators to add additional class code - if self.class_info.custom_generator: + if self.class_info.custom_generator_instance: self.cpp_string += ( - self.class_info.custom_generator.get_class_cpp_def_code( + self.class_info.custom_generator_instance.get_class_cpp_def_code( class_py_name ) ) diff --git a/cppwg/writers/constructor_writer.py b/cppwg/writers/constructor_writer.py index b2aff6c..02073ba 100644 --- a/cppwg/writers/constructor_writer.py +++ b/cppwg/writers/constructor_writer.py @@ -107,29 +107,39 @@ def exclude(self) -> bool: # Exclude constructors with args matching patterns in calldef_excludes calldef_excludes = [ - x.replace(" ", "") - for x in self.class_info.hierarchy_attribute_gather("calldef_excludes") + ex + for ex_list in self.class_info.hierarchy_attribute_gather( + "calldef_excludes" + ) + for ex in ex_list ] + calldef_excludes = [ex.replace(" ", "") for ex in calldef_excludes] for arg_type in arg_types: if arg_type in calldef_excludes: return True # Exclude constructors with args matching patterns in constructor_arg_type_excludes ctor_arg_type_excludes = [ - x.replace(" ", "") - for x in self.class_info.hierarchy_attribute_gather( + ex + for ex_list in self.class_info.hierarchy_attribute_gather( "constructor_arg_type_excludes" ) + for ex in ex_list ] + ctor_arg_type_excludes = [ex.replace(" ", "") for ex in ctor_arg_type_excludes] for exclude_type in ctor_arg_type_excludes: for arg_type in arg_types: if exclude_type in arg_type: return True # Exclude constructors matching a signature in constructor_signature_excludes - ctor_signature_excludes = self.class_info.hierarchy_attribute_gather( - "constructor_signature_excludes" - ) + ctor_signature_excludes = [ + ex + for ex_list in self.class_info.hierarchy_attribute_gather( + "constructor_signature_excludes" + ) + for ex in ex_list + ] for exclude_types in ctor_signature_excludes: if len(exclude_types) != len(arg_types): diff --git a/cppwg/writers/header_collection_writer.py b/cppwg/writers/header_collection_writer.py index baab7ea..838e0cd 100644 --- a/cppwg/writers/header_collection_writer.py +++ b/cppwg/writers/header_collection_writer.py @@ -49,11 +49,11 @@ def __init__( self.class_dict: Dict[str, CppClassInfo] = {} self.free_func_dict: Dict[str, CppFreeFunctionInfo] = {} - for module_info in self.package_info.module_info_collection: - for class_info in module_info.class_info_collection: + for module_info in self.package_info.module_collection: + for class_info in module_info.class_collection: self.class_dict[class_info.name] = class_info - for free_function_info in module_info.free_function_info_collection: + for free_function_info in module_info.free_function_collection: self.free_func_dict[free_function_info.name] = free_function_info def should_include_all(self) -> bool: @@ -65,7 +65,7 @@ def should_include_all(self) -> bool: bool """ # True if any module uses all classes or all free functions - for module_info in self.package_info.module_info_collection: + for module_info in self.package_info.module_collection: if module_info.use_all_classes or module_info.use_all_free_functions: return True return False @@ -73,7 +73,9 @@ def should_include_all(self) -> bool: def write(self) -> None: """Generate the header file output string and write it to file.""" # Add the top prefix text - self.hpp_collection += self.package_info.prefix_text + "\n" + prefix_text = self.package_info.hierarchy_attribute("prefix_text") + if prefix_text: + self.hpp_collection += prefix_text + "\n" # Add opening header guard self.hpp_collection += f"#ifndef {self.package_info.name}_HEADERS_HPP_\n" @@ -93,8 +95,8 @@ def write(self) -> None: else: # Include specific headers needed by classes - for module_info in self.package_info.module_info_collection: - for class_info in module_info.class_info_collection: + for module_info in self.package_info.module_collection: + for class_info in module_info.class_collection: # Skip excluded classes if class_info.excluded: continue @@ -105,11 +107,9 @@ def write(self) -> None: seen_files.add(filename) # Include specific headers needed by free functions - for free_function_info in module_info.free_function_info_collection: - if free_function_info.source_file_full_path: - filename = os.path.basename( - free_function_info.source_file_full_path - ) + for free_function_info in module_info.free_function_collection: + if free_function_info.source_file_path: + filename = os.path.basename(free_function_info.source_file_path) if filename not in seen_files: self.hpp_collection += f'#include "{filename}"\n' seen_files.add(filename) @@ -119,8 +119,8 @@ def write(self) -> None: template_instantiations = "" template_typedefs = "" - for module_info in self.package_info.module_info_collection: - for class_info in module_info.class_info_collection: + for module_info in self.package_info.module_collection: + for class_info in module_info.class_collection: # Skip excluded classes if class_info.excluded: continue diff --git a/cppwg/writers/module_writer.py b/cppwg/writers/module_writer.py index 2bba96a..0edb345 100644 --- a/cppwg/writers/module_writer.py +++ b/cppwg/writers/module_writer.py @@ -44,7 +44,7 @@ def __init__( # classes to be wrapped in the module self.class_decls: List["class_t"] = [] # noqa: F821 - for class_info in self.module_info.class_info_collection: + for class_info in self.module_info.class_collection: # Skip excluded classes if class_info.excluded: continue @@ -72,7 +72,9 @@ def write_module_wrapper(self) -> None: cpp_string = "" # Add the top prefix text - cpp_string += self.module_info.package_info.prefix_text + "\n" + prefix_text = self.module_info.hierarchy_attribute("prefix_text") + if prefix_text: + cpp_string += prefix_text + "\n" # Add top level includes cpp_string += "#include \n" @@ -81,11 +83,13 @@ def write_module_wrapper(self) -> None: cpp_string += f'#include "{CPPWG_HEADER_COLLECTION_FILENAME}"\n' # Add outputs from running custom generator code - if self.module_info.custom_generator: - cpp_string += self.module_info.custom_generator.get_module_pre_code() + if self.module_info.custom_generator_instance: + cpp_string += ( + self.module_info.custom_generator_instance.get_module_pre_code() + ) # Add includes for class wrappers in the module - for class_info in self.module_info.class_info_collection: + for class_info in self.module_info.class_collection: # Skip excluded classes if class_info.excluded: continue @@ -105,14 +109,14 @@ def write_module_wrapper(self) -> None: cpp_string += "{\n" # Add free functions - for free_function_info in self.module_info.free_function_info_collection: + for free_function_info in self.module_info.free_function_collection: function_writer = CppFreeFunctionWrapperWriter( free_function_info, self.wrapper_templates ) cpp_string += function_writer.generate_wrapper() # Add classes - for class_info in self.module_info.class_info_collection: + for class_info in self.module_info.class_collection: # Skip excluded classes if class_info.excluded: continue @@ -122,8 +126,8 @@ def write_module_wrapper(self) -> None: cpp_string += f" register_{py_name}_class(m);\n" # Add code from the module's custom generator - if self.module_info.custom_generator: - cpp_string += self.module_info.custom_generator.get_module_code() + if self.module_info.custom_generator_instance: + cpp_string += self.module_info.custom_generator_instance.get_module_code() cpp_string += "}\n" # End of the pybind11 module @@ -143,7 +147,7 @@ def write_class_wrappers(self) -> None: """Write wrappers for classes in the module.""" logger = logging.getLogger() - for class_info in self.module_info.class_info_collection: + for class_info in self.module_info.class_collection: # Skip excluded classes if class_info.excluded: logger.info(f"Skipping class {class_info.name}") diff --git a/cppwg/writers/package_writer.py b/cppwg/writers/package_writer.py index b311b43..229a9cc 100644 --- a/cppwg/writers/package_writer.py +++ b/cppwg/writers/package_writer.py @@ -33,7 +33,7 @@ def write(self) -> None: """ Write all the wrappers required for the package. """ - for module_info in self.package_info.module_info_collection: + for module_info in self.package_info.module_collection: module_writer = CppModuleWrapperWriter( module_info, self.wrapper_templates, From d335561f5040c67bcdf781cb57dc334158d36240 Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Fri, 27 Sep 2024 19:20:25 +0000 Subject: [PATCH 5/6] #4 Rename CppTypeInfo CppEntityInfo --- cppwg/info/base_info.py | 26 +++++------ cppwg/info/class_info.py | 46 +++++++++---------- .../{cpp_type_info.py => cpp_entity_info.py} | 28 +++++------ cppwg/info/free_function_info.py | 10 ++-- cppwg/info/method_info.py | 12 +++-- cppwg/info/module_info.py | 4 +- cppwg/info/package_info.py | 2 +- cppwg/info/variable_info.py | 4 +- cppwg/writers/class_writer.py | 20 +++----- 9 files changed, 74 insertions(+), 78 deletions(-) rename cppwg/info/{cpp_type_info.py => cpp_entity_info.py} (58%) diff --git a/cppwg/info/base_info.py b/cppwg/info/base_info.py index 65f14e6..801329a 100644 --- a/cppwg/info/base_info.py +++ b/cppwg/info/base_info.py @@ -15,9 +15,9 @@ class BaseInfo: A generic information structure for features. Features include packages, modules, classes, free functions, etc. - Information structures are used to store information about the features. The - information structures for each feature type inherit from BaseInfo, which - sets a number of default attributes common to all features. + Information structures are used to store information about the features. + BaseInfo is the base information structure, and set up attributes that are + common to all features. Attributes ---------- @@ -183,18 +183,18 @@ def __init__(self, name: str, info_config: Optional[Dict[str, Any]] = None) -> N self.load_custom_generator() @property - def parent(self) -> Optional["BaseInfo"]: + def owner(self) -> Optional["BaseInfo"]: """ - Get this object's parent. + Get this object's owner. - Return the parent object of the feature in the hierarchy. This is - overriden in subclasses e.g. ModuleInfo returns a PackageInfo, ClassInfo - returns a ModuleInfo, etc. + Return the higher level object that holds this object. This is + overriden in subclasses e.g. a ModuleInfo object returns a PackageInfo + object, a ClassInfo object returns a ModuleInfo object, etc. Returns ------- Optional[BaseInfo] - The parent object. + The owning object. """ return None @@ -261,11 +261,11 @@ def hierarchy_attribute(self, attribute_name: str) -> Any: if value: return value - if self.parent is None: + if self.owner is None: # Reached the top of the hierarchy (i.e. PackageInfo) return None - return self.parent.hierarchy_attribute(attribute_name) + return self.owner.hierarchy_attribute(attribute_name) def hierarchy_attribute_gather(self, attribute_name: str) -> List[Any]: """ @@ -290,9 +290,9 @@ def hierarchy_attribute_gather(self, attribute_name: str) -> List[Any]: if value: value_list.append(value) - if self.parent is None: + if self.owner is None: # Reached the top of the hierarchy (i.e. PackageInfo) return value_list - value_list.extend(self.parent.hierarchy_attribute_gather(attribute_name)) + value_list.extend(self.owner.hierarchy_attribute_gather(attribute_name)) return value_list diff --git a/cppwg/info/class_info.py b/cppwg/info/class_info.py index 6cd76cb..97e6f91 100644 --- a/cppwg/info/class_info.py +++ b/cppwg/info/class_info.py @@ -8,18 +8,18 @@ from pygccxml.declarations.matchers import access_type_matcher_t from pygccxml.declarations.runtime_errors import declaration_not_found_t -from cppwg.info.cpp_type_info import CppTypeInfo +from cppwg.info.cpp_entity_info import CppEntityInfo from cppwg.utils import utils -class CppClassInfo(CppTypeInfo): +class CppClassInfo(CppEntityInfo): """ An information structure for individual C++ classes to be wrapped. Attributes ---------- base_decls : pygccxml.declarations.declaration_t - Declarations for this type's base classes, one per template instantiation + Declarations for the base classes, one per template instantiation cpp_names : List[str] The C++ names of the class e.g. ["Foo<2,2>", "Foo<3,3>"] py_names : List[str] @@ -258,12 +258,11 @@ def update_py_names(self) -> None: """ Set the Python names for the class, accounting for template args. - Set the name of the class as it will appear on the Python side. This - collapses template arguments, separating them by underscores and removes - special characters. The return type is a list, as a class can have - multiple names if it is templated. For example, a class "Foo" with - template arguments [[2, 2], [3, 3]] will have a python name list - ["Foo_2_2", "Foo_3_3"]. + Set the name(s) of the class as it should appear in Python. This + collapses template arguments, separates them by underscores, and removes + special characters. There can be multiple names, one for each template + class instantiation. For example, class "Foo" with template arguments + [[2, 2], [3, 3]] will have a Python name list ["Foo_2_2", "Foo_3_3"]. """ # Handles untemplated classes if not self.template_arg_lists: @@ -277,21 +276,21 @@ def update_py_names(self) -> None: rm_chars = {"<": None, ">": None, ",": None, " ": None} rm_table = str.maketrans(rm_chars) - # Clean the type name - type_name = self.name + # Clean the class name + class_name = self.name if self.name_override: - type_name = self.name_override + class_name = self.name_override # Do standard name replacements e.g. "unsigned int" -> "Unsigned" for name, replacement in self.name_replacements.items(): - type_name = type_name.replace(name, replacement) + class_name = class_name.replace(name, replacement) # Remove special characters - type_name = type_name.translate(rm_table) + class_name = class_name.translate(rm_table) # Capitalize the first letter e.g. "foo" -> "Foo" - if len(type_name) > 1: - type_name = type_name[0].capitalize() + type_name[1:] + if len(class_name) > 1: + class_name = class_name[0].capitalize() + class_name[1:] # Create a string of template args separated by "_" e.g. 2_2 for template_arg_list in self.template_arg_lists: @@ -317,17 +316,16 @@ def update_py_names(self) -> None: if idx < len(template_arg_list) - 1: template_string += "_" - self.py_names.append(type_name + "_" + template_string) + self.py_names.append(class_name + "_" + template_string) def update_cpp_names(self) -> None: """ Set the C++ names for the class, accounting for template args. - Set the name of the class as it should appear in C++. - The return type is a list, as a class can have multiple names - if it is templated. For example, a class "Foo" with - template arguments [[2, 2], [3, 3]] will have a C++ name list - ["Foo<2, 2>", "Foo<3, 3>"]. + Set the name(s) of the class as it appears in C++. There can be + multiple names, one for each template class instantiation. + For example, a class "Foo" with template arguments [[2, 2], [3, 3]] + will have a C++ name list ["Foo<2, 2>", "Foo<3, 3>"]. """ # Handles untemplated classes if not self.template_arg_lists: @@ -350,8 +348,8 @@ def update_names(self) -> None: self.update_py_names() @property - def parent(self) -> "ModuleInfo": # noqa: F821 + def owner(self) -> "ModuleInfo": # noqa: F821 """ - Returns the parent module info object. + Returns the module info object that holds this class info object. """ return self.module_info diff --git a/cppwg/info/cpp_type_info.py b/cppwg/info/cpp_entity_info.py similarity index 58% rename from cppwg/info/cpp_type_info.py rename to cppwg/info/cpp_entity_info.py index 9bdbd6f..e5761ce 100644 --- a/cppwg/info/cpp_type_info.py +++ b/cppwg/info/cpp_entity_info.py @@ -1,39 +1,39 @@ -"""C++ type information structure.""" +"""C++ entity information structure.""" from typing import Any, Dict, List, Optional from cppwg.info.base_info import BaseInfo -class CppTypeInfo(BaseInfo): +class CppEntityInfo(BaseInfo): """ - An information structure for C++ types including classes, free functions etc. + An information structure for C++ entities including classes, free functions etc. Attributes ---------- name_override : str The name override specified in config e.g. "CustomFoo" -> "Foo" source_file : str - The source file containing the type + The source file containing the entity source_file_path : str - The full path to the source file containing the type + The full path to the source file containing the entity module_info : ModuleInfo - The module info parent object associated with this type + The module info object that this entity belongs to decls : pygccxml.declarations.declaration_t - The pygccxml declarations associated with this type, one per template arg if templated + The pygccxml declarations associated with this entity, one per template arg if templated template_arg_lists : List[List[Any]] List of template replacement arguments e.g. [[2, 2], [3, 3]] template_params : List[str] List of template parameters e.g. ["DIM_A", "DIM_B"] template_signature : str - The template signature of the type e.g. "" + The template signature of the entity e.g. "" """ - def __init__(self, name: str, type_config: Optional[Dict[str, Any]] = None): + def __init__(self, name: str, entity_config: Optional[Dict[str, Any]] = None): - super().__init__(name, type_config) + super().__init__(name, entity_config) self.name_override: str = "" self.source_file: str = "" @@ -46,10 +46,10 @@ def __init__(self, name: str, type_config: Optional[Dict[str, Any]] = None): self.template_params: List[str] = [] self.template_signature: str = "" - if type_config: - self.name_override = type_config.get("name_override", self.name_override) - self.source_file = type_config.get("source_file", self.source_file) - self.source_file_path = type_config.get( + if entity_config: + self.name_override = entity_config.get("name_override", self.name_override) + self.source_file = entity_config.get("source_file", self.source_file) + self.source_file_path = entity_config.get( "source_file_path", self.source_file_path ) diff --git a/cppwg/info/free_function_info.py b/cppwg/info/free_function_info.py index d4ef490..a7525f1 100644 --- a/cppwg/info/free_function_info.py +++ b/cppwg/info/free_function_info.py @@ -2,10 +2,10 @@ from typing import Any, Dict, Optional -from cppwg.info.cpp_type_info import CppTypeInfo +from cppwg.info.cpp_entity_info import CppEntityInfo -class CppFreeFunctionInfo(CppTypeInfo): +class CppFreeFunctionInfo(CppEntityInfo): """An information structure for individual free functions to be wrapped.""" def __init__( @@ -15,8 +15,10 @@ def __init__( super().__init__(name, free_function_config) @property - def parent(self) -> "ModuleInfo": # noqa: F821 - """Returns the parent module info object.""" + def owner(self) -> "ModuleInfo": # noqa: F821 + """ + Returns the module info object that holds this free function info object. + """ return self.module_info def update_from_ns(self, source_ns: "namespace_t") -> None: # noqa: F821 diff --git a/cppwg/info/method_info.py b/cppwg/info/method_info.py index aa55a82..4ad9d75 100644 --- a/cppwg/info/method_info.py +++ b/cppwg/info/method_info.py @@ -2,17 +2,17 @@ from typing import Optional -from cppwg.info.cpp_type_info import CppTypeInfo +from cppwg.info.cpp_entity_info import CppEntityInfo -class CppMethodInfo(CppTypeInfo): +class CppMethodInfo(CppEntityInfo): """ An information structure for individual methods to be wrapped. Attributes ---------- class_info : CppClassInfo - The class info parent object associated with this method + The class info object associated this method belongs to. """ def __init__(self, name: str, _) -> None: @@ -22,6 +22,8 @@ def __init__(self, name: str, _) -> None: self.class_info: Optional["CppClassInfo"] = None # noqa: F821 @property - def parent(self) -> "CppClassInfo": # noqa: F821 - """Returns the parent class info object.""" + def owner(self) -> "CppClassInfo": # noqa: F821 + """ + Returns the class info object that holds this method info object. + """ return self.class_info diff --git a/cppwg/info/module_info.py b/cppwg/info/module_info.py index c77b151..6caf87c 100644 --- a/cppwg/info/module_info.py +++ b/cppwg/info/module_info.py @@ -76,9 +76,9 @@ def __init__( ) @property - def parent(self) -> "PackageInfo": # noqa: F821 + def owner(self) -> "PackageInfo": # noqa: F821 """ - Returns the associated package info object that this module belongs to. + Returns the package info object that holds this module info object. """ return self.package_info diff --git a/cppwg/info/package_info.py b/cppwg/info/package_info.py index b23549a..0b29e37 100644 --- a/cppwg/info/package_info.py +++ b/cppwg/info/package_info.py @@ -65,7 +65,7 @@ def __init__( ) @property - def parent(self) -> None: + def owner(self) -> None: """Returns None as this is the top level object in the hierarchy.""" return None diff --git a/cppwg/info/variable_info.py b/cppwg/info/variable_info.py index bdaea78..8e58f93 100644 --- a/cppwg/info/variable_info.py +++ b/cppwg/info/variable_info.py @@ -2,10 +2,10 @@ from typing import Any, Dict, Optional -from cppwg.info.cpp_type_info import CppTypeInfo +from cppwg.info.cpp_entity_info import CppEntityInfo -class CppVariableInfo(CppTypeInfo): +class CppVariableInfo(CppEntityInfo): """An information structure for individual variables to be wrapped.""" def __init__(self, name: str, variable_config: Optional[Dict[str, Any]] = None): diff --git a/cppwg/writers/class_writer.py b/cppwg/writers/class_writer.py index 5782159..50889e3 100644 --- a/cppwg/writers/class_writer.py +++ b/cppwg/writers/class_writer.py @@ -154,12 +154,9 @@ def add_cpp_header(self, class_cpp_name: str, class_py_name: str) -> None: self.cpp_string += code_line + "\n" # Run any custom generators to add additional prefix code - if self.class_info.custom_generator_instance: - self.cpp_string += ( - self.class_info.custom_generator_instance.get_class_cpp_pre_code( - class_py_name - ) - ) + generator = self.class_info.custom_generator_instance + if generator: + self.cpp_string += generator.get_class_cpp_pre_code(class_py_name) def add_virtual_overrides( self, template_idx: int @@ -168,7 +165,7 @@ def add_virtual_overrides( Add virtual "trampoline" overrides for the class. Identify any methods needing overrides (i.e. any that are virtual in the - current class or in a parent), and add the overrides to the cpp string. + current class or in a base class), and add the overrides to the cpp string. Parameters ---------- @@ -363,12 +360,9 @@ def write(self, work_dir: str) -> None: self.cpp_string += method_writer.generate_wrapper() # Run any custom generators to add additional class code - if self.class_info.custom_generator_instance: - self.cpp_string += ( - self.class_info.custom_generator_instance.get_class_cpp_def_code( - class_py_name - ) - ) + generator = self.class_info.custom_generator_instance + if generator: + self.cpp_string += generator.get_class_cpp_def_code(class_py_name) # Close the class definition self.cpp_string += " ;\n}\n" From f3c8db8dd46bfed79f150f64c39821a47eabc443 Mon Sep 17 00:00:00 2001 From: Kwabena N Amponsah Date: Sat, 28 Sep 2024 09:30:52 +0000 Subject: [PATCH 6/6] #4 Abstract parent property --- cppwg/info/base_info.py | 114 ++++++++++++------------------- cppwg/info/class_info.py | 13 +--- cppwg/info/cpp_entity_info.py | 20 ++++-- cppwg/info/free_function_info.py | 7 -- cppwg/info/method_info.py | 13 +++- cppwg/info/module_info.py | 45 ++++++------ cppwg/info/package_info.py | 8 ++- 7 files changed, 94 insertions(+), 126 deletions(-) diff --git a/cppwg/info/base_info.py b/cppwg/info/base_info.py index 801329a..dac9197 100644 --- a/cppwg/info/base_info.py +++ b/cppwg/info/base_info.py @@ -4,13 +4,14 @@ import logging import os import sys +from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional import cppwg.templates.custom as cppwg_custom from cppwg.utils.constants import CPPWG_SOURCEROOT_STRING -class BaseInfo: +class BaseInfo(ABC): """ A generic information structure for features. @@ -125,78 +126,47 @@ def __init__(self, name: str, info_config: Optional[Dict[str, Any]] = None) -> N self.custom_generator_instance: cppwg_custom.Custom = None if info_config: - # Paths - self.source_includes = info_config.get( - "source_includes", self.source_includes - ) - self.source_root = info_config.get("source_root", self.source_root) - - # Exclusions - self.arg_type_excludes = info_config.get( - "arg_type_excludes", self.arg_type_excludes - ) - self.calldef_excludes = info_config.get( - "calldef_excludes", self.calldef_excludes - ) - self.constructor_arg_type_excludes = info_config.get( - "constructor_arg_type_excludes", self.constructor_arg_type_excludes - ) - self.constructor_signature_excludes = info_config.get( - "constructor_signature_excludes", self.constructor_signature_excludes - ) - self.excluded = info_config.get("excluded", self.excluded) - self.excluded_methods = info_config.get( - "excluded_methods", self.excluded_methods - ) - self.excluded_variables = info_config.get( - "excluded_variables", self.excluded_variables - ) - self.return_type_excludes = info_config.get( - "return_type_excludes", self.return_type_excludes - ) - - # Pointers - self.pointer_call_policy = info_config.get( - "pointer_call_policy", self.pointer_call_policy - ) - self.reference_call_policy = info_config.get( - "reference_call_policy", self.reference_call_policy - ) - self.smart_ptr_type = info_config.get("smart_ptr_type", self.smart_ptr_type) - - # Substitutions - self.template_substitutions = info_config.get( - "template_substitutions", self.template_substitutions - ) - self.name_replacements = info_config.get( - "name_replacements", self.name_replacements - ) - - # Custom Code - self.extra_code = info_config.get("extra_code", self.extra_code) - self.prefix_code = info_config.get("prefix_code", self.prefix_code) - self.prefix_text = info_config.get("prefix_text", self.prefix_text) - self.custom_generator = info_config.get( - "custom_generator", self.custom_generator - ) + for key in [ + "arg_type_excludes", + "calldef_excludes", + "constructor_arg_type_excludes", + "constructor_signature_excludes", + "custom_generator", + "excluded", + "excluded_methods", + "excluded_variables", + "extra_code", + "name_replacements", + "pointer_call_policy", + "prefix_code", + "prefix_text", + "reference_call_policy", + "return_type_excludes", + "smart_ptr_type", + "source_includes", + "source_root", + "template_substitutions", + ]: + if key in info_config: + setattr(self, key, info_config[key]) self.load_custom_generator() @property - def owner(self) -> Optional["BaseInfo"]: + @abstractmethod + def parent(self) -> Optional["BaseInfo"]: """ - Get this object's owner. + Returns this object's parent node in the info tree hierarchy. - Return the higher level object that holds this object. This is - overriden in subclasses e.g. a ModuleInfo object returns a PackageInfo - object, a ClassInfo object returns a ModuleInfo object, etc. + This property is supplied by subclasses e.g. a ModuleInfo's parent + is a PackageInfo, a ClassInfo's parent is a ModuleInfo etc. Returns ------- Optional[BaseInfo] - The owning object. + The parent node in the info tree hierarchy. """ - return None + pass def load_custom_generator(self) -> None: """ @@ -242,10 +212,10 @@ def load_custom_generator(self) -> None: def hierarchy_attribute(self, attribute_name: str) -> Any: """ - Get the attribute value from this object or the one that owns this. + Get the attribute value from this object or one further up the info tree. - Search higher level objects recursively and return the first - value found for the attribute. + Ascend the info tree hierarchy searching for the attribute and return + the first value found for it. Parameters ---------- @@ -261,18 +231,18 @@ def hierarchy_attribute(self, attribute_name: str) -> Any: if value: return value - if self.owner is None: + if self.parent is None: # Reached the top of the hierarchy (i.e. PackageInfo) return None - return self.owner.hierarchy_attribute(attribute_name) + return self.parent.hierarchy_attribute(attribute_name) def hierarchy_attribute_gather(self, attribute_name: str) -> List[Any]: """ - Get a list of attribute values from this object or the one that owns it. + Get a list of attribute values from this object and others in the info tree. - Search higher level objects recursively, gathering attribute values - into a list wherever the attribute is found. + Ascend the info tree hierarchy searching for the attribute and return + a list of all the values found for it. Parameters ---------- @@ -290,9 +260,9 @@ def hierarchy_attribute_gather(self, attribute_name: str) -> List[Any]: if value: value_list.append(value) - if self.owner is None: + if self.parent is None: # Reached the top of the hierarchy (i.e. PackageInfo) return value_list - value_list.extend(self.owner.hierarchy_attribute_gather(attribute_name)) + value_list.extend(self.parent.hierarchy_attribute_gather(attribute_name)) return value_list diff --git a/cppwg/info/class_info.py b/cppwg/info/class_info.py index 97e6f91..1388ce3 100644 --- a/cppwg/info/class_info.py +++ b/cppwg/info/class_info.py @@ -102,9 +102,9 @@ def extract_templates_from_source(self) -> None: self.template_params.append(param) break - def is_child_of(self, other: "ClassInfo") -> bool: # noqa: F821 + def extends(self, other: "ClassInfo") -> bool: # noqa: F821 """ - Check if the class is a child of the specified class. + Check if the class extends the specified class. Parameters ---------- @@ -114,7 +114,7 @@ def is_child_of(self, other: "ClassInfo") -> bool: # noqa: F821 Returns ------- bool - True if the class is a child of the specified class, False otherwise + True if the class extends the specified class, False otherwise """ if not self.base_decls: return False @@ -346,10 +346,3 @@ def update_names(self) -> None: """ self.update_cpp_names() self.update_py_names() - - @property - def owner(self) -> "ModuleInfo": # noqa: F821 - """ - Returns the module info object that holds this class info object. - """ - return self.module_info diff --git a/cppwg/info/cpp_entity_info.py b/cppwg/info/cpp_entity_info.py index e5761ce..bbc60f0 100644 --- a/cppwg/info/cpp_entity_info.py +++ b/cppwg/info/cpp_entity_info.py @@ -47,14 +47,20 @@ def __init__(self, name: str, entity_config: Optional[Dict[str, Any]] = None): self.template_signature: str = "" if entity_config: - self.name_override = entity_config.get("name_override", self.name_override) - self.source_file = entity_config.get("source_file", self.source_file) - self.source_file_path = entity_config.get( - "source_file_path", self.source_file_path - ) + for key in ["name_override", "source_file", "source_file_path"]: + if key in entity_config: + setattr(self, key, entity_config[key]) - def set_module(self, module_info: "ModuleInfo") -> None: # noqa: F821 + @property + def parent(self) -> "ModuleInfo": # noqa: F821 """ - Set the associated module info object. + Returns the module info object that holds this entity. + """ + return self.module_info + + @parent.setter + def parent(self, module_info: "ModuleInfo") -> None: # noqa: F821 + """ + Set the module info object that holds this entity. """ self.module_info = module_info diff --git a/cppwg/info/free_function_info.py b/cppwg/info/free_function_info.py index a7525f1..3c927a9 100644 --- a/cppwg/info/free_function_info.py +++ b/cppwg/info/free_function_info.py @@ -14,13 +14,6 @@ def __init__( super().__init__(name, free_function_config) - @property - def owner(self) -> "ModuleInfo": # noqa: F821 - """ - Returns the module info object that holds this free function info object. - """ - return self.module_info - def update_from_ns(self, source_ns: "namespace_t") -> None: # noqa: F821 """ Update with information from the source namespace. diff --git a/cppwg/info/method_info.py b/cppwg/info/method_info.py index 4ad9d75..d004903 100644 --- a/cppwg/info/method_info.py +++ b/cppwg/info/method_info.py @@ -12,7 +12,7 @@ class CppMethodInfo(CppEntityInfo): Attributes ---------- class_info : CppClassInfo - The class info object associated this method belongs to. + The class info object that holds this method. """ def __init__(self, name: str, _) -> None: @@ -22,8 +22,15 @@ def __init__(self, name: str, _) -> None: self.class_info: Optional["CppClassInfo"] = None # noqa: F821 @property - def owner(self) -> "CppClassInfo": # noqa: F821 + def parent(self) -> "CppClassInfo": # noqa: F821 """ - Returns the class info object that holds this method info object. + Returns the class info object that holds this method. """ return self.class_info + + @parent.setter + def parent(self, class_info: "CppClassInfo") -> None: # noqa: F821 + """ + Set the class info object that holds this method. + """ + self.class_info = class_info diff --git a/cppwg/info/module_info.py b/cppwg/info/module_info.py index 6caf87c..3060886 100644 --- a/cppwg/info/module_info.py +++ b/cppwg/info/module_info.py @@ -62,46 +62,49 @@ def __init__( self.variable_collection: List["CppVariableInfo"] = [] # noqa: F821 if module_config: - self.source_locations = module_config.get( - "source_locations", self.source_locations - ) - self.use_all_classes = module_config.get( - "use_all_classes", self.use_all_classes - ) - self.use_all_free_functions = module_config.get( - "use_all_free_functions", self.use_all_free_functions - ) - self.use_all_variables = module_config.get( - "use_all_variables", self.use_all_variables - ) + for key in [ + "source_locations", + "use_all_classes", + "use_all_free_functions", + "use_all_variables", + ]: + if key in module_config: + setattr(self, key, module_config[key]) @property - def owner(self) -> "PackageInfo": # noqa: F821 + def parent(self) -> "PackageInfo": # noqa: F821 """ Returns the package info object that holds this module info object. """ return self.package_info + @parent.setter + def parent(self, package_info: "PackageInfo") -> None: # noqa: F821 + """ + Set the package info object that holds this module. + """ + self.package_info = package_info + def add_class(self, class_info: CppClassInfo) -> None: """ Add a class info object to the module. """ self.class_collection.append(class_info) - class_info.set_module(self) + class_info.parent = self def add_free_function(self, free_function_info: CppFreeFunctionInfo) -> None: """ Add a free function info object to the module. """ self.free_function_collection.append(free_function_info) - free_function_info.set_module(self) + free_function_info.parent = self def add_variable(self, variable_info: "CppVariableInfo") -> None: # noqa: F821 """ Add a variable info object to the module. """ self.variable_collection.append(variable_info) - variable_info.set_module(self) + variable_info.parent = self def is_decl_in_source_path(self, decl: "declaration_t") -> bool: # noqa: F821 """ @@ -127,12 +130,6 @@ def is_decl_in_source_path(self, decl: "declaration_t") -> bool: # noqa: F821 return False - def set_package(self, package_info: "PackageInfo") -> None: # noqa: F821 - """ - Set the package info object associated with this module. - """ - self.package_info = package_info - def sort_classes(self) -> None: """ Sort the class info collection in order of dependence. @@ -161,12 +158,12 @@ def compare(a: CppClassInfo, b: CppClassInfo) -> int: a_req_b = a.requires(b) b_req_a = b.requires(a) - if a.is_child_of(b) or (a_req_b and not b_req_a): + if a.extends(b) or (a_req_b and not b_req_a): # a comes after b (ignore cyclic dependencies) cache[(a, b)] = 1 cache[(b, a)] = -1 return 1 - elif b.is_child_of(a) or (b_req_a and not a_req_b): + elif b.extends(a) or (b_req_a and not a_req_b): # a comes before b (ignore cyclic dependencies) cache[(a, b)] = -1 cache[(b, a)] = 1 diff --git a/cppwg/info/package_info.py b/cppwg/info/package_info.py index 0b29e37..0ca426d 100644 --- a/cppwg/info/package_info.py +++ b/cppwg/info/package_info.py @@ -65,8 +65,10 @@ def __init__( ) @property - def owner(self) -> None: - """Returns None as this is the top level object in the hierarchy.""" + def parent(self) -> None: + """ + Returns None, as this is the top level of the info tree hierarchy. + """ return None def add_module(self, module_info: "ModuleInfo") -> None: # noqa: F821 @@ -79,7 +81,7 @@ def add_module(self, module_info: "ModuleInfo") -> None: # noqa: F821 The module info object to add """ self.module_collection.append(module_info) - module_info.set_package(self) + module_info.parent = self def init(self, restricted_paths: List[str]) -> None: """