Skip to content

Commit

Permalink
Allow !((py-atom print) "Hello, World!" (Kwargs (end "") (flush True)…
Browse files Browse the repository at this point in the history
…)) to work
  • Loading branch information
TeamSPoon committed Jan 18, 2025
1 parent 9e1378a commit 20231f7
Showing 1 changed file with 233 additions and 22 deletions.
255 changes: 233 additions & 22 deletions prolog/metta_lang/metta_python.pl
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@
return func.__name__
return f"{func.__module__}.{func.__name__}"

DEBUG_MODE = False # Set this to True to enable debug output

import importlib
import types
Expand Down Expand Up @@ -680,9 +681,6 @@
# If none of the above, raise an error
raise TypeError("The provided arguments do not form a callable invocation.")

import inspect


def py_call_w_args(callable_obj, *w_args):
"""
Calls a Python callable with the provided arguments, handling both positional and keyword arguments.
Expand Down Expand Up @@ -749,6 +747,13 @@
else:
raise TypeError("Non-keyword arguments found after processing all parameters")

# Debugging output
if DEBUG_MODE:
print("Debug Information:")
print(f"Callable object: {callable_obj}")
print(f"Positional arguments: {method_args}")
print(f"Keyword arguments: {kwargs}")

try:
return callable_obj(*method_args, **kwargs)
finally:
Expand All @@ -764,6 +769,196 @@
def test_wild_test_function():
py_call_method_and_args(test_function, 1, 2, 4, 5, d=6, e=7)


def py_call_method_and_args_kw(kwa, *method_and_args):
"""
Calls a Python callable (function, method, or constructor) with the provided arguments.

Handles various cases including:
- Bound methods.
- Function or method name as a string with an instance.
- Class and method name as a string.
- Object and method name as a string.
- Unbound methods with an instance.
- Other callable objects.

Args:
method_and_args: Variable length argument list.

Returns:
The result of the callable invocation.

Raises:
ValueError: If no callable is provided.
TypeError: If the callable cannot be invoked.
AttributeError: If the method does not exist on the given class or instance.
"""

if DEBUG_MODE:
print("Debug: Initial method_and_args =", method_and_args)
print("Debug: Initial kwa =", kwa)

# Check if a single argument is provided and if it is a list or tuple
if len(method_and_args) == 1 and isinstance(method_and_args[0], (list, tuple)):
method_and_args = method_and_args[0]
if DEBUG_MODE:
print("Debug: Unpacked method_and_args =", method_and_args)

kwargs = kwa


# Ensure there is at least one element to extract the callable
if not method_and_args:
raise ValueError("No callable provided to invoke.")

callable_obj, *args = list(method_and_args)

# Debug after extracting callable and args
if DEBUG_MODE:
print("Debug: Callable object =", callable_obj)
print("Debug: Positional arguments =", args)

# Case 1: Bound method
if callable(callable_obj) and hasattr(callable_obj, ''__self__'') and callable_obj.__self__ is not None:
# Call the bound method with the arguments
return py_call_kw_args(kwargs, callable_obj, *args)

# Case 2: Function or method name as a string with an instance
if isinstance(callable_obj, str) and len(args) > 0 and isinstance(args[0], object):
method_name = callable_obj
instance = args[0]
method_args = args[1:]

# Attempt to retrieve the method from the instance
method = getattr(instance, method_name, None)
if method is None or not callable(method):
raise AttributeError(f"The instance has no callable method named ''{method_name}''.")

# Call the method with the arguments
return py_call_kw_args(kwargs, method, *method_args)

# Case 3: Class and method name as a string
if isinstance(callable_obj, type) and len(args) > 0 and isinstance(args[0], str):
cls = callable_obj
method_name = args[0]
method_args = args[1:]

# Attempt to retrieve the method from the class
method = getattr(cls, method_name, None)
if method is None or not callable(method):
raise AttributeError(f"The class ''{cls.__name__}'' has no callable method named ''{method_name}''.")

# Call the method with the arguments
return py_call_kw_args(kwargs, method, *method_args)

# Case 4: Object and method name as a string
if len(method_and_args) > 1 and isinstance(method_and_args[0], object) and isinstance(method_and_args[1], str):
obj = method_and_args[0]
method_name = method_and_args[1]
args = method_and_args[2:]

# Retrieve the method from the object
method = getattr(obj, method_name, None)
if method is None or not callable(method):
raise AttributeError(f"The object has no callable method named ''{method_name}''.")

# Call the method with the arguments
return py_call_kw_args(kwargs, method, *args)

# Case 5: Unbound method (function) with an instance
if len(args) > 0 and callable(callable_obj) and isinstance(args[0], object):
instance = args[0]
method_args = args[1:]

# Bind the method to the instance and call it
return py_call_kw_args(kwargs, callable_obj, *method_args)

# Case 6: Other callable objects
if callable(callable_obj):
return py_call_kw_args(kwargs, callable_obj, *args)

# If none of the above, raise an error
raise TypeError("The provided arguments do not form a callable invocation.")

import inspect


def py_call_kw_args(kwargs, callable_obj, *w_args):
"""
Calls a callable object with positional and keyword arguments,
ensuring compatibility with its signature.

