Last active
December 18, 2023 19:19
-
-
Save QNimbus/ff877ae2d0657d597fcd506fa41b73f2 to your computer and use it in GitHub Desktop.
Python scripts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env -S python -W ignore | |
# code_outliner_exporter.py | |
# Standard library imports | |
import os | |
import ast | |
import argparse | |
from pathlib import Path | |
# Third-party imports | |
# ... | |
# Local application/library imports | |
# ... | |
def extract_signatures(file_path: Path, exclude_docstrings: bool = False, escape_docstrings: bool = False) -> list: | |
""" | |
Extracts the function and class signatures along with their docstrings from a given file. | |
Args: | |
file_path (Path): The path to the file. | |
exclude_docstrings (bool): Whether to exclude docstrings or not. | |
escape_docstrings (bool): Whether to escape docstrings with ''' instead of triple double-quotes. | |
Returns: | |
list: A list of function and class signatures with docstrings. | |
""" | |
with open(file_path, "r", encoding="utf-8") as file: | |
def extract_from_node(node, class_name=None, parent_func_name=None) -> list: | |
signatures = [] | |
for n in node.body: | |
if isinstance(n, ast.FunctionDef): | |
func_signature = extract_function_signature(n, class_name=class_name, parent_func_name=parent_func_name, exclude_docstrings=exclude_docstrings) | |
signatures.append(func_signature) | |
# Recursively search inside the function with the current function name as parent | |
signatures.extend(extract_from_node(n, class_name, parent_func_name=n.name)) | |
elif isinstance(n, ast.ClassDef) and class_name is None: # Only top-level classes | |
class_header = f"class {n.name}:" | |
if not exclude_docstrings: | |
docstring = ast.get_docstring(n) | |
if docstring: | |
docstring = docstring.replace("\n", "\n ") | |
class_header += f'\n """\n {docstring}\n """' | |
signatures.append(class_header) | |
# Extract from class body | |
signatures.extend(extract_from_node(n, class_name=n.name)) | |
if escape_docstrings: | |
signatures = [s.replace('"""', "'''") for s in signatures] | |
return signatures | |
# Use the recursive function | |
return extract_from_node(ast.parse(file.read())) | |
def extract_function_signature(func_def, class_name: str = None, parent_func_name: str = None, exclude_docstrings: bool = False) -> str: | |
""" | |
Extracts the signature of a function or method, including the return type if present. | |
Args: | |
func_def (ast.FunctionDef): The function definition node. | |
class_name (Optional[str]): The name of the class if it's a method. | |
parent_func_name (Optional[str]): The name of the parent function if it's a nested function. | |
exclude_docstrings (bool): Whether to exclude docstrings or not. | |
Returns: | |
str: The function or method signature with return type. | |
""" | |
# Process decorators | |
decorators = [f"@{ast.unparse(decorator)}" for decorator in func_def.decorator_list] | |
decorator_str = "\n".join(decorators) + "\n" if decorators else "" | |
# Function header construction | |
prefix = f"{class_name}." if class_name else "" | |
nested_prefix = f"{parent_func_name} > " if parent_func_name else "" | |
func_header = f"{nested_prefix}def {prefix}{func_def.name}(" | |
# Parameters construction | |
params = [ast.unparse(arg) for arg in func_def.args.args] | |
func_header += ", ".join(params) | |
if func_def.args.vararg: | |
func_header += ", *" + func_def.args.vararg.arg | |
if func_def.args.kwonlyargs: | |
kwonlyargs = [ast.unparse(arg) for arg in func_def.args.kwonlyargs] | |
func_header += ", " + ", ".join(kwonlyargs) if params else ", ".join(kwonlyargs) | |
if func_def.args.kwarg: | |
func_header += ", **" + func_def.args.kwarg.arg | |
# Append return type if present | |
if func_def.returns: | |
return_type = ast.unparse(func_def.returns) | |
func_header += f") -> {return_type}:" | |
else: | |
func_header += "):" | |
# Append docstring if present | |
if not exclude_docstrings: | |
docstring = ast.get_docstring(func_def) | |
if docstring: | |
docstring = docstring.replace("\n", "\n ") | |
func_header += f'\n """\n {docstring}\n """' | |
return decorator_str + func_header | |
def write_to_markdown(functions, output_file: Path): | |
"""Write the function signatures to a Markdown file.""" | |
with open(output_file, "w", encoding="utf-8") as file: | |
file.write("```python\n") | |
for func in functions: | |
file.write(f"\n{func}\n") | |
file.write("\n```\n\n") | |
def main(): | |
""" | |
Extracts Python function signatures and class definitions from a given Python file and writes them to a Markdown file. | |
Usage: | |
python_file: str - Python file to extract code outline from. | |
output: str - Output Markdown file to write the function signatures to. If not provided, a default file name will be used. | |
Returns: | |
None | |
""" | |
parser = argparse.ArgumentParser(description="Extract Python function signatures.") | |
parser.add_argument("python_file", type=str, help="Python file to extract from") | |
parser.add_argument("-n", "--no-docstrings", action="store_true", help="Exclude docstrings") | |
parser.add_argument("-e", "--escape-docstrings", action="store_true", help="Escape docstrings with ''' instead of \"\"\"") | |
parser.add_argument("-o", "--output", type=str, help="Output Markdown file") | |
args = parser.parse_args() | |
python_file = args.python_file | |
output_file = args.output if args.output else os.path.splitext(python_file)[0] + "_sigs.md" | |
function_signatures = extract_signatures(python_file, exclude_docstrings=args.no_docstrings, escape_docstrings=args.escape_docstrings) | |
write_to_markdown(function_signatures, output_file) | |
print(f"Function signatures extracted to {output_file}") | |
if __name__ == "__main__": | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env -S python -W ignore | |
# func_call_analyzer.py | |
# Standard library imports | |
import ast | |
import argparse | |
import importlib | |
from typing import Set, Tuple, Dict, List | |
# Third-party imports | |
# ... | |
# Local application/library imports | |
# ... | |
def map_imports(node) -> Dict[str, str]: | |
""" | |
Maps imported functions to their respective modules. | |
Args: | |
node: The AST node to analyze. | |
Returns: | |
Dict[str, str]: A dictionary mapping function names to module names. | |
""" | |
imports = {} | |
for child in ast.iter_child_nodes(node): | |
if isinstance(child, ast.Import): | |
for name in child.names: | |
imports[name.name] = name.name | |
elif isinstance(child, ast.ImportFrom): | |
module = child.module if child.module else "" | |
for name in child.names: | |
imports[name.name] = module | |
return imports | |
def is_builtin_function(name: str) -> bool: | |
""" | |
Check if a function name is a built-in function. | |
Args: | |
name: The name of the function. | |
Returns: | |
bool: True if it's a built-in function, False otherwise. | |
""" | |
return name in dir(__builtins__) | |
def find_function_calls_and_exceptions(node, internal_funcs: Set[str], imports: Dict[str, str], function_parameters: List[str]) -> Tuple[Set[str], Set[str], Set[str]]: | |
""" | |
Recursively analyzes an abstract syntax tree (AST) node to find function calls and raised exceptions. | |
Args: | |
node (ast.AST): The AST node to analyze. | |
internal_funcs (Set[str]): A set of internal function names. | |
imports (Dict[str, str]): A dictionary mapping imported function names to their respective modules. | |
function_parameters (List[str]): A list of function parameter names. | |
Returns: | |
Tuple[Set[str], Set[str], Set[str]]: A tuple containing three sets: | |
- internal_calls: Set of internal function calls found in the AST. | |
- external_calls: Set of external function calls found in the AST. | |
- raised_exceptions: Set of raised exceptions found in the AST. | |
""" | |
internal_calls = set() | |
external_calls = set() | |
raised_exceptions = set() | |
for child in ast.iter_child_nodes(node): | |
if isinstance(child, ast.FunctionDef): | |
# Update function parameters for this new function scope | |
new_function_parameters = [arg.arg for arg in child.args.args] | |
child_internal, child_external, child_exceptions = find_function_calls_and_exceptions(child, internal_funcs, imports, new_function_parameters) | |
internal_calls |= child_internal | |
external_calls |= child_external | |
raised_exceptions |= child_exceptions | |
elif isinstance(child, ast.Call) and isinstance(child.func, ast.Name): | |
if child.func.id in internal_funcs: | |
internal_calls.add(child.func.id) | |
elif child.func.id in function_parameters: | |
# This is a callable variable, skip it | |
continue | |
else: | |
if is_builtin_function(child.func.id): | |
external_calls.add(f"built-in.{child.func.id}") | |
else: | |
module = imports.get(child.func.id, "unknown_module") | |
external_calls.add(f"{module}.{child.func.id}") | |
elif isinstance(child, ast.Raise): | |
if child.exc: | |
if isinstance(child.exc, ast.Name): | |
raised_exceptions.add(child.exc.id) | |
elif isinstance(child.exc, ast.Call) and isinstance(child.exc.func, ast.Name): | |
raised_exceptions.add(child.exc.func.id) | |
else: | |
# Recursively process other nodes | |
child_internal, child_external, child_exceptions = find_function_calls_and_exceptions(child, internal_funcs, imports, function_parameters) | |
internal_calls |= child_internal | |
external_calls |= child_external | |
raised_exceptions |= child_exceptions | |
return internal_calls, external_calls, raised_exceptions | |
def analyze_function_calls(module_name: str, function_name: str) -> None: | |
""" | |
Analyzes the function calls within a module and prints the internal and external function calls of a target function. | |
Args: | |
module_name (str): The name of the module to analyze. | |
function_name (str): The name of the target function. | |
Returns: | |
None | |
""" | |
module = importlib.import_module(module_name) | |
with open(module.__file__, "r", encoding="utf-8") as file: | |
module_ast = ast.parse(file.read()) | |
# Map imports | |
imports = map_imports(module_ast) | |
# Collect internal function names | |
internal_funcs = {node.name for node in ast.walk(module_ast) if isinstance(node, ast.FunctionDef)} | |
# Find the target function | |
target_func = next((node for node in ast.walk(module_ast) if isinstance(node, ast.FunctionDef) and node.name == function_name), None) | |
if not target_func: | |
print(f"Function '{function_name}' not found in the module.") | |
return | |
# Find function calls and raised exceptions | |
internal_calls, external_calls, raised_exceptions = find_function_calls_and_exceptions(target_func, internal_funcs, imports, []) | |
# Print internal function calls in bulleted list format | |
print(f"Internal function calls in '{function_name}':") | |
for call in internal_calls: | |
print(f"- {call}") | |
# Print external function calls in bulleted list format | |
print(f"External function calls in '{function_name}':") | |
for call in external_calls: | |
print(f"- {call}") | |
print(f"Exceptions raised in '{function_name}':") | |
for exception in raised_exceptions: | |
print(f"- {exception}") | |
def main(): | |
parser = argparse.ArgumentParser(description="Analyze function calls within a specified function of a module.") | |
parser.add_argument("module_path", type=str, help="Path to the module") | |
parser.add_argument("function_name", type=str, help="Function name to analyze") | |
args = parser.parse_args() | |
analyze_function_calls(args.module_path, args.function_name) | |
if __name__ == "__main__": | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# python_patterns_settings.py.py | |
# Standard library imports | |
import os | |
from functools import lru_cache | |
from pydantic import BaseSettings | |
# Third-party imports | |
# ... | |
# Local application/library imports | |
# ... | |
class Settings(BaseSettings): | |
""" | |
Settings class for this application. | |
Utilizes the BaseSettings from pydantic for environment variables. | |
""" | |
openai_api_key: str | |
class Config: | |
env_file = ".env" | |
@lru_cache() | |
def get_settings(): | |
"""Function to get and cache settings. | |
The settings are cached to avoid repeated disk I/O. | |
""" | |
return Settings() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment