Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
16b0d6d
#102 Emit a JSON model of the wrapped package
kwabenantim Aug 11, 2026
bc8f56c
#102 Add cppwg_initgen.py to generate the Python package layer
kwabenantim Aug 11, 2026
4544bf0
#102 Generate the shapes and cells package layers
kwabenantim Aug 11, 2026
38d2299
#102 Emit the model as YAML instead of JSON
kwabenantim Aug 11, 2026
5ccde67
#102 Add a flatten-to-root option for top-level class access
kwabenantim Aug 11, 2026
9b427c8
#102 Key a nested templated template-argument by its Python class name
kwabenantim Aug 11, 2026
b721f31
#102 Make the template-argument name separator configurable
kwabenantim Aug 12, 2026
9ebac3a
#102 Tidy stale docstrings and comments
kwabenantim Aug 12, 2026
2d40f0d
#102 Rename package-layer artifacts for clarity
kwabenantim Aug 12, 2026
7a55af0
#102 Add a 'cppwg genpackage' subcommand for the package-layer generator
kwabenantim Aug 12, 2026
160818b
#102 Add a do-not-edit banner to the emitted package model
kwabenantim Aug 12, 2026
ad1438c
#102 Document Python package generation
kwabenantim Aug 12, 2026
47e1969
#102 Cover the genpackage subcommand's main()
kwabenantim Aug 12, 2026
70745b5
#102 Address review comments in the package-layer generator
kwabenantim Aug 12, 2026
68dafbb
#102 Fix nested template args for classes with a name_override
kwabenantim Aug 12, 2026
48860fd
#102 Emit the package model as JSON instead of YAML
kwabenantim Aug 12, 2026
1ed9cd3
#102 Warn about orphaned _generated.py files
kwabenantim Aug 12, 2026
f9e80d4
#102 Drop the redundant __main__ guard in genpackage
kwabenantim Aug 12, 2026
8398b2e
#102 Apply the repo coverage config in the shapes/cells flags
kwabenantim Aug 12, 2026
02b2801
#102 Remove the tools/ launcher; generate via the cppwg genpackage su…
kwabenantim Aug 12, 2026
2cdb617
#102 Rename the model's cxx_name field to cpp_name for consistency
kwabenantim Aug 12, 2026
697c60b
#102 Clarify genpackage helper names; document _write_generated
kwabenantim Aug 12, 2026
4ed2b34
#102 Share the free-function exclusion predicate via the info layer
kwabenantim Aug 12, 2026
8694917
#102 Address package-layer generator review comments
kwabenantim Aug 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/test-cells-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,10 @@ jobs:
COVERAGE_FILE: ${{ github.workspace }}/.coverage
run: |
python3 -m coverage combine --rcfile=.cov_sub.rc
python3 -m coverage xml --rcfile=.cov_sub.rc
# Report with the repo's coverage config so [tool.coverage.report]
# exclude_also applies (.cov_sub.rc only sets [run]); otherwise excluded
# lines in cppwg modules this flag never imports are reported as missed.
python3 -m coverage xml --rcfile="$GITHUB_WORKSPACE/pyproject.toml"

- name: Upload coverage to Codecov
if: matrix.python-version == '3.12'
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/test-shapes-pip.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ jobs:

- name: Generate coverage report
if: matrix.python-version == '3.12'
run: python3 -m coverage xml
# Use the repo's coverage config so [tool.coverage.report] exclude_also
# applies here too (this runs from examples/shapes, where it isn't found);
# otherwise excluded lines (e.g. `if __name__`) in cppwg modules this flag
# never imports are wrongly reported as missed, failing codecov's patch gate.
run: python3 -m coverage xml --rcfile="$GITHUB_WORKSPACE/pyproject.toml"
working-directory: examples/shapes

- name: Upload coverage to Codecov
Expand Down
15 changes: 14 additions & 1 deletion cppwg/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import argparse
import logging
import sys
from datetime import datetime
from pathlib import Path

Expand Down Expand Up @@ -54,6 +55,7 @@ def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog="cppwg",
description="Generate Python Wrappers for C++ code",
epilog="Run 'cppwg genpackage --help' for the Python package-layer generator.",
)

parser.add_argument(
Expand Down Expand Up @@ -193,7 +195,18 @@ def generate(args: argparse.Namespace) -> None:


def main() -> None:
"""Generate wrappers from command line arguments."""
"""Generate wrappers from command line arguments.

``cppwg genpackage ...`` is dispatched to the package-layer generator
(:mod:`cppwg.genpackage`); any other invocation runs wrapper generation as
usual. The subcommand is intercepted before argument parsing so the normal
CLI (a leading ``SOURCE_ROOT`` positional) is entirely unaffected.
"""
if len(sys.argv) > 1 and sys.argv[1] == "genpackage":
from cppwg.genpackage import main as genpackage_main

raise SystemExit(genpackage_main(sys.argv[2:]))

args = parse_args()

log_handlers = []
Expand Down
28 changes: 28 additions & 0 deletions cppwg/generators.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""The main interface for generating Python wrappers."""

import json
import logging
import os
import re
Expand All @@ -18,7 +19,10 @@
from cppwg.utils.constants import (
CPPWG_DEFAULT_WRAPPER_DIR,
CPPWG_HEADER_COLLECTION_FILENAME,
CPPWG_PACKAGE_MODEL_FILENAME,
CPPWG_PACKAGE_MODEL_NOTICE,
)
from cppwg.utils.package_model import build_package_model
from cppwg.version import __version__ as cppwg_version
from cppwg.writers.header_collection_writer import CppHeaderCollectionWriter
from cppwg.writers.package_writer import CppPackageWrapperWriter
Expand Down Expand Up @@ -422,6 +426,26 @@ def write_wrappers(self) -> None:
)
package_writer.write()

def write_package_model(self) -> None:
"""
Write the package model (cppwg_package_model.json) to the wrapper root.

A small JSON description of the generated modules and their classes /
instantiations / enums / free functions, so a separate step (the
``cppwg genpackage`` subcommand) can generate the Python package layer
without re-parsing the source. Written last, once the info tree is final.
JSON is used deliberately: this is a machine-generated, git-diff-checked
artifact, so a deterministic serialization matters more than YAML's
readability (the hand-authored configs stay YAML).
"""
model = build_package_model(self.package_info)
# A do-not-edit notice as `_comment` (JSON has no comments); the generator
# ignores unknown top-level keys.
model = {"_comment": CPPWG_PACKAGE_MODEL_NOTICE, **model}
model_path = os.path.join(self.wrapper_root, CPPWG_PACKAGE_MODEL_FILENAME)
content = json.dumps(model, indent=2, sort_keys=True) + "\n"
utils.write_file_if_changed(model_path, content, self.overwrite)

def generate(self) -> None:
"""
Parse yaml configuration and C++ source to generate Python wrappers.
Expand Down Expand Up @@ -482,3 +506,7 @@ def generate(self) -> None:

# Write the wrapper code for the package
self.write_wrappers()

# Write the package model (cppwg_package_model.json) for the package-layer
# generator (cppwg genpackage).
self.write_package_model()
Loading
Loading