-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathdundars-list.py
More file actions
executable file
·124 lines (99 loc) · 4.41 KB
/
dundars-list.py
File metadata and controls
executable file
·124 lines (99 loc) · 4.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#!/usr/bin/env python3
"""dundars-list.py here.
https://github.com/wilsonmar/python-samples/blob/main/dunders-list.py
This script lists the value of module-level special dunder variables
from all python (.py) files in this repo, without opening the files.
Examples: (such as <tt>__last_change__</tt>)
```
__license__ = "MIT"
__commit_date__ = "2025-05-20"
__version__ = "1.0.0". # https://peps.python.org/pep-0396/
__status__ = "Debugging"
__doc__ = "This module demonstrates the use of custom dunder (double underscore) variables in Python."
__last_change__ = "25-05-20 v001 + new :dunders-list.py"
```
https://www.pythonmorsels.com/dunder-variables/
Dunder (double underscore) variables in the module define metadata about the program. PEP specifies that such variables can be read by other programs without opening the source code.
Run usage:
ruff check dundars-list.py
chmod +x dundars-list.py
./dundars-list.py
"""
__last_change__ = "25-09-15 v011 + pgm_status :dundars-list.py"
__doc__ = (
"List the contents of special dunder variables from all python (.py) files in this repo, without opening the files."
)
__status__ = "Seems to work."
import os
import time
# External libraries defined in requirements.txt:
try:
import ast
except Exception as e:
print(f"Python module import failed: {e}")
print("Please activate your virtual environment:\n uv env env\n source .venv/bin/activate")
exit(9)
SHOW_VERBOSE = True
SHOW_DEBUG = False
def get_module_docstring(module_path):
"""Get value of __doc__ for module."""
with open(module_path, "r", encoding="utf-8") as file:
source = file.read()
parsed = ast.parse(source)
return ast.get_docstring(parsed)
def get_dunder_variable(module_path, var_name):
"""Extract a dunder variable value from a Python file using AST parsing."""
try:
with open(module_path, "r", encoding="utf-8") as file:
source = file.read()
parsed = ast.parse(source)
for node in ast.walk(parsed):
if isinstance(node, ast.Assign):
for target in node.targets:
if isinstance(target, ast.Name) and target.id == var_name:
if isinstance(node.value, ast.Str): # For older Python versions
return node.value.s
elif isinstance(node.value, ast.Constant): # For Python 3.8+
return node.value.value
return None
except Exception:
return None
def print_sorted_last_change(folder_path="."):
"""Get list of all Python files with their full paths."""
py_files = [os.path.join(folder_path, f) for f in os.listdir(folder_path) if f.endswith(".py")]
# Sort files by last modification time:
py_files.sort(key=os.path.getmtime)
for filepath in py_files:
pgm_name = os.path.basename(filepath)
# DateTime Last Modified such as "2024-04-20 10:05:08 AM MDT-0600"
mod_time = os.path.getmtime(filepath)
readable_time = time.strftime("%Y-%m-%d %I:%M:%S %p %Z%z", time.localtime(mod_time))
# print(f"{readable_time} {pgm_name:<30}")
print(f"{readable_time} {pgm_name}")
# TODO: File size
if SHOW_VERBOSE:
last_change = get_dunder_variable(filepath, "__last_change__")
if last_change:
print(f" {last_change}")
pgm_status = get_dunder_variable(filepath, "__status__")
if pgm_status:
print(f" {pgm_status}")
# else:
# print(f" No __last_change__ found")
# print(f" {__doc__}")
# docstring = get_module_docstring(os.path.basename(filepath))
# print(f" {docstring}")
# print(f" {os.path.basename(filepath).__last_change__}")
# print([method for method in dir(obj) if method.startswith('__') and method.endswith('__')])
divider_string = "-" * 80
print(divider_string)
print(f"{len(py_files)} *.py files listed.")
# Example usage: Replace '.' with the folder you want to inspect
print("\n# All __last_change__ dundar entries:")
print_sorted_last_change(".")
if SHOW_DEBUG:
print("\n# All dundar methods:")
# See https://www.pythonmorsels.com/every-dunder-method/
# See https://rszalski.github.io/magicmethods/
dunders = [method for method in dir(int) if method.startswith("__") and method.endswith("__")]
print(dunders)