Skip to content

Commit

Permalink
Add 'init' command to cli with separate arg parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
emdoyle committed Feb 8, 2024
1 parent 8cfcb45 commit 5f45326
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 23 deletions.
73 changes: 61 additions & 12 deletions modguard/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import os
import sys
from modguard.check import check, ErrorInfo
from modguard.init import init_project


class BCOLORS:
Expand Down Expand Up @@ -39,17 +40,37 @@ def print_invalid_exclude(path: str) -> None:
)


def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
def parse_base_arguments(args) -> argparse.Namespace:
base_parser = argparse.ArgumentParser(
prog="modguard",
description="Verify module boundaries are correctly implemented.",
add_help=True,
epilog="Make sure modguard is run from the root of your repo that a directory is being specified. For example: `modguard .`",
)
base_parser.add_argument(
"path",
type=str,
help="The path of the root of your project that contains all defined boundaries.",
)
base_parser.add_argument(
"-e",
"--exclude",
required=False,
type=str,
metavar="file_or_path,...",
help="Comma separated path list to exclude. tests/,ci/,etc.",
)
return base_parser.parse_args(args)


def parse_init_arguments(args) -> argparse.Namespace:
parser = argparse.ArgumentParser(
prog="modguard init",
description="Initialize boundaries in a repository with modguard",
)
parser.add_argument(
"path",
type=str,
help="The path of the root of your project that contains all defined boundaries.",
help="The path of the Python project in which boundaries should be initialized.",
)
parser.add_argument(
"-e",
Expand All @@ -59,10 +80,11 @@ def build_parser() -> argparse.ArgumentParser:
metavar="file_or_path,...",
help="Comma separated path list to exclude. tests/,ci/,etc.",
)
return parser

return parser.parse_args(args)


def main(args: argparse.Namespace):
def handle_shared_arguments(args: argparse.Namespace):
path = args.path
if not os.path.isdir(path):
print_invalid_path(path)
Expand All @@ -80,19 +102,46 @@ def main(args: argparse.Namespace):
print_invalid_exclude(exclude_path)
if has_error:
sys.exit(1)
result: list[ErrorInfo] = check(path, exclude_paths=exclude_paths)

return argparse.Namespace(path=path, exclude_paths=exclude_paths)


def modguard(args: argparse.Namespace):
shared_args = handle_shared_arguments(args)
try:
result: list[ErrorInfo] = check(
shared_args.path, exclude_paths=shared_args.exclude_paths
)
except Exception as e:
print(str(e))
sys.exit(1)

if result:
print_errors(result)
sys.exit(1)
print(f"✅ {BCOLORS.OKGREEN}All modules safely guarded!")
sys.exit(0)


def modguard() -> None:
parser = build_parser()
args = parser.parse_args()
main(args)
def modguard_init(args: argparse.Namespace):
shared_args = handle_shared_arguments(args)

try:
init_project(shared_args.path, exclude_paths=shared_args.exclude_paths)
except Exception as e:
print(str(e))
sys.exit(1)

print(f"✅ {BCOLORS.OKGREEN}Modguard initialized.")
sys.exit(0)


def main() -> None:
if len(sys.argv) > 1 and sys.argv[1] == "init":
modguard_init(parse_init_arguments(sys.argv[2:]))
else:
modguard(parse_base_arguments(sys.argv[1:]))


if __name__ == "__main__":
modguard()
main()
15 changes: 11 additions & 4 deletions modguard/init.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os
import errors
from . import errors
from .check import check_import
from .core import PublicMember
from .parsing import utils
Expand All @@ -21,7 +21,9 @@ def init_project(root: str, exclude_paths: list[str] = None):
exclude_paths = list(map(utils.canonical, exclude_paths)) if exclude_paths else None

boundary_trie = build_boundary_trie(root, exclude_paths=exclude_paths)
initial_boundary_paths = [boundary.full_path for boundary in boundary_trie]
initial_boundary_paths = [
boundary.full_path for boundary in boundary_trie if boundary.full_path
]

for dirpath in utils.walk_pypackages(root, exclude_paths=exclude_paths):
added_boundary = ensure_boundary(dirpath + "/__init__.py")
Expand Down Expand Up @@ -58,5 +60,10 @@ def init_project(root: str, exclude_paths: list[str] = None):
continue

file_path, member_name = utils.module_to_file_path(import_mod_path)
mark_as_public(file_path, member_name)
violated_boundary.add_public_member(PublicMember(name=import_mod_path))
try:
mark_as_public(file_path, member_name)
violated_boundary.add_public_member(PublicMember(name=import_mod_path))
except errors.ModguardParseError:
print(
f"Skipping member {member_name} in {file_path}; could not mark as public"
)
10 changes: 5 additions & 5 deletions modguard/parsing/public.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def _public_module_prelude(should_import: bool = True) -> str:


IMPORT_MODGUARD = "import modguard"
PUBLIC_DECORATOR = "@public"
PUBLIC_DECORATOR = "@modguard.public"


@public
Expand Down Expand Up @@ -204,13 +204,13 @@ def mark_as_public(file_path: str, member_name: str = ""):
)

with open(file_path, "w") as file:
file_lines = file_content.splitlines()
file_lines = file_content.splitlines(keepends=True)
lines_to_write = [
*file_lines[: member_finder.matched_lineno - 1],
PUBLIC_DECORATOR,
PUBLIC_DECORATOR + "\n",
*file_lines[member_finder.matched_lineno - 1 :],
]
if not modguard_public_is_imported:
lines_to_write = [IMPORT_MODGUARD, *lines_to_write]
lines_to_write = [IMPORT_MODGUARD + "\n", *lines_to_write]

file.write("\n".join(lines_to_write))
file.write("".join(lines_to_write))
2 changes: 1 addition & 1 deletion modguard/parsing/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def module_to_file_path(
last_sep_index = fs_path.rfind(os.path.sep)
file_path = fs_path[:last_sep_index] + ".py"
if os.path.exists(file_path):
member_name = fs_path[last_sep_index:]
member_name = fs_path[last_sep_index + 1 :]
return file_path, member_name

raise errors.ModguardParseError(
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,4 @@ requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[project.scripts]
modguard = "modguard.cli:modguard"
modguard = "modguard.cli:main"

0 comments on commit 5f45326

Please sign in to comment.