Repository: cpplint/cpplint
Pull request #315: PEP 621: Migrate from setup.{py, cfg} to pyproject.toml
Author: login: cclauss
Created: 2025-03-02T07:56:19Z | Merged: 2025-03-07T05:32:20Z

https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html
* https://peps.python.org/pep-0621

Migrate `setup.py` to `setup.cfg` using [setuptools-py2cfg](https://pypi.org/project/setuptools-py2cfg) plus manual modifications.  Then migrate `setup.cfg` to `pyproject.toml` using [ini2toml](https://pypi.org/project/ini2toml) to do the file conversion and running [pyproject-fmt](https://pypi.org/project/pyproject-fmt) and then [validate-pyproject](https://github.com/abravalheri/validate-pyproject) to validate the results.

```yaml
    strategy:
      matrix:
        fail-fast: false
        # Python 3.8 is EOL. Also adapt tox.ini
        python-version: ['3.8', 'pypy3.11', '3.13']
        # macOS on ARM, Ubuntu on x86, Windows on X86
        os: [macos-latest, ubuntu-latest, windows-latest]
```


Diff:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index bf522c6..32fc77b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,11 +18,12 @@ jobs:
   build-test:
     runs-on: ${{ matrix.os }}
     strategy:
-      matrix:
-        # Python 3.8 is the last non-EOL version. Also adapt tox.ini
-        python-version: ['3.8', 'pypy3.10', '3.12']
-        os: [ubuntu-latest, windows-latest]
       fail-fast: false
+      matrix:
+        # Python 3.8 is EOL. Also adapt tox.ini
+        python-version: ['3.8', 'pypy3.11', '3.x']
+        # macOS on ARM, Ubuntu on x86, Windows on X86
+        os: [macos-latest, ubuntu-latest, windows-latest]
     steps:
       - name: Checkout
         uses: actions/checkout@v4
@@ -32,13 +33,11 @@ jobs:
         uses: actions/setup-python@v5
         with:
           python-version: ${{ matrix.python-version }}
-          architecture: x64
           cache: 'pip'
-          cache-dependency-path: '**/test-requirements'
+          cache-dependency-path: 'pyproject.toml'
       - name: Install dependencies
         run: |
           python -m pip install --upgrade pip
-          python -m pip install -e .[dev]
-          python -m pip install 'tox-gh-actions<4.0.0'
+          python -m pip install --editable .[dev]
       - name: Test with tox
-        run: tox
+        run: tox -e py
diff --git a/cpplint.py b/cpplint.py
index 7a8b8ab..0e658d2 100755
--- a/cpplint.py
+++ b/cpplint.py
@@ -44,7 +44,7 @@
 import codecs
 import collections
 import copy
-import getopt
+import getopt  # pylint: disable=deprecated-module
 import glob
 import itertools
 import math  # for log
diff --git a/dev-requirements b/dev-requirements
deleted file mode 100644
index 62a36ea..0000000
--- a/dev-requirements
+++ /dev/null
@@ -1,5 +0,0 @@
-# requirements to run development steps
-
-flake8>=4.0.1
-pylint>=2.11.0
-setuptools
diff --git a/pyproject.toml b/pyproject.toml
index e4bb940..91f944a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,77 @@
 [build-system]
-build-backend = "setuptools.build_meta:__legacy__"
-requires = [
+build-backend = "setuptools.build_meta"
+requires = [ "setuptools>=61.2" ]
+
+[project]
+name = "cpplint"
+version = "2.0.0"
+description = "Automated checker to ensure C++ files follow Google's style guide"
+readme = "README.rst"
+keywords = [ "c++", "lint", "python" ]
+license = { text = "BSD-3-Clause" }
+maintainers = [ { name = "cpplint Developers" } ]
+requires-python = ">=3.8"
+classifiers = [
+  "Development Status :: 5 - Production/Stable",
+  "Environment :: Console",
+  "Intended Audience :: End Users/Desktop",
+  "License :: Freely Distributable",
+  "License :: OSI Approved :: BSD License",
+  "Natural Language :: English",
+  "Programming Language :: C++",
+  "Programming Language :: Python :: 3 :: Only",
+  "Programming Language :: Python :: 3.8",
+  "Programming Language :: Python :: 3.9",
+  "Programming Language :: Python :: 3.10",
+  "Programming Language :: Python :: 3.11",
+  "Programming Language :: Python :: 3.12",
+  "Programming Language :: Python :: 3.13",
+  "Topic :: Software Development :: Quality Assurance",
+]
+dependencies = [  ]
+
+optional-dependencies.dev = [
+  "flake8>=4.0.1",
+  "parameterized",
+  "pylint>=2.11",
+  "pytest",
+  "pytest-cov",
+  "pytest-timeout",
   "setuptools",
-  "wheel",
+  "testfixtures",
+  "tox<5",
+]
+optional-dependencies.test = [
+  "parameterized",
+  "pytest",
+  "pytest-cov",
+  "pytest-timeout",
+  "testfixtures",
+  "tox<5",
+]
+optional-dependencies.testing = [
+  "parameterized",
+  "pytest",
+  "pytest-cov",
+  "pytest-timeout",
+  "testfixtures",
+  "tox<5",
 ]
+urls.Download = "https://github.com/cpplint/cpplint"
+urls.Homepage = "https://github.com/cpplint/cpplint"
+scripts.cpplint = "cpplint:main"
+
+[tool.setuptools]
+py-modules = [ "cpplint" ]
+include-package-data = false
+
+[tool.pytest.ini_options]
+python_files = [ "*test.py" ]
+testpaths = [ "." ]
+required_plugins = [ "pytest-cov", "pytest-timeout" ]
+timeout = 60
+# fail if coverage is under 90%
+addopts = "--color=yes --cov-fail-under=90 --cov=cpplint"
+
+[tool.aliases]
+test = "pytest"
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index eebbf0a..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,10 +0,0 @@
-[aliases]
-test = pytest
-
-[tool:pytest]
-python_files = *test.py
-testpaths = .
-required_plugins = pytest-cov pytest-timeout
-timeout = 60
-# fail if coverage is under 90%
-addopts = --color=yes --cov-fail-under=90 --cov=cpplint
diff --git a/setup.py b/setup.py
deleted file mode 100755
index 2325a71..0000000
--- a/setup.py
+++ /dev/null
@@ -1,48 +0,0 @@
-#! /usr/bin/env python
-from setuptools import setup
-import cpplint
-
-# some pip versions bark on comments
-def read_without_comments(filename):
-    with open(filename) as f:
-        return [line for line in f.read().splitlines() if not len(line) == 0 and not line.startswith('#')]
-
-test_required = read_without_comments('test-requirements')
-
-setup(name='cpplint',
-      version=cpplint.__VERSION__,
-      py_modules=['cpplint'],
-      # generate platform specific start script
-      entry_points={
-          'console_scripts': [
-              'cpplint = cpplint:main'
-          ]
-      },
-      install_requires=[],
-      url='https://github.com/cpplint/cpplint',
-      download_url='https://github.com/cpplint/cpplint',
-      keywords=['lint', 'python', 'c++'],
-      maintainer='cpplint Developers',
-      classifiers=['Development Status :: 5 - Production/Stable',
-                   'Environment :: Console',
-                   'Intended Audience :: End Users/Desktop',
-                   'License :: Freely Distributable',
-                   'License :: OSI Approved :: BSD License',
-                   'Natural Language :: English',
-                   'Programming Language :: Python :: 3 :: Only',
-                   'Programming Language :: Python :: 3.8',
-                   'Programming Language :: Python :: 3.9',
-                   'Programming Language :: Python :: 3.10',
-                   'Programming Language :: Python :: 3.11',
-                   'Programming Language :: Python :: 3.12',
-                   'Programming Language :: C++',
-                   'Topic :: Software Development :: Quality Assurance'],
-      description='Automated checker to ensure C++ files follow Google\'s style guide',
-      long_description=open('README.rst').read(),
-      license='BSD-3-Clause',
-      tests_require=test_required,
-      # extras_require allow pip install .[dev]
-      extras_require={
-          'test': test_required,
-          'dev': read_without_comments('dev-requirements') + test_required
-      })
diff --git a/test-requirements b/test-requirements
deleted file mode 100644
index 9c9abb5..0000000
--- a/test-requirements
+++ /dev/null
@@ -1,8 +0,0 @@
-# requirements for testing steps
-tox<5.0.0
-
-pytest
-pytest-cov
-pytest-timeout
-testfixtures
-parameterized
diff --git a/tox.ini b/tox.ini
index d7e3e82..1fc6c00 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,16 +1,7 @@
 [tox]
-envlist = py38, py39, py3.10, py311, py312, pypy3
+envlist = py38, py39, py3.10, py311, py312, py313, pypy3
 skip_missing_interpreters = true
 
-[gh-actions]
-python =
-    3.8: py38
-    3.9: py39
-    3.10: py310
-    3.11: py311
-    3.12: py312
-    pypy3.10: pypy3
-
 [testenv]
 extras = dev
 


Reviews:
user: login: aaronliu0130
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-02T19:20:17Z
user: login: aaronliu0130
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-02T19:20:35Z
user: login: cclauss
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T14:03:16Z
user: login: cclauss
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-03T14:04:25Z
user: login: jayvdb
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-07T00:58:19Z
user: login: aaronliu0130
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-07T20:33:13Z
user: login: cclauss
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-07T21:12:00Z

Review comments:
diff_hunk: @@ -12,11 +12,19 @@ jobs:
   build-test:
     runs-on: ${{ matrix.os }}
     strategy:
-      matrix:
-        # Python 3.8 is the last non-EOL version. Also adapt tox.ini
-        python-version: ['3.8', 'pypy3.10', '3.12']
-        os: [ubuntu-latest, windows-latest]
       fail-fast: false
+      matrix:
+        # Python 3.9 is the last non-EOL version. Also adapt tox.ini
+        python-version: ['3.9', 'pypy3.11', '3.13']
+        # macOS on ARM, Ubuntu on x86, Windows oo X86
+        os: [macos-latest, ubuntu-latest, windows-latest]
+        include:
+          - python-version: 3.x
+            os: macos-13  # macOS on Intel
path: .github/workflows/ci.yml
user: login: aaronliu0130
body: I think the only platform difference that could have a difference to need standalone testing for macOS is ARM, so I don't get this. In fact, I don't get why we need any of the "include" things instead of just replacing 3.13 with 3.x.
created_at: 2025-03-02T19:20:16Z
author_association: MEMBER
diff_hunk: @@ -12,11 +12,19 @@ jobs:
   build-test:
     runs-on: ${{ matrix.os }}
     strategy:
-      matrix:
-        # Python 3.8 is the last non-EOL version. Also adapt tox.ini
-        python-version: ['3.8', 'pypy3.10', '3.12']
-        os: [ubuntu-latest, windows-latest]
       fail-fast: false
+      matrix:
+        # Python 3.9 is the last non-EOL version. Also adapt tox.ini
+        python-version: ['3.9', 'pypy3.11', '3.13']
+        # macOS on ARM, Ubuntu on x86, Windows oo X86
+        os: [macos-latest, ubuntu-latest, windows-latest]
+        include:
+          - python-version: 3.x
+            os: macos-13  # macOS on Intel
+          - python-version: 3.x
+            os: ubuntu-latest  # Ubuntu on ARM
+          - python-version: 3.x
+            os: windows-2025
path: .github/workflows/ci.yml
user: login: aaronliu0130
body: ditto
created_at: 2025-03-02T19:20:35Z
author_association: MEMBER
diff_hunk: @@ -12,11 +12,19 @@ jobs:
   build-test:
     runs-on: ${{ matrix.os }}
     strategy:
-      matrix:
-        # Python 3.8 is the last non-EOL version. Also adapt tox.ini
-        python-version: ['3.8', 'pypy3.10', '3.12']
-        os: [ubuntu-latest, windows-latest]
       fail-fast: false
+      matrix:
+        # Python 3.9 is the last non-EOL version. Also adapt tox.ini
+        python-version: ['3.9', 'pypy3.11', '3.13']
+        # macOS on ARM, Ubuntu on x86, Windows oo X86
+        os: [macos-latest, ubuntu-latest, windows-latest]
+        include:
+          - python-version: 3.x
+            os: macos-13  # macOS on Intel
path: .github/workflows/ci.yml
user: login: cclauss
body: Done.
created_at: 2025-03-03T14:03:15Z
author_association: MEMBER
diff_hunk: @@ -12,11 +12,19 @@ jobs:
   build-test:
     runs-on: ${{ matrix.os }}
     strategy:
-      matrix:
-        # Python 3.8 is the last non-EOL version. Also adapt tox.ini
-        python-version: ['3.8', 'pypy3.10', '3.12']
-        os: [ubuntu-latest, windows-latest]
       fail-fast: false
+      matrix:
+        # Python 3.9 is the last non-EOL version. Also adapt tox.ini
+        python-version: ['3.9', 'pypy3.11', '3.13']
+        # macOS on ARM, Ubuntu on x86, Windows oo X86
+        os: [macos-latest, ubuntu-latest, windows-latest]
+        include:
+          - python-version: 3.x
+            os: macos-13  # macOS on Intel
+          - python-version: 3.x
+            os: ubuntu-latest  # Ubuntu on ARM
+          - python-version: 3.x
+            os: windows-2025
path: .github/workflows/ci.yml
user: login: cclauss
body: Done.
created_at: 2025-03-03T14:04:25Z
author_association: MEMBER
diff_hunk: @@ -1,6 +1,77 @@
 [build-system]
-build-backend = "setuptools.build_meta:__legacy__"
-requires = [
+build-backend = "setuptools.build_meta"
+requires = [ "setuptools>=61.2" ]
+
+[project]
+name = "cpplint"
+version = "2.0.0"
+description = "Automated checker to ensure C++ files follow Google's style guide"
+readme = "README.rst"
+keywords = [ "c++", "lint", "python" ]
+license = { text = "BSD-3-Clause" }
+maintainers = [ { name = "cpplint Developers" } ]
+requires-python = ">=3.8"
+classifiers = [
+  "Development Status :: 5 - Production/Stable",
+  "Environment :: Console",
+  "Intended Audience :: End Users/Desktop",
+  "License :: Freely Distributable",
+  "License :: OSI Approved :: BSD License",
+  "Natural Language :: English",
+  "Programming Language :: C++",
+  "Programming Language :: Python :: 3 :: Only",
+  "Programming Language :: Python :: 3.8",
+  "Programming Language :: Python :: 3.9",
+  "Programming Language :: Python :: 3.10",
+  "Programming Language :: Python :: 3.11",
+  "Programming Language :: Python :: 3.12",
+  "Programming Language :: Python :: 3.13",
+  "Topic :: Software Development :: Quality Assurance",
+]
+dependencies = [  ]
+
+optional-dependencies.dev = [
+  "flake8>=4.0.1",
+  "parameterized",
+  "pylint>=2.11",
+  "pytest",
+  "pytest-cov",
+  "pytest-timeout",
   "setuptools",
-  "wheel",
+  "testfixtures",
+  "tox<5",
+]
+optional-dependencies.test = [
+  "parameterized",
+  "pytest",
+  "pytest-cov",
+  "pytest-timeout",
+  "testfixtures",
+  "tox<5",
+]
+optional-dependencies.testing = [
+  "parameterized",
+  "pytest",
+  "pytest-cov",
+  "pytest-timeout",
+  "testfixtures",
+  "tox<5",
 ]
path: pyproject.toml
user: login: aaronliu0130
body: Rebasing rn, is there a reason the text extra was duplicated lol
created_at: 2025-03-07T20:33:13Z
author_association: MEMBER
diff_hunk: @@ -1,6 +1,77 @@
 [build-system]
-build-backend = "setuptools.build_meta:__legacy__"
-requires = [
+build-backend = "setuptools.build_meta"
+requires = [ "setuptools>=61.2" ]
+
+[project]
+name = "cpplint"
+version = "2.0.0"
+description = "Automated checker to ensure C++ files follow Google's style guide"
+readme = "README.rst"
+keywords = [ "c++", "lint", "python" ]
+license = { text = "BSD-3-Clause" }
+maintainers = [ { name = "cpplint Developers" } ]
+requires-python = ">=3.8"
+classifiers = [
+  "Development Status :: 5 - Production/Stable",
+  "Environment :: Console",
+  "Intended Audience :: End Users/Desktop",
+  "License :: Freely Distributable",
+  "License :: OSI Approved :: BSD License",
+  "Natural Language :: English",
+  "Programming Language :: C++",
+  "Programming Language :: Python :: 3 :: Only",
+  "Programming Language :: Python :: 3.8",
+  "Programming Language :: Python :: 3.9",
+  "Programming Language :: Python :: 3.10",
+  "Programming Language :: Python :: 3.11",
+  "Programming Language :: Python :: 3.12",
+  "Programming Language :: Python :: 3.13",
+  "Topic :: Software Development :: Quality Assurance",
+]
+dependencies = [  ]
+
+optional-dependencies.dev = [
+  "flake8>=4.0.1",
+  "parameterized",
+  "pylint>=2.11",
+  "pytest",
+  "pytest-cov",
+  "pytest-timeout",
   "setuptools",
-  "wheel",
+  "testfixtures",
+  "tox<5",
+]
+optional-dependencies.test = [
+  "parameterized",
+  "pytest",
+  "pytest-cov",
+  "pytest-timeout",
+  "testfixtures",
+  "tox<5",
+]
+optional-dependencies.testing = [
+  "parameterized",
+  "pytest",
+  "pytest-cov",
+  "pytest-timeout",
+  "testfixtures",
+  "tox<5",
 ]
path: pyproject.toml
user: login: cclauss
body: .test, .testing, and .dev all existed in `setup.py` so I replicated them in `pyproject.toml` (plus requirements files) to be sure I did not break anything.

Once we merge the open pre-commit PRs the only tools we will need in `.dev` will be pytest-related.  At that point, we can drop .test and .testing and just have dev.

The other issue is that the version is hard coded in both pyproject.toml and in Python code.  That should be fixed by making version dynamic in pyproject.toml and read out of the Python code.
created_at: 2025-03-07T21:12:00Z
author_association: MEMBER

PR comments:
user: login: aaronliu0130
created_at: 2025-03-02T19:04:50Z
body: This is awkward... I may be biased, but I prefer my #307 PR, which also includes the migrations but with dynamic version (based on cpplint.py) and dependencies (based on the dependency listing files). (I also read in pytest docs that they prefer storing settings in .pytest.ini so that's what I did.)

My PR would also automatically use the newest Python. (I also don't see reason to drop EOL Python versions for no reason; I think we should only drop it when something becomes an issue. And by "issue" I mean things like the EOL 3.7 not having the walrus operator.)
author_association: MEMBER
user: login: aaronliu0130
created_at: 2025-03-02T19:13:07Z
body: > I also don't see reason to drop EOL.

Sorry about that, I've realized the issue now: newer versions of Pylint you need don't support 3.9. So I support bumping minimum to 3.9 now.
author_association: MEMBER
user: login: cclauss
created_at: 2025-03-02T20:44:13Z
body: I believe we should all push projects to not build on foundations that no longer receive security updates.

~Please cherry pick anything that is interesting here and put it in #307 and we can land that and close this.~
author_association: MEMBER
user: login: cclauss
created_at: 2025-03-02T23:27:02Z
body: > I may be biased, but I prefer my #307 PR

You create pull requests under one username and review pull requests on the same repository under a different username?  Seems duplicitous.
author_association: MEMBER
user: login: aaronliu0130
created_at: 2025-03-03T00:42:16Z
body: > You create pull requests under one username and review pull requests on the same repository under a different username? Seems duplicitous.

I accidentally merged a PR that broke everything once; I started a PR to fix that. As @jayvdb (the only other active maintainer) is not active and the PR sat for weeks, I started doing that and jayvdb doesn't seem to mind.
author_association: MEMBER
user: login: cclauss
created_at: 2025-03-03T09:41:18Z
body: > I accidentally merged a PR that broke everything once

This is not something that happened just once.  I find a repeated duplicitous and dangerous pattern of [eight pull requests created by @norab0130](https://github.com/cpplint/cpplint/pulls?q=is%3Apr+author%3Anorab0130) and approved and merged by @aaronliu0130 when the same person controls both accounts.
* #270 
* #274
* #277
* #285
* #289
* #290
* #291
* #304

___This repeated pattern intentionally circumvents the supply chain security policy of this repo___ that a maintainer who is not the author reviews and approves every pull request before it is merged.  This policy is standard for Google and most other widely installed software tools.  If anyone thinks this is not a problem, they are not paying enough attention to software supply chain security issues.

The code in this repo is not owned by one person but is instead the work of many that needs to be treated safely and with respect.  If a maintainer is not paying enough attention to review pull requests on widely deployed production code, then one or more new maintainers must be added to the project.

Actions: I have the commit-bit for [my previous work on this repo](https://github.com/cpplint/cpplint/pulls?q=is%3Apr+author%3Acclauss).
1. I will close the three open PRs created by @norab0130.  I ask that this user not create any new pull requests on this repo.  This is done out of an abundance of caution given the circumvention of the security policies of this repo.  If @aaronliu0130 wants to recreate them under their username, then another maintainer (myself included) can review and merge them.
2. I ask @jayvdb @mattyclarkson @tkruse @theandrewdavis to review the eight pull requests above to ensure that nothing suspicious has already been merged into this broadly deployed codebase.
3. I ask that if this duplicitous and dangerous pattern is being repeated on other GitHub repos, it must be stopped immediately.

---
___EDIT:___
If this was deliberately deceptive @norab0130 would be called a ["sock puppet account"](https://en.wikipedia.org/wiki/Sock_puppet_account) in the [OSSF Scorecard docs](https://github.com/ossf/scorecard/blob/main/docs/checks.md#code-review).
author_association: MEMBER
user: login: aaronliu0130
created_at: 2025-03-03T23:45:31Z
body: This is the only place where I've used that account for this purpose, as someone from a "pgrouting" repository started complaining about the errors on the development branch. No problem not using that account if there's other active maintainers around to use that account. I'm happy for you, an active maintainer, to review my pull requests from now on!
author_association: MEMBER