Passed
Push — master ( f8912e...f21663 )
by Vinicius
06:13 queued 12s
created

kytos.core.helpers   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 183
Duplicated Lines 0 %

Test Coverage

Coverage 82.22%

Importance

Changes 0
Metric Value
eloc 47
dl 0
loc 183
ccs 37
cts 45
cp 0.8222
rs 10
c 0
b 0
f 0
wmc 9

5 Functions

Rating   Name   Duplication   Size   Complexity  
A get_thread_pool_max_workers() 0 3 1
A get_time() 0 30 3
A run_on_thread() 0 21 1
A listen_to() 0 90 3
A now() 0 11 1
1
"""Utilities functions used in Kytos."""
2 2
from concurrent.futures import ThreadPoolExecutor
3 2
from datetime import datetime, timezone
4 2
from threading import Thread
5
6 2
from kytos.core.config import KytosConfig
7
8 2
__all__ = ['listen_to', 'now', 'run_on_thread', 'get_time']
9
10
11
# APP_MSG = "[App %s] %s | ID: %02d | R: %02d | P: %02d | F: %s"
12
13
14 2
def get_thread_pool_max_workers():
15
    """Get the number of thread pool max workers."""
16 2
    return int(KytosConfig().options["daemon"].thread_pool_max_workers)
17
18
19
# pylint: disable=invalid-name
20 2
executor = None
21 2
if get_thread_pool_max_workers():
22 2
    executor = ThreadPoolExecutor(max_workers=get_thread_pool_max_workers())
23
24
25 2
def listen_to(event, *events):
26
    """Decorate Event Listener methods.
27
28
    This decorator was built to be used on NAPPs methods to define which
29
    type of event the method will handle. With this, we will be able to
30
    'schedule' the app/method to receive an event when a new event is
31
    registered on the controller buffers.
32
    By using the run_on_thread decorator, we also guarantee that the method
33
    (handler) will be called from inside a new thread, avoiding this method to
34
    block its caller.
35
36
    The decorator will add an attribute to the method called 'events', that
37
    will be a list of the events that the method will handle.
38
39
    The event that will be listened to is always a string, but it can represent
40
    a regular expression to match against multiple Event Types. All listened
41
    events are documented in :doc:`/developer/listened_events` section.
42
43
    Example of usage:
44
45
    .. code-block:: python3
46
47
        class MyAppClass(KytosApp):
48
            @listen_to('kytos/of_core.messages.in')
49
            def my_handler_of_message_in(self, event):
50
                # Do stuff here...
51
52
            @listen_to('kytos/of_core.messages.out')
53
            def my_handler_of_message_out(self, event):
54
                # Do stuff here...
55
56
            @listen_to('kytos/of_core.messages.in.ofpt_hello',
57
                       'kytos/of_core.messages.out.ofpt_hello')
58
            def my_handler_of_hello_messages(self, event):
59
                # Do stuff here...
60
61
            @listen_to('kytos/of_core.message.*.hello')
62
            def my_other_handler_of_hello_messages(self, event):
63
                # Do stuff here...
64
65
            @listen_to('kytos/of_core.message.*.hello')
66
            def my_handler_of_hello_messages(self, event):
67
                # Do stuff here...
68
69
            @listen_to('kytos/of_core.message.*')
70
            def my_stats_handler_of_any_message(self, event):
71
                # Do stuff here...
72
    """
73 2
    def thread_decorator(handler):
74
        """Decorate the handler method.
75
76
        Returns:
77
            A method with an `events` attribute (list of events to be listened)
78
            and also decorated to run on a new thread.
79
80
        """
81
        @run_on_thread
82
        def threaded_handler(*args):
83
            """Decorate the handler to run from a new thread."""
84
            handler(*args)
85
86
        threaded_handler.events = [event]
87
        threaded_handler.events.extend(events)
88
        return threaded_handler
89
90 2
    def thread_pool_decorator(handler):
91
        """Decorate the handler method.
92
93
        Returns:
94
            A method with an `events` attribute (list of events to be listened)
95
            and also decorated to run on in the thread pool
96
97
        """
98 2
        def done_callback(future):
99
            """Done callback."""
100
            if not future.exception():
101
                _ = future.result()
102
103 2
        def inner(*args):
104
            """Decorate the handler to run in the thread pool."""
105 2
            future = executor.submit(handler, *args)
106 2
            future.add_done_callback(done_callback)
107
108 2
        inner.events = [event]
109 2
        inner.events.extend(events)
110 2
        return inner
111
112 2
    if get_thread_pool_max_workers():
113 2
        return thread_pool_decorator
114
    return thread_decorator
115
116
117 2
def now(tzone=timezone.utc):
118
    """Return the current datetime (default to UTC).
119
120
    Args:
121
        tzone (datetime.timezone): Specific time zone used in datetime.
122
123
    Returns:
124
        datetime.datetime.now: Date time with specific time zone.
125
126
    """
127 2
    return datetime.now(tzone)
128
129
130 2
def run_on_thread(method):
131
    """Decorate to run the decorated method inside a new thread.
132
133
    Args:
134
        method (function): function used to run as a new thread.
135
136
    Returns:
137
        Decorated method that will run inside a new thread.
138
        When the decorated method is called, it will not return the created
139
        thread to the caller.
140
141
    """
142 2
    def threaded_method(*args):
143
        """Ensure the handler method runs inside a new thread."""
144 2
        thread = Thread(target=method, args=args)
145
146
        # Set daemon mode so that we don't have to wait for these threads
147
        # to finish when exiting Kytos
148 2
        thread.daemon = True
149 2
        thread.start()
150 2
    return threaded_method
151
152
153 2
def get_time(data=None):
154
    """Receive a dictionary or a string and return a datatime instance.
155
156
    data = {"year": 2006,
157
            "month": 11,
158
            "day": 21,
159
            "hour": 16,
160
            "minute": 30 ,
161
            "second": 00}
162
163
    or
164
165
    data = "21/11/06 16:30:00"
166
167
    2018-04-17T17:13:50Z
168
169
    Args:
170
        data (str, dict): python dict or string to be converted to datetime
171
172
    Returns:
173
        datetime: datetime instance.
174
175
    """
176 2
    if isinstance(data, str):
177 2
        date = datetime.strptime(data, "%Y-%m-%dT%H:%M:%S")
178 2
    elif isinstance(data, dict):
179 2
        date = datetime(**data)
180
    else:
181 2
        return None
182
    return date.replace(tzinfo=timezone.utc)
183