easy-utils-dev 2.140__tar.gz → 2.142__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of easy-utils-dev might be problematic. Click here for more details.

Files changed (44) hide show
  1. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/PKG-INFO +2 -1
  2. easy_utils_dev-2.142/easy_utils_dev/uiserver.py +227 -0
  3. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/wsnoclib.py +104 -4
  4. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev.egg-info/PKG-INFO +2 -1
  5. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev.egg-info/requires.txt +1 -0
  6. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/setup.py +2 -1
  7. easy_utils_dev-2.140/easy_utils_dev/uiserver.py +0 -193
  8. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/MANIFEST.in +0 -0
  9. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/EasySsh.py +0 -0
  10. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/Events.py +0 -0
  11. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/FastQueue.py +0 -0
  12. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/NameObject.py +0 -0
  13. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/__init__.py +0 -0
  14. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/abortable.py +0 -0
  15. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/brevosmtp.py +0 -0
  16. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/check_license.py +0 -0
  17. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/cplib.py +0 -0
  18. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/custom_env.py +0 -0
  19. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/debugger-C-PF4PAMMP.py +0 -0
  20. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/debugger.py +0 -0
  21. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/easy_oracle.py +0 -0
  22. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/encryptor.py +0 -0
  23. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/ept.py +0 -0
  24. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/ept_sql/create_dirs.sql +0 -0
  25. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/ept_sql/create_ept_tables.sql +0 -0
  26. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/exceptions.py +0 -0
  27. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/filescompressor.py +0 -0
  28. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/generate_license.py +0 -0
  29. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/keycloakapi.py +0 -0
  30. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/lralib.py +0 -0
  31. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/ne1830PSS.py +0 -0
  32. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/nsp_kafka.py +0 -0
  33. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/openid_server.py +0 -0
  34. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/optics_utils.py +0 -0
  35. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/require_auth.py +0 -0
  36. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/simple_sqlite.py +0 -0
  37. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/temp_memory.py +0 -0
  38. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/utils.py +0 -0
  39. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/winserviceapi.py +0 -0
  40. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev/wsselib.py +0 -0
  41. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev.egg-info/SOURCES.txt +0 -0
  42. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev.egg-info/dependency_links.txt +0 -0
  43. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/easy_utils_dev.egg-info/top_level.txt +0 -0
  44. {easy_utils_dev-2.140 → easy_utils_dev-2.142}/setup.cfg +0 -0
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easy_utils_dev
3
- Version: 2.140
3
+ Version: 2.142
4
4
  Keywords: python3
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Requires-Dist: psutil
7
7
  Requires-Dist: ping3
8
+ Requires-Dist: snakebite-py3
8
9
  Requires-Dist: flask
9
10
  Requires-Dist: flask_cors
10
11
  Requires-Dist: xmltodict
