cmdbox 0.5.0.3__py3-none-any.whl → 0.5.0.5__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 cmdbox might be problematic. Click here for more details.

@@ -4,6 +4,7 @@ from pathlib import Path
4
4
  from typing import Dict, Any, Tuple, List, Union
5
5
  import argparse
6
6
  import logging
7
+ import multiprocessing
7
8
 
8
9
 
9
10
  class GuiStart(feature.UnsupportEdgeFeature):
@@ -88,6 +89,12 @@ class GuiStart(feature.UnsupportEdgeFeature):
88
89
  dict(opt="session_timeout", type=Options.T_INT, default="600", required=False, multi=False, hide=True, choice=None,
89
90
  discription_ja="サインインしたユーザーのセッションタイムアウトの時間を秒で指定します。",
90
91
  discription_en="Specify the session timeout in seconds for signed-in users."),
92
+ dict(opt="guvicorn_workers", type=Options.T_INT, default=multiprocessing.cpu_count()*2, required=False, multi=False, hide=True, choice=None,
93
+ discription_ja="guvicornワーカー数を指定します。Linux環境でのみ有効です。-1又は未指定の場合はCPU数の2倍を使用します。",
94
+ discription_en="Specifies the number of guvicorn workers, valid only in Linux environment. If -1 or unspecified, twice the number of CPUs is used."),
95
+ dict(opt="guvicorn_timeout", type=Options.T_INT, default=30, required=False, multi=False, hide=True, choice=None,
96
+ discription_ja="guvicornワーカーのタイムアウトの時間を秒で指定します。",
97
+ discription_en="Specify the timeout duration of the guvicorn worker in seconds."),
91
98
  dict(opt="client_only", type=Options.T_BOOL, default=False, required=False, multi=False, hide=True, choice=[True, False],
92
99
  discription_ja="サーバーへの接続を行わないようにします。",
93
100
  discription_en="Do not make connections to the server."),
@@ -4,6 +4,7 @@ from pathlib import Path
4
4
  from typing import Dict, Any, Tuple, List, Union
5
5
  import argparse
6
6
  import logging
7
+ import multiprocessing
7
8
 
8
9
 
9
10
  class WebStart(feature.UnsupportEdgeFeature):
@@ -88,6 +89,12 @@ class WebStart(feature.UnsupportEdgeFeature):
88
89
  dict(opt="session_timeout", type=Options.T_INT, default="600", required=False, multi=False, hide=True, choice=None,
89
90
  discription_ja="サインインしたユーザーのセッションタイムアウトの時間を秒で指定します。",
90
91
  discription_en="Specify the session timeout in seconds for signed-in users."),
92
+ dict(opt="guvicorn_workers", type=Options.T_INT, default=multiprocessing.cpu_count()*2, required=False, multi=False, hide=True, choice=None,
93
+ discription_ja="guvicornワーカー数を指定します。Linux環境でのみ有効です。-1又は未指定の場合はCPU数の2倍を使用します。",
94
+ discription_en="Specifies the number of guvicorn workers, valid only in Linux environment. If -1 or unspecified, twice the number of CPUs is used."),
95
+ dict(opt="guvicorn_timeout", type=Options.T_INT, default=30, required=False, multi=False, hide=True, choice=None,
96
+ discription_ja="guvicornワーカーのタイムアウトの時間を秒で指定します。",
97
+ discription_en="Specify the timeout duration of the guvicorn worker in seconds."),
91
98
  dict(opt="client_only", type=Options.T_BOOL, default=False, required=False, multi=False, hide=True, choice=[True, False],
92
99
  discription_ja="サーバーへの接続を行わないようにします。",
93
100
  discription_en="Do not make connections to the server."),
@@ -158,7 +165,8 @@ class WebStart(feature.UnsupportEdgeFeature):
158
165
  ssl_cert=ssl_cert, ssl_key=ssl_key, ssl_keypass=args.ssl_keypass, ssl_ca_certs=ssl_ca_certs,
159
166
  session_domain=args.session_domain, session_path=args.session_path,
160
167
  session_secure=args.session_secure, session_timeout=args.session_timeout,
