contrib: Use text=True in subprocess over manual encoding handling

All touched Python scripts already assume and require UTF8, so manually
specifying encoding or decoding for functions in the subprocess module
is redundant to just using text=True, which exists since Python 3.7
This commit is contained in:
MarcoFalke committed 2025-11-26 11:31:29 +01:00
1 parent fa71c15f86
commit fab085c15f
12 files changed
+43 -47

No files matched your search

+5 -5
View File
@@ -54,12 +54,12 @@ GIT_LS_CMD = 'git ls-files --full-name'.split(' ')
GIT_TOPLEVEL_CMD = 'git rev-parse --show-toplevel'.split(' ') GIT_TOPLEVEL_CMD = 'git rev-parse --show-toplevel'.split(' ')
def call_git_ls(base_directory): def call_git_ls(base_directory):
out = subprocess.check_output([*GIT_LS_CMD, base_directory]) out = subprocess.check_output([*GIT_LS_CMD, base_directory], text=True)
return [f for f in out.decode("utf-8").split('\n') if f != ''] return [f for f in out.split('\n') if f != '']
def call_git_toplevel(): def call_git_toplevel():
"Returns the absolute path to the project root" "Returns the absolute path to the project root"
return subprocess.check_output(GIT_TOPLEVEL_CMD).strip().decode("utf-8") return subprocess.check_output(GIT_TOPLEVEL_CMD, text=True).strip()
def get_filenames_to_examine(base_directory): def get_filenames_to_examine(base_directory):
"Returns an array of absolute paths to any project files in the base_directory that pass the include/exclude filters" "Returns an array of absolute paths to any project files in the base_directory that pass the include/exclude filters"
@@ -298,8 +298,8 @@ def report_cmd(argv):
GIT_LOG_CMD = "git log --pretty=format:%%ai %s" GIT_LOG_CMD = "git log --pretty=format:%%ai %s"
def call_git_log(filename): def call_git_log(filename):
out = subprocess.check_output((GIT_LOG_CMD % filename).split(' ')) out = subprocess.check_output((GIT_LOG_CMD % filename).split(' '), text=True)
return out.decode("utf-8").split('\n') return out.split('\n')
def get_git_change_years(filename): def get_git_change_years(filename):
git_log_lines = call_git_log(filename) git_log_lines = call_git_log(filename)
+8 -8
View File
@@ -140,8 +140,8 @@ def main():
# Check that the commit (and parents) was signed with a trusted key # Check that the commit (and parents) was signed with a trusted key
valid_sig = False valid_sig = False
verify_res = subprocess.run([GIT, '-c', 'gpg.program={}/gpg.sh'.format(dirname), 'verify-commit', "--raw", current_commit], capture_output=True) verify_res = subprocess.run([GIT, '-c', 'gpg.program={}/gpg.sh'.format(dirname), 'verify-commit', "--raw", current_commit], text=True, capture_output=True)
for line in verify_res.stderr.decode().splitlines(): for line in verify_res.stderr.splitlines():
if line.startswith("[GNUPG:] VALIDSIG "): if line.startswith("[GNUPG:] VALIDSIG "):
key = line.split(" ")[-1] key = line.split(" ")[-1]
valid_sig = key in trusted_keys valid_sig = key in trusted_keys
@@ -152,7 +152,7 @@ def main():
if prev_commit != "": if prev_commit != "":
print("No parent of {} was signed with a trusted key!".format(prev_commit), file=sys.stderr) print("No parent of {} was signed with a trusted key!".format(prev_commit), file=sys.stderr)
print("Parents are:", file=sys.stderr) print("Parents are:", file=sys.stderr)
parents = subprocess.check_output([GIT, 'show', '-s', '--format=format:%P', prev_commit]).decode('utf8').splitlines()[0].split(' ') parents = subprocess.check_output([GIT, 'show', '-s', '--format=format:%P', prev_commit], text=True).splitlines()[0].split(' ')
for parent in parents: for parent in parents:
subprocess.call([GIT, 'show', '-s', parent], stdout=sys.stderr) subprocess.call([GIT, 'show', '-s', parent], stdout=sys.stderr)
else: else:
@@ -162,29 +162,29 @@ def main():
# Check the Tree-SHA512 # Check the Tree-SHA512
if (verify_tree or prev_commit == "") and current_commit not in incorrect_sha512_allowed: if (verify_tree or prev_commit == "") and current_commit not in incorrect_sha512_allowed:
tree_hash = tree_sha512sum(current_commit) tree_hash = tree_sha512sum(current_commit)
if ("Tree-SHA512: {}".format(tree_hash)) not in subprocess.check_output([GIT, 'show', '-s', '--format=format:%B', current_commit]).decode('utf8').splitlines(): if ("Tree-SHA512: {}".format(tree_hash)) not in subprocess.check_output([GIT, 'show', '-s', '--format=format:%B', current_commit], text=True).splitlines():
print("Tree-SHA512 did not match for commit " + current_commit, file=sys.stderr) print("Tree-SHA512 did not match for commit " + current_commit, file=sys.stderr)
sys.exit(1) sys.exit(1)
# Merge commits should only have two parents # Merge commits should only have two parents
parents = subprocess.check_output([GIT, 'show', '-s', '--format=format:%P', current_commit]).decode('utf8').splitlines()[0].split(' ') parents = subprocess.check_output([GIT, 'show', '-s', '--format=format:%P', current_commit], text=True).splitlines()[0].split(' ')
if len(parents) > 2: if len(parents) > 2:
print("Commit {} is an octopus merge".format(current_commit), file=sys.stderr) print("Commit {} is an octopus merge".format(current_commit), file=sys.stderr)
sys.exit(1) sys.exit(1)
# Check that the merge commit is clean # Check that the merge commit is clean
commit_time = int(subprocess.check_output([GIT, 'show', '-s', '--format=format:%ct', current_commit]).decode('utf8').splitlines()[0]) commit_time = int(subprocess.check_output([GIT, 'show', '-s', '--format=format:%ct', current_commit], text=True).splitlines()[0])
check_merge = commit_time > time.time() - args.clean_merge * 24 * 60 * 60 # Only check commits in clean_merge days check_merge = commit_time > time.time() - args.clean_merge * 24 * 60 * 60 # Only check commits in clean_merge days
allow_unclean = current_commit in unclean_merge_allowed allow_unclean = current_commit in unclean_merge_allowed
if len(parents) == 2 and check_merge and not allow_unclean: if len(parents) == 2 and check_merge and not allow_unclean:
current_tree = subprocess.check_output([GIT, 'show', '--format=%T', current_commit]).decode('utf8').splitlines()[0] current_tree = subprocess.check_output([GIT, 'show', '--format=%T', current_commit], text=True).splitlines()[0]
# This merge-tree functionality requires git >= 2.38. The # This merge-tree functionality requires git >= 2.38. The
# --write-tree option was added in order to opt-in to the new # --write-tree option was added in order to opt-in to the new
# behavior. Older versions of git will not recognize the option and # behavior. Older versions of git will not recognize the option and
# will instead exit with code 128. # will instead exit with code 128.
try: try:
recreated_tree = subprocess.check_output([GIT, "merge-tree", "--write-tree", parents[0], parents[1]]).decode('utf8').splitlines()[0] recreated_tree = subprocess.check_output([GIT, "merge-tree", "--write-tree", parents[0], parents[1]], text=True).splitlines()[0]
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
if e.returncode == 128: if e.returncode == 128:
print("git v2.38+ is required for this functionality.", file=sys.stderr) print("git v2.38+ is required for this functionality.", file=sys.stderr)
@@ -43,15 +43,13 @@ class FeatureFrameworkStartupFailures(BitcoinTestFramework):
args = [sys.executable] + sys.argv + [f"--tmpdir={self.options.tmpdir}/{subdir}", f"--internal_test={test.__name__}"] + internal_args args = [sys.executable] + sys.argv + [f"--tmpdir={self.options.tmpdir}/{subdir}", f"--internal_test={test.__name__}"] + internal_args
try: try:
# The subprocess encoding argument gives different results for e.output
# on Linux/Windows, so handle decoding by ourselves for consistency.
output = subprocess.run(args, timeout=60 * self.options.timeout_factor, output = subprocess.run(args, timeout=60 * self.options.timeout_factor,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT).stdout.decode("utf-8") stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True).stdout
except subprocess.TimeoutExpired as e: except subprocess.TimeoutExpired as e:
print("Unexpected child process timeout!\n" print("Unexpected child process timeout!\n"
"WARNING: Timeouts like this halt execution of TestNode logic, " "WARNING: Timeouts like this halt execution of TestNode logic, "
"meaning dangling bitcoind processes are to be expected.\n" "meaning dangling bitcoind processes are to be expected.\n"
f"<CHILD OUTPUT BEGIN>\n{e.output.decode('utf-8')}\n<CHILD OUTPUT END>", f"<CHILD OUTPUT BEGIN>\n{e.output}\n<CHILD OUTPUT END>",
file=sys.stderr) file=sys.stderr)
raise raise
+5 -5
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Copyright (c) 2022 The Bitcoin Core developers # Copyright (c) 2022-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test signet miner tool""" """Test signet miner tool"""
@@ -102,8 +102,8 @@ class SignetMinerTest(BitcoinTestFramework):
'genpsbt', 'genpsbt',
f'--address={node.getnewaddress()}', f'--address={node.getnewaddress()}',
'--poolnum=98', '--poolnum=98',
], check=True, input=json.dumps(template).encode('utf8'), capture_output=True) ], check=True, text=True, input=json.dumps(template), capture_output=True)
psbt = genpsbt.stdout.decode('utf8').strip() psbt = genpsbt.stdout.strip()
if sign: if sign:
self.log.debug("Sign the PSBT") self.log.debug("Sign the PSBT")
res = node.walletprocesspsbt(psbt=psbt, sign=True, sighashtype='ALL') res = node.walletprocesspsbt(psbt=psbt, sign=True, sighashtype='ALL')
@@ -112,8 +112,8 @@ class SignetMinerTest(BitcoinTestFramework):
solvepsbt = subprocess.run(base_cmd + [ solvepsbt = subprocess.run(base_cmd + [
'solvepsbt', 'solvepsbt',
f'--grind-cmd={shlex.join(util_argv)}', f'--grind-cmd={shlex.join(util_argv)}',
], check=True, input=psbt.encode('utf8'), capture_output=True) ], check=True, text=True, input=psbt, capture_output=True)
node.submitblock(solvepsbt.stdout.decode('utf8').strip()) node.submitblock(solvepsbt.stdout.strip())
assert_equal(node.getblockcount(), n_blocks + 1) assert_equal(node.getblockcount(), n_blocks + 1)
def run_test(self): def run_test(self):
+5 -7
View File
@@ -1,13 +1,11 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Copyright (c) 2015-2022 The Bitcoin Core developers # Copyright (c) 2015-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
''' '''
This checks if all command line args are documented. This checks if all command line args are documented.
Return value is 0 to indicate no error. Return value is 0 to indicate no error.
Author: @MarcoFalke
''' '''
from subprocess import check_output from subprocess import check_output
@@ -27,8 +25,8 @@ SET_DOC_OPTIONAL = set(['-h', '-?', '-dbcrashratio', '-forcecompactdb', '-ipccon
def lint_missing_argument_documentation(): def lint_missing_argument_documentation():
used = check_output(CMD_GREP_ARGS, shell=True).decode('utf8').strip() used = check_output(CMD_GREP_ARGS, shell=True, text=True).strip()
docd = check_output(CMD_GREP_DOCS, shell=True).decode('utf8').strip() docd = check_output(CMD_GREP_DOCS, shell=True, text=True).strip()
args_used = set(re.findall(re.compile(REGEX_ARG), used)) args_used = set(re.findall(re.compile(REGEX_ARG), used))
args_docd = set(re.findall(re.compile(REGEX_DOC), docd)).union(SET_DOC_OPTIONAL) args_docd = set(re.findall(re.compile(REGEX_DOC), docd)).union(SET_DOC_OPTIONAL)
@@ -46,8 +44,8 @@ def lint_missing_argument_documentation():
def lint_missing_hidden_wallet_args(): def lint_missing_hidden_wallet_args():
wallet_args = check_output(CMD_GREP_WALLET_ARGS, shell=True).decode('utf8').strip() wallet_args = check_output(CMD_GREP_WALLET_ARGS, shell=True, text=True).strip()
wallet_hidden_args = check_output(CMD_GREP_WALLET_HIDDEN_ARGS, shell=True).decode('utf8').strip() wallet_hidden_args = check_output(CMD_GREP_WALLET_HIDDEN_ARGS, shell=True, text=True).strip()
wallet_args = set(re.findall(re.compile(REGEX_DOC), wallet_args)) wallet_args = set(re.findall(re.compile(REGEX_DOC), wallet_args))
wallet_hidden_args = set(re.findall(re.compile(r' "([^"=]+)'), wallet_hidden_args)) wallet_hidden_args = set(re.findall(re.compile(r' "([^"=]+)'), wallet_hidden_args))
+3 -3
View File
@@ -76,7 +76,7 @@ def get_git_file_metadata() -> dict[str, FileMeta]:
''' '''
Return a dictionary mapping the name of all files in the repository to git tree metadata. Return a dictionary mapping the name of all files in the repository to git tree metadata.
''' '''
files_raw = check_output(CMD_ALL_FILES).decode("utf8").rstrip("\0").split("\0") files_raw = check_output(CMD_ALL_FILES, text=True).rstrip("\0").split("\0")
files = {} files = {}
for file_spec in files_raw: for file_spec in files_raw:
meta = FileMeta(file_spec) meta = FileMeta(file_spec)
@@ -169,7 +169,7 @@ def check_shebang_file_permissions(files_meta) -> int:
""" """
Checks every file that contains a shebang line to ensure it has an executable permission Checks every file that contains a shebang line to ensure it has an executable permission
""" """
filenames = check_output(CMD_SHEBANG_FILES).decode("utf8").strip().split("\n") filenames = check_output(CMD_SHEBANG_FILES, text=True).strip().split("\n")
# The git grep command we use returns files which contain a shebang on any line within the file # The git grep command we use returns files which contain a shebang on any line within the file
# so we need to filter the list to only files with the shebang on the first line # so we need to filter the list to only files with the shebang on the first line
@@ -198,7 +198,7 @@ def check_shebang_file_permissions(files_meta) -> int:
def main() -> NoReturn: def main() -> NoReturn:
root_dir = check_output(CMD_TOP_LEVEL).decode("utf8").strip() root_dir = check_output(CMD_TOP_LEVEL, text=True).strip()
os.chdir(root_dir) os.chdir(root_dir)
files = get_git_file_metadata() files = get_git_file_metadata()
+1 -1
View File
@@ -31,7 +31,7 @@ def _get_header_file_lst() -> list[str]:
""" """
git_cmd_lst = ['git', 'ls-files', '--', '*.h'] git_cmd_lst = ['git', 'ls-files', '--', '*.h']
header_file_lst = check_output( header_file_lst = check_output(
git_cmd_lst).decode('utf-8').splitlines() git_cmd_lst, text=True).splitlines()
header_file_lst = [hf for hf in header_file_lst header_file_lst = [hf for hf in header_file_lst
if not any(ef in hf for ef if not any(ef in hf for ef
+4 -4
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# #
# Copyright (c) 2022 The Bitcoin Core developers # Copyright (c) 2022-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -24,15 +24,15 @@ def check_vulture_install():
def main(): def main():
check_vulture_install() check_vulture_install()
files = check_output(FILES_ARGS).decode("utf-8").splitlines() files = check_output(FILES_ARGS, text=True).splitlines()
# --min-confidence 100 will only report code that is guaranteed to be unused within the analyzed files. # --min-confidence 100 will only report code that is guaranteed to be unused within the analyzed files.
# Any value below 100 introduces the risk of false positives, which would create an unacceptable maintenance burden. # Any value below 100 introduces the risk of false positives, which would create an unacceptable maintenance burden.
vulture_args = ['vulture', '--min-confidence=100'] + files vulture_args = ['vulture', '--min-confidence=100'] + files
try: try:
check_output(vulture_args, stderr=STDOUT) check_output(vulture_args, stderr=STDOUT, text=True)
except CalledProcessError as e: except CalledProcessError as e:
print(e.output.decode("utf-8"), end="") print(e.output, end="")
print("Python dead code detection found some issues") print("Python dead code detection found some issues")
exit(1) exit(1)
+2 -2
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# #
# Copyright (c) 2022 The Bitcoin Core developers # Copyright (c) 2022-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -37,7 +37,7 @@ def check_dependencies():
def main(): def main():
check_dependencies() check_dependencies()
mypy_files = subprocess.check_output(MYPY_FILES_ARGS).decode("utf-8").splitlines() mypy_files = subprocess.check_output(MYPY_FILES_ARGS, text=True).splitlines()
mypy_args = ['mypy', '--show-error-codes'] + mypy_files mypy_args = ['mypy', '--show-error-codes'] + mypy_files
try: try:
+2 -2
View File
@@ -27,10 +27,10 @@ def get_shell_files_list():
'*.sh', '*.sh',
] ]
try: try:
return subprocess.check_output(command, stderr = subprocess.STDOUT).decode('utf-8').splitlines() return subprocess.check_output(command, stderr = subprocess.STDOUT, text=True).splitlines()
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
if e.returncode > 1: # return code is 1 when match is empty if e.returncode > 1: # return code is 1 when match is empty
print(e.output.decode('utf-8'), end='') print(e.output, end='')
sys.exit(1) sys.exit(1)
return [] return []
+4 -4
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# #
# Copyright (c) 2022 The Bitcoin Core developers # Copyright (c) 2022-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -29,13 +29,13 @@ def check_codespell_install():
def main(): def main():
check_codespell_install() check_codespell_install()
files = check_output(FILES_ARGS).decode("utf-8").splitlines() files = check_output(FILES_ARGS, text=True).splitlines()
codespell_args = ['codespell', '--check-filenames', '--disable-colors', '--quiet-level=7', '--ignore-words={}'.format(IGNORE_WORDS_FILE)] + files codespell_args = ['codespell', '--check-filenames', '--disable-colors', '--quiet-level=7', '--ignore-words={}'.format(IGNORE_WORDS_FILE)] + files
try: try:
check_output(codespell_args, stderr=STDOUT) check_output(codespell_args, stderr=STDOUT, text=True)
except CalledProcessError as e: except CalledProcessError as e:
print(e.output.decode("utf-8"), end="") print(e.output, end="")
print('^ Warning: codespell identified likely spelling errors. Any false positives? Add them to the list of ignored words in {}'.format(IGNORE_WORDS_FILE)) print('^ Warning: codespell identified likely spelling errors. Any false positives? Add them to the list of ignored words in {}'.format(IGNORE_WORDS_FILE))
+2 -2
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# #
# Copyright (c) 2022 The Bitcoin Core developers # Copyright (c) 2022-present The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php. # file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -13,7 +13,7 @@ import sys
def main(): def main():
submodules_list = subprocess.check_output(['git', 'submodule', 'status', '--recursive'], submodules_list = subprocess.check_output(['git', 'submodule', 'status', '--recursive'],
text = True, encoding = 'utf8').rstrip('\n') text = True).rstrip('\n')
if submodules_list: if submodules_list:
print("These submodules were found, delete them:\n", submodules_list) print("These submodules were found, delete them:\n", submodules_list)
sys.exit(1) sys.exit(1)