From bc8dae55353cedb6adc724a59600224971d77089 Mon Sep 17 00:00:00 2001 From: JensDiemer Date: Wed, 9 Aug 2023 18:43:22 +0200 Subject: [PATCH] Use https://github.com/jedie/cli-base-utilities --- manageprojects/__init__.py | 2 +- manageprojects/cli/cli_app.py | 2 +- manageprojects/cli/dev.py | 2 +- manageprojects/format_file.py | 2 +- manageprojects/git.py | 3 +- manageprojects/test_utils/click_cli_utils.py | 3 +- .../tests/test_utilities_publish.py | 2 +- manageprojects/tests/test_version_info.py | 1 - manageprojects/utilities/code_style.py | 3 +- manageprojects/utilities/publish.py | 2 +- manageprojects/utilities/subprocess_utils.py | 246 ------------------ pyproject.toml | 2 +- requirements.dev.txt | 34 ++- requirements.txt | 22 +- 14 files changed, 48 insertions(+), 278 deletions(-) delete mode 100644 manageprojects/utilities/subprocess_utils.py diff --git a/manageprojects/__init__.py b/manageprojects/__init__.py index 494b4f2..9b772f8 100644 --- a/manageprojects/__init__.py +++ b/manageprojects/__init__.py @@ -3,5 +3,5 @@ Manage Python / Django projects """ -__version__ = '0.13.0' +__version__ = '0.14.0' __author__ = 'Jens Diemer ' diff --git a/manageprojects/cli/cli_app.py b/manageprojects/cli/cli_app.py index bcbf1ef..f2dff75 100644 --- a/manageprojects/cli/cli_app.py +++ b/manageprojects/cli/cli_app.py @@ -10,6 +10,7 @@ import rich_click as click from bx_py_utils.path import assert_is_dir, assert_is_file +from cli_base.cli_tools.subprocess_utils import verbose_check_call from rich import print # noqa from rich_click import RichGroup @@ -29,7 +30,6 @@ from manageprojects.data_classes import CookiecutterResult from manageprojects.format_file import format_one_file from manageprojects.utilities.log_utils import log_config -from manageprojects.utilities.subprocess_utils import verbose_check_call from manageprojects.utilities.version_info import print_version diff --git a/manageprojects/cli/dev.py b/manageprojects/cli/dev.py index 39b6a13..ea670df 100644 --- a/manageprojects/cli/dev.py +++ b/manageprojects/cli/dev.py @@ -7,6 +7,7 @@ import rich_click as click from bx_py_utils.path import assert_is_file +from cli_base.cli_tools.subprocess_utils import verbose_check_call from rich import print # noqa; noqa from rich_click import RichGroup @@ -14,7 +15,6 @@ from manageprojects import constants from manageprojects.utilities import code_style from manageprojects.utilities.publish import publish_package -from manageprojects.utilities.subprocess_utils import verbose_check_call from manageprojects.utilities.version_info import print_version diff --git a/manageprojects/format_file.py b/manageprojects/format_file.py index 146b3ec..bc1f4ac 100644 --- a/manageprojects/format_file.py +++ b/manageprojects/format_file.py @@ -3,6 +3,7 @@ from typing import Optional from bx_py_utils.dict_utils import dict_get +from cli_base.cli_tools.subprocess_utils import ToolsExecutor from editorconfig import EditorConfigError, get_properties from packaging.specifiers import SpecifierSet from packaging.version import Version @@ -13,7 +14,6 @@ from manageprojects.exceptions import NoPyProjectTomlFound from manageprojects.git import Git, GitError, NoGitRepoError from manageprojects.utilities.pyproject_toml import TomlDocument, get_pyproject_toml -from manageprojects.utilities.subprocess_utils import ToolsExecutor MAX_PY3_VER = 15 diff --git a/manageprojects/git.py b/manageprojects/git.py index 28def5f..7ad7667 100644 --- a/manageprojects/git.py +++ b/manageprojects/git.py @@ -10,10 +10,9 @@ from shutil import which from bx_py_utils.path import assert_is_file +from cli_base.cli_tools.subprocess_utils import verbose_check_call, verbose_check_output from packaging.version import InvalidVersion, Version -from manageprojects.utilities.subprocess_utils import verbose_check_call, verbose_check_output - logger = logging.getLogger(__name__) diff --git a/manageprojects/test_utils/click_cli_utils.py b/manageprojects/test_utils/click_cli_utils.py index 14c17ce..7efd5a2 100644 --- a/manageprojects/test_utils/click_cli_utils.py +++ b/manageprojects/test_utils/click_cli_utils.py @@ -4,10 +4,9 @@ import rich_click from bx_py_utils.path import assert_is_file from bx_py_utils.test_utils.context_managers import MassContextManager +from cli_base.cli_tools.subprocess_utils import verbose_check_output from click.testing import CliRunner, Result -from manageprojects.utilities.subprocess_utils import verbose_check_output - TERMINAL_WIDTH = 100 diff --git a/manageprojects/tests/test_utilities_publish.py b/manageprojects/tests/test_utilities_publish.py index 6d2261d..e09df81 100644 --- a/manageprojects/tests/test_utilities_publish.py +++ b/manageprojects/tests/test_utilities_publish.py @@ -4,6 +4,7 @@ from unittest import TestCase from unittest.mock import patch +from cli_base.cli_tools import subprocess_utils from packaging.version import Version import manageprojects @@ -12,7 +13,6 @@ from manageprojects.test_utils.logs import AssertLogs from manageprojects.test_utils.subprocess import FakeStdout, SubprocessCallMock from manageprojects.tests.base import GIT_BIN_PARENT -from manageprojects.utilities import subprocess_utils from manageprojects.utilities.publish import ( PublisherGit, build, diff --git a/manageprojects/tests/test_version_info.py b/manageprojects/tests/test_version_info.py index b670d9a..6ee7d63 100644 --- a/manageprojects/tests/test_version_info.py +++ b/manageprojects/tests/test_version_info.py @@ -39,7 +39,6 @@ def test_no_git(self): with AssertLogs(self, loggers=('manageprojects',)) as logs, RedirectOut() as buffer: print_version(module=manageprojects, project_root=temp_path) - self.assertEqual(buffer.stderr, '') self.assertIn(f'manageprojects v{__version__} ', buffer.stdout) logs.assert_in('Error print version', 'Traceback') diff --git a/manageprojects/utilities/code_style.py b/manageprojects/utilities/code_style.py index 3e392a8..31c79ba 100644 --- a/manageprojects/utilities/code_style.py +++ b/manageprojects/utilities/code_style.py @@ -1,8 +1,7 @@ import sys from pathlib import Path - -from manageprojects.utilities.subprocess_utils import ToolsExecutor, verbose_check_call +from cli_base.cli_tools.subprocess_utils import ToolsExecutor, verbose_check_call def _call_darker(*args, package_root: Path, color: bool = True, verbose: bool = False): diff --git a/manageprojects/utilities/publish.py b/manageprojects/utilities/publish.py index dc6a5f7..4cf6029 100644 --- a/manageprojects/utilities/publish.py +++ b/manageprojects/utilities/publish.py @@ -16,11 +16,11 @@ import sys +from cli_base.cli_tools.subprocess_utils import verbose_check_call, verbose_check_output from packaging.version import Version from rich import print # noqa from manageprojects.git import Git, GitError -from manageprojects.utilities.subprocess_utils import verbose_check_call, verbose_check_output def exit_with_error(txt, hint=None): diff --git a/manageprojects/utilities/subprocess_utils.py b/manageprojects/utilities/subprocess_utils.py deleted file mode 100644 index 36054ad..0000000 --- a/manageprojects/utilities/subprocess_utils.py +++ /dev/null @@ -1,246 +0,0 @@ -import os -import shutil -import subprocess -import sys -from pathlib import Path - -from bx_py_utils.path import assert_is_dir, assert_is_file -from rich import print # noqa - -from manageprojects.constants import PY_BIN_PATH - - -DEFAULT_TIMEOUT = 5 * 60 - - -def make_absolute_path(path): - """ - Resolve the path, but not for symlinks. - e.g.: '.venv/bin/python' is a symlink to e.g.: '/usr/bin/python3.x' - We would like to keep the origin symlink ;) - """ - if not isinstance(path, Path): - path = Path(path) - - if path.is_symlink(): - path = path.absolute() - else: - path = path.resolve(strict=True) - - return path - - -def make_relative_path(path, relative_to): - """ - Makes {path} relative to {relative_to}, if possible. - """ - path = make_absolute_path(path=path) - relative_to = make_absolute_path(path=relative_to) - - try: - is_relative_to = path.is_relative_to(relative_to) - except AttributeError: # is_relative_to() is new in Python 3.9 - try: - path = path.relative_to(relative_to) - except ValueError: - # {path} doesn't start with {relative_to} -> do nothing - pass - else: - if is_relative_to: - path = path.relative_to(relative_to) - - return path - - -def _print_info(popenargs, *, cwd, kwargs): - print() - print('_' * 100) - - if len(popenargs) > 1: - command, *args = popenargs - else: - command = popenargs[0] - args = [] - - command_path = make_relative_path(Path(command), relative_to=cwd) - - command_name = command_path.name - command_dir = command_path.parent - - print(f'[white]{cwd}[/white][bold]$[/bold]', end=' ') - - if command_dir and command_dir != Path.cwd(): - print(f'[green]{command_dir}{os.sep}[/green]', end='') - - if command_name: - print(f'[yellow bold]{command_name}[/yellow bold]', end='') - - for arg in args: - if isinstance(arg, str): - if arg.startswith('--'): - print(f' [magenta]{arg}[/magenta]', end='') - continue - elif arg.startswith('-'): - print(f' [cyan]{arg}[/cyan]', end='') - continue - - print(f' [blue]{arg}[/blue]', end='') - - if kwargs: - verbose_kwargs = ', '.join(f'{k}={v!r}' for k, v in sorted(kwargs.items())) - print(f' (kwargs: {verbose_kwargs})', end='') - - print('\n', flush=True) - - -def prepare_popenargs(popenargs, cwd=None): - if cwd is None: - cwd = Path.cwd() - else: - assert_is_dir(cwd) - - command_path = Path(popenargs[0]) - if not command_path.is_file(): - # Lookup in current venv bin path first: - command = shutil.which(str(command_path), path=str(PY_BIN_PATH)) - if not command: - # Search in PATH for this command that doesn't point to a existing file: - command = shutil.which(str(command_path)) - if not command: - raise FileNotFoundError(f'Command "{popenargs[0]}" not found in PATH!') - - command = make_absolute_path(path=command) - - # Replace command name with full path: - popenargs = list(popenargs) - popenargs[0] = command - - return popenargs, cwd - - -def verbose_check_call( - *popenargs, - verbose=True, - cwd=None, - extra_env=None, - exit_on_error=False, - timeout=DEFAULT_TIMEOUT, - env=None, - text=True, - **kwargs, -): - """ - 'verbose' version of subprocess.check_call() - Will try to find the command in current Python venv/bin/ path - """ - - popenargs, cwd = prepare_popenargs(popenargs, cwd=cwd) - - if verbose: - _print_info(popenargs, cwd=cwd, kwargs=kwargs) - - if env is None: - env = os.environ.copy() - - if extra_env: - env.update(extra_env) - - try: - return subprocess.check_call( - [str(part) for part in popenargs], # e.g.: Path() instance -> str, - text=text, - env=env, - cwd=cwd, - timeout=timeout, - **kwargs, - ) - except subprocess.CalledProcessError as err: - if verbose: - print(f'[red]Process "{popenargs[0]}" finished with exit code {err.returncode!r}[/red]') - if exit_on_error: - sys.exit(err.returncode) - raise - - -def verbose_check_output( - *popenargs, - verbose=True, - cwd=None, - extra_env=None, - exit_on_error=False, - print_output_on_error=True, - timeout=DEFAULT_TIMEOUT, - env=None, - text=True, - **kwargs, -): - """ - 'verbose' version of subprocess.check_output() - Will try to find the command in current Python venv/bin/ path - """ - - popenargs, cwd = prepare_popenargs(popenargs, cwd=cwd) - - if verbose: - _print_info(popenargs, cwd=cwd, kwargs=kwargs) - - if env is None: - env = os.environ.copy() - - if extra_env: - env.update(extra_env) - - try: - output = subprocess.check_output( - [str(part) for part in popenargs], # e.g.: Path() instance -> str, - text=text, - env=env, - cwd=cwd, - stderr=subprocess.STDOUT, - timeout=timeout, - **kwargs, - ) - except subprocess.CalledProcessError as err: - if print_output_on_error or exit_on_error: - if not verbose: - _print_info(popenargs, cwd=cwd, kwargs=kwargs) - print(f'[red]Process "{popenargs[0]}" finished with exit code {err.returncode!r}[/red]') - print('-' * 79) - print(err.stdout) - print('-' * 79) - if exit_on_error: - sys.exit(err.returncode) - raise - else: - return output - - -class ToolsExecutor: - def __init__(self, cwd: Path): - self.cwd = cwd - - self.extra_env = {} - - bin_path_str = str(PY_BIN_PATH) - if bin_path_str not in os.environ['PATH']: - self.extra_env['PATH'] = bin_path_str + os.pathsep + os.environ['PATH'] - - self.extra_env['PYTHONUNBUFFERED'] = '1' - - def verbose_check_output(self, bin, *popenargs, verbose=True, exit_on_error=True, **kwargs): - """'verbose' version of subprocess.check_output()""" - bin_path = PY_BIN_PATH / bin - assert_is_file(bin_path) - - try: - verbose_check_call( - bin_path, - *popenargs, - verbose=verbose, - cwd=self.cwd, - extra_env=self.extra_env, - exit_on_error=exit_on_error, - **kwargs, - ) - except subprocess.CalledProcessError: - pass diff --git a/pyproject.toml b/pyproject.toml index 7f77b5b..e896e13 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ dependencies = [ "codespell", # https://github.com/codespell-project/codespell "mypy", # https://github.com/python/mypy - "bx_py_utils", # https://github.com/boxine/bx_py_utils + "cli-base-utilities", # https://github.com/jedie/cli-base-utilities "click", # https://github.com/pallets/click/ "rich-click", # https://github.com/ewels/rich-click "rich", # https://github.com/Textualize/rich diff --git a/requirements.dev.txt b/requirements.dev.txt index 89f4f96..3e13c4c 100644 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -59,7 +59,7 @@ build==0.10.0 \ bx-py-utils==85 \ --hash=sha256:8d6ee4bb0c431304b812f5bebb1bc8e2ab05f1b6c2f8d16d352cbcee5e916cd2 \ --hash=sha256:df023fa05cda8e969d2cbdb4cc348d8b7670567a2fe775faf7a0c869ec56eaa2 - # via manageprojects (pyproject.toml) + # via cli-base-utilities cachetools==5.3.1 \ --hash=sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590 \ --hash=sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b @@ -217,11 +217,16 @@ charset-normalizer==3.2.0 \ --hash=sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac \ --hash=sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa # via requests +cli-base-utilities==0.2.0 \ + --hash=sha256:4fb89f4980c45eeeae69c7d745ddf7449740d42a072a42dd2c46ae1519ad7128 \ + --hash=sha256:5edd26c07063ab031e30712c80fd3b8a2b9dffe33143ebd25cb1bc932a55488e + # via manageprojects (pyproject.toml) click==8.1.6 \ --hash=sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd \ --hash=sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5 # via # black + # cli-base-utilities # cookiecutter # manageprojects (pyproject.toml) # pip-tools @@ -508,9 +513,9 @@ pathspec==0.11.2 \ --hash=sha256:1d6ed233af05e679efb96b1851550ea95bbb64b7c490b0f5aa52996c11e92a20 \ --hash=sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3 # via black -pip-tools==7.2.0 \ - --hash=sha256:360b8d30bc9ee2531857494afb98c41e96534f2eec9dad6500ae2a71f7c631c4 \ - --hash=sha256:616488b539e14b8aa85436ed597a33c291f4885c1d2e0bec97400abe5aff2c0d +pip-tools==7.3.0 \ + --hash=sha256:8717693288720a8c6ebd07149c93ab0be1fced0b5191df9e9decd3263e20d85e \ + --hash=sha256:8e9c99127fe024c025b46a0b2d15c7bd47f18f33226cf7330d35493663fc1d1d # via manageprojects (pyproject.toml) pkginfo==1.9.6 \ --hash=sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546 \ @@ -544,9 +549,9 @@ pyflakes==3.1.0 \ # autoflake # flake8 # manageprojects (pyproject.toml) -pygments==2.15.1 \ - --hash=sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c \ - --hash=sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1 +pygments==2.16.1 \ + --hash=sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692 \ + --hash=sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29 # via # darker # readme-renderer @@ -637,6 +642,7 @@ rich==13.5.2 \ --hash=sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808 \ --hash=sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39 # via + # cli-base-utilities # cookiecutter # manageprojects (pyproject.toml) # rich-click @@ -644,7 +650,9 @@ rich==13.5.2 \ rich-click==1.6.1 \ --hash=sha256:0fcf4d1a09029d79322dd814ab0b2e66ac183633037561881d45abae8a161d95 \ --hash=sha256:f8ff96693ec6e261d1544e9f7d9a5811c5ef5d74c8adb4978430fc0dac16777e - # via manageprojects (pyproject.toml) + # via + # cli-base-utilities + # manageprojects (pyproject.toml) ruamel-yaml==0.17.32 \ --hash=sha256:23cd2ed620231677564646b0c6a89d138b6822a0d78656df7abda5879ec4f447 \ --hash=sha256:ec939063761914e14542972a5cba6d33c23b0859ab6342f61cf070cfc600efc2 @@ -733,10 +741,12 @@ tomli==2.0.1 \ tomlkit==0.12.1 \ --hash=sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86 \ --hash=sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899 - # via manageprojects (pyproject.toml) -tox==4.6.4 \ - --hash=sha256:1b8f8ae08d6a5475cad9d508236c51ea060620126fd7c3c513d0f5c7f29cc776 \ - --hash=sha256:5e2ad8845764706170d3dcaac171704513cc8a725655219acb62fe4380bdadda + # via + # cli-base-utilities + # manageprojects (pyproject.toml) +tox==4.7.0 \ + --hash=sha256:79399a3d4641d1fd15eb6bd62c2f35923988038bf0ecf37a688b5e7a767de7d7 \ + --hash=sha256:89120e1568c763924301cfde61ba7d4b5c4615eeb1086d5370deb03e9cf63c41 # via manageprojects (pyproject.toml) twine==4.0.2 \ --hash=sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8 \ diff --git a/requirements.txt b/requirements.txt index 00326aa..cadc09d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -51,7 +51,7 @@ black==23.7.0 \ bx-py-utils==85 \ --hash=sha256:8d6ee4bb0c431304b812f5bebb1bc8e2ab05f1b6c2f8d16d352cbcee5e916cd2 \ --hash=sha256:df023fa05cda8e969d2cbdb4cc348d8b7670567a2fe775faf7a0c869ec56eaa2 - # via manageprojects (pyproject.toml) + # via cli-base-utilities certifi==2023.7.22 \ --hash=sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082 \ --hash=sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9 @@ -137,11 +137,16 @@ charset-normalizer==3.2.0 \ --hash=sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac \ --hash=sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa # via requests +cli-base-utilities==0.2.0 \ + --hash=sha256:4fb89f4980c45eeeae69c7d745ddf7449740d42a072a42dd2c46ae1519ad7128 \ + --hash=sha256:5edd26c07063ab031e30712c80fd3b8a2b9dffe33143ebd25cb1bc932a55488e + # via manageprojects (pyproject.toml) click==8.1.6 \ --hash=sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd \ --hash=sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5 # via # black + # cli-base-utilities # cookiecutter # manageprojects (pyproject.toml) # rich-click @@ -304,9 +309,9 @@ pyflakes==3.1.0 \ # autoflake # flake8 # manageprojects (pyproject.toml) -pygments==2.15.1 \ - --hash=sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c \ - --hash=sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1 +pygments==2.16.1 \ + --hash=sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692 \ + --hash=sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29 # via # darker # rich @@ -372,13 +377,16 @@ rich==13.5.2 \ --hash=sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808 \ --hash=sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39 # via + # cli-base-utilities # cookiecutter # manageprojects (pyproject.toml) # rich-click rich-click==1.6.1 \ --hash=sha256:0fcf4d1a09029d79322dd814ab0b2e66ac183633037561881d45abae8a161d95 \ --hash=sha256:f8ff96693ec6e261d1544e9f7d9a5811c5ef5d74c8adb4978430fc0dac16777e - # via manageprojects (pyproject.toml) + # via + # cli-base-utilities + # manageprojects (pyproject.toml) six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 @@ -407,7 +415,9 @@ tomli==2.0.1 \ tomlkit==0.12.1 \ --hash=sha256:38e1ff8edb991273ec9f6181244a6a391ac30e9f5098e7535640ea6be97a7c86 \ --hash=sha256:712cbd236609acc6a3e2e97253dfc52d4c2082982a88f61b640ecf0817eab899 - # via manageprojects (pyproject.toml) + # via + # cli-base-utilities + # manageprojects (pyproject.toml) typing-extensions==4.7.1 \ --hash=sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36 \ --hash=sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2