1

Before I transferred this code to the class, get_owner_by_number_input_value1 worked fine, but in the class, now it does not work and gives an error "get_owner_number_input_value1 () Got Multiple Values for Argument 'Self'" and now only the option with get_owner_by_number_input_value2. I do not understand what a mistake, I looked like similar questions and did not find similarity with my. The head hurts, spent a bunch of time, but no sense. Big request to help, because I do not know what to do and where else to contact)

import inspect


documents = [
    {"type": "passport", "number": "2207 876234", "name": "Gloria", },
    {"type": "invoice", "number": "11-2", "name": "Jack Nickolas", },
    {"type": "insurance", "number": "10006", "name": "John", }
]

class DocumentManager:
    def __init__(self, doc):
        self._documents = doc

    def input_arguments_with_prompts(*prompts):
        def decorator(func):
            def wrapper(*args, **kwargs):
                parameters = inspect.signature(func).parameters
                for prompt, (name, parameter) in zip(prompts, parameters.items()):
                    while True:
                        value = input(prompt)
                        if parameter.annotation is inspect._empty:
                            converted_value = value
                            break
                        try:
                            converted_value = parameter.annotation(value)
                        except ValueError:
                            pass
                        else:
                            break
                    kwargs[name] = converted_value
                return func(*args, **kwargs)
            return wrapper
        return decorator

    def get_owner_by_number(self, number: str) -> str:
        for document in self._documents:
            if document["number"] == number:
                return document["name"]
        return ""

    @input_arguments_with_prompts(
        "Enter the document number, the owner of which you want to get: ",
    )
    def get_owner_by_number_input_value1(self, input_number: str):
        return self.get_owner_by_number(input_number)

    def get_owner_by_number_input_value2(self):
        input_number = input('Enter number')
        return self.get_owner_by_number(input_number)

dm = DocumentManager(documents)
dm.get_owner_by_number_input_value1()
  • Have you tried moving the decorator outside of the class? If you have not seen it yet: https://stackoverflow.com/questions/11740626/can-a-method-be-a-decorator-of-another-method-of-the-same-class – PM 77-1 Dec 01 '21 at 16:43
  • @PM77-1 Yes, I've tried both with self inside the class and without, and outside the class. The error is the same, but when inside the class specifying self, it throws an error that the required input_number argument is missing – Роман Яровой Dec 01 '21 at 18:30

1 Answers1

0

This isn't perfect because apparently you can't detect whether a function is in a class, but you can use a heuristic for checking that case and hope the users of this decorator follow the convention of naming the first argument "self" for methods in a class.

import inspect


documents = [
    {"type": "passport", "number": "2207 876234", "name": "Gloria", },
    {"type": "invoice", "number": "11-2", "name": "Jack Nickolas", },
    {"type": "insurance", "number": "10006", "name": "John", }
]

def input_arguments_with_prompts(*prompts):
    def decorator(func):
        def wrapper(*args, **kwargs):
            parameter_list = list(inspect.signature(func).parameters.items())
            if parameter_list[0][0] == "self":
                parameter_list = parameter_list[1:]
            for prompt, (name, parameter) in zip(prompts, parameter_list):
                while True:
                    value = input(prompt)
                    if parameter.annotation is inspect._empty:
                        converted_value = value
                        break
                    try:
                        converted_value = parameter.annotation(value)
                    except ValueError:
                        pass
                    else:
                        break
                kwargs[name] = converted_value
            return func(*args, **kwargs)
        return wrapper
    return decorator

class DocumentManager:
    def __init__(self, doc):
        self._documents = doc

    def get_owner_by_number(self, number: str) -> str:
        for document in self._documents:
            if document["number"] == number:
                return document["name"]
        return ""

    @input_arguments_with_prompts(
        "Enter the document number, the owner of which you want to get: ",
    )
    def get_owner_by_number_input_value1(self, input_number: str):
        return self.get_owner_by_number(input_number)

    def get_owner_by_number_input_value2(self):
        input_number = input('Enter number')
        return self.get_owner_by_number(input_number)

dm = DocumentManager(documents)
print(dm.get_owner_by_number_input_value1())

If you want to support classmethods as well, you could possibly change the check to if parameter_list[0][0] in {"self", "cls"}. There's also a heuristic from the answer I linked for checking if there's a . in the method's qualified name so you could also add that if you wanted.

rchome
  • 2,623
  • 8
  • 21
  • Hello again) The question is a little off topic, but with such an implementation, I can't do that, for example, if the parameter need_input = True in the function, then the decorator is executed, if False is not executed. https://pastebin.com/72aFvGME I did so, but it simply does not react, no matter what parameter need_input I specify when calling the function, only the one that is specified when the function is declared will be taken – Роман Яровой Dec 02 '21 at 15:08
  • 1
    maybe ask it as a separate question so it gets more eyes on it? – rchome Dec 02 '21 at 15:31