Best Way To Extend Httpstatus With Custom Value
Solution 1:
That's because http.HTTPStatus
is an Enum
and Python doesn't really, trully have Enum
as a generic type (which is why you can do what you're doing - languages that actually recognize Enum
as something special wouldn't let you mess with it like this in general). Of course, Python does its best to make Enums
behave as they should (immutable, iterable, mappable...).
There is actually a collections.OrderedDict
underneath (Enum._member_map_
) that gets created when you create a new Enum
type - it reads in the members, aliases the duplicates and adds an additional value -> enum member
map as Enum._value2member_map_
(all of that is done by the enum.EnumMeta
metaclass). When you dir()
an enum - you get that map (or more precisely, the enum names list available in the Enum._member_names_
list) and any changes you may have applied at runtime doesn't count (otherwise it wouldn't be immutable). In other words, when you do HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT = 573
you're not extending the Enum
, you're just adding a dynamic property to the Enum
object in question.
You should extend your Enum
types the regular, OOP way if you want to add custom members... except Python won't let you do this either. So if you really insist doing it run-time you can, kind of, hack the internal structure to make Python believe your enum value was there all along:
# HERE BE DRAGONS!# DO NOT do this unless you absolutely have to.from http import HTTPStatus
defadd_http_status(name, value, phrase, description=''):
# call our new member factory, it's essentially the `HTTPStatus.__new__` method
new_status = HTTPStatus.__new_member__(HTTPStatus, value, phrase, description)
new_status._name_ = name # store the enum's member internal name
new_status.__objclass__ = HTTPStatus.__class__ # store the enum's member parent classsetattr(HTTPStatus, name, new_status) # add it to the global HTTPStatus namespace
HTTPStatus._member_map_[name] = new_status # add it to the name=>member map
HTTPStatus._member_names_.append(name) # append the names so it appears in __members__
HTTPStatus._value2member_map_[value] = new_status # add it to the value=>member map
And now you can 'really' extend the HTTPStatus
at runtime:
try:
print(HTTPStatus(573))
except ValueError as e:
print(e)
print("MY_CUSTOM_SERVICE_TIMEOUT" in dir(HTTPStatus))
add_http_status("MY_CUSTOM_SERVICE_TIMEOUT", 573, "Custom service timeout")
print("MY_CUSTOM_SERVICE_TIMEOUT" in dir(HTTPStatus))
print(HTTPStatus(573))
print(HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT.value)
print(HTTPStatus(573).phrase)
Which will give you:
573 is not a valid HTTPStatus False True HTTPStatus.MY_CUSTOM_SERVICE_TIMEOUT 573 Custom service timeout
Keep in mind that this code doesn't handle aliasing, de-duplication and other nice things that you should absolutely be doing if you want to arbitrary extend an Enum
so don't use duplicate or invalid values or you'll break it (in a sense it won't work as expected afterwards). Check the additional steps taken in enum.EnumMeta.__new__()
to ensure its validity.
Solution 2:
Use the extend_enum
function from the aenum library
:
import aenum
import http
aenum.extend_enum(http.HTTPStatus, 'CustomTimeout', 537, 'more helpful phrase here')
Which results in:
>>> list(http.HTTPStatus)
[<HTTPStatus.CONTINUE: 100>,
...,
<HTTPStatus.CustomTimeout: 537>]
Disclosure: I am the author of the Python stdlib Enum
, the enum34
backport, and the Advanced Enumeration (aenum
) library.
Post a Comment for "Best Way To Extend Httpstatus With Custom Value"