projecttree-by-almira 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- projecttree_by_almira-0.1.0/PKG-INFO +14 -0
- projecttree_by_almira-0.1.0/README.md +1 -0
- projecttree_by_almira-0.1.0/projecttree/__init__.py +1 -0
- projecttree_by_almira-0.1.0/projecttree/cli.py +85 -0
- projecttree_by_almira-0.1.0/projecttree/formatter.py +35 -0
- projecttree_by_almira-0.1.0/projecttree/models.py +9 -0
- projecttree_by_almira-0.1.0/projecttree/scanner.py +48 -0
- projecttree_by_almira-0.1.0/projecttree/utils.py +17 -0
- projecttree_by_almira-0.1.0/projecttree_by_almira.egg-info/PKG-INFO +14 -0
- projecttree_by_almira-0.1.0/projecttree_by_almira.egg-info/SOURCES.txt +14 -0
- projecttree_by_almira-0.1.0/projecttree_by_almira.egg-info/dependency_links.txt +1 -0
- projecttree_by_almira-0.1.0/projecttree_by_almira.egg-info/entry_points.txt +2 -0
- projecttree_by_almira-0.1.0/projecttree_by_almira.egg-info/requires.txt +1 -0
- projecttree_by_almira-0.1.0/projecttree_by_almira.egg-info/top_level.txt +1 -0
- projecttree_by_almira-0.1.0/pyproject.toml +26 -0
- projecttree_by_almira-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: projecttree-by-almira
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A beautiful CLI tool to generate project tree structures.
|
|
5
|
+
Author: Almira
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: pyperclip>=1.8.2
|
|
13
|
+
|
|
14
|
+
# ProjectTree
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# ProjectTree
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from projecttree.scanner import TreeScanner
|
|
5
|
+
from projecttree.formatter import TreeFormatter
|
|
6
|
+
from projecttree.utils import save_to_file, copy_to_clipboard
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def parse_arguments():
|
|
10
|
+
parser = argparse.ArgumentParser(
|
|
11
|
+
description="projecttree — утилита для красивого вывода структуры проекта."
|
|
12
|
+
)
|
|
13
|
+
parser.add_argument(
|
|
14
|
+
"path",
|
|
15
|
+
nargs="?",
|
|
16
|
+
default=".",
|
|
17
|
+
help="Путь к сканируемой директории (по умолчанию: текущая)."
|
|
18
|
+
)
|
|
19
|
+
parser.add_argument(
|
|
20
|
+
"-d", "--depth",
|
|
21
|
+
type=int,
|
|
22
|
+
default=None,
|
|
23
|
+
help="Максимальная глубина отображения дерева."
|
|
24
|
+
)
|
|
25
|
+
parser.add_argument(
|
|
26
|
+
"-i", "--ignore",
|
|
27
|
+
nargs="+",
|
|
28
|
+
default=[],
|
|
29
|
+
help="Список имен файлов/папок для игнорирования."
|
|
30
|
+
)
|
|
31
|
+
parser.add_argument(
|
|
32
|
+
"--dirs-only",
|
|
33
|
+
action="store_true",
|
|
34
|
+
help="Отображать только директории."
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument(
|
|
37
|
+
"-o", "--output",
|
|
38
|
+
type=str,
|
|
39
|
+
default=None,
|
|
40
|
+
help="Экспортировать результат в указанный текстовый файл."
|
|
41
|
+
)
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"-m", "--markdown",
|
|
44
|
+
action="store_true",
|
|
45
|
+
help="Вывести структуру в формате Markdown."
|
|
46
|
+
)
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"-c", "--copy",
|
|
49
|
+
action="store_true",
|
|
50
|
+
help="Скопировать результат в буфер обмена."
|
|
51
|
+
)
|
|
52
|
+
return parser.parse_args()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def main():
|
|
56
|
+
args = parse_arguments()
|
|
57
|
+
|
|
58
|
+
if not os.path.exists(args.path):
|
|
59
|
+
print(f"Указанный путь не существует: {args.path}", file=sys.stderr)
|
|
60
|
+
sys.exit(1)
|
|
61
|
+
|
|
62
|
+
scanner = TreeScanner(
|
|
63
|
+
ignore_patterns=args.ignore,
|
|
64
|
+
dirs_only=args.dirs_only,
|
|
65
|
+
max_depth=args.depth
|
|
66
|
+
)
|
|
67
|
+
tree_model = scanner.scan(args.path)
|
|
68
|
+
|
|
69
|
+
if args.markdown:
|
|
70
|
+
result_text = TreeFormatter.to_markdown(tree_model)
|
|
71
|
+
else:
|
|
72
|
+
result_text = TreeFormatter.to_text(tree_model)
|
|
73
|
+
|
|
74
|
+
print(result_text)
|
|
75
|
+
print("-" * 40)
|
|
76
|
+
|
|
77
|
+
if args.output:
|
|
78
|
+
save_to_file(args.output, result_text)
|
|
79
|
+
|
|
80
|
+
if args.copy:
|
|
81
|
+
copy_to_clipboard(result_text)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
main()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from projecttree.models import Node
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TreeFormatter:
|
|
5
|
+
@staticmethod
|
|
6
|
+
def to_text(node: Node, prefix: str = "") -> str:
|
|
7
|
+
lines = []
|
|
8
|
+
if not prefix:
|
|
9
|
+
lines.append(node.name + "/")
|
|
10
|
+
prefix = ""
|
|
11
|
+
|
|
12
|
+
count = len(node.children)
|
|
13
|
+
for i, child in enumerate(node.children):
|
|
14
|
+
is_last = (i == count - 1)
|
|
15
|
+
connector = "└── " if is_last else "├── "
|
|
16
|
+
|
|
17
|
+
suffix = "/" if child.is_dir else ""
|
|
18
|
+
lines.append(f"{prefix}{connector}{child.name}{suffix}")
|
|
19
|
+
|
|
20
|
+
if child.is_dir:
|
|
21
|
+
extension = " " if is_last else "│ "
|
|
22
|
+
lines.append(TreeFormatter.to_text(child, prefix + extension))
|
|
23
|
+
|
|
24
|
+
return "\n".join(line for line in lines if line.strip())
|
|
25
|
+
|
|
26
|
+
@staticmethod
|
|
27
|
+
def to_markdown(node: Node, depth: int = 0) -> str:
|
|
28
|
+
indent = " " * depth
|
|
29
|
+
suffix = "/" if node.is_dir else ""
|
|
30
|
+
lines = [f"{indent}- {node.name}{suffix}"]
|
|
31
|
+
|
|
32
|
+
for child in node.children:
|
|
33
|
+
lines.append(TreeFormatter.to_markdown(child, depth + 1))
|
|
34
|
+
|
|
35
|
+
return "\n".join(lines)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
from projecttree.models import Node
|
|
4
|
+
|
|
5
|
+
class TreeScanner:
|
|
6
|
+
def __init__(
|
|
7
|
+
self,
|
|
8
|
+
ignore_patterns: Optional[List[str]] = None,
|
|
9
|
+
dirs_only: bool = False,
|
|
10
|
+
max_depth: Optional[int] = None
|
|
11
|
+
):
|
|
12
|
+
self.ignore_patterns = ignore_patterns or []
|
|
13
|
+
self.dirs_only = dirs_only
|
|
14
|
+
self.max_depth = max_depth
|
|
15
|
+
|
|
16
|
+
self.default_ignore = {'.git', '__pycache__', '.pytest_cache', '.venv', 'venv'}
|
|
17
|
+
|
|
18
|
+
def _should_ignore(self, name: str) -> bool:
|
|
19
|
+
if name in self.default_ignore:
|
|
20
|
+
return True
|
|
21
|
+
for pattern in self.ignore_patterns:
|
|
22
|
+
if pattern in name:
|
|
23
|
+
return True
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
def scan(self, path: str, current_depth: int = 1) -> Node:
|
|
27
|
+
name = os.path.basename(os.path.abspath(path)) or path
|
|
28
|
+
node = Node(name=name, is_dir=True, path=path)
|
|
29
|
+
|
|
30
|
+
if self.max_depth is not None and current_depth > self.max_depth:
|
|
31
|
+
return node
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
entries = sorted(os.scandir(path), key=lambda e: (not e.is_dir(), e.name.lower()))
|
|
35
|
+
except PermissionError:
|
|
36
|
+
return node
|
|
37
|
+
|
|
38
|
+
for entry in entries:
|
|
39
|
+
if self._should_ignore(entry.name):
|
|
40
|
+
continue
|
|
41
|
+
|
|
42
|
+
if entry.is_dir():
|
|
43
|
+
child_node = self.scan(entry.path, current_depth + 1)
|
|
44
|
+
node.children.append(child_node)
|
|
45
|
+
elif not self.dirs_only:
|
|
46
|
+
node.children.append(Node(name=entry.name, is_dir=False, path=entry.path))
|
|
47
|
+
|
|
48
|
+
return node
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import pyperclip
|
|
3
|
+
|
|
4
|
+
def save_to_file(filepath: str, content: str) -> None:
|
|
5
|
+
try:
|
|
6
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
7
|
+
f.write(content)
|
|
8
|
+
print(f"Структура успешно экспортирована в: {filepath}")
|
|
9
|
+
except Exception as e:
|
|
10
|
+
print(f"Ошибка при записи в файл: {e}", file=sys.stderr)
|
|
11
|
+
|
|
12
|
+
def copy_to_clipboard(content: str) -> None:
|
|
13
|
+
try:
|
|
14
|
+
pyperclip.copy(content)
|
|
15
|
+
print("Структура успешно скопирована в буфер обмена!")
|
|
16
|
+
except Exception as e:
|
|
17
|
+
print(f"Не удалось скопировать в буфер обмена: {e}", file=sys.stderr)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: projecttree-by-almira
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A beautiful CLI tool to generate project tree structures.
|
|
5
|
+
Author: Almira
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.11
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: pyperclip>=1.8.2
|
|
13
|
+
|
|
14
|
+
# ProjectTree
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
projecttree/__init__.py
|
|
4
|
+
projecttree/cli.py
|
|
5
|
+
projecttree/formatter.py
|
|
6
|
+
projecttree/models.py
|
|
7
|
+
projecttree/scanner.py
|
|
8
|
+
projecttree/utils.py
|
|
9
|
+
projecttree_by_almira.egg-info/PKG-INFO
|
|
10
|
+
projecttree_by_almira.egg-info/SOURCES.txt
|
|
11
|
+
projecttree_by_almira.egg-info/dependency_links.txt
|
|
12
|
+
projecttree_by_almira.egg-info/entry_points.txt
|
|
13
|
+
projecttree_by_almira.egg-info/requires.txt
|
|
14
|
+
projecttree_by_almira.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pyperclip>=1.8.2
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
projecttree
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "projecttree-by-almira"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A beautiful CLI tool to generate project tree structures."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Almira" }
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
dependencies = [
|
|
22
|
+
"pyperclip>=1.8.2",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.scripts]
|
|
26
|
+
projecttree = "projecttree.cli:main"
|