Type-safe (bit)flags for python 3
Project description
Quick Intro
With this module you can define type-safe (bit)flags. The style of the flag definition is very similar to the enum definitions you can create using the standard enum module of python 3.
Defining flags with the class syntax:
>>> from flags import Flags
>>> class TextStyle(Flags):
>>> bold = 1 # value = 1 << 0
>>> italic = 2 # value = 1 << 1
>>> underline = 4 # value = 1 << 2
In most cases you just want to use the flags as a set (of bool variables) and the actual flag values aren’t important. To avoid manually setting unique flag values you can use auto assignment. To auto-assign a unique flag value use an empty iterable (for example empty tuple or list) as the value of the flag. Auto-assignment picks the first unused least significant bit for each auto-assignable flag in top-to-bottom order.
>>> class TextStyle(Flags):
>>> bold = () # value = 1 << 0
>>> italic = () # value = 1 << 1
>>> underline = () # value = 1 << 2
As a shortcut you can call a flags class to create a subclass of it. This pattern has also been stolen from the standard enum module. The following flags definition is equivalent to the previous definition that uses the class syntax:
>>> TextStyle = Flags('TextStyle', 'bold italic underline')
Flags have human readable string representations and repr with more info:
>>> print(TextStyle.bold)
TextStyle.bold
>>> print(repr(TextStyle.bold))
<TextStyle.bold bits=0x0001 data=None>
The type of a flag is the flags class it belongs to:
>>> type(TextStyle.bold)
<class '__main__.TextStyle'>
>>> isinstance(TextStyle.bold, TextStyle)
True
You can combine flags with bool operators. The result is also an instance of the flags class with the previously described properties.
>>> result = TextStyle.bold | TextStyle.italic
>>>
>>> print(result)
TextStyle(bold|italic)
>>> print(repr(result))
<TextStyle(bold|italic) bits=0x0003>
Operators work in a type-safe way: you can combine only flags of the same type. Trying to combine them with instances of other types results in error:
>> result = TextStyle.bold | 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for |: 'TextStyle' and 'int'
>>>
>>> class OtherFlags(Flags):
... flag0 = ()
...
>>> result = TextStyle.bold | OtherFlags.flag0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for |: 'TextStyle' and 'OtherFlags'
Flags and their combinations (basically the instances of the flags class) are immutable and hashable so they can be used as set members and dictionary keys:
>>> font_files = {}
>>> font_files[TextStyle.bold] = 'bold.ttf'
>>> font_files[TextStyle.italic] = 'italic.ttf'
>>> font_files == {TextStyle.bold: 'bold.ttf', TextStyle.italic: 'italic.ttf'}
True
The flags you define automatically have two “virtual” flags: no_flags and all_flags. no_flags is basically the zero flag and all_flags is the combination of all flags you’ve defined:
>>> TextStyle.no_flags
<TextStyle() bits=0x0000>
>>> TextStyle.all_flags
<TextStyle(bold|italic|underline) bits=0x0007>
Testing whether specific flags are set:
>>> result = TextStyle.bold | TextStyle.italic
>>> bool(result & TextStyle.bold) # 1. oldschool bit twiddling
True
>>> TextStyle.bold in result # 2. in operator
True
>>> result.bold # 3. attribute-style access
True
>From the above testing methods the attribute-style access can only check the presence of a single flag. With the & and in operators you can check the presence of multiple flags at the same time:
>>> bool((TextStyle.bold | TextStyle.italic) & TextStyle.all_flags)
True
>>> (TextStyle.bold | TextStyle.italic) in TextStyle.all_flags
True
If for some reason you need the actual integer value of the flags then you can cast them to int:
>>> int(TextStyle.bold)
1
You can convert the int() and str() representations of flags back into flags instances:
>>> TextStyle(2)
<TextStyle.italic bits=0x0002 data=None>
>>> TextStyle('TextStyle.bold')
<TextStyle.bold bits=0x0001 data=None>
Installation
pip install py-flags
Alternatively you can download the distribution from the following places:
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.