ominfra 0.0.0.dev193__py3-none-any.whl → 0.0.0.dev195__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.
- 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.dev195.dist-info}/METADATA +4 -4
- {ominfra-0.0.0.dev193.dist-info → ominfra-0.0.0.dev195.dist-info}/RECORD +14 -15
- ominfra/configs.py +0 -102
- {ominfra-0.0.0.dev193.dist-info → ominfra-0.0.0.dev195.dist-info}/LICENSE +0 -0
- {ominfra-0.0.0.dev193.dist-info → ominfra-0.0.0.dev195.dist-info}/WHEEL +0 -0
- {ominfra-0.0.0.dev193.dist-info → ominfra-0.0.0.dev195.dist-info}/entry_points.txt +0 -0
- {ominfra-0.0.0.dev193.dist-info → ominfra-0.0.0.dev195.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,
|