From a73096ab924fac0b964bad24aebe04cac60653e5 Mon Sep 17 00:00:00 2001 From: tcumby Date: Tue, 27 Oct 2026 17:25:09 -0600 Subject: [PATCH 1/3] (AI) add support for Python 3.14 and 3.15, --- .github/workflows/ci.yml | 10 +++++----- pcpp/pcmd.py | 17 ++++++++++++++--- pyproject.toml | 5 ++++- setup.py | 17 ++++++++++++----- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3eadaef..d527863 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-24.04 strategy: matrix: - python-version: ['3.x', 'pypy3.11'] + python-version: ['3.x', '3.15', 'pypy3.11'] steps: - uses: actions/checkout@v5 @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-24.04 strategy: matrix: - python-version: ['3.x', 'pypy3.11'] + python-version: ['3.x', '3.15', 'pypy3.11'] steps: - uses: actions/checkout@v5 @@ -56,7 +56,7 @@ jobs: runs-on: ubuntu-24.04 strategy: matrix: - python-version: ['3.x', 'pypy3.11'] + python-version: ['3.x', '3.15', 'pypy3.11'] steps: - uses: actions/checkout@v5 @@ -79,7 +79,7 @@ jobs: runs-on: ubuntu-24.04 strategy: matrix: - python-version: ['3.x', 'pypy3.11'] + python-version: ['3.x', '3.15', 'pypy3.11'] steps: - uses: actions/checkout@v5 @@ -109,7 +109,7 @@ jobs: runs-on: ubuntu-24.04 strategy: matrix: - python-version: ['3.x', 'pypy3.11'] + python-version: ['3.x', '3.15', 'pypy3.11'] steps: - uses: actions/checkout@v5 diff --git a/pcpp/pcmd.py b/pcpp/pcmd.py index 55da3d2..27441eb 100644 --- a/pcpp/pcmd.py +++ b/pcpp/pcmd.py @@ -12,6 +12,17 @@ __all__ = [] +# argparse.FileType is deprecated from Python 3.14, so this replicates it +def FileType(mode): + def opener(path): + if path == '-': + return sys.stdin if 'r' in mode else sys.stdout + try: + return open(path, mode) + except OSError as e: + raise argparse.ArgumentTypeError("can't open '%s': %s" % (path, e)) + return opener + class FileAction(argparse.Action): def __init__(self, option_strings, dest, **kwargs): super(FileAction, self).__init__(option_strings, dest, **kwargs) @@ -21,7 +32,7 @@ def __call__(self, parser, namespace, values, option_string=None): items = [] else: items = copy.copy(getattr(namespace, self.dest)) - items += [argparse.FileType('rt')(value) for value in values] + items += [FileType('rt')(value) for value in values] setattr(namespace, self.dest, items) class CmdPreprocessor(Preprocessor): @@ -37,7 +48,7 @@ def __init__(self, argv): '''Note that so pcpp can stand in for other preprocessor tooling, it ignores any arguments it does not understand.''') argp.add_argument('inputs', metavar = 'input', default = [sys.stdin], nargs = '*', action = FileAction, help = 'Files to preprocess (use \'-\' for stdin)') - argp.add_argument('-o', dest = 'output', metavar = 'path', type = argparse.FileType('wt'), default=sys.stdout, nargs = '?', help = 'Output to a file instead of stdout') + argp.add_argument('-o', dest = 'output', metavar = 'path', type = FileType('wt'), default=sys.stdout, nargs = '?', help = 'Output to a file instead of stdout') argp.add_argument('-D', dest = 'defines', metavar = 'macro[=val]', nargs = 1, action = 'append', help = 'Predefine name as a macro [with value]') argp.add_argument('-U', dest = 'undefines', metavar = 'macro', nargs = 1, action = 'append', help = 'Pre-undefine name as a macro') argp.add_argument('-N', dest = 'nevers', metavar = 'macro', nargs = 1, action = 'append', help = 'Never define name as a macro, even if defined during the preprocessing.') @@ -55,7 +66,7 @@ def __init__(self, argv): argp.add_argument('--line-directive', dest = 'line_directive', metavar = 'form', default = '#line', nargs = '?', help = "Form of line directive to use, defaults to #line, specify nothing to disable output of line directives") argp.add_argument('--debug', dest = 'debug', action = 'store_true', help = 'Generate a pcpp_debug.log file logging execution') argp.add_argument('--time', dest = 'time', action = 'store_true', help = 'Print the time it took to #include each file') - argp.add_argument('--filetimes', dest = 'filetimes', metavar = 'path', type = argparse.FileType('wt'), default=None, nargs = '?', help = 'Write CSV file with time spent inside each included file, inclusive and exclusive') + argp.add_argument('--filetimes', dest = 'filetimes', metavar = 'path', type = FileType('wt'), default=None, nargs = '?', help = 'Write CSV file with time spent inside each included file, inclusive and exclusive') argp.add_argument('--compress', dest = 'compress', action = 'store_true', help = 'Make output as small as possible') argp.add_argument('--assume-input-encoding', dest = 'assume_input_encoding', metavar = '', default = None, nargs = 1, help = 'The text encoding to assume inputs are in') argp.add_argument('--output-encoding', dest = 'output_encoding', metavar = '', default = None, nargs = 1, help = 'The text encoding to use when writing files') diff --git a/pyproject.toml b/pyproject.toml index a2c6c5d..9059265 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,10 @@ classifiers = [ "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12" + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3.15" ] requires-python = ">=3.6" diff --git a/setup.py b/setup.py index 67cf514..faa4ba9 100644 --- a/setup.py +++ b/setup.py @@ -1,21 +1,25 @@ #!/usr/bin/env python -from setuptools import setup -from setuptools.command.test import test as TestCommand +from setuptools import setup, Command import os import re import sys -class PyTest(TestCommand): +# setuptools.command.test was removed in setuptools 72, so implement the +# test command directly on top of Command +class PyTest(Command): """Custom test command that uses pytest""" user_options = [('pytest-args=', 'a', "Arguments to pass to pytest")] def initialize_options(self): - TestCommand.initialize_options(self) self.pytest_args = [] - def run_tests(self): + def finalize_options(self): + if isinstance(self.pytest_args, str): + self.pytest_args = self.pytest_args.split() + + def run(self): import subprocess import sys @@ -73,6 +77,9 @@ def run_tests(self): 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', + 'Programming Language :: Python :: 3.14', + 'Programming Language :: Python :: 3.15', ], cmdclass={'test': PyTest}, ) From 83a47e9da12c7bc1d7d4c45647c9bd388babcc1f Mon Sep 17 00:00:00 2001 From: tcumby Date: Wed, 28 Oct 2026 09:53:38 -0600 Subject: [PATCH 2/3] (AI) treat the UTF-16 files as binary so the line endings aren't changed on Windows. --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitattributes b/.gitattributes index ee812ad..8e3b03f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -95,3 +95,6 @@ TODO text svneol=native#text/plain *.tpp text svneol=native#text/plain *.jam text svneol=native#text/plain *.java text svneol=native#text/plain + +# UTF-16 test fixtures must not have line endings converted +*_ucs16le.c binary From dc3c3558d1d6223cd72439fac56fe7198b672c59 Mon Sep 17 00:00:00 2001 From: tcumby Date: Wed, 28 Oct 2026 09:54:04 -0600 Subject: [PATCH 3/3] (AI) move build, pytest, wheel, etc. to optional dependencies --- pyproject.toml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 9059265..fab2cef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,15 @@ classifiers = [ ] requires-python = ">=3.6" +[project.optional-dependencies] +dev = [ + "build>=0.9.0", + "pytest>=7.0.1", + "setuptools>=59.6.0", + "twine>=3.8.0", + "wheel>=0.37.1", +] + [project.scripts] pcpp = "pcpp:main"