pyenv-virtualenv for Windows 1.2
A 'pyenv' plugin to manage Python virtual environments, depending on different Python versions, for various Python projects.
Loading...
Searching...
No Matches
tre.py
Go to the documentation of this file.
1##
2# @package tre
3# @file tre.py
4# @author Michael Paul Korthals
5# @date 2025-07-10
6# @version 1.0.0
7# @copyright © 2025 Michael Paul Korthals. All rights reserved.
8# See License details in the documentation.
9#
10# Library to output the project properties "pyenv-virtualenv"
11# folder tree in the CLI terminal.
12# I derived this code for "pyenv-virtualenv" from a StackOverflow article.
13# Many thanks to its contributor.
14# @see https://stackoverflow.com/questions/9727673/list-directory-tree-structure-in-python
15#
16
17# --- IMPORTS ----------------------------------------------------------
18
19# Python
20from itertools import islice
21from pathlib import Path
22import sys
23
24# Community
25# (None)
26
27# My
28try:
29 # noinspection PyUnresolvedReferences
30 import lib.log as log
31except ImportError():
32 try:
33 # noinspection PyUnresolvedReferences
34 import log
35 except ImportError():
36 # noinspection PyUnresolvedReferences
37 import log
38
39# --- GLOBAL VARIABLES -------------------------------------------------
40
41space = '\x1b[94m \x1b[0m'
42branch = '\x1b[94m│ \x1b[0m'
43tee = '\x1b[94m├───\x1b[0m'
44last = '\x1b[94m└───\x1b[0m'
45
46# --- HELPER -----------------------------------------------------------
47
48## Get the content of project property file.
49#
50# @param file_path Path to project property file.
51# @return Text, which the file is containing or empty string in case of error.
52def getProjectPropertyFileStr(file_path: str) -> str:
53 result = ''
54 # noinspection PyBroadException
55 try:
56 with open(file_path, 'r') as f:
57 result = f.read().strip()
58 return result
59 except:
60 log.error(sys.exc_info())
61 return ''
62
63## Given a directory path, print a visual tree structure.
64# Output and highlight the content of 'pyenv-virtualenv'
65# project property files with colors:
66# * LIGHT CYAN: Python version.
67# * LIGHT YELLOW: Virtual environment name.
68#
69# @param root_path Folder path, from which the tree will be parsed.
70# @param recursion_level Max recursion level to parse.
71# Default: -1 = unlimited recursion.
72# @param limit_to_directories Flag to permit parsing for folders only.
73# Default: False = parse for both, files and folders
74# @param length_limit Max. output length of the tree in lines.
75# Default: 1000.
76# @param exclude List of folder names to exclude.
77# Default empty list = nothing to exclude.
78def tree(
79 root_path: (Path, str),
80 recursion_level: int = -1,
81 limit_to_directories: bool = False,
82 length_limit: int = 1000,
83 exclude: tuple = ()
84):
85 # Accept type "str" coercible to "Path"
86 if isinstance(root_path, str):
87 root_path = Path(root_path)
88 # Initialize
89 files = 0
90 directories = 0
91
92 def inner(
93 dir_path: Path,
94 prefix: str= '',
95 level: int = -1,
96 excl: tuple = ()
97 ):
98 nonlocal files, directories
99 if not level:
100 return # 0, stop iterating
101 # Scan for files and folders Path objects
102 if limit_to_directories:
103 objects = dir_path.iterdir()
104 contents = [d for d in objects if d.is_dir()]
105 contents1 = list(objects)
106 else:
107 contents = list(dir_path.iterdir())
108 contents1 = contents
109 # Detect file ".tree-excludes" in files
110 excl1 = excl
111 for content in contents1:
112 if content.is_file() and (content.name == '.tree-excludes'):
113 with open(content, 'r') as f:
114 excl1 = eval(f.read())
115 if content not in contents:
116 contents.insert(0, content)
117 break
118 # End if
119 # End for
120 pointers = [tee] * (len(contents) - 1) + [last]
121 for pointer, path in zip(pointers, contents):
122 if path.is_dir():
123 # Exclude spam folders
124 excluded = False
125 for item in excl1:
126 if path.parts[-1] == item:
127 excluded = True
128 break
129 # End if
130 # End for
131 if excluded: continue
132 # OUTPUT: colorized directory
133 name = path.name
134 if path.is_junction():
135 name = f'\x1b[105m {name} \x1b[0m \x1b[95m→\x1b[0m \x1b[104m {str(path.readlink()).split(f'\\\\?\\')[-1]} \x1b[0m'
136 else: # Is directory
137 name = f'\x1b[104m {name} \x1b[0m'
138 yield prefix + pointer + name
139 directories += 1
140 extension = branch if pointer == tee else space
141 # OUTPUT: path recursively
142 yield from inner(
143 path,
144 prefix=prefix+extension,
145 level=level - 1,
146 excl=excl1
147 )
148 elif not limit_to_directories:
149 # OUTPUT: colorized file
150 name = path.name
151 if path.is_symlink():
152 name1 = f'\x1b[95m● {name}\x1b[0m'
153 if name == '.python-version':
154 name1 += f' \x1b[96m({
156 str(path)
157 )
158 })\x1b[0m'
159 elif name == '.python-env':
160 name1 += f' \x1b[93m({
162 str(path)
163 )
164 })\x1b[0m'
165 elif name == '.tree-excludes':
166 name1 += f' \x1b[95m{
168 str(path)
169 )
170 }\x1b[0m'
171 else:
172 name1 = f'\x1b[95m● {name} → \x1b[37m● {str(path.readlink())}\x1b[0m'
173 else: # Is file
174 name1 = f'\x1b[37m● {name}\x1b[0m'
175 if name == '.python-version':
176 name1 += f' \x1b[96m({
178 str(path)
179 )
180 })\x1b[0m'
181 elif name == '.python-env':
182 name1 += f' \x1b[93m({
184 str(path)
185 )
186 })\x1b[0m'
187 elif name == '.tree-excludes':
188 name1 += f' \x1b[95m{
190 str(path)
191 )
192 }\x1b[0m'
193 else:
194 name1 = f'\x1b[37m● {name}\x1b[0m'
195 yield prefix + pointer + name1
196 files += 1
197
198 # Output the root directory path in the beginning
199 print(f'\x1b[104m {str(root_path)} \x1b[0m')
200 # Exclude spam folders
201 for excl_item in exclude:
202 if root_path.parts[-1] == excl_item:
203 return
204 # Iterate through the folder tree
205 iterator = inner(
206 root_path,
207 level=recursion_level,
208 excl=exclude
209 )
210 # OUTPUT: folder tree
211 for line in islice(iterator, length_limit):
212 print(line)
213 if next(iterator, None):
214 # Obey to the length_limit
215 print(f'... length_limit, {length_limit}, reached, counted:')
216 # OUTPUT: statistics
217 print(
218 f'\n{directories} directories' + (
219 f', {files} files' if files else ''
220 )
221 )
222
223
224# --- END OF CODE ------------------------------------------------------
error((str, tuple) msg)
Log error message colored to console only.
Definition log.py:179
tree((Path, str) root_path, int recursion_level=-1, bool limit_to_directories=False, int length_limit=1000, tuple exclude=())
Given a directory path, print a visual tree structure.
Definition tre.py:84
str getProjectPropertyFileStr(str file_path)
Get the content of project property file.
Definition tre.py:52