From 53bb407664e3cccf169e9ae628201f5bbe77cb78 Mon Sep 17 00:00:00 2001 From: Peter Schuster Date: Tue, 17 Mar 2026 15:36:21 +0100 Subject: [PATCH 1/6] chore: extract glob for pyupgrade to separate script for cross-platform compatibility 'sh' in tox.ini does not work on Windows in PowerShell. Signed-off-by: Peter Schuster --- tools/run_pyupgrade.py | 35 +++++++++++++++++++++++++++++++++++ tox.ini | 6 ++---- 2 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 tools/run_pyupgrade.py diff --git a/tools/run_pyupgrade.py b/tools/run_pyupgrade.py new file mode 100644 index 000000000..f67ae15d2 --- /dev/null +++ b/tools/run_pyupgrade.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +# Wrapper around pyupgrade to perform a lookup of all *.py/*.pyi files in passed directories +# and pass them to pyupgrade in a single invocation. +# +# Usage: run_pyupgrade.py -- + +import subprocess # nosec - subprocess is used to run pyupgrade and not part of published library +import sys +from pathlib import Path + +if '--' not in sys.argv: + print('Usage: run_pyupgrade.py -- ', file=sys.stderr) + sys.exit(1) + +sep = sys.argv.index('--') +pyupgrade_args = sys.argv[1:sep] +directories = sys.argv[sep + 1:] + +if not directories: + print('Error: at least one directory must be specified after --', file=sys.stderr) + sys.exit(1) + +files = sorted({ + str(file) + for directory in directories + for pattern in ['*.py', '*.pyi'] + for file in Path(directory).rglob(pattern) +}) + +result = subprocess.run( # nosec - shell=False is used to prevent injection, all arg passed as a list + [sys.executable, '-m', 'pyupgrade', *pyupgrade_args, *files], + shell=False # w/o shell all args are passed directly to the process without the need for quotes or escaping +) +sys.exit(result.returncode) diff --git a/tox.ini b/tox.ini index af228b75a..8afcf3aa0 100644 --- a/tox.ini +++ b/tox.ini @@ -52,10 +52,8 @@ commands = poetry run deptry -v . [testenv:pyupgrade] -allowlist_externals = poetry, sh -commands = sh -c "\ - find cyclonedx typings tests tools examples -type f \( -name '*.py' -or -name '*.pyi' \) -print0 \ - | xargs -0 poetry run pyupgrade --py39-plus {posargs} " +# first -- stops command parsing by poetry run, the second -- splits pyupgrade args from args for glob patterns +commands = poetry run -- python tools/run_pyupgrade.py --py39-plus {posargs} -- cyclonedx typings tests tools examples [testenv:isort] commands = poetry run isort . From 9c8d05de92da91bb5608987b341dffd24ae21f2a Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Mon, 23 Mar 2026 14:50:55 +0100 Subject: [PATCH 2/6] docs Signed-off-by: Jan Kowalleck --- tools/run_pyupgrade.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/run_pyupgrade.py b/tools/run_pyupgrade.py index f67ae15d2..d7e8459fe 100644 --- a/tools/run_pyupgrade.py +++ b/tools/run_pyupgrade.py @@ -1,9 +1,11 @@ #!/usr/bin/env python3 -# Wrapper around pyupgrade to perform a lookup of all *.py/*.pyi files in passed directories -# and pass them to pyupgrade in a single invocation. -# -# Usage: run_pyupgrade.py -- +""" +Wrapper around pyupgrade to perform a lookup of all *.py/*.pyi files in passed directories +and pass them to pyupgrade in a single invocation. + +Usage: run_pyupgrade.py -- +""" import subprocess # nosec - subprocess is used to run pyupgrade and not part of published library import sys From e65cb85282a6fcd9ce94290883511679f76fdbea Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Mon, 23 Mar 2026 14:54:25 +0100 Subject: [PATCH 3/6] executable Signed-off-by: Jan Kowalleck --- tools/run_pyupgrade.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 tools/run_pyupgrade.py diff --git a/tools/run_pyupgrade.py b/tools/run_pyupgrade.py old mode 100644 new mode 100755 From 13e9ef15ecf9a7239503380b3cc1fc24fbd6d4b7 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Mon, 23 Mar 2026 15:02:10 +0100 Subject: [PATCH 4/6] docs Signed-off-by: Jan Kowalleck --- tools/run_pyupgrade.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/run_pyupgrade.py b/tools/run_pyupgrade.py index d7e8459fe..1d936c256 100755 --- a/tools/run_pyupgrade.py +++ b/tools/run_pyupgrade.py @@ -1,18 +1,18 @@ #!/usr/bin/env python3 -""" +import subprocess # nosec - subprocess is used to run pyupgrade and not part of published library +import sys +from pathlib import Path + +HELP=f""" Wrapper around pyupgrade to perform a lookup of all *.py/*.pyi files in passed directories and pass them to pyupgrade in a single invocation. -Usage: run_pyupgrade.py -- +Usage: {sys.argv[0]} [pyupgrade-args ...] -- """ -import subprocess # nosec - subprocess is used to run pyupgrade and not part of published library -import sys -from pathlib import Path - if '--' not in sys.argv: - print('Usage: run_pyupgrade.py -- ', file=sys.stderr) + print(HELP, file=sys.stderr) sys.exit(1) sep = sys.argv.index('--') @@ -20,8 +20,8 @@ directories = sys.argv[sep + 1:] if not directories: - print('Error: at least one directory must be specified after --', file=sys.stderr) - sys.exit(1) + print('Error: at least one directory must be specified after --', '\n', HELP, file=sys.stderr) + sys.exit(2) files = sorted({ str(file) From cd7e434b4b4e3664d543e4af7dff11de79800b77 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Mon, 23 Mar 2026 15:02:50 +0100 Subject: [PATCH 5/6] style Signed-off-by: Jan Kowalleck --- tools/run_pyupgrade.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/run_pyupgrade.py b/tools/run_pyupgrade.py index 1d936c256..fb4a48563 100755 --- a/tools/run_pyupgrade.py +++ b/tools/run_pyupgrade.py @@ -4,7 +4,7 @@ import sys from pathlib import Path -HELP=f""" +HELP = f""" Wrapper around pyupgrade to perform a lookup of all *.py/*.pyi files in passed directories and pass them to pyupgrade in a single invocation. @@ -30,7 +30,7 @@ for file in Path(directory).rglob(pattern) }) -result = subprocess.run( # nosec - shell=False is used to prevent injection, all arg passed as a list +result = subprocess.run( # nosec - shell=False is used to prevent injection, all arg passed as a list [sys.executable, '-m', 'pyupgrade', *pyupgrade_args, *files], shell=False # w/o shell all args are passed directly to the process without the need for quotes or escaping ) From 0018b1bfd7602c5671cd66bacb6f0d27a812dc04 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Mon, 23 Mar 2026 15:06:07 +0100 Subject: [PATCH 6/6] license-header Signed-off-by: Jan Kowalleck --- tools/run_pyupgrade.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tools/run_pyupgrade.py b/tools/run_pyupgrade.py index fb4a48563..e040c5071 100755 --- a/tools/run_pyupgrade.py +++ b/tools/run_pyupgrade.py @@ -1,5 +1,22 @@ #!/usr/bin/env python3 +# This file is part of CycloneDX Python Library +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) OWASP Foundation. All Rights Reserved. + import subprocess # nosec - subprocess is used to run pyupgrade and not part of published library import sys from pathlib import Path