示例#1
0
    def fill(self, argument, convert_to_ast=False, call_my_args=True):
        """ Try to fill this argument slot with the given argument. Return
        a ConstantArg containing the result. If there is a type problem,
        raise a TATypeError. """
        if isinstance(argument, ast.AST):
            convert_to_ast = True

        # 1. can the argument be called?
        (func_disjunction, args) = (None, [])
        if (isinstance(argument, tuple) and argument
                and callable(argument[0])):
            func_disjunction = argument[0]
            if len(argument) >= 2 and isinstance(argument[1], LogoCode):
                args = argument[2:]
            else:
                args = argument[1:]
        elif callable(argument):
            func_disjunction = argument

        # make sure we can loop over func_disjunction
        if not isinstance(func_disjunction, PrimitiveDisjunction):
            func_disjunction = PrimitiveDisjunction((func_disjunction, ))

        error = None
        bad_value = argument  # the value that caused the TATypeError
        for func in func_disjunction:
            error = None
            for slot in self.get_alternatives():

                if isinstance(slot.wrapper, PrimitiveDisjunction):
                    wrapper_disjunction = slot.wrapper
                else:
                    wrapper_disjunction = PrimitiveDisjunction((slot.wrapper,))

                for wrapper in wrapper_disjunction:

                    # check if the argument can fill this slot (type-wise)
                    # (lambda functions are always accepted)
                    if getattr(func, '__name__', None) == '<lambda>':
                        converter = identity
                        old_type = TYPE_OBJECT
                        new_type = slot.type
                    else:
                        if wrapper is not None:
                            arg_types = get_type(wrapper)[0]
                            bad_value = wrapper
                        elif func is not None:
                            arg_types = get_type(func)[0]
                            bad_value = func
                        else:
                            arg_types = get_type(argument)[0]
                            bad_value = argument
                        converter = None
                        if not isinstance(arg_types, TypeDisjunction):
                            arg_types = TypeDisjunction((arg_types, ))
                        if isinstance(slot.type, TypeDisjunction):
                            slot_types = slot.type
                        else:
                            slot_types = TypeDisjunction((slot.type, ))
                        for old_type in arg_types:
                            for new_type in slot_types:
                                converter = get_converter(old_type, new_type)
                                if converter is not None:
                                    break
                            if converter is not None:
                                break
                        # unable to convert, try next wrapper/ slot/ func
                        if converter is None:
                            continue

                    # 1. (cont'd) call the argument or pass it on as a callable
                    called_argument = argument
                    if func is not None:
                        func_prim = func
                        if not isinstance(func_prim, Primitive):
                            func_prim = Primitive(
                                func_prim,
                                [ArgSlot(TYPE_OBJECT)] * len(args))
                        try:
                            func_prim = func_prim.fill_slots(
                                args,
                                convert_to_ast=convert_to_ast,
                                call_my_args=(slot.call_arg and call_my_args))
                        except TATypeError as error:
                            if Primitive._DEBUG:
                                traceback.print_exc()
                            # on failure, try next wrapper/ slot/ func
                            bad_value = error.bad_value
                            continue
                        if convert_to_ast:
                            called_argument = func_prim.get_ast()
                        else:
                            if slot.call_arg and call_my_args:
                                # call and pass on the return value
                                called_argument = func_prim()
                            else:
                                # don't call and pass on the callable
                                called_argument = func_prim

                    # 2. apply any wrappers
                    wrapped_argument = called_argument
                    if wrapper is not None:
                        if convert_to_ast:
                            if not hasattr(wrapper, "get_ast"):
                                raise PyExportError(
                                    ("cannot convert callable"
                                     " %s to an AST") % (repr(wrapper)))
                            wrapped_argument = wrapper.get_ast(
                                called_argument)
                        else:
                            if slot.call_arg and call_my_args:
                                wrapped_argument = wrapper(called_argument)
                            else:
                                wrapped_argument = wrapper.fill_slots(
                                    [called_argument], call_my_args=False)

                    # last chance to convert raw values to ASTs
                    # (but not lists of ASTs)
                    if (convert_to_ast and
                            not isinstance(wrapped_argument, ast.AST) and
                            not (isinstance(wrapped_argument, list) and
                                 wrapped_argument and
                                 isinstance(wrapped_argument[0], ast.AST))):
                        wrapped_argument = value_to_ast(wrapped_argument)

                    # 3. check the type and convert the argument if necessary
                    converted_argument = wrapped_argument
                    if slot.call_arg and call_my_args:
                        try:
                            converted_argument = convert(
                                wrapped_argument,
                                new_type, old_type=old_type,
                                converter=converter)
                        except TATypeError as error:
                            if Primitive._DEBUG:
                                traceback.print_exc()
                            # on failure, try next wrapper/ slot/ func
                            bad_value = wrapped_argument
                            continue
                    elif converter != identity:
                        converted_argument = Primitive(
                            converter,
                            return_type=new_type,
                            arg_descs=[ConstantArg(wrapped_argument,
                                                   value_type=old_type,
                                                   call_arg=False)])
                    # on success, return the result
                    return ConstantArg(
                        converted_argument,
                        value_type=new_type,
                        call_arg=(slot.call_arg and call_my_args))

        # if we haven't returned anything yet, then all alternatives failed
        if error is not None:
            raise error
        else:
            raise TATypeError(bad_value=bad_value, bad_type=old_type,
                              req_type=new_type)
    def fill(self, argument, convert_to_ast=False, call_my_args=True):
        """ Try to fill this argument slot with the given argument. Return
        a ConstantArg containing the result. If there is a type problem,
        raise a TATypeError. """
        if isinstance(argument, ast.AST):
            convert_to_ast = True

        # 1. can the argument be called?
        (func_disjunction, args) = (None, [])
        if (isinstance(argument, tuple) and argument
                and callable(argument[0])):
            func_disjunction = argument[0]
            if len(argument) >= 2 and isinstance(argument[1], LogoCode):
                args = argument[2:]
            else:
                args = argument[1:]
        elif callable(argument):
            func_disjunction = argument

        # make sure we can loop over func_disjunction
        if not isinstance(func_disjunction, PrimitiveDisjunction):
            func_disjunction = PrimitiveDisjunction((func_disjunction, ))

        error = None
        bad_value = argument  # the value that caused the TATypeError
        for func in func_disjunction:
            error = None
            for slot in self.get_alternatives():

                if isinstance(slot.wrapper, PrimitiveDisjunction):
                    wrapper_disjunction = slot.wrapper
                else:
                    wrapper_disjunction = PrimitiveDisjunction((slot.wrapper,))

                for wrapper in wrapper_disjunction:

                    # check if the argument can fill this slot (type-wise)
                    # (lambda functions are always accepted)
                    if getattr(func, '__name__', None) == '<lambda>':
                        converter = identity
                        old_type = TYPE_OBJECT
                        new_type = slot.type
                    else:
                        if wrapper is not None:
                            arg_types = get_type(wrapper)[0]
                            bad_value = wrapper
                        elif func is not None:
                            arg_types = get_type(func)[0]
                            bad_value = func
                        else:
                            arg_types = get_type(argument)[0]
                            bad_value = argument
                        converter = None
                        if not isinstance(arg_types, TypeDisjunction):
                            arg_types = TypeDisjunction((arg_types, ))
                        if isinstance(slot.type, TypeDisjunction):
                            slot_types = slot.type
                        else:
                            slot_types = TypeDisjunction((slot.type, ))
                        for old_type in arg_types:
                            for new_type in slot_types:
                                converter = get_converter(old_type, new_type)
                                if converter is not None:
                                    break
                            if converter is not None:
                                break
                        # unable to convert, try next wrapper/ slot/ func
                        if converter is None:
                            continue

                    # 1. (cont'd) call the argument or pass it on as a callable
                    called_argument = argument
                    if func is not None:
                        func_prim = func
                        if not isinstance(func_prim, Primitive):
                            func_prim = Primitive(
                                func_prim,
                                [ArgSlot(TYPE_OBJECT)] * len(args))
                        try:
                            func_prim = func_prim.fill_slots(
                                args,
                                convert_to_ast=convert_to_ast,
                                call_my_args=(slot.call_arg and call_my_args))
                        except TATypeError as error:
                            if Primitive._DEBUG:
                                traceback.print_exc()
                            # on failure, try next wrapper/ slot/ func
                            bad_value = error.bad_value
                            continue
                        if convert_to_ast:
                            called_argument = func_prim.get_ast()
                        else:
                            if slot.call_arg and call_my_args:
                                # call and pass on the return value
                                called_argument = func_prim()
                            else:
                                # don't call and pass on the callable
                                called_argument = func_prim

                    # 2. apply any wrappers
                    wrapped_argument = called_argument
                    if wrapper is not None:
                        if convert_to_ast:
                            if not hasattr(wrapper, "get_ast"):
                                raise PyExportError(
                                    ("cannot convert callable"
                                     " %s to an AST") % (repr(wrapper)))
                            wrapped_argument = wrapper.get_ast(
                                called_argument)
                        else:
                            if slot.call_arg and call_my_args:
                                wrapped_argument = wrapper(called_argument)
                            else:
                                wrapped_argument = wrapper.fill_slots(
                                    [called_argument], call_my_args=False)

                    # last chance to convert raw values to ASTs
                    # (but not lists of ASTs)
                    if (convert_to_ast and
                            not isinstance(wrapped_argument, ast.AST) and
                            not (isinstance(wrapped_argument, list) and
                                 wrapped_argument and
                                 isinstance(wrapped_argument[0], ast.AST))):
                        wrapped_argument = value_to_ast(wrapped_argument)

                    # 3. check the type and convert the argument if necessary
                    converted_argument = wrapped_argument
                    if slot.call_arg and call_my_args:
                        try:
                            converted_argument = convert(
                                wrapped_argument,
                                new_type, old_type=old_type,
                                converter=converter)
                        except TATypeError as error:
                            if Primitive._DEBUG:
                                traceback.print_exc()
                            # on failure, try next wrapper/ slot/ func
                            bad_value = wrapped_argument
                            continue
                    elif converter != identity:
                        converted_argument = Primitive(
                            converter,
                            return_type=new_type,
                            arg_descs=[ConstantArg(wrapped_argument,
                                                   value_type=old_type,
                                                   call_arg=False)])
                    # on success, return the result
                    return ConstantArg(
                        converted_argument,
                        value_type=new_type,
                        call_arg=(slot.call_arg and call_my_args))

        # if we haven't returned anything yet, then all alternatives failed
        if error is not None:
            raise error
        else:
            raise TATypeError(bad_value=bad_value, bad_type=old_type,
                              req_type=new_type)