@@ -0,0 +1,227 @@
1
+ import copy
2
+ import gc
3
+ import time
4
+ from werkzeug.serving import ThreadedWSGIServer
5
+ from easy_utils_dev.utils import getRandomKey , generateToken , getTimestamp
6
+ from flask_socketio import SocketIO
7
+ from engineio.async_drivers import gevent
8
+ from flask_cors import CORS
9
+ import logging , os
10
+ from flask import jsonify, request , current_app
11
+ from flask import Flask
12
+ from threading import Thread
13
+ import threading
14
+ from easy_utils_dev.custom_env import cenv
15
+ from easy_utils_dev.utils import kill_thread
16
+ from multiprocessing import Process
17
+ from werkzeug.serving import make_ssl_devcert
18
+ from time import sleep
19
+ from easy_utils_dev.utils import start_thread , getRandomKeysAndStr , mkdirs
20
+ from easy_utils_dev.temp_memory import TemporaryMemory
21
+
22
+ def getClassById( id ) :
23
+ return cenv[id]
24
+
25
+ def create_ssl(host,output) :
26
+ '''
27
+ host : is the IP/Adress of the server which servers the web-server
28
+ output: the output locaiton to generate the ssl certificate. it should end with filename without extension
29
+ '''
30
+ return make_ssl_devcert( output , host=host)
31
+
32
+ def clone_request(request):
33
+ """Return a plain dict clone of Flask request data."""
34
+ return {
35
+ "method": request.method,
36
+ "path": request.path,
37
+ "url": request.url,
38
+ "headers": dict(request.headers),
39
+ "args": request.args.to_dict(flat=False),
40
+ "form": request.form.to_dict(flat=False),
41
+ "json": request.get_json(silent=True),
42
+ "data": request.get_data(), # raw body bytes
43
+ "files": {k: v.filename for k, v in request.files.items()},
44
+ "remote_addr": request.remote_addr,
45
+ "cookies": request.cookies,
46
+ }
47
+
48
+ class AbortRequest :
49
+ def __init__(self, request ) :
50
+ self.request = clone_request(request)
51
+ self.abort_id = None
52
+ self.abortable = False
53
+ self.thread = None
54
+ self.cache = None
55
+ self.start_ts = getTimestamp()
56
+
57
+ def abort(self) :
58
+ kill_thread(self.thread)
59
+ self.cache.delete(self.abort_id)
60
+ try :
61
+ gc.collect()
62
+ except :
63
+ pass
64
+
65
+
66
+ class UISERVER :
67
+ def __init__(self ,
68
+ id=getRandomKey(n=15),
69
+ secretkey=generateToken(),
70
+ address='localhost',
71
+ port=5312 ,
72
+ https=False ,
73
+ ssl_crt=None,
74
+ ssl_key=None,
75
+ template_folder='templates/' ,
76
+ static_folder = 'templates/assets'
77
+ ,**kwargs
78
+ ) -> None:
79
+ self.id = id
80
+ self.static_folder = static_folder
81
+ self.app = app = Flask(self.id , template_folder=template_folder , static_folder=self.static_folder )
82
+ app.config['SECRET_KEY'] = secretkey
83
+ CORS(app,resources={r"/*":{"origins":"*"}})
84
+ self.address= address
85
+ self.port = port
86
+ self.thread = None
87
+ self.ssl_crt=ssl_crt
88
+ self.ssl_key=ssl_key
89
+ self.enable_test_url=True
90
+ self.abort_requests = {}
91
+ self.abort_base_url = '/request/abort'
92
+ if https :
93
+ self.httpProtocol = 'https'
94
+ else :
95
+ self.httpProtocol = 'http'
96
+ self.socketio = SocketIO(app , cors_allowed_origins="*" ,async_mode='threading' , engineio_logger=False , always_connect=True ,**kwargs )
97
+ cenv[id] = self
98
+ self.fullAddress = f"{self.httpProtocol}://{self.address}:{self.port}"
99
+ self.cache = TemporaryMemory()
100
+
101
+ def update_cert(self , crt, ssl ) :
102
+ self.ssl_crt=crt
103
+ self.ssl_key=ssl
104
+
105
+ def register_abortable_request(self , request , abort_id = None ) :
106
+ path = request.path
107
+ Abort = AbortRequest(request)
108
+ if not path.startswith(self.abort_base_url) :
109
+ if not abort_id :
110
+ if not request.headers.get('abortid') :
111
+ abort_id = getRandomKeysAndStr(n=20)
112
+ else :
113
+ abort_id = request.headers.get('abortid')
114
+
115
+ Abort.abort_id = abort_id
116
+ current_thread = threading.current_thread()
117
+ Abort.thread = current_thread
118
+ Abort.cache = self.cache
119
+ Abort.start_ts = getTimestamp()
120
+ self.cache.set( Abort , custom_key=abort_id , auto_destroy_period=120 , store_deleted_key=False )
121
+ return Abort
122
+
123
+ def start_before_request(self) :
124
+
125
+ @self.app.route(f'{self.abort_base_url}/<id>' , methods=['DELETE'])
126
+ def abort_request(id : str ) :
127
+ abort : AbortRequest = self.cache.get(id)
128
+ timestamp = getTimestamp()
129
+ if abort :
130
+ abort.abort()
131
+ for i in range(30) :
132
+ th = abort.thread
133
+ alive = th.is_alive()
134
+ if not alive :
135
+ break
136
+ time.sleep(.25)
137
+ return { 'status' : 200 , 'message' : 'Request aborted' , 'abort_timestamp' : timestamp , 'abort_id' : id , 'alive' : alive , 'url' : abort.request.get('path')}
138
+ else :
139
+ return { 'status' : 404 , 'message' : 'Request not found or request is not abortable. Check request headers for abortable flag.'}
140
+
141
+ @self.app.before_request
142
+ def before_request() :
143
+ abortable = request.headers.get('abortable')
144
+ if abortable :
145
+ abort = self.register_abortable_request(request)
146
+ request.abortable = True
147
+ request.abort_id = abort.abort_id
148
+ # check here if async in the headers
149
+ # if yes . i will trigger the function in thread
150
+ # start_tread(#how to get the target function here ? )
151
+ # now i want to return response to UI { status : 200 , message : 'request now in running bg' , abort_id : abort.abort_id }
152
+ # the flask function should not be called again
153
+ if request.headers.get('async') == 'false' :
154
+ target_func = current_app.view_functions.get(request.endpoint)
155
+ if not target_func:
156
+ return jsonify({"error": "Route not found"}), 404
157
+ th = start_thread(target=target_func, args=request.args, kwargs=request.form)
158
+ abort.thread = th
159
+ return {"status": 200, "message": "Request now in running bg", "abort_id": abort.abort_id} , 200
160
+
161
+ @self.app.after_request
162
+ def after_request(response) :
163
+ try :
164
+ if request.abortable :
165
+ response.headers['abortid'] = request.abort_id
166
+ response.headers['abortable'] = True
167
+ except :
168
+ response.headers['abortable'] = False
169
+ return response
170
+
171
+
172
+ def getInstance(self) :
173
+ return self.getFlask() , self.getSocketio() , self.getWsgi()
174
+
175
+ def getSocketio( self ):
176
+ return self.socketio
177
+
178
+ def getFlask( self ):
179
+ return self.app
180
+
181
+ def getWsgi(self) :
182
+ return self.wsgi_server
183
+
184
+ def shutdownUi(self) :
185
+ kill_thread(self.thread)
186
+ self.wsgi_server.server_close()
187
+ self.wsgi_server.shutdown()
188
+
189
+ def _wait_th(self , t ) :
190
+ t.join()
191
+
192
+ def thrStartUi(self , suppress_prints=True) :
193
+ if self.enable_test_url :
194
+ if not suppress_prints :
195
+ print(f'TEST URL GET-METHOD /connection/test/internal')
196
+ @self.app.route('/connection/test/internal' , methods=['GET'])
197
+ def test_connection():
198
+ return f"Status=200<br> ID={self.id}<br> one-time-token={getRandomKey(20)}"
199
+ if self.httpProtocol == 'http' :
200
+ con = None
201
+ elif self.httpProtocol == 'https' :
202
+ con=(self.ssl_crt , self.ssl_key)
203
+ self.wsgi_server = wsgi_server = ThreadedWSGIServer(
204
+ host = self.address ,
205
+ ssl_context=con,
206
+ # ssl_context=('ssl.crt', 'ssl.key'),
207
+ port = self.port,
208
+ app = self.app )
209
+ if not suppress_prints :
210
+ print(f"web-socket: {self.fullAddress}")
211
+ print(f"UI URL : {self.fullAddress}")
212
+ log = logging.getLogger('werkzeug')
213
+ log.setLevel(logging.ERROR)
214
+ wsgi_server.serve_forever()
215
+
216
+ def startUi(self ,daemon , suppress_prints=True) :
217
+ self.start_before_request()
218
+ self.thread = self.flaskprocess = Thread(target=self.thrStartUi , args=[suppress_prints])
219
+ self.flaskprocess.daemon = False
220
+ self.flaskprocess.start()
221
+ start_thread(target=self._wait_th , args=[self.thread] , daemon=daemon)
222
+ return self.thread
223
+
224
+ def stopUi(self) :
225
+ kill_thread(self.thread)
226
+ return True
227
+
@@ -1,7 +1,7 @@
1
1
  from easy_utils_dev.debugger import DEBUGGER
2
2
  import requests , json , subprocess
3
3
  from requests.auth import HTTPBasicAuth as BAuth
4
- from .utils import pingAddress , fixTupleForSql , start_thread , mkdirs
4
+ from .utils import pingAddress , fixTupleForSql , start_thread , mkdirs , getTimestamp
5
5
  from time import sleep
6
6
  from urllib3.exceptions import InsecureRequestWarning
7
7
  from urllib3 import disable_warnings
@@ -15,7 +15,8 @@ import tempfile , os
15
15
  from kafka import KafkaConsumer
16
16
  from easy_utils_dev.utils import kill_thread
17
17
  import atexit
18
-
18
+ from snakebite.client import Client as HdfsClient
19
+ from datetime import datetime
19
20
 
20
21
 
21
22
  class KafkaConfig :
@@ -38,7 +39,6 @@ class KafkaConfig :
38
39
  self.enable_auto_refresh = False
39
40
 
40
41
 
41
-
42
42
  class WSNOCLIB :
43
43
  def __init__(
44
44
  self,
@@ -51,7 +51,8 @@ class WSNOCLIB :
51
51
  request_max_count=30,
52
52
  tmp_dir = tempfile.gettempdir() ,
53
53
  kafka = KafkaConfig(),
54
- register_atexit=True
54
+ register_atexit=True,
55
+ trust_env=True
55
56
  ):
56
57
  self.logger = DEBUGGER(f'{debug_name}-{ip}',level=debug_level,homePath=debug_homepath)
57
58
  self.disabledWarnings = self.disableUrlWarnings()
@@ -59,6 +60,7 @@ class WSNOCLIB :
59
60
  self.address = ip
60
61
  self.username = username
61
62
  self.password = password
63
+ self.trust_env = trust_env
62
64
  self.external_nsp = False
63
65
  self.api_count = 0
64
66
  self.api_count_limit = 999999999999
@@ -82,6 +84,8 @@ class WSNOCLIB :
82
84
  self.refresh_thread = None
83
85
  self.token_refresh_count = 0
84
86
  self.session = WSNOCSession(self)
87
+ self.connected = False
88
+ self.pm_hadoop = PmHadoopClient(self)
85
89
  if register_atexit :
86
90
  atexit.register(self.goodbye)
87
91
 
@@ -150,6 +154,7 @@ class WSNOCLIB :
150
154
  if auto_refresh_token :
151
155
  self.autoRefreshThread = self.refresh_thread = start_thread(target=self.runAutoRefreshThread)
152
156
  self.logger.debug(f'token => {r.text}')
157
+ self.connected = True
153
158
  return self.token
154
159
 
155
160
 
@@ -182,6 +187,7 @@ class WSNOCLIB :
182
187
  r.close()
183
188
  except :
184
189
  pass
190
+ self.connected = False
185
191
  return True
186
192
 
187
193
  def goodbye(self):
@@ -610,6 +616,7 @@ class WSNOCLIB :
610
616
  self.bearer_token = f'Bearer {self.access_token}'
611
617
  self.token = r.json()
612
618
  self.token.update({'bearer_token' : self.bearer_token })
619
+ self.connected = True
613
620
  return r
614
621
 
615
622
  def session_info(self) :
@@ -706,6 +713,9 @@ class WSNOCSession(requests.Session):
706
713
  self._wsnoc = wsnoc
707
714
  self.verify = False
708
715
  self.retries = 0
716
+ if not wsnoc.trust_env :
717
+ os.environ['https_proxy'] = ""
718
+ os.environ['http_proxy'] = ""
709
719
  self.debug_this_request = False
710
720
  self.skip_hold_for_token_refresh = False
711
721
 
@@ -749,6 +759,96 @@ class WSNOCSession(requests.Session):
749
759
  self._wsnoc.logger.info(f'[{method}] : {url} - [{request.status_code}]')
750
760
  return request
751
761
 
762
+ class PmHadoopClient :
763
+
764
+
765
+ def __init__(self , _wsnoc : WSNOCLIB):
766
+ self.ip = _wsnoc.address
767
+ self.wsnoc = _wsnoc
768
+ self.client : HdfsClient = None
769
+ self.hdfs_port = 8020
770
+ self.hdfs_user = 'otn'
771
+ self.hdfs_root = '/'
772
+ self.hdfs_use_trash = False
773
+ self.PM24H = 1
774
+ self.PM15M = 2
775
+ self.KPIAGGR = 3
776
+ self.logger : DEBUGGER = self.wsnoc.logger
777
+
778
+
779
+ def connect(self) :
780
+ if not self.wsnoc.connected :
781
+ raise Exception('WSNOC is not connected')
782
+ try :
783
+ self.client = HdfsClient(self.ip, self.hdfs_port, use_trash=self.hdfs_use_trash)
784
+ except Exception as e:
785
+ self.logger.warning('PM Hadoop client is using WSNOC port 8020/custom port. Please check if it is not blocked by firewall.' , source='PmHadoopClient')
786
+ self.logger.error(f'Failed to connect to PM Hadoop: {e}' , source='PmHadoopClient')
787
+ raise
788
+
789
+ def change_date_to_timestamp(self , date_str) :
790
+ '''
791
+ this is a helper function to convert date string to timestamp
792
+ date_str : must be in the format of %Y%m%d example: 20250101
793
+ return : timestamp
794
+ '''
795
+ dt = datetime.strptime(date_str, "%Y%m%d")
796
+ return int(dt.timestamp())
797
+
798
+ def pm_list(self , mode , date_range=[]) :
799
+ '''
800
+ mode : must be one of the following:
801
+ - self.PM24H
802
+ - self.PM15M
803
+ - self.KPIAGGR
804
+ date_range : must be a list of two integers in the format of [start_timestamp, end_timestamp]
805
+ - for example: [1718217600, 1718221200]
806
+ - if date_range is not provided, all available PM dates will be returned
807
+ '''
808
+ if mode == self.PM24H :
809
+ _mode = 'ONE_DAY'
810
+ elif mode == self.PM15M :
811
+ _mode = 'FIFTEEN_MINS'
812
+ elif mode == self.KPIAGGR :
813
+ _mode = 'KPIAGGR'
814
+ else :
815
+ raise Exception(f'Invalid mode: {mode}')
816
+ self.logger.info(f"Getting available PM dates for {_mode}" , source='PmHadoopClient')
817
+ dirs = list(self.client.ls([f'/PMDATA/{_mode}']))
818
+ ts_now = getTimestamp()
819
+ for index , dir in enumerate(dirs) :
820
+ self.logger.info(f"Processing {dir.get('path')}" , source='PmHadoopClient')
821
+ date_str = dir.get('path').split('=')[-1]
822
+ dir['pm_date'] = int(date_str)
823
+ dir['mode'] = _mode
824
+ dt = datetime.strptime(date_str, "%Y%m%d")
825
+ dir['pm_date_timestamp'] = int(dt.timestamp())
826
+ if len(date_range) > 0 :
827
+ if dir['pm_date_timestamp'] < date_range[0] :
828
+ del dirs[index]
829
+ if dir['pm_date_timestamp'] > date_range[1] :
830
+ del dirs[index]
831
+ return dirs
832
+
833
+ def _download_dir(self , hdfs_path, local_path):
834
+ for entry in self.client.ls([hdfs_path]):
835
+ # print(entry)
836
+ entry_path = entry['path']
837
+ entry_type = entry['file_type']
838
+ if entry_type == 'd':
839
+ subdir = os.path.join(local_path, os.path.basename(entry_path))
840
+ mkdirs(subdir)
841
+ self._download_dir(entry_path, subdir)
842
+ else: # FILE
843
+ self.logger.debug(f"Downloading {entry_path} → {local_path}" , source='PmHadoopClient')
844
+ self.client.copyToLocal([entry_path], local_path)
845
+
846
+ def download(self , obj , destination_path) :
847
+ self.wsnoc.logger.info(f'Downloading {obj.get("pm_date")} to {destination_path}')
848
+ destination_path = f"{destination_path}/{obj.get('mode')}/{obj.get('pm_date')}"
849
+ mkdirs(destination_path)
850
+ self._download_dir(obj.get('path'), destination_path)
851
+
752
852
 
753
853
  if __name__ == '__main__' :
754
854
  # noc = WSNOCLIB('10.20.30.55' , 'admin' , 'Nokia@2024')
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: easy_utils_dev
3
- Version: 2.140
3
+ Version: 2.142
4
4
  Keywords: python3
5
5
  Classifier: Programming Language :: Python :: 3
6
6
  Requires-Dist: psutil
7
7
  Requires-Dist: ping3
8
+ Requires-Dist: snakebite-py3
8
9
  Requires-Dist: flask
9
10
  Requires-Dist: flask_cors
10
11
  Requires-Dist: xmltodict
@@ -1,5 +1,6 @@
1
1
  psutil
2
2
  ping3
3
+ snakebite-py3
3
4
  flask
4
5
  flask_cors
5
6
  xmltodict
@@ -1,6 +1,6 @@
1
1
  from setuptools import setup, find_packages
2
2
 
3
- VERSION = '2.140'
3
+ VERSION = '2.142'
4
4
 
5
5
  # Setting up
6
6
  setup(
@@ -11,6 +11,7 @@ setup(
11
11
  install_requires=[
12
12
  'psutil' ,
13
13
  'ping3' ,
14
+ 'snakebite-py3',
14
15
  'flask' ,
15
16
  'flask_cors' ,
16
17
  'xmltodict' ,
@@ -1,193 +0,0 @@
1
- from werkzeug.serving import ThreadedWSGIServer
2
- from easy_utils_dev.utils import getRandomKey , generateToken , getTimestamp
3
- from flask_socketio import SocketIO
4
- from engineio.async_drivers import gevent
5
- from engineio.async_drivers import threading
6
- from flask_cors import CORS
7
- import logging , os
8
- from flask import Flask
9
- from threading import Thread
10
- from easy_utils_dev.custom_env import cenv
11
- from easy_utils_dev.utils import kill_thread
12
- from multiprocessing import Process
13
- from werkzeug.serving import make_ssl_devcert
14
- from time import sleep
15
- from easy_utils_dev.utils import start_thread , getRandomKeysAndStr , mkdirs
16
-
17
- def getClassById( id ) :
18
- return cenv[id]
19
-
20
- def create_ssl(host,output) :
21
- '''
22
- host : is the IP/Adress of the server which servers the web-server
23
- output: the output locaiton to generate the ssl certificate. it should end with filename without extension
24
- '''
25
- return make_ssl_devcert( output , host=host)
26
-
27
-
28
-
29
- class Abort :
30
- def __init__(self, requestId=getRandomKeysAndStr(50)) :
31
- self.requestId = requestId
32
- self.result= None
33
- self.thread = None
34
- self.aborted=False
35
- self.response = {}
36
- self.error = False
37
- self.message = ''
38
- self.starttimestamp = getTimestamp()
39
- self.endtimestamp = 0
40
- self.kill = self.api_abort
41
- pass
42
-
43
-
44
- def abortable_process(self , operation , args=[] , kwargs={}) :
45
- def thread_run() :
46
- try :
47
- self.result = operation( *args, **kwargs )
48
- self.error = False
49
- self.message = ''
50
- self.endtimestamp = getTimestamp()
51
- except Exception as error :
52
- self.error = True
53
- self.message = str(error)
54
- thread = self.thread = start_thread( target = thread_run )
55
- while thread.is_alive() :
56
- sleep(.1)
57
- if self.aborted :
58
- self.response = {
59
- 'message' : 'request aborted.' ,
60
- 'id' : self.requestId ,
61
- 'status' : 405 ,
62
- 'result' : None,
63
- 'error' : self.error ,
64
- 'error_message' : '',
65
- 'starttimestamp' : self.starttimestamp,
66
- 'endtimestamp' : self.endtimestamp,
67
- 'aborted' : True,
68
- 'threadIsAlive' : thread.is_alive()
69
- }
70
- return self.response
71
- sleep(.2)
72
- self.response = {
73
- 'message' : 'request completed.' ,
74
- 'id' : self.requestId ,
75
- 'status' : 200 ,
76
- 'result' : self.result ,
77
- 'error' : self.error ,
78
- 'error_message' : self.message ,
79
- 'starttimestamp' : self.starttimestamp,
80
- 'endtimestamp' : self.endtimestamp,
81
- 'aborted' : False ,
82
- 'threadIsAlive' : thread.is_alive()
83
- }
84
- return self.response
85
-
86
-
87
- def api_abort(self) :
88
- self.endtimestamp = getTimestamp()
89
- kill_thread(self.thread)
90
- sleep(.5)
91
- self.aborted=True
92
-
93
-
94
- class UISERVER :
95
- def __init__(self ,
96
- id=getRandomKey(n=15),
97
- secretkey=generateToken(),
98
- address='localhost',
99
- port=5312 ,
100
- https=False ,
101
- ssl_crt=None,
102
- ssl_key=None,
103
- template_folder='templates/' ,
104
- static_folder = 'templates/assets'
105
- ,**kwargs
106
- ) -> None:
107
- self.id = id
108
- self.static_folder = static_folder
109
- self.app = app = Flask(self.id , template_folder=template_folder , static_folder=self.static_folder )
110
- app.config['SECRET_KEY'] = secretkey
111
- CORS(app,resources={r"/*":{"origins":"*"}})
112
- self.address= address
113
- self.port = port
114
- self.thread = None
115
- self.ssl_crt=ssl_crt
116
- self.ssl_key=ssl_key
117
- self.enable_test_url=True
118
- self.abort_requests = {}
119
- if https :
120
- self.httpProtocol = 'https'
121
- else :
122
- self.httpProtocol = 'http'
123
- self.socketio = SocketIO(app , cors_allowed_origins="*" ,async_mode='threading' , engineio_logger=False , always_connect=True ,**kwargs )
124
- cenv[id] = self
125
- self.fullAddress = f"{self.httpProtocol}://{self.address}:{self.port}"
126
-
127
- def update_cert(self , crt, ssl ) :
128
- self.ssl_crt=crt
129
- self.ssl_key=ssl
130
-
131
-
132
- def getAbort(self , id ) :
133
- result : Abort = self.abort_requests.get(id , Abort)
134
- return result
135
-
136
- def updateAbort( self , id , abort ) :
137
- self.abort_requests[id] = abort
138
-
139
- def getInstance(self) :
140
- return self.getFlask() , self.getSocketio() , self.getWsgi()
141
-
142
- def getSocketio( self ):
143
- return self.socketio
144
-
145
- def getFlask( self ):
146
- return self.app
147
-
148
- def getWsgi(self) :
149
- return self.wsgi_server
150
-
151
- def shutdownUi(self) :
152
- kill_thread(self.thread)
153
- self.wsgi_server.server_close()
154
- self.wsgi_server.shutdown()
155
-
156
- def _wait_th(self , t ) :
157
- t.join()
158
-
159
- def thrStartUi(self , suppress_prints=True) :
160
- if self.enable_test_url :
161
- if not suppress_prints :
162
- print(f'TEST URL GET-METHOD /connection/test/internal')
163
- @self.app.route('/connection/test/internal' , methods=['GET'])
164
- def test_connection():
165
- return f"Status=200<br> ID={self.id}<br> one-time-token={getRandomKey(20)}"
166
- if self.httpProtocol == 'http' :
167
- con = None
168
- elif self.httpProtocol == 'https' :
169
- con=(self.ssl_crt , self.ssl_key)
170
- self.wsgi_server = wsgi_server = ThreadedWSGIServer(
171
- host = self.address ,
172
- ssl_context=con,
173
- # ssl_context=('ssl.crt', 'ssl.key'),
174
- port = self.port,
175
- app = self.app )
176
- if not suppress_prints :
177
- print(f"web-socket: {self.fullAddress}")
178
- print(f"UI URL : {self.fullAddress}")
179
- log = logging.getLogger('werkzeug')
180
- log.setLevel(logging.ERROR)
181
- wsgi_server.serve_forever()
182
-
183
- def startUi(self ,daemon , suppress_prints=True) :
184
- self.thread = self.flaskprocess = Thread(target=self.thrStartUi , args=[suppress_prints])
185
- self.flaskprocess.daemon = False
186
- self.flaskprocess.start()
187
- start_thread(target=self._wait_th , args=[self.thread] , daemon=daemon)
188
- return self.thread
189
-
190
- def stopUi(self) :
191
- kill_thread(self.thread)
192
- return True
193
-
File without changes