diff --git a/.flake8 b/.flake8 index 0f89d48..a298ec3 100644 --- a/.flake8 +++ b/.flake8 @@ -6,4 +6,6 @@ exclude= build, doc, examples, + cppwg/templates, + tests, docstring-convention=numpy diff --git a/cppwg/__init__.py b/cppwg/__init__.py index 6fb6f19..986567b 100644 --- a/cppwg/__init__.py +++ b/cppwg/__init__.py @@ -1,5 +1,33 @@ -from .generators import CppWrapperGenerator +""" +cppwg: an automatic Python wrapper generator for C++ code. + +Available subpackages +--------------------- +generators + Contains the main interface for generating Python wrappers. +input + Contains information structures for C++ code to be wrapped. +parsers + Contains parsers for C++ code and input yaml. +templates + Contains string templates for Python wrappers. +utils + Contains utility functions and constants. +writers + Contains writers for creating Python wrappers and writing to file. + +Utilities +--------- +__version__ + cppwg version string +""" + +from importlib import metadata + +from cppwg.generators import CppWrapperGenerator __all__ = [ "CppWrapperGenerator", ] + +__version__ = metadata.version("cppwg") diff --git a/cppwg/generators.py b/cppwg/generators.py index b1ead36..551f521 100644 --- a/cppwg/generators.py +++ b/cppwg/generators.py @@ -1,3 +1,5 @@ +"""Contains the main interface for generating Python wrappers.""" + import fnmatch import logging import os @@ -23,7 +25,7 @@ class CppWrapperGenerator: """ - Main class for generating C++ wrappers + Main class for generating C++ wrappers. Attributes ---------- @@ -149,11 +151,12 @@ def __init__( def collect_source_hpp_files(self) -> None: """ + Collect *.hpp files from the source root. + Walk through the source root and add any files matching the provided patterns e.g. "*.hpp". Skip the wrapper root and wrappers to avoid pollution. """ - for root, _, filenames in os.walk(self.source_root, followlinks=True): for pattern in self.package_info.source_hpp_patterns: for filename in fnmatch.filter(filenames, pattern): @@ -171,10 +174,7 @@ def collect_source_hpp_files(self) -> None: self.package_info.source_hpp_files.append(filepath) def extract_templates_from_source(self) -> None: - """ - Extract template arguments for each class from the associated source file - """ - + """Extract template arguments for each class from the associated source file.""" for module_info in self.package_info.module_info_collection: info_helper = CppInfoHelper(module_info) for class_info in module_info.class_info_collection: @@ -182,8 +182,10 @@ def extract_templates_from_source(self) -> None: def map_classes_to_hpp_files(self) -> None: """ + Map each class to a header file. + Attempt to map source file paths to each class, assuming the containing - file name is the class name + file name is the class name. """ for module_info in self.package_info.module_info_collection: for class_info in module_info.class_info_collection: @@ -196,10 +198,11 @@ def map_classes_to_hpp_files(self) -> None: def parse_header_collection(self) -> None: """ + Parse the hpp files to collect C++ declarations. + Parse the headers with pygccxml and CastXML to populate the source - namespace with C++ declarations collected from the source tree + namespace with C++ declarations collected from the source tree. """ - source_parser = CppSourceParser( self.source_root, self.header_collection_filepath, @@ -209,11 +212,8 @@ def parse_header_collection(self) -> None: ) self.source_ns = source_parser.parse() - def parse_package_info(self): - """ - Parse the package info file to create a PackageInfo object - """ - + def parse_package_info(self) -> None: + """Parse the package info file to create a PackageInfo object.""" if self.package_info_path: # If a package info file exists, parse it to create a PackageInfo object info_parser = PackageInfoParser(self.package_info_path, self.source_root) @@ -225,10 +225,11 @@ def parse_package_info(self): def update_class_info(self) -> None: """ + Add decls to class info objects. + Update the class info with class declarations parsed by pygccxml from the C++ source code. """ - for module_info in self.package_info.module_info_collection: if module_info.use_all_classes: # Create class info objects for all class declarations found @@ -256,10 +257,11 @@ def update_class_info(self) -> None: def update_free_function_info(self) -> None: """ + Add decls to free function info objects. + Update the free function info with declarations parsed by pygccxml from the C++ source code. """ - for module_info in self.package_info.module_info_collection: if module_info.use_all_free_functions: # Create free function info objects for all free function @@ -286,10 +288,7 @@ def update_free_function_info(self) -> None: free_function_info.decl = free_functions[0] def write_header_collection(self) -> None: - """ - Write the header collection to file - """ - + """Write the header collection to file.""" header_collection_writer = CppHeaderCollectionWriter( self.package_info, self.wrapper_root, @@ -298,9 +297,7 @@ def write_header_collection(self) -> None: header_collection_writer.write() def write_wrappers(self) -> None: - """ - Write all the wrappers required for the package - """ + """Write all the wrappers required for the package.""" for module_info in self.package_info.module_info_collection: module_writer = CppModuleWrapperWriter( self.source_ns, @@ -311,10 +308,7 @@ def write_wrappers(self) -> None: module_writer.write() def generate_wrapper(self) -> None: - """ - Main method for generating all the wrappers - """ - + """Parse input yaml and C++ source to generate Python wrappers.""" # Parse the input yaml for package, module, and class information self.parse_package_info() diff --git a/cppwg/input/__init__.py b/cppwg/input/__init__.py index e69de29..7a76cf6 100644 --- a/cppwg/input/__init__.py +++ b/cppwg/input/__init__.py @@ -0,0 +1 @@ +"""Contains information structures for C++ code to be wrapped.""" diff --git a/cppwg/input/base_info.py b/cppwg/input/base_info.py index b2f3515..d5783ce 100644 --- a/cppwg/input/base_info.py +++ b/cppwg/input/base_info.py @@ -1,10 +1,16 @@ +"""Generic information structure.""" + from typing import Any, Dict, List, Optional class BaseInfo: """ - Generic information structure for features (i.e packages, modules, classes, - free functions, etc.) + 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. Attributes ---------- @@ -39,7 +45,8 @@ class BaseInfo: arg_type_excludes : List[str] List of exclude patterns for arg types. name_replacements : Dict[str, str] - A dictionary of name replacements e.g. {"double":"Double", "unsigned int":"Unsigned"} + A dictionary of name replacements e.g. {"double":"Double", "unsigned + int":"Unsigned"} """ def __init__(self, name): @@ -77,6 +84,8 @@ def __init__(self, name): @property def parent(self) -> Optional["BaseInfo"]: """ + Get this object's parent. + 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. @@ -90,6 +99,8 @@ def parent(self) -> Optional["BaseInfo"]: def hierarchy_attribute(self, attribute_name: str) -> Any: """ + Get the attribute value from this object or one of its parents. + 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. @@ -104,7 +115,6 @@ def hierarchy_attribute(self, attribute_name: str) -> Any: Any The attribute value. """ - if hasattr(self, attribute_name) and getattr(self, attribute_name) is not None: return getattr(self, attribute_name) @@ -115,6 +125,8 @@ def hierarchy_attribute(self, attribute_name: str) -> Any: def hierarchy_attribute_gather(self, attribute_name: str) -> List[Any]: """ + Get a list of attribute values from this object and its parents. + For the supplied attribute, iterate through parent objects gathering list entries. Parameters @@ -127,7 +139,6 @@ def hierarchy_attribute_gather(self, attribute_name: str) -> List[Any]: List[Any] The list of attribute values. """ - att_list: List[Any] = [] if hasattr(self, attribute_name) and getattr(self, attribute_name) is not None: diff --git a/cppwg/input/class_info.py b/cppwg/input/class_info.py index 26af236..3ab57af 100644 --- a/cppwg/input/class_info.py +++ b/cppwg/input/class_info.py @@ -1,12 +1,12 @@ +"""Class information structure.""" + from typing import Any, Dict, Optional from cppwg.input.cpp_type_info import CppTypeInfo class CppClassInfo(CppTypeInfo): - """ - This class holds information for individual C++ classes to be wrapped - """ + """An information structure for individual C++ classes to be wrapped.""" def __init__(self, name: str, class_config: Optional[Dict[str, Any]] = None): @@ -14,7 +14,5 @@ def __init__(self, name: str, class_config: Optional[Dict[str, Any]] = None): @property def parent(self) -> "ModuleInfo": # noqa: F821 - """ - Returns the parent module info object - """ + """Returns the parent module info object.""" return self.module_info diff --git a/cppwg/input/cpp_type_info.py b/cppwg/input/cpp_type_info.py index 202c3a1..23d1655 100644 --- a/cppwg/input/cpp_type_info.py +++ b/cppwg/input/cpp_type_info.py @@ -1,3 +1,5 @@ +"""C++ type information structure.""" + from typing import Any, Dict, List, Optional from pygccxml.declarations import declaration_t @@ -7,7 +9,7 @@ class CppTypeInfo(BaseInfo): """ - This class holds information for C++ types including classes, free functions etc. + An information structure for C++ types including classes, free functions etc. Attributes ---------- @@ -43,6 +45,8 @@ def __init__(self, name: str, type_config: Optional[Dict[str, Any]] = None): # TODO: Consider setting short and full names on init as read-only properties def get_short_names(self) -> List[str]: """ + Get the Python name for the class e.g. Foo2_2. + Return 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 @@ -55,7 +59,6 @@ def get_short_names(self) -> List[str]: List[str] The list of short names """ - # Handles untemplated classes if self.template_arg_lists is None: if self.name_override is None: @@ -114,6 +117,8 @@ def get_short_names(self) -> List[str]: def get_full_names(self) -> List[str]: """ + Get the C++ name for the class e.g. Foo<2,2>. + Return the name (declaration) of the class as it appears on the C++ side. The return type is a list, as a class can have multiple names (declarations) if it is templated. For example, a class "Foo" with @@ -125,7 +130,6 @@ def get_full_names(self) -> List[str]: List[str] The list of full names """ - # Handles untemplated classes if self.template_arg_lists is None: return [self.name] @@ -143,10 +147,7 @@ def get_full_names(self) -> List[str]: # TODO: This method is not used, remove it? def needs_header_file_instantiation(self): - """ - Does this class need to be instantiated in the header file - """ - + """Check if this class needs to be instantiated in the header file.""" return ( (self.template_arg_lists is not None) and (not self.include_file_only) @@ -156,16 +157,13 @@ def needs_header_file_instantiation(self): # TODO: This method is not used, remove it? def needs_header_file_typdef(self): """ - Does this type need to be typdef'd with a nicer name in the header - file. All template classes need this. - """ + Check if this type need to be typdef'd with a nicer name. + The typedefs are declared in the header file. All template classes need this. + """ return (self.template_arg_lists is not None) and (not self.include_file_only) # TODO: This method is not used, remove it? def needs_auto_wrapper_generation(self): - """ - Does this class need a wrapper to be autogenerated. - """ - + """Check if this class needs a wrapper to be autogenerated.""" return not self.include_file_only diff --git a/cppwg/input/free_function_info.py b/cppwg/input/free_function_info.py index 5397e21..9879011 100644 --- a/cppwg/input/free_function_info.py +++ b/cppwg/input/free_function_info.py @@ -1,12 +1,12 @@ +"""Free function information structure.""" + from typing import Any, Dict, Optional from cppwg.input.cpp_type_info import CppTypeInfo class CppFreeFunctionInfo(CppTypeInfo): - """ - This class holds information for individual free functions to be wrapped - """ + """An information structure for individual free functions to be wrapped.""" def __init__( self, name: str, free_function_config: Optional[Dict[str, Any]] = None @@ -16,7 +16,5 @@ def __init__( @property def parent(self) -> "ModuleInfo": # noqa: F821 - """ - Returns the parent module info object - """ + """Returns the parent module info object.""" return self.module_info diff --git a/cppwg/input/info_helper.py b/cppwg/input/info_helper.py index d62a0b6..facafb5 100644 --- a/cppwg/input/info_helper.py +++ b/cppwg/input/info_helper.py @@ -1,3 +1,5 @@ +"""Helper utilities for info structures.""" + import logging import os import re @@ -10,9 +12,12 @@ class CppInfoHelper: """ + Adds information extracted from C++ source code to info objects. + Helper class that attempts to automatically fill in extra feature information based on simple analysis of the source tree. + __________ Attributes __________ module_info : ModuleInfo @@ -33,9 +38,9 @@ def __init__(self, module_info: ModuleInfo): def extract_templates_from_source(self, feature_info: BaseInfo) -> None: """ - Extract template arguments for a feature from the associated source - file. + Get template args from the source file associated with an info object. + __________ Parameters __________ feature_info : BaseInfo diff --git a/cppwg/input/method_info.py b/cppwg/input/method_info.py index 12e603e..49e6109 100644 --- a/cppwg/input/method_info.py +++ b/cppwg/input/method_info.py @@ -1,3 +1,5 @@ +"""Method information structure.""" + from typing import Optional from cppwg.input.cpp_type_info import CppTypeInfo @@ -5,7 +7,7 @@ class CppMethodInfo(CppTypeInfo): """ - This class holds information for individual methods to be wrapped + An information structure for individual methods to be wrapped. Attributes ---------- @@ -21,7 +23,5 @@ def __init__(self, name: str, _): @property def parent(self) -> "CppClassInfo": # noqa: F821 - """ - Returns the parent class info object - """ + """Returns the parent class info object.""" return self.class_info diff --git a/cppwg/input/module_info.py b/cppwg/input/module_info.py index 93dc1bd..0479315 100644 --- a/cppwg/input/module_info.py +++ b/cppwg/input/module_info.py @@ -1,3 +1,5 @@ +"""Module information structure.""" + import os from typing import Any, Dict, List, Optional @@ -8,7 +10,7 @@ class ModuleInfo(BaseInfo): """ - This class holds information for individual modules + A structure to hold information for individual modules. Attributes ---------- @@ -49,14 +51,12 @@ def __init__(self, name: str, module_config: Optional[Dict[str, Any]] = None): @property def parent(self) -> "PackageInfo": # noqa: F821 - """ - Returns the parent package info object - """ + """Returns the parent package info object.""" return self.package_info def is_decl_in_source_path(self, decl: declaration_t) -> bool: """ - Check if the declaration is associated with a file in a specified source path + Check if the declaration is associated with a file in the specified source paths. Parameters ---------- @@ -68,7 +68,6 @@ def is_decl_in_source_path(self, decl: declaration_t) -> bool: bool True if the declaration is associated with a file in a specified source path """ - if self.source_locations is None: return True diff --git a/cppwg/input/package_info.py b/cppwg/input/package_info.py index 17e9950..0a6c497 100644 --- a/cppwg/input/package_info.py +++ b/cppwg/input/package_info.py @@ -1,3 +1,5 @@ +"""Package information structure.""" + from typing import Any, Dict, List, Optional from cppwg.input.base_info import BaseInfo @@ -5,7 +7,7 @@ class PackageInfo(BaseInfo): """ - This class holds the package information + A structure to hold information about the package. Attributes ---------- @@ -30,16 +32,22 @@ def __init__( name: str, source_root: str, package_config: Optional[Dict[str, Any]] = None, - ): + ) -> None: """ - Parameters: - ----------- + 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. + + 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(PackageInfo, self).__init__(name) self.name: str = name @@ -56,7 +64,5 @@ def __init__( @property def parent(self) -> None: - """ - Returns None as this is the top level object in the hierarchy - """ + """Returns None as this is the top level object in the hierarchy.""" return None diff --git a/cppwg/input/variable_info.py b/cppwg/input/variable_info.py index 14690f0..3cfd792 100644 --- a/cppwg/input/variable_info.py +++ b/cppwg/input/variable_info.py @@ -1,12 +1,12 @@ +"""Variable information structure.""" + from typing import Any, Dict, Optional from cppwg.input.cpp_type_info import CppTypeInfo class CppVariableInfo(CppTypeInfo): - """ - This class holds information for individual variables to be wrapped - """ + """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/parsers/__init__.py b/cppwg/parsers/__init__.py index e69de29..13685a2 100644 --- a/cppwg/parsers/__init__.py +++ b/cppwg/parsers/__init__.py @@ -0,0 +1 @@ +"""Contains parsers for C++ code and input yaml.""" diff --git a/cppwg/parsers/package_info.py b/cppwg/parsers/package_info.py index 5adc887..06cd6ca 100644 --- a/cppwg/parsers/package_info.py +++ b/cppwg/parsers/package_info.py @@ -1,3 +1,5 @@ +"""Parser for input yaml.""" + import importlib.util import logging import os @@ -18,7 +20,7 @@ class PackageInfoParser: """ - Parse for the package info yaml file + Parser for the package info yaml file. Attributes ---------- @@ -33,15 +35,6 @@ class PackageInfoParser: """ def __init__(self, input_filepath: str, source_root: str): - """ - Parameters - ---------- - input_filepath : str - The path to the package info yaml file. - source_root : str - The root directory of the C++ source code. - """ - self.input_filepath: str = input_filepath self.source_root: str = source_root @@ -104,6 +97,8 @@ def check_for_custom_generators(self, info: BaseInfo) -> None: def parse(self) -> PackageInfo: """ + Parse the yaml file. + Parse the package info yaml file to extract information about the package, modules, classes, and free functions. diff --git a/cppwg/parsers/source_parser.py b/cppwg/parsers/source_parser.py index 1c9cbe9..ff4a73c 100644 --- a/cppwg/parsers/source_parser.py +++ b/cppwg/parsers/source_parser.py @@ -1,3 +1,5 @@ +"""Parser for C++ source code.""" + import logging from pathlib import Path from typing import List, Optional @@ -22,7 +24,7 @@ class CppSourceParser: """ - Parser for C++ source code + Parser for C++ source code. Attributes ---------- @@ -61,7 +63,7 @@ def __init__( def parse(self) -> namespace_t: """ - Parses C++ source code from the header collection using CastXML and pygccxml. + Parse the C++ source code from the header collection using CastXML and pygccxml. Returns ------- diff --git a/cppwg/utils/__init__.py b/cppwg/utils/__init__.py index e69de29..a9a4b96 100644 --- a/cppwg/utils/__init__.py +++ b/cppwg/utils/__init__.py @@ -0,0 +1 @@ +"""Contains utility functions and constants.""" diff --git a/cppwg/utils/constants.py b/cppwg/utils/constants.py index a81f002..8bca1d2 100644 --- a/cppwg/utils/constants.py +++ b/cppwg/utils/constants.py @@ -1,6 +1,4 @@ -""" -Constants for the cppwg package -""" +"""Constants for the cppwg package.""" CPPWG_SOURCEROOT_STRING = "CPPWG_SOURCEROOT" CPPWG_ALL_STRING = "CPPWG_ALL" diff --git a/cppwg/utils/utils.py b/cppwg/utils/utils.py index 7eaef6e..08b8fb3 100644 --- a/cppwg/utils/utils.py +++ b/cppwg/utils/utils.py @@ -1,6 +1,4 @@ -""" -Utility functions for the cppwg package -""" +"""Utility functions for the cppwg package.""" from typing import Any, Dict @@ -13,7 +11,7 @@ def is_option_ALL(input_obj: Any, option_ALL_string: str = CPPWG_ALL_STRING) -> bool: """ - Check if the input is a string that matches the "ALL" indicator e.g. "CPPWG_ALL" + Check if the input is a string that matches the "ALL" indicator e.g. "CPPWG_ALL". Parameters ---------- @@ -32,6 +30,8 @@ def is_option_ALL(input_obj: Any, option_ALL_string: str = CPPWG_ALL_STRING) -> def substitute_bool_for_string(input_dict: Dict[Any, Any], key: Any) -> None: """ + Convert a string in the input dictionary to a boolean. + Substitute a string in the input dictionary with a boolean value if the string is a boolean indicator e.g. "ON", "OFF", "YES", "NO", "TRUE", "FALSE" @@ -42,7 +42,6 @@ def substitute_bool_for_string(input_dict: Dict[Any, Any], key: Any) -> None: key : Any The key to check """ - if not isinstance(input_dict[key], str): return diff --git a/cppwg/writers/__init__.py b/cppwg/writers/__init__.py index e69de29..dc280d8 100644 --- a/cppwg/writers/__init__.py +++ b/cppwg/writers/__init__.py @@ -0,0 +1 @@ +"""Contains writers for creating Python wrappers and writing to file.""" diff --git a/cppwg/writers/base_writer.py b/cppwg/writers/base_writer.py index aba0190..93086cd 100644 --- a/cppwg/writers/base_writer.py +++ b/cppwg/writers/base_writer.py @@ -1,3 +1,5 @@ +"""Base for wrapper code writers.""" + from collections import OrderedDict from typing import Dict, List @@ -6,7 +8,7 @@ class CppBaseWrapperWriter: """ - Base class for wrapper writers + Base class for wrapper writers. Attributes ---------- @@ -16,7 +18,7 @@ class CppBaseWrapperWriter: A dictionary of replacements to use when tidying up C++ declarations """ - def __init__(self, wrapper_templates: Dict[str, str]): + def __init__(self, wrapper_templates: Dict[str, str]) -> None: self.wrapper_templates = wrapper_templates self.tidy_replacements = OrderedDict( @@ -34,8 +36,7 @@ def __init__(self, wrapper_templates: Dict[str, str]): def tidy_name(self, name: str) -> str: """ - This method replaces full C++ declarations with a simple version for use - in typedefs + Replace full C++ declarations with a simple version for use in typedefs. Example: "::foo::bar" -> "_foo_bar_lt_double_2_gt_" @@ -50,7 +51,6 @@ def tidy_name(self, name: str) -> str: str The tidied up C++ declaration """ - for key, value in self.tidy_replacements.items(): name = name.replace(key, value) @@ -63,8 +63,7 @@ def exclusion_criteria( self, decl: free_function_t, exclusion_args: List[str] ) -> bool: """ - Checks if any of the types in the function declaration appear in the - exclusion args. + Check if the function should be excluded from the wrapper code. Parameters ---------- @@ -78,13 +77,12 @@ def exclusion_criteria( bool True if the function should be excluded from the wrapper code """ - - # Are any return types not wrappable + # Check if any return types are not wrappable return_type = decl.return_type.decl_string.replace(" ", "") if return_type in exclusion_args: return True - # Are any arguments not wrappable + # Check if any arguments not wrappable for decl_arg_type in decl.argument_types: arg_type = decl_arg_type.decl_string.split()[0].replace(" ", "") if arg_type in exclusion_args: @@ -95,12 +93,11 @@ def exclusion_criteria( # TODO: This method is currently a placeholder. Consider implementing or removing. def default_arg_exclusion_criteria(self) -> bool: """ - Check if default arguments should be excluded from the wrapper code + Check if default arguments should be excluded from the wrapper code. Returns ------- bool True if the default arguments should be excluded """ - return False diff --git a/cppwg/writers/class_writer.py b/cppwg/writers/class_writer.py index 0d30636..e0eaa9f 100644 --- a/cppwg/writers/class_writer.py +++ b/cppwg/writers/class_writer.py @@ -1,3 +1,5 @@ +"""Wrapper code writer for C++ classes.""" + import logging import os from typing import Dict, List @@ -15,7 +17,7 @@ class CppClassWrapperWriter(CppBaseWrapperWriter): """ - This class generates wrapper code for C++ classes + Writer to generate wrapper code for C++ classes. Attributes ---------- @@ -46,7 +48,7 @@ def __init__( class_info: CppClassInfo, wrapper_templates: Dict[str, str], exposed_class_full_names: List[str], - ): + ) -> None: logger = logging.getLogger() super(CppClassWrapperWriter, self).__init__(wrapper_templates) @@ -74,14 +76,13 @@ def __init__( def add_hpp(self, class_short_name: str) -> None: """ - Fill the class hpp string for a single class using the wrapper template + Fill the class hpp string for a single class using the wrapper template. Parameters ---------- class_short_name: str The short name of the class e.g. Foo2_2 """ - class_hpp_dict = {"class_short_name": class_short_name} self.hpp_string += self.wrapper_templates["class_hpp_header"].format( @@ -90,7 +91,7 @@ def add_hpp(self, class_short_name: str) -> None: def add_cpp_header(self, class_full_name: str, class_short_name: str) -> None: """ - Add the 'top' of the class wrapper cpp file for a single class + Add the 'top' of the class wrapper cpp file for a single class. Parameters ---------- @@ -99,7 +100,6 @@ def add_cpp_header(self, class_full_name: str, class_short_name: str) -> None: class_short_name : str The short name of the class e.g. Foo2_2 """ - # Add the includes for this class includes = "" @@ -160,6 +160,8 @@ def add_virtual_overrides( self, class_decl: class_t, short_class_name: str ) -> List[member_function_t]: """ + 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. @@ -174,7 +176,6 @@ def add_virtual_overrides( ------- list[member_function_t]: A list of member functions needing override """ - methods_needing_override: List[member_function_t] = [] return_types: List[str] = [] # e.g. ["void", "unsigned int", "::Bar<2> *"] @@ -235,7 +236,7 @@ def add_virtual_overrides( def write(self, work_dir: str) -> None: """ - Write the hpp and cpp wrapper codes to file + Write the hpp and cpp wrapper codes to file. Parameters ---------- @@ -383,7 +384,7 @@ def write(self, work_dir: str) -> None: def write_files(self, work_dir: str, class_short_name: str) -> None: """ - Write the hpp and cpp wrapper code to file + Write the hpp and cpp wrapper code to file. Parameters ---------- @@ -392,7 +393,6 @@ def write_files(self, work_dir: str, class_short_name: str) -> None: class_short_name : str The short name of the class e.g. Foo2_2 """ - hpp_filepath = os.path.join(work_dir, f"{class_short_name}.{CPPWG_EXT}.hpp") cpp_filepath = os.path.join(work_dir, f"{class_short_name}.{CPPWG_EXT}.cpp") diff --git a/cppwg/writers/constructor_writer.py b/cppwg/writers/constructor_writer.py index 1888daf..3396f38 100644 --- a/cppwg/writers/constructor_writer.py +++ b/cppwg/writers/constructor_writer.py @@ -1,3 +1,5 @@ +"""Wrapper code writer for C++ class constructors.""" + from typing import Dict, Optional from pygccxml import declarations @@ -10,7 +12,7 @@ class CppConstructorWrapperWriter(CppBaseWrapperWriter): """ - Manage addition of constructor wrapper code + Manage addition of constructor wrapper code. Attributes ---------- @@ -33,7 +35,7 @@ def __init__( class_decl: class_t, wrapper_templates: Dict[str, str], class_short_name: Optional[str] = None, - ): + ) -> None: super(CppConstructorWrapperWriter, self).__init__(wrapper_templates) @@ -47,14 +49,13 @@ def __init__( def exclusion_criteria(self) -> bool: """ - Check if the constructor should be excluded from the wrapper code + Check if the constructor should be excluded from the wrapper code. Returns ------- bool True if the constructor should be excluded, False otherwise """ - # Exclude constructors for classes with private pure virtual methods if any( mf.virtuality == "pure virtual" and mf.access_type == "private" @@ -118,7 +119,9 @@ def exclusion_criteria(self) -> bool: def add_self(self, cpp_string: str) -> str: """ - Add the constructor wrapper code to the input string for example: + Add the constructor wrapper code to the input string. + + Example output: .def(py::init(), py::arg("i") = 1, py::arg("b") = false) Parameters @@ -131,7 +134,6 @@ def add_self(self, cpp_string: str) -> str: str The input string with the constructor wrapper code added """ - # Skip excluded constructors if self.exclusion_criteria(): return cpp_string diff --git a/cppwg/writers/free_function_writer.py b/cppwg/writers/free_function_writer.py index b5f97ab..831b79d 100644 --- a/cppwg/writers/free_function_writer.py +++ b/cppwg/writers/free_function_writer.py @@ -1,3 +1,5 @@ +"""Wrapper code writer for C++ free functions.""" + from typing import Dict, List from cppwg.input.free_function_info import CppFreeFunctionInfo @@ -6,7 +8,7 @@ class CppFreeFunctionWrapperWriter(CppBaseWrapperWriter): """ - Manage addition of free function wrapper code + Manage addition of free function wrapper code. Attributes ---------- @@ -18,7 +20,7 @@ class CppFreeFunctionWrapperWriter(CppBaseWrapperWriter): A list of argument types to exclude from the wrapper code """ - def __init__(self, free_function_info, wrapper_templates): + def __init__(self, free_function_info, wrapper_templates) -> None: super(CppFreeFunctionWrapperWriter, self).__init__(wrapper_templates) @@ -28,7 +30,7 @@ def __init__(self, free_function_info, wrapper_templates): def add_self(self, wrapper_string) -> str: """ - Add the free function wrapper code to the wrapper code string + Add the free function wrapper code to the wrapper code string. Parameters ---------- @@ -40,7 +42,6 @@ def add_self(self, wrapper_string) -> str: str The updated C++ wrapper code string """ - # Skip this free function if it uses any excluded arg types or return types if self.exclusion_criteria(self.free_function_info.decl, self.exclusion_args): return wrapper_string diff --git a/cppwg/writers/header_collection_writer.py b/cppwg/writers/header_collection_writer.py index c795307..61ce3ec 100644 --- a/cppwg/writers/header_collection_writer.py +++ b/cppwg/writers/header_collection_writer.py @@ -1,3 +1,5 @@ +"""Writer for header collection hpp file.""" + import os from typing import Dict @@ -8,11 +10,12 @@ class CppHeaderCollectionWriter: """ - This class manages the generation of the header collection file, which - includes all the headers to be parsed by CastXML. The header collection file - also contains explicit template instantiations and their corresponding - typedefs (e.g. typedef Foo<2,2> Foo2_2) for all - classes that are to be automatically wrapped. + Class to manage the generation of the header collection file. + + The header collection file includes all the headers to be parsed by CastXML. + It also contains explicit template instantiations and their corresponding + typedefs (e.g. typedef Foo<2,2> Foo2_2) for all classes that are to be + automatically wrapped. Attributes ---------- @@ -55,13 +58,12 @@ def __init__( def should_include_all(self) -> bool: """ - Return whether all source files in the module source locations should be included + Return whether all source files in the module source locations should be included. Returns ------- bool """ - # True if any module uses all classes or all free functions for module_info in self.package_info.module_info_collection: if module_info.use_all_classes or module_info.use_all_free_functions: @@ -69,10 +71,7 @@ def should_include_all(self) -> bool: return False def write(self) -> None: - """ - Generate the header file output string and write it to file - """ - + """Generate the header file output string and write it to file.""" # Add opening header guard self.hpp_collection_string = f"#ifndef {self.package_info.name}_HEADERS_HPP_\n" self.hpp_collection_string += f"#define {self.package_info.name}_HEADERS_HPP_\n" diff --git a/cppwg/writers/method_writer.py b/cppwg/writers/method_writer.py index 386b31a..29677de 100644 --- a/cppwg/writers/method_writer.py +++ b/cppwg/writers/method_writer.py @@ -1,3 +1,5 @@ +"""Wrapper code writer for C++ methods.""" + from typing import Dict, Optional from pygccxml import declarations @@ -10,7 +12,7 @@ class CppMethodWrapperWriter(CppBaseWrapperWriter): """ - Manage addition of method wrapper code + Manage addition of method wrapper code. Attributes ---------- @@ -33,7 +35,7 @@ def __init__( class_decl: class_t, wrapper_templates: Dict[str, str], class_short_name: Optional[str] = None, - ): + ) -> None: super(CppMethodWrapperWriter, self).__init__(wrapper_templates) @@ -47,14 +49,13 @@ def __init__( def exclusion_criteria(self) -> bool: """ - Check if the method should be excluded from the wrapper code + Check if the method should be excluded from the wrapper code. Returns ------- bool True if the method should be excluded, False otherwise """ - # Exclude private methods without over-rides if self.method_decl.access_type == "private": return True @@ -97,7 +98,9 @@ def exclusion_criteria(self) -> bool: def add_self(self, cpp_string) -> str: """ - Add the method wrapper code to the input string. For example: + Add the method wrapper code to the input string. + + Example output: .def("bar", (void(Foo::*)(double)) &Foo::bar, " ", py::arg("d") = 1.0) Parameters @@ -110,7 +113,6 @@ def add_self(self, cpp_string) -> str: str The input string with the method wrapper code added """ - # Skip excluded methods if self.exclusion_criteria(): return cpp_string @@ -202,7 +204,6 @@ def add_override(self, cpp_string) -> str: str The input string with the virtual override wrapper code added """ - # Skip private methods if self.method_decl.access_type == "private": return cpp_string diff --git a/cppwg/writers/module_writer.py b/cppwg/writers/module_writer.py index cb3091b..74ded58 100644 --- a/cppwg/writers/module_writer.py +++ b/cppwg/writers/module_writer.py @@ -1,3 +1,5 @@ +"""Wrapper code writer for modules.""" + import logging import os from typing import Dict, List @@ -13,7 +15,12 @@ class CppModuleWrapperWriter: """ - This class automatically generates Python bindings using a rule based approach + Class to automatically generates Python bindings for modules. + + A module is a collection of classes and free functions that are to be + wrapped in Python. The module writer generates the main cpp file for the + module, which contains the pybind11 module definition. Within the module + definition, the module's free functions and classes are registered. Attributes ---------- @@ -57,24 +64,22 @@ def __init__( def write_module_wrapper(self) -> None: """ - Generate the contents of the main cpp file for the module and write it - to modulename.main.cpp. This file contains the pybind11 module - definition. Within the module definition, the module's free functions - and classes are registered. + Generate the contents of the main cpp file for the module. - For example, the generated file might look like this: + The main cpp file is named `modulename.main.cpp`. This file contains the + pybind11 module definition, within which the module's classes and free + functions are registered. + + Example output: ``` - #include - #include "Foo.cppwg.hpp" + #include #include "Foo.cppwg.hpp" - PYBIND11_MODULE(_packagename_modulename, m) - { + PYBIND11_MODULE(_packagename_modulename, m) { register_Foo_class(m); } ``` """ - # Add top level includes cpp_string = "#include \n" @@ -132,9 +137,7 @@ def write_module_wrapper(self) -> None: out_file.write(cpp_string) def write_class_wrappers(self) -> None: - """ - Write wrappers for classes in the module - """ + """Write wrappers for classes in the module.""" logger = logging.getLogger() for class_info in self.module_info.class_info_collection: @@ -157,9 +160,7 @@ def write_class_wrappers(self) -> None: class_writer.write(module_dir) def write(self) -> None: - """ - Main method for writing the module - """ + """Generate the module and class wrappers.""" logger = logging.getLogger() logger.info(f"Generating wrappers for module {self.module_info.name}")