From 7ef5313d73370e556a9020458c8ead71c704157d Mon Sep 17 00:00:00 2001 From: vlitvintseva <24_LitvintsevaVD@iux.local> Date: Sun, 6 Sep 2026 22:58:22 +0300 Subject: [PATCH] feat: initial commit with CLI, core logic and tests --- .gitignore | 5 +++ README.md | 9 +++++ pyproject.toml | 22 ++++++++++++ requirements.txt | 0 src/text_analyzer/__init__.py | 0 src/text_analyzer/cli.py | 40 ++++++++++++++++++++++ src/text_analyzer/core.py | 28 +++++++++++++++ tests/__init__.py | 0 tests/test_core.py | 64 +++++++++++++++++++++++++++++++++++ 9 files changed, 168 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 src/text_analyzer/__init__.py create mode 100644 src/text_analyzer/cli.py create mode 100644 src/text_analyzer/core.py create mode 100644 tests/__init__.py create mode 100644 tests/test_core.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9b5d64 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.venv/ +__pycache__/ +*.egg-info/ +.pytest_cache/ +.coverage diff --git a/README.md b/README.md new file mode 100644 index 0000000..52f5c46 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Text Analyzer CLI + +CLI-инструмент на Python для анализа текстовых файлов (подсчёт строк, слов, символов и топ-N популярных слов). + +## Установка + +1. Клонируйте репозиторий и перейдите в папку проекта: +```bash +cd lb_1 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..768f9fa --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "text_analyzer" +version = "0.1.0" +description = "CLI tool for analyzing text files" +requires-python = ">=3.12" +dependencies = [] + +[project.scripts] +text-analyzer = "text_analyzer.cli:main" + +[tool.ruff] +line-length = 88 + +[tool.mypy] +strict = true + +[tool.pytest.ini_options] +testpaths = ["tests"] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/text_analyzer/__init__.py b/src/text_analyzer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/text_analyzer/cli.py b/src/text_analyzer/cli.py new file mode 100644 index 0000000..9ca9d15 --- /dev/null +++ b/src/text_analyzer/cli.py @@ -0,0 +1,40 @@ +import argparse +import json +import sys +from pathlib import Path + +from text_analyzer.core import analyze_file + + +def main() -> None: + parser = argparse.ArgumentParser(description="Text Analyzer CLI tool") + parser.add_argument("file", type=Path, help="Path to text file") + parser.add_argument("--top", type=int, default=5, help="Top N words") + parser.add_argument("--min-len", type=int, default=1, help="Minimum word length") + parser.add_argument( + "--format", choices=["text", "json"], default="text", help="Output format" + ) + + args = parser.parse_args() + + try: + results = analyze_file(args.file, top_n=args.top, min_len=args.min_len) + except FileNotFoundError as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + if args.format == "json": + print(json.dumps(results, indent=2)) + else: + print(f"Lines: {results['lines']}") + print(f"Words: {results['words']}") + print(f"Chars: {results['chars']}") + print("Top words:") + top_words = results["top_words"] + if isinstance(top_words, list): + for word, count in top_words: + print(f" {word}: {count}") + + +if __name__ == "__main__": + main() diff --git a/src/text_analyzer/core.py b/src/text_analyzer/core.py new file mode 100644 index 0000000..b5c2c49 --- /dev/null +++ b/src/text_analyzer/core.py @@ -0,0 +1,28 @@ +from collections import Counter +from pathlib import Path + + +def analyze_file( + file_path: Path, top_n: int = 5, min_len: int = 1 +) -> dict[str, object]: + # Чтение файла и сбор базовой статистики + if not file_path.exists(): + raise FileNotFoundError(f"File not found: {file_path}") + + text = file_path.read_text(encoding="utf-8") + lines = text.splitlines() + words = [ + w.lower().strip(".,!?;:\"'()") + for w in text.split() + if len(w.strip(".,!?;:\"'()")) >= min_len + ] + + counter = Counter(words) + top_words = counter.most_common(top_n) + + return { + "lines": len(lines), + "words": len(words), + "chars": len(text), + "top_words": top_words, + } diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_core.py b/tests/test_core.py new file mode 100644 index 0000000..28774db --- /dev/null +++ b/tests/test_core.py @@ -0,0 +1,64 @@ +from pathlib import Path + +import pytest + +from text_analyzer.core import analyze_file + + +def test_basic_analysis(tmp_path: Path) -> None: + test_file = tmp_path / "test.txt" + test_file.write_text("hello world\nhello python") + + res = analyze_file(test_file) + assert res["lines"] == 2 + assert res["words"] == 4 + assert res["top_words"] == [("hello", 2), ("world", 1), ("python", 1)] + + +def test_file_not_found() -> None: + with pytest.raises(FileNotFoundError): + analyze_file(Path("non_existent_file_123.txt")) + + +def test_empty_file(tmp_path: Path) -> None: + test_file = tmp_path / "empty.txt" + test_file.write_text("") + + res = analyze_file(test_file) + assert res["lines"] == 0 + assert res["words"] == 0 + assert res["top_words"] == [] + + +@pytest.mark.parametrize( + "min_len,expected_words", + [ + (1, 4), + (3, 2), + (4, 1), + ], +) +def test_min_length_filter(tmp_path: Path, min_len: int, expected_words: int) -> None: + test_file = tmp_path / "filter.txt" + test_file.write_text("a ab abc abcd") + + res = analyze_file(test_file, min_len=min_len) + assert res["words"] == expected_words + + +def test_top_n_param(tmp_path: Path) -> None: + test_file = tmp_path / "top.txt" + test_file.write_text("one two two three three three") + + res = analyze_file(test_file, top_n=2) + top_words = res["top_words"] + assert isinstance(top_words, list) + assert len(top_words) == 2 + + +def test_punctuation_stripping(tmp_path: Path) -> None: + test_file = tmp_path / "punct.txt" + test_file.write_text("hello, world! hello...") + + res = analyze_file(test_file) + assert res["top_words"] == [("hello", 2), ("world", 1)]