:param kwargs: Dictionary of keyword arguments.
:param callable_obj: The callable object to be invoked.
:param w_args: Additional positional arguments.
:return: The result of invoking the callable object.
:raises ValueError: If the first argument is not callable.
"""
if not callable(callable_obj):
raise ValueError("First argument must be callable.")

args = list(w_args) # Positional arguments
sig = inspect.signature(callable_obj)

# Separate the expected keyword arguments from the function signature
kwarg_names = {param.name for param in sig.parameters.values()
if param.kind in [inspect.Parameter.KEYWORD_ONLY, inspect.Parameter.VAR_KEYWORD]}

# Prepare arguments for the callable
method_args = []
method_kwargs = {}

# Positional arguments from the signature
for i, (name, param) in enumerate(sig.parameters.items()):
if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:
if i < len(args):
method_args.append(args[i])
elif name in kwargs:
method_args.append(kwargs.pop(name))
elif param.default is not param.empty:
method_args.append(param.default)
else:
raise TypeError(f"Missing required positional argument: \'{name}\'")

# Handle *args (VAR_POSITIONAL)
for param in sig.parameters.values():
if param.kind == inspect.Parameter.VAR_POSITIONAL:
remaining_args = args[len(method_args):] # Extract remaining arguments
method_args.extend(remaining_args)
break

# Handle keyword-only arguments
for name, param in sig.parameters.items():
if param.kind == inspect.Parameter.KEYWORD_ONLY:
if name in kwargs:
method_kwargs[name] = kwargs.pop(name)
elif param.default is not param.empty:
method_kwargs[name] = param.default
else:
raise TypeError(f"Missing required keyword-only argument: \'{name}\'")

# Handle **kwargs if present in the signature
if any(param.kind == inspect.Parameter.VAR_KEYWORD for param in sig.parameters.values()):
method_kwargs.update(kwargs)
elif kwargs:
# If the function does not accept **kwargs and extras are provided
raise TypeError(f"Got unexpected keyword arguments: {\', \'.join(kwargs.keys())}")

# Debugging output
if DEBUG_MODE:
print("Debug Information:")
print(f"Callable object: {callable_obj}")
print(f"Positional arguments: {method_args}")
print(f"Keyword arguments: {method_kwargs}")

# Call the function with the prepared arguments
try:
return callable_obj(*method_args, **method_kwargs)
finally:
flush_stdout_stderr()



import importlib
import types
from types import MethodType
Expand Down Expand Up @@ -805,9 +1000,7 @@
if not isinstance(method, str):
raise TypeError(f"The method should be a string or callable, got {type(method)} instead.")



# Resolve the target if it is a string (assumed to be a module or class name)
# Resolve the target if it is a string (assumed to be a module or class name)
if isinstance(target, str):
# search the string type for the method name
str_callable = getattr(target, method, None)
Expand Down Expand Up @@ -967,15 +1160,32 @@
%
% @arg O The input list to be converted toi call.
% @arg Py The result.
py_call_method_and_args([F|List], Py):- maplist(py_arg,List,PyArgs),py_obi(py_call_method_and_args([F|PyArgs]),Py),!.
py_call_method_and_args([F|List], Py):- select([Kw|Args],List,NewList), Kw=='Kwargs', must_det_lls((make_kw_args(Args,KeyWordArgs),
maplist(py_arg,NewList,PyArgs),
py_list([F|PyArgs],PyList),
py_obi(py_call_method_and_args_kw(KeyWordArgs,PyList),Py))),!.
py_call_method_and_args([F|List], Py):- must_det_lls((maplist(py_arg,List,PyArgs),py_obi(py_call_method_and_args([F|PyArgs]),Py))),!.

/*
map_tuple(P2,DashA,DashB):- compound(DashA),compound_name_arguments(DashA,'-',ArgsA),maplist(P2,ArgsA,ArgsB),compound_name_arguments(DashB,'-',ArgsB).
map_tuple(P2,A,AA):- \+ compound(A),call(P2,A,AA),!.
map_tuple(P2,-A,-AA):- map_tuple(P2,A,AA),!.
map_tuple(P2,A-B,AA-BB):- map_tuple(P2,A,AA),!,map_tuple(P2,B,BB),!.
map_tuple(P2,A,AA):- call(P2,A,AA),!.
*/
pair_arg(NonCompound,_,_):- \+ compound(NonCompound), !,fail.
% Handle compound terms like (key=value)
pair_arg(Compound, Key,PyValue) :- compound(Compound), Compound = (Key=Value), !, py_arg(Value, PyValue).
% Handle list with key-value pair represented as [key, ':', value]
pair_arg([Key, Delimiter, Value], Key,PyValue) :- Delimiter == ':', !, py_arg(Value, PyValue).
% Handle list with key-value pair represented as [key=value]
pair_arg([KeyEquals, Value], Key,PyValue) :- symbol(KeyEquals), atom_concat(Key, '=', KeyEquals), !, py_arg(Value, PyValue).
% Handle list with key-value pair represented as [key:value]
pair_arg([KeyColon, Value], Key,PyValue) :- symbol(KeyColon),atom_concat(Key, ':', KeyColon), !, py_arg(Value, PyValue).

make_kw_args(KwArgs,KeyWordArgs):- lists_to_pairlist(KwArgs,Pairs),py_dict(Pairs,KeyWordArgs).
lists_to_pairlist(L,L):- \+ compound(L),!.
lists_to_pairlist([Kw,Val|Args],[[Key,Value]|KeyWordArgs]):- symbol(Kw),pair_arg_s([Kw,Val],Key,Value),!, lists_to_pairlist(Args,KeyWordArgs).
lists_to_pairlist([Kw,Eq,Val|Args],[[Key,Value]|KeyWordArgs]):- symbol(Kw),pair_arg_s([Kw,Eq,Val],Key,Value),!, lists_to_pairlist(Args,KeyWordArgs).
lists_to_pairlist([List|Args],[[Key,Value]|KeyWordArgs]):- pair_arg_s(List,Key,Value),!, lists_to_pairlist(Args,KeyWordArgs).
lists_to_pairlist([[List|Args]],[[Key,Value]|KeyWordArgs]):- pair_arg_s(List,Key,Value),!, lists_to_pairlist(Args,KeyWordArgs).
%lists_to_pairlist([],[]).

pair_arg_s(List,Key,PyValue):- pair_arg(List,Key,PyValue),!.
pair_arg_s([Key,Value],Key,PyValue):- symbolic(Key), py_arg(Value, PyValue),!.

% !((py-atom print) "Hello, World!" [ = end "\n\n\n" ])
% py_arg(Var,Var):- var(Var),!.
Expand All @@ -993,13 +1203,7 @@
%py_arg(Dict, PyObject) :- compound(Dict),Dict='{}'(KVL),conjuncts_to_list(KVL,List),is_dict(Number), py_dict(Dict, PyObject), !.
py_arg(PythonObject, PythonObject) :- py_is_object(PythonObject), !.
% Handle compound terms like (key=value)
py_arg(Compound, {Key: PyValue}) :- compound(Compound), Compound = (Key=Value), !, py_arg(Value, PyValue).
% Handle list with key-value pair represented as [key, ':', value]
py_arg([Key, Delimiter, Value], {Key: PyValue}) :- Delimiter == ':', !, py_arg(Value, PyValue).
% Handle list with key-value pair represented as [key=value]
py_arg([KeyEquals, Value], {Key: PyValue}) :- symbol(KeyEquals), atom_concat(Key, '=', KeyEquals), !, py_arg(Value, PyValue).
% Handle list with key-value pair represented as [key:value]
py_arg([KeyColon, Value], {Key: PyValue}) :- symbol(KeyColon),atom_concat(Key, ':', KeyColon), !, py_arg(Value, PyValue).
py_arg(Compound, {Key: PyValue}) :- pair_arg(Compound, Key,PyValue).
% Handle Python-native objects
py_arg(PythonNativeObject, PythonNativeObject) :- py_is_py(PythonNativeObject), !.
% Handle strings
Expand Down Expand Up @@ -1118,14 +1322,21 @@
raise ValueError("Provided object cannot be iterated or converted to an iterable.")

# chain python objects with | (syntactic sugar for langchain)
def py_chain(metta_tuple):
def py_chain_metta(metta_tuple):
unwrap1 = rust_deref(metta_tuple)
objects = [rust_deref(a) for a in get_children(unwrap1)]
result = objects[0]
for obj in objects[1:]:
result = result | obj
return result

def py_chain(objects):
result = objects[0]
for obj in objects[1:]:
result = result | obj
return result


def rust_metta_run(obj):
return runner.run(obj)

Expand Down

0 comments on commit 20231f7

Please sign in to comment.