ul-api-utils 7.9.7__py3-none-any.whl → 7.10.1__py3-none-any.whl

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 ul-api-utils might be problematic. Click here for more details.

example/conf.py CHANGED
@@ -10,8 +10,6 @@ from example.permissions import permissions
10
10
  sdk = ApiSdk(ApiSdkConfig(
11
11
  sockets_config=SocketIOConfig(
12
12
  message_queue='redis://localhost:16379',
13
- logs_enabled=True,
14
- engineio_logs_enabled=False,
15
13
  ),
16
14
  service_name='example_service',
17
15
  permissions=permissions,
example/main.py CHANGED
@@ -1,7 +1,3 @@
1
- # from gevent import monkey
2
- #
3
- # monkey.patch_all()
4
-
5
1
  import os
6
2
 
7
3
  if int(os.environ.get('PROFILE_MEM', '0')):
@@ -1,13 +1,16 @@
1
1
  import logging
2
2
  from time import sleep
3
3
  from datetime import datetime, timedelta
4
- from typing import List, Optional, Tuple
4
+ from typing import List, Optional, Tuple, Any
5
5
 
6
+ from flask_socketio import emit
6
7
 
8
+ from ul_api_utils.resources.sockets import socket_io
7
9
  from flask import jsonify
8
10
  from pydantic import BaseModel
9
11
  from ul_db_utils.modules.postgres_modules.db import db
10
12
  from werkzeug import Response as BaseResponse
13
+ from flask import request
11
14
  from example.conf import sdk
12
15
  from example.permissions import SOME_PERMISSION, SOME_PERMISSION2
13
16
  from ul_api_utils.api_resource.api_request import ApiRequestQuery
@@ -318,3 +321,21 @@ def somefile(api_resource: ApiResource) -> FileApiResponse:
318
321
  as_attachment=True,
319
322
  attachment_filename='import_devices_log.txt',
320
323
  )
324
+
325
+
326
+ @socket_io.on('message')
327
+ def handle(data: Any, message: Any) -> None:
328
+ sid = request.sid
329
+ emit('message', {"data1": 1, "data": 2}, room=sid)
330
+
331
+
332
+ @socket_io.event
333
+ def connect():
334
+ logger.info("Connected!")
335
+ logger.info("SESSION INFO: " + str(request.sid))
336
+
337
+
338
+ @socket_io.event('disconnect')
339
+ def disconnect():
340
+ logger.info("Client disconnected!")
341
+ logger.info("SESSION INFO: " + str(request.sid))
@@ -6,7 +6,7 @@ import inspect
6
6
  import os
7
7
  import logging
8
8
  from datetime import datetime
9
- from typing import Dict, Callable, Any, List, TextIO
9
+ from typing import Dict, Callable, Any, List, TextIO, Set
10
10
  from flask import url_for, Flask
11
11
  from ul_py_tool.commands.cmd import Cmd
12
12
 
@@ -4,6 +4,7 @@ import re
4
4
  import subprocess
5
5
  import sys
6
6
  from datetime import datetime
7
+ from typing import Optional
7
8
 
8
9
  from ul_py_tool.commands.cmd import Cmd
9
10
  from ul_py_tool.utils.arg_str2bool import arg_str2bool
@@ -26,6 +27,7 @@ class CmdStart(Cmd):
26
27
  max_requests: int
27
28
  max_requests_jitter: int
28
29
  worker_class: str
30
+ statsd_endpoint: Optional[str]
29
31
 
30
32
  @property
31
33
  def app_rel_dir(self) -> str:
@@ -51,6 +53,7 @@ class CmdStart(Cmd):
51
53
  parser.add_argument('--max-requests', dest='max_requests', type=int, default=1000, required=False)
52
54
  parser.add_argument('--max-requests-jitter', dest='max_requests_jitter', type=int, default=50, required=False)
53
55
  parser.add_argument('--worker-class', dest='worker_class', type=str, default='sync', required=False)
56
+ parser.add_argument('--statsd_endpoint', dest='statsd_endpoint', type=str, default=None, required=False)
54
57
 
55
58
  def run(self) -> None:
56
59
  env = os.environ.copy()
@@ -84,6 +87,10 @@ class CmdStart(Cmd):
84
87
  *(['--reload'] if debug else ['--preload']),
85
88
  f'{self.app_module}:{self.app_name}',
86
89
  ]
90
+
91
+ if self.statsd_endpoint and self.env != ENV_LOCAL:
92
+ args.extend([f'--statsd-host={self.statsd_endpoint}', f"--statsd-prefix={self.app_module.split('.')[1]}:{self.env}"])
93
+
87
94
  write_stdout(f'name={name}')
88
95
  write_stdout(f'args={args}')
89
96
  subprocess.run(['gunicorn', '--check-config', '--print-config', *args], cwd=os.getcwd(), stdout=sys.stdout, stderr=sys.stderr, text=True, env=env)
@@ -8,7 +8,7 @@ from base64 import b64decode
8
8
  from datetime import datetime
9
9
  from functools import wraps
10
10
  from typing import List, Union, Callable, Tuple, Any, Optional, TypeVar, cast, Set, TYPE_CHECKING
11
- from flask_socketio import SocketIO # type: ignore
11
+ from ul_api_utils.resources.sockets import socket_io
12
12
  from flask import Response, request, Flask, url_for as flask_url_for, _app_ctx_stack
13
13
  from pydantic import BaseModel
14
14
  from redis.connection import parse_url
@@ -50,7 +50,6 @@ from ul_api_utils.utils.load_modules import load_modules_by_template
50
50
  from ul_api_utils.utils.uuid_converter import UUID4Converter
51
51
 
52
52
  if TYPE_CHECKING:
53
- import flask_socketio
54
53
  import flask_sqlalchemy
55
54
  from flask_mongoengine import MongoEngine # type: ignore # lib without mypy stubs
56
55
  from ul_db_utils.modules.postgres_modules.db import DbConfig
@@ -106,7 +105,6 @@ class ApiSdk:
106
105
  '_templates_dir',
107
106
  '_fn_registry',
108
107
  '_flask_app_cache',
109
- '_sio',
110
108
  '_limiter_enabled',
111
109
  '_cache',
112
110
  '_db',
@@ -122,7 +120,7 @@ class ApiSdk:
122
120
  self._flask_app_cache: Optional[Flask] = None
123
121
  self._limiter_enabled = False
124
122
  self._cache = None
125
- self._sio: Optional['flask_socketio.SocketIO'] = None
123
+
126
124
  self._templates_dir = os.path.join(APPLICATION_DIR, 'templates')
127
125
 
128
126
  self._fn_registry: List[ApiSdkResource] = []
@@ -132,14 +130,7 @@ class ApiSdk:
132
130
  def config(self) -> ApiSdkConfig:
133
131
  return self._config
134
132
 
135
- @property
136
- def socket(self) -> 'flask_socketio.SocketIO':
137
- assert self._sio is not None, "SocketIO is not configured, try adding SocketIOConfig to your ApiSdk first."
138
- return self._sio
139
-
140
133
  def init_with_flask(self, app_name: str, *, db_config: Optional['MongoDbConfig'] | Optional['DbConfig'] = None) -> Flask:
141
- if self.config.sockets_config:
142
- self._sio = SocketIO()
143
134
  self._initialized_flask_name = try_init(self, app_name)
144
135
 
145
136
  if db_config is not None and type(db_config).__name__ == 'MongoDbConfig':
@@ -208,6 +199,15 @@ class ApiSdk:
208
199
 
209
200
  load_swagger(self, self._fn_registry, self._config.api_route_path_prefix)
210
201
 
202
+ if self.config.sockets_config:
203
+ socket_io.init_app(
204
+ self._flask_app,
205
+ message_queue=self.config.sockets_config.message_queue,
206
+ async_mode=SocketAsyncModesEnum.GEVENT.value,
207
+ channel=self.config.sockets_config.channel,
208
+ cors_allowed_origins=self.config.sockets_config.cors_allowed_origins,
209
+ )
210
+
211
211
  return self._flask_app
212
212
 
213
213
  @property
@@ -255,19 +255,6 @@ class ApiSdk:
255
255
  debugger_enabled=self._debugger_enabled_with_pin,
256
256
  ))
257
257
 
258
- if self.config.sockets_config:
259
- assert self._sio # mypy
260
- self._sio.init_app(
261
- flask_app,
262
- message_queue=self.config.sockets_config.message_queue,
263
- async_mode=SocketAsyncModesEnum.GEVENT.value,
264
- channel=self.config.sockets_config.channel,
265
- cors_allowed_origins=self.config.sockets_config.cors_allowed_origins,
266
- logger=self.config.sockets_config.logs_enabled,
267
- engineio_logger=self.config.sockets_config.engineio_logs_enabled,
268
- )
269
- load_modules_by_template([os.path.join(APPLICATION_DIR, 'sockets', '*')])
270
-
271
258
  return flask_app
272
259
 
273
260
  @property
@@ -40,8 +40,6 @@ class SocketIOConfig(BaseModel):
40
40
  message_queue: str
41
41
  channel: Optional[str] = "flask-socketio"
42
42
  cors_allowed_origins: Optional[str] = "*"
43
- logs_enabled: Optional[bool] = False
44
- engineio_logs_enabled: Optional[bool] = False
45
43
 
46
44
 
47
45
  class ApiSdkConfig(BaseModel):
@@ -0,0 +1,3 @@
1
+ from flask_socketio import SocketIO # type: ignore
2
+
3
+ socket_io = SocketIO()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ul-api-utils
3
- Version: 7.9.7
3
+ Version: 7.10.1
4
4
  Summary: Python api utils
5
5
  Author: Unic-lab
6
6
  Author-email:
@@ -30,7 +30,6 @@ Requires-Dist: pycryptodome (==3.15.0)
30
30
  Requires-Dist: pyjwt (==2.4.0)
31
31
  Requires-Dist: gunicorn (==20.1.0)
32
32
  Requires-Dist: gevent (==24.2.1)
33
- Requires-Dist: gevent-websocket (==0.10.1)
34
33
  Requires-Dist: pyyaml (==6.0)
35
34
  Requires-Dist: requests (==2.28.1)
36
35
  Requires-Dist: cryptography (==38.0.1)
@@ -1,18 +1,12 @@
1
1
  example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- example/conf.py,sha256=3dAGM78iFiMhrhgFJjn9xkqLjOpOPITRoYYSILaYXAA,1082
3
- example/main.py,sha256=y24aWpD4d3d9HIn8Gx2wE_5-MJeiv--iVe5FeRSWAcg,471
2
+ example/conf.py,sha256=tdt3X2gLdTDf_0oMErMpAqqYjIEhpmG5GrOLHqzJH7Y,1018
3
+ example/main.py,sha256=8jOO1VliFr92UoCLDInzSDPRLluzPuO-MMkzHc5Xd2w,419
4
4
  example/permissions.py,sha256=i8_zOOPdra3oMXZfyTspewRYNdn21PCqOD1ATG69Itk,277
5
5
  example/pure_flask_example.py,sha256=A7cbcjTr28FS1sVNAsQbj1N9EgEFIXDB4aRwOV6_tbU,1329
6
6
  example/rate_limit_load.py,sha256=U2Bgp8UztT4TNKdv9NVioxWfE68aCsC7uKz7xPCy6XM,225
7
7
  example/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  example/routes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- example/routes/api_some.py,sha256=fnFO1uFQx-oypCqPNkUfueABrTsAWLdB5spRnhWSZ58,12790
10
- example/sockets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- example/sockets/on_connect.py,sha256=3sluHnVT_-e2Oa4YsRZUJIc2L4VnMSJu5rgI_mkg8BQ,411
12
- example/sockets/on_disconnect.py,sha256=ev0uJutqmTSsbrBcQIRudBX-6g7uSmZj8Imuf6pVIMU,277
13
- example/sockets/on_json.py,sha256=sTh0Pqu5iITmnRRjc6-rns1VFlEN_xo33_4lUF5VzNk,200
14
- example/sockets/on_message.py,sha256=Lj0kxEQc3UiQ9Y5_G8_fA48i5eJneEk8Bb2CYNASGc0,168
15
- example/sockets/on_open.py,sha256=1xfgVqoTuOPxHyBC_rFuj60096gjgbg6rNfCvUqUN8A,405
9
+ example/routes/api_some.py,sha256=Gkyb3r1AT7Fg8cAhJk7StTS5aGb-vylvyEuXx7g3Yc4,13316
16
10
  example/workers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
11
  example/workers/worker.py,sha256=flMYq50OhLtNSaA2qyDJSMeXSNXIqhdBIsaxcmO5-xQ,681
18
12
  ul_api_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -39,8 +33,8 @@ ul_api_utils/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
39
33
  ul_api_utils/commands/cmd_enc_keys.py,sha256=-Tblh6lI7G6M5YVwbVQqZmXhBMiIpB3a7b0Lv1MFufk,8453
40
34
  ul_api_utils/commands/cmd_gen_api_user_token.py,sha256=Vg7oEYHvof7DSLat9yJ_k5AYL9ZOC4Jvd38DBn5U-R0,2730
41
35
  ul_api_utils/commands/cmd_gen_new_api_user.py,sha256=ICZbKqz2D6DRvjwtNM08rNjIlWN3qClcUQw5L8FxRBY,4549
42
- ul_api_utils/commands/cmd_generate_api_docs.py,sha256=pafX88RjcSMFruSk6qy8Xfao3MXo1yolI4rkC_bmMSQ,9612
43
- ul_api_utils/commands/cmd_start.py,sha256=aMz6o6HBzLxWrNpV2_rayQ7Nb-VX3-DHgzOUk39R7NA,3623
36
+ ul_api_utils/commands/cmd_generate_api_docs.py,sha256=cEBUOkn8iQIir78yXzHK_gy_hna2cP58x8N5DzCeZEA,9617
37
+ ul_api_utils/commands/cmd_start.py,sha256=QBi7m2GJr2jIvW235OHAIk5VeRsk_ThaJm4UgZZW5y0,3990
44
38
  ul_api_utils/commands/cmd_worker_start.py,sha256=1tt4_mL8T8_q7i1bqnfjPSkSYlRNNNp8eJ-5rTYj36w,2593
45
39
  ul_api_utils/commands/start/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
40
  ul_api_utils/commands/start/gunicorn.conf.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -63,8 +57,8 @@ ul_api_utils/internal_api/__tests__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQ
63
57
  ul_api_utils/internal_api/__tests__/internal_api.py,sha256=X2iopeso6vryszeeA__lcqXQVtz3Nwt3ngH7M4OuN1U,1116
64
58
  ul_api_utils/internal_api/__tests__/internal_api_content_type.py,sha256=mfiYPkzKtfZKFpi4RSnWAoCd6mRijr6sFsa2TF-s5t8,749
65
59
  ul_api_utils/modules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
- ul_api_utils/modules/api_sdk.py,sha256=XINEiQ_-f4E1xtlBSQz06plaKnOPCHEYBAW81_EROiM,26306
67
- ul_api_utils/modules/api_sdk_config.py,sha256=_yxctcD5TyTblMuLjBDna61ANGH49C8v-lnUUH939pE,2480
60
+ ul_api_utils/modules/api_sdk.py,sha256=lUT5D2V1upKeNik1pZDk6VGn0oab0aMauCm3XRDo_KQ,25660
61
+ ul_api_utils/modules/api_sdk_config.py,sha256=fKTHvpE7Cl7L-uL_FLnpPhf7aoE4NFeFv9Pn1w4z9JY,2389
68
62
  ul_api_utils/modules/api_sdk_jwt.py,sha256=2XRfb0LxHUnldSL67S60v1uyoDpVPNaq4zofUtkeg88,15112
69
63
  ul_api_utils/modules/intermediate_state.py,sha256=7ZZ3Sypbb8LaSfrVhaXaWRDnj8oyy26NUbmFK7vr-y4,1270
70
64
  ul_api_utils/modules/worker_context.py,sha256=jGjopeuYuTtIDmsrqK7TcbTD-E81t8OWvWS1JpTC6b0,802
@@ -78,6 +72,7 @@ ul_api_utils/resources/debugger_scripts.py,sha256=qqf6zeJ1aE_STNYX1j9-ZfBgB_jcQo
78
72
  ul_api_utils/resources/not_implemented.py,sha256=OQE5LGA4KqZDwP5Wtub3Aw-icwzbqCSKcEFoFp4w7_k,973
79
73
  ul_api_utils/resources/permissions.py,sha256=8c8cEPkm69zxgXbDiwUkW6Mi_496-MZXbPOxHITetKs,1436
80
74
  ul_api_utils/resources/rate_limitter.py,sha256=QMpaLu1LoFbvtIEhxF8HbAZIAW4oGKTClt7XEUsqx98,3358
75
+ ul_api_utils/resources/sockets.py,sha256=gftL6QR762GetSrm8Yjh9kfoMAwH64ijqOBexXBz0BY,76
81
76
  ul_api_utils/resources/swagger.py,sha256=fK8S9X4YCSqe_weCzV_BcMPoL_NR073BsGUzn2ImbHI,5391
82
77
  ul_api_utils/resources/health_check/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
78
  ul_api_utils/resources/health_check/const.py,sha256=QzVZP_ZKmVKleUACiOGjzP-v54FD7tEN6NEF4Jb50Ow,78
