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 .