{
"$type": "site.standard.document",
"canonicalUrl": "https://rednafi.com/python/apply-constraint-with-assert/",
"description": "Use Python assert statements to apply runtime constraints more succinctly than raising ValueError. Learn the trade-offs and best practices.",
"path": "/python/apply-constraint-with-assert/",
"publishedAt": "2022-07-10T00:00:00.000Z",
"site": "at://did:plc:fgtm2c26vfcj74rfmeggbyqj/site.standard.publication/3mnl6f7ob462z",
"tags": [
"Python"
],
"textContent": "Whenever I need to apply some runtime constraints on a value while building an API, I\nusually compare the value to an expected range and raise a ValueError if it's not within\nthe range. For example, let's define a function that throttles some fictitious operation.\nThe throttle function limits the number of times an operation can be performed by\nspecifying the throttle_after parameter. This parameter defines the number of iterations\nafter which the operation will be halted. The current_iter parameter tracks the current\nnumber of times the operation has been performed. Here's the implementation:\n\nWe return early if the value of throttle_after is -1. Otherwise, we check to see if\ncurrent_iter is a positive integer and throttle_after is a non-zero positive integer. If\nnot, we raise a ValueError. When the parameters pass these checks then we compare\ncurrent_iter with throttle_after. If the value of current_iter exceeds that of the\nthrottle_after parameter, we throttle the operation.\n\nWhile this works fine, recently, I've started to use assert to replace the _conditionals\nwith ValueError_ pattern. It works as follows:\n\nSo, instead of using the if not expression ... raise ValueError pattern, we can leverage\nassert expression, \"Error message\" pattern. In the latter case, assert will raise\nAssertionError with the \"Error message\" if the expression evaluates to a falsy value.\nOtherwise, the statement will remain silent and allow the execution to move forward.\n\nThis is more succinct and makes the code flatter. I've no idea why I haven't started using\nit earlier and this [assert usage in Starlette] jolted my brain. Eh bien, better late than\nnever, I guess.\n\nBreadcrumbs\n\nAfter this blog was published, several people [mentioned on Twitter] that the second\napproach has a small caveat. Python has a flag that allows you to disable assert\nstatements in a script. You can disable the assertions in the snippet above by running the\nscript with the -OO flag:\n\nRemoving assert statements will disable the constraints needed for the second throttle\nfunction to work, which could lead to unexpected behavior or even subtle bugs. However, I\nsee this being used frequently in frameworks like [Starlette] and [FastAPI]. Also, from my\nexperience, using assertions is much more common than running production code with the\noptimization flag.\n\n\n\n\n[assert usage in Starlette]:\n https://github.com/encode/starlette/blob/14ef6bbbd6c5f03f0e1222a0a1b33ccc3a5f04cf/starlette/applications.py#L63\n\n[starlette]:\n https://github.com/encode/starlette\n\n[mentioned on Twitter]:\n https://twitter.com/rednafi/status/1546010546297659392\n\n[fastapi]:\n https://github.com/tiangolo/fastapi/blob/bcabbf8b37db3fbc020560e94ad2f90e64d1510a/fastapi/applications",
"title": "Apply constraints with 'assert' in Python"
}