MisterPatate
MisterPatate

Reputation: 287

Python check if one or more values is None and knows which ones are

I have a Python app with a Firebase-database backend.

When I retrieve the data from my database, I want to check if those values are available (if not, that means that the database is somehow corrupted, as mandatories fields are missing)

My current implementation is the following:

self.foo = myDbRef.get('foo')
self.bar =  myDbRef.get('bar')
self.bip =  myDbRef.get('bip')
self.plop = myDbRef.get('plop')

if self.foo is None or self.bar is None or self.bip is None or self.plop is None:
    self.isValid = False
    return ErrorCode.CORRUPTED_DATABASE

This works fine, is compact, but have a major issue: I will get the information that the database is corrupted, but not what field is missing (could be just one of them, or more, or all !)

The idiomatic approach should be

if self.foo is None:
    self.isValid = False
    return ErrorCode.CORRUPTED_DATABASE, "FOO IS MISSING" # could be a string, an enum value, whatever, I have the information

if self.bar is None:
    self.isValid = False
    return ErrorCode.CORRUPTED_DATABASE, "BAR IS MISSING"

if self.bip is None:
    self.isValid = False
    return ErrorCode.CORRUPTED_DATABASE, "BIP IS MISSING"

But this is not pretty, not factorized (All my 'init from db' functions use the same pattern... I don't want to multiply my number of lines by a factor of 10 for such a case).

This is not a '100% python' question, but I hope the langage has something for me to handle this like a boss (it's python: it usually does !)

Upvotes: 5

Views: 334

Answers (4)

John Kugelman
John Kugelman

Reputation: 361849

You could extract the checks into a generator and leave the flag and return statements outside.

def invalid_fields():
    if self.foo is None: yield "FOO"
    if self.bar is None: yield "BAR"
    if self.bip is None: yield "BIP"

invalid = list(invalid_fields())
if invalid:
    self.isValid = False
    return ErrorCode.CORRUPTED_DATABASE, "MISSING {}".format(", ".join(invalid))

This has the advantage of telling you about all the missing fields if there are more than one.

Upvotes: 1

r.ook
r.ook

Reputation: 13888

You can dynamically create and search your instance attributes like so:

class Foo():
    def __init__(self):

        # First, define the list of attributes you want to look for and an empty list of errors
        self.attrbs = ['foo','bar','bip','plop']
        self.errors = []

        # Iterate through the attributes list
        for attrb in self.attrbs:

            # Create and assign self.foo to MyDbRef.get('foo'), etc
            self.__dict__[attrb] = myDbRef.get(attrb)

            # Check if attribute is empty, if so, add to error
            if not self.__dict__[attrb]:
                self.errors.append(attrb.upper())

        # Check if there are any errors
        if self.errors:
            self.is_valid = False
            return (ErrorCode.CORRUPTED_DATABASE, "MISSING {errs}".format(errs='/'.join(self.errors)))
        else:
            self.is_valid = True

Upvotes: 0

LucasB
LucasB

Reputation: 3563

Use a function and a loop:

def checknone(**things_with_names):
    for name, thing in things_with_names.items():
        if thing is None:
            return ErrorCode.CORRUPTED_DATABASE, name + " IS MISSING"
    return True

And use as such:

result = checknone(foo=self.foo, bar=self.bar, bip=self.bip, plop=self.plop)
if result is not True:
    self.isValid = False
    return result

For maximum gains, put it as a method of a class that you will Mixin into all your classes that use this. That way it can also set isValid.

Upvotes: 0

JacobIRR
JacobIRR

Reputation: 8956

I made a class to contain some of your functionality that I can't access. I also made ErrorCode a string as a hack, since that's not defined in my tools and I'm not sure how you want the None names returned with/beside the ErrorCode.

Build a dict of names and values, check that the dict contains no None values, and if it does, return which keys:

myDbRef = {'foo' : None,
            'bar': 1,
            'bip': 2,
            'plop': 3}
class Foo():

    def __init__(self):

        self.foo = myDbRef.get('foo')
        self.bar =  myDbRef.get('bar')
        self.bip =  myDbRef.get('bip')
        self.plop = myDbRef.get('plop')


    def check(self):

        temp_dict = {}

        for key in ['foo','bar','bip','plop']:
            temp_dict[key] = myDbRef.get(key)

        vals = {k:v for k,v in temp_dict.items() if v is None}
        if vals:
            self.isValid = False
            return ("ErrorCode.CORRUPTED_DATABASE", [k for k in vals.keys()])


f = Foo()
print(f.check())

Result: ('ErrorCode.CORRUPTED_DATABASE', ['foo'])

Upvotes: 0

Related Questions