Skip to content

Commit

Permalink
3.0.1 clean up and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewdeanmartin committed Jan 20, 2024
1 parent c89078a commit e1b2e32
Show file tree
Hide file tree
Showing 65 changed files with 1,756 additions and 653 deletions.
16 changes: 10 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,30 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.0.1] - 2024-01-19

### Fixed
- More unit tests, fix link in meta, uses Path instead of str for paths

## [3.0.0] - 2024-01-19

### Fixed
- only truncate long version on console output
- removed three dependencies (toml, hypothesis, importlib-metadata)

### Added
- filter an audit by tags
- install_docs, install_command to show what to do when there are problems
- `single` to validate one tool without a config file
- `.html` output

## Fixed
- only truncate long version on console output
- removed three dependencies (toml, hypothesis, importlib-metadata)


## [2.0.0] - 2024-01-19

### Fixed
- Cache now adds gitingore and clears files older than 30 days on startup
- Audit is now a sub command with arguments.

### Breaking
### Changed
- Gnu options with dashes, no underscores
- Global opts that apply to only some commands move to subparser
- check_only_for_existence is now schema type "existence" snapshot_version is now schema type "snapshot"
Expand Down
12 changes: 8 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ clean: clean-pyc clean-test

# tests can't be expected to pass if dependencies aren't installed.
# tests are often slow and linting is fast, so run tests on linted code.
test: clean .build_history/pylint .build_history/bandit poetry.lock
test: clean poetry.lock
@echo "Running unit tests"
$(VENV) pytest --doctest-modules cli_tool_audit
$(VENV) python -m unittest discover
$(VENV) py.test tests --cov=cli_tool_audit --cov-report=html --cov-fail-under 63
$(VENV) py.test tests -vv -n auto --cov=cli_tool_audit --cov-report=html --cov-fail-under 75
$(VENV) bash basic_test.sh


Expand Down Expand Up @@ -123,8 +123,12 @@ check_changelog:
# pipx install keepachangelog-manager
$(VENV) changelogmanager validate

check_all: check_docs check_md check_spelling
check_all: check_docs check_md check_spelling check_changelog audit

check_own_ver:
# Can it verify itself?
$(VENV) python dog_food_check.py
$(VENV) python dog_food_check.py

audit:
# $(VENV) python -m cli_tool_audit audit
$(VENV) tool_audit single cli_tool_audit --version=">=2.0.0"
2 changes: 1 addition & 1 deletion cli_tool_audit/__about__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
]

__title__ = "cli_tool_audit"
__version__ = "3.0.0"
__version__ = "3.0.1"
__description__ = "Audit for existence and version number of cli tools."
__author__ = "Matthew Martin"
__author_email__ = "matthewdeanmartin@gmail.com"
Expand Down
19 changes: 10 additions & 9 deletions cli_tool_audit/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@
import sys
from collections.abc import Sequence
from dataclasses import fields
from pathlib import Path
from typing import Any, Optional

import cli_tool_audit.config_manager as config_manager
import cli_tool_audit.freeze as freeze
import cli_tool_audit.interactive as interactive
import cli_tool_audit.logging_config as logging_config
import cli_tool_audit.models as models
import cli_tool_audit.view_npm_stress_test as demo_npm
import cli_tool_audit.view_pipx_stress_test as demo_pipx
import cli_tool_audit.view_venv_stress_test as demo_venv
import cli_tool_audit.views as views
from cli_tool_audit.__about__ import __description__, __version__
from cli_tool_audit.models import CliToolConfig

logger = logging.getLogger(__name__)

Expand All @@ -29,7 +30,7 @@ def handle_read(args: argparse.Namespace) -> None:
Args:
args: The args from the command line.
"""
manager = config_manager.ConfigManager(args.config)
manager = config_manager.ConfigManager(Path(args.config))
manager.read_config()
for tool, config in manager.tools.items():
print(f"{tool}")
Expand All @@ -46,7 +47,7 @@ def handle_create(args: argparse.Namespace) -> None:
"""
kwargs = reduce_args_tool_cli_tool_config_args(args)

