#!/usr/bin/env python3 import ast import io import os import re import tokenize from pathlib import Path ROOT = Path(__file__).resolve().parents[1] OUT = ROOT.parent / "исходный_код_WESP.txt" SKIP_DIRS = { "__pycache__", ".git", ".pytest_cache", ".cursor", "tests", "migrations", "vendor", "logs", "legacy", } SKIP_FILES = { "proga.py", } EXTS = {".py", ".js", ".html", ".css"} def should_skip_file(path: Path) -> bool: if path.name in SKIP_FILES: return True if "legacy" in path.name.lower(): return True return False def should_skip_dir(name: str) -> bool: return name in SKIP_DIRS def iter_source_files(): for dirpath, dirnames, filenames in os.walk(ROOT): dirnames[:] = [d for d in dirnames if not should_skip_dir(d)] rel = Path(dirpath).relative_to(ROOT) if any(part in SKIP_DIRS for part in rel.parts): continue for name in sorted(filenames): path = Path(dirpath) / name if path.suffix.lower() in EXTS and not should_skip_file(path): yield path def _drop_docstring(body): if not body: return body first = body[0] if not isinstance(first, ast.Expr): return body val = first.value if isinstance(val, ast.Constant) and isinstance(val.value, str): return body[1:] return body class _DocstringStripper(ast.NodeTransformer): def visit_FunctionDef(self, node): self.generic_visit(node) node.body = _drop_docstring(node.body) return node def visit_AsyncFunctionDef(self, node): self.generic_visit(node) node.body = _drop_docstring(node.body) return node def visit_ClassDef(self, node): self.generic_visit(node) node.body = _drop_docstring(node.body) return node def visit_Module(self, node): self.generic_visit(node) node.body = _drop_docstring(node.body) return node def strip_python_comments(source: str) -> str: out = [] try: for tok in tokenize.generate_tokens(io.StringIO(source).readline): if tok.type == tokenize.COMMENT: continue out.append(tok) return tokenize.untokenize(out) except (tokenize.TokenError, SyntaxError): return source def strip_python(source: str) -> str: try: tree = ast.parse(source) tree = _DocstringStripper().visit(tree) ast.fix_missing_locations(tree) source = ast.unparse(tree) except SyntaxError: pass return strip_python_comments(source) def strip_js(source: str) -> str: result = [] i = 0 n = len(source) in_single = False in_double = False in_template = False in_line_comment = False in_block_comment = False escape = False while i < n: ch = source[i] nxt = source[i + 1] if i + 1 < n else "" if in_line_comment: if ch == "\n": in_line_comment = False result.append(ch) i += 1 continue if in_block_comment: if ch == "*" and nxt == "/": in_block_comment = False i += 2 continue i += 1 continue if in_single: result.append(ch) if escape: escape = False elif ch == "\\": escape = True elif ch == "'": in_single = False i += 1 continue if in_double: result.append(ch) if escape: escape = False elif ch == "\\": escape = True elif ch == '"': in_double = False i += 1 continue if in_template: result.append(ch) if escape: escape = False elif ch == "\\": escape = True elif ch == "`": in_template = False i += 1 continue if ch == "/" and nxt == "/": in_line_comment = True i += 2 continue if ch == "/" and nxt == "*": in_block_comment = True i += 2 continue if ch == "'": in_single = True result.append(ch) i += 1 continue if ch == '"': in_double = True result.append(ch) i += 1 continue if ch == "`": in_template = True result.append(ch) i += 1 continue result.append(ch) i += 1 return "".join(result) def strip_html_css(source: str) -> str: source = re.sub(r"", "", source) source = strip_js(source) return source def collapse_blank_lines(text: str) -> str: lines = [ln.rstrip() for ln in text.splitlines()] cleaned = [] blank_run = 0 for ln in lines: if not ln.strip(): blank_run += 1 if blank_run <= 2: cleaned.append("") continue blank_run = 0 cleaned.append(ln) return "\n".join(cleaned).strip("\n") def strip_file(path: Path) -> str: text = path.read_text(encoding="utf-8", errors="replace") ext = path.suffix.lower() if ext == ".py": body = strip_python(text) elif ext == ".js": body = strip_js(text) else: body = strip_html_css(text) return collapse_blank_lines(body) def main(): chunks = [] for path in iter_source_files(): body = strip_file(path) if body: chunks.append(body) content = "\n\n".join(chunks) OUT.write_text(content + "\n", encoding="utf-8") lines = content.count("\n") + (1 if content else 0) print(f"Written: {OUT}") print(f"Files: {len(chunks)}") print(f"Lines: {lines}") print(f"Size: {OUT.stat().st_size} bytes") if __name__ == "__main__": main()