Make pcpp more optional

This commit is contained in:
Dustin Spicuzza 2023-10-08 00:56:25 -04:00
parent 2a17b27225
commit 9dd573e433
2 changed files with 66 additions and 31 deletions

View File

@ -8,14 +8,16 @@ import os
import typing import typing
from .options import PreprocessorFunction from .options import PreprocessorFunction
from pcpp import Preprocessor, OutputDirective, Action
class PreprocessorError(Exception): class PreprocessorError(Exception):
pass pass
class _CustomPreprocessor(Preprocessor): try:
import pcpp
from pcpp import Preprocessor, OutputDirective, Action
class _CustomPreprocessor(Preprocessor):
def __init__( def __init__(
self, self,
encoding: typing.Optional[str], encoding: typing.Optional[str],
@ -35,6 +37,9 @@ class _CustomPreprocessor(Preprocessor):
def on_comment(self, *ignored): def on_comment(self, *ignored):
return True return True
except ImportError:
pcpp = None
def _filter_self(fname: str, fp: typing.TextIO) -> str: def _filter_self(fname: str, fp: typing.TextIO) -> str:
# the output of pcpp includes the contents of all the included files, which # the output of pcpp includes the contents of all the included files, which
@ -82,6 +87,9 @@ def make_pcpp_preprocessor(
""" """
if pcpp is None:
raise PreprocessorError("pcpp is not installed")
def _preprocess_file(filename: str, content: str) -> str: def _preprocess_file(filename: str, content: str) -> str:
pp = _CustomPreprocessor(encoding, passthru_includes) pp = _CustomPreprocessor(encoding, passthru_includes)
if include_paths: if include_paths:

View File

@ -1,9 +1,13 @@
import os import os
import pathlib import pathlib
import pytest
import re import re
import shutil
import subprocess
import typing
from cxxheaderparser.options import ParserOptions from cxxheaderparser.options import ParserOptions, PreprocessorFunction
from cxxheaderparser.preprocessor import make_pcpp_preprocessor from cxxheaderparser import preprocessor
from cxxheaderparser.simple import ( from cxxheaderparser.simple import (
NamespaceScope, NamespaceScope,
ParsedData, ParsedData,
@ -22,12 +26,26 @@ from cxxheaderparser.types import (
) )
def test_basic_preprocessor() -> None: @pytest.fixture(params=["pcpp"])
def make_pp(request) -> typing.Callable[..., PreprocessorFunction]:
param = request.param
if param == "pcpp":
if preprocessor.pcpp is None:
pytest.skip("pcpp not installed")
return preprocessor.make_pcpp_preprocessor
else:
assert False
def test_basic_preprocessor(
make_pp: typing.Callable[..., PreprocessorFunction]
) -> None:
content = """ content = """
#define X 1 #define X 1
int x = X; int x = X;
""" """
options = ParserOptions(preprocessor=make_pcpp_preprocessor())
options = ParserOptions(preprocessor=make_pp())
data = parse_string(content, cleandoc=True, options=options) data = parse_string(content, cleandoc=True, options=options)
assert data == ParsedData( assert data == ParsedData(
@ -45,7 +63,10 @@ def test_basic_preprocessor() -> None:
) )
def test_preprocessor_omit_content(tmp_path: pathlib.Path) -> None: def test_preprocessor_omit_content(
make_pp: typing.Callable[..., PreprocessorFunction],
tmp_path: pathlib.Path,
) -> None:
"""Ensure that content in other headers is omitted""" """Ensure that content in other headers is omitted"""
h_content = '#include "t2.h"' "\n" "int x = X;\n" h_content = '#include "t2.h"' "\n" "int x = X;\n"
h2_content = "#define X 2\n" "int omitted = 1;\n" h2_content = "#define X 2\n" "int omitted = 1;\n"
@ -56,7 +77,7 @@ def test_preprocessor_omit_content(tmp_path: pathlib.Path) -> None:
with open(tmp_path / "t2.h", "w") as fp: with open(tmp_path / "t2.h", "w") as fp:
fp.write(h2_content) fp.write(h2_content)
options = ParserOptions(preprocessor=make_pcpp_preprocessor()) options = ParserOptions(preprocessor=make_pp())
data = parse_file(tmp_path / "t1.h", options=options) data = parse_file(tmp_path / "t1.h", options=options)
assert data == ParsedData( assert data == ParsedData(
@ -74,7 +95,10 @@ def test_preprocessor_omit_content(tmp_path: pathlib.Path) -> None:
) )
def test_preprocessor_omit_content2(tmp_path: pathlib.Path) -> None: def test_preprocessor_omit_content2(
make_pp: typing.Callable[..., PreprocessorFunction],
tmp_path: pathlib.Path,
) -> None:
""" """
Ensure that content in other headers is omitted while handling pcpp Ensure that content in other headers is omitted while handling pcpp
relative path quirk relative path quirk
@ -91,9 +115,7 @@ def test_preprocessor_omit_content2(tmp_path: pathlib.Path) -> None:
with open(tmp_path2 / "t2.h", "w") as fp: with open(tmp_path2 / "t2.h", "w") as fp:
fp.write(h2_content) fp.write(h2_content)
options = ParserOptions( options = ParserOptions(preprocessor=make_pp(include_paths=[str(tmp_path)]))
preprocessor=make_pcpp_preprocessor(include_paths=[str(tmp_path)])
)
# Weirdness happens here # Weirdness happens here
os.chdir(tmp_path) os.chdir(tmp_path)
@ -114,7 +136,9 @@ def test_preprocessor_omit_content2(tmp_path: pathlib.Path) -> None:
) )
def test_preprocessor_encoding(tmp_path: pathlib.Path) -> None: def test_preprocessor_encoding(
make_pp: typing.Callable[..., PreprocessorFunction], tmp_path: pathlib.Path
) -> None:
"""Ensure we can handle alternate encodings""" """Ensure we can handle alternate encodings"""
h_content = b"// \xa9 2023 someone\n" b'#include "t2.h"' b"\n" b"int x = X;\n" h_content = b"// \xa9 2023 someone\n" b'#include "t2.h"' b"\n" b"int x = X;\n"
@ -126,7 +150,7 @@ def test_preprocessor_encoding(tmp_path: pathlib.Path) -> None:
with open(tmp_path / "t2.h", "wb") as fp: with open(tmp_path / "t2.h", "wb") as fp:
fp.write(h2_content) fp.write(h2_content)
options = ParserOptions(preprocessor=make_pcpp_preprocessor(encoding="cp1252")) options = ParserOptions(preprocessor=make_pp(encoding="cp1252"))
data = parse_file(tmp_path / "t1.h", options=options, encoding="cp1252") data = parse_file(tmp_path / "t1.h", options=options, encoding="cp1252")
assert data == ParsedData( assert data == ParsedData(
@ -144,6 +168,7 @@ def test_preprocessor_encoding(tmp_path: pathlib.Path) -> None:
) )
@pytest.mark.skipif(preprocessor.pcpp is None, reason="pcpp not installed")
def test_preprocessor_passthru_includes(tmp_path: pathlib.Path) -> None: def test_preprocessor_passthru_includes(tmp_path: pathlib.Path) -> None:
"""Ensure that all #include pass through""" """Ensure that all #include pass through"""
h_content = '#include "t2.h"\n' h_content = '#include "t2.h"\n'
@ -155,7 +180,9 @@ def test_preprocessor_passthru_includes(tmp_path: pathlib.Path) -> None:
fp.write("") fp.write("")
options = ParserOptions( options = ParserOptions(
preprocessor=make_pcpp_preprocessor(passthru_includes=re.compile(".+")) preprocessor=preprocessor.make_pcpp_preprocessor(
passthru_includes=re.compile(".+")
)
) )
data = parse_file(tmp_path / "t1.h", options=options) data = parse_file(tmp_path / "t1.h", options=options)