forked from ManageIQ/integration_tests
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsoft_get.py
75 lines (68 loc) · 3.21 KB
/
soft_get.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import collections
from difflib import SequenceMatcher
class MultipleResultsException(Exception):
pass
def soft_get(obj,
field_base_name,
dict_=False,
case_sensitive=False,
best_match=True,
dont_include=None):
"""
This function used for cases that we want to get some attribute that we
either know only few parts of its name or want to prevent from case issues.
Example:
Imagine you have a relationships table and you want to get 'image' field.
Since sometimes the exact name of the field is changing among versions, pages, etc.
it could be appear as 'Images', 'Image', 'Container Images', Containers Images', etc.
Since we don't care for the exact name and know that 'image' is a unique in the table,
we can use this function to prevent from this complexity.
Args:
* obj: The object which we want to get the attribute
* field_base_name: The base name, a string that we know
for sure that is a sub-string of the target field
* dict_: Whether this is a dict AND we want to perform the same functionality on its keys
* case_sensitive: Whether the search is a sensitive case.
* best_match: If True: in case that it found more than 1 match field,
it will take the closest one
If False: in case that it found more than 1 match field,
it will raise an error
* dont_include: Strings that should not be a part of the field.
Used to prevent cases like: soft_get(obj, 'image') -> obj.image_registry
Returns:
The value of the target attribute
"""
dont_include = dont_include or []
signature = ('soft_get({}, {}, dict_={}, case_sensitive={})'
.format(obj, field_base_name, dict_, case_sensitive))
if not case_sensitive:
field_base_name = field_base_name.lower()
if dict_:
if not isinstance(obj, collections.Mapping):
raise TypeError('{}: {} is not a dict (type={}). '
.format(signature, obj, type(obj)))
all_fields = obj.keys()
else:
all_fields = dir(obj)
found_fields = []
if not case_sensitive:
dont_include = [s.lower() for s in dont_include]
for field in all_fields:
origin_field = field
if not case_sensitive:
field = field.lower()
if (field_base_name in field) and \
all([(s not in field) for s in dont_include]):
found_fields.append(origin_field)
if not found_fields:
raise AttributeError('{}: Could not find a member for field {}.'
.format(signature, field_base_name))
elif len(found_fields) > 1:
if not best_match:
raise MultipleResultsException('{}: Found more than 1 member for {}: {}'
.format(signature, field_base_name, found_fields))
found_fields = [max(found_fields, key=lambda s:
SequenceMatcher(None, s, field_base_name).ratio())]
if dict_:
return obj[found_fields[0]]
return getattr(obj, found_fields[0])