0

In the process of trying to validate a class property using "the canonical way to check for type in Python" I was trying to make a class with a bit of validation but where I'm trying to raise an error, instead the error is coming from the fact that the property isn't effectively being set. What am I doing wrong and how should I approach this.

# A class to represent a dataset upload replacement operation
import os

class DataReplacement:
def __init__(self, portal, inputfolder, input, output, audience):
    self.portal = portal
    self.inputfolder = inputfolder
    self.input = input
    self.output = output
    self.audience = audience
    self.fullpath = os.path.join(inputfolder, input)

@property
def fullpath(self):
    return self._fullpath

@fullpath.setter
def fullpath(self, value):
    try:
        isinstance(self.inputfolder, str) and isinstance(self.input, str)  
    except ValueError:
        raise ValueError("inputfolder and input must be a string") from None
    fp = os.path.join(self.inputfolder, self.input)
    if os.path.exists(fp):
        self._fullpath = os.path.join(self.inputfolder, self.input)
    else:
        raise IOError(f"file {self.fullpath} does not exist.") from None

So for example if I set an instance to a file that actually exists, I get no error but in a second example if the file doesn't exist I get an error but it wasn't the one I wanted to get (if that makes sense?).

dr = DataReplacement('www.mywebsite.com', r'/Volumes/Project01/homedev/data', 'test_csv.csv', 'wa4r-1992', 'private')

The above works but 'ashdfiwuehv.csv' doesn't exist

dr2 = DataReplacement('www.mywebsite.com', r'/Volumes/Project01/homedev/data', 'ashdfiwuehv.csv', 'wa4r-1992', 'private')

I get ... AttributeError: 'DataReplacement' object has no attribute '_fullpath' instead of the IOError I was trying to raise.

jbchurchill
  • 172
  • 2
  • 10
  • 2
    Can't help thinking your use of input as an argument variable is causing issues as input is a reserved key word. Try using another variable name. – Galo do Leste Feb 08 '23 at 03:45
  • 1
    Please review [formatting help](/help/formatting): it is not sufficient to simply indent _some_ lines of your code. You must indent _all_ of them, or indent none and add a line above and below with three backticks (`\``) – Pranav Hosangadi Feb 08 '23 at 03:45
  • 1
    In your own words, what do you think `isinstance(self.inputfolder, str)` will do, if `self.inputfolder` is a string? What do you think it will do, if it isn't? What do you think would cause it to raise a `ValueError`? Why should the `except` block ever run? – Karl Knechtel Feb 08 '23 at 04:02
  • @JohnGordon not so; using the same method name is normal and expected (IIRC it will actually break otherwise) when creating a property with a setter using the decorator that way; and `self.fullpath =` in `__init__` will use the setter (which is perfectly valid, as long as `__init__` has already done whatever work is necessary for the setter to work, if any). – Karl Knechtel Feb 08 '23 at 04:05
  • 1
    @GalodoLeste it is a bad idea and can cause other problems, but it doesn't cause a specific problem here. The problem with this code is that it tries to use `try`/`except` to check something that **won't raise that exception**. – Karl Knechtel Feb 08 '23 at 04:06
  • 2
    Oh, wait. That isn't what was complained about either. Oh, I see it: Please try to think about the logic - how can we create an exception using the message `f"file {self.fullpath} does not exist."`, if the **purpose of the exception is to indicate** that `self._fullpath` was not set? (Keeping in mind that accessing `self.fullpath` depends on that setting.) – Karl Knechtel Feb 08 '23 at 04:12
  • But also, trying to do this kind of validation **doesn't make a lot of sense**. What if the file doesn't exist when someone tries to create the instance, but is created before the instance will be used? What if it *does* exist, but is *deleted* first? – Karl Knechtel Feb 08 '23 at 04:13
  • 1
    As @KarlKnechtel as mentioned you have a lot of errors in the code above. First isisntance method will return a boolean value depending if the arguments are of the types asked, but wouldn't raise a exception. – pipelog Feb 08 '23 at 04:27
  • 2
    But for your specific question the answer is simple, before raising the IOError python will execute the formating string ussing self.fullpath, but that will call the getter method defined with the @property decorator. That in turn will try to return the self._fullpath attribute but you didn't define it before that call, so it will rise the Attribute Error. – pipelog Feb 08 '23 at 04:36
  • Thanks all especially @KarlKnectel ... the "Oh wait" comment has it. I think this is telling me that I need to investigate capturing the error I'm getting and have it return the message I formulated. Also in the current version of my script I replaced "input" with "inputfile" since it is a bit smelly. – jbchurchill Feb 10 '23 at 02:11
  • I worked through this successfully with everyone's help. I don't see why it is a duplicate of a question that it doesn't even remotely represent. I'm using the recommended way of checking whether or not something is an instance of something else and had no confusion about that whatsoever but I digress. – jbchurchill Feb 10 '23 at 03:05
  • @pipelog, I was bitten by that same bug a few months back. – unlockme Feb 28 '23 at 16:01

0 Answers0