🐛 fix(validate.py): fix spacing and formatting issues in the code to improve readability
✨ feat(validate.py): add support for default imports in the dynamic class constructor to improve usability and reduce code duplication 🔧 chore(validate.py): refactor prepare_global_scope function to accept code string as an argument for better modularity and reusability 🔧 chore(validate.py): refactor build_class_constructor function to improve readability and remove unnecessary imports
This commit is contained in:
parent
67e520cfd6
commit
831f665e06
1 changed files with 42 additions and 13 deletions
|
|
@ -156,12 +156,12 @@ def create_class(code, class_name):
|
||||||
ast.TypeIgnore = create_type_ignore_class()
|
ast.TypeIgnore = create_type_ignore_class()
|
||||||
|
|
||||||
module = ast.parse(code)
|
module = ast.parse(code)
|
||||||
exec_globals = prepare_global_scope(module)
|
exec_globals = prepare_global_scope(code, module)
|
||||||
|
|
||||||
class_code = extract_class_code(module, class_name)
|
class_code = extract_class_code(module, class_name)
|
||||||
compiled_class = compile_class_code(class_code)
|
compiled_class = compile_class_code(class_code)
|
||||||
|
|
||||||
return build_class_constructor(compiled_class, exec_globals, class_name)
|
return build_class_constructor( compiled_class, exec_globals, class_name)
|
||||||
|
|
||||||
|
|
||||||
def create_type_ignore_class():
|
def create_type_ignore_class():
|
||||||
|
|
@ -177,7 +177,7 @@ def create_type_ignore_class():
|
||||||
return TypeIgnore
|
return TypeIgnore
|
||||||
|
|
||||||
|
|
||||||
def prepare_global_scope(module):
|
def prepare_global_scope(code, module):
|
||||||
"""
|
"""
|
||||||
Prepares the global scope with necessary imports from the provided code module.
|
Prepares the global scope with necessary imports from the provided code module.
|
||||||
|
|
||||||
|
|
@ -185,6 +185,7 @@ def prepare_global_scope(module):
|
||||||
:return: Dictionary representing the global scope with imported modules
|
:return: Dictionary representing the global scope with imported modules
|
||||||
"""
|
"""
|
||||||
exec_globals = globals().copy()
|
exec_globals = globals().copy()
|
||||||
|
exec_globals.update(get_default_imports(code))
|
||||||
for node in module.body:
|
for node in module.body:
|
||||||
if isinstance(node, ast.Import):
|
if isinstance(node, ast.Import):
|
||||||
for alias in node.names:
|
for alias in node.names:
|
||||||
|
|
@ -192,7 +193,7 @@ def prepare_global_scope(module):
|
||||||
exec_globals[alias.asname or alias.name] = importlib.import_module(alias.name)
|
exec_globals[alias.asname or alias.name] = importlib.import_module(alias.name)
|
||||||
except ModuleNotFoundError as e:
|
except ModuleNotFoundError as e:
|
||||||
raise ModuleNotFoundError(f"Module {alias.name} not found. Please install it and try again.") from e
|
raise ModuleNotFoundError(f"Module {alias.name} not found. Please install it and try again.") from e
|
||||||
elif isinstance(node, ast.ImportFrom):
|
elif isinstance(node, ast.ImportFrom) and node.module is not None:
|
||||||
try:
|
try:
|
||||||
imported_module = importlib.import_module(node.module)
|
imported_module = importlib.import_module(node.module)
|
||||||
for alias in node.names:
|
for alias in node.names:
|
||||||
|
|
@ -211,6 +212,7 @@ def extract_class_code(module, class_name):
|
||||||
:return: AST node of the specified class
|
:return: AST node of the specified class
|
||||||
"""
|
"""
|
||||||
class_code = next(node for node in module.body if isinstance(node, ast.ClassDef) and node.name == class_name)
|
class_code = next(node for node in module.body if isinstance(node, ast.ClassDef) and node.name == class_name)
|
||||||
|
|
||||||
class_code.parent = None
|
class_code.parent = None
|
||||||
return class_code
|
return class_code
|
||||||
|
|
||||||
|
|
@ -226,7 +228,7 @@ def compile_class_code(class_code):
|
||||||
return code_obj
|
return code_obj
|
||||||
|
|
||||||
|
|
||||||
def build_class_constructor(compiled_class, exec_globals, class_name):
|
def build_class_constructor( compiled_class, exec_globals, class_name):
|
||||||
"""
|
"""
|
||||||
Builds a constructor function for the dynamically created class.
|
Builds a constructor function for the dynamically created class.
|
||||||
|
|
||||||
|
|
@ -235,11 +237,6 @@ def build_class_constructor(compiled_class, exec_globals, class_name):
|
||||||
:param class_name: Name of the class
|
:param class_name: Name of the class
|
||||||
:return: Constructor function for the class
|
:return: Constructor function for the class
|
||||||
"""
|
"""
|
||||||
# Add basic imports from typing module
|
|
||||||
# List, Dict, Tuple, Union, Optional
|
|
||||||
# to the global scope
|
|
||||||
for name in ["List", "Dict", "Tuple", "Union", "Optional"]:
|
|
||||||
exec_globals[name] = getattr(importlib.import_module("typing"), name)
|
|
||||||
|
|
||||||
exec(compiled_class, exec_globals, locals())
|
exec(compiled_class, exec_globals, locals())
|
||||||
exec_globals[class_name] = locals()[class_name]
|
exec_globals[class_name] = locals()[class_name]
|
||||||
|
|
@ -257,9 +254,41 @@ def build_class_constructor(compiled_class, exec_globals, class_name):
|
||||||
return build_custom_class
|
return build_custom_class
|
||||||
|
|
||||||
|
|
||||||
# Example usage:
|
def get_default_imports(code_string):
|
||||||
# class_builder = create_class("class MyClass: pass", "MyClass")
|
"""
|
||||||
# my_instance = class_builder()
|
Returns a dictionary of default imports for the dynamic class constructor.
|
||||||
|
"""
|
||||||
|
default_imports = {
|
||||||
|
"Optional": importlib.import_module("typing").Optional,
|
||||||
|
"List": importlib.import_module("typing").List,
|
||||||
|
"Dict": importlib.import_module("typing").Dict,
|
||||||
|
"Union": importlib.import_module("typing").Union,
|
||||||
|
# Add more imports from the typing module as needed
|
||||||
|
}
|
||||||
|
|
||||||
|
langflow_imports = [
|
||||||
|
"AgentExecutor", "BaseChatMemory", "BaseLanguageModel", "BaseLLM",
|
||||||
|
"BaseLoader", "BaseMemory", "BaseOutputParser", "BasePromptTemplate",
|
||||||
|
"BaseRetriever", "Callable", "Chain", "ChatPromptTemplate", "Data",
|
||||||
|
"Document", "Embeddings", "NestedDict", "Object", "PromptTemplate",
|
||||||
|
"TextSplitter", "Tool", "VectorStore"
|
||||||
|
]
|
||||||
|
necessary_imports = find_names_in_code(code_string, langflow_imports + ["Optional", "List", "Dict", "Union"])
|
||||||
|
langflow_module = importlib.import_module("langflow.field_typing")
|
||||||
|
default_imports.update({name: getattr(langflow_module, name) for name in necessary_imports})
|
||||||
|
|
||||||
|
return default_imports
|
||||||
|
|
||||||
|
def find_names_in_code(code, names):
|
||||||
|
"""
|
||||||
|
Finds if any of the specified names are present in the given code string.
|
||||||
|
|
||||||
|
:param code: The source code as a string.
|
||||||
|
:param names: A list of names to check for in the code.
|
||||||
|
:return: A set of names that are found in the code.
|
||||||
|
"""
|
||||||
|
found_names = {name for name in names if name in code}
|
||||||
|
return found_names
|
||||||
|
|
||||||
|
|
||||||
def extract_function_name(code):
|
def extract_function_name(code):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue