onaplogging: Docstrings, refactor, type hinting
[logging-analytics.git] / pylog / onaplogging / marker / marker.py
1 # Copyright 2018 ke liang <lokyse@163.com>.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import abc
16
17 from typing import Iterable, List, Optional, Union, Iterator
18 from deprecated import deprecated
19 from warnings import warn
20 from logging import LogRecord
21
22 MARKER_TAG = "marker"
23
24
25 class Marker(object):
26     """Abstract class for defining the marker structure.
27
28     TODO:
29         after deprecated child methods are removed, rename them here.
30     Extends:
31         object
32     Method list:
33         getName
34         addChild
35         addChilds
36         removeChild
37         contains
38     Raises:
39         NotImplementedError
40     """
41
42     __metaclass__ = abc.ABCMeta
43
44     @abc.abstractmethod
45     def getName(self):
46         raise NotImplementedError()
47
48     @abc.abstractmethod
49     def contains(self, item=None):
50         raise NotImplementedError()
51
52     @abc.abstractmethod
53     def addChild(self, item):
54         raise NotImplementedError()
55
56     @abc.abstractmethod
57     def removeChild(self, item):
58         raise NotImplementedError()
59
60     @abc.abstractmethod
61     def __eq__(self, other):
62         raise NotImplementedError()
63
64     @abc.abstractmethod
65     def __hash__(self):
66         raise NotImplementedError()
67
68     @abc.abstractmethod
69     def __iter__(self):
70         raise NotImplementedError()
71
72
73 class BaseMarker(Marker):
74     """Basic marker class.
75
76     It is a marker with base functionalities that add sub-level markers and
77     check  if another marker  exists as the parent itself  or as its  child.
78
79     Extends:
80         Marker
81     Properties:
82         name            : The name of the marker.
83         children (list) : The list of all children (sub-level) markers.
84     Arguments:
85         name (str)      : The name of the marker.
86     Methods:
87         getName         : Returns the name of the marker.
88         addChild        : Adds a sub-level marker.
89         addChilds       : Adds a list of sub-level markers.
90         removeChild     : Removes a specified sub-level marker.
91         contains        : Checks if a sub-level marker exists.
92     """
93
94     @property
95     def name(self):
96         # type: () -> str
97         """Name of the parent marker."""
98         return self.__name
99
100     @property
101     def children(self):
102         # type: () -> List[Marker]
103         """Child markers of one parent marker."""
104         return self.__childs
105
106     @name.setter
107     def name(self, value):
108         # type: (str) -> None
109         self.__name = value
110
111     @children.setter
112     def children(self, value):
113         # type: (List[Marker]) -> None
114         self.__childs = value
115
116     def __init__(self, name):  # type: (str)
117         """
118         Raises:
119             TypeError   : If the `name` parameter  is  not  a string.
120             ValueError  : If the `name` parameter is an empty string.
121         """
122
123         super(BaseMarker, self).__init__()
124
125         if not isinstance(name, str):
126             raise TypeError("not str type")
127
128         if name == "":
129             raise ValueError("empty value")
130
131         warn("Attribute `__childs` is replaced by the property `children`."
132             "Use children instead.", DeprecationWarning)
133
134         self.__name = name
135         self.__childs = []
136
137     def add_child(self, marker):
138         # type: (Marker) -> None
139         """Append a marker to child markers.
140
141         Use this method to describe a different level of logs. For example,
142         error  log  would use the  ERROR marker.  However it's  possible to
143         create a,  for instance,  TYPE_ERROR  to mark  type related events.
144         In this case TYPE_ERROR will be a child of parent ERROR.
145
146         Args:
147             marker      : marker describing a different log level.
148         Raises:
149             TypeError   : if the marker object has different type.
150         """
151
152         if not isinstance(marker, Marker):
153             raise TypeError("Bad marker type.                     \
154                              Can only add markers of type Marker. \
155                              Type %s was passed." % type(marker))
156
157         if self == marker:
158             return
159
160         if marker not in self.children:
161             self.children.append(marker)
162
163     def add_children(self, markers):
164         # type: (Iterable[List]) -> None
165         """ Append a list of markers to child markers.
166
167         Args:
168             markers     : An iterable object, containing markers.
169         Raises:
170             Exception   : If  `marker` parameter is not iterable.
171         """
172
173         try:
174             iter(markers)
175         except Exception as e:
176             raise e
177
178         for marker in markers:
179             self.children.append(marker)
180
181     def remove_child(self, marker):
182         # type: (Marker) -> None
183         """Use this method to remove a marker from the children list.
184
185         Args:
186             marker   : A child marker object.
187         Raises:
188             TypeError: if the marker object has different type.
189         """
190
191         if not isinstance(marker, Marker):
192             raise TypeError("Bad marker type.                     \
193                              Can only add markers of type Marker. \
194                              Type %s was passed." % type(marker))
195
196         if marker in self.children:
197             self.children.remove(marker)
198
199     def contains(self, item=None):
200         # type: (Optional[Union[Marker, str]]) -> bool
201         """
202         Use it to check if a marker exists as a parent itself or its chidren.
203
204         Args:
205             item    : A child marker object. Defaults to None.
206         Returns:
207             bool    : True if the marker exists.
208         """
209
210         warn("`item` argument will be replaced with `marker`. "
211              "Default value None will be removed.",
212               DeprecationWarning)
213         marker = item
214
215         if isinstance(marker, Marker):
216             if marker == self:
217                 return True
218             return len(list(filter(
219                 lambda x: x == marker, self.children))) > 0
220
221         elif isinstance(marker, str):
222             if marker == self.name:
223                 return True
224
225             return len(list(filter(
226                 lambda x: x.name == marker, self.children))) > 0
227
228         return False
229
230     def __iter__(self):
231         # type: () -> Iterator[List[Marker]]
232         return iter(self.__childs)
233
234     def __hash__(self):
235         # type (): -> int
236         return hash(self.__name)
237
238     def __eq__(self, other):
239         # type: (Marker) -> bool
240         if not isinstance(other, Marker):
241             return False
242         if id(self) == id(other):
243             return True
244
245         return self.__name == other.getName()
246
247     @deprecated(reason="Will be removed. Call the `name` property instead.")
248     def getName(self):
249         """Class attribute getter."""
250         return self.name
251
252     @deprecated(reason="Will be removed. Call add_children(markers) instead.")
253     def addChilds(self, childs):
254         """Add a list of sub-level markers. See add_children(markers)"""
255         self.add_children(childs)
256
257     @deprecated(reason="Will be removed. Call add_child(marker) instead.")
258     def addChild(self, item):
259         """Add a sub-level marker. See add_child(marker)"""
260         self.add_child(item)
261
262     @deprecated(reason="Will be removed. Call remove_child(marker) instead.")
263     def removeChild(self, item):
264         """Remove a sub-level marker. See remove_child(marker)"""
265         self.remove_child(item)
266
267
268 @deprecated(reason="Will be removed. "
269     "Call match_marker(record, marker_to_match) instead.")
270 def matchMarkerHelp(record, markerToMatch):
271     """See match_marker(record, marker_to_match)."""
272     return match_markers(record, markerToMatch)
273
274
275 def match_markers(record, marker_to_match):
276     # type: (LogRecord, Union[Marker, List]) -> bool
277     """
278     Use this method to match a marker (or a list of markers) with a LogRecord
279     record.
280
281     Args:
282         record          : a record that may contain a marker.
283         markerToMatch   : a marker or a list of markers.
284     Raises:
285         Exception       : if match check went wrong.
286     Returns:
287         bool            : whether the check can be done or the marker is found.
288     """
289     record_marker = getattr(record, MARKER_TAG, None)
290
291     if record_marker is None or \
292        marker_to_match is None:
293         return False
294
295     if not isinstance(record_marker, Marker):
296         return False
297
298     try:
299         if isinstance(marker_to_match, list):
300             return len(list(filter(
301                 lambda x: record_marker.contains(x), marker_to_match))) > 0
302
303         return record_marker.contains(marker_to_match)
304     except Exception as e:
305         raise e