community.general/plugins/module_utils/homebrew.py
Alexei Znamensky b85e263466
Some checks are pending
EOL CI / EOL Sanity (Ⓐ2.17) (push) Waiting to run
EOL CI / EOL Units (Ⓐ2.17+py3.10) (push) Waiting to run
EOL CI / EOL Units (Ⓐ2.17+py3.12) (push) Waiting to run
EOL CI / EOL Units (Ⓐ2.17+py3.7) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.17+alpine319+py:azp/posix/1/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.17+alpine319+py:azp/posix/2/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.17+alpine319+py:azp/posix/3/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.17+fedora39+py:azp/posix/1/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.17+fedora39+py:azp/posix/2/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.17+fedora39+py:azp/posix/3/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.17+ubuntu2004+py:azp/posix/1/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.17+ubuntu2004+py:azp/posix/2/) (push) Waiting to run
EOL CI / EOL I (Ⓐ2.17+ubuntu2004+py:azp/posix/3/) (push) Waiting to run
nox / Run extra sanity tests (push) Waiting to run
use f-strings in module utils (#10901)
* use f-strings in module utils

* Apply suggestions from code review

Co-authored-by: Felix Fontein <felix@fontein.de>

* remove unused imports

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
2025-10-11 11:43:43 +02:00

135 lines
4.1 KiB
Python

# Copyright (c) Ansible project
# Simplified BSD License (see LICENSES/BSD-2-Clause.txt or https://opensource.org/licenses/BSD-2-Clause)
# SPDX-License-Identifier: BSD-2-Clause
from __future__ import annotations
import os
import re
def _create_regex_group_complement(s):
lines = (line.strip() for line in s.split("\n") if line.strip())
chars = filter(None, (line.split("#")[0].strip() for line in lines))
group = rf"[^{''.join(chars)}]"
return re.compile(group)
class HomebrewValidate(object):
# class regexes ------------------------------------------------ {{{
VALID_PATH_CHARS = rf"""
\w # alphanumeric characters (i.e., [a-zA-Z0-9_])
\s # spaces
: # colons
{os.path.sep} # the OS-specific path separator
. # dots
\- # dashes
"""
VALID_BREW_PATH_CHARS = rf"""
\w # alphanumeric characters (i.e., [a-zA-Z0-9_])
\s # spaces
{os.path.sep} # the OS-specific path separator
. # dots
\- # dashes
"""
VALID_PACKAGE_CHARS = r"""
\w # alphanumeric characters (i.e., [a-zA-Z0-9_])
. # dots
/ # slash (for taps)
\+ # plusses
\- # dashes
: # colons (for URLs)
@ # at-sign
"""
INVALID_PATH_REGEX = _create_regex_group_complement(VALID_PATH_CHARS)
INVALID_BREW_PATH_REGEX = _create_regex_group_complement(VALID_BREW_PATH_CHARS)
INVALID_PACKAGE_REGEX = _create_regex_group_complement(VALID_PACKAGE_CHARS)
# /class regexes ----------------------------------------------- }}}
# class validations -------------------------------------------- {{{
@classmethod
def valid_path(cls, path):
"""
`path` must be one of:
- list of paths
- a string containing only:
- alphanumeric characters
- dashes
- dots
- spaces
- colons
- os.path.sep
"""
if isinstance(path, str):
return not cls.INVALID_PATH_REGEX.search(path)
try:
iter(path)
except TypeError:
return False
else:
paths = path
return all(cls.valid_brew_path(path_) for path_ in paths)
@classmethod
def valid_brew_path(cls, brew_path):
"""
`brew_path` must be one of:
- None
- a string containing only:
- alphanumeric characters
- dashes
- dots
- spaces
- os.path.sep
"""
if brew_path is None:
return True
return isinstance(
brew_path, str
) and not cls.INVALID_BREW_PATH_REGEX.search(brew_path)
@classmethod
def valid_package(cls, package):
"""A valid package is either None or alphanumeric."""
if package is None:
return True
return isinstance(
package, str
) and not cls.INVALID_PACKAGE_REGEX.search(package)
def parse_brew_path(module):
# type: (...) -> str
"""Attempt to find the Homebrew executable path.
Requires:
- module has a `path` parameter
- path is a valid path string for the target OS. Otherwise, module.fail_json()
is called with msg="Invalid_path: <path>".
"""
path = module.params["path"]
if not HomebrewValidate.valid_path(path):
module.fail_json(msg=f"Invalid path: {path}")
if isinstance(path, str):
paths = path.split(":")
elif isinstance(path, list):
paths = path
else:
module.fail_json(msg=f"Invalid path: {path}")
brew_path = module.get_bin_path("brew", required=True, opt_dirs=paths)
if not HomebrewValidate.valid_brew_path(brew_path):
module.fail_json(msg=f"Invalid brew path: {brew_path}")
return brew_path