Skip to content

Commit

Permalink
🔨 refactor UI to add an ozi-fix interactive mode
Browse files Browse the repository at this point in the history
Signed-off-by: rjdbcm <[email protected]>
  • Loading branch information
rjdbcm committed Oct 31, 2024
1 parent fd1f0e7 commit 4734d92
Show file tree
Hide file tree
Showing 21 changed files with 1,066 additions and 247 deletions.
541 changes: 541 additions & 0 deletions ozi_core/_locales.py

Large diffs are not rendered by default.

15 changes: 8 additions & 7 deletions ozi_core/data/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ btn-ok: "✔ Ok"
btn-prompt: "⌂ Prompt"
btn-skip: "⇒ Skip"
btn-yes: "Yes"
dlg-title: ozi-new interactive prompt
new-dlg-title: ozi-new interactive prompt
fix-dlg-title: ozi-fix interactive prompt
edit-menu-btn-audience: Intended Audience
edit-menu-btn-author: Author
edit-menu-btn-email: Author-email
Expand Down Expand Up @@ -66,10 +67,10 @@ err-too-long: input is too long.
err-version-check: OZI package version check failed with status code $status
lang-en: English
lang-zh: 简体中文 Chinese (Simplified)
main-menu-btn-edit: "✎ Edit"
main-menu-btn-metadata: "∋ Metadata"
main-menu-btn-options: "⚙ Options"
main-menu-btn-reset: "↺ Reset"
btn-edit: "✎ Edit"
btn-metadata: "∋ Metadata"
btn-options: "⚙ Options"
btn-reset: "↺ Reset"
main-menu-text: "Main menu, select an action:"
main-menu-yn-exit: Exit the prompt?
main-menu-yn-reset: Reset prompt and start over?
Expand Down Expand Up @@ -143,10 +144,10 @@ pro-readme-type-radio-md: Markdown
pro-readme-type-radio-rst: reStructuredText
pro-readme-type-radio-txt: Plaintext
pro-requires-dist: Add or remove dependency requirements to $projectname
pro-requires-dist-add: Add
btn-add: Add
pro-requires-dist-cbl-remove: "Select packages to delete:"
pro-requires-dist-msg-remove-no-requirements: No requirements to remove.
pro-requires-dist-remove: Remove
btn-remove: Remove
pro-requires-dist-search: "Search PyPI packages:"
pro-summary: >
What does the project, $projectname, do?
Expand Down
15 changes: 8 additions & 7 deletions ozi_core/data/zh.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ btn-ok: 好的
btn-prompt: 提示符
btn-skip: 跳过
btn-yes: 是的
dlg-title: ozi-new 互 動 提 示 符
new-dlg-title: ozi-new 互 動 提 示 符
fix-dlg-title: ozi-fix 互 動 提 示 符
edit-menu-btn-audience: 目标受众(Intended Audience)
edit-menu-btn-author: 作者(Author)
edit-menu-btn-email: 电子邮件(Author-email)
Expand Down Expand Up @@ -56,10 +57,10 @@ err-too-long: 输入太长。
err-version-check: OZI软件包版本检查失败,状态代码为:$status
lang-en: 英语(English)
lang-zh: 简体中文
main-menu-btn-edit: 编辑
main-menu-btn-metadata: 查看元数据
main-menu-btn-options: 更改选项
main-menu-btn-reset: 重置
btn-edit: 编辑
btn-metadata: 查看元数据
btn-options: 更改选项
btn-reset: 重置
main-menu-text: 選單,选择动作:
main-menu-yn-exit: 退出提示符?
main-menu-yn-reset: 重置提示并重新开始?
Expand Down Expand Up @@ -131,10 +132,10 @@ pro-readme-type-radio-md: 轻量级标记语言Markdown
pro-readme-type-radio-rst: 轻量级标记语言reStructuredText
pro-readme-type-radio-txt: 纯文本
pro-requires-dist: 新增或删除对『$projectname』的依赖关系要求。
pro-requires-dist-add: 新增
btn-add: 新增
pro-requires-dist-cbl-remove: 选择要删除的包:
pro-requires-dist-msg-remove-no-requirements: 没有删除请求。
pro-requires-dist-remove: 删除
btn-remove: 删除
pro-requires-dist-search: 搜寻PyPI包:
pro-summary: >
项目『$projectname』是做什么的?
Expand Down
98 changes: 65 additions & 33 deletions ozi_core/fix/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,33 @@
"""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 pathlib import Path
from typing import TYPE_CHECKING
from typing import NoReturn
from unittest.mock import Mock

if sys.platform != 'win32':
import termios
import tty
else: # pragma: no cover
tty = Mock()
termios = Mock()
tty.setraw = lambda x: None
termios.tcgetattr = lambda x: None
termios.tcsetattr = lambda x, y, z: None

from ozi_spec import METADATA
from ozi_templates import load_environment
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.parser import parser
from ozi_core.fix.rewrite_command import Rewriter
from ozi_core.new.validate import valid_copyright_head
Expand All @@ -34,46 +49,63 @@ def _setup(project: Namespace) -> tuple[Namespace, Environment]: # pragma: no c
TAP.bail_out(f'target: {project.target} does not exist.')
elif not project.target.is_dir():
TAP.bail_out(f'target: {project.target} is not a directory.')
project.add.remove('ozi.phony')
with suppress(ValueError):
project.add.remove('ozi.phony')
project.remove.remove('ozi.phony')
project.add = list(set(project.add))
project.remove.remove('ozi.phony')
project.remove = list(set(project.remove))
env = load_environment(vars(project), METADATA.asdict()) # pyright: ignore
return project, env


def main() -> NoReturn: # pragma: no cover
def main(args: list[str] | None = None) -> NoReturn: # pragma: no cover
"""Main ozi.fix entrypoint."""
project = parser.parse_args()
project.missing = project.fix == 'missing' or project.fix == 'm'
with TAP() as t:
match [project.missing, project.strict]:
case [True, False]:
project = parser.parse_args(args=args)
project.missing = project.fix in {'missing', 'm', 'mis'}
project.interactive = project.fix in {'interactive', 'i'}
match [project.interactive, project.missing, project.strict]:
case [True, False, _]:
with TAP.suppress(): # pyright: ignore
project, env = _setup(project)
name, *_ = report(project.target)
project.name = underscorify(name)
fd = sys.stdin.fileno()
original_attributes = termios.tcgetattr(fd)
tty.setraw(sys.stdin)
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)
case [False, True, False]:
project, _ = _setup(project)
name, *_ = report(project.target)
case [False, False, _]:
with TAP.suppress(): # pyright: ignore
project, env = _setup(project)
name, *_ = report(project.target)
project.name = underscorify(name)
project.license_file = 'LICENSE.txt'
project.copyright_head = valid_copyright_head(
project.copyright_head, name, project.license_file
)
rewriter = Rewriter(str(project.target), project.name, project.fix, env)
rewriter += project.add
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)
)
else:
parser.print_help()
case [False, True, True]:
with TAP.strict(): # pyright: ignore
project, _ = _setup(project)
name, *_ = report(project.target)
case [False, _]:
with t.suppress(): # pyright: ignore
project, env = _setup(project)
name, *_ = report(project.target)
project.name = underscorify(name)
project.license_file = 'LICENSE.txt'
project.copyright_head = valid_copyright_head(
project.copyright_head, name, project.license_file
)
rewriter = Rewriter(str(project.target), project.name, project.fix, env)
rewriter += project.add
rewriter -= project.remove
t.plan()
if len(project.add) > 0 or len(project.remove) > 0:
print(
json.dumps(rewriter.commands, indent=4 if project.pretty else None)
)
else:
parser.print_help()
case [True, True]:
with t.strict(): # pyright: ignore
project, _ = _setup(project)
name, *_ = report(project.target)
case [_, _]:
t.bail_out('Name discovery failed.')
case [True, True, _]:
TAP.bail_out('subcommands `interactive` and `missing` are mutually exclusive')
case [_, _, _]:
TAP.bail_out('Name discovery failed.')
exit(0)
40 changes: 20 additions & 20 deletions ozi_core/fix/build_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ def inspect_files(
rel_path: Path,
found_files: list[str],
extra_files: list[str],
) -> list[str]: # pragma: no cover
) -> dict[str, list[str]]: # pragma: no cover
build_files = [str(rel_path / 'meson.build'), str(rel_path / 'meson.options')]
_found_files = []
_found_files = {'found': [], 'missing': []}
for file in extra_files:
found_literal = query_build_value(
str(target / rel_path),
Expand All @@ -43,11 +43,11 @@ def inspect_files(
TAP.ok(f'{build_file} {TRANSLATION("term-found")} {rel_path / file}')
build_files += [str(rel_path / file)]
comment.comment_diagnostic(target, rel_path, file)
_found_files.append(file)
_found_files['found'].append(file)
if str(rel_path / file) not in build_files and file not in found_files:
build_file = str(rel_path / 'meson.build')
TAP.not_ok(f'{build_file} {TRANSLATION("term-missing")} {rel_path / file}')
_found_files.append(file)
_found_files['missing'].append(file)
return _found_files


Expand All @@ -69,12 +69,13 @@ def process(
extra_files = []
found_files = found_files if found_files else []
extra_files = list(set(extra_files).symmetric_difference(set(found_files)))
return inspect_files(
files = inspect_files(
target=target,
rel_path=rel_path,
found_files=found_files,
extra_files=extra_files,
)
return files['found'] + files['missing']


def validate(
Expand All @@ -85,21 +86,20 @@ def validate(
) -> Generator[Path, None, None]: # pragma: no cover
"""Validate an OZI standard build definition's directories."""
for directory in subdirs:
match directory, children:
case [directory, _] if directory not in IGNORE_MISSING:
TAP.ok(
str(rel_path / 'meson.build'),
TRANSLATION('term-subdir'),
str(directory),
)
yield Path(rel_path / directory)
case [directory, _]:
TAP.ok(
str(rel_path / 'meson.build'),
TRANSLATION('term-missing'),
str(directory),
skip=True,
)
if directory not in IGNORE_MISSING:
TAP.ok(
str(rel_path / 'meson.build'),
TRANSLATION('term-subdir'),
str(directory),
)
yield Path(rel_path / directory)
else:
TAP.ok(
str(rel_path / 'meson.build'),
TRANSLATION('term-missing'),
str(directory),
skip=True,
)


def walk(
Expand Down
Loading

0 comments on commit 4734d92

Please sign in to comment.