yfrake.client.session   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 128
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 61
dl 0
loc 128
rs 10
c 0
b 0
f 0
wmc 12

8 Functions

Rating   Name   Duplication   Size   Complexity  
A close_thread() 0 12 3
A run_background_thread() 0 4 1
A open_thread() 0 6 1
A is_locked() 0 2 1
A open_async() 0 12 1
A asyncio_run_threadsafe() 0 3 1
A get_event_loop() 0 2 1
A close_async() 0 4 1

1 Method

Rating   Name   Duplication   Size   Complexity  
A SessionSingleton.__new__() 0 4 2
1
# ==================================================================================== #
2
#    session.py - This file is part of the YFrake package.                             #
3
# ------------------------------------------------------------------------------------ #
4
#                                                                                      #
5
#    MIT License                                                                       #
6
#                                                                                      #
7
#    Copyright (c) 2022 Mattias Aabmets                                                #
8
#                                                                                      #
9
#    Permission is hereby granted, free of charge, to any person obtaining a copy      #
10
#    of this software and associated documentation files (the "Software"), to deal     #
11
#    in the Software without restriction, including without limitation the rights      #
12
#    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell         #
13
#    copies of the Software, and to permit persons to whom the Software is             #
14
#    furnished to do so, subject to the following conditions:                          #
15
#                                                                                      #
16
#    The above copyright notice and this permission notice shall be included in all    #
17
#    copies or substantial portions of the Software.                                   #
18
#                                                                                      #
19
#    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR        #
20
#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,          #
21
#    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE       #
22
#    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER            #
23
#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,     #
24
#    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     #
25
#    SOFTWARE.                                                                         #
26
#                                                                                      #
27
# ==================================================================================== #
28
from ..config.config import ConfigSingleton
29
from . import base_url
30
from threading import Thread
31
import aiohttp
32
import asyncio
33
import time
34
35
36
# ==================================================================================== #
37
class SessionSingleton:
38
    """
39
    This class holds a connection to the Yahoo
40
    Finance API servers. A session is established
41
    by the client 'configure' decorator. There can
42
    only be a single session active at any time.
43
    """
44
    # ------------------------------------------------------------------------------------ #
45
    timeout: aiohttp.ClientTimeout = None
46
    session: aiohttp.ClientSession = None
47
    connector: aiohttp.TCPConnector = None
48
    loop: asyncio.AbstractEventLoop = None
49
    thread: Thread = None
50
    locked: bool = False
51
    __instance__ = None
52
53
    # Singleton pattern
54
    # ------------------------------------------------------------------------------------ #
55
    def __new__(cls):
56
        if not cls.__instance__:
57
            cls.__instance__ = super(SessionSingleton, cls).__new__(cls)
58
        return cls.__instance__
59
60
61
# ==================================================================================== #
62
def is_locked() -> bool:
63
    return SessionSingleton().locked
64
65
66
# ------------------------------------------------------------------------------------ #
67
def get_event_loop() -> asyncio.AbstractEventLoop:
68
    return SessionSingleton().loop
69
70
71
# ------------------------------------------------------------------------------------ #
72
async def open_async() -> None:
73
    ss = SessionSingleton()
74
    settings = ConfigSingleton().settings['client']
75
    ss.timeout = aiohttp.ClientTimeout(total=settings['timeout'])
76
    ss.connector = aiohttp.TCPConnector(limit=settings['limit'])
77
    ss.session = aiohttp.ClientSession(
78
        connector=ss.connector,
79
        timeout=ss.timeout,
80
        base_url=base_url,
81
        raise_for_status=True
82
    )
83
    ss.locked = True
84
85
86
# ------------------------------------------------------------------------------------ #
87
async def close_async() -> None:
88
    ss = SessionSingleton()
89
    await ss.session.close()
90
    ss.locked = False
91
92
93
# ------------------------------------------------------------------------------------ #
94
def open_thread() -> None:
95
    ss = SessionSingleton()
96
    ss.loop = asyncio.new_event_loop()
97
    ss.thread = Thread(target=run_background_thread, daemon=True)
98
    ss.thread.start()
99
    asyncio_run_threadsafe(open_async(), ss.loop)  # blocking
100
101
102
# ------------------------------------------------------------------------------------ #
103
def close_thread() -> None:
104
    ss = SessionSingleton()
105
    asyncio_run_threadsafe(close_async(), ss.loop)  # blocking
106
    ss.loop.call_soon_threadsafe(ss.loop.stop)
107
108
    force_iter = True
109
    while force_iter or ss.loop.is_running():
110
        force_iter = False
111
        time.sleep(0)
112
113
    ss.loop.close()
114
    ss.thread.join()
115
116
117
# ------------------------------------------------------------------------------------ #
118
def asyncio_run_threadsafe(coro, loop) -> None:
119
    future = asyncio.run_coroutine_threadsafe(coro, loop)
120
    future.result()  # blocks until concurrent future is done
121
122
123
# ------------------------------------------------------------------------------------ #
124
def run_background_thread() -> None:
125
    ss = SessionSingleton()
126
    asyncio.set_event_loop(ss.loop)
127
    ss.loop.run_forever()
128