diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 247e4ea..0c75f08 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -52,7 +52,8 @@ jobs: cppwg src/ \ --wrapper_root wrapper/ \ --package_info wrapper/package_info.yaml \ - --includes src/*/ + --includes src/*/ \ + --std c++17 - name: Build Python module run: | diff --git a/README.md b/README.md index 8245f62..2e6b242 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,8 @@ cd examples/shapes cppwg src/ \ --wrapper_root wrapper/ \ --package_info wrapper/package_info.yaml \ - --includes src/geometry/ src/math_funcs/ src/primitives/ + --includes src/geometry/ src/math_funcs/ src/primitives/ \ + --std c++17 ``` To build the example package do: diff --git a/cppwg/__main__.py b/cppwg/__main__.py index d9374b9..7271ae0 100644 --- a/cppwg/__main__.py +++ b/cppwg/__main__.py @@ -3,7 +3,7 @@ import argparse import logging -from cppwg import CppWrapperGenerator +from cppwg import CppWrapperGenerator, __version__ def parse_args() -> argparse.Namespace: @@ -44,11 +44,13 @@ def parse_args() -> argparse.Namespace: help="Path to the castxml executable.", ) + # Note: we're passing in std directly because syntax like + # --castxml_cflags "-std=c++17" isn't supported by argparse because of + # the initial "-" in the argument. See https://bugs.python.org/issue9334 parser.add_argument( - "-f", - "--castxml_cflags", + "--std", type=str, - help="Extra cflags for CastXML e.g. '-std=c++17'.", + help="C++ standard e.g. c++17.", ) parser.add_argument( @@ -63,7 +65,15 @@ def parse_args() -> argparse.Namespace: "-q", "--quiet", action="store_true", - help="Disable info messages.", + help="Disable informational messages.", + ) + + parser.add_argument( + "-v", + "--version", + action="version", + version=__version__, + help="Print cppwg version.", ) args = parser.parse_args() @@ -80,13 +90,17 @@ def generate(args: argparse.Namespace) -> None: args : argparse.Namespace The parsed command line arguments. """ + castxml_cflags = None + if args.std: + castxml_cflags = f"-std={args.std}" + generator = CppWrapperGenerator( source_root=args.source_root, source_includes=args.includes, wrapper_root=args.wrapper_root, package_info_path=args.package_info, castxml_binary=args.castxml_binary, - castxml_cflags=args.castxml_cflags, + castxml_cflags=castxml_cflags, ) generator.generate_wrapper() @@ -103,7 +117,7 @@ def main() -> None: logger = logging.getLogger() if args.quiet: - logger.setLevel(logging.WARNING) + logger.setLevel(logging.ERROR) else: logger.setLevel(logging.INFO) diff --git a/cppwg/input/info_helper.py b/cppwg/input/info_helper.py index facafb5..9d1671f 100644 --- a/cppwg/input/info_helper.py +++ b/cppwg/input/info_helper.py @@ -68,7 +68,7 @@ def extract_templates_from_source(self, feature_info: BaseInfo) -> None: raise FileNotFoundError() # Get list of template substitutions from this feature and its parents - # e.g. {"signature":"","replacement":[[2,2], [3,3]]} + # e.g. {"signature":"","replacement":[[2,2], [3,3]]} template_substitutions: List[Dict[str, Any]] = ( feature_info.hierarchy_attribute_gather("template_substitutions") ) @@ -77,8 +77,8 @@ def extract_templates_from_source(self, feature_info: BaseInfo) -> None: if len(template_substitutions) == 0: return - # Remove spaces from template substitution signatures - # e.g. -> + # Remove spaces from template signatures + # e.g. -> for tpl_sub in template_substitutions: tpl_sub["signature"] = tpl_sub["signature"].replace(" ", "") @@ -94,7 +94,7 @@ def extract_templates_from_source(self, feature_info: BaseInfo) -> None: next_line = lines[idx + 1] for template_substitution in template_substitutions: - # e.g. template + # e.g. template signature: str = "template" + template_substitution["signature"] # e.g. [[2,2], [3,3]] @@ -106,34 +106,32 @@ def extract_templates_from_source(self, feature_info: BaseInfo) -> None: declaration_found = False if feature_string == next_line: - # template + # template # classFoo declaration_found = True elif next_line.startswith(feature_string + "{"): - # template + # template # classFoo{ declaration_found = True elif next_line.startswith(feature_string + ":"): - # template - # classFoo:publicBar + # template + # classFoo:publicBar declaration_found = True elif curr_line == signature + feature_string: - # templateclassFoo + # templateclassFoo declaration_found = True elif curr_line.startswith(signature + feature_string + "{"): - # templateclassFoo{ + # templateclassFoo{ declaration_found = True elif curr_line.startswith(signature + feature_string + ":"): - # templateclassFoo:publicBar + # templateclassFoo:publicBar declaration_found = True - # TODO: Add support for more cases, or find a better way e.g. regex or castxml? - if declaration_found: feature_info.template_arg_lists = replacement break diff --git a/cppwg/templates/pybind11_default.py b/cppwg/templates/pybind11_default.py index eaa9a63..b4dfd26 100644 --- a/cppwg/templates/pybind11_default.py +++ b/cppwg/templates/pybind11_default.py @@ -1,42 +1,45 @@ +from cppwg.utils.constants import CPPWG_CLASS_OVERRIDE_SUFFIX, CPPWG_EXT + class_cpp_header = """\ #include #include {includes} -#include "{class_short_name}.cppwg.hpp" +#include "{class_short_name}.%s.hpp" namespace py = pybind11; typedef {class_full_name} {class_short_name}; {smart_ptr_handle}; -""" +""" % CPPWG_EXT class_cpp_header_chaste = """\ #include #include {includes} //#include "PythonObjectConverters.hpp" -#include "{class_short_name}.cppwg.hpp" +#include "{class_short_name}.%s.hpp" namespace py = pybind11; //PYBIND11_CVECTOR_TYPECASTER2(); //PYBIND11_CVECTOR_TYPECASTER3(); typedef {class_full_name} {class_short_name}; {smart_ptr_handle}; -""" +""" % CPPWG_EXT class_hpp_header = """\ -#ifndef {class_short_name}_hpp__pyplusplus_wrapper -#define {class_short_name}_hpp__pyplusplus_wrapper +#ifndef {class_short_name}_hpp__%s_wrapper +#define {class_short_name}_hpp__%s_wrapper -namespace py = pybind11; -void register_{class_short_name}_class(py::module &m); -#endif // {class_short_name}_hpp__pyplusplus_wrapper -""" +#include + +void register_{class_short_name}_class(pybind11::module &m); +#endif // {class_short_name}_hpp__%s_wrapper +""" % tuple([CPPWG_EXT]*3) class_virtual_override_header = """\ -class {class_short_name}_Overloads : public {class_short_name}{{ +class {class_short_name}%s : public {class_short_name}{{ public: using {class_short_name}::{class_base_name}; -""" +""" % CPPWG_CLASS_OVERRIDE_SUFFIX class_virtual_override_footer = "}\n" diff --git a/cppwg/utils/constants.py b/cppwg/utils/constants.py index 686eb40..32799c5 100644 --- a/cppwg/utils/constants.py +++ b/cppwg/utils/constants.py @@ -10,3 +10,5 @@ CPPWG_FALSE_STRINGS = ["OFF", "NO", "N", "FALSE", "F"] CPPWG_DEFAULT_WRAPPER_DIR = "cppwg_wrappers" + +CPPWG_CLASS_OVERRIDE_SUFFIX = "_Overrides" diff --git a/cppwg/writers/base_writer.py b/cppwg/writers/base_writer.py index 93086cd..336d314 100644 --- a/cppwg/writers/base_writer.py +++ b/cppwg/writers/base_writer.py @@ -1,9 +1,7 @@ """Base for wrapper code writers.""" from collections import OrderedDict -from typing import Dict, List - -from pygccxml.declarations import free_function_t +from typing import Dict class CppBaseWrapperWriter: @@ -56,40 +54,6 @@ def tidy_name(self, name: str) -> str: return name - # TODO: Consider moving this implementation of exclusion_criteria to the - # free function writer it is only used there. exclusion_criteria is - # currently overriden in method writer and constructor writer. - def exclusion_criteria( - self, decl: free_function_t, exclusion_args: List[str] - ) -> bool: - """ - Check if the function should be excluded from the wrapper code. - - Parameters - ---------- - decl : free_function_t - The declaration of the function or class - exclusion_args : List[str] - A list of arguments to exclude from the wrapper code - - Returns - ------- - bool - True if the function should be excluded from the wrapper code - """ - # Check if any return types are not wrappable - return_type = decl.return_type.decl_string.replace(" ", "") - if return_type in exclusion_args: - return True - - # 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: - return True - - return False - # TODO: This method is currently a placeholder. Consider implementing or removing. def default_arg_exclusion_criteria(self) -> bool: """ diff --git a/cppwg/writers/class_writer.py b/cppwg/writers/class_writer.py index e0eaa9f..b6241fe 100644 --- a/cppwg/writers/class_writer.py +++ b/cppwg/writers/class_writer.py @@ -9,7 +9,11 @@ from pygccxml.declarations.class_declaration import class_t from cppwg.input.class_info import CppClassInfo -from cppwg.utils.constants import CPPWG_EXT, CPPWG_HEADER_COLLECTION_FILENAME +from cppwg.utils.constants import ( + CPPWG_CLASS_OVERRIDE_SUFFIX, + CPPWG_EXT, + CPPWG_HEADER_COLLECTION_FILENAME, +) from cppwg.writers.base_writer import CppBaseWrapperWriter from cppwg.writers.constructor_writer import CppConstructorWrapperWriter from cppwg.writers.method_writer import CppMethodWrapperWriter @@ -204,7 +208,7 @@ def add_virtual_overrides( # Override virtual methods if methods_needing_override: # Add virtual override class, e.g.: - # class Foo_Overloads : public Foo{{ + # class Foo_Overrides : public Foo { # public: # using Foo::Foo; override_header_dict = { @@ -217,8 +221,13 @@ def add_virtual_overrides( ].format(**override_header_dict) # Override each method, e.g.: - # void bar(int a, bool b) override {{ - # PYBIND11_OVERRIDE(void, Foo, bar, a, b); + # void bar(double d) const override { + # PYBIND11_OVERRIDE_PURE( + # bar, + # Foo2_2, + # bar, + # d); + # } for method in methods_needing_override: method_writer = CppMethodWrapperWriter( self.class_info, @@ -227,8 +236,7 @@ def add_virtual_overrides( self.wrapper_templates, short_class_name, ) - # TODO: Consider returning the override string instead - self.cpp_string = method_writer.add_override(self.cpp_string) + self.cpp_string += method_writer.generate_virtual_override_wrapper() self.cpp_string += "\n};\n" @@ -293,13 +301,12 @@ def write(self, work_dir: str) -> None: self.add_virtual_overrides(class_decl, short_name) ) - # Add the virtual "trampoline" overrides from "Foo_Overloads" to + # Add the virtual "trampoline" overrides from "Foo_Overrides" to # the "Foo" wrapper class definition if needed - # e.g. py::class_(m, "Foo") + # e.g. py::class_(m, "Foo") overrides_string = "" if methods_needing_override: - # TODO: Assign the "_Overloads" literal to a constant - overrides_string = f", {short_name}_Overloads" + overrides_string = f", {short_name}{CPPWG_CLASS_OVERRIDE_SUFFIX}" # Add smart pointer support to the wrapper class definition if needed # e.g. py::class_ >(m, "Foo") @@ -344,8 +351,7 @@ def write(self, work_dir: str) -> None: self.wrapper_templates, short_name, ) - # TODO: Consider returning the constructor string instead - self.cpp_string = constructor_writer.add_self(self.cpp_string) + self.cpp_string += constructor_writer.generate_wrapper() # Add public member functions query = declarations.access_type_matcher_t("public") @@ -364,8 +370,7 @@ def write(self, work_dir: str) -> None: self.wrapper_templates, short_name, ) - # TODO: Consider returning the member string instead - self.cpp_string = method_writer.add_self(self.cpp_string) + self.cpp_string += method_writer.generate_wrapper() # Run any custom generators to add additional class code if self.class_info.custom_generator: diff --git a/cppwg/writers/constructor_writer.py b/cppwg/writers/constructor_writer.py index 3396f38..a0a2a54 100644 --- a/cppwg/writers/constructor_writer.py +++ b/cppwg/writers/constructor_writer.py @@ -117,34 +117,29 @@ def exclusion_criteria(self) -> bool: return False - def add_self(self, cpp_string: str) -> str: + def generate_wrapper(self) -> str: """ - Add the constructor wrapper code to the input string. + Generate the constructor wrapper code. Example output: .def(py::init(), py::arg("i") = 1, py::arg("b") = false) - Parameters - ---------- - cpp_string : str - The input string containing current wrapper code - Returns ------- str - The input string with the constructor wrapper code added + The constructor wrapper code. """ # Skip excluded constructors if self.exclusion_criteria(): - return cpp_string + return "" # Get the arg signature e.g. "int, bool" - cpp_string += " .def(py::init<" + wrapper_string = " .def(py::init<" arg_types = [t.decl_string for t in self.ctor_decl.argument_types] - cpp_string += ", ".join(arg_types) + wrapper_string += ", ".join(arg_types) - cpp_string += " >()" + wrapper_string += " >()" # Default args e.g. py::arg("i") = 1 default_args = "" @@ -156,6 +151,6 @@ def add_self(self, cpp_string: str) -> str: # TODO: Fix in default args (see method_writer) default_args += f" = {arg.default_value}" - cpp_string += default_args + ")\n" + wrapper_string += default_args + ")\n" - return cpp_string + return wrapper_string diff --git a/cppwg/writers/free_function_writer.py b/cppwg/writers/free_function_writer.py index 831b79d..6264404 100644 --- a/cppwg/writers/free_function_writer.py +++ b/cppwg/writers/free_function_writer.py @@ -28,14 +28,9 @@ def __init__(self, free_function_info, wrapper_templates) -> None: self.wrapper_templates: Dict[str, str] = wrapper_templates self.exclusion_args: List[str] = [] - def add_self(self, wrapper_string) -> str: + def generate_wrapper(self) -> str: """ - Add the free function wrapper code to the wrapper code string. - - Parameters - ---------- - wrapper_string : str - String containing the current C++ wrapper code + Generate the free function wrapper code. Returns ------- @@ -43,22 +38,12 @@ def add_self(self, wrapper_string) -> 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 + if self.exclusion_criteria(): + return "" # Pybind11 def type e.g. "_static" for def_static() def_adorn = "" - # TODO: arg_signature isn't used. Remove? - # Get the arg signature - arg_signature = "" - arg_types = self.free_function_info.decl.argument_types - num_arg_types = len(arg_types) - for idx, eachArg in enumerate(arg_types): - arg_signature += eachArg.decl_string - if idx < num_arg_types - 1: - arg_signature += ", " - # Pybind11 arg string with or without default values. # e.g. without default values: ', py::arg("foo"), py::arg("bar")' # e.g. with default values: ', py::arg("foo") = 1, py::arg("bar") = 2' @@ -76,6 +61,30 @@ def add_self(self, wrapper_string) -> str: "function_docs": '" "', "default_args": default_args, } - wrapper_string += self.wrapper_templates["free_function"].format(**func_dict) + wrapper_string = self.wrapper_templates["free_function"].format(**func_dict) return wrapper_string + + def exclusion_criteria(self) -> bool: + """ + Check if the function should be excluded from the wrapper code. + + Returns + ------- + bool + True if the function should be excluded from wrapper code, False otherwise. + """ + # Check if any return types are not wrappable + return_type = self.free_function_info.decl.return_type.decl_string.replace( + " ", "" + ) + if return_type in self.exclusion_args: + return True + + # Check if any arguments not wrappable + for decl_arg_type in self.free_function_info.decl.argument_types: + arg_type = decl_arg_type.decl_string.split()[0].replace(" ", "") + if arg_type in self.exclusion_args: + return True + + return False diff --git a/cppwg/writers/method_writer.py b/cppwg/writers/method_writer.py index 29677de..d61e189 100644 --- a/cppwg/writers/method_writer.py +++ b/cppwg/writers/method_writer.py @@ -96,26 +96,21 @@ def exclusion_criteria(self) -> bool: return False - def add_self(self, cpp_string) -> str: + def generate_wrapper(self) -> str: """ - Add the method wrapper code to the input string. + Generate the method wrapper code. Example output: .def("bar", (void(Foo::*)(double)) &Foo::bar, " ", py::arg("d") = 1.0) - Parameters - ---------- - cpp_string : str - The input string containing current wrapper code - Returns ------- str - The input string with the method wrapper code added + The method wrapper code. """ # Skip excluded methods if self.exclusion_criteria(): - return cpp_string + return "" # Pybind11 def type e.g. "_static" for def_static() def_adorn = "" @@ -186,27 +181,33 @@ def add_self(self, cpp_string) -> str: "call_policy": call_policy, } class_method_template = self.wrapper_templates["class_method"] - cpp_string += class_method_template.format(**method_dict) + wrapper_string = class_method_template.format(**method_dict) - return cpp_string + return wrapper_string - def add_override(self, cpp_string) -> str: + def generate_virtual_override_wrapper(self) -> str: """ - Add overrides for virtual methods to the input string. + Generate wrapper code for overriding virtual methods. - Parameters - ---------- - cpp_string : str - The input string containing current wrapper code + Example output: + ``` + void bar(double d) const override { + PYBIND11_OVERRIDE_PURE( + bar, + Foo2_2, + bar, + d); + } + ``` Returns ------- str - The input string with the virtual override wrapper code added + The virtual override wrapper code. """ # Skip private methods if self.method_decl.access_type == "private": - return cpp_string + return "" # Get list of arguments and types arg_list = [] @@ -245,8 +246,8 @@ def add_override(self, cpp_string) -> str: "short_class_name": self.class_short_name, "args_string": arg_name_string, } - cpp_string += self.wrapper_templates["method_virtual_override"].format( + wrapper_string = self.wrapper_templates["method_virtual_override"].format( **override_dict ) - return cpp_string + return wrapper_string diff --git a/cppwg/writers/module_writer.py b/cppwg/writers/module_writer.py index 74ded58..71225f3 100644 --- a/cppwg/writers/module_writer.py +++ b/cppwg/writers/module_writer.py @@ -111,8 +111,7 @@ def write_module_wrapper(self) -> None: function_writer = CppFreeFunctionWrapperWriter( free_function_info, self.wrapper_templates ) - # TODO: Consider returning the function string instead - cpp_string = function_writer.add_self(cpp_string) + cpp_string += function_writer.generate_wrapper() # Add classes for class_info in self.module_info.class_info_collection: diff --git a/examples/shapes/wrapper/geometry/Point2.cppwg.hpp b/examples/shapes/wrapper/geometry/Point2.cppwg.hpp index 554af60..0df78b5 100644 --- a/examples/shapes/wrapper/geometry/Point2.cppwg.hpp +++ b/examples/shapes/wrapper/geometry/Point2.cppwg.hpp @@ -1,6 +1,7 @@ -#ifndef Point2_hpp__pyplusplus_wrapper -#define Point2_hpp__pyplusplus_wrapper +#ifndef Point2_hpp__cppwg_wrapper +#define Point2_hpp__cppwg_wrapper -namespace py = pybind11; -void register_Point2_class(py::module &m); -#endif // Point2_hpp__pyplusplus_wrapper +#include + +void register_Point2_class(pybind11::module &m); +#endif // Point2_hpp__cppwg_wrapper diff --git a/examples/shapes/wrapper/geometry/Point3.cppwg.hpp b/examples/shapes/wrapper/geometry/Point3.cppwg.hpp index 7739da8..769cfc5 100644 --- a/examples/shapes/wrapper/geometry/Point3.cppwg.hpp +++ b/examples/shapes/wrapper/geometry/Point3.cppwg.hpp @@ -1,6 +1,7 @@ -#ifndef Point3_hpp__pyplusplus_wrapper -#define Point3_hpp__pyplusplus_wrapper +#ifndef Point3_hpp__cppwg_wrapper +#define Point3_hpp__cppwg_wrapper -namespace py = pybind11; -void register_Point3_class(py::module &m); -#endif // Point3_hpp__pyplusplus_wrapper +#include + +void register_Point3_class(pybind11::module &m); +#endif // Point3_hpp__cppwg_wrapper diff --git a/examples/shapes/wrapper/primitives/Cuboid.cppwg.hpp b/examples/shapes/wrapper/primitives/Cuboid.cppwg.hpp index e41a4f6..48c919f 100644 --- a/examples/shapes/wrapper/primitives/Cuboid.cppwg.hpp +++ b/examples/shapes/wrapper/primitives/Cuboid.cppwg.hpp @@ -1,6 +1,7 @@ -#ifndef Cuboid_hpp__pyplusplus_wrapper -#define Cuboid_hpp__pyplusplus_wrapper +#ifndef Cuboid_hpp__cppwg_wrapper +#define Cuboid_hpp__cppwg_wrapper -namespace py = pybind11; -void register_Cuboid_class(py::module &m); -#endif // Cuboid_hpp__pyplusplus_wrapper +#include + +void register_Cuboid_class(pybind11::module &m); +#endif // Cuboid_hpp__cppwg_wrapper diff --git a/examples/shapes/wrapper/primitives/Rectangle.cppwg.hpp b/examples/shapes/wrapper/primitives/Rectangle.cppwg.hpp index f2e490e..e063518 100644 --- a/examples/shapes/wrapper/primitives/Rectangle.cppwg.hpp +++ b/examples/shapes/wrapper/primitives/Rectangle.cppwg.hpp @@ -1,6 +1,7 @@ -#ifndef Rectangle_hpp__pyplusplus_wrapper -#define Rectangle_hpp__pyplusplus_wrapper +#ifndef Rectangle_hpp__cppwg_wrapper +#define Rectangle_hpp__cppwg_wrapper -namespace py = pybind11; -void register_Rectangle_class(py::module &m); -#endif // Rectangle_hpp__pyplusplus_wrapper +#include + +void register_Rectangle_class(pybind11::module &m); +#endif // Rectangle_hpp__cppwg_wrapper diff --git a/examples/shapes/wrapper/primitives/Shape2.cppwg.hpp b/examples/shapes/wrapper/primitives/Shape2.cppwg.hpp index c71941d..436f7ac 100644 --- a/examples/shapes/wrapper/primitives/Shape2.cppwg.hpp +++ b/examples/shapes/wrapper/primitives/Shape2.cppwg.hpp @@ -1,6 +1,7 @@ -#ifndef Shape2_hpp__pyplusplus_wrapper -#define Shape2_hpp__pyplusplus_wrapper +#ifndef Shape2_hpp__cppwg_wrapper +#define Shape2_hpp__cppwg_wrapper -namespace py = pybind11; -void register_Shape2_class(py::module &m); -#endif // Shape2_hpp__pyplusplus_wrapper +#include + +void register_Shape2_class(pybind11::module &m); +#endif // Shape2_hpp__cppwg_wrapper diff --git a/examples/shapes/wrapper/primitives/Shape3.cppwg.hpp b/examples/shapes/wrapper/primitives/Shape3.cppwg.hpp index f3f5643..eda2e80 100644 --- a/examples/shapes/wrapper/primitives/Shape3.cppwg.hpp +++ b/examples/shapes/wrapper/primitives/Shape3.cppwg.hpp @@ -1,6 +1,7 @@ -#ifndef Shape3_hpp__pyplusplus_wrapper -#define Shape3_hpp__pyplusplus_wrapper +#ifndef Shape3_hpp__cppwg_wrapper +#define Shape3_hpp__cppwg_wrapper -namespace py = pybind11; -void register_Shape3_class(py::module &m); -#endif // Shape3_hpp__pyplusplus_wrapper +#include + +void register_Shape3_class(pybind11::module &m); +#endif // Shape3_hpp__cppwg_wrapper diff --git a/pyproject.toml b/pyproject.toml index 4ca84ea..6856659 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Software Development", ] @@ -45,7 +46,12 @@ Repository = "https://github.com/Chaste/cppwg/" [tool.black] target-version = ["py38", "py39", "py310", "py311", "py312"] -extend-exclude = "^/examples/shapes/wrapper/pybind11/" +extend-exclude = """ +( + ^/examples/shapes/wrapper/pybind11/ + | ^/cppwg/templates/ +) +""" [tool.isort] profile = "black"