11 changed files with 1357 additions and 95 deletions
@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python3 |
||||
# MIT License |
||||
# |
||||
# Copyright (c) 2025 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com> |
||||
# |
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
# of this software and associated documentation files (the "Software"), to deal |
||||
# in the Software without restriction, including without limitation the rights |
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
# copies of the Software, and to permit persons to whom the Software is |
||||
# furnished to do so, subject to the following conditions: |
||||
# |
||||
# The above copyright notice and this permission notice shall be included in all |
||||
# copies or substantial portions of the Software. |
||||
# |
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
# SOFTWARE. |
||||
|
||||
import re |
||||
import json |
||||
import argparse |
||||
import inspect |
||||
from typing import List, Optional, Dict, Tuple, Any |
||||
|
||||
def read_file_to_lines(file_path: str) -> List[str]: |
||||
""" |
||||
Reads a file and returns a list of strings, one for each line. |
||||
|
||||
Args: |
||||
file_path (str): The path to the file. |
||||
|
||||
Returns: |
||||
list: A list of strings, where each string is a line from the file. |
||||
Returns None if the file cannot be opened. |
||||
""" |
||||
with open(file_path, 'r') as f: |
||||
lines: List[str] = f.readlines() |
||||
return lines |
||||
raise Exception() |
||||
|
||||
def write_lines_to_file(lines: List[str], output_path: str) -> bool: |
||||
""" |
||||
Writes a list of strings to a file, one string per line. |
||||
|
||||
Args: |
||||
lines (list): The list of strings to write. |
||||
output_path (str): The path to the output file. |
||||
|
||||
Returns: |
||||
bool: True if the write was successful, False otherwise. |
||||
""" |
||||
try: |
||||
with open(output_path, 'w') as f: |
||||
for line in lines: |
||||
f.write(line) |
||||
return True |
||||
except Exception as e: |
||||
print(f"Error: An error occurred while writing to the file: {e}") |
||||
return False |
||||
|
||||
def _index_of(_m:str, lines:List[str]) -> int: |
||||
for i,l in enumerate(lines): |
||||
if _m in l: |
||||
return i |
||||
raise ValueError(f"End Delimiter '{_m}' not found in the filtered lines") |
||||
|
||||
marker_start = "#--FORWARD-AUTOGEN-START--#" |
||||
marker_end = "#--FORWARD-AUTOGEN-END--#" |
||||
|
||||
from TermTk.TTkAbstract.abstractscrollarea import _ForwardData |
||||
def autogen_methods(data: _ForwardData) -> List[str]: |
||||
""" |
||||
Generates a list of method signatures and return types for a given class. |
||||
|
||||
Args: |
||||
data (Dict[str, Any]): A dictionary containing the class name and a list of methods. |
||||
Example: {'class': 'TTkTexteditView', 'methods': ['clear', 'setText', ...]} |
||||
|
||||
Returns: |
||||
List[str]: A list of strings, where each string is a method signature and return type. |
||||
""" |
||||
import TermTk as ttk |
||||
|
||||
class_name = data.forwardClass.__name__ |
||||
signatures: List[str] = [] |
||||
|
||||
for method_name in data.methods: |
||||
try: |
||||
# Get the method from the class |
||||
method = getattr(data.forwardClass, method_name) |
||||
sig = inspect.signature(method) |
||||
doc = inspect.getdoc(method) |
||||
return_type = sig.return_annotation |
||||
params = ', '.join([f"{_p}={_p}" for _p in sig.parameters.keys() if _p != 'self']) |
||||
|
||||
source = inspect.getsource(method) |
||||
# Extract the first line, which should be the method definition |
||||
lines = source.splitlines() |
||||
index_func = _index_of(' def ',lines) |
||||
lines = lines[:index_func+1] |
||||
doc_indent = " " |
||||
lines.extend([ |
||||
doc_indent + f"'''", |
||||
doc_indent + f".. seealso:: this method is forwarded to :py:meth:`{class_name}.{method_name}`\n", |
||||
]) |
||||
if doc: |
||||
lines.extend([doc_indent + _l for _l in doc.split('\n')]) |
||||
lines.append(doc_indent + "'''") |
||||
# Format the signature string |
||||
signatures.extend([ |
||||
*[f"{_l}\n" for _l in lines], |
||||
# f" def {method_name}{sig}:", |
||||
]) |
||||
if '=kwargs' in params and '=args' in params: |
||||
signatures.append(f" return {data.instance}.{method_name}(*args, **kwargs)\n") |
||||
elif '=kwargs' in params: |
||||
signatures.append(f" return {data.instance}.{method_name}(**kwargs)\n") |
||||
elif '=args' in params: |
||||
signatures.append(f" return {data.instance}.{method_name}(*args)\n") |
||||
else: |
||||
signatures.append(f" return {data.instance}.{method_name}({params})\n") |
||||
|
||||
except AttributeError: |
||||
print(f"Error: Method '{method_name}' not found in class '{class_name}'.") |
||||
except Exception as e: |
||||
print(f"Error: An error occurred while processing method '{method_name}': {e}") |
||||
|
||||
return signatures |
||||
|
||||
def get_classes_with_source_from_module(module) -> List[Dict[str, Any]]: |
||||
classes_with_source: List[Dict[str, Any]] = [] |
||||
|
||||
for name, obj in inspect.getmembers(module): |
||||
if inspect.isclass(obj) and hasattr(obj,'_ttk_forward'): |
||||
try: |
||||
source = inspect.getsource(obj) |
||||
filename = inspect.getfile(obj) |
||||
classes_with_source.append({ |
||||
'class': obj, |
||||
'name': name, |
||||
'forward': obj._ttk_forward, |
||||
'module': module.__name__, |
||||
'filename': filename, |
||||
'source': source |
||||
}) |
||||
except OSError as e: |
||||
print(f"Could not get source for class {name}: {e}") |
||||
except Exception as e: |
||||
print(f"Unexpected error getting source for class {name}: {e}") |
||||
|
||||
return classes_with_source |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
parser = argparse.ArgumentParser(description="Read a file and write its lines to another file.") |
||||
parser.add_argument('--apply', action='store_true', help='Apply the changes') |
||||
args = parser.parse_args() |
||||
|
||||
import TermTk as ttk |
||||
classes = get_classes_with_source_from_module(ttk) |
||||
args = parser.parse_args() |
||||
if classes: |
||||
for class_data in classes: |
||||
print(f"Class Name: {class_data['name']}") |
||||
print(f" Class: {class_data['class']}") |
||||
print(f" Forward: {class_data['forward']}") |
||||
print(f" Module: {class_data['module']}") |
||||
print(f" Filename: {class_data['filename']}") |
||||
# print(f" Source:\n{class_data['source']}") |
||||
autogenenerated = autogen_methods(class_data['forward']) |
||||
if args.apply: |
||||
lines = read_file_to_lines(class_data['filename']) |
||||
index_start = _index_of(marker_start,lines) |
||||
index_end = _index_of(marker_end,lines) |
||||
lines[index_start+1:index_end] = autogenenerated |
||||
write_lines_to_file(lines,class_data['filename']) |
||||
else: |
||||
print(''.join(autogenenerated)) |
||||
else: |
||||
print("No classes found in the module.") |
||||
Loading…
Reference in new issue