@@ -140,9 +135,9 @@ ul_api_utils/validators/validate_empty_object.py,sha256=3Ck_iwyJE_M5e7l6s1i88aqb
140
135
  ul_api_utils/validators/validate_uuid.py,sha256=EfvlRirv2EW0Z6w3s8E8rUa9GaI8qXZkBWhnPs8NFrA,257
141
136
  ul_api_utils/validators/__tests__/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
142
137
  ul_api_utils/validators/__tests__/test_custom_fields.py,sha256=QLZ7DFta01Z7DOK9Z5Iq4uf_CmvDkVReis-GAl_QN48,1447
143
- ul_api_utils-7.9.7.dist-info/LICENSE,sha256=6Qo8OdcqI8aGrswJKJYhST-bYqxVQBQ3ujKdTSdq-80,1062
144
- ul_api_utils-7.9.7.dist-info/METADATA,sha256=qW0ACllEuAvHkWWLmFMfJS9xCI0APS3907zZhNAkEoA,14714
145
- ul_api_utils-7.9.7.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
146
- ul_api_utils-7.9.7.dist-info/entry_points.txt,sha256=8tL3ySHWTyJMuV1hx1fHfN8zumDVOCOm63w3StphkXg,53
147
- ul_api_utils-7.9.7.dist-info/top_level.txt,sha256=1XsW8iOSFaH4LOzDcnNyxHpHrbKU3fSn-aIAxe04jmw,21
148
- ul_api_utils-7.9.7.dist-info/RECORD,,
138
+ ul_api_utils-7.10.1.dist-info/LICENSE,sha256=6Qo8OdcqI8aGrswJKJYhST-bYqxVQBQ3ujKdTSdq-80,1062
139
+ ul_api_utils-7.10.1.dist-info/METADATA,sha256=9PDBYfLu331BdOI4UKOIGWPCe8pfsz40N-NReWWuDkA,14672
140
+ ul_api_utils-7.10.1.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
141
+ ul_api_utils-7.10.1.dist-info/entry_points.txt,sha256=8tL3ySHWTyJMuV1hx1fHfN8zumDVOCOm63w3StphkXg,53
142
+ ul_api_utils-7.10.1.dist-info/top_level.txt,sha256=1XsW8iOSFaH4LOzDcnNyxHpHrbKU3fSn-aIAxe04jmw,21
143
+ ul_api_utils-7.10.1.dist-info/RECORD,,
File without changes
@@ -1,16 +0,0 @@
1
- import logging
2
-
3
- from flask import request
4
- from flask_socketio import emit # type: ignore
5
-
6
- from example.conf import sdk
7
-
8
-
9
- logger = logging.getLogger(__name__)
10
-
11
-
12
- @sdk.socket.on('connect')
13
- def handle_connect() -> None:
14
- logger.info("Connected!")
15
- logger.info("SESSION INFO: " + str(request.sid)) # type: ignore
16
- emit('message', {"data1": 1, "data": 2}, room=request.sid, broadcast=True) # type: ignore
@@ -1,14 +0,0 @@
1
- import logging
2
-
3
- from flask import request
4
-
5
- from example.conf import sdk
6
-
7
-
8
- logger = logging.getLogger(__name__)
9
-
10
-
11
- @sdk.socket.on('disconnect')
12
- def disconnect() -> None:
13
- logger.info("Client disconnected!")
14
- logger.info("SESSION INFO: " + str(request.sid)) # type: ignore
@@ -1,10 +0,0 @@
1
- from typing import Any
2
-
3
- from flask_socketio import send # type: ignore
4
-
5
- from example.conf import sdk
6
-
7
-
8
- @sdk.socket.on('json')
9
- def handle_json(json: dict[str, Any]) -> None:
10
- send(json, json=True)
@@ -1,9 +0,0 @@
1
- from typing import Any
2
-
3
- from example.conf import sdk
4
-
5
-
6
- @sdk.socket.on('message')
7
- def handle_message(data: Any) -> None:
8
- print('received message: ')
9
- print(data)
@@ -1,16 +0,0 @@
1
- import logging
2
-
3
- from flask import request
4
- from flask_socketio import emit # type: ignore
5
-
6
- from example.conf import sdk
7
-
8
-
9
- logger = logging.getLogger(__name__)
10
-
11
-
12
- @sdk.socket.on('open')
13
- def handle_open() -> None:
14
- logger.info("Connected!")
15
- logger.info("SESSION INFO: " + str(request.sid)) # type: ignore
16
- emit('message', {"data1": 1, "data": 2}, room=request.sid, broadcast=True) # type: ignore