Guimoute
Guimoute

Reputation: 4649

How to elegantly make a custom assertion error?

I made a bunch of functions that control a message-based instrument. I know how to use them but the functions should be fool-proof in case somebody else wants to code with it after I'm gone. Here is an example function:

def AutoRange(self, Function, Source, Value):
    """Enable or disable the instrument autoranging feature.\n
    :Function: "source" or "measure".
    :Source: "v" or "i".
    :Value: 0 or 1."""

    # <Something goes here to check if the inputs are valid>
    self.WRITE("smua.%s.autorange%s = %s" %(Function, Source, Value))

To comply with the input requirements I initially added a RaiseException at the start in an ugly way:

    AllowedFunctions = ("source", "measure")
    AllowedSources = ("v", "i")
    AllowedValues = (0, 1)
    if not(Function in AllowedFunctions and Source in AllowedSources and Value in AllowedValues):
        raise Exception("/!\ Error in Keithley.Autorange(): Input not allowed.")

After reading about assert() I replaced the if with this:

    MyErrorMessage = "/!\ Error in Keithley.Autorange(): Input not allowed."
    assert (Function in ("source", "measure")), MyErrorMessage
    assert (Source in ("v", "i")), MyErrorMessage
    assert (Value in (0, 1)), MyErrorMessage

A possible neater variant would be this:

    ValidInputs = (    Function in ("source", "measure")
                   and Source in ("v", "i")
                   and Value in (0, 1))

    assert (ValidInputs), "/!\ Error in Keithley.Autorange(): Input not allowed."

The second option is nicer now that I think of it because then I can tell specifically which input was invalid. So my question is: is this how you should handle assertions? 1 assert() for 1 input? If not, what is the adviced structure? Thanks in advance.

Upvotes: 1

Views: 1274

Answers (1)

Amadan
Amadan

Reputation: 198476

Why not do what Python does?

Let's see an example:

open("foo", "q")
# => ValueError: invalid mode: 'q'

open is in C, but you can see how it looks like for e.g gzip.open:

if "t" in mode:
    if "b" in mode:
        raise ValueError("Invalid mode: %r" % (mode,))

Don't be scared of making the code a little longer; you can't really avoid it in Python. And also, "Error in Keithley.Autorange(): Input not allowed." doesn't tell the programmer which input is not allowed. In this case, explicit and clear is better than clever and short.

I'd do:

def auto_range(self, function, source, value):
    if function not in ["source", "measure"]:
        raise ValueError("Invalid function: %s" % function)
    if source not in ["v", "i"]:
        raise ValueError("Invalid source: %s" % source)
    if value not in [0, 1]:
        raise ValueError("Invalid value: %d" % value)
    # ...

Not directly relevant to your questions, but it is best to stick to the language's coding style: in Python, variables are in snake_case (lowercase with underscores).

Upvotes: 3

Related Questions