Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
  • Loading branch information
jedie committed Dec 1, 2023
commit 008d43b35e86f4c0e52fd195ea873598e6e398ba
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
__pycache__
/dist/
/coverage.*
*.orig

!.github
!.editorconfig
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ Usage: ./dev-cli.py [OPTIONS] COMMAND [ARGS]...
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─ Commands ───────────────────────────────────────────────────────────────────────────────────────╮
│ check-code-style Check code style by calling darker + flake8 │
│ coverage Run and show coverage.
│ coverage Run tests and show coverage report.
│ fix-code-style Fix code style of all manageprojects source code files via darker │
│ install Run pip-sync and install 'manageprojects' via pip as editable. │
│ mypy Run Mypy (configured in pyproject.toml) │
Expand All @@ -322,6 +322,7 @@ See also git tags: https://github.com/jedie/manageprojects/tags
[comment]: <> (✂✂✂ auto generated history start ✂✂✂)

* [**dev**](https://github.com/jedie/manageprojects/compare/v0.15.4...main)
* 2023-12-01 - Apply https://github.com/jedie/cookiecutter_templates updates
* 2023-12-01 - Use: cli_base.cli_tools.test_utils.logs.AssertLogs
* [v0.15.4](https://github.com/jedie/manageprojects/compare/v0.15.3...v0.15.4)
* 2023-11-27 - Use "flake8-bugbear", too.
Expand Down
9 changes: 9 additions & 0 deletions manageprojects/cli/cli_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from cli_base.cli_tools.verbosity import OPTION_KWARGS_VERBOSE
from cli_base.cli_tools.version_info import print_version
from rich import print # noqa
from rich.console import Console
from rich.traceback import install as rich_traceback_install
from rich_click import RichGroup

import manageprojects
Expand Down Expand Up @@ -422,6 +424,13 @@ def version():

def main():
print_version(manageprojects)
console = Console()
rich_traceback_install(
width=console.size.width, # full terminal width
show_locals=True,
suppress=[click],
max_frames=2,
)

# Execute Click CLI:
cli.name = './cli.py'
Expand Down
142 changes: 52 additions & 90 deletions manageprojects/cli/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@

import rich_click as click
from bx_py_utils.path import assert_is_file
from cli_base.cli_tools.dev_tools import run_coverage, run_tox, run_unittest_cli
from cli_base.cli_tools.subprocess_utils import verbose_check_call
from cli_base.cli_tools.test_utils.snapshot import UpdateTestSnapshotFiles
from cli_base.cli_tools.verbosity import OPTION_KWARGS_VERBOSE
from cli_base.cli_tools.version_info import print_version
from rich import print # noqa; noqa
from rich.console import Console
from rich.traceback import install as rich_traceback_install
from rich_click import RichGroup

import manageprojects
Expand Down Expand Up @@ -59,31 +63,15 @@ def cli():


@click.command()
@click.option('--verbose/--no-verbose', **OPTION_ARGS_DEFAULT_FALSE)
def mypy(verbose: bool = True):
@click.option('-v', '--verbosity', **OPTION_KWARGS_VERBOSE)
def mypy(verbosity: int):
"""Run Mypy (configured in pyproject.toml)"""
verbose_check_call('mypy', '.', cwd=PACKAGE_ROOT, verbose=verbose, exit_on_error=True)
verbose_check_call('mypy', '.', cwd=PACKAGE_ROOT, verbose=verbosity > 0, exit_on_error=True)


cli.add_command(mypy)


@click.command()
@click.option('--verbose/--no-verbose', **OPTION_ARGS_DEFAULT_FALSE)
def coverage(verbose: bool = True):
"""
Run and show coverage.
"""
verbose_check_call('coverage', 'run', verbose=verbose, exit_on_error=True)
verbose_check_call('coverage', 'combine', '--append', verbose=verbose, exit_on_error=True)
verbose_check_call('coverage', 'report', '--fail-under=30', verbose=verbose, exit_on_error=True)
verbose_check_call('coverage', 'xml', verbose=verbose, exit_on_error=True)
verbose_check_call('coverage', 'json', verbose=verbose, exit_on_error=True)


cli.add_command(coverage)


@click.command()
def install():
"""
Expand Down Expand Up @@ -163,7 +151,7 @@ def publish():
"""
Build and upload this project to PyPi
"""
_run_unittest_cli(verbose=False, exit_after_run=False) # Don't publish a broken state
run_unittest_cli(verbose=False, exit_after_run=False) # Don't publish a broken state

publish_package(
module=manageprojects,
Expand All @@ -177,116 +165,79 @@ def publish():

@click.command()
@click.option('--color/--no-color', **OPTION_ARGS_DEFAULT_TRUE)
@click.option('--verbose/--no-verbose', **OPTION_ARGS_DEFAULT_FALSE)
def fix_code_style(color: bool = True, verbose: bool = False):
@click.option('-v', '--verbosity', **OPTION_KWARGS_VERBOSE)
def fix_code_style(color: bool, verbosity: int):
"""
Fix code style of all manageprojects source code files via darker
"""
code_style.fix(package_root=PACKAGE_ROOT, color=color, verbose=verbose)
code_style.fix(package_root=PACKAGE_ROOT, color=color, verbose=verbosity > 0)


cli.add_command(fix_code_style)


@click.command()
@click.option('--color/--no-color', **OPTION_ARGS_DEFAULT_TRUE)
@click.option('--verbose/--no-verbose', **OPTION_ARGS_DEFAULT_FALSE)
def check_code_style(color: bool = True, verbose: bool = False):
@click.option('-v', '--verbosity', **OPTION_KWARGS_VERBOSE)
def check_code_style(color: bool, verbosity: int):
"""
Check code style by calling darker + flake8
"""
code_style.check(package_root=PACKAGE_ROOT, color=color, verbose=verbose)
code_style.check(package_root=PACKAGE_ROOT, color=color, verbose=verbosity > 0)


cli.add_command(check_code_style)


@click.command()
def update_test_snapshot_files():
@click.option('-v', '--verbosity', **OPTION_KWARGS_VERBOSE)
def update_test_snapshot_files(verbosity: int):
"""
Update all test snapshot files (by remove and recreate all snapshot files)
"""

def iter_snapshot_files():
yield from PACKAGE_ROOT.rglob('*.snapshot.*')

removed_file_count = 0
for item in iter_snapshot_files():
item.unlink()
removed_file_count += 1
print(f'{removed_file_count} test snapshot files removed... run tests...')

# Just recreate them by running tests:
_run_unittest_cli(
extra_env=dict(
RAISE_SNAPSHOT_ERRORS='0', # Recreate snapshot files without error
),
verbose=False,
exit_after_run=False,
)

new_files = len(list(iter_snapshot_files()))
print(f'{new_files} test snapshot files created, ok.\n')
with UpdateTestSnapshotFiles(root_path=PACKAGE_ROOT, verbose=verbosity > 0):
# Just recreate them by running tests:
run_unittest_cli(
extra_env=dict(
RAISE_SNAPSHOT_ERRORS='0', # Recreate snapshot files without error
),
verbose=verbosity > 1,
exit_after_run=False,
)


cli.add_command(update_test_snapshot_files)


def _run_unittest_cli(extra_env=None, verbose=True, exit_after_run=True):
@click.command() # Dummy command
def test():
"""
Call the origin unittest CLI and pass all args to it.
Run unittests
"""
if extra_env is None:
extra_env = dict()

extra_env.update(
dict(
PYTHONUNBUFFERED='1',
PYTHONWARNINGS='always',
)
)
run_unittest_cli()

args = sys.argv[2:]
if not args:
if verbose:
args = ('--verbose', '--locals', '--buffer')
else:
args = ('--locals', '--buffer')

verbose_check_call(
sys.executable,
'-m',
'unittest',
*args,
timeout=15 * 60,
extra_env=extra_env,
)
if exit_after_run:
sys.exit(0)
cli.add_command(test)


@click.command() # Dummy command
def test():
def coverage():
"""
Run unittests
Run tests and show coverage report.
"""
_run_unittest_cli()
run_coverage()


cli.add_command(test)


def _run_tox():
verbose_check_call(sys.executable, '-m', 'tox', *sys.argv[2:])
sys.exit(0)
cli.add_command(coverage)


@click.command() # Dummy "tox" command
def tox():
"""
Run tox
"""
_run_tox()
run_tox()


cli.add_command(tox)
Expand All @@ -305,13 +256,24 @@ def version():
def main():
print_version(manageprojects)

console = Console()
rich_traceback_install(
width=console.size.width, # full terminal width
show_locals=True,
suppress=[click],
max_frames=2,
)

if len(sys.argv) >= 2:
# Check if we just pass a command call
# Check if we can just pass a command call to origin CLI:
command = sys.argv[1]
if command == 'test':
_run_unittest_cli()
elif command == 'tox':
_run_tox()
command_map = {
'test': run_unittest_cli,
'tox': run_tox,
'coverage': run_coverage,
}
if real_func := command_map.get(command):
real_func(argv=sys.argv, exit_after_run=True)

# Execute Click CLI:
cli()
32 changes: 24 additions & 8 deletions manageprojects/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
import os
import unittest.util
from pathlib import Path

from manageprojects.utilities.log_utils import log_config
from bx_py_utils.test_utils.deny_requests import deny_any_real_request
from cli_base.cli_tools.verbosity import MAX_LOG_LEVEL, setup_logging
from rich import print # noqa


# Hacky way to expand the failed test output:
unittest.util._MAX_LENGTH = os.environ.get('UNITTEST_MAX_LENGTH', 300)
def pre_configure_tests() -> None:
print(f'Configure unittests via "load_tests Protocol" from {Path(__file__).relative_to(Path.cwd())}')

# Hacky way to display more "assert"-Context in failing tests:
_MIN_MAX_DIFF = unittest.util._MAX_LENGTH - unittest.util._MIN_DIFF_LEN
unittest.util._MAX_LENGTH = int(os.environ.get('UNITTEST_MAX_LENGTH', 300))
unittest.util._MIN_DIFF_LEN = unittest.util._MAX_LENGTH - _MIN_MAX_DIFF

log_config(
format='%(levelname)s %(name)s.%(funcName)s %(lineno)d | %(message)s',
log_in_file=False,
raise_log_output=True,
)
# Deny any request via docket/urllib3 because tests they should mock all requests:
deny_any_real_request()

# Display DEBUG logs in tests:
setup_logging(verbosity=MAX_LOG_LEVEL)


def load_tests(loader, tests, pattern):
"""
Use unittest "load_tests Protocol" as a hook to setup test environment before running tests.
https://docs.python.org/3/library/unittest.html#load-tests-protocol
"""
pre_configure_tests()
return loader.discover(start_dir=Path(__file__).parent, pattern=pattern)
4 changes: 1 addition & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,6 @@ commands_pre =
pip-sync requirements.dev.txt
commands =
{envpython} -m coverage run --context='{envname}'
{envpython} -m coverage combine --append
{envpython} -m coverage xml
{envpython} -m coverage report
"""


Expand Down Expand Up @@ -165,6 +162,7 @@ applied_migrations = [
"8d0ebe1", # 2023-08-17T18:15:10+02:00
"be3f649", # 2023-08-22T19:36:57+02:00
"385f654", # 2023-10-08T21:09:24+02:00
"d1ed4b1", # 2023-12-01T21:41:29+01:00
]

[manageprojects.cookiecutter_context.cookiecutter]
Expand Down