views:

1769

answers:

4

I was wondering about the best practices for indicating invalid argument combinations in Python. I've come across a few situations where you have a function like so:

def import_to_orm(name, save=False, recurse=False):
    """
    :param name: Name of some external entity to import.
    :param save: Save the ORM object before returning.
    :param recurse: Attempt to import associated objects as well. Because you
        need the original object to have a key to relate to, save must be
        `True` for recurse to be `True`.
    :raise BadValueError: If `recurse and not save`.
    :return: The ORM object.
    """
    pass

The only annoyance with this is that every package has its own, usually slightly differing BadValueError. I know that in Java there exists java.lang.IllegalArgumentException -- is it well understood that everybody will be creating their own BadValueErrors in Python or is there another, preferred method?

+2  A: 

I've mostly just seen the builtin ValueError used in this situation.

Eli Courtwright
+2  A: 

I would inherit from ValueError

class IllegalArgumentError(ValueError):
    pass

It is often best to create your own exceptions, but inherit from a built-in one, which is as close to what you want as possible.

MizardX
A: 

I'm not sure I agree with inheritance from ValueError -- my interpretation of the documentation is that ValueError is only supposed to be raised by builtins... inheriting from it or raising it yourself seems incorrect.

Raised when a built-in operation or function receives an argument that has the right type but an inappropriate value, and the situation is not described by a more precise exception such as IndexError.

-- ValueError documentation

cdleary
Built-in operation *OR* function..
dbr
Compare http://www.google.com/codesearch?q=lang:python+class\+\w*Error\(([^E]\w*|E[^x]\w*)\): with http://www.google.com/codesearch?q=lang:python+class\+\w*Error\(Exception\):
MizardX
@dbr: Yeah, I think they mean "(built-in operation or function)", not "(built-in operation) or function". I would think they'd have contrasted it by saying "user defined" in the second case.
cdleary
@MizardX: That is interesting, as is http://www.google.com/codesearch?q=lang%3Apython+class\+IllegalArgumentException -- they split between inheritance from ValueError, Exception, and BaseException.
cdleary
That blurb simply means that built-ins raise it, and not that *only* built-ins can raise it. It would not be entirely appropriate in this instance for the Python documentation to talk about what external libraries raise.
Ignacio Vazquez-Abrams
Every piece of Python software I've ever seen has used `ValueError` for this sort of thing, so I think you're trying to read too much into the documentation.
James Bennett
@James Bennett: We cited several projects in the above Google code searches that do not use ValueError directly, so there at least seems to be a need for clarification.
cdleary
Err, if we're going to use Google Code searches to argue this: http://www.google.com/codesearch?q=lang%3Apython+raise%5C+ValueError # 66,300 cases of raising ValueError, including Zope, xen, Django, Mozilla (and that's just from the first page of results). If a builtin exception fits, use it..
dbr
+19  A: 

I would just raise ValueError, unless you need a more specific exception..

def import_to_orm(name, save=False, recurse=False):
    if recurse and not save:
        raise ValueError("save must be True if recurse is True")

There's really no point in doing class BadValueError(ValueError):pass - your custom class is identical in use to ValueError, so why not use that?

dbr
Agreed -- I almost always use ValueError for stuff like this, too.
mipadi
> "so why not use that?" - Specificity. Perhaps I want to catch at some outer layer "MyValueError", but not any/all "ValueError".
Kevin Little
Yeah, so part of the question of specificity is where else ValueError is raised. If the callee function likes your arguments but calls math.sqrt(-1) internally, a caller may be catching ValueError expect that *its* arguments were inappropriate. Maybe you just check the message in this case...
cdleary