Search code examples
pythonclassdecoratorpython-decorators

Decorator class which checks if passed instance variable is not None


I have an issue where I'm trying to create a simple Class decorator which checks for passed variables presence (that is they're not None). Problem is, this is done inside another class, so the checked variables are from self.. Basically I'm trying to create a wrapper which iterates over given self variables and checks if they're not None.

class CheckForVariablesPresence:
  """Wrapper for checking if given variable(s) are not None.
  """

  def __init__(self, *variables):
    self.variables = variables

  def __call__(self, func):
    @wraps(func)
    def wrapper(*args, **kwargs):
      for variable in self.variables:
        if variable is None:
          raise KeyError(
            "Variable not found.\n",
            "Run the appropriate method first.",
          )

   return func(*args, **kwargs)

The thing is, I can't pass self.variable in the method's decorator, as self isn't even yet created.

@CheckForVariablesPresence(self.variable)
def __repr__(self):
  return str(self.variable)

Any ideas how I could do this differently (and better)? I started with one function for checking the exact variable, but the variables checked are growing and depend on each and different method I'm calling, thus the reason I wanted to use a wrapper which could iterate over them.


Solution

  • Self answer: This idea of doing this in that way is quite bad - as pointed out by @chepner in the comments. Although I think he meant doing something else, I resolved it in another way (due to limitations of how it had to work because of another module). Basically the __getattr__ method is what I was looking for and I've implemented a custom AttributeError message for when the attribute that's available, but not yet initialised, was called.

    This is a sample.

    def __getattr__(self, name: str):
        value = self.__dict__.get(name)
        if value is None and name in self.__available_attributes:
            raise AttributeError(
                f"Attribute {name} not found. Run the appropriate method first or set it."
            )
        elif value is None:
            raise AttributeError(f"{self.__class__.__name__}.{name} is invalid")
    
        return value