-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cleaned enum structures and added new features #61
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,30 @@ | ||
import re | ||
|
||
from collections import MutableSet, OrderedDict | ||
from itertools import chain | ||
|
||
|
||
ENUM_KEY_PATTERN = re.compile(r'^[a-zA-Z][a-zA-Z0-9_]*$') | ||
|
||
|
||
class AbstractEnum: | ||
|
||
def __init__(self, *items): | ||
for k, _ in items: | ||
if not isinstance(k, str): | ||
raise ValueError('Enum key "{}" must be string'.format(k)) | ||
if not ENUM_KEY_PATTERN.match(k): | ||
raise ValueError('Enum key "{}" has invalid format'.format(k)) | ||
|
||
self._container = OrderedDict(items) | ||
self._reverse_container = {item[1]: item[0] for item in items} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. původně se iterovalo v _container, což může být časově trošku horší, tak si připravuji i reverzní dict |
||
|
||
def _has_attr(self, name): | ||
return name in self._container | ||
|
||
def _get_attr_val(self, name): | ||
return name | ||
|
||
def __getattr__(self, name): | ||
if self._has_attr(name): | ||
return self._get_attr_val(name) | ||
return self._container[name] | ||
raise AttributeError('Missing attribute %s' % name) | ||
|
||
def __copy__(self, *args, **kwargs): | ||
|
@@ -23,127 +35,86 @@ def __deepcopy__(self, *args, **kwargs): | |
# Enum is immutable | ||
return self | ||
|
||
def __contains__(self, item): | ||
return item in self._reverse_container | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. na enumy funguje operátor in |
||
|
||
def __iter__(self): | ||
return self._container.values().__iter__() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. enumy jsou iterable, není nutné volat all, ale all vytvoří tuple namísto generátoru, což se může hodit |
||
|
||
@property | ||
def all(self): | ||
return tuple(self) | ||
|
||
def get_name(self, val): | ||
return self._reverse_container.get(val) | ||
|
||
|
||
class Enum(AbstractEnum): | ||
|
||
def __init__(self, *items): | ||
self._container = OrderedDict(( | ||
super().__init__(*( | ||
item if isinstance(item, (list, tuple)) else (item, item) | ||
for item in items | ||
)) | ||
super(Enum, self).__init__() | ||
|
||
def _get_attr_val(self, name): | ||
return self._container[name] | ||
|
||
def __iter__(self): | ||
return self._container.values().__iter__() | ||
|
||
|
||
class NumEnum(AbstractEnum): | ||
class NumEnum(Enum): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. num enum je nyní jen speciální typ enumu, který kontroluje, že hodnoty jsou čísla a umí hondnoty auto generovat (jinak vlastně není rozdíl) |
||
|
||
def __init__(self, *items): | ||
self._container = OrderedDict() | ||
super(NumEnum, self).__init__() | ||
used_ids = set() | ||
|
||
enum_items = [] | ||
i = 0 | ||
for item in items: | ||
if len(item) == 2: | ||
key, i = item | ||
if not isinstance(i, int): | ||
raise ValueError('Last value of item must by integer') | ||
raise ValueError('Choice value of item must by integer') | ||
else: | ||
key = item | ||
i += 1 | ||
|
||
if i in self._container.values(): | ||
if i in used_ids: | ||
raise ValueError('Index %s already exists, please renumber choices') | ||
self._container[key] = i | ||
|
||
def _get_attr_val(self, name): | ||
return self._container[name] | ||
used_ids.add(i) | ||
enum_items.append((key, i)) | ||
super().__init__(*enum_items) | ||
|
||
|
||
class AbstractChoicesEnum: | ||
|
||
def _get_labels_dict(self): | ||
return dict(self._get_choices()) | ||
def __init__(self, *items): | ||
enum_items = [] | ||
for item in items: | ||
assert len(item) in {2, 3}, 'Choice item array length must be two or three' | ||
|
||
def _get_choices(self): | ||
raise NotImplementedError | ||
if len(item) == 3: | ||
enum_items.append((item[0], item[2])) | ||
else: | ||
enum_items.append(item[0]) | ||
|
||
@property | ||
def choices(self): | ||
return self._get_choices() | ||
super().__init__(*enum_items) | ||
self.choices = tuple( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nyní jsou choices ve vlastní struktuře, dříve se prasila základní struktura enumu a blbě se s tím pracovalo, navíc nám správně nefungovala dědičnost enumů |
||
(k, items[i][1]) for i, k in enumerate(self._container.values()) | ||
) | ||
|
||
@property | ||
def all(self): | ||
return (key for key, _ in self._get_choices()) | ||
def _get_labels_dict(self): | ||
return dict(self.choices) | ||
|
||
def get_label(self, name): | ||
labels = dict(self._get_choices()) | ||
labels = self._get_labels_dict() | ||
if name in labels: | ||
return labels[name] | ||
raise AttributeError('Missing label with index %s' % name) | ||
|
||
|
||
class ChoicesEnum(AbstractChoicesEnum, AbstractEnum): | ||
|
||
def __init__(self, *items): | ||
self._container = OrderedDict() | ||
super(ChoicesEnum, self).__init__() | ||
for item in items: | ||
if len(item) == 3: | ||
key, label, val = item | ||
elif len(item) == 2: | ||
key, label = item | ||
val = key | ||
self._container[key] = (val, label) | ||
|
||
def get_name(self, i): | ||
for key, (val, _) in self._container.items(): | ||
if val == i: | ||
return key | ||
return None | ||
|
||
def _get_attr_val(self, name): | ||
return self._container[name][0] | ||
|
||
def _get_choices(self): | ||
return list(self._container.values()) | ||
|
||
|
||
class ChoicesNumEnum(AbstractChoicesEnum, AbstractEnum): | ||
|
||
def __init__(self, *items): | ||
self._container = OrderedDict() | ||
super(ChoicesNumEnum, self).__init__() | ||
i = 0 | ||
for item in items: | ||
if len(item) == 3: | ||
key, val, i = item | ||
if not isinstance(i, int): | ||
raise ValueError('Last value of item must by integer') | ||
elif len(item) == 2: | ||
key, val = item | ||
i += 1 | ||
else: | ||
raise ValueError('Wrong input data format') | ||
|
||
if i in {j for j, _ in self._container.values()}: | ||
raise ValueError('Index %s already exists, please renumber choices') | ||
self._container[key] = (i, val) | ||
|
||
def get_name(self, i): | ||
for key, (number, _) in self._container.items(): | ||
if number == i: | ||
return key | ||
return None | ||
class ChoicesEnum(AbstractChoicesEnum, Enum): | ||
pass | ||
|
||
def _get_attr_val(self, name): | ||
return self._container[name][0] | ||
|
||
def _get_choices(self): | ||
return list(self._container.values()) | ||
class ChoicesNumEnum(AbstractChoicesEnum, NumEnum): | ||
pass | ||
|
||
|
||
class SubstatesChoicesNumEnum(ChoicesNumEnum): | ||
|
@@ -171,7 +142,7 @@ def __init__(self, items, initial_states=None): | |
self.initial_states = initial_states | ||
|
||
# The last value of every item are omitted and send to ChoicesEnum constructor | ||
super(SequenceChoicesEnumMixin, self).__init__(*(item[:-1] for item in items if item[0] is not None)) | ||
super().__init__(*(item[:-1] for item in items if item[0] is not None)) | ||
|
||
self.first_choices = self._get_first_choices(items) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -91,11 +91,11 @@ SmartModel | |
|
||
List of defined pre or post save dispatchers. More obout it will find _dispatchers | ||
|
||
.. property:: has_changed | ||
.. attribute:: has_changed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. property neexistuje. |
||
|
||
Returns ``True`` or ``False`` depending on whether instance was changed | ||
|
||
.. property:: initial_values | ||
.. attribute:: initial_values | ||
|
||
Returns initial values of the object from loading instance from database. It should represent actual state of the object in the database | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,18 +33,75 @@ Enums | |
|
||
Base enumeration class with controlled ``__getattr__``. | ||
|
||
.. attribute:: chamber.utils.datastructures.AbstractEnum.all | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. troška té dokumentace s příklady |
||
|
||
Returns all enum values in a tuple. | ||
|
||
.. method:: chamber.utils.datastructures.AbstractEnum.__iter__ | ||
|
||
Returns iterator over enum values. | ||
|
||
.. method:: chamber.utils.datastructures.AbstractEnum.get_name | ||
|
||
Returns name of the choice accorting to its value. | ||
|
||
.. method:: chamber.utils.datastructures.AbstractEnum.get_name | ||
|
||
Implementation of in operator. Return ``True`` if value is part of the enum. | ||
|
||
|
||
.. class:: chamber.utils.datastructures.Enum | ||
|
||
Python's ``set`` with ``AbstractEnum`` behaviour. | ||
Python's ``set`` with ``AbstractEnum`` behaviour. Enum key can be only string type. Key and values can be different. | ||
|
||
:: | ||
|
||
>>> enum = NumEnum('A', 'B') # {'A': 'A', 'B': 'B'} | ||
>>> enum.all | ||
('A', 'B') | ||
>>> 'A' in enum | ||
True | ||
>>> list(enum) | ||
['A', 'B'] | ||
>>> enum.A | ||
'A' | ||
|
||
>>> enum = NumEnum(('A', 'a'), ('B', 'b')) # {'A': 'a', 'B': 'b'} | ||
>>> enum.all | ||
('a', 'b') | ||
>>> 'a' in enum | ||
True | ||
>>> list(enum) | ||
['a', 'b'] | ||
>>> enum.A | ||
'a' | ||
|
||
|
||
.. class:: chamber.utils.datastructures.NumEnum | ||
|
||
Python's ``dict`` with ``AbstractEnum`` behaviour. | ||
|
||
:: | ||
|
||
>>> NumEnum('a', 'b') | ||
{'a': 1, 'b': 2} | ||
>>> enum = NumEnum('A', 'B') # {'A': 1, 'B': 2} | ||
>>> enum.all | ||
(1, 2) | ||
>>> 1 in enum | ||
True | ||
>>> list(enum) | ||
[1, 2] | ||
>>> enum.A | ||
1 | ||
|
||
>>> enum = NumEnum(('A', 6), ('B', 5)) # {'A': 6, 'B': 5} | ||
>>> enum.all | ||
(6, 5) | ||
>>> 5 in enum | ||
True | ||
>>> list(enum) | ||
[6, 5] | ||
>>> enum.A | ||
6 | ||
|
||
.. class:: chamber.utils.datastructures.AbstractChoicesEnum | ||
|
||
|
@@ -57,13 +114,21 @@ Base choices class (can be used as a model field's ``choices`` argument). | |
|
||
:: | ||
|
||
>>> enum = ChoicesEnum(('OK', 'ok'), ('KO', 'ko')) | ||
>>> enum | ||
{'OK': 'ok', 'KO': 'ko'} | ||
>>> enum = ChoicesEnum(('OK', 'label ok'), ('KO', 'label ko')) # {'OK': ('OK', 'label ok), 'ko': ('KO', 'label ko)} | ||
>>> enum.OK | ||
'ok' | ||
'OK' | ||
>>> enum.choices | ||
[('OK', 'ok'), ('KO', 'ko')] | ||
>>> enum.get_label(enum.OK) | ||
'label ok' | ||
|
||
>>> enum = ChoicesEnum(('OK', 'label ok', 'ok'), ('KO', 'label ko', 'ko')) # {'OK': ('ok', 'label ok), 'ko': ('ko', 'label ko)} | ||
>>> enum.OK | ||
'ok' | ||
>>> enum.choices | ||
[('ok', 'label ok'), ('ko', 'label ko')] | ||
>>> enum.get_label(enum.OK) | ||
'label ok' | ||
|
||
.. class:: chamber.utils.datastructures.ChoicesNumEnum | ||
|
||
|
@@ -72,7 +137,7 @@ Base choices class (can be used as a model field's ``choices`` argument). | |
|
||
:: | ||
|
||
>>> enum = ChoicesNumEnum(('OK', 'ok', 1), ('KO', 'ko', 2)) | ||
>>> enum = ChoicesNumEnum(('OK', 'label ok', 1), ('KO', 'label ko', 2)) | ||
>>> enum.KO | ||
2 | ||
>>> enum.choices | ||
|
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
omezil jsem názvy klíčů enumů