How to limit the permitted values that can be passed to a method parameter (Using Type Hinting to allow Static Code Analysis)
Question:
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'
?
Answers:
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.
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()
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
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
Why not use a Literal for the method argument?
def my_request(protocol_type: Literal["http","https"], url: str):
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'
?
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.
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()
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
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
Why not use a Literal for the method argument?
def my_request(protocol_type: Literal["http","https"], url: str):