161
- outputs_key=args.outputs_key)
168
+ outputs_key=args.outputs_key, guvicorn_workers=args.guvicorn_workers, guvicorn_timeout=args.guvicorn_timeout)
169
+
162
170
  msg = dict(success="web complate.")
163
171
  common.print_format(msg, args.format, tm, args.output_json, args.output_json_append, pf=pf)
164
172
  return 0, msg, w
cmdbox/app/web.py CHANGED
@@ -12,6 +12,7 @@ import ctypes
12
12
  import datetime
13
13
  import gevent
14
14
  import logging
15
+ import multiprocessing
15
16
  import os
16
17
  import platform
17
18
  import requests
@@ -111,6 +112,7 @@ class Web:
111
112
  self.options = options.Options.getInstance()
112
113
  self.webcap_client = requests.Session()
113
114
  self.load_signin_file()
115
+
114
116
  if self.logger.level == logging.DEBUG:
115
117
  self.logger.debug(f"web init parameter: data={self.data} -> {self.data.absolute() if self.data is not None else None}")
116
118
  self.logger.debug(f"web init parameter: redis_host={self.redis_host}")
@@ -173,6 +175,7 @@ class Web:
173
175
  if self.logger.level == logging.DEBUG:
174
176
  self.logger.debug(f"hashed apikey: {apikey}")
175
177
  find_user = None
178
+ self.load_signin_file() # サインインファイルの更新をチェック
176
179
  for user in self.signin_file_data['users']:
177
180
  if 'apikeys' not in user:
178
181
  continue
@@ -223,6 +226,7 @@ class Web:
223
226
  if self.signin_file_data is None:
224
227
  raise ValueError(f'signin_file_data is None. ({self.signin_file})')
225
228
  if 'signin' in req.session:
229
+ self.load_signin_file() # サインインファイルの更新をチェック
226
230
  path_jadge = self.check_path(req, req.url.path)
227
231
  if path_jadge is not None:
228
232
  return path_jadge
@@ -399,6 +403,12 @@ class Web:
399
403
  if self.signin_file is not None:
400
404
  if not self.signin_file.is_file():
401
405
  raise HTTPException(status_code=500, detail=f'signin_file is not found. ({self.signin_file})')
406
+ # サインインファイル読込み済みなら返すが、別プロセスがサインインファイルを更新していたら読込みを実施する。
407
+ if not hasattr(self, 'signin_file_last'):
408
+ self.signin_file_last = self.signin_file.stat().st_mtime
409
+ if self.signin_file_last >= self.signin_file.stat().st_mtime and self.signin_file_data is not None:
410
+ return
411
+ self.signin_file_last = self.signin_file.stat().st_mtime
402
412
  yml = common.load_yml(self.signin_file)
403
413
  # usersのフォーマットチェック
404
414
  if 'users' not in yml:
@@ -1100,7 +1110,7 @@ class Web:
1100
1110
  user_data = req.session['user_data']
1101
1111
  else:
1102
1112
  # セッションにユーザーデータがない場合はファイルから読み込む
1103
- if user_path.exists():
1113
+ if user_path.is_file():
1104
1114
  user_data = common.loaduser(user_path)
1105
1115
  else:
1106
1116
  user_data = dict()
@@ -1127,7 +1137,8 @@ class Web:
1127
1137
 
1128
1138
  def start(self, allow_host:str="0.0.0.0", listen_port:int=8081, ssl_listen_port:int=8443,
1129
1139
  ssl_cert:Path=None, ssl_key:Path=None, ssl_keypass:str=None, ssl_ca_certs:Path=None,
1130
- session_domain:str=None, session_path:str='/', session_secure:bool=False, session_timeout:int=600, outputs_key:List[str]=[]):
1140
+ session_domain:str=None, session_path:str='/', session_secure:bool=False, session_timeout:int=600, outputs_key:List[str]=[],
1141
+ guvicorn_workers:int=-1, guvicorn_timeout:int=30):
1131
1142
  """
1132
1143
  Webサーバを起動する
1133
1144
 
@@ -1144,6 +1155,8 @@ class Web:
1144
1155
  session_secure (bool, optional): セッションセキュア. Defaults to False.
1145
1156
  session_timeout (int, optional): セッションタイムアウト. Defaults to 600.
1146
1157
  outputs_key (list, optional): 出力キー. Defaults to [].
1158
+ guvicorn_workers (int, optional): Gunicornワーカー数. Defaults to -1.
1159
+ guvicorn_timeout (int, optional): Gunicornタイムアウト. Defaults to 30.
1147
1160
  """
