Add utils, templates, config, interactive, and github modules
Some checks failed
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled
CI / release (push) Has been cancelled

This commit is contained in:
2026-02-05 08:45:40 +00:00
parent 260032642e
commit f2d988ce97

View File

@@ -0,0 +1,190 @@
"""Path utilities for the Auto README Generator."""
import os
import re
from pathlib import Path
from typing import Optional
class PathUtils:
"""Utility class for path operations."""
IGNORED_DIRS = {
"__pycache__",
".git",
".svn",
".hg",
"__MACOSX",
".DS_Store",
"node_modules",
".venv",
"venv",
"ENV",
"env",
".tox",
".nox",
"build",
"dist",
"target",
".idea",
".vscode",
}
IGNORED_FILES = {
".DS_Store",
".gitignore",
".gitattributes",
".gitmodules",
}
SOURCE_EXTENSIONS = {
".py",
".js",
".ts",
".jsx",
".tsx",
".go",
".rs",
".java",
".c",
".cpp",
".h",
".hpp",
".rb",
".php",
".swift",
".kt",
".scala",
}
CONFIG_EXTENSIONS = {".json", ".yaml", ".yml", ".toml", ".cfg", ".ini", ".conf"}
DOCUMENTATION_EXTENSIONS = {".md", ".rst", ".txt", ".adoc"}
TEST_PATTERNS = [
re.compile(r"^test_"),
re.compile(r"_test\.py$"),
re.compile(r"^tests?\/"),
re.compile(r"spec\."),
re.compile(r"\.test\."),
re.compile(r"\.tests\."),
]
@classmethod
def normalize_path(cls, path: str | Path) -> Path:
"""Normalize a path to absolute form."""
return Path(path).resolve()
@classmethod
def is_ignored_dir(cls, path: Path) -> bool:
"""Check if a directory should be ignored."""
return path.name in cls.IGNORED_DIRS or path.name.startswith(".")
@classmethod
def is_ignored_file(cls, path: Path) -> bool:
"""Check if a file should be ignored."""
return path.name in cls.IGNORED_FILES
@classmethod
def is_source_file(cls, path: Path) -> bool:
"""Check if a file is a source code file."""
return path.suffix in cls.SOURCE_EXTENSIONS
@classmethod
def is_config_file(cls, path: Path) -> bool:
"""Check if a file is a configuration file."""
return path.suffix in cls.CONFIG_EXTENSIONS
@classmethod
def is_documentation_file(cls, path: Path) -> bool:
"""Check if a file is a documentation file."""
return path.suffix in cls.DOCUMENTATION_EXTENSIONS
@classmethod
def is_test_file(cls, path: Path) -> bool:
"""Check if a file is a test file."""
for pattern in cls.TEST_PATTERNS:
if pattern.search(str(path)):
return True
return False
@classmethod
def get_relative_path(cls, path: Path, base: Path) -> Path:
"""Get relative path from base directory."""
try:
return path.relative_to(base)
except ValueError:
return path
@classmethod
def is_hidden(cls, path: Path) -> bool:
"""Check if a path is hidden (starts with dot)."""
return path.name.startswith(".")
@classmethod
def detect_project_root(cls, path: Path) -> Optional[Path]:
"""Detect project root by looking for marker files."""
markers = [
"pyproject.toml",
"package.json",
"go.mod",
"Cargo.toml",
"setup.py",
"setup.cfg",
"requirements.txt",
"Pipfile",
"pom.xml",
"build.gradle",
]
current = cls.normalize_path(path)
for _ in range(50):
for marker in markers:
if (current / marker).exists():
return current
if current.parent == current:
break
current = current.parent
return None
@classmethod
def is_file_empty(cls, path: Path) -> bool:
"""Check if a file is empty."""
try:
return os.path.getsize(path) == 0
except OSError:
return True
@classmethod
def get_file_size(cls, path: Path) -> int:
"""Get file size in bytes."""
try:
return os.path.getsize(path)
except OSError:
return 0
@classmethod
def count_lines(cls, path: Path) -> int:
"""Count lines in a file."""
try:
with open(path, "r", encoding="utf-8", errors="ignore") as f:
return sum(1 for _ in f)
except OSError:
return 0
@classmethod
def safe_read(cls, path: Path, max_size: int = 1024 * 1024) -> Optional[str]:
"""Safely read file contents with size limit."""
try:
if cls.get_file_size(path) > max_size:
return None
with open(path, "r", encoding="utf-8", errors="ignore") as f:
return f.read()
except OSError:
return None
def normalize_path(path: str | Path) -> Path:
"""Normalize a path to absolute form."""
return PathUtils.normalize_path(path)