2

In Python 3, I want to limit the permitted values that are passed to this method:

my_request(protocol_type, url)

Using type hinting I can write:

my_request(protocol_type: str, url: str)

so the protocol and url are limited to strings, but how can I validate that protocol_type accepts only limited set of values, e.g. 'http' and 'https'?

Ethan Furman
  • 57,917
  • 18
  • 142
  • 218
RaamEE
  • 2,475
  • 3
  • 24
  • 47
  • Does this answer your question? [Type hint for a function that returns only a specific set of values](https://stackoverflow.com/questions/39398138/type-hint-for-a-function-that-returns-only-a-specific-set-of-values) – Georgy Aug 21 '20 at 09:32

4 Answers4

8

One way is to write code in the method to validate that the value passed in is 'http' or 'https', something in the lines of:

if (protocol_type == 'http') or (protocol_type == 'https'):
  Do Something
else:
  Throw an exception

Which will work fine during runtime, but doesn't provide an indication of a problem while writing the code.

This is why I prefer using Enum and the type-hinting mechanism that Pycharm and mypy implement.

For the code example below you will get a warning in Pycharm from its code-inspection, see attached screenshot. The screenshot shows that if you enter a value that is not enum you will get the "Expected Type:..." warning.

Code:

"""Test of ENUM"""

from enum import Enum


class ProtocolEnum(Enum):
    """
    ENUM to hold the allowed values for protocol
    """
    HTTP: str = 'http'
    HTTPS: str = 'https'


def try_protocol_enum(protocol: ProtocolEnum) -> None:
    """
    Test of ProtocolEnum
    :rtype: None
    :param protocol: a ProtocolEnum value allows for HTTP or HTTPS only
    :return:
    """
    print(type(protocol))
    print(protocol.value)
    print(protocol.name)


try_protocol_enum(ProtocolEnum.HTTP)

try_protocol_enum('https')

Output:

<enum 'ProtocolEnum'>
http
HTTP

Warnings issued by Pycharm Static Code Analysis - Code Inspection

RaamEE
  • 2,475
  • 3
  • 24
  • 47
1

I guess you can use decorators, I have a similar situation but I wanted to validate the parameter types:

def accepts(*types):
    """
    Enforce parameter types for function
    Modified from https://stackoverflow.com/questions/15299878/how-to-use-python-decorators-to-check-function-arguments
    :param types: int, (int,float), if False, None or [] will be skipped
    """
    def check_accepts(f):
        def new_f(*args, **kwds):
            for (a, t) in zip(args, types):
                if t:
                    assert isinstance(a, t), \
                           "arg %r does not match %s" % (a, t)
            return f(*args, **kwds)
        new_f.func_name = f.__name__
        return new_f
    return check_accepts

And then use as:

@accepts(Decimal)
def calculate_price(monthly_item_price):
    ...

You can modify my decorator to achieve what you want.

James Lin
  • 22,616
  • 32
  • 117
  • 213
1

You can just check if the input is correct in the function:

def my_request(protocol_type: str, url: str):
    if protocol_type in ('http', 'https'):
        # Do x
    else:
        return 'Invalid Input'  # or raise an error
Alec
  • 7,110
  • 7
  • 28
  • 53
0

Use an if statement that raises an exception if protocol_type isn't in a list of allowed values :

allowed_protocols = ['http', 'https']
if protocol_type not in allowed_protocols:
    raise ValueError()
MaximGi
  • 498
  • 2
  • 11