1148
1161
  self.allow_host = allow_host
1149
1162
  self.listen_port = listen_port
@@ -1157,6 +1170,8 @@ class Web:
1157
1170
  self.session_path = session_path
1158
1171
  self.session_secure = session_secure
1159
1172
  self.session_timeout = session_timeout
1173
+ self.guvicorn_workers = guvicorn_workers
1174
+ self.guvicorn_timeout = guvicorn_timeout
1160
1175
  if self.logger.level == logging.DEBUG:
1161
1176
  self.logger.debug(f"web start parameter: allow_host={self.allow_host}")
1162
1177
  self.logger.debug(f"web start parameter: listen_port={self.listen_port}")
@@ -1170,6 +1185,8 @@ class Web:
1170
1185
  self.logger.debug(f"web start parameter: session_path={self.session_path}")
1171
1186
  self.logger.debug(f"web start parameter: session_secure={self.session_secure}")
1172
1187
  self.logger.debug(f"web start parameter: session_timeout={self.session_timeout}")
1188
+ self.logger.debug(f"web start parameter: guvicorn_worker={self.guvicorn_workers}")
1189
+ self.logger.debug(f"web start parameter: guvicorn_timeout={self.guvicorn_timeout}")
1173
1190
 
1174
1191
  app = FastAPI()
1175
1192
  mwparam = dict(path=self.session_path, max_age=self.session_timeout, secret_key=common.random_string())
@@ -1182,7 +1199,8 @@ class Web:
1182
1199
 
1183
1200
  self.is_running = True
1184
1201
  #uvicorn.run(app, host=self.allow_host, port=self.listen_port, workers=2)
1185
- th = ThreadedUvicorn(self.logger, config=Config(app=app, host=self.allow_host, port=self.listen_port))
1202
+ th = ThreadedUvicorn(self.logger, config=Config(app=app, host=self.allow_host, port=self.listen_port),
1203
+ guvicorn_config=dict(workers=self.guvicorn_workers, timeout=self.guvicorn_timeout))
1186
1204
  th.start()
1187
1205
  browser_port = self.listen_port
1188
1206
  th_ssl = None
@@ -1190,7 +1208,8 @@ class Web:
1190
1208
  th_ssl = ThreadedUvicorn(self.logger,
1191
1209
  config=Config(app=app, host=self.allow_host, port=self.ssl_listen_port,
1192
1210
  ssl_certfile=self.ssl_cert, ssl_keyfile=self.ssl_key,
1193
- ssl_keyfile_password=self.ssl_keypass, ssl_ca_certs=self.ssl_ca_certs))
1211
+ ssl_keyfile_password=self.ssl_keypass, ssl_ca_certs=self.ssl_ca_certs),
1212
+ guvicorn_config=dict(workers=self.guvicorn_workers, timeout=self.guvicorn_timeout))
1194
1213
  th_ssl.start()
1195
1214
  browser_port = self.ssl_listen_port
1196
1215
  try:
@@ -1227,34 +1246,45 @@ class Web:
1227
1246
  self.logger.info(f"Exit web.")
1228
1247
 
1229
1248
  class ThreadedUvicorn:
1230
- def __init__(self, logger:logging.Logger, config:Config):
1249
+ def __init__(self, logger:logging.Logger, config:Config, guvicorn_config:Dict[str, Any]):
1231
1250
  self.logger = logger
1251
+ self.guvicorn_config = guvicorn_config
1232
1252
  if platform.system() == "Windows":
1233
1253
  self.server = uvicorn.Server(config)
1234
1254
  self.thread = RaiseThread(daemon=True, target=self.server.run)
1235
1255
  else:
1236
1256
  from gunicorn.app.wsgiapp import WSGIApplication
1237
- import multiprocessing
1238
1257
  class App(WSGIApplication):
1239
- def __init__(self, app, options=None):
1240
- self.options = options or {}
1258
+ def __init__(self, app, options):
1259
+ self.options = options
1241
1260
  self.application = app
1242
1261
  self.started = True
1243
1262
  super().__init__()
1244
1263
  def load_config(self):
1245
- config = {key: value for key, value in self.options.items() if key in self.cfg.settings and value is not None}
1264
+ config = {k: v for k, v in self.options.items() if k in self.cfg.settings and v is not None}
1246
1265
  for key, value in config.items():
1247
1266
  self.cfg.set(key.lower(), value)
1248
1267
  def load(self):
1249
1268
  return self.application
1250
1269
  opt = dict(bind=f"{config.host}:{config.port}",
1251
- workers=multiprocessing.cpu_count()*2,
1252
1270
  worker_class="uvicorn.workers.UvicornWorker",
1253
1271
  access_log_format='[%(t)s] %(p)s %(l)s %(h)s "%(r)s" %(s)s',
1254
1272
  loglevel=logging.getLevelName(self.logger.level),
1255
1273
  keyfile=config.ssl_keyfile, certfile=config.ssl_certfile,
1256
1274
  ca_certs=config.ssl_ca_certs, keyfile_password=config.ssl_keyfile_password,
1257
1275
  limit_request_line=8190, limit_request_fields=100, limit_request_field_size=8190)
1276
+
1277
+ self.guvicorn_config = self.guvicorn_config or {}
1278
+ if 'workers' not in self.guvicorn_config:
1279
+ self.guvicorn_config['workers'] = None
1280
+ if self.guvicorn_config['workers'] is None or self.guvicorn_config['workers'] <= 0:
1281
+ self.guvicorn_config['workers'] = multiprocessing.cpu_count()*2
1282
+ if 'timeout' not in self.guvicorn_config:
1283
+ self.guvicorn_config['timeout'] = None
1284
+ if self.guvicorn_config['timeout'] is None or self.guvicorn_config['timeout'] <= 0:
1285
+ self.guvicorn_config['timeout'] = 30
1286
+
1287
+ opt = {**opt, **self.guvicorn_config}
1258
1288
  self.server = App(config.app, opt)
1259
1289
  #self.thread = RaiseThread(daemon=True, target=self.server.run)
1260
1290
 
cmdbox/version.py CHANGED
@@ -1,9 +1,9 @@
1
1
  import datetime
2
2
 
3
- dt_now = datetime.datetime(2025, 3, 9)
3
+ dt_now = datetime.datetime(2025, 3, 11)
4
4
  __appid__ = 'cmdbox'
5
5
  __title__ = 'cmdbox (Command Development Application)'
6
- __version__ = '0.5.0.3'
6
+ __version__ = '0.5.0.5'
7
7
  __copyright__ = f'Copyright © 2023-{dt_now.strftime("%Y")} hamacom2004jp'
8
8
  __pypiurl__ = 'https://pypi.org/project/cmdbox/'
9
9
  __srcurl__ = 'https://github.com/hamacom2004jp/cmdbox'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cmdbox
3
- Version: 0.5.0.3
3
+ Version: 0.5.0.5
4
4
  Summary: cmdbox: It is a command line application with a plugin mechanism.
5
5
  Home-page: https://github.com/hamacom2004jp/cmdbox
6
6
  Author: hamacom2004jp
@@ -6,7 +6,7 @@ cmdbox/logconf_edge.yml,sha256=RkbUebCJV2z5dLnpgvf4GSFkd-Blzu4vUUDP9CQFwdM,686
6
6
  cmdbox/logconf_gui.yml,sha256=T3yhWoiyp0DW06RjiFG6kS7jScqXYs-KLfC5EYKUImk,686
7
7
  cmdbox/logconf_server.yml,sha256=tpDpKQXgTWzUnHKGU-Vvsha7n1hyIyFdLnSeCgnOgyk,701
8
8
  cmdbox/logconf_web.yml,sha256=lzr3ytjqRbQutbhEOJdHJT0hrrR_h9sPkaEQkzX02lo,686
9
- cmdbox/version.py,sha256=g5ymF-XgyWuNimAa3bcjc0gNXwoq9A9XTMYGO2sQBIM,1962
9
+ cmdbox/version.py,sha256=4Ccsz3DWtdZ7xn-dg9rNj01Ndhejo7WvGZEb_085XOQ,1963
10
10
  cmdbox/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  cmdbox/app/app.py,sha256=2rQpTbezCLuy36df89EhKsAfcPjtGepGHSTQZXimcqA,8949
12
12
  cmdbox/app/client.py,sha256=SNM4xxDh5ARxV6I7cDrmbzGc7sXUUwB3tSyIksdT1RY,19562
@@ -17,7 +17,7 @@ cmdbox/app/filer.py,sha256=L_DSMTvnbN_ffr3JIt0obbOmVoTHEfVm2cAVz3rLH-Q,16059
17
17
  cmdbox/app/options.py,sha256=A4q4dAFAsTaV_k4z7n7hT2Dl6Ei6DrQ7X0xKoVDK6sM,31092
18
18
  cmdbox/app/server.py,sha256=rrH_a6zzrx7glp_SqRrc_2gBgRoT9FX0F1bpGxVLX1I,9467
19
19
  cmdbox/app/signin.py,sha256=Ry4jxUuroOAIAfTYSolMm_izaF27-IS7914I4vhr6yU,2577
20
- cmdbox/app/web.py,sha256=lbFfJdvWWL2dbIIJNQFIyHcJPez9HtVNIYjBc5Lvexw,77902
20
+ cmdbox/app/web.py,sha256=6MXgl_lb4MZO8Lq1kZnGNiIZw6d0eO4c9T-izD_XzZY,80017
21
21
  cmdbox/app/commons/convert.py,sha256=etWeutkPyE8FMz11jw4KJ5uip7qu_CoD1yIqF5iypgg,6754
22
22
  cmdbox/app/commons/loghandler.py,sha256=TQXXg8KdAzHMPwSQGq7RP0qTHxb35PhUTqqDFWkimgk,3306
23
23
  cmdbox/app/commons/module.py,sha256=RsEyqP9Qty4v0qIEE6VuY4pgMlkhJKNTUhsN5p-sSoM,4880
@@ -33,7 +33,7 @@ cmdbox/app/features/cli/cmdbox_client_file_upload.py,sha256=w8vbNttQ_V-R3UWkj0iK
33
33
  cmdbox/app/features/cli/cmdbox_client_server_info.py,sha256=6cCWFoFF0stCw7Mn57LUFgUDCKFPThzAerTMuYnLc-I,9398
34
34
  cmdbox/app/features/cli/cmdbox_edge_config.py,sha256=CzuF4YlngUTOBOr6jDAe-jzQ-psb9OVXMxIEtjKddYQ,7240
35
35
  cmdbox/app/features/cli/cmdbox_edge_start.py,sha256=SWI0SEdergvVBuewyypH5cWrCZSm_M365RRkGxSLqB8,3849
36
- cmdbox/app/features/cli/cmdbox_gui_start.py,sha256=QbKe8Up1aam9dg7QrZfECLTKZXWktgxakO7TecmLVfw,13519
36
+ cmdbox/app/features/cli/cmdbox_gui_start.py,sha256=A15Iol1nRCd_kTMKTyM-uKYEnEd3I_ZYPQRI8fw6Tks,14403
37
37
  cmdbox/app/features/cli/cmdbox_gui_stop.py,sha256=nxaKEM_JSbndcbuHo2CSv7XX4hQwSf3_XC_aasVcs_Y,2730
38
38
  cmdbox/app/features/cli/cmdbox_server_list.py,sha256=xinPmftRK2WkhuZiC6RPh7ldbqEpYErbNaWZSUcT03I,5689
39
39
  cmdbox/app/features/cli/cmdbox_server_start.py,sha256=2u-J5GlkJxX7a6Sh8Gw8OfsbhkzYz4zhDGXEtSd2CwM,7294
@@ -46,7 +46,7 @@ cmdbox/app/features/cli/cmdbox_web_group_add.py,sha256=pxhR1Pg-EngB3vQmPnAUys4EV
46
46
  cmdbox/app/features/cli/cmdbox_web_group_del.py,sha256=nQfS2zgqDGTZaMdiEZvscIqF84k2Kf_9cwt_aqeJmAE,6597
47
47
  cmdbox/app/features/cli/cmdbox_web_group_edit.py,sha256=M-B3txvtLc4sh6URFWVaHaDJCdz3MrLwkmQKSZ2mr-o,7326
48
48
  cmdbox/app/features/cli/cmdbox_web_group_list.py,sha256=sSj237xq5ZrvBSVh2w7ZDHwbzdDR1oJDwmaCfqbp808,6692
49
- cmdbox/app/features/cli/cmdbox_web_start.py,sha256=VtbbesiW9CaUncQg8Z32k4l_Ansn2_tvyozCuZS8t9Q,14307
49
+ cmdbox/app/features/cli/cmdbox_web_start.py,sha256=aa4TncMVK3cbXDOSYhJ_N__WZziXzbYBIW3b6Fn3nr0,15272
50
50
  cmdbox/app/features/cli/cmdbox_web_stop.py,sha256=mGFRgTvm4lbt9D9O-Gnp3p0E44KQ9TyURMYk_ngTWto,3593
51
51
  cmdbox/app/features/cli/cmdbox_web_user_add.py,sha256=agMXexo99lKALEo4nAQA9BDFN1oTmu8_nCjWL6BSGuc,8547
52
52
  cmdbox/app/features/cli/cmdbox_web_user_del.py,sha256=kj1te2Sogdg8huVPbuVa_a6EBR9sJ6kbmw4VgSRHkaY,6560
@@ -248,9 +248,9 @@ cmdbox/web/assets/tree-menu/image/file.png,sha256=Uw4zYkHyuoZ_kSVkesHAeSeA_g9_LP
248
248
  cmdbox/web/assets/tree-menu/image/folder-close.png,sha256=TcgsKTBBF2ejgzekOEDBFBxsJf-Z5u0x9IZVi4GBR-I,284
249
249
  cmdbox/web/assets/tree-menu/image/folder-open.png,sha256=DT7y1GRK4oXJkFvqTN_oSGM5ZYARzPvjoCGL6wqkoo0,301
250
250
  cmdbox/web/assets/tree-menu/js/tree-menu.js,sha256=-GkZxI7xzHuXXHYQBHAVTcuKX4TtoiMuyIms6Xc3pxk,1029
251
- cmdbox-0.5.0.3.dist-info/LICENSE,sha256=sBzzPc5v-5LBuIFi2V4olsnoVg-3EBI0zRX5r19SOxE,1117
252
- cmdbox-0.5.0.3.dist-info/METADATA,sha256=kK2PVfi3XEzyDavUOcYycotjKx85JbrLzPZRpShhI-E,24050
253
- cmdbox-0.5.0.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
254
- cmdbox-0.5.0.3.dist-info/entry_points.txt,sha256=PIoRz-tr503YwdMmd6nxuSn2dDltf4cUMVs98E9WgaA,48
255
- cmdbox-0.5.0.3.dist-info/top_level.txt,sha256=eMEkD5jn8_0PkCAL8h5xJu4qAzF2O8Wf3vegFkKUXR4,7
256
- cmdbox-0.5.0.3.dist-info/RECORD,,
251
+ cmdbox-0.5.0.5.dist-info/LICENSE,sha256=sBzzPc5v-5LBuIFi2V4olsnoVg-3EBI0zRX5r19SOxE,1117
252
+ cmdbox-0.5.0.5.dist-info/METADATA,sha256=8Gn7XHd-1DqfMVnMAaknymWNbYMJ-fqXfWuepfw1O6M,24050
253
+ cmdbox-0.5.0.5.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
254
+ cmdbox-0.5.0.5.dist-info/entry_points.txt,sha256=PIoRz-tr503YwdMmd6nxuSn2dDltf4cUMVs98E9WgaA,48
255
+ cmdbox-0.5.0.5.dist-info/top_level.txt,sha256=eMEkD5jn8_0PkCAL8h5xJu4qAzF2O8Wf3vegFkKUXR4,7
256
+ cmdbox-0.5.0.5.dist-info/RECORD,,