Skip to content

Commit

Permalink
perf: fix.build_definition.walk function now a generator
Browse files Browse the repository at this point in the history
Signed-off-by: rjdbcm <[email protected]>
  • Loading branch information
rjdbcm committed Nov 3, 2024
1 parent 441184c commit 86385ad
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 66 deletions.
13 changes: 4 additions & 9 deletions ozi_core/fix/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
"""ozi-fix: Project fix script that outputs a meson rewriter JSON array."""
from __future__ import annotations

from contextlib import redirect_stdout, suppress
import io
import json
import os
import sys
from contextlib import suppress
from pathlib import Path
from typing import TYPE_CHECKING
from typing import NoReturn
Expand All @@ -30,8 +29,8 @@
from ozi_templates.filter import underscorify # pyright: ignore
from tap_producer import TAP

from ozi_core.fix.missing import report
from ozi_core.fix.interactive import interactive_prompt
from ozi_core.fix.missing import report
from ozi_core.fix.parser import parser
from ozi_core.fix.rewrite_command import Rewriter
from ozi_core.new.validate import valid_copyright_head
Expand Down Expand Up @@ -75,9 +74,7 @@ def main(args: list[str] | None = None) -> NoReturn: # pragma: no cover
args = interactive_prompt(project)
termios.tcsetattr(fd, termios.TCSADRAIN, original_attributes)
TAP.comment(f'ozi-fix {" ".join(args)}')
json_out = io.StringIO()
with redirect_stdout(json_out):
main(args)
main(args)
case [False, True, False]:
project, _ = _setup(project)
name, *_ = report(project.target)
Expand All @@ -95,9 +92,7 @@ def main(args: list[str] | None = None) -> NoReturn: # pragma: no cover
rewriter -= project.remove
TAP.plan()
if len(project.add) > 0 or len(project.remove) > 0:
print(
json.dumps(rewriter.commands, indent=4 if project.pretty else None)
)
print(json.dumps(rewriter.commands, indent=4 if project.pretty else None))
else:
parser.print_help()
case [False, True, True]:
Expand Down
10 changes: 6 additions & 4 deletions ozi_core/fix/build_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def process(
target: Path,
rel_path: Path,
found_files: list[str] | None = None,
) -> list[str]: # pragma: no cover
) -> dict[str, list[str]]: # pragma: no cover
"""Process an OZI project build definition's files."""
try:
extra_files = [
Expand All @@ -75,7 +75,7 @@ def process(
found_files=found_files,
extra_files=extra_files,
)
return files['found'] + files['missing']
return files


def validate(
Expand Down Expand Up @@ -107,9 +107,11 @@ def walk(
rel_path: Path,
found_files: list[str] | None = None,
project_name: str | None = None,
) -> None: # pragma: no cover
) -> Generator[dict[Path, dict[str, list[str]]], None, None]: # pragma: no cover
"""Walk an OZI standard build definition directory."""
found_files = process(target, rel_path, found_files)
files = process(target, rel_path, found_files)
yield {rel_path: files}
found_files = files['found'] + files['missing']
children = list(
validate(
target,
Expand Down
12 changes: 8 additions & 4 deletions ozi_core/fix/build_definition.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,22 @@ from typing import Generator

"""Build definition check utilities."""
IGNORE_MISSING = ...
def inspect_files(target: Path, rel_path: Path, found_files: list[str], extra_files: list[str]) -> list[str]:
def inspect_files(target: Path, rel_path: Path, found_files: list[str], extra_files: list[str]) -> dict[str, list[str]]:
...

def process(target: Path, rel_path: Path, found_files: list[str] | None = ...) -> list[str]:
def process(target: Path, rel_path: Path, found_files: list[str] | None = ...) -> dict[str, list[str]]:
"""Process an OZI project build definition's files."""
...

def validate(target: Path, rel_path: Path, subdirs: list[str], children: set[str] | None) -> Generator[Path, None, None]:
"""Validate an OZI standard build definition's directories."""
...

def walk(target: Path, rel_path: Path, found_files: list[str] | None = ..., project_name: str | None = ...) -> None:
def walk(
target: Path,
rel_path: Path,
found_files: list[str] | None = None,
project_name: str | None = None,
) -> Generator[dict[Path, dict[str, list[str]]], None, None]:
"""Walk an OZI standard build definition directory."""
...

117 changes: 79 additions & 38 deletions ozi_core/fix/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@

import os
import sys
from io import UnsupportedOperation
from typing import TYPE_CHECKING
from unittest.mock import Mock

from ozi_core._i18n import TRANSLATION
from ozi_core.fix.build_definition import inspect_files
from ozi_core.fix.missing import get_relpath_expected_files
from ozi_core.ui._style import _style
from ozi_core.ui.dialog import input_dialog
from ozi_core.ui.menu import MenuButton

from prompt_toolkit.shortcuts.dialogs import button_dialog
from prompt_toolkit.shortcuts.dialogs import checkboxlist_dialog
from prompt_toolkit.shortcuts.dialogs import radiolist_dialog
from prompt_toolkit.shortcuts.dialogs import message_dialog
from prompt_toolkit.shortcuts.dialogs import radiolist_dialog
from prompt_toolkit.shortcuts.dialogs import yes_no_dialog
from tap_producer import TAP

from ozi_core._i18n import TRANSLATION
from ozi_core.fix.build_definition import walk
from ozi_core.fix.missing import get_relpath_expected_files
from ozi_core.ui._style import _style
from ozi_core.ui.menu import MenuButton

if sys.platform != 'win32':
import curses
Expand All @@ -27,6 +28,7 @@

if TYPE_CHECKING:
from argparse import Namespace
from pathlib import Path


def main_menu( # pragma: no cover
Expand Down Expand Up @@ -68,6 +70,27 @@ def main_menu( # pragma: no cover


class Prompt:
def __init__(self: Prompt, target: Path) -> None:
self.target = target
self.fix: str = ''

def set_fix_mode(
self: Prompt,
project_name: str,
output: dict[str, list[str]],
prefix: dict[str, str],
) -> tuple[list[str] | str | bool | None, dict[str, list[str]], dict[str, str]]:
self.fix = radiolist_dialog(
title=TRANSLATION('fix-dlg-title'),
text=TRANSLATION('fix-add'),
style=_style,
cancel_text=MenuButton.BACK._str,
values=[('source', 'source'), ('test', 'test'), ('root', 'root')],
).run()
if self.fix is not None:
output['fix'].append(self.fix)
return None, output, prefix

def add_or_remove(
self: Prompt,
project_name: str,
Expand Down Expand Up @@ -96,29 +119,36 @@ def add_or_remove(
style=_style,
).run():
case MenuButton.ADD.value:
add_path = radiolist_dialog(
title=TRANSLATION('fix-dlg-title'),
text=TRANSLATION('fix-add'),
style=_style,
cancel_text=MenuButton.BACK._str,
values=[('source', 'source'), ('test', 'test'), ('root', 'root')],
).run()
if add_path:
rel_path, expected = get_relpath_expected_files(add_path, project_name)
inspect_files(project.target, rel_path, expected)
input_dialog(
rel_path, _ = get_relpath_expected_files(self.fix, project_name)
files = []
with TAP.suppress():
for d in walk(self.target, rel_path, []):
for k, v in d.items():
files += [str(k / i) for i in v['missing']]
if len(files) > 0:
result = checkboxlist_dialog(
title=TRANSLATION('fix-dlg-title'),
text=''
)
add_files += []
prefix.update(
{
f'Add: {add_path}': (
f'Add: {add_path}'
),
},
)
output['--add'].append(add_path)
text='',
values=[(i, i) for i in files],
style=_style,
).run()
if result is not None:
add_files += result
prefix.update(
{
f'Add-{self.fix}: {add_files}': (
f'Add-{self.fix}: {add_files}'
),
},
)
for f in files:
output['--add'].append(f)
else:
message_dialog(
title=TRANSLATION('fix-dlg-title'),
text=f'no missing {self.fix} files',
style=_style,
).run()
case MenuButton.REMOVE.value:
if len(add_files) != 0:
del_files = checkboxlist_dialog(
Expand Down Expand Up @@ -153,18 +183,29 @@ def add_or_remove(
return None, output, prefix


def interactive_prompt(project: Namespace) -> list[str]: # pragma: no cover
def interactive_prompt(project: Namespace) -> list[str]: # pragma: no cover # noqa: C901
ret_args = ['source']
curses.setupterm()
e3 = curses.tigetstr('E3') or b''
clear_screen_seq = curses.tigetstr('clear') or b''
os.write(sys.stdout.fileno(), e3 + clear_screen_seq)
p = Prompt()
result, output, prefix = p.add_or_remove(project_name=project.name, output={}, prefix={})
try:
curses.setupterm()
e3 = curses.tigetstr('E3') or b''
clear_screen_seq = curses.tigetstr('clear') or b''
os.write(sys.stdout.fileno(), e3 + clear_screen_seq)
except UnsupportedOperation:
pass
p = Prompt(project.target)
result, output, prefix = p.set_fix_mode(
project_name=project.name, output={'fix': []}, prefix={}
)
if isinstance(result, list):
return result
result, output, prefix = p.add_or_remove(
project_name=project.name, output=output, prefix=prefix
)
if isinstance(result, list):
return result
fix = output.pop('fix')
for k, v in output.items():
for i in v:
if len(i) > 0:
ret_args += [k, i]
return ret_args + ['.']
return fix + ret_args + ['.']
1 change: 1 addition & 0 deletions ozi_core/fix/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ config_files = [
'__init__.pyi',
'build_definition.py',
'build_definition.pyi',
'interactive.py',
'missing.py',
'missing.pyi',
'parser.py',
Expand Down
9 changes: 8 additions & 1 deletion ozi_core/fix/missing.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,14 @@ def required_files(
continue # pragma: defer to https://github.com/nedbat/coveragepy/issues/198
TAP.ok(str(f))
found_files.append(file)
walk(target, rel_path, found_files=found_files, project_name=underscorify(name).lower())
list(
walk(
target,
rel_path,
found_files=found_files,
project_name=underscorify(name).lower(),
)
)
return found_files


Expand Down
6 changes: 6 additions & 0 deletions ozi_core/fix/missing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ if TYPE_CHECKING:
...
PKG_INFO = ...
readme_ext_to_content_type = ...

def get_relpath_expected_files(
kind: str,
name: str,
) -> tuple[Path, tuple[str, ...] | tuple[()]]: ...

def render_requirements(target: Path) -> str:
"""Render requirements.in as it would appear in PKG-INFO"""
...
Expand Down
7 changes: 4 additions & 3 deletions ozi_core/new/interactive/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
from prompt_toolkit.shortcuts import yes_no_dialog # pyright: ignore

from ozi_core._i18n import TRANSLATION
from ozi_core.trove import Prefix
from ozi_core.trove import from_prefix
from ozi_core.ui._style import _style
from ozi_core.ui.dialog import admonition_dialog
from ozi_core.ui.dialog import input_dialog
from ozi_core.trove import Prefix
from ozi_core.trove import from_prefix
from ozi_core.ui.menu import MenuButton, checkbox
from ozi_core.ui.menu import MenuButton
from ozi_core.ui.menu import checkbox


def main_menu( # pragma: no cover
Expand Down
1 change: 0 additions & 1 deletion ozi_core/new/interactive/menu.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ from typing import Any

from ozi_spec import METADATA


def main_menu(project: Any, output: dict[str, list[str]], prefix: dict[str, str]) -> tuple[None | list[str] | bool, dict[str, list[str]], dict[str, str]]:
...

Expand Down
8 changes: 4 additions & 4 deletions ozi_core/new/interactive/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@
from prompt_toolkit.validation import Validator # pyright: ignore

from ozi_core._i18n import TRANSLATION
from ozi_core.ui._style import _style
from ozi_core.ui.dialog import admonition_dialog
from ozi_core.ui.dialog import input_dialog
from ozi_core.ui.menu import MenuButton
from ozi_core.new.interactive.menu import main_menu
from ozi_core.new.interactive.validator import LengthValidator
from ozi_core.new.interactive.validator import NotReservedValidator
Expand All @@ -24,6 +20,10 @@
from ozi_core.new.interactive.validator import validate_message
from ozi_core.trove import Prefix
from ozi_core.trove import from_prefix
from ozi_core.ui._style import _style
from ozi_core.ui.dialog import admonition_dialog
from ozi_core.ui.dialog import input_dialog
from ozi_core.ui.menu import MenuButton

if TYPE_CHECKING: # pragma: no cover
from collections.abc import Sequence
Expand Down
5 changes: 3 additions & 2 deletions ozi_core/ui/_style.pyi
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
This type stub file was generated by pyright.
"""
from prompt_toolkit.styles import Style # pyright: ignore

_style_dict = ...
_style = ...
_style_dict: dict[str, str]
_style: Style

0 comments on commit 86385ad

Please sign in to comment.