Skip to main content

Python's Enum with extra powers to play nice with labels and choices fields

Project description

https://img.shields.io/pypi/v/choicesenum.svg https://travis-ci.org/loggi/python-choicesenum.svg?branch=master Documentation Status https://coveralls.io/repos/github/loggi/python-choicesenum/badge.svg?branch=master

Python’s Enum with extra powers to play nice with labels and choices fields.

Installation

Install choicesenum using pip:

$ pip install choicesenum

Features

  • An ChoicesEnum that can be used to create constant groups.

  • ChoicesEnum can define labels to be used in choices fields.

  • Django fields included: EnumCharField and EnumIntegerField.

  • All ChoicesEnum types can be compared against their primitive values directly.

  • Support (tested) for Python 2.7, 3.5, 3.6, 3.7 and 3.8.

  • Support (tested) for Django 1.9, 1.10, 1.11, 2.0, 2.1, 2.2 and 3.0.

Usage examples

Example with HttpStatuses:

class HttpStatuses(ChoicesEnum):
    OK = 200
    BAD_REQUEST = 400
    UNAUTHORIZED = 401
    FORBIDDEN = 403

Example with Colors:

from choicesenum import ChoicesEnum

class Colors(ChoicesEnum):
    RED = '#f00', 'Vermelho'
    GREEN = '#0f0', 'Verde'
    BLUE = '#00f', 'Azul'

Comparison

All Enum types can be compared against their values:

assert HttpStatuses.OK == 200
assert HttpStatuses.BAD_REQUEST == 400
assert HttpStatuses.UNAUTHORIZED == 401
assert HttpStatuses.FORBIDDEN == 403

status_code = HttpStatuses.OK
assert 200 <= status_code <= 300

assert Colors.RED == '#f00'
assert Colors.GREEN == '#0f0'
assert Colors.BLUE == '#00f'

Label for free

All Enum types have by default a display derived from the enum identifier:

assert HttpStatuses.OK.display == 'Ok'
assert HttpStatuses.BAD_REQUEST.display == 'Bad request'
assert HttpStatuses.UNAUTHORIZED.display == 'Unauthorized'
assert HttpStatuses.FORBIDDEN.display == 'Forbidden'

You can easily define your own custom display for an Enum item using a tuple:

class HttpStatuses(ChoicesEnum):
    OK = 200, 'Everything is fine'
    BAD_REQUEST = 400, 'You did a mistake'
    UNAUTHORIZED = 401, 'I know your IP'
    FORBIDDEN = 403

assert HttpStatuses.OK.display == 'Everything is fine'
assert HttpStatuses.BAD_REQUEST.display == 'You did a mistake'
assert HttpStatuses.UNAUTHORIZED.display == 'I know your IP'
assert HttpStatuses.FORBIDDEN.display == 'Forbidden'

Dynamic properties

For each enum item, a dynamic property is_<enum_item> is generated to allow quick boolean checks:

color = Colors.RED
assert color.is_red
assert not color.is_blue
assert not color.is_green

This feature is usefull to avoid comparing a received enum value against a know enum item.

For example, you can replace code like this:

# status = HttpStatuses.BAD_REQUEST

def check_status(status):
    if status == HttpStatuses.OK:
        print("Ok!")

To this:

def check_status(status):
    if status.is_ok:
        print("Ok!")

Custom methods and properties

You can declare custom properties and methods:

class HttpStatuses(ChoicesEnum):
    OK = 200, 'Everything is fine'
    BAD_REQUEST = 400, 'You did a mistake'
    UNAUTHORIZED = 401, 'I know your IP'
    FORBIDDEN = 403

    @property
    def is_error(self):
        return self >= self.BAD_REQUEST

assert HttpStatuses.OK.is_error is False
assert HttpStatuses.BAD_REQUEST.is_error is True
assert HttpStatuses.UNAUTHORIZED.is_error is True

Iteration

