Skip to content

Standardized error messages for invalid arguments/parameters #3984

@seisman

Description

@seisman

For invalid arguments, we usually raise error messages like:

pygmt/pygmt/figure.py

Lines 497 to 500 in c156826

msg = (
f"Invalid display method '{method}'. "
"Valid values are 'external', 'notebook', 'none' or None."
)

msg = f"Invalid dataset name '{name}'."

msg = (
f"Invalid earth relief data source '{data_source}'. "
"Valid values are 'igpp', 'gebco', 'gebcosi', and 'synbath'."
)

There are also error messages about required parameters or invalid combinations of parameters, e.g.,

pygmt/pygmt/src/coast.py

Lines 208 to 213 in c156826

if not args_in_kwargs(args=["C", "G", "S", "I", "N", "E", "Q", "W"], kwargs=kwargs):
msg = (
"At least one of the following parameters must be specified: "
"lakes, land, water, rivers, borders, dcw, Q, or shorelines."
)
raise GMTInvalidInput(msg)

if not all(arg in kwargs for arg in ["D", "F", "N"]) and "Q" not in kwargs:
msg = (
"At least one of the following parameters must be specified: "
"distance, filters, or sectors."
)
raise GMTInvalidInput(msg)

pygmt/pygmt/src/filter1d.py

Lines 114 to 116 in c156826

if kwargs.get("F") is None:
msg = "Pass a required argument to 'filter_type'."
raise GMTInvalidInput(msg)

pygmt/pygmt/src/grdclip.py

Lines 109 to 114 in c156826

if all(v is None for v in (above, below, between, replace)):
msg = (
"Must specify at least one of the following parameters: ",
"'above', 'below', 'between', or 'replace'.",
)
raise GMTInvalidInput(msg)

So, it's better to centralize and standardize the error messasges. As far as I can see, there are some options:

Option 1: Multiple helper functions

We may define many helper functions like:

def errmsg_invalid_arguments(name, value, valid_values=None):
    msg = f"Invalid value for parameter {name}: '{value}'." 
    if valid_values:
        msg += f" Valid values are {valid_values}'.".  
    return msg

Option 2: Single helper functions

def errmsg(type, *args, **kwargs):
    match type:
        case "invalid_values":
            msg = f"Invalid value for parameter {name}: '{value}'." 
            if kwargs.get("valid_values"):
                msg += f" Valid values are {kwargs.get('valid_values')}'.".  
        case "invalid_parameters":
             ...
    return msg

Option 3: Custom exception with custom error messages

class InvalidValueError(ValueError):
    def __init__(self, arg, valid_values=None):
        message = f"Invalid value: {arg!r}"
        if valid_values is not None:
            message += f". Valid values are: {valid_values!r}"
        super().__init__(message)

I think options 2 or 3 are better. What do you think?

Option 3 is related to #3707.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementImproving an existing feature

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions