ominfra 0.0.0.dev193__py3-none-any.whl → 0.0.0.dev194__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- ominfra/clouds/aws/journald2aws/main.py +5 -2
- ominfra/manage/deploy/conf/manager.py +2 -2
- ominfra/manage/main.py +3 -3
- ominfra/scripts/journald2aws.py +446 -95
- ominfra/scripts/manage.py +467 -168
- ominfra/scripts/supervisor.py +1055 -659
- ominfra/supervisor/configs.py +4 -3
- ominfra/supervisor/main.py +2 -2
- {ominfra-0.0.0.dev193.dist-info → ominfra-0.0.0.dev194.dist-info}/METADATA +4 -4
- {ominfra-0.0.0.dev193.dist-info → ominfra-0.0.0.dev194.dist-info}/RECORD +14 -15
- ominfra/configs.py +0 -102
- {ominfra-0.0.0.dev193.dist-info → ominfra-0.0.0.dev194.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev193.dist-info → ominfra-0.0.0.dev194.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev193.dist-info → ominfra-0.0.0.dev194.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev193.dist-info → ominfra-0.0.0.dev194.dist-info}/top_level.txt +0 -0
ominfra/scripts/supervisor.py
CHANGED
@@ -36,6 +36,7 @@ import abc
|
|
36
36
|
import base64
|
37
37
|
import collections
|
38
38
|
import collections.abc
|
39
|
+
import configparser
|
39
40
|
import contextlib
|
40
41
|
import contextvars
|
41
42
|
import ctypes as ct
|
@@ -79,7 +80,7 @@ import types
|
|
79
80
|
import typing as ta
|
80
81
|
import uuid
|
81
82
|
import warnings
|
82
|
-
import weakref
|
83
|
+
import weakref
|
83
84
|
|
84
85
|
|
85
86
|
########################################
|
@@ -96,6 +97,12 @@ if sys.version_info < (3, 8):
|
|
96
97
|
K = ta.TypeVar('K')
|
97
98
|
V = ta.TypeVar('V')
|
98
99
|
|
100
|
+
# ../../omlish/configs/types.py
|
101
|
+
ConfigMap = ta.Mapping[str, ta.Any]
|
102
|
+
|
103
|
+
# ../../omlish/formats/ini/sections.py
|
104
|
+
IniSectionSettingsMap = ta.Mapping[str, ta.Mapping[str, ta.Union[str, ta.Sequence[str]]]] # ta.TypeAlias
|
105
|
+
|
99
106
|
# ../../omlish/formats/toml/parser.py
|
100
107
|
TomlParseFloat = ta.Callable[[str], ta.Any]
|
101
108
|
TomlKey = ta.Tuple[str, ...]
|
@@ -125,6 +132,9 @@ SocketAddress = ta.Any
|
|
125
132
|
EventCallback = ta.Callable[['Event'], None]
|
126
133
|
ProcessOutputChannel = ta.Literal['stdout', 'stderr'] # ta.TypeAlias
|
127
134
|
|
135
|
+
# ../../omlish/configs/formats.py
|
136
|
+
ConfigDataT = ta.TypeVar('ConfigDataT', bound='ConfigData')
|
137
|
+
|
128
138
|
# ../../omlish/http/parsing.py
|
129
139
|
HttpHeaders = http.client.HTTPMessage # ta.TypeAlias
|
130
140
|
|
@@ -141,9 +151,6 @@ InjectorBindingOrBindings = ta.Union['InjectorBinding', 'InjectorBindings']
|
|
141
151
|
# ../../omlish/sockets/handlers.py
|
142
152
|
SocketHandlerFactory = ta.Callable[[SocketAddress, ta.BinaryIO, ta.BinaryIO], 'SocketHandler']
|
143
153
|
|
144
|
-
# ../configs.py
|
145
|
-
ConfigMapping = ta.Mapping[str, ta.Any]
|
146
|
-
|
147
154
|
# ../../omlish/http/handlers.py
|
148
155
|
HttpHandler = ta.Callable[['HttpHandlerRequest'], 'HttpHandlerResponse'] # ta.TypeAlias
|
149
156
|
|
@@ -621,6 +628,54 @@ def parse_octal(arg: ta.Union[str, int]) -> int:
|
|
621
628
|
raise ValueError(f'{arg} can not be converted to an octal type') # noqa
|
622
629
|
|
623
630
|
|
631
|
+
########################################
|
632
|
+
# ../../../omlish/configs/types.py
|
633
|
+
|
634
|
+
|
635
|
+
#
|
636
|
+
|
637
|
+
|
638
|
+
########################################
|
639
|
+
# ../../../omlish/formats/ini/sections.py
|
640
|
+
|
641
|
+
|
642
|
+
##
|
643
|
+
|
644
|
+
|
645
|
+
def extract_ini_sections(cp: configparser.ConfigParser) -> IniSectionSettingsMap:
|
646
|
+
config_dct: ta.Dict[str, ta.Any] = {}
|
647
|
+
for sec in cp.sections():
|
648
|
+
cd = config_dct
|
649
|
+
for k in sec.split('.'):
|
650
|
+
cd = cd.setdefault(k, {})
|
651
|
+
cd.update(cp.items(sec))
|
652
|
+
return config_dct
|
653
|
+
|
654
|
+
|
655
|
+
##
|
656
|
+
|
657
|
+
|
658
|
+
def render_ini_sections(
|
659
|
+
settings_by_section: IniSectionSettingsMap,
|
660
|
+
) -> str:
|
661
|
+
out = io.StringIO()
|
662
|
+
|
663
|
+
for i, (section, settings) in enumerate(settings_by_section.items()):
|
664
|
+
if i:
|
665
|
+
out.write('\n')
|
666
|
+
|
667
|
+
out.write(f'[{section}]\n')
|
668
|
+
|
669
|
+
for k, v in settings.items():
|
670
|
+
if isinstance(v, str):
|
671
|
+
out.write(f'{k}={v}\n')
|
672
|
+
else:
|
673
|
+
for vv in v:
|
674
|
+
out.write(f'{k}={vv}\n')
|
675
|
+
|
676
|
+
return out.getvalue()
|
677
|
+
|
678
|
+
|
624
679
|
########################################
|
625
680
|
# ../../../omlish/formats/toml/parser.py
|
626
681
|
# SPDX-License-Identifier: MIT
|
@@ -1439,6 +1494,129 @@ def toml_make_safe_parse_float(parse_float: TomlParseFloat) -> TomlParseFloat:
|
|
1439
1494
|
return safe_parse_float
|
1440
1495
|
|
1441
1496
|
|
1497
|
+
########################################
|
1498
|
+
# ../../../omlish/formats/toml/writer.py
|
1499
|
+
|
1500
|
+
|
1501
|
+
class TomlWriter:
|
1502
|
+
@dc.dataclass(frozen=True)
|
1503
|
+
class Literal:
|
1504
|
+
s: str
|
1505
|
+
|
1506
|
+
def __init__(self, out: ta.TextIO) -> None:
|
1507
|
+
super().__init__()
|
1508
|
+
self._out = out
|
1509
|
+
|
1510
|
+
self._indent = 0
|
1511
|
+
self._wrote_indent = False
|
1512
|
+
|
1513
|
+
#
|
1514
|
+
|
1515
|
+
def _w(self, s: str) -> None:
|
1516
|
+
if not self._wrote_indent:
|
1517
|
+
self._out.write(' ' * self._indent)
|
1518
|
+
self._wrote_indent = True
|
1519
|
+
self._out.write(s)
|
1520
|
+
|
1521
|
+
def _nl(self) -> None:
|
1522
|
+
self._out.write('\n')
|
1523
|
+
self._wrote_indent = False
|
1524
|
+
|
1525
|
+
def _needs_quote(self, s: str) -> bool:
|
1526
|
+
return (
|
1527
|
+
not s or
|
1528
|
+
any(c in s for c in '\'"\n') or
|
1529
|
+
s[0] not in string.ascii_letters
|
1530
|
+
)
|
1531
|
+
|
1532
|
+
def _maybe_quote(self, s: str) -> str:
|
1533
|
+
if self._needs_quote(s):
|
1534
|
+
return repr(s)
|
1535
|
+
else:
|
1536
|
+
return s
|
1537
|
+
|
1538
|
+
#
|
1539
|
+
|
1540
|
+
def write_root(self, obj: ta.Mapping) -> None:
|
1541
|
+
for i, (k, v) in enumerate(obj.items()):
|
1542
|
+
if i:
|
1543
|
+
self._nl()
|
1544
|
+
self._w('[')
|
1545
|
+
self._w(self._maybe_quote(k))
|
1546
|
+
self._w(']')
|
1547
|
+
self._nl()
|
1548
|
+
self.write_table_contents(v)
|
1549
|
+
|
1550
|
+
def write_table_contents(self, obj: ta.Mapping) -> None:
|
1551
|
+
for k, v in obj.items():
|
1552
|
+
self.write_key(k)
|
1553
|
+
self._w(' = ')
|
1554
|
+
self.write_value(v)
|
1555
|
+
self._nl()
|
1556
|
+
|
1557
|
+
def write_array(self, obj: ta.Sequence) -> None:
|
1558
|
+
self._w('[')
|
1559
|
+
self._nl()
|
1560
|
+
self._indent += 1
|
1561
|
+
for e in obj:
|
1562
|
+
self.write_value(e)
|
1563
|
+
self._w(',')
|
1564
|
+
self._nl()
|
1565
|
+
self._indent -= 1
|
1566
|
+
self._w(']')
|
1567
|
+
|
1568
|
+
def write_inline_table(self, obj: ta.Mapping) -> None:
|
1569
|
+
self._w('{')
|
1570
|
+
for i, (k, v) in enumerate(obj.items()):
|
1571
|
+
if i:
|
1572
|
+
self._w(', ')
|
1573
|
+
self.write_key(k)
|
1574
|
+
self._w(' = ')
|
1575
|
+
self.write_value(v)
|
1576
|
+
self._w('}')
|
1577
|
+
|
1578
|
+
def write_inline_array(self, obj: ta.Sequence) -> None:
|
1579
|
+
self._w('[')
|
1580
|
+
for i, e in enumerate(obj):
|
1581
|
+
if i:
|
1582
|
+
self._w(', ')
|
1583
|
+
self.write_value(e)
|
1584
|
+
self._w(']')
|
1585
|
+
|
1586
|
+
def write_key(self, obj: ta.Any) -> None:
|
1587
|
+
if isinstance(obj, TomlWriter.Literal):
|
1588
|
+
self._w(obj.s)
|
1589
|
+
elif isinstance(obj, str):
|
1590
|
+
self._w(self._maybe_quote(obj.replace('_', '-')))
|
1591
|
+
elif isinstance(obj, int):
|
1592
|
+
self._w(repr(str(obj)))
|
1593
|
+
else:
|
1594
|
+
raise TypeError(obj)
|
1595
|
+
|
1596
|
+
def write_value(self, obj: ta.Any) -> None:
|
1597
|
+
if isinstance(obj, bool):
|
1598
|
+
self._w(str(obj).lower())
|
1599
|
+
elif isinstance(obj, (str, int, float)):
|
1600
|
+
self._w(repr(obj))
|
1601
|
+
elif isinstance(obj, ta.Mapping):
|
1602
|
+
self.write_inline_table(obj)
|
1603
|
+
elif isinstance(obj, ta.Sequence):
|
1604
|
+
if not obj:
|
1605
|
+
self.write_inline_array(obj)
|
1606
|
+
else:
|
1607
|
+
self.write_array(obj)
|
1608
|
+
else:
|
1609
|
+
raise TypeError(obj)
|
1610
|
+
|
1611
|
+
#
|
1612
|
+
|
1613
|
+
@classmethod
|
1614
|
+
def write_str(cls, obj: ta.Any) -> str:
|
1615
|
+
out = io.StringIO()
|
1616
|
+
cls(out).write_value(obj)
|
1617
|
+
return out.getvalue()
|
1618
|
+
|
1619
|
+
|
1442
1620
|
########################################
|
1443
1621
|
# ../../../omlish/http/versions.py
|
1444
1622
|
|
@@ -3125,6 +3303,283 @@ def get_user(name: str) -> User:
|
|
3125
3303
|
)
|
3126
3304
|
|
3127
3305
|
|
3306
|
+
########################################
|
3307
|
+
# ../../../omlish/configs/formats.py
|
3308
|
+
"""
|
3309
|
+
Notes:
|
3310
|
+
- necessarily string-oriented
|
3311
|
+
- single file, as this is intended to be amalg'd and thus all included anyway
|
3312
|
+
|
3313
|
+
TODO:
|
3314
|
+
- ConfigDataMapper? to_map -> ConfigMap?
|
3315
|
+
- nginx ?
|
3316
|
+
- raw ?
|
3317
|
+
"""
|
3318
|
+
|
3319
|
+
|
3320
|
+
##
|
3321
|
+
|
3322
|
+
|
3323
|
+
@dc.dataclass(frozen=True)
|
3324
|
+
class ConfigData(abc.ABC): # noqa
|
3325
|
+
@abc.abstractmethod
|
3326
|
+
def as_map(self) -> ConfigMap:
|
3327
|
+
raise NotImplementedError
|
3328
|
+
|
3329
|
+
|
3330
|
+
#
|
3331
|
+
|
3332
|
+
|
3333
|
+
class ConfigLoader(abc.ABC, ta.Generic[ConfigDataT]):
|
3334
|
+
@property
|
3335
|
+
def file_exts(self) -> ta.Sequence[str]:
|
3336
|
+
return ()
|
3337
|
+
|
3338
|
+
def match_file(self, n: str) -> bool:
|
3339
|
+
return '.' in n and n.split('.')[-1] in check.not_isinstance(self.file_exts, str)
|
3340
|
+
|
3341
|
+
#
|
3342
|
+
|
3343
|
+
def load_file(self, p: str) -> ConfigDataT:
|
3344
|
+
with open(p) as f:
|
3345
|
+
return self.load_str(f.read())
|
3346
|
+
|
3347
|
+
@abc.abstractmethod
|
3348
|
+
def load_str(self, s: str) -> ConfigDataT:
|
3349
|
+
raise NotImplementedError
|
3350
|
+
|
3351
|
+
|
3352
|
+
#
|
3353
|
+
|
3354
|
+
|
3355
|
+
class ConfigRenderer(abc.ABC, ta.Generic[ConfigDataT]):
|
3356
|
+
@property
|
3357
|
+
@abc.abstractmethod
|
3358
|
+
def data_cls(self) -> ta.Type[ConfigDataT]:
|
3359
|
+
raise NotImplementedError
|
3360
|
+
|
3361
|
+
def match_data(self, d: ConfigDataT) -> bool:
|
3362
|
+
return isinstance(d, self.data_cls)
|
3363
|
+
|
3364
|
+
#
|
3365
|
+
|
3366
|
+
@abc.abstractmethod
|
3367
|
+
def render(self, d: ConfigDataT) -> str:
|
3368
|
+
raise NotImplementedError
|
3369
|
+
|
3370
|
+
|
3371
|
+
##
|
3372
|
+
|
3373
|
+
|
3374
|
+
@dc.dataclass(frozen=True)
|
3375
|
+
class ObjConfigData(ConfigData, abc.ABC):
|
3376
|
+
obj: ta.Any
|
3377
|
+
|
3378
|
+
def as_map(self) -> ConfigMap:
|
3379
|
+
return check.isinstance(self.obj, collections.abc.Mapping)
|
3380
|
+
|
3381
|
+
|
3382
|
+
##
|
3383
|
+
|
3384
|
+
|
3385
|
+
@dc.dataclass(frozen=True)
|
3386
|
+
class JsonConfigData(ObjConfigData):
|
3387
|
+
pass
|
3388
|
+
|
3389
|
+
|
3390
|
+
class JsonConfigLoader(ConfigLoader[JsonConfigData]):
|
3391
|
+
file_exts = ('json',)
|
3392
|
+
|
3393
|
+
def load_str(self, s: str) -> JsonConfigData:
|
3394
|
+
return JsonConfigData(json.loads(s))
|
3395
|
+
|
3396
|
+
|
3397
|
+
class JsonConfigRenderer(ConfigRenderer[JsonConfigData]):
|
3398
|
+
data_cls = JsonConfigData
|
3399
|
+
|
3400
|
+
def render(self, d: JsonConfigData) -> str:
|
3401
|
+
return json_dumps_pretty(d.obj)
|
3402
|
+
|
3403
|
+
|
3404
|
+
##
|
3405
|
+
|
3406
|
+
|
3407
|
+
@dc.dataclass(frozen=True)
|
3408
|
+
class TomlConfigData(ObjConfigData):
|
3409
|
+
pass
|
3410
|
+
|
3411
|
+
|
3412
|
+
class TomlConfigLoader(ConfigLoader[TomlConfigData]):
|
3413
|
+
file_exts = ('toml',)
|
3414
|
+
|
3415
|
+
def load_str(self, s: str) -> TomlConfigData:
|
3416
|
+
return TomlConfigData(toml_loads(s))
|
3417
|
+
|
3418
|
+
|
3419
|
+
class TomlConfigRenderer(ConfigRenderer[TomlConfigData]):
|
3420
|
+
data_cls = TomlConfigData
|
3421
|
+
|
3422
|
+
def render(self, d: TomlConfigData) -> str:
|
3423
|
+
return TomlWriter.write_str(d.obj)
|
3424
|
+
|
3425
|
+
|
3426
|
+
##
|
3427
|
+
|
3428
|
+
|
3429
|
+
@dc.dataclass(frozen=True)
|
3430
|
+
class YamlConfigData(ObjConfigData):
|
3431
|
+
pass
|
3432
|
+
|
3433
|
+
|
3434
|
+
class YamlConfigLoader(ConfigLoader[YamlConfigData]):
|
3435
|
+
file_exts = ('yaml', 'yml')
|
3436
|
+
|
3437
|
+
def load_str(self, s: str) -> YamlConfigData:
|
3438
|
+
return YamlConfigData(__import__('yaml').safe_load(s))
|
3439
|
+
|
3440
|
+
|
3441
|
+
class YamlConfigRenderer(ConfigRenderer[YamlConfigData]):
|
3442
|
+
data_cls = YamlConfigData
|
3443
|
+
|
3444
|
+
def render(self, d: YamlConfigData) -> str:
|
3445
|
+
return __import__('yaml').safe_dump(d.obj)
|
3446
|
+
|
3447
|
+
|
3448
|
+
##
|
3449
|
+
|
3450
|
+
|
3451
|
+
@dc.dataclass(frozen=True)
|
3452
|
+
class IniConfigData(ConfigData):
|
3453
|
+
sections: IniSectionSettingsMap
|
3454
|
+
|
3455
|
+
def as_map(self) -> ConfigMap:
|
3456
|
+
return self.sections
|
3457
|
+
|
3458
|
+
|
3459
|
+
class IniConfigLoader(ConfigLoader[IniConfigData]):
|
3460
|
+
file_exts = ('ini',)
|
3461
|
+
|
3462
|
+
def load_str(self, s: str) -> IniConfigData:
|
3463
|
+
cp = configparser.ConfigParser()
|
3464
|
+
cp.read_string(s)
|
3465
|
+
return IniConfigData(extract_ini_sections(cp))
|
3466
|
+
|
3467
|
+
|
3468
|
+
class IniConfigRenderer(ConfigRenderer[IniConfigData]):
|
3469
|
+
data_cls = IniConfigData
|
3470
|
+
|
3471
|
+
def render(self, d: IniConfigData) -> str:
|
3472
|
+
return render_ini_sections(d.sections)
|
3473
|
+
|
3474
|
+
|
3475
|
+
##
|
3476
|
+
|
3477
|
+
|
3478
|
+
@dc.dataclass(frozen=True)
|
3479
|
+
class SwitchedConfigFileLoader:
|
3480
|
+
loaders: ta.Sequence[ConfigLoader]
|
3481
|
+
default: ta.Optional[ConfigLoader] = None
|
3482
|
+
|
3483
|
+
def load_file(self, p: str) -> ConfigData:
|
3484
|
+
n = os.path.basename(p)
|
3485
|
+
|
3486
|
+
for l in self.loaders:
|
3487
|
+
if l.match_file(n):
|
3488
|
+
return l.load_file(p)
|
3489
|
+
|
3490
|
+
if (d := self.default) is not None:
|
3491
|
+
return d.load_file(p)
|
3492
|
+
|
3493
|
+
raise NameError(n)
|
3494
|
+
|
3495
|
+
|
3496
|
+
DEFAULT_CONFIG_LOADERS: ta.Sequence[ConfigLoader] = [
|
3497
|
+
JsonConfigLoader(),
|
3498
|
+
TomlConfigLoader(),
|
3499
|
+
YamlConfigLoader(),
|
3500
|
+
IniConfigLoader(),
|
3501
|
+
]
|
3502
|
+
|
3503
|
+
DEFAULT_CONFIG_LOADER: ConfigLoader = JsonConfigLoader()
|
3504
|
+
|
3505
|
+
DEFAULT_CONFIG_FILE_LOADER = SwitchedConfigFileLoader(
|
3506
|
+
loaders=DEFAULT_CONFIG_LOADERS,
|
3507
|
+
default=DEFAULT_CONFIG_LOADER,
|
3508
|
+
)
|
3509
|
+
|
3510
|
+
|
3511
|
+
##
|
3512
|
+
|
3513
|
+
|
3514
|
+
@dc.dataclass(frozen=True)
|
3515
|
+
class SwitchedConfigRenderer:
|
3516
|
+
renderers: ta.Sequence[ConfigRenderer]
|
3517
|
+
|
3518
|
+
def render(self, d: ConfigData) -> str:
|
3519
|
+
for r in self.renderers:
|
3520
|
+
if r.match_data(d):
|
3521
|
+
return r.render(d)
|
3522
|
+
raise TypeError(d)
|
3523
|
+
|
3524
|
+
|
3525
|
+
DEFAULT_CONFIG_RENDERERS: ta.Sequence[ConfigRenderer] = [
|
3526
|
+
JsonConfigRenderer(),
|
3527
|
+
TomlConfigRenderer(),
|
3528
|
+
YamlConfigRenderer(),
|
3529
|
+
IniConfigRenderer(),
|
3530
|
+
]
|
3531
|
+
|
3532
|
+
DEFAULT_CONFIG_RENDERER = SwitchedConfigRenderer(DEFAULT_CONFIG_RENDERERS)
|
3533
|
+
|
3534
|
+
|
3535
|
+
########################################
|
3536
|
+
# ../../../omlish/configs/processing/names.py
|
3537
|
+
"""
|
3538
|
+
usecase: supervisor process groups
|
3539
|
+
"""
|
3540
|
+
|
3541
|
+
|
3542
|
+
##
|
3543
|
+
|
3544
|
+
|
3545
|
+
def build_config_named_children(
|
3546
|
+
o: ta.Union[
|
3547
|
+
ta.Sequence[ConfigMap],
|
3548
|
+
ta.Mapping[str, ConfigMap],
|
3549
|
+
None,
|
3550
|
+
],
|
3551
|
+
*,
|
3552
|
+
name_key: str = 'name',
|
3553
|
+
) -> ta.Optional[ta.Sequence[ConfigMap]]:
|
3554
|
+
if o is None:
|
3555
|
+
return None
|
3556
|
+
|
3557
|
+
lst: ta.List[ConfigMap] = []
|
3558
|
+
if isinstance(o, ta.Mapping):
|
3559
|
+
for k, v in o.items():
|
3560
|
+
check.isinstance(v, ta.Mapping)
|
3561
|
+
if name_key in v:
|
3562
|
+
n = v[name_key]
|
3563
|
+
if k != n:
|
3564
|
+
raise KeyError(f'Given names do not match: {n} != {k}')
|
3565
|
+
lst.append(v)
|
3566
|
+
else:
|
3567
|
+
lst.append({name_key: k, **v})
|
3568
|
+
|
3569
|
+
else:
|
3570
|
+
check.not_isinstance(o, str)
|
3571
|
+
lst.extend(o)
|
3572
|
+
|
3573
|
+
seen = set()
|
3574
|
+
for d in lst:
|
3575
|
+
n = d['name']
|
3576
|
+
if n in d:
|
3577
|
+
raise KeyError(f'Duplicate name: {n}')
|
3578
|
+
seen.add(n)
|
3579
|
+
|
3580
|
+
return lst
|
3581
|
+
|
3582
|
+
|
3128
3583
|
########################################
|
3129
3584
|
# ../../../omlish/http/parsing.py
|
3130
3585
|
# PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
|
@@ -5858,94 +6313,302 @@ class SocketHandler(abc.ABC):
|
|
5858
6313
|
|
5859
6314
|
|
5860
6315
|
########################################
|
5861
|
-
#
|
6316
|
+
# ../configs.py
|
5862
6317
|
|
5863
6318
|
|
5864
6319
|
##
|
5865
6320
|
|
5866
6321
|
|
5867
|
-
|
5868
|
-
|
5869
|
-
f: ta.TextIO,
|
5870
|
-
) -> ConfigMapping:
|
5871
|
-
if name.endswith('.toml'):
|
5872
|
-
return toml_loads(f.read())
|
5873
|
-
|
5874
|
-
elif any(name.endswith(e) for e in ('.yml', '.yaml')):
|
5875
|
-
yaml = __import__('yaml')
|
5876
|
-
return yaml.safe_load(f)
|
5877
|
-
|
5878
|
-
elif name.endswith('.ini'):
|
5879
|
-
import configparser
|
5880
|
-
cp = configparser.ConfigParser()
|
5881
|
-
cp.read_file(f)
|
5882
|
-
config_dct: ta.Dict[str, ta.Any] = {}
|
5883
|
-
for sec in cp.sections():
|
5884
|
-
cd = config_dct
|
5885
|
-
for k in sec.split('.'):
|
5886
|
-
cd = cd.setdefault(k, {})
|
5887
|
-
cd.update(cp.items(sec))
|
5888
|
-
return config_dct
|
5889
|
-
|
5890
|
-
else:
|
5891
|
-
return json.loads(f.read())
|
5892
|
-
|
5893
|
-
|
5894
|
-
def read_config_file(
|
5895
|
-
path: str,
|
5896
|
-
cls: ta.Type[T],
|
5897
|
-
*,
|
5898
|
-
prepare: ta.Optional[ta.Callable[[ConfigMapping], ConfigMapping]] = None,
|
5899
|
-
msh: ObjMarshalerManager = OBJ_MARSHALER_MANAGER,
|
5900
|
-
) -> T:
|
5901
|
-
with open(path) as cf:
|
5902
|
-
config_dct = parse_config_file(os.path.basename(path), cf)
|
6322
|
+
class RestartWhenExitUnexpected:
|
6323
|
+
pass
|
5903
6324
|
|
5904
|
-
if prepare is not None:
|
5905
|
-
config_dct = prepare(config_dct)
|
5906
6325
|
|
5907
|
-
|
6326
|
+
class RestartUnconditionally:
|
6327
|
+
pass
|
5908
6328
|
|
5909
6329
|
|
5910
6330
|
##
|
5911
6331
|
|
5912
6332
|
|
5913
|
-
|
5914
|
-
|
5915
|
-
|
5916
|
-
|
5917
|
-
|
5918
|
-
|
5919
|
-
|
5920
|
-
name_key: str = 'name',
|
5921
|
-
) -> ta.Optional[ta.Sequence[ConfigMapping]]:
|
5922
|
-
if o is None:
|
5923
|
-
return None
|
6333
|
+
@dc.dataclass(frozen=True)
|
6334
|
+
class ProcessConfig:
|
6335
|
+
# A Python string expression that is used to compose the supervisor process name for this process. You usually don't
|
6336
|
+
# need to worry about setting this unless you change numprocs. The string expression is evaluated against a
|
6337
|
+
# dictionary that includes group_name, host_node_name, process_num, program_name, and here (the directory of the
|
6338
|
+
# supervisord config file).
|
6339
|
+
name: str
|
5924
6340
|
|
5925
|
-
|
5926
|
-
|
5927
|
-
|
5928
|
-
|
5929
|
-
|
5930
|
-
|
5931
|
-
|
5932
|
-
|
5933
|
-
|
5934
|
-
|
5935
|
-
|
6341
|
+
# The command that will be run when this program is started. The command can be either absolute (e.g.
|
6342
|
+
# /path/to/programname) or relative (e.g. programname). If it is relative, the supervisord's environment $PATH will
|
6343
|
+
# be searched for the executable. Programs can accept arguments, e.g. /path/to/program foo bar. The command line can
|
6344
|
+
# use double quotes to group arguments with spaces in them to pass to the program, e.g. /path/to/program/name -p
|
6345
|
+
# "foo bar". Note that the value of command may include Python string expressions, e.g. /path/to/programname
|
6346
|
+
# --port=80%(process_num)02d might expand to /path/to/programname --port=8000 at runtime. String expressions are
|
6347
|
+
# evaluated against a dictionary containing the keys group_name, host_node_name, program_name, process_num,
|
6348
|
+
# numprocs, here (the directory of the supervisord config file), and all supervisord's environment variables
|
6349
|
+
# prefixed with ENV_. Controlled programs should themselves not be daemons, as supervisord assumes it is responsible
|
6350
|
+
# for daemonizing its subprocesses
|
6351
|
+
command: str
|
5936
6352
|
|
5937
|
-
|
5938
|
-
check.not_isinstance(o, str)
|
5939
|
-
lst.extend(o)
|
6353
|
+
#
|
5940
6354
|
|
5941
|
-
|
5942
|
-
|
5943
|
-
|
5944
|
-
|
5945
|
-
raise KeyError(f'Duplicate name: {n}')
|
5946
|
-
seen.add(n)
|
6355
|
+
# Supervisor will start as many instances of this program as named by numprocs. Note that if numprocs > 1, the
|
6356
|
+
# process_name expression must include %(process_num)s (or any other valid Python string expression that includes
|
6357
|
+
# process_num) within it.
|
6358
|
+
num_procs: int = 1
|
5947
6359
|
|
5948
|
-
|
6360
|
+
# An integer offset that is used to compute the number at which process_num starts.
|
6361
|
+
num_procs_start: int = 0
|
6362
|
+
|
6363
|
+
#
|
6364
|
+
|
6365
|
+
# Instruct supervisord to use this UNIX user account as the account which runs the program. The user can only be
|
6366
|
+
# switched if supervisord is run as the root user. If supervisord can't switch to the specified user, the program
|
6367
|
+
# will not be started.
|
6368
|
+
#
|
6369
|
+
# Note: The user will be changed using setuid only. This does not start a login shell and does not change
|
6370
|
+
# environment variables like USER or HOME
|
6371
|
+
user: ta.Optional[str] = None
|
6372
|
+
uid: ta.Optional[int] = None
|
6373
|
+
|
6374
|
+
# An octal number (e.g. 002, 022) representing the umask of the process.
|
6375
|
+
umask: ta.Optional[int] = None
|
6376
|
+
|
6377
|
+
#
|
6378
|
+
|
6379
|
+
# A file path representing a directory to which supervisord should temporarily chdir before exec'ing the child.
|
6380
|
+
directory: ta.Optional[str] = None
|
6381
|
+
|
6382
|
+
# A list of key/value pairs in the form KEY="val",KEY2="val2" that will be placed in the child process' environment.
|
6383
|
+
# The environment string may contain Python string expressions that will be evaluated against a dictionary
|
6384
|
+
# containing group_name, host_node_name, process_num, program_name, and here (the directory of the supervisord
|
6385
|
+
# config file). Values containing non-alphanumeric characters should be quoted (e.g. KEY="val:123",KEY2="val,456").
|
6386
|
+
# Otherwise, quoting the values is optional but recommended. Note that the subprocess will inherit the environment
|
6387
|
+
# variables of the shell used to start “supervisord” except for the ones overridden here.
|
6388
|
+
environment: ta.Optional[ta.Mapping[str, str]] = None
|
6389
|
+
|
6390
|
+
#
|
6391
|
+
|
6392
|
+
# The relative priority of the program in the start and shutdown ordering. Lower priorities indicate programs that
|
6393
|
+
# start first and shut down last at startup and when aggregate commands are used in various clients (e.g. “start
|
6394
|
+
# all”/”stop all”). Higher priorities indicate programs that start last and shut down first.
|
6395
|
+
priority: int = 999
|
6396
|
+
|
6397
|
+
# If true, this program will start automatically when supervisord is started.
|
6398
|
+
auto_start: bool = True
|
6399
|
+
|
6400
|
+
# Specifies if supervisord should automatically restart a process if it exits when it is in the RUNNING state. May
|
6401
|
+
# be one of false, unexpected, or true. If false, the process will not be autorestarted. If unexpected, the process
|
6402
|
+
# will be restarted when the program exits with an exit code that is not one of the exit codes associated with this
|
6403
|
+
# process' configuration (see exitcodes). If true, the process will be unconditionally restarted when it exits,
|
6404
|
+
# without regard to its exit code.
|
6405
|
+
#
|
6406
|
+
# Note: autorestart controls whether supervisord will autorestart a program if it exits after it has successfully
|
6407
|
+
# started up (the process is in the RUNNING state). supervisord has a different restart mechanism for when the
|
6408
|
+
# process is starting up (the process is in the STARTING state). Retries during process startup are controlled by
|
6409
|
+
# startsecs and startretries.
|
6410
|
+
auto_restart: str = 'unexpected'
|
6411
|
+
|
6412
|
+
# The total number of seconds which the program needs to stay running after a startup to consider the start
|
6413
|
+
# successful (moving the process from the STARTING state to the RUNNING state). Set to 0 to indicate that the
|
6414
|
+
# program needn't stay running for any particular amount of time.
|
6415
|
+
#
|
6416
|
+
# Note: Even if a process exits with an “expected” exit code (see exitcodes), the start will still be considered a
|
6417
|
+
# failure if the process exits quicker than startsecs.
|
6418
|
+
start_secs: int = 1
|
6419
|
+
|
6420
|
+
# The number of serial failure attempts that supervisord will allow when attempting to start the program before
|
6421
|
+
# giving up and putting the process into an FATAL state.
|
6422
|
+
#
|
6423
|
+
# Note: After each failed restart, process will be put in BACKOFF state and each retry attempt will take
|
6424
|
+
# increasingly more time.
|
6425
|
+
start_retries: int = 3
|
6426
|
+
|
6427
|
+
# The signal used to kill the program when a stop is requested. This can be specified using the signal's name or its
|
6428
|
+
# number. It is normally one of: TERM, HUP, INT, QUIT, KILL, USR1, or USR2.
|
6429
|
+
stop_signal: int = signal.SIGTERM
|
6430
|
+
|
6431
|
+
# The number of seconds to wait for the OS to return a SIGCHLD to supervisord after the program has been sent a
|
6432
|
+
# stopsignal. If this number of seconds elapses before supervisord receives a SIGCHLD from the process, supervisord
|
6433
|
+
# will attempt to kill it with a final SIGKILL.
|
6434
|
+
stop_wait_secs: int = 10
|
6435
|
+
|
6436
|
+
# If true, the flag causes supervisor to send the stop signal to the whole process group and implies killasgroup is
|
6437
|
+
# true. This is useful for programs, such as Flask in debug mode, that do not propagate stop signals to their
|
6438
|
+
# children, leaving them orphaned.
|
6439
|
+
stop_as_group: bool = False
|
6440
|
+
|
6441
|
+
# If true, when resorting to send SIGKILL to the program to terminate it send it to its whole process group instead,
|
6442
|
+
# taking care of its children as well, useful e.g with Python programs using multiprocessing.
|
6443
|
+
kill_as_group: bool = False
|
6444
|
+
|
6445
|
+
# The list of “expected” exit codes for this program used with autorestart. If the autorestart parameter is set to
|
6446
|
+
# unexpected, and the process exits in any other way than as a result of a supervisor stop request, supervisord will
|
6447
|
+
# restart the process if it exits with an exit code that is not defined in this list.
|
6448
|
+
#
|
6449
|
+
# Note: In Supervisor versions prior to 4.0, the default was 0,2. In Supervisor 4.0, the default was changed to 0.
|
6450
|
+
exitcodes: ta.Sequence[int] = (0,)
|
6451
|
+
|
6452
|
+
#
|
6453
|
+
|
6454
|
+
@dc.dataclass(frozen=True)
|
6455
|
+
class Log:
|
6456
|
+
file: ta.Optional[str] = None
|
6457
|
+
capture_max_bytes: ta.Optional[int] = None
|
6458
|
+
events_enabled: bool = False
|
6459
|
+
syslog: bool = False
|
6460
|
+
backups: ta.Optional[int] = None
|
6461
|
+
max_bytes: ta.Optional[int] = None
|
6462
|
+
|
6463
|
+
stdout: Log = Log()
|
6464
|
+
stderr: Log = Log()
|
6465
|
+
|
6466
|
+
# If true, cause the process' stderr output to be sent back to supervisord on its stdout file descriptor (in UNIX
|
6467
|
+
# shell terms, this is the equivalent of executing /the/program 2>&1).
|
6468
|
+
#
|
6469
|
+
# Note: Do not set redirect_stderr=true in an [eventlistener:x] section. Eventlisteners use stdout and stdin to
|
6470
|
+
# communicate with supervisord. If stderr is redirected, output from stderr will interfere with the eventlistener
|
6471
|
+
# protocol.
|
6472
|
+
redirect_stderr: bool = False
|
6473
|
+
|
6474
|
+
|
6475
|
+
@dc.dataclass(frozen=True)
|
6476
|
+
class ProcessGroupConfig:
|
6477
|
+
name: str
|
6478
|
+
|
6479
|
+
priority: int = 999
|
6480
|
+
|
6481
|
+
processes: ta.Optional[ta.Sequence[ProcessConfig]] = None
|
6482
|
+
|
6483
|
+
|
6484
|
+
@dc.dataclass(frozen=True)
|
6485
|
+
class ServerConfig:
|
6486
|
+
# Instruct supervisord to switch users to this UNIX user account before doing any meaningful processing. The user
|
6487
|
+
# can only be switched if supervisord is started as the root user.
|
6488
|
+
user: ta.Optional[str] = None
|
6489
|
+
|
6490
|
+
# If true, supervisord will start in the foreground instead of daemonizing.
|
6491
|
+
nodaemon: bool = False
|
6492
|
+
|
6493
|
+
# The umask of the supervisord process.
|
6494
|
+
umask: int = 0o22
|
6495
|
+
|
6496
|
+
#
|
6497
|
+
|
6498
|
+
# When supervisord daemonizes, switch to this directory. This option can include the value %(here)s, which expands
|
6499
|
+
# to the directory in which the supervisord configuration file was found.
|
6500
|
+
directory: ta.Optional[str] = None
|
6501
|
+
|
6502
|
+
# The location in which supervisord keeps its pid file. This option can include the value %(here)s, which expands to
|
6503
|
+
# the directory in which the supervisord configuration file was found.
|
6504
|
+
pidfile: str = 'supervisord.pid'
|
6505
|
+
|
6506
|
+
# The identifier string for this supervisor process, used by the RPC interface.
|
6507
|
+
identifier: str = 'supervisor'
|
6508
|
+
|
6509
|
+
# The minimum number of file descriptors that must be available before supervisord will start successfully.
|
6510
|
+
min_fds: int = 1024
|
6511
|
+
# The minimum number of process descriptors that must be available before supervisord will start successfully.
|
6512
|
+
min_procs: int = 200
|
6513
|
+
|
6514
|
+
# Prevent supervisord from clearing any existing AUTO child log files at startup time. Useful for debugging
|
6515
|
+
nocleanup: bool = False
|
6516
|
+
|
6517
|
+
# Strip all ANSI escape sequences from child log files.
|
6518
|
+
strip_ansi: bool = False
|
6519
|
+
|
6520
|
+
#
|
6521
|
+
|
6522
|
+
# The path to the activity log of the supervisord process. This option can include the value %(here)s, which expands
|
6523
|
+
# to the directory in which the supervisord configuration file was found.
|
6524
|
+
logfile: str = 'supervisord.log'
|
6525
|
+
|
6526
|
+
# The maximum number of bytes that may be consumed by the activity log file before it is rotated (suffix multipliers
|
6527
|
+
# like “KB”, “MB”, and “GB” can be used in the value). Set this value to 0 to indicate an unlimited log size.
|
6528
|
+
logfile_max_bytes: int = 50 * 1024 * 1024
|
6529
|
+
|
6530
|
+
# The number of backups to keep around resulting from activity log file rotation. If set to 0, no backups will be
|
6531
|
+
# kept.
|
6532
|
+
logfile_backups: int = 10
|
6533
|
+
|
6534
|
+
# The logging level, dictating what is written to the supervisord activity log. One of critical, error, warn, info,
|
6535
|
+
# debug, trace, or blather. Note that at log level debug, the supervisord log file will record the stderr/stdout
|
6536
|
+
# output of its child processes and extended info about process state changes, which is useful for debugging a
|
6537
|
+
# process which isn't starting properly.
|
6538
|
+
loglevel: int = logging.INFO
|
6539
|
+
|
6540
|
+
# The directory used for AUTO child log files. This option can include the value %(here)s, which expands to the
|
6541
|
+
# directory in which the supervisord configuration file was found.
|
6542
|
+
child_logdir: str = '/dev/null'
|
6543
|
+
|
6544
|
+
# If true and not daemonized, logs will not be directed to stdout.
|
6545
|
+
silent: bool = False
|
6546
|
+
|
6547
|
+
#
|
6548
|
+
|
6549
|
+
groups: ta.Optional[ta.Sequence[ProcessGroupConfig]] = None
|
6550
|
+
|
6551
|
+
# TODO: implement - make sure to accept broken symlinks
|
6552
|
+
group_config_dirs: ta.Optional[ta.Sequence[str]] = None
|
6553
|
+
|
6554
|
+
#
|
6555
|
+
|
6556
|
+
http_port: ta.Optional[int] = None
|
6557
|
+
|
6558
|
+
#
|
6559
|
+
|
6560
|
+
@classmethod
|
6561
|
+
def new(
|
6562
|
+
cls,
|
6563
|
+
*,
|
6564
|
+
umask: ta.Union[int, str] = 0o22,
|
6565
|
+
directory: ta.Optional[str] = None,
|
6566
|
+
logfile: str = 'supervisord.log',
|
6567
|
+
logfile_max_bytes: ta.Union[int, str] = 50 * 1024 * 1024,
|
6568
|
+
loglevel: ta.Union[int, str] = logging.INFO,
|
6569
|
+
pidfile: str = 'supervisord.pid',
|
6570
|
+
child_logdir: ta.Optional[str] = None,
|
6571
|
+
**kwargs: ta.Any,
|
6572
|
+
) -> 'ServerConfig':
|
6573
|
+
return cls(
|
6574
|
+
umask=parse_octal(umask),
|
6575
|
+
directory=check_existing_dir(directory) if directory is not None else None,
|
6576
|
+
logfile=check_path_with_existing_dir(logfile),
|
6577
|
+
logfile_max_bytes=parse_bytes_size(logfile_max_bytes),
|
6578
|
+
loglevel=parse_logging_level(loglevel),
|
6579
|
+
pidfile=check_path_with_existing_dir(pidfile),
|
6580
|
+
child_logdir=child_logdir if child_logdir else tempfile.gettempdir(),
|
6581
|
+
**kwargs,
|
6582
|
+
)
|
6583
|
+
|
6584
|
+
|
6585
|
+
##
|
6586
|
+
|
6587
|
+
|
6588
|
+
def prepare_process_group_config(dct: ConfigMap) -> ConfigMap:
|
6589
|
+
out = dict(dct)
|
6590
|
+
out['processes'] = build_config_named_children(out.get('processes'))
|
6591
|
+
return out
|
6592
|
+
|
6593
|
+
|
6594
|
+
def prepare_server_config(dct: ta.Mapping[str, ta.Any]) -> ta.Mapping[str, ta.Any]:
|
6595
|
+
out = dict(dct)
|
6596
|
+
group_dcts = build_config_named_children(out.get('groups'))
|
6597
|
+
out['groups'] = [prepare_process_group_config(group_dct) for group_dct in group_dcts or []]
|
6598
|
+
return out
|
6599
|
+
|
6600
|
+
|
6601
|
+
##
|
6602
|
+
|
6603
|
+
|
6604
|
+
def parse_logging_level(value: ta.Union[str, int]) -> int:
|
6605
|
+
if isinstance(value, int):
|
6606
|
+
return value
|
6607
|
+
s = str(value).lower()
|
6608
|
+
level = logging.getLevelNamesMapping().get(s.upper())
|
6609
|
+
if level is None:
|
6610
|
+
raise ValueError(f'bad logging level name {value!r}')
|
6611
|
+
return level
|
5949
6612
|
|
5950
6613
|
|
5951
6614
|
########################################
|
@@ -6095,6 +6758,38 @@ class UnsupportedMethodHttpHandlerError(Exception):
|
|
6095
6758
|
pass
|
6096
6759
|
|
6097
6760
|
|
6761
|
+
########################################
|
6762
|
+
# ../../../omlish/lite/configs.py
|
6763
|
+
|
6764
|
+
|
6765
|
+
##
|
6766
|
+
|
6767
|
+
|
6768
|
+
def load_config_file_obj(
|
6769
|
+
f: str,
|
6770
|
+
cls: ta.Type[T],
|
6771
|
+
*,
|
6772
|
+
prepare: ta.Union[
|
6773
|
+
ta.Callable[[ConfigMap], ConfigMap],
|
6774
|
+
ta.Iterable[ta.Callable[[ConfigMap], ConfigMap]],
|
6775
|
+
] = (),
|
6776
|
+
msh: ObjMarshalerManager = OBJ_MARSHALER_MANAGER,
|
6777
|
+
) -> T:
|
6778
|
+
config_data = DEFAULT_CONFIG_FILE_LOADER.load_file(f)
|
6779
|
+
|
6780
|
+
config_dct = config_data.as_map()
|
6781
|
+
|
6782
|
+
if prepare is not None:
|
6783
|
+
if isinstance(prepare, ta.Iterable):
|
6784
|
+
pfs = list(prepare)
|
6785
|
+
else:
|
6786
|
+
pfs = [prepare]
|
6787
|
+
for pf in pfs:
|
6788
|
+
config_dct = pf(config_dct)
|
6789
|
+
|
6790
|
+
return msh.unmarshal_obj(config_dct, cls)
|
6791
|
+
|
6792
|
+
|
6098
6793
|
########################################
|
6099
6794
|
# ../../../omlish/logs/standard.py
|
6100
6795
|
"""
|
@@ -6217,303 +6912,184 @@ def configure_standard_logging(
|
|
6217
6912
|
return StandardConfiguredLogHandler(handler)
|
6218
6913
|
|
6219
6914
|
|
6220
|
-
########################################
|
6221
|
-
# ../
|
6222
|
-
|
6223
|
-
|
6224
|
-
##
|
6225
|
-
|
6226
|
-
|
6227
|
-
class RestartWhenExitUnexpected:
|
6228
|
-
pass
|
6229
|
-
|
6230
|
-
|
6231
|
-
class RestartUnconditionally:
|
6232
|
-
pass
|
6233
|
-
|
6234
|
-
|
6235
|
-
##
|
6236
|
-
|
6237
|
-
|
6238
|
-
@dc.dataclass(frozen=True)
|
6239
|
-
class ProcessConfig:
|
6240
|
-
# A Python string expression that is used to compose the supervisor process name for this process. You usually don't
|
6241
|
-
# need to worry about setting this unless you change numprocs. The string expression is evaluated against a
|
6242
|
-
# dictionary that includes group_name, host_node_name, process_num, program_name, and here (the directory of the
|
6243
|
-
# supervisord config file).
|
6244
|
-
name: str
|
6245
|
-
|
6246
|
-
# The command that will be run when this program is started. The command can be either absolute (e.g.
|
6247
|
-
# /path/to/programname) or relative (e.g. programname). If it is relative, the supervisord's environment $PATH will
|
6248
|
-
# be searched for the executable. Programs can accept arguments, e.g. /path/to/program foo bar. The command line can
|
6249
|
-
# use double quotes to group arguments with spaces in them to pass to the program, e.g. /path/to/program/name -p
|
6250
|
-
# "foo bar". Note that the value of command may include Python string expressions, e.g. /path/to/programname
|
6251
|
-
# --port=80%(process_num)02d might expand to /path/to/programname --port=8000 at runtime. String expressions are
|
6252
|
-
# evaluated against a dictionary containing the keys group_name, host_node_name, program_name, process_num,
|
6253
|
-
# numprocs, here (the directory of the supervisord config file), and all supervisord's environment variables
|
6254
|
-
# prefixed with ENV_. Controlled programs should themselves not be daemons, as supervisord assumes it is responsible
|
6255
|
-
# for daemonizing its subprocesses
|
6256
|
-
command: str
|
6257
|
-
|
6258
|
-
#
|
6259
|
-
|
6260
|
-
# Supervisor will start as many instances of this program as named by numprocs. Note that if numprocs > 1, the
|
6261
|
-
# process_name expression must include %(process_num)s (or any other valid Python string expression that includes
|
6262
|
-
# process_num) within it.
|
6263
|
-
num_procs: int = 1
|
6264
|
-
|
6265
|
-
# An integer offset that is used to compute the number at which process_num starts.
|
6266
|
-
num_procs_start: int = 0
|
6267
|
-
|
6268
|
-
#
|
6269
|
-
|
6270
|
-
# Instruct supervisord to use this UNIX user account as the account which runs the program. The user can only be
|
6271
|
-
# switched if supervisord is run as the root user. If supervisord can't switch to the specified user, the program
|
6272
|
-
# will not be started.
|
6273
|
-
#
|
6274
|
-
# Note: The user will be changed using setuid only. This does not start a login shell and does not change
|
6275
|
-
# environment variables like USER or HOME
|
6276
|
-
user: ta.Optional[str] = None
|
6277
|
-
uid: ta.Optional[int] = None
|
6278
|
-
|
6279
|
-
# An octal number (e.g. 002, 022) representing the umask of the process.
|
6280
|
-
umask: ta.Optional[int] = None
|
6281
|
-
|
6282
|
-
#
|
6283
|
-
|
6284
|
-
# A file path representing a directory to which supervisord should temporarily chdir before exec'ing the child.
|
6285
|
-
directory: ta.Optional[str] = None
|
6286
|
-
|
6287
|
-
# A list of key/value pairs in the form KEY="val",KEY2="val2" that will be placed in the child process' environment.
|
6288
|
-
# The environment string may contain Python string expressions that will be evaluated against a dictionary
|
6289
|
-
# containing group_name, host_node_name, process_num, program_name, and here (the directory of the supervisord
|
6290
|
-
# config file). Values containing non-alphanumeric characters should be quoted (e.g. KEY="val:123",KEY2="val,456").
|
6291
|
-
# Otherwise, quoting the values is optional but recommended. Note that the subprocess will inherit the environment
|
6292
|
-
# variables of the shell used to start “supervisord” except for the ones overridden here.
|
6293
|
-
environment: ta.Optional[ta.Mapping[str, str]] = None
|
6294
|
-
|
6295
|
-
#
|
6296
|
-
|
6297
|
-
# The relative priority of the program in the start and shutdown ordering. Lower priorities indicate programs that
|
6298
|
-
# start first and shut down last at startup and when aggregate commands are used in various clients (e.g. “start
|
6299
|
-
# all”/”stop all”). Higher priorities indicate programs that start last and shut down first.
|
6300
|
-
priority: int = 999
|
6301
|
-
|
6302
|
-
# If true, this program will start automatically when supervisord is started.
|
6303
|
-
auto_start: bool = True
|
6304
|
-
|
6305
|
-
# Specifies if supervisord should automatically restart a process if it exits when it is in the RUNNING state. May
|
6306
|
-
# be one of false, unexpected, or true. If false, the process will not be autorestarted. If unexpected, the process
|
6307
|
-
# will be restarted when the program exits with an exit code that is not one of the exit codes associated with this
|
6308
|
-
# process' configuration (see exitcodes). If true, the process will be unconditionally restarted when it exits,
|
6309
|
-
# without regard to its exit code.
|
6310
|
-
#
|
6311
|
-
# Note: autorestart controls whether supervisord will autorestart a program if it exits after it has successfully
|
6312
|
-
# started up (the process is in the RUNNING state). supervisord has a different restart mechanism for when the
|
6313
|
-
# process is starting up (the process is in the STARTING state). Retries during process startup are controlled by
|
6314
|
-
# startsecs and startretries.
|
6315
|
-
auto_restart: str = 'unexpected'
|
6915
|
+
########################################
|
6916
|
+
# ../types.py
|
6316
6917
|
|
6317
|
-
# The total number of seconds which the program needs to stay running after a startup to consider the start
|
6318
|
-
# successful (moving the process from the STARTING state to the RUNNING state). Set to 0 to indicate that the
|
6319
|
-
# program needn't stay running for any particular amount of time.
|
6320
|
-
#
|
6321
|
-
# Note: Even if a process exits with an “expected” exit code (see exitcodes), the start will still be considered a
|
6322
|
-
# failure if the process exits quicker than startsecs.
|
6323
|
-
start_secs: int = 1
|
6324
6918
|
|
6325
|
-
|
6326
|
-
# giving up and putting the process into an FATAL state.
|
6327
|
-
#
|
6328
|
-
# Note: After each failed restart, process will be put in BACKOFF state and each retry attempt will take
|
6329
|
-
# increasingly more time.
|
6330
|
-
start_retries: int = 3
|
6919
|
+
##
|
6331
6920
|
|
6332
|
-
# The signal used to kill the program when a stop is requested. This can be specified using the signal's name or its
|
6333
|
-
# number. It is normally one of: TERM, HUP, INT, QUIT, KILL, USR1, or USR2.
|
6334
|
-
stop_signal: int = signal.SIGTERM
|
6335
6921
|
|
6336
|
-
|
6337
|
-
|
6338
|
-
# will attempt to kill it with a final SIGKILL.
|
6339
|
-
stop_wait_secs: int = 10
|
6922
|
+
class ExitNow(Exception): # noqa
|
6923
|
+
pass
|
6340
6924
|
|
6341
|
-
# If true, the flag causes supervisor to send the stop signal to the whole process group and implies killasgroup is
|
6342
|
-
# true. This is useful for programs, such as Flask in debug mode, that do not propagate stop signals to their
|
6343
|
-
# children, leaving them orphaned.
|
6344
|
-
stop_as_group: bool = False
|
6345
6925
|
|
6346
|
-
|
6347
|
-
# taking care of its children as well, useful e.g with Python programs using multiprocessing.
|
6348
|
-
kill_as_group: bool = False
|
6926
|
+
ServerEpoch = ta.NewType('ServerEpoch', int)
|
6349
6927
|
|
6350
|
-
# The list of “expected” exit codes for this program used with autorestart. If the autorestart parameter is set to
|
6351
|
-
# unexpected, and the process exits in any other way than as a result of a supervisor stop request, supervisord will
|
6352
|
-
# restart the process if it exits with an exit code that is not defined in this list.
|
6353
|
-
#
|
6354
|
-
# Note: In Supervisor versions prior to 4.0, the default was 0,2. In Supervisor 4.0, the default was changed to 0.
|
6355
|
-
exitcodes: ta.Sequence[int] = (0,)
|
6356
6928
|
|
6357
|
-
|
6929
|
+
##
|
6358
6930
|
|
6359
|
-
@dc.dataclass(frozen=True)
|
6360
|
-
class Log:
|
6361
|
-
file: ta.Optional[str] = None
|
6362
|
-
capture_max_bytes: ta.Optional[int] = None
|
6363
|
-
events_enabled: bool = False
|
6364
|
-
syslog: bool = False
|
6365
|
-
backups: ta.Optional[int] = None
|
6366
|
-
max_bytes: ta.Optional[int] = None
|
6367
6931
|
|
6368
|
-
|
6369
|
-
|
6932
|
+
@functools.total_ordering
|
6933
|
+
class ConfigPriorityOrdered(abc.ABC):
|
6934
|
+
@property
|
6935
|
+
@abc.abstractmethod
|
6936
|
+
def config(self) -> ta.Any:
|
6937
|
+
raise NotImplementedError
|
6370
6938
|
|
6371
|
-
|
6372
|
-
|
6373
|
-
#
|
6374
|
-
# Note: Do not set redirect_stderr=true in an [eventlistener:x] section. Eventlisteners use stdout and stdin to
|
6375
|
-
# communicate with supervisord. If stderr is redirected, output from stderr will interfere with the eventlistener
|
6376
|
-
# protocol.
|
6377
|
-
redirect_stderr: bool = False
|
6939
|
+
def __lt__(self, other):
|
6940
|
+
return self.config.priority < other.config.priority
|
6378
6941
|
|
6942
|
+
def __eq__(self, other):
|
6943
|
+
return self.config.priority == other.config.priority
|
6379
6944
|
|
6380
|
-
@dc.dataclass(frozen=True)
|
6381
|
-
class ProcessGroupConfig:
|
6382
|
-
name: str
|
6383
6945
|
|
6384
|
-
|
6946
|
+
##
|
6385
6947
|
|
6386
|
-
processes: ta.Optional[ta.Sequence[ProcessConfig]] = None
|
6387
6948
|
|
6949
|
+
class SupervisorStateManager(abc.ABC):
|
6950
|
+
@property
|
6951
|
+
@abc.abstractmethod
|
6952
|
+
def state(self) -> SupervisorState:
|
6953
|
+
raise NotImplementedError
|
6388
6954
|
|
6389
|
-
@
|
6390
|
-
|
6391
|
-
|
6392
|
-
# can only be switched if supervisord is started as the root user.
|
6393
|
-
user: ta.Optional[str] = None
|
6955
|
+
@abc.abstractmethod
|
6956
|
+
def set_state(self, state: SupervisorState) -> None:
|
6957
|
+
raise NotImplementedError
|
6394
6958
|
|
6395
|
-
# If true, supervisord will start in the foreground instead of daemonizing.
|
6396
|
-
nodaemon: bool = False
|
6397
6959
|
|
6398
|
-
|
6399
|
-
umask: int = 0o22
|
6960
|
+
##
|
6400
6961
|
|
6401
|
-
#
|
6402
6962
|
|
6403
|
-
|
6404
|
-
|
6405
|
-
|
6963
|
+
class HasDispatchers(abc.ABC):
|
6964
|
+
@abc.abstractmethod
|
6965
|
+
def get_dispatchers(self) -> 'Dispatchers':
|
6966
|
+
raise NotImplementedError
|
6406
6967
|
|
6407
|
-
# The location in which supervisord keeps its pid file. This option can include the value %(here)s, which expands to
|
6408
|
-
# the directory in which the supervisord configuration file was found.
|
6409
|
-
pidfile: str = 'supervisord.pid'
|
6410
6968
|
|
6411
|
-
|
6412
|
-
|
6969
|
+
class ProcessDispatcher(FdioHandler, abc.ABC):
|
6970
|
+
@property
|
6971
|
+
@abc.abstractmethod
|
6972
|
+
def channel(self) -> ProcessOutputChannel:
|
6973
|
+
raise NotImplementedError
|
6413
6974
|
|
6414
|
-
|
6415
|
-
|
6416
|
-
|
6417
|
-
|
6975
|
+
@property
|
6976
|
+
@abc.abstractmethod
|
6977
|
+
def process(self) -> 'Process':
|
6978
|
+
raise NotImplementedError
|
6418
6979
|
|
6419
|
-
# Prevent supervisord from clearing any existing AUTO child log files at startup time. Useful for debugging
|
6420
|
-
nocleanup: bool = False
|
6421
6980
|
|
6422
|
-
|
6423
|
-
|
6981
|
+
class ProcessOutputDispatcher(ProcessDispatcher, abc.ABC):
|
6982
|
+
@abc.abstractmethod
|
6983
|
+
def remove_logs(self) -> None:
|
6984
|
+
raise NotImplementedError
|
6424
6985
|
|
6425
|
-
|
6986
|
+
@abc.abstractmethod
|
6987
|
+
def reopen_logs(self) -> None:
|
6988
|
+
raise NotImplementedError
|
6426
6989
|
|
6427
|
-
# The path to the activity log of the supervisord process. This option can include the value %(here)s, which expands
|
6428
|
-
# to the directory in which the supervisord configuration file was found.
|
6429
|
-
logfile: str = 'supervisord.log'
|
6430
6990
|
|
6431
|
-
|
6432
|
-
|
6433
|
-
|
6991
|
+
class ProcessInputDispatcher(ProcessDispatcher, abc.ABC):
|
6992
|
+
@abc.abstractmethod
|
6993
|
+
def write(self, chars: ta.Union[bytes, str]) -> None:
|
6994
|
+
raise NotImplementedError
|
6434
6995
|
|
6435
|
-
|
6436
|
-
|
6437
|
-
|
6996
|
+
@abc.abstractmethod
|
6997
|
+
def flush(self) -> None:
|
6998
|
+
raise NotImplementedError
|
6438
6999
|
|
6439
|
-
# The logging level, dictating what is written to the supervisord activity log. One of critical, error, warn, info,
|
6440
|
-
# debug, trace, or blather. Note that at log level debug, the supervisord log file will record the stderr/stdout
|
6441
|
-
# output of its child processes and extended info about process state changes, which is useful for debugging a
|
6442
|
-
# process which isn't starting properly.
|
6443
|
-
loglevel: int = logging.INFO
|
6444
7000
|
|
6445
|
-
|
6446
|
-
# directory in which the supervisord configuration file was found.
|
6447
|
-
child_logdir: str = '/dev/null'
|
7001
|
+
##
|
6448
7002
|
|
6449
|
-
# If true and not daemonized, logs will not be directed to stdout.
|
6450
|
-
silent: bool = False
|
6451
7003
|
|
6452
|
-
|
7004
|
+
class Process(
|
7005
|
+
ConfigPriorityOrdered,
|
7006
|
+
HasDispatchers,
|
7007
|
+
abc.ABC,
|
7008
|
+
):
|
7009
|
+
@property
|
7010
|
+
@abc.abstractmethod
|
7011
|
+
def name(self) -> str:
|
7012
|
+
raise NotImplementedError
|
6453
7013
|
|
6454
|
-
|
7014
|
+
@property
|
7015
|
+
@abc.abstractmethod
|
7016
|
+
def config(self) -> ProcessConfig:
|
7017
|
+
raise NotImplementedError
|
6455
7018
|
|
6456
|
-
|
6457
|
-
|
7019
|
+
@property
|
7020
|
+
@abc.abstractmethod
|
7021
|
+
def group(self) -> 'ProcessGroup':
|
7022
|
+
raise NotImplementedError
|
7023
|
+
|
7024
|
+
@property
|
7025
|
+
@abc.abstractmethod
|
7026
|
+
def pid(self) -> Pid:
|
7027
|
+
raise NotImplementedError
|
6458
7028
|
|
6459
7029
|
#
|
6460
7030
|
|
6461
|
-
|
7031
|
+
@abc.abstractmethod
|
7032
|
+
def finish(self, sts: Rc) -> None:
|
7033
|
+
raise NotImplementedError
|
6462
7034
|
|
6463
|
-
|
7035
|
+
@abc.abstractmethod
|
7036
|
+
def stop(self) -> ta.Optional[str]:
|
7037
|
+
raise NotImplementedError
|
6464
7038
|
|
6465
|
-
@
|
6466
|
-
def
|
6467
|
-
|
6468
|
-
|
6469
|
-
|
6470
|
-
|
6471
|
-
|
6472
|
-
|
6473
|
-
|
6474
|
-
|
6475
|
-
|
6476
|
-
|
6477
|
-
|
6478
|
-
|
6479
|
-
|
6480
|
-
|
6481
|
-
logfile=check_path_with_existing_dir(logfile),
|
6482
|
-
logfile_max_bytes=parse_bytes_size(logfile_max_bytes),
|
6483
|
-
loglevel=parse_logging_level(loglevel),
|
6484
|
-
pidfile=check_path_with_existing_dir(pidfile),
|
6485
|
-
child_logdir=child_logdir if child_logdir else tempfile.gettempdir(),
|
6486
|
-
**kwargs,
|
6487
|
-
)
|
7039
|
+
@abc.abstractmethod
|
7040
|
+
def give_up(self) -> None:
|
7041
|
+
raise NotImplementedError
|
7042
|
+
|
7043
|
+
@abc.abstractmethod
|
7044
|
+
def transition(self) -> None:
|
7045
|
+
raise NotImplementedError
|
7046
|
+
|
7047
|
+
@property
|
7048
|
+
@abc.abstractmethod
|
7049
|
+
def state(self) -> ProcessState:
|
7050
|
+
raise NotImplementedError
|
7051
|
+
|
7052
|
+
@abc.abstractmethod
|
7053
|
+
def after_setuid(self) -> None:
|
7054
|
+
raise NotImplementedError
|
6488
7055
|
|
6489
7056
|
|
6490
7057
|
##
|
6491
7058
|
|
6492
7059
|
|
6493
|
-
|
6494
|
-
|
6495
|
-
|
6496
|
-
|
7060
|
+
class ProcessGroup(
|
7061
|
+
ConfigPriorityOrdered,
|
7062
|
+
KeyedCollectionAccessors[str, Process],
|
7063
|
+
abc.ABC,
|
7064
|
+
):
|
7065
|
+
@property
|
7066
|
+
@abc.abstractmethod
|
7067
|
+
def name(self) -> str:
|
7068
|
+
raise NotImplementedError
|
6497
7069
|
|
7070
|
+
@property
|
7071
|
+
@abc.abstractmethod
|
7072
|
+
def config(self) -> ProcessGroupConfig:
|
7073
|
+
raise NotImplementedError
|
6498
7074
|
|
6499
|
-
|
6500
|
-
|
6501
|
-
|
6502
|
-
|
6503
|
-
return out
|
7075
|
+
@property
|
7076
|
+
@abc.abstractmethod
|
7077
|
+
def by_name(self) -> ta.Mapping[str, Process]:
|
7078
|
+
raise NotImplementedError
|
6504
7079
|
|
7080
|
+
#
|
6505
7081
|
|
6506
|
-
|
7082
|
+
@abc.abstractmethod
|
7083
|
+
def stop_all(self) -> None:
|
7084
|
+
raise NotImplementedError
|
6507
7085
|
|
7086
|
+
@abc.abstractmethod
|
7087
|
+
def get_unstopped_processes(self) -> ta.List[Process]:
|
7088
|
+
raise NotImplementedError
|
6508
7089
|
|
6509
|
-
|
6510
|
-
|
6511
|
-
|
6512
|
-
s = str(value).lower()
|
6513
|
-
level = logging.getLevelNamesMapping().get(s.upper())
|
6514
|
-
if level is None:
|
6515
|
-
raise ValueError(f'bad logging level name {value!r}')
|
6516
|
-
return level
|
7090
|
+
@abc.abstractmethod
|
7091
|
+
def before_remove(self) -> None:
|
7092
|
+
raise NotImplementedError
|
6517
7093
|
|
6518
7094
|
|
6519
7095
|
########################################
|
@@ -7022,368 +7598,59 @@ class CoroHttpServer:
|
|
7022
7598
|
##
|
7023
7599
|
|
7024
7600
|
|
7025
|
-
class CoroHttpServerSocketHandler(SocketHandler):
|
7026
|
-
def __init__(
|
7027
|
-
self,
|
7028
|
-
client_address: SocketAddress,
|
7029
|
-
rfile: ta.BinaryIO,
|
7030
|
-
wfile: ta.BinaryIO,
|
7031
|
-
*,
|
7032
|
-
server_factory: CoroHttpServerFactory,
|
7033
|
-
log_handler: ta.Optional[ta.Callable[[CoroHttpServer, CoroHttpServer.AnyLogIo], None]] = None,
|
7034
|
-
) -> None:
|
7035
|
-
super().__init__(
|
7036
|
-
client_address,
|
7037
|
-
rfile,
|
7038
|
-
wfile,
|
7039
|
-
)
|
7040
|
-
|
7041
|
-
self._server_factory = server_factory
|
7042
|
-
self._log_handler = log_handler
|
7043
|
-
|
7044
|
-
def handle(self) -> None:
|
7045
|
-
server = self._server_factory(self._client_address)
|
7046
|
-
|
7047
|
-
gen = server.coro_handle()
|
7048
|
-
|
7049
|
-
o = next(gen)
|
7050
|
-
while True:
|
7051
|
-
if isinstance(o, CoroHttpServer.AnyLogIo):
|
7052
|
-
i = None
|
7053
|
-
if self._log_handler is not None:
|
7054
|
-
self._log_handler(server, o)
|
7055
|
-
|
7056
|
-
elif isinstance(o, CoroHttpServer.ReadIo):
|
7057
|
-
i = self._rfile.read(o.sz)
|
7058
|
-
|
7059
|
-
elif isinstance(o, CoroHttpServer.ReadLineIo):
|
7060
|
-
i = self._rfile.readline(o.sz)
|
7061
|
-
|
7062
|
-
elif isinstance(o, CoroHttpServer.WriteIo):
|
7063
|
-
i = None
|
7064
|
-
self._wfile.write(o.data)
|
7065
|
-
self._wfile.flush()
|
7066
|
-
|
7067
|
-
else:
|
7068
|
-
raise TypeError(o)
|
7069
|
-
|
7070
|
-
try:
|
7071
|
-
if i is not None:
|
7072
|
-
o = gen.send(i)
|
7073
|
-
else:
|
7074
|
-
o = next(gen)
|
7075
|
-
except StopIteration:
|
7076
|
-
break
|
7077
|
-
|
7078
|
-
|
7079
|
-
########################################
|
7080
|
-
# ../types.py
|
7081
|
-
|
7082
|
-
|
7083
|
-
##
|
7084
|
-
|
7085
|
-
|
7086
|
-
class ExitNow(Exception): # noqa
|
7087
|
-
pass
|
7088
|
-
|
7089
|
-
|
7090
|
-
ServerEpoch = ta.NewType('ServerEpoch', int)
|
7091
|
-
|
7092
|
-
|
7093
|
-
##
|
7094
|
-
|
7095
|
-
|
7096
|
-
@functools.total_ordering
|
7097
|
-
class ConfigPriorityOrdered(abc.ABC):
|
7098
|
-
@property
|
7099
|
-
@abc.abstractmethod
|
7100
|
-
def config(self) -> ta.Any:
|
7101
|
-
raise NotImplementedError
|
7102
|
-
|
7103
|
-
def __lt__(self, other):
|
7104
|
-
return self.config.priority < other.config.priority
|
7105
|
-
|
7106
|
-
def __eq__(self, other):
|
7107
|
-
return self.config.priority == other.config.priority
|
7108
|
-
|
7109
|
-
|
7110
|
-
##
|
7111
|
-
|
7112
|
-
|
7113
|
-
class SupervisorStateManager(abc.ABC):
|
7114
|
-
@property
|
7115
|
-
@abc.abstractmethod
|
7116
|
-
def state(self) -> SupervisorState:
|
7117
|
-
raise NotImplementedError
|
7118
|
-
|
7119
|
-
@abc.abstractmethod
|
7120
|
-
def set_state(self, state: SupervisorState) -> None:
|
7121
|
-
raise NotImplementedError
|
7122
|
-
|
7123
|
-
|
7124
|
-
##
|
7125
|
-
|
7126
|
-
|
7127
|
-
class HasDispatchers(abc.ABC):
|
7128
|
-
@abc.abstractmethod
|
7129
|
-
def get_dispatchers(self) -> 'Dispatchers':
|
7130
|
-
raise NotImplementedError
|
7131
|
-
|
7132
|
-
|
7133
|
-
class ProcessDispatcher(FdioHandler, abc.ABC):
|
7134
|
-
@property
|
7135
|
-
@abc.abstractmethod
|
7136
|
-
def channel(self) -> ProcessOutputChannel:
|
7137
|
-
raise NotImplementedError
|
7138
|
-
|
7139
|
-
@property
|
7140
|
-
@abc.abstractmethod
|
7141
|
-
def process(self) -> 'Process':
|
7142
|
-
raise NotImplementedError
|
7143
|
-
|
7144
|
-
|
7145
|
-
class ProcessOutputDispatcher(ProcessDispatcher, abc.ABC):
|
7146
|
-
@abc.abstractmethod
|
7147
|
-
def remove_logs(self) -> None:
|
7148
|
-
raise NotImplementedError
|
7149
|
-
|
7150
|
-
@abc.abstractmethod
|
7151
|
-
def reopen_logs(self) -> None:
|
7152
|
-
raise NotImplementedError
|
7153
|
-
|
7154
|
-
|
7155
|
-
class ProcessInputDispatcher(ProcessDispatcher, abc.ABC):
|
7156
|
-
@abc.abstractmethod
|
7157
|
-
def write(self, chars: ta.Union[bytes, str]) -> None:
|
7158
|
-
raise NotImplementedError
|
7159
|
-
|
7160
|
-
@abc.abstractmethod
|
7161
|
-
def flush(self) -> None:
|
7162
|
-
raise NotImplementedError
|
7163
|
-
|
7164
|
-
|
7165
|
-
##
|
7166
|
-
|
7167
|
-
|
7168
|
-
class Process(
|
7169
|
-
ConfigPriorityOrdered,
|
7170
|
-
HasDispatchers,
|
7171
|
-
abc.ABC,
|
7172
|
-
):
|
7173
|
-
@property
|
7174
|
-
@abc.abstractmethod
|
7175
|
-
def name(self) -> str:
|
7176
|
-
raise NotImplementedError
|
7177
|
-
|
7178
|
-
@property
|
7179
|
-
@abc.abstractmethod
|
7180
|
-
def config(self) -> ProcessConfig:
|
7181
|
-
raise NotImplementedError
|
7182
|
-
|
7183
|
-
@property
|
7184
|
-
@abc.abstractmethod
|
7185
|
-
def group(self) -> 'ProcessGroup':
|
7186
|
-
raise NotImplementedError
|
7187
|
-
|
7188
|
-
@property
|
7189
|
-
@abc.abstractmethod
|
7190
|
-
def pid(self) -> Pid:
|
7191
|
-
raise NotImplementedError
|
7192
|
-
|
7193
|
-
#
|
7194
|
-
|
7195
|
-
@abc.abstractmethod
|
7196
|
-
def finish(self, sts: Rc) -> None:
|
7197
|
-
raise NotImplementedError
|
7198
|
-
|
7199
|
-
@abc.abstractmethod
|
7200
|
-
def stop(self) -> ta.Optional[str]:
|
7201
|
-
raise NotImplementedError
|
7202
|
-
|
7203
|
-
@abc.abstractmethod
|
7204
|
-
def give_up(self) -> None:
|
7205
|
-
raise NotImplementedError
|
7206
|
-
|
7207
|
-
@abc.abstractmethod
|
7208
|
-
def transition(self) -> None:
|
7209
|
-
raise NotImplementedError
|
7210
|
-
|
7211
|
-
@property
|
7212
|
-
@abc.abstractmethod
|
7213
|
-
def state(self) -> ProcessState:
|
7214
|
-
raise NotImplementedError
|
7215
|
-
|
7216
|
-
@abc.abstractmethod
|
7217
|
-
def after_setuid(self) -> None:
|
7218
|
-
raise NotImplementedError
|
7219
|
-
|
7220
|
-
|
7221
|
-
##
|
7222
|
-
|
7223
|
-
|
7224
|
-
class ProcessGroup(
|
7225
|
-
ConfigPriorityOrdered,
|
7226
|
-
KeyedCollectionAccessors[str, Process],
|
7227
|
-
abc.ABC,
|
7228
|
-
):
|
7229
|
-
@property
|
7230
|
-
@abc.abstractmethod
|
7231
|
-
def name(self) -> str:
|
7232
|
-
raise NotImplementedError
|
7233
|
-
|
7234
|
-
@property
|
7235
|
-
@abc.abstractmethod
|
7236
|
-
def config(self) -> ProcessGroupConfig:
|
7237
|
-
raise NotImplementedError
|
7238
|
-
|
7239
|
-
@property
|
7240
|
-
@abc.abstractmethod
|
7241
|
-
def by_name(self) -> ta.Mapping[str, Process]:
|
7242
|
-
raise NotImplementedError
|
7243
|
-
|
7244
|
-
#
|
7245
|
-
|
7246
|
-
@abc.abstractmethod
|
7247
|
-
def stop_all(self) -> None:
|
7248
|
-
raise NotImplementedError
|
7249
|
-
|
7250
|
-
@abc.abstractmethod
|
7251
|
-
def get_unstopped_processes(self) -> ta.List[Process]:
|
7252
|
-
raise NotImplementedError
|
7253
|
-
|
7254
|
-
@abc.abstractmethod
|
7255
|
-
def before_remove(self) -> None:
|
7256
|
-
raise NotImplementedError
|
7257
|
-
|
7258
|
-
|
7259
|
-
########################################
|
7260
|
-
# ../../../omlish/http/coro/fdio.py
|
7261
|
-
|
7262
|
-
|
7263
|
-
class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
|
7601
|
+
class CoroHttpServerSocketHandler(SocketHandler):
|
7264
7602
|
def __init__(
|
7265
7603
|
self,
|
7266
|
-
|
7267
|
-
|
7268
|
-
|
7604
|
+
client_address: SocketAddress,
|
7605
|
+
rfile: ta.BinaryIO,
|
7606
|
+
wfile: ta.BinaryIO,
|
7269
7607
|
*,
|
7270
|
-
|
7271
|
-
write_size: int = 0x10000,
|
7608
|
+
server_factory: CoroHttpServerFactory,
|
7272
7609
|
log_handler: ta.Optional[ta.Callable[[CoroHttpServer, CoroHttpServer.AnyLogIo], None]] = None,
|
7273
7610
|
) -> None:
|
7274
|
-
|
7275
|
-
|
7276
|
-
|
7277
|
-
|
7278
|
-
self._handler = handler
|
7279
|
-
self._read_size = read_size
|
7280
|
-
self._write_size = write_size
|
7281
|
-
self._log_handler = log_handler
|
7282
|
-
|
7283
|
-
self._read_buf = ReadableListBuffer()
|
7284
|
-
self._write_buf: IncrementalWriteBuffer | None = None
|
7285
|
-
|
7286
|
-
self._coro_srv = CoroHttpServer(
|
7287
|
-
addr,
|
7288
|
-
handler=self._handler,
|
7611
|
+
super().__init__(
|
7612
|
+
client_address,
|
7613
|
+
rfile,
|
7614
|
+
wfile,
|
7289
7615
|
)
|
7290
|
-
self._srv_coro: ta.Optional[ta.Generator[CoroHttpServer.Io, ta.Optional[bytes], None]] = self._coro_srv.coro_handle() # noqa
|
7291
7616
|
|
7292
|
-
self.
|
7293
|
-
self.
|
7617
|
+
self._server_factory = server_factory
|
7618
|
+
self._log_handler = log_handler
|
7294
7619
|
|
7295
|
-
|
7620
|
+
def handle(self) -> None:
|
7621
|
+
server = self._server_factory(self._client_address)
|
7296
7622
|
|
7297
|
-
|
7298
|
-
coro = check.not_none(self._srv_coro)
|
7623
|
+
gen = server.coro_handle()
|
7299
7624
|
|
7300
|
-
|
7301
|
-
o = self._cur_io
|
7625
|
+
o = next(gen)
|
7302
7626
|
while True:
|
7303
|
-
if o is None:
|
7304
|
-
try:
|
7305
|
-
if d is not None:
|
7306
|
-
o = coro.send(d)
|
7307
|
-
d = None
|
7308
|
-
else:
|
7309
|
-
o = next(coro)
|
7310
|
-
except StopIteration:
|
7311
|
-
self.close()
|
7312
|
-
o = None
|
7313
|
-
break
|
7314
|
-
|
7315
7627
|
if isinstance(o, CoroHttpServer.AnyLogIo):
|
7628
|
+
i = None
|
7316
7629
|
if self._log_handler is not None:
|
7317
|
-
self._log_handler(
|
7318
|
-
o = None
|
7630
|
+
self._log_handler(server, o)
|
7319
7631
|
|
7320
7632
|
elif isinstance(o, CoroHttpServer.ReadIo):
|
7321
|
-
|
7322
|
-
break
|
7323
|
-
o = None
|
7633
|
+
i = self._rfile.read(o.sz)
|
7324
7634
|
|
7325
7635
|
elif isinstance(o, CoroHttpServer.ReadLineIo):
|
7326
|
-
|
7327
|
-
break
|
7328
|
-
o = None
|
7636
|
+
i = self._rfile.readline(o.sz)
|
7329
7637
|
|
7330
7638
|
elif isinstance(o, CoroHttpServer.WriteIo):
|
7331
|
-
|
7332
|
-
self.
|
7333
|
-
|
7639
|
+
i = None
|
7640
|
+
self._wfile.write(o.data)
|
7641
|
+
self._wfile.flush()
|
7334
7642
|
|
7335
7643
|
else:
|
7336
7644
|
raise TypeError(o)
|
7337
7645
|
|
7338
|
-
|
7339
|
-
|
7340
|
-
|
7341
|
-
|
7342
|
-
|
7343
|
-
|
7344
|
-
|
7345
|
-
def writable(self) -> bool:
|
7346
|
-
return self._write_buf is not None
|
7347
|
-
|
7348
|
-
#
|
7349
|
-
|
7350
|
-
def on_readable(self) -> None:
|
7351
|
-
try:
|
7352
|
-
buf = check.not_none(self._sock).recv(self._read_size)
|
7353
|
-
except BlockingIOError:
|
7354
|
-
return
|
7355
|
-
except ConnectionResetError:
|
7356
|
-
self.close()
|
7357
|
-
return
|
7358
|
-
if not buf:
|
7359
|
-
self.close()
|
7360
|
-
return
|
7361
|
-
|
7362
|
-
self._read_buf.feed(buf)
|
7363
|
-
|
7364
|
-
if isinstance(self._cur_io, CoroHttpServer.AnyReadIo):
|
7365
|
-
self._next_io()
|
7366
|
-
|
7367
|
-
def on_writable(self) -> None:
|
7368
|
-
check.isinstance(self._cur_io, CoroHttpServer.WriteIo)
|
7369
|
-
wb = check.not_none(self._write_buf)
|
7370
|
-
while wb.rem > 0:
|
7371
|
-
def send(d: bytes) -> int:
|
7372
|
-
try:
|
7373
|
-
return check.not_none(self._sock).send(d)
|
7374
|
-
except ConnectionResetError:
|
7375
|
-
self.close()
|
7376
|
-
return 0
|
7377
|
-
except BlockingIOError:
|
7378
|
-
return 0
|
7379
|
-
if not wb.write(send):
|
7646
|
+
try:
|
7647
|
+
if i is not None:
|
7648
|
+
o = gen.send(i)
|
7649
|
+
else:
|
7650
|
+
o = next(gen)
|
7651
|
+
except StopIteration:
|
7380
7652
|
break
|
7381
7653
|
|
7382
|
-
if wb.rem < 1:
|
7383
|
-
self._write_buf = None
|
7384
|
-
self._cur_io = None
|
7385
|
-
self._next_io()
|
7386
|
-
|
7387
7654
|
|
7388
7655
|
########################################
|
7389
7656
|
# ../dispatchers.py
|
@@ -8085,6 +8352,135 @@ class SupervisorSetupImpl(SupervisorSetup):
|
|
8085
8352
|
os.umask(self._config.umask)
|
8086
8353
|
|
8087
8354
|
|
8355
|
+
########################################
|
8356
|
+
# ../../../omlish/http/coro/fdio.py
|
8357
|
+
|
8358
|
+
|
8359
|
+
class CoroHttpServerConnectionFdioHandler(SocketFdioHandler):
|
8360
|
+
def __init__(
|
8361
|
+
self,
|
8362
|
+
addr: SocketAddress,
|
8363
|
+
sock: socket.socket,
|
8364
|
+
handler: HttpHandler,
|
8365
|
+
*,
|
8366
|
+
read_size: int = 0x10000,
|
8367
|
+
write_size: int = 0x10000,
|
8368
|
+
log_handler: ta.Optional[ta.Callable[[CoroHttpServer, CoroHttpServer.AnyLogIo], None]] = None,
|
8369
|
+
) -> None:
|
8370
|
+
check.state(not sock.getblocking())
|
8371
|
+
|
8372
|
+
super().__init__(addr, sock)
|
8373
|
+
|
8374
|
+
self._handler = handler
|
8375
|
+
self._read_size = read_size
|
8376
|
+
self._write_size = write_size
|
8377
|
+
self._log_handler = log_handler
|
8378
|
+
|
8379
|
+
self._read_buf = ReadableListBuffer()
|
8380
|
+
self._write_buf: IncrementalWriteBuffer | None = None
|
8381
|
+
|
8382
|
+
self._coro_srv = CoroHttpServer(
|
8383
|
+
addr,
|
8384
|
+
handler=self._handler,
|
8385
|
+
)
|
8386
|
+
self._srv_coro: ta.Optional[ta.Generator[CoroHttpServer.Io, ta.Optional[bytes], None]] = self._coro_srv.coro_handle() # noqa
|
8387
|
+
|
8388
|
+
self._cur_io: CoroHttpServer.Io | None = None
|
8389
|
+
self._next_io()
|
8390
|
+
|
8391
|
+
#
|
8392
|
+
|
8393
|
+
def _next_io(self) -> None: # noqa
|
8394
|
+
coro = check.not_none(self._srv_coro)
|
8395
|
+
|
8396
|
+
d: bytes | None = None
|
8397
|
+
o = self._cur_io
|
8398
|
+
while True:
|
8399
|
+
if o is None:
|
8400
|
+
try:
|
8401
|
+
if d is not None:
|
8402
|
+
o = coro.send(d)
|
8403
|
+
d = None
|
8404
|
+
else:
|
8405
|
+
o = next(coro)
|
8406
|
+
except StopIteration:
|
8407
|
+
self.close()
|
8408
|
+
o = None
|
8409
|
+
break
|
8410
|
+
|
8411
|
+
if isinstance(o, CoroHttpServer.AnyLogIo):
|
8412
|
+
if self._log_handler is not None:
|
8413
|
+
self._log_handler(self._coro_srv, o)
|
8414
|
+
o = None
|
8415
|
+
|
8416
|
+
elif isinstance(o, CoroHttpServer.ReadIo):
|
8417
|
+
if (d := self._read_buf.read(o.sz)) is None:
|
8418
|
+
break
|
8419
|
+
o = None
|
8420
|
+
|
8421
|
+
elif isinstance(o, CoroHttpServer.ReadLineIo):
|
8422
|
+
if (d := self._read_buf.read_until(b'\n')) is None:
|
8423
|
+
break
|
8424
|
+
o = None
|
8425
|
+
|
8426
|
+
elif isinstance(o, CoroHttpServer.WriteIo):
|
8427
|
+
check.none(self._write_buf)
|
8428
|
+
self._write_buf = IncrementalWriteBuffer(o.data, write_size=self._write_size)
|
8429
|
+
break
|
8430
|
+
|
8431
|
+
else:
|
8432
|
+
raise TypeError(o)
|
8433
|
+
|
8434
|
+
self._cur_io = o
|
8435
|
+
|
8436
|
+
#
|
8437
|
+
|
8438
|
+
def readable(self) -> bool:
|
8439
|
+
return True
|
8440
|
+
|
8441
|
+
def writable(self) -> bool:
|
8442
|
+
return self._write_buf is not None
|
8443
|
+
|
8444
|
+
#
|
8445
|
+
|
8446
|
+
def on_readable(self) -> None:
|
8447
|
+
try:
|
8448
|
+
buf = check.not_none(self._sock).recv(self._read_size)
|
8449
|
+
except BlockingIOError:
|
8450
|
+
return
|
8451
|
+
except ConnectionResetError:
|
8452
|
+
self.close()
|
8453
|
+
return
|
8454
|
+
if not buf:
|
8455
|
+
self.close()
|
8456
|
+
return
|
8457
|
+
|
8458
|
+
self._read_buf.feed(buf)
|
8459
|
+
|
8460
|
+
if isinstance(self._cur_io, CoroHttpServer.AnyReadIo):
|
8461
|
+
self._next_io()
|
8462
|
+
|
8463
|
+
def on_writable(self) -> None:
|
8464
|
+
check.isinstance(self._cur_io, CoroHttpServer.WriteIo)
|
8465
|
+
wb = check.not_none(self._write_buf)
|
8466
|
+
while wb.rem > 0:
|
8467
|
+
def send(d: bytes) -> int:
|
8468
|
+
try:
|
8469
|
+
return check.not_none(self._sock).send(d)
|
8470
|
+
except ConnectionResetError:
|
8471
|
+
self.close()
|
8472
|
+
return 0
|
8473
|
+
except BlockingIOError:
|
8474
|
+
return 0
|
8475
|
+
if not wb.write(send):
|
8476
|
+
break
|
8477
|
+
|
8478
|
+
if wb.rem < 1:
|
8479
|
+
self._write_buf = None
|
8480
|
+
self._cur_io = None
|
8481
|
+
self._next_io()
|
8482
|
+
|
8483
|
+
|
8088
8484
|
########################################
|
8089
8485
|
# ../groups.py
|
8090
8486
|
|
@@ -9660,7 +10056,7 @@ def main(
|
|
9660
10056
|
|
9661
10057
|
# if we hup, restart by making a new Supervisor()
|
9662
10058
|
for epoch in itertools.count():
|
9663
|
-
config =
|
10059
|
+
config = load_config_file_obj(
|
9664
10060
|
os.path.expanduser(cf),
|
9665
10061
|
ServerConfig,
|
9666
10062
|
prepare=prepare_server_config,
|