Test Failed
Pull Request — master (#149)
by Vinicius
06:57
created

kytos.core.helpers   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 182
Duplicated Lines 0 %

Test Coverage

Coverage 82.22%

Importance

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