Validator for vertices of a polygon

Posted on

Problem

There is an object which has few properties. I wrote a function for test cases which check if the properties are satisfied or not. The function should throw an exception if the property is not satisfied.

But, I think there are better ways to write those test cases. A subset of the property of the object is as follows:

x[‘polygon’] is a list of >= 3 integer (x,y) pairs representing the
corners of the polygon in clockwise or anticlockwise order.

Current function is as follows:

def validate_object(x):
    """This function validates an object x that is supposed to represent an object
    inside an image, and throws an exception on failure.
    Specifically it is checking that:
      x['polygon'] is a list of >= 3 integer (x,y) pairs representing the corners
                    of the polygon in clockwise or anticlockwise order.
    """
    if type(x) != dict:
        raise ValueError('dict type input required.')

    if 'polygon' not in x:
        raise ValueError('polygon object required.')

    if not isinstance(x['polygon'], (list,)):
        raise ValueError('list type polygon object required.')

    points_list = x['polygon']
    if len(points_list) < 3:
        raise ValueError('More than two points required.')

    for x, y in points_list:
        if type(x) != int or type(y) != int:
            raise ValueError('integer (x,y) pairs required.')

    return

It would be really helpful if someone could please suggest better ways of writing those test cases.

Solution

In Python, if you do not specify the return value of a function, then by default it will return None. So no need to hard code return in your case.

You do not need to create the extra variable points_list as you can directly write:

  if len(x['polygon']) < 3:
        raise ValueError('More than two points required.')

and:

 for x, y in x['polygon']:
    if type(x) != int or type(y) != int:
        raise ValueError('integer (x,y) pairs required.')

Of course, choosing a meaningful and significant name for the object x will be better.

When we check if stuff in some_iterable we get True or False. I mean that a ValueError exception is not the most suitable thing that can happen here.

isnstance() returns True or False, and may raise a TypeError exception under certain conditions (check the link), so here also raising a ValueError may not be appropriate.

Because when you raise the exception, you communicate to the user only what he needs to know (through the messages you specified for each case) and because of the last 2 points I mentioned previously, I suggest you to create a custom exception which message can be overridden for each situation:

class InvalidPolygonObject(Exception):
   pass

And your function can be written this way:

def validate_object(polygon_object):  
    if type(polygon_object) != dict:
        raise InvalidPolygonObject('dict type input required.')

    if 'polygon' not in polygon_object:
        raise InvalidPolygonObject('polygon object required.')

    if not isinstance(polygon_object['polygon'], (list,)):
        raise InvalidPolygonObject('list type polygon object required.')

    if len(polygon_object['polygon']) < 3:
        raise InvalidPolygonObject('More than two points required.')

    for x, y in polygon_object['polygon']:
        if type(x) != int or type(y) != int:
            raise InvalidPolygonObject('integer (x,y) pairs required.')

Leave a Reply

Your email address will not be published. Required fields are marked *