manager = config_manager.ConfigManager(args.config)
manager = config_manager.ConfigManager(Path(args.config))
manager.create_tool_config(args.tool, kwargs)
print(f"Tool {args.tool} created.")

Expand All @@ -62,7 +63,7 @@ def reduce_args_tool_cli_tool_config_args(args: argparse.Namespace) -> dict[str,
"""
kwargs = {}
args_dict = vars(args)
cli_tool_fields = {f.name for f in fields(config_manager.CliToolConfig)}
cli_tool_fields = {f.name for f in fields(models.CliToolConfig)}
for key, value in args_dict.items():
if key in cli_tool_fields:
kwargs[key] = value
Expand All @@ -71,7 +72,7 @@ def reduce_args_tool_cli_tool_config_args(args: argparse.Namespace) -> dict[str,

def handle_update(args: argparse.Namespace) -> None:
kwargs = reduce_args_tool_cli_tool_config_args(args)
manager = config_manager.ConfigManager(args.config)
manager = config_manager.ConfigManager(Path(args.config))
manager.update_tool_config(args.tool, {k: v for k, v in kwargs.items() if k != "tool"})
print(f"Tool {args.tool} updated.")

Expand All @@ -82,7 +83,7 @@ def handle_delete(args: argparse.Namespace) -> None:
Args:
args: The args from the command line.
"""
manager = config_manager.ConfigManager(args.config)
manager = config_manager.ConfigManager(Path(args.config))
manager.delete_tool_config(args.tool)
print(f"Tool {args.tool} deleted.")

Expand All @@ -93,7 +94,7 @@ def handle_interactive(args: argparse.Namespace) -> None:
Args:
args: The args from the command line.
"""
manager = config_manager.ConfigManager(args.config)
manager = config_manager.ConfigManager(Path(args.config))
interactive.interactive_config_manager(manager)


Expand All @@ -119,7 +120,7 @@ def handle_audit(args: argparse.Namespace) -> None:
args: The args from the command line.
"""
views.report_from_pyproject_toml(
file_path=args.config,
file_path=Path(args.config),
exit_code_on_failure=not args.never_fail,
file_format=args.format,
no_cache=args.no_cache,
Expand All @@ -135,7 +136,7 @@ def handle_single(args):
Args:
args: The args from the command line.
"""
config = CliToolConfig(
config = models.CliToolConfig(
name=args.tool, version=args.version, version_switch=args.version_switch, schema=args.schema, if_os=args.if_os
)
views.report_from_pyproject_toml(
Expand Down
40 changes: 21 additions & 19 deletions cli_tool_audit/audit_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
from typing import Any, Optional

import cli_tool_audit.audit_manager as audit_manager
from cli_tool_audit.json_utils import custom_json_serializer
from cli_tool_audit.models import CliToolConfig, SchemaType, ToolCheckResult
import cli_tool_audit.json_utils as json_utils
import cli_tool_audit.models as models

__all__ = ["AuditFacade"]


def custom_json_deserializer(data: dict[str, Any]) -> dict[str, Any]:
Expand All @@ -28,23 +30,23 @@ def custom_json_deserializer(data: dict[str, Any]) -> dict[str, Any]:
if "tool_config" in data and data["tool_config"]:
for key, value in data["tool_config"].items():
if isinstance(value, str) and key == "schema":
data["tool_config"][key] = SchemaType(value)
data["tool_config"] = CliToolConfig(**data["tool_config"])
data["tool_config"][key] = models.SchemaType(value)
data["tool_config"] = models.CliToolConfig(**data["tool_config"])
return data


logger = logging.getLogger(__name__)


class AuditFacade:
def __init__(self, cache_dir: Optional[str] = None) -> None:
def __init__(self, cache_dir: Optional[Path] = None) -> None:
"""
Initialize the facade.
Args:
cache_dir (Optional[str], optional): The directory to use for caching. Defaults to None.
"""
self.audit_manager = audit_manager.AuditManager()
self.cache_dir = Path(cache_dir) if cache_dir else Path.cwd() / ".cli_tool_audit_cache"
self.cache_dir = cache_dir if cache_dir else Path.cwd() / ".cli_tool_audit_cache"
self.cache_dir.mkdir(exist_ok=True)
with open(self.cache_dir / ".gitignore", "w", encoding="utf-8") as file:
file.write("*\n!.gitignore\n")
Expand All @@ -64,11 +66,11 @@ def clear_old_cache_files(self) -> None:
if cache_file.exists():
cache_file.unlink(missing_ok=True) # Delete the file

def get_cache_filename(self, tool_config: CliToolConfig) -> Path:
def get_cache_filename(self, tool_config: models.CliToolConfig) -> Path:
"""
Get the cache filename for the given tool.
Args:
tool_config (CliToolConfig): The tool to get the cache filename for.
tool_config (models.CliToolConfig): The tool to get the cache filename for.
Returns:
Path: The cache filename.
Expand All @@ -77,21 +79,21 @@ def get_cache_filename(self, tool_config: CliToolConfig) -> Path:
the_hash = tool_config.cache_hash()
return self.cache_dir / f"{sanitized_name}_{the_hash}.json"

def read_from_cache(self, tool_config: CliToolConfig) -> Optional[ToolCheckResult]:
def read_from_cache(self, tool_config: models.CliToolConfig) -> Optional[models.ToolCheckResult]:
"""
Read the cached result for the given tool.
Args:
tool_config (CliToolConfig): The tool to get the cached result for.
tool_config (models.CliToolConfig): The tool to get the cached result for.
Returns:
Optional[ToolCheckResult]: The cached result or None if not found.
Optional[models.ToolCheckResult]: The cached result or None if not found.
"""
cache_file = self.get_cache_filename(tool_config)
if cache_file.exists():
logger.debug(f"Cache hit for {tool_config.name}")
try:
with open(cache_file, encoding="utf-8") as file:
hit = ToolCheckResult(**json.load(file, object_hook=custom_json_deserializer))
hit = models.ToolCheckResult(**json.load(file, object_hook=custom_json_deserializer))
self.cache_hit = True
return hit
except TypeError:
Expand All @@ -102,26 +104,26 @@ def read_from_cache(self, tool_config: CliToolConfig) -> Optional[ToolCheckResul
self.cache_hit = False
return None

def write_to_cache(self, tool_config: CliToolConfig, result: ToolCheckResult) -> None:
def write_to_cache(self, tool_config: models.CliToolConfig, result: models.ToolCheckResult) -> None:
"""
Write the given result to the cache.
Args:
tool_config (CliToolConfig): The tool to write the result for.
result (ToolCheckResult): The result to write.
tool_config (models.CliToolConfig): The tool to write the result for.
result (models.ToolCheckResult): The result to write.
"""
cache_file = self.get_cache_filename(tool_config)
with open(cache_file, "w", encoding="utf-8") as file:
logger.debug(f"Caching {tool_config.name}")
json.dump(result.__dict__, file, ensure_ascii=False, indent=4, default=custom_json_serializer)
json.dump(result.__dict__, file, ensure_ascii=False, indent=4, default=json_utils.custom_json_serializer)

def call_and_check(self, tool_config: CliToolConfig) -> ToolCheckResult:
def call_and_check(self, tool_config: models.CliToolConfig) -> models.ToolCheckResult:
"""
Call and check the given tool.
Args:
tool_config (CliToolConfig): The tool to call and check.
tool_config (models.CliToolConfig): The tool to call and check.
Returns:
ToolCheckResult: The result of the check.
models.ToolCheckResult: The result of the check.
"""
cached_result = self.read_from_cache(tool_config)
if cached_result:
Expand Down
Loading

0 comments on commit e1b2e32

Please sign in to comment.