The enum type is iterable:

>>> for color in Colors:
...     print(repr(color))
Color('#f00').RED
Color('#0f0').GREEN
Color('#00f').BLUE

Order is guaranteed only for py3.4+. For fixed order in py2.7, you can implement a magic attribute _order_:

from choicesenum import ChoicesEnum

class Colors(ChoicesEnum):
    _order_ = 'RED GREEN BLUE'

    RED = '#f00', 'Vermelho'
    GREEN = '#0f0', 'Verde'
    BLUE = '#00f', 'Azul'

Choices

Use .choices() method to receive a list of tuples (item, display):

assert list(Colors.choices()) == [
    ('#f00', 'Vermelho'),
    ('#0f0', 'Verde'),
    ('#00f', 'Azul'),
]

Values

Use .values() method to receive a list of the inner values:

assert Colors.values() == ['#f00', '#0f0', '#00f', ]

Options

Even if a ChoicesEnum class is an iterator by itself, you can use .options() to convert the enum items to a list:

assert Colors.options() == [Colors.RED, Colors.GREEN, Colors.BLUE]

A “dict like” get

Use .get(value, default=None) method to receive default if value is not an item of enum:

assert Colors.get(Colors.RED) == Colors.RED
assert Colors.get('#f00') == Colors.RED
assert Colors.get('undefined_color') is None
assert Colors.get('undefined_color', Colors.RED) == Colors.RED

Compatibility

The enum item can be used whenever the value is needed:

assert u'Currrent color is {c} ({c.display})'.format(c=color) ==\
       u'Currrent color is #f00 (Vermelho)'

Even in dicts and sets, as it shares the same hash() from his value:

d = {
    HttpStatuses.OK.value: "using value",
    HttpStatuses.BAD_REQUEST: "using enum",
    401: "from original value",
}
assert d[HttpStatuses.OK] == "using value"
assert d[HttpStatuses.BAD_REQUEST.value] == "using enum"
assert d[HttpStatuses.OK] == d[HttpStatuses.OK.value]
assert d[HttpStatuses.UNAUTHORIZED] == d[401]

There’s also optimistic casting of inner types:

assert int(HttpStatuses.OK) == 200
assert float(HttpStatuses.OK) == 200.0
assert str(HttpStatuses.BAD_REQUEST) == "400"

Check membership:

assert HttpStatuses.OK in HttpStatuses
assert 200 in HttpStatuses
assert 999 not in HttpStatuses

JSON

If you want json serialization, you have at least two options:

  1. Patch the default serializer.

  2. Write a custom JSONEncoder.

ChoicesEnum comes with a handy patch funtion, you need to add this code to somewhere at the top of everything to automagically add json serialization capabilities:

from choicesenum.patches import patch_json
patch_json()

Django

Fields

Usage with the custom Django fields:

from django.db import models
from choicesenum.django.fields import EnumCharField

class ColorModel(models.Model):
    color = EnumCharField(
        max_length=100,
        enum=Colors,
        default=Colors.GREEN,
    )

instance = ColorModel()
assert instance.color ==  Colors.GREEN
assert instance.color.is_green is True
assert instance.color.value == Colors.GREEN.value == '#0f0'
assert instance.color.display == Colors.GREEN.display

instance.color = '#f00'
assert instance.color == '#f00'
assert instance.color.value == '#f00'
assert instance.color.display == 'Vermelho'

Is guaranteed that the field value is always a ChoicesEnum item. Pay attention that the field will only accept valid values for the Enum in use, so if your field allow null, your enum should also:

from django.db import models
from choicesenum import ChoicesEnum
from choicesenum.django.fields import EnumIntegerField

class UserStatus(ChoicesEnum):
    UNDEFINED = None
    PENDING = 1
    ACTIVE = 2
    INACTIVE = 3
    DELETED = 4


class User(models.Model):
    status = EnumIntegerField(enum=UserStatus, null=True, )

