ul-api-utils 9.3.0__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.
- example/__init__.py +0 -0
- example/conf.py +35 -0
- example/main.py +24 -0
- example/models/__init__.py +0 -0
- example/permissions.py +6 -0
- example/pure_flask_example.py +65 -0
- example/rate_limit_load.py +10 -0
- example/redis_repository.py +22 -0
- example/routes/__init__.py +0 -0
- example/routes/api_some.py +335 -0
- example/sockets/__init__.py +0 -0
- example/sockets/on_connect.py +16 -0
- example/sockets/on_disconnect.py +14 -0
- example/sockets/on_json.py +10 -0
- example/sockets/on_message.py +13 -0
- example/sockets/on_open.py +16 -0
- example/workers/__init__.py +0 -0
- example/workers/worker.py +28 -0
- ul_api_utils/__init__.py +0 -0
- ul_api_utils/access/__init__.py +122 -0
- ul_api_utils/api_resource/__init__.py +0 -0
- ul_api_utils/api_resource/api_request.py +105 -0
- ul_api_utils/api_resource/api_resource.py +414 -0
- ul_api_utils/api_resource/api_resource_config.py +20 -0
- ul_api_utils/api_resource/api_resource_error_handling.py +21 -0
- ul_api_utils/api_resource/api_resource_fn_typing.py +356 -0
- ul_api_utils/api_resource/api_resource_type.py +16 -0
- ul_api_utils/api_resource/api_response.py +300 -0
- ul_api_utils/api_resource/api_response_db.py +26 -0
- ul_api_utils/api_resource/api_response_payload_alias.py +25 -0
- ul_api_utils/api_resource/db_types.py +9 -0
- ul_api_utils/api_resource/signature_check.py +41 -0
- ul_api_utils/commands/__init__.py +0 -0
- ul_api_utils/commands/cmd_enc_keys.py +172 -0
- ul_api_utils/commands/cmd_gen_api_user_token.py +77 -0
- ul_api_utils/commands/cmd_gen_new_api_user.py +106 -0
- ul_api_utils/commands/cmd_generate_api_docs.py +181 -0
- ul_api_utils/commands/cmd_start.py +110 -0
- ul_api_utils/commands/cmd_worker_start.py +76 -0
- ul_api_utils/commands/start/__init__.py +0 -0
- ul_api_utils/commands/start/gunicorn.conf.local.py +0 -0
- ul_api_utils/commands/start/gunicorn.conf.py +26 -0
- ul_api_utils/commands/start/wsgi.py +22 -0
- ul_api_utils/conf/ul-debugger-main.js +1 -0
- ul_api_utils/conf/ul-debugger-ui.js +1 -0
- ul_api_utils/conf.py +70 -0
- ul_api_utils/const.py +78 -0
- ul_api_utils/debug/__init__.py +0 -0
- ul_api_utils/debug/debugger.py +119 -0
- ul_api_utils/debug/malloc.py +93 -0
- ul_api_utils/debug/stat.py +444 -0
- ul_api_utils/encrypt/__init__.py +0 -0
- ul_api_utils/encrypt/encrypt_decrypt_abstract.py +15 -0
- ul_api_utils/encrypt/encrypt_decrypt_aes_xtea.py +59 -0
- ul_api_utils/errors.py +200 -0
- ul_api_utils/internal_api/__init__.py +0 -0
- ul_api_utils/internal_api/__tests__/__init__.py +0 -0
- ul_api_utils/internal_api/__tests__/internal_api.py +29 -0
- ul_api_utils/internal_api/__tests__/internal_api_content_type.py +22 -0
- ul_api_utils/internal_api/internal_api.py +369 -0
- ul_api_utils/internal_api/internal_api_check_context.py +42 -0
- ul_api_utils/internal_api/internal_api_error.py +17 -0
- ul_api_utils/internal_api/internal_api_response.py +296 -0
- ul_api_utils/main.py +29 -0
- ul_api_utils/modules/__init__.py +0 -0
- ul_api_utils/modules/__tests__/__init__.py +0 -0
- ul_api_utils/modules/__tests__/test_api_sdk_jwt.py +195 -0
- ul_api_utils/modules/api_sdk.py +555 -0
- ul_api_utils/modules/api_sdk_config.py +63 -0
- ul_api_utils/modules/api_sdk_jwt.py +377 -0
- ul_api_utils/modules/intermediate_state.py +34 -0
- ul_api_utils/modules/worker_context.py +35 -0
- ul_api_utils/modules/worker_sdk.py +109 -0
- ul_api_utils/modules/worker_sdk_config.py +13 -0
- ul_api_utils/py.typed +0 -0
- ul_api_utils/resources/__init__.py +0 -0
- ul_api_utils/resources/caching.py +196 -0
- ul_api_utils/resources/debugger_scripts.py +97 -0
- ul_api_utils/resources/health_check/__init__.py +0 -0
- ul_api_utils/resources/health_check/const.py +2 -0
- ul_api_utils/resources/health_check/health_check.py +439 -0
- ul_api_utils/resources/health_check/health_check_template.py +64 -0
- ul_api_utils/resources/health_check/resource.py +97 -0
- ul_api_utils/resources/not_implemented.py +25 -0
- ul_api_utils/resources/permissions.py +29 -0
- ul_api_utils/resources/rate_limitter.py +84 -0
- ul_api_utils/resources/socketio.py +55 -0
- ul_api_utils/resources/swagger.py +119 -0
- ul_api_utils/resources/web_forms/__init__.py +0 -0
- ul_api_utils/resources/web_forms/custom_fields/__init__.py +0 -0
- ul_api_utils/resources/web_forms/custom_fields/custom_checkbox_select.py +5 -0
- ul_api_utils/resources/web_forms/custom_widgets/__init__.py +0 -0
- ul_api_utils/resources/web_forms/custom_widgets/custom_select_widget.py +86 -0
- ul_api_utils/resources/web_forms/custom_widgets/custom_text_input_widget.py +42 -0
- ul_api_utils/resources/web_forms/uni_form.py +75 -0
- ul_api_utils/sentry.py +52 -0
- ul_api_utils/utils/__init__.py +0 -0
- ul_api_utils/utils/__tests__/__init__.py +0 -0
- ul_api_utils/utils/__tests__/api_path_version.py +16 -0
- ul_api_utils/utils/__tests__/unwrap_typing.py +67 -0
- ul_api_utils/utils/api_encoding.py +51 -0
- ul_api_utils/utils/api_format.py +61 -0
- ul_api_utils/utils/api_method.py +55 -0
- ul_api_utils/utils/api_pagination.py +58 -0
- ul_api_utils/utils/api_path_version.py +60 -0
- ul_api_utils/utils/api_request_info.py +6 -0
- ul_api_utils/utils/avro.py +131 -0
- ul_api_utils/utils/broker_topics_message_count.py +47 -0
- ul_api_utils/utils/cached_per_request.py +23 -0
- ul_api_utils/utils/colors.py +31 -0
- ul_api_utils/utils/constants.py +7 -0
- ul_api_utils/utils/decode_base64.py +9 -0
- ul_api_utils/utils/deprecated.py +19 -0
- ul_api_utils/utils/flags.py +29 -0
- ul_api_utils/utils/flask_swagger_generator/__init__.py +0 -0
- ul_api_utils/utils/flask_swagger_generator/conf.py +4 -0
- ul_api_utils/utils/flask_swagger_generator/exceptions.py +7 -0
- ul_api_utils/utils/flask_swagger_generator/specifiers/__init__.py +0 -0
- ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_models.py +57 -0
- ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_specifier.py +48 -0
- ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_three_specifier.py +777 -0
- ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_version.py +40 -0
- ul_api_utils/utils/flask_swagger_generator/utils/__init__.py +0 -0
- ul_api_utils/utils/flask_swagger_generator/utils/input_type.py +77 -0
- ul_api_utils/utils/flask_swagger_generator/utils/parameter_type.py +51 -0
- ul_api_utils/utils/flask_swagger_generator/utils/replace_in_dict.py +18 -0
- ul_api_utils/utils/flask_swagger_generator/utils/request_type.py +52 -0
- ul_api_utils/utils/flask_swagger_generator/utils/schema_type.py +15 -0
- ul_api_utils/utils/flask_swagger_generator/utils/security_type.py +39 -0
- ul_api_utils/utils/imports.py +16 -0
- ul_api_utils/utils/instance_checks.py +16 -0
- ul_api_utils/utils/jinja/__init__.py +0 -0
- ul_api_utils/utils/jinja/t_url_for.py +19 -0
- ul_api_utils/utils/jinja/to_pretty_json.py +11 -0
- ul_api_utils/utils/json_encoder.py +126 -0
- ul_api_utils/utils/load_modules.py +15 -0
- ul_api_utils/utils/memory_db/__init__.py +0 -0
- ul_api_utils/utils/memory_db/__tests__/__init__.py +0 -0
- ul_api_utils/utils/memory_db/errors.py +8 -0
- ul_api_utils/utils/memory_db/repository.py +102 -0
- ul_api_utils/utils/token_check.py +14 -0
- ul_api_utils/utils/token_check_through_request.py +16 -0
- ul_api_utils/utils/unwrap_typing.py +117 -0
- ul_api_utils/utils/uuid_converter.py +22 -0
- ul_api_utils/validators/__init__.py +0 -0
- ul_api_utils/validators/__tests__/__init__.py +0 -0
- ul_api_utils/validators/__tests__/test_custom_fields.py +32 -0
- ul_api_utils/validators/custom_fields.py +66 -0
- ul_api_utils/validators/validate_empty_object.py +10 -0
- ul_api_utils/validators/validate_uuid.py +11 -0
- ul_api_utils-9.3.0.dist-info/LICENSE +21 -0
- ul_api_utils-9.3.0.dist-info/METADATA +279 -0
- ul_api_utils-9.3.0.dist-info/RECORD +156 -0
- ul_api_utils-9.3.0.dist-info/WHEEL +5 -0
- ul_api_utils-9.3.0.dist-info/entry_points.txt +2 -0
- ul_api_utils-9.3.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import gc
|
|
3
|
+
import os
|
|
4
|
+
import re
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from ul_py_tool.commands.cmd import Cmd
|
|
11
|
+
from ul_py_tool.utils.arg_str2bool import arg_str2bool
|
|
12
|
+
from ul_py_tool.utils.write_stdout import write_stdout
|
|
13
|
+
|
|
14
|
+
from ul_api_utils.conf import APPLICATION_GUNICORN_WORKERS
|
|
15
|
+
from ul_api_utils.const import THIS_LIB_CWD
|
|
16
|
+
|
|
17
|
+
ENV_LOCAL = 'local'
|
|
18
|
+
PY_FILE_SUF = '.py'
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CmdStart(Cmd):
|
|
22
|
+
app_dir: str
|
|
23
|
+
app_file_name: str = 'main.py'
|
|
24
|
+
app_name: str = 'flask_app'
|
|
25
|
+
env: str
|
|
26
|
+
port: int
|
|
27
|
+
debug: bool
|
|
28
|
+
max_requests: int
|
|
29
|
+
max_requests_jitter: int
|
|
30
|
+
worker_class: str
|
|
31
|
+
freeze_gc: bool
|
|
32
|
+
statsd_endpoint: Optional[str] = None
|
|
33
|
+
statsd_prefix: Optional[str] = None
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def app_rel_dir(self) -> str:
|
|
37
|
+
return os.path.relpath(self.app_dir, os.getcwd())
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def app_file_path(self) -> str:
|
|
41
|
+
return os.path.join(self.app_dir, self.app_file_name)
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def app_module(self) -> str:
|
|
45
|
+
file_rel_path = os.path.relpath(self.app_file_path, os.getcwd())
|
|
46
|
+
if file_rel_path.endswith(PY_FILE_SUF):
|
|
47
|
+
file_rel_path = file_rel_path[:-len(PY_FILE_SUF)]
|
|
48
|
+
return re.sub(r'/+', '.', file_rel_path.replace('\\', '/')).strip('.')
|
|
49
|
+
|
|
50
|
+
@staticmethod
|
|
51
|
+
def add_parser_args(parser: argparse.ArgumentParser) -> None:
|
|
52
|
+
parser.add_argument('--app-dir', dest='app_dir', type=str, required=True, help="dir to import ")
|
|
53
|
+
parser.add_argument('--env', dest='env', type=str, required=True)
|
|
54
|
+
parser.add_argument('--port', dest='port', type=int, required=False, default=30000)
|
|
55
|
+
parser.add_argument('--debug', dest='debug', type=arg_str2bool, default=False, required=False)
|
|
56
|
+
parser.add_argument('--max-requests', dest='max_requests', type=int, default=1000, required=False)
|
|
57
|
+
parser.add_argument('--max-requests-jitter', dest='max_requests_jitter', type=int, default=50, required=False)
|
|
58
|
+
parser.add_argument('--worker-class', dest='worker_class', type=str, default='sync', required=False)
|
|
59
|
+
parser.add_argument('--statsd_endpoint', dest='statsd_endpoint', type=str, default=None, required=False)
|
|
60
|
+
parser.add_argument('--statsd_prefix', dest='statsd_prefix', type=str, default=None, required=False)
|
|
61
|
+
parser.add_argument('--freeze-gc', dest='freeze_gc', type=bool, default=False, required=False)
|
|
62
|
+
|
|
63
|
+
def run(self) -> None:
|
|
64
|
+
if self.freeze_gc:
|
|
65
|
+
gc.disable()
|
|
66
|
+
|
|
67
|
+
env = os.environ.copy()
|
|
68
|
+
name = re.sub(r'[^0-9a-z]+', '-', f'gncrn-{os.path.relpath(self.app_dir, os.getcwd()).lower().strip("/").strip()}')
|
|
69
|
+
env['PYTHONUNBUFFERED'] = os.environ.get('PYTHONUNBUFFERED', '0')
|
|
70
|
+
env['PYTHONPATH'] = os.getcwd()
|
|
71
|
+
env['APPLICATION_START_DT'] = datetime.now().isoformat()
|
|
72
|
+
env['APPLICATION_ENV'] = self.env
|
|
73
|
+
env['APPLICATION_DIR'] = self.app_dir
|
|
74
|
+
env['APPLICATION_DEBUG'] = '1' if self.debug else '0'
|
|
75
|
+
env['FLASK_APP'] = self.app_file_path
|
|
76
|
+
|
|
77
|
+
assert len(APPLICATION_GUNICORN_WORKERS) > 0
|
|
78
|
+
|
|
79
|
+
debug = (self.debug and self.env == ENV_LOCAL)
|
|
80
|
+
local_conf = os.path.abspath(os.path.normpath(os.path.join(THIS_LIB_CWD, "commands", "start", "gunicorn.conf.local.py")))
|
|
81
|
+
prod_conf = os.path.abspath(os.path.normpath(os.path.join(THIS_LIB_CWD, "commands", "start", "gunicorn.conf.py")))
|
|
82
|
+
gunicorn_config = prod_conf if self.freeze_gc and not debug else local_conf
|
|
83
|
+
|
|
84
|
+
args = [
|
|
85
|
+
f'-n={name}',
|
|
86
|
+
f'-w={APPLICATION_GUNICORN_WORKERS}',
|
|
87
|
+
f'--worker-class={self.worker_class}',
|
|
88
|
+
f'-b=0.0.0.0:{self.port}',
|
|
89
|
+
f'--config={gunicorn_config}',
|
|
90
|
+
'--log-level=INFO',
|
|
91
|
+
f'--max-requests={self.max_requests}',
|
|
92
|
+
f'--max-requests-jitter={self.max_requests_jitter}',
|
|
93
|
+
'--timeout=60',
|
|
94
|
+
'--access-logfile=-',
|
|
95
|
+
'--error-logfile=-',
|
|
96
|
+
'--disable-redirect-access-to-syslog',
|
|
97
|
+
*(['--reload'] if debug else ['--preload']),
|
|
98
|
+
f'{self.app_module}:{self.app_name}',
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
if self.statsd_endpoint and self.env != ENV_LOCAL:
|
|
102
|
+
if self.statsd_prefix:
|
|
103
|
+
args.extend([f'--statsd-host={self.statsd_endpoint}', f"--statsd-prefix={self.statsd_prefix}"])
|
|
104
|
+
else:
|
|
105
|
+
args.extend([f'--statsd-host={self.statsd_endpoint}', f"--statsd-prefix={self.app_module.split('.')[1]}_{self.env}"])
|
|
106
|
+
|
|
107
|
+
write_stdout(f'name={name}')
|
|
108
|
+
write_stdout(f'args={args}')
|
|
109
|
+
subprocess.run(['gunicorn', '--check-config', '--print-config', *args], cwd=os.getcwd(), stdout=sys.stdout, stderr=sys.stderr, text=True, env=env)
|
|
110
|
+
os.execvpe('gunicorn', args, env)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
import re
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
from ul_py_tool.commands.cmd import Cmd
|
|
9
|
+
from ul_py_tool.utils.arg_str2bool import arg_str2bool
|
|
10
|
+
|
|
11
|
+
from ul_api_utils.const import APPLICATION_ENV__LOCAL, PY_FILE_SUF
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CmdWorkerStart(Cmd):
|
|
15
|
+
app_dir: str
|
|
16
|
+
env: str
|
|
17
|
+
debug: bool
|
|
18
|
+
worker_name: str
|
|
19
|
+
app_file_name: str = 'main.py'
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def app_file_path(self) -> str:
|
|
23
|
+
return os.path.join(self.app_dir, self.app_file_name)
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def app_module(self) -> str:
|
|
27
|
+
file_rel_path = os.path.relpath(self.app_file_path, os.getcwd())
|
|
28
|
+
if file_rel_path.endswith(PY_FILE_SUF):
|
|
29
|
+
file_rel_path = file_rel_path[:-len(PY_FILE_SUF)]
|
|
30
|
+
return re.sub(r'/+', '.', file_rel_path.replace('\\', '/')).strip('.')
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def add_parser_args(parser: argparse.ArgumentParser) -> None:
|
|
34
|
+
parser.add_argument('--app-dir', dest='app_dir', type=str, required=True, help="dir to import main.py")
|
|
35
|
+
parser.add_argument('--env', dest='env', type=str, required=True)
|
|
36
|
+
parser.add_argument('--worker', dest='worker_name', type=str, required=True)
|
|
37
|
+
parser.add_argument('--debug', dest='debug', type=arg_str2bool, default=False, required=False)
|
|
38
|
+
|
|
39
|
+
def run(self) -> None:
|
|
40
|
+
env = os.environ.copy()
|
|
41
|
+
env['PYTHONUNBUFFERED'] = '1'
|
|
42
|
+
env['PYTHONPATH'] = os.getcwd()
|
|
43
|
+
env['APPLICATION_START_DT'] = datetime.now().isoformat()
|
|
44
|
+
env['APPLICATION_ENV'] = self.env
|
|
45
|
+
env['APPLICATION_DIR'] = self.app_dir
|
|
46
|
+
env['APPLICATION_DEBUG'] = '1' if self.debug else '0'
|
|
47
|
+
|
|
48
|
+
if self.debug and self.env == APPLICATION_ENV__LOCAL:
|
|
49
|
+
subprocess.run(
|
|
50
|
+
[
|
|
51
|
+
'watchmedo',
|
|
52
|
+
'auto-restart',
|
|
53
|
+
'--pattern=*.py',
|
|
54
|
+
'--recursive',
|
|
55
|
+
f'--directory={os.getcwd()}',
|
|
56
|
+
f'--command=\'python3 -m {self.app_module} --worker={self.worker_name}\'',
|
|
57
|
+
'.',
|
|
58
|
+
],
|
|
59
|
+
cwd=os.getcwd(),
|
|
60
|
+
stdout=sys.stdout,
|
|
61
|
+
stderr=sys.stderr,
|
|
62
|
+
text=True,
|
|
63
|
+
env=env,
|
|
64
|
+
)
|
|
65
|
+
else:
|
|
66
|
+
subprocess.run(
|
|
67
|
+
[
|
|
68
|
+
'python3', '-m', f'{self.app_module}',
|
|
69
|
+
f'--worker={self.worker_name}',
|
|
70
|
+
],
|
|
71
|
+
cwd=os.getcwd(),
|
|
72
|
+
stdout=sys.stdout,
|
|
73
|
+
stderr=sys.stderr,
|
|
74
|
+
text=True,
|
|
75
|
+
env=env,
|
|
76
|
+
)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import gc
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from ul_py_tool.utils.write_stdout import write_stdout
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def when_ready(server: Any) -> None:
|
|
8
|
+
"""
|
|
9
|
+
Use only with --preload option.
|
|
10
|
+
Called just after the server is started.
|
|
11
|
+
Freeze garbage collector objects after preloading the application.
|
|
12
|
+
https://docs.gunicorn.org/en/20.1.0/settings.html?highlight=preload#when-ready
|
|
13
|
+
"""
|
|
14
|
+
gc.freeze()
|
|
15
|
+
write_stdout("Objects frozen in permanent generation: ", gc.get_freeze_count())
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def post_fork(server: Any, worker: Any) -> None:
|
|
19
|
+
"""
|
|
20
|
+
Works only with --preload.
|
|
21
|
+
Called just after a worker has been forked.
|
|
22
|
+
Enable garbage collection on each worker if it's not enabled for some reason.
|
|
23
|
+
https://docs.gunicorn.org/en/20.1.0/settings.html?highlight=preload#post-fork
|
|
24
|
+
"""
|
|
25
|
+
write_stdout("Enabling GC for worker", worker.pid)
|
|
26
|
+
gc.enable()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from typing import Dict, Any, Optional
|
|
2
|
+
|
|
3
|
+
from gunicorn.app.wsgiapp import WSGIApplication # type: ignore
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class UnicLabWSGIApplication(WSGIApplication):
|
|
7
|
+
def __init__(self, app_uri: str, options: Optional[Dict[str, Any]] = None) -> None:
|
|
8
|
+
self.options = options or {}
|
|
9
|
+
self.app_uri = app_uri
|
|
10
|
+
super().__init__()
|
|
11
|
+
|
|
12
|
+
def load_config(self) -> None:
|
|
13
|
+
config = {
|
|
14
|
+
key: value
|
|
15
|
+
for key, value in self.options.items()
|
|
16
|
+
if key in self.cfg.settings and value is not None
|
|
17
|
+
}
|
|
18
|
+
for key, value in config.items():
|
|
19
|
+
self.cfg.set(key.lower(), value)
|
|
20
|
+
|
|
21
|
+
def load(self) -> str:
|
|
22
|
+
return self.app_uri
|