instance = User()
assert instance.status.is_undefined is True
assert instance.status.value is None
assert instance.status == UserStatus.UNDEFINED
assert instance.status.display == 'Undefined'

# again...
instance.status = None
assert instance.status.is_undefined is True

Graphene

Usage with Graphene Enums:

UserStatusEnum = graphene.Enum.from_enum(UserStatus)

Schematics

Usage with Schematics Enums:

from schematics.models import Model as SchematicModel
from schematics.types import StringType, DateTimeType
from choicesenum import ChoicesEnum
from choicesenum.schematics.types import ChoicesEnumType

class HttpStatus(ChoicesEnum):
    OK = 200
    BAD_REQUEST = 400
    UNAUTHORIZED = 401
    FORBIDDEN = 403

class CustomSchematicModel(SchematicModel):
    name = StringType(required=True, max_length=255)
    created = DateTimeType(required=True, formats=('%d/%m/%Y', ''))
    http = ChoicesEnumType(HttpStatuses, required=True)

History

0.7.0 (2020-08-02)

  • Add support Django 3.0 (tks @klette).

  • Drop support for Python 3.4.

  • Fix issue when using Django EnumIntegerField on Admin.

0.6.0 (2019-09-05)

  • Adding schematics contrib type ChoicesEnumType.

  • Drop support for Django 1.6, 1.7, 1.8.

0.5.3 (2019-02-06)

  • Fix Django migrations with default values for Django 1.7+.

0.5.2 (2019-01-18)

  • Optimize member check and dynamic creation of is_<name> properties.

0.5.1 (2019-01-04)

  • Fix readme RST (requires new Pypi upload).

0.5.0 (2019-01-04)

  • Membership test (item in Enum) returning valid results for primitive values.

  • New dict-like .get method able to return a default value (thanks @leandrogs).

  • Django: Support Postgres array functions and queries (thanks @tomfa).

  • Django: Support for deferring an enum field using queryset.defer() (thanks @noamkush).

  • Django: 2.1 support.

0.4.0 (2018-07-13)

  • Optimistic casting of inner types (thanks @gabisurita).

  • Optional stdlib patch to automagic json serialization support.

  • Add Python3.7 to the test matrix.

0.3.0 (2018-06-22)

  • Official Django 2.0 support (0.2.2 just works fine too).

  • ChoicesEnum sharing the same hash() as his value. Can be used to retrieve/restore items in dicts (d[enum] == d[enum.value]).

0.2.2 (2017-12-01)

  • Fix: Support queries through select_related with no None value defined (thanks @klette).

0.2.1 (2017-09-30)

  • Fix South migrations for Django 1.6.

0.2.0 (2017-09-11)

  • ChoicesEnum items are comparable by their values (==, !=, >, >=, <, <=) (thanks @jodal).

  • +``ChoicesEnum.values``: Returns all the Enum’s raw values (eq: [x.value for x in Enum]).

0.1.7 (2017-09-10)

  • Fix: ChoicesEnum is now hashable (thanks @jodal).

0.1.6 (2017-09-08)

  • Fix: Proxy __len__ calls to the inner enum value.

0.1.5 (2017-09-05)

  • +ChoicesEnum.description: Alias for label, allow enum descriptors to be used by Graphene.

0.1.4 (2017-08-28)

  • Fix South migrations for Django 1.6.

  • ChoicesEnum repr can be used to reconstruct an instance (item == eval(repr(item))).

0.1.3 (2017-08-28)

  • Fix sdist not including sub-modules (django contrib).

0.1.2 (2017-08-27)

  • README fixes and improvements.

0.1.0 (2017-08-27)

  • First release on PyPI.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

choicesenum-0.7.0.tar.gz (25.2 kB view hashes)

Uploaded Source

Built Distribution

choicesenum-0.7.0-py2.py3-none-any.whl (12.1 kB view hashes)

Uploaded Python 2 Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page