omdev 0.0.0.dev157__py3-none-any.whl → 0.0.0.dev159__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of omdev might be problematic. Click here for more details.

@@ -115,7 +115,7 @@ CallableVersionOperator = ta.Callable[['Version', str], bool]
115
115
  # ../../omlish/argparse/cli.py
116
116
  ArgparseCommandFn = ta.Callable[[], ta.Optional[int]] # ta.TypeAlias
117
117
 
118
- # ../../omlish/lite/subprocesses.py
118
+ # ../../omlish/subprocesses.py
119
119
  SubprocessChannelOption = ta.Literal['pipe', 'stdout', 'devnull'] # ta.TypeAlias
120
120
 
121
121
 
@@ -2328,6 +2328,13 @@ json_dump_compact: ta.Callable[..., bytes] = functools.partial(json.dump, **JSON
2328
2328
  json_dumps_compact: ta.Callable[..., str] = functools.partial(json.dumps, **JSON_COMPACT_KWARGS)
2329
2329
 
2330
2330
 
2331
+ ########################################
2332
+ # ../../../omlish/lite/logs.py
2333
+
2334
+
2335
+ log = logging.getLogger(__name__)
2336
+
2337
+
2331
2338
  ########################################
2332
2339
  # ../../../omlish/lite/reflect.py
2333
2340
 
@@ -2450,6 +2457,116 @@ def format_num_bytes(num_bytes: int) -> str:
2450
2457
  return f'{num_bytes / 1024 ** (len(FORMAT_NUM_BYTES_SUFFIXES) - 1):.2f}{FORMAT_NUM_BYTES_SUFFIXES[-1]}'
2451
2458
 
2452
2459
 
2460
+ ########################################
2461
+ # ../../../omlish/logs/filters.py
2462
+
2463
+
2464
+ class TidLogFilter(logging.Filter):
2465
+ def filter(self, record):
2466
+ record.tid = threading.get_native_id()
2467
+ return True
2468
+
2469
+
2470
+ ########################################
2471
+ # ../../../omlish/logs/proxy.py
2472
+
2473
+
2474
+ class ProxyLogFilterer(logging.Filterer):
2475
+ def __init__(self, underlying: logging.Filterer) -> None: # noqa
2476
+ self._underlying = underlying
2477
+
2478
+ @property
2479
+ def underlying(self) -> logging.Filterer:
2480
+ return self._underlying
2481
+
2482
+ @property
2483
+ def filters(self):
2484
+ return self._underlying.filters
2485
+
2486
+ @filters.setter
2487
+ def filters(self, filters):
2488
+ self._underlying.filters = filters
2489
+
2490
+ def addFilter(self, filter): # noqa
2491
+ self._underlying.addFilter(filter)
2492
+
2493
+ def removeFilter(self, filter): # noqa
2494
+ self._underlying.removeFilter(filter)
2495
+
2496
+ def filter(self, record):
2497
+ return self._underlying.filter(record)
2498
+
2499
+
2500
+ class ProxyLogHandler(ProxyLogFilterer, logging.Handler):
2501
+ def __init__(self, underlying: logging.Handler) -> None: # noqa
2502
+ ProxyLogFilterer.__init__(self, underlying)
2503
+
2504
+ _underlying: logging.Handler
2505
+
2506
+ @property
2507
+ def underlying(self) -> logging.Handler:
2508
+ return self._underlying
2509
+
2510
+ def get_name(self):
2511
+ return self._underlying.get_name()
2512
+
2513
+ def set_name(self, name):
2514
+ self._underlying.set_name(name)
2515
+
2516
+ @property
2517
+ def name(self):
2518
+ return self._underlying.name
2519
+
2520
+ @property
2521
+ def level(self):
2522
+ return self._underlying.level
2523
+
2524
+ @level.setter
2525
+ def level(self, level):
2526
+ self._underlying.level = level
2527
+
2528
+ @property
2529
+ def formatter(self):
2530
+ return self._underlying.formatter
2531
+
2532
+ @formatter.setter
2533
+ def formatter(self, formatter):
2534
+ self._underlying.formatter = formatter
2535
+
2536
+ def createLock(self):
2537
+ self._underlying.createLock()
2538
+
2539
+ def acquire(self):
2540
+ self._underlying.acquire()
2541
+
2542
+ def release(self):
2543
+ self._underlying.release()
2544
+
2545
+ def setLevel(self, level):
2546
+ self._underlying.setLevel(level)
2547
+
2548
+ def format(self, record):
2549
+ return self._underlying.format(record)
2550
+
2551
+ def emit(self, record):
2552
+ self._underlying.emit(record)
2553
+
2554
+ def handle(self, record):
2555
+ return self._underlying.handle(record)
2556
+
2557
+ def setFormatter(self, fmt):
2558
+ self._underlying.setFormatter(fmt)
2559
+
2560
+ def flush(self):
2561
+ self._underlying.flush()
2562
+
2563
+ def close(self):
2564
+ self._underlying.close()
2565
+
2566
+ def handleError(self, record):
2567
+ self._underlying.handleError(record)
2568
+
2569
+
2453
2570
  ########################################
2454
2571
  # ../../cexts/magic.py
2455
2572
 
@@ -3207,6 +3324,83 @@ class SpecifierSet(BaseSpecifier):
3207
3324
  return iter(filtered)
3208
3325
 
3209
3326
 
3327
+ ########################################
3328
+ # ../reqs.py
3329
+ """
3330
+ TODO:
3331
+ - embed pip._internal.req.parse_requirements, add additional env stuff? breaks compat with raw pip
3332
+ """
3333
+
3334
+
3335
+ class RequirementsRewriter:
3336
+ def __init__(
3337
+ self,
3338
+ venv: ta.Optional[str] = None,
3339
+ ) -> None:
3340
+ super().__init__()
3341
+ self._venv = venv
3342
+
3343
+ @cached_nullary
3344
+ def _tmp_dir(self) -> str:
3345
+ return tempfile.mkdtemp('-omlish-reqs')
3346
+
3347
+ VENV_MAGIC = '# @omlish-venv'
3348
+
3349
+ def rewrite_file(self, in_file: str) -> str:
3350
+ with open(in_file) as f:
3351
+ src = f.read()
3352
+
3353
+ in_lines = src.splitlines(keepends=True)
3354
+ out_lines = []
3355
+
3356
+ for l in in_lines:
3357
+ if self.VENV_MAGIC in l:
3358
+ lp, _, rp = l.partition(self.VENV_MAGIC)
3359
+ rp = rp.partition('#')[0]
3360
+ omit = False
3361
+ for v in rp.split():
3362
+ if v[0] == '!':
3363
+ if self._venv is not None and self._venv == v[1:]:
3364
+ omit = True
3365
+ break
3366
+ else:
3367
+ raise NotImplementedError
3368
+
3369
+ if omit:
3370
+ out_lines.append('# OMITTED: ' + l)
3371
+ continue
3372
+
3373
+ out_req = self.rewrite(l.rstrip('\n'), for_file=True)
3374
+ out_lines.append(out_req + '\n')
3375
+
3376
+ out_file = os.path.join(self._tmp_dir(), os.path.basename(in_file))
3377
+ if os.path.exists(out_file):
3378
+ raise Exception(f'file exists: {out_file}')
3379
+
3380
+ with open(out_file, 'w') as f:
3381
+ f.write(''.join(out_lines))
3382
+ log.info('Rewrote requirements file %s to %s', in_file, out_file)
3383
+ return out_file
3384
+
3385
+ def rewrite(self, in_req: str, *, for_file: bool = False) -> str:
3386
+ if in_req.strip().startswith('-r'):
3387
+ l = in_req.strip()
3388
+ lp, _, rp = l.partition(' ')
3389
+ if lp == '-r':
3390
+ inc_in_file, _, rest = rp.partition(' ')
3391
+ else:
3392
+ inc_in_file, rest = lp[2:], rp
3393
+
3394
+ inc_out_file = self.rewrite_file(inc_in_file)
3395
+ if for_file:
3396
+ return ' '.join(['-r ', inc_out_file, rest])
3397
+ else:
3398
+ return '-r' + inc_out_file
3399
+
3400
+ else:
3401
+ return in_req
3402
+
3403
+
3210
3404
  ########################################
3211
3405
  # ../../../omlish/argparse/cli.py
3212
3406
  """
@@ -3482,311 +3676,41 @@ class ArgparseCli:
3482
3676
 
3483
3677
 
3484
3678
  ########################################
3485
- # ../../../omlish/lite/logs.py
3679
+ # ../../../omlish/lite/marshal.py
3486
3680
  """
3487
3681
  TODO:
3488
- - translate json keys
3489
- - debug
3682
+ - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
3683
+ - namedtuple
3684
+ - literals
3685
+ - newtypes?
3490
3686
  """
3491
3687
 
3492
3688
 
3493
- log = logging.getLogger(__name__)
3494
-
3495
-
3496
3689
  ##
3497
3690
 
3498
3691
 
3499
- class TidLogFilter(logging.Filter):
3500
-
3501
- def filter(self, record):
3502
- record.tid = threading.get_native_id()
3503
- return True
3692
+ @dc.dataclass(frozen=True)
3693
+ class ObjMarshalOptions:
3694
+ raw_bytes: bool = False
3695
+ nonstrict_dataclasses: bool = False
3504
3696
 
3505
3697
 
3506
- ##
3698
+ class ObjMarshaler(abc.ABC):
3699
+ @abc.abstractmethod
3700
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3701
+ raise NotImplementedError
3507
3702
 
3703
+ @abc.abstractmethod
3704
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3705
+ raise NotImplementedError
3508
3706
 
3509
- class JsonLogFormatter(logging.Formatter):
3510
3707
 
3511
- KEYS: ta.Mapping[str, bool] = {
3512
- 'name': False,
3513
- 'msg': False,
3514
- 'args': False,
3515
- 'levelname': False,
3516
- 'levelno': False,
3517
- 'pathname': False,
3518
- 'filename': False,
3519
- 'module': False,
3520
- 'exc_info': True,
3521
- 'exc_text': True,
3522
- 'stack_info': True,
3523
- 'lineno': False,
3524
- 'funcName': False,
3525
- 'created': False,
3526
- 'msecs': False,
3527
- 'relativeCreated': False,
3528
- 'thread': False,
3529
- 'threadName': False,
3530
- 'processName': False,
3531
- 'process': False,
3532
- }
3708
+ class NopObjMarshaler(ObjMarshaler):
3709
+ def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3710
+ return o
3533
3711
 
3534
- def format(self, record: logging.LogRecord) -> str:
3535
- dct = {
3536
- k: v
3537
- for k, o in self.KEYS.items()
3538
- for v in [getattr(record, k)]
3539
- if not (o and v is None)
3540
- }
3541
- return json_dumps_compact(dct)
3542
-
3543
-
3544
- ##
3545
-
3546
-
3547
- STANDARD_LOG_FORMAT_PARTS = [
3548
- ('asctime', '%(asctime)-15s'),
3549
- ('process', 'pid=%(process)-6s'),
3550
- ('thread', 'tid=%(thread)x'),
3551
- ('levelname', '%(levelname)s'),
3552
- ('name', '%(name)s'),
3553
- ('separator', '::'),
3554
- ('message', '%(message)s'),
3555
- ]
3556
-
3557
-
3558
- class StandardLogFormatter(logging.Formatter):
3559
-
3560
- @staticmethod
3561
- def build_log_format(parts: ta.Iterable[ta.Tuple[str, str]]) -> str:
3562
- return ' '.join(v for k, v in parts)
3563
-
3564
- converter = datetime.datetime.fromtimestamp # type: ignore
3565
-
3566
- def formatTime(self, record, datefmt=None):
3567
- ct = self.converter(record.created) # type: ignore
3568
- if datefmt:
3569
- return ct.strftime(datefmt) # noqa
3570
- else:
3571
- t = ct.strftime('%Y-%m-%d %H:%M:%S')
3572
- return '%s.%03d' % (t, record.msecs) # noqa
3573
-
3574
-
3575
- ##
3576
-
3577
-
3578
- class ProxyLogFilterer(logging.Filterer):
3579
- def __init__(self, underlying: logging.Filterer) -> None: # noqa
3580
- self._underlying = underlying
3581
-
3582
- @property
3583
- def underlying(self) -> logging.Filterer:
3584
- return self._underlying
3585
-
3586
- @property
3587
- def filters(self):
3588
- return self._underlying.filters
3589
-
3590
- @filters.setter
3591
- def filters(self, filters):
3592
- self._underlying.filters = filters
3593
-
3594
- def addFilter(self, filter): # noqa
3595
- self._underlying.addFilter(filter)
3596
-
3597
- def removeFilter(self, filter): # noqa
3598
- self._underlying.removeFilter(filter)
3599
-
3600
- def filter(self, record):
3601
- return self._underlying.filter(record)
3602
-
3603
-
3604
- class ProxyLogHandler(ProxyLogFilterer, logging.Handler):
3605
- def __init__(self, underlying: logging.Handler) -> None: # noqa
3606
- ProxyLogFilterer.__init__(self, underlying)
3607
-
3608
- _underlying: logging.Handler
3609
-
3610
- @property
3611
- def underlying(self) -> logging.Handler:
3612
- return self._underlying
3613
-
3614
- def get_name(self):
3615
- return self._underlying.get_name()
3616
-
3617
- def set_name(self, name):
3618
- self._underlying.set_name(name)
3619
-
3620
- @property
3621
- def name(self):
3622
- return self._underlying.name
3623
-
3624
- @property
3625
- def level(self):
3626
- return self._underlying.level
3627
-
3628
- @level.setter
3629
- def level(self, level):
3630
- self._underlying.level = level
3631
-
3632
- @property
3633
- def formatter(self):
3634
- return self._underlying.formatter
3635
-
3636
- @formatter.setter
3637
- def formatter(self, formatter):
3638
- self._underlying.formatter = formatter
3639
-
3640
- def createLock(self):
3641
- self._underlying.createLock()
3642
-
3643
- def acquire(self):
3644
- self._underlying.acquire()
3645
-
3646
- def release(self):
3647
- self._underlying.release()
3648
-
3649
- def setLevel(self, level):
3650
- self._underlying.setLevel(level)
3651
-
3652
- def format(self, record):
3653
- return self._underlying.format(record)
3654
-
3655
- def emit(self, record):
3656
- self._underlying.emit(record)
3657
-
3658
- def handle(self, record):
3659
- return self._underlying.handle(record)
3660
-
3661
- def setFormatter(self, fmt):
3662
- self._underlying.setFormatter(fmt)
3663
-
3664
- def flush(self):
3665
- self._underlying.flush()
3666
-
3667
- def close(self):
3668
- self._underlying.close()
3669
-
3670
- def handleError(self, record):
3671
- self._underlying.handleError(record)
3672
-
3673
-
3674
- ##
3675
-
3676
-
3677
- class StandardLogHandler(ProxyLogHandler):
3678
- pass
3679
-
3680
-
3681
- ##
3682
-
3683
-
3684
- @contextlib.contextmanager
3685
- def _locking_logging_module_lock() -> ta.Iterator[None]:
3686
- if hasattr(logging, '_acquireLock'):
3687
- logging._acquireLock() # noqa
3688
- try:
3689
- yield
3690
- finally:
3691
- logging._releaseLock() # type: ignore # noqa
3692
-
3693
- elif hasattr(logging, '_lock'):
3694
- # https://github.com/python/cpython/commit/74723e11109a320e628898817ab449b3dad9ee96
3695
- with logging._lock: # noqa
3696
- yield
3697
-
3698
- else:
3699
- raise Exception("Can't find lock in logging module")
3700
-
3701
-
3702
- def configure_standard_logging(
3703
- level: ta.Union[int, str] = logging.INFO,
3704
- *,
3705
- json: bool = False,
3706
- target: ta.Optional[logging.Logger] = None,
3707
- force: bool = False,
3708
- handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
3709
- ) -> ta.Optional[StandardLogHandler]:
3710
- with _locking_logging_module_lock():
3711
- if target is None:
3712
- target = logging.root
3713
-
3714
- #
3715
-
3716
- if not force:
3717
- if any(isinstance(h, StandardLogHandler) for h in list(target.handlers)):
3718
- return None
3719
-
3720
- #
3721
-
3722
- if handler_factory is not None:
3723
- handler = handler_factory()
3724
- else:
3725
- handler = logging.StreamHandler()
3726
-
3727
- #
3728
-
3729
- formatter: logging.Formatter
3730
- if json:
3731
- formatter = JsonLogFormatter()
3732
- else:
3733
- formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
3734
- handler.setFormatter(formatter)
3735
-
3736
- #
3737
-
3738
- handler.addFilter(TidLogFilter())
3739
-
3740
- #
3741
-
3742
- target.addHandler(handler)
3743
-
3744
- #
3745
-
3746
- if level is not None:
3747
- target.setLevel(level)
3748
-
3749
- #
3750
-
3751
- return StandardLogHandler(handler)
3752
-
3753
-
3754
- ########################################
3755
- # ../../../omlish/lite/marshal.py
3756
- """
3757
- TODO:
3758
- - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
3759
- - namedtuple
3760
- - literals
3761
- - newtypes?
3762
- """
3763
-
3764
-
3765
- ##
3766
-
3767
-
3768
- @dc.dataclass(frozen=True)
3769
- class ObjMarshalOptions:
3770
- raw_bytes: bool = False
3771
- nonstrict_dataclasses: bool = False
3772
-
3773
-
3774
- class ObjMarshaler(abc.ABC):
3775
- @abc.abstractmethod
3776
- def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3777
- raise NotImplementedError
3778
-
3779
- @abc.abstractmethod
3780
- def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3781
- raise NotImplementedError
3782
-
3783
-
3784
- class NopObjMarshaler(ObjMarshaler):
3785
- def marshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3786
- return o
3787
-
3788
- def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3789
- return o
3712
+ def unmarshal(self, o: ta.Any, ctx: 'ObjMarshalContext') -> ta.Any:
3713
+ return o
3790
3714
 
3791
3715
 
3792
3716
  @dc.dataclass()
@@ -4205,12 +4129,66 @@ def is_debugger_attached() -> bool:
4205
4129
  return any(frame[1].endswith('pydevd.py') for frame in inspect.stack())
4206
4130
 
4207
4131
 
4208
- REQUIRED_PYTHON_VERSION = (3, 8)
4132
+ LITE_REQUIRED_PYTHON_VERSION = (3, 8)
4209
4133
 
4210
4134
 
4211
- def check_runtime_version() -> None:
4212
- if sys.version_info < REQUIRED_PYTHON_VERSION:
4213
- raise OSError(f'Requires python {REQUIRED_PYTHON_VERSION}, got {sys.version_info} from {sys.executable}') # noqa
4135
+ def check_lite_runtime_version() -> None:
4136
+ if sys.version_info < LITE_REQUIRED_PYTHON_VERSION:
4137
+ raise OSError(f'Requires python {LITE_REQUIRED_PYTHON_VERSION}, got {sys.version_info} from {sys.executable}') # noqa
4138
+
4139
+
4140
+ ########################################
4141
+ # ../../../omlish/logs/json.py
4142
+ """
4143
+ TODO:
4144
+ - translate json keys
4145
+ """
4146
+
4147
+
4148
+ class JsonLogFormatter(logging.Formatter):
4149
+ KEYS: ta.Mapping[str, bool] = {
4150
+ 'name': False,
4151
+ 'msg': False,
4152
+ 'args': False,
4153
+ 'levelname': False,
4154
+ 'levelno': False,
4155
+ 'pathname': False,
4156
+ 'filename': False,
4157
+ 'module': False,
4158
+ 'exc_info': True,
4159
+ 'exc_text': True,
4160
+ 'stack_info': True,
4161
+ 'lineno': False,
4162
+ 'funcName': False,
4163
+ 'created': False,
4164
+ 'msecs': False,
4165
+ 'relativeCreated': False,
4166
+ 'thread': False,
4167
+ 'threadName': False,
4168
+ 'processName': False,
4169
+ 'process': False,
4170
+ }
4171
+
4172
+ def __init__(
4173
+ self,
4174
+ *args: ta.Any,
4175
+ json_dumps: ta.Optional[ta.Callable[[ta.Any], str]] = None,
4176
+ **kwargs: ta.Any,
4177
+ ) -> None:
4178
+ super().__init__(*args, **kwargs)
4179
+
4180
+ if json_dumps is None:
4181
+ json_dumps = json_dumps_compact
4182
+ self._json_dumps = json_dumps
4183
+
4184
+ def format(self, record: logging.LogRecord) -> str:
4185
+ dct = {
4186
+ k: v
4187
+ for k, o in self.KEYS.items()
4188
+ for v in [getattr(record, k)]
4189
+ if not (o and v is None)
4190
+ }
4191
+ return self._json_dumps(dct)
4214
4192
 
4215
4193
 
4216
4194
  ########################################
@@ -4405,84 +4383,129 @@ class PyprojectConfigPreparer:
4405
4383
 
4406
4384
 
4407
4385
  ########################################
4408
- # ../reqs.py
4386
+ # ../../../omlish/logs/standard.py
4409
4387
  """
4410
4388
  TODO:
4411
- - embed pip._internal.req.parse_requirements, add additional env stuff? breaks compat with raw pip
4389
+ - structured
4390
+ - prefixed
4391
+ - debug
4392
+ - optional noisy? noisy will never be lite - some kinda configure_standard callback mechanism?
4412
4393
  """
4413
4394
 
4414
4395
 
4415
- class RequirementsRewriter:
4416
- def __init__(
4417
- self,
4418
- venv: ta.Optional[str] = None,
4419
- ) -> None:
4420
- super().__init__()
4421
- self._venv = venv
4396
+ ##
4422
4397
 
4423
- @cached_nullary
4424
- def _tmp_dir(self) -> str:
4425
- return tempfile.mkdtemp('-omlish-reqs')
4426
4398
 
4427
- VENV_MAGIC = '# @omlish-venv'
4399
+ STANDARD_LOG_FORMAT_PARTS = [
4400
+ ('asctime', '%(asctime)-15s'),
4401
+ ('process', 'pid=%(process)-6s'),
4402
+ ('thread', 'tid=%(thread)x'),
4403
+ ('levelname', '%(levelname)s'),
4404
+ ('name', '%(name)s'),
4405
+ ('separator', '::'),
4406
+ ('message', '%(message)s'),
4407
+ ]
4428
4408
 
4429
- def rewrite_file(self, in_file: str) -> str:
4430
- with open(in_file) as f:
4431
- src = f.read()
4432
4409
 
4433
- in_lines = src.splitlines(keepends=True)
4434
- out_lines = []
4410
+ class StandardLogFormatter(logging.Formatter):
4411
+ @staticmethod
4412
+ def build_log_format(parts: ta.Iterable[ta.Tuple[str, str]]) -> str:
4413
+ return ' '.join(v for k, v in parts)
4435
4414
 
4436
- for l in in_lines:
4437
- if self.VENV_MAGIC in l:
4438
- lp, _, rp = l.partition(self.VENV_MAGIC)
4439
- rp = rp.partition('#')[0]
4440
- omit = False
4441
- for v in rp.split():
4442
- if v[0] == '!':
4443
- if self._venv is not None and self._venv == v[1:]:
4444
- omit = True
4445
- break
4446
- else:
4447
- raise NotImplementedError
4415
+ converter = datetime.datetime.fromtimestamp # type: ignore
4448
4416
 
4449
- if omit:
4450
- out_lines.append('# OMITTED: ' + l)
4451
- continue
4417
+ def formatTime(self, record, datefmt=None):
4418
+ ct = self.converter(record.created) # type: ignore
4419
+ if datefmt:
4420
+ return ct.strftime(datefmt) # noqa
4421
+ else:
4422
+ t = ct.strftime('%Y-%m-%d %H:%M:%S')
4423
+ return '%s.%03d' % (t, record.msecs) # noqa
4452
4424
 
4453
- out_req = self.rewrite(l.rstrip('\n'), for_file=True)
4454
- out_lines.append(out_req + '\n')
4455
4425
 
4456
- out_file = os.path.join(self._tmp_dir(), os.path.basename(in_file))
4457
- if os.path.exists(out_file):
4458
- raise Exception(f'file exists: {out_file}')
4426
+ ##
4459
4427
 
4460
- with open(out_file, 'w') as f:
4461
- f.write(''.join(out_lines))
4462
- log.info('Rewrote requirements file %s to %s', in_file, out_file)
4463
- return out_file
4464
4428
 
4465
- def rewrite(self, in_req: str, *, for_file: bool = False) -> str:
4466
- if in_req.strip().startswith('-r'):
4467
- l = in_req.strip()
4468
- lp, _, rp = l.partition(' ')
4469
- if lp == '-r':
4470
- inc_in_file, _, rest = rp.partition(' ')
4471
- else:
4472
- inc_in_file, rest = lp[2:], rp
4429
+ class StandardConfiguredLogHandler(ProxyLogHandler):
4430
+ def __init_subclass__(cls, **kwargs):
4431
+ raise TypeError('This class serves only as a marker and should not be subclassed.')
4432
+
4433
+
4434
+ ##
4435
+
4436
+
4437
+ @contextlib.contextmanager
4438
+ def _locking_logging_module_lock() -> ta.Iterator[None]:
4439
+ if hasattr(logging, '_acquireLock'):
4440
+ logging._acquireLock() # noqa
4441
+ try:
4442
+ yield
4443
+ finally:
4444
+ logging._releaseLock() # type: ignore # noqa
4445
+
4446
+ elif hasattr(logging, '_lock'):
4447
+ # https://github.com/python/cpython/commit/74723e11109a320e628898817ab449b3dad9ee96
4448
+ with logging._lock: # noqa
4449
+ yield
4450
+
4451
+ else:
4452
+ raise Exception("Can't find lock in logging module")
4453
+
4454
+
4455
+ def configure_standard_logging(
4456
+ level: ta.Union[int, str] = logging.INFO,
4457
+ *,
4458
+ json: bool = False,
4459
+ target: ta.Optional[logging.Logger] = None,
4460
+ force: bool = False,
4461
+ handler_factory: ta.Optional[ta.Callable[[], logging.Handler]] = None,
4462
+ ) -> ta.Optional[StandardConfiguredLogHandler]:
4463
+ with _locking_logging_module_lock():
4464
+ if target is None:
4465
+ target = logging.root
4466
+
4467
+ #
4468
+
4469
+ if not force:
4470
+ if any(isinstance(h, StandardConfiguredLogHandler) for h in list(target.handlers)):
4471
+ return None
4472
+
4473
+ #
4474
+
4475
+ if handler_factory is not None:
4476
+ handler = handler_factory()
4477
+ else:
4478
+ handler = logging.StreamHandler()
4479
+
4480
+ #
4481
+
4482
+ formatter: logging.Formatter
4483
+ if json:
4484
+ formatter = JsonLogFormatter()
4485
+ else:
4486
+ formatter = StandardLogFormatter(StandardLogFormatter.build_log_format(STANDARD_LOG_FORMAT_PARTS))
4487
+ handler.setFormatter(formatter)
4488
+
4489
+ #
4490
+
4491
+ handler.addFilter(TidLogFilter())
4492
+
4493
+ #
4494
+
4495
+ target.addHandler(handler)
4496
+
4497
+ #
4473
4498
 
4474
- inc_out_file = self.rewrite_file(inc_in_file)
4475
- if for_file:
4476
- return ' '.join(['-r ', inc_out_file, rest])
4477
- else:
4478
- return '-r' + inc_out_file
4499
+ if level is not None:
4500
+ target.setLevel(level)
4479
4501
 
4480
- else:
4481
- return in_req
4502
+ #
4503
+
4504
+ return StandardConfiguredLogHandler(handler)
4482
4505
 
4483
4506
 
4484
4507
  ########################################
4485
- # ../../../omlish/lite/subprocesses.py
4508
+ # ../../../omlish/subprocesses.py
4486
4509
 
4487
4510
 
4488
4511
  ##
@@ -4533,8 +4556,8 @@ def subprocess_close(
4533
4556
  ##
4534
4557
 
4535
4558
 
4536
- class AbstractSubprocesses(abc.ABC): # noqa
4537
- DEFAULT_LOGGER: ta.ClassVar[ta.Optional[logging.Logger]] = log
4559
+ class BaseSubprocesses(abc.ABC): # noqa
4560
+ DEFAULT_LOGGER: ta.ClassVar[ta.Optional[logging.Logger]] = None
4538
4561
 
4539
4562
  def __init__(
4540
4563
  self,
@@ -4547,6 +4570,9 @@ class AbstractSubprocesses(abc.ABC): # noqa
4547
4570
  self._log = log if log is not None else self.DEFAULT_LOGGER
4548
4571
  self._try_exceptions = try_exceptions if try_exceptions is not None else self.DEFAULT_TRY_EXCEPTIONS
4549
4572
 
4573
+ def set_logger(self, log: ta.Optional[logging.Logger]) -> None:
4574
+ self._log = log
4575
+
4550
4576
  #
4551
4577
 
4552
4578
  def prepare_args(
@@ -4658,23 +4684,25 @@ class AbstractSubprocesses(abc.ABC): # noqa
4658
4684
  ##
4659
4685
 
4660
4686
 
4661
- class Subprocesses(AbstractSubprocesses):
4687
+ class AbstractSubprocesses(BaseSubprocesses, abc.ABC):
4688
+ @abc.abstractmethod
4662
4689
  def check_call(
4663
4690
  self,
4664
4691
  *cmd: str,
4665
4692
  stdout: ta.Any = sys.stderr,
4666
4693
  **kwargs: ta.Any,
4667
4694
  ) -> None:
4668
- with self.prepare_and_wrap(*cmd, stdout=stdout, **kwargs) as (cmd, kwargs): # noqa
4669
- subprocess.check_call(cmd, **kwargs)
4695
+ raise NotImplementedError
4670
4696
 
4697
+ @abc.abstractmethod
4671
4698
  def check_output(
4672
4699
  self,
4673
4700
  *cmd: str,
4674
4701
  **kwargs: ta.Any,
4675
4702
  ) -> bytes:
4676
- with self.prepare_and_wrap(*cmd, **kwargs) as (cmd, kwargs): # noqa
4677
- return subprocess.check_output(cmd, **kwargs)
4703
+ raise NotImplementedError
4704
+
4705
+ #
4678
4706
 
4679
4707
  def check_output_str(
4680
4708
  self,
@@ -4716,412 +4744,142 @@ class Subprocesses(AbstractSubprocesses):
4716
4744
  return ret.decode().strip()
4717
4745
 
4718
4746
 
4719
- subprocesses = Subprocesses()
4747
+ ##
4720
4748
 
4721
4749
 
4722
- ########################################
4723
- # ../../git.py
4724
- """
4725
- git status
4726
- --porcelain=v1
4727
- --ignore-submodules
4728
- 2>/dev/null
4729
- """
4750
+ class Subprocesses(AbstractSubprocesses):
4751
+ def check_call(
4752
+ self,
4753
+ *cmd: str,
4754
+ stdout: ta.Any = sys.stderr,
4755
+ **kwargs: ta.Any,
4756
+ ) -> None:
4757
+ with self.prepare_and_wrap(*cmd, stdout=stdout, **kwargs) as (cmd, kwargs): # noqa
4758
+ subprocess.check_call(cmd, **kwargs)
4759
+
4760
+ def check_output(
4761
+ self,
4762
+ *cmd: str,
4763
+ **kwargs: ta.Any,
4764
+ ) -> bytes:
4765
+ with self.prepare_and_wrap(*cmd, **kwargs) as (cmd, kwargs): # noqa
4766
+ return subprocess.check_output(cmd, **kwargs)
4767
+
4768
+
4769
+ subprocesses = Subprocesses()
4730
4770
 
4731
4771
 
4732
4772
  ##
4733
4773
 
4734
4774
 
4735
- def git_clone_subtree(
4736
- *,
4737
- base_dir: str,
4738
- repo_url: str,
4739
- repo_dir: str,
4740
- branch: ta.Optional[str] = None,
4741
- rev: ta.Optional[str] = None,
4742
- repo_subtrees: ta.Sequence[str],
4743
- ) -> None:
4744
- if not bool(branch) ^ bool(rev):
4745
- raise ValueError('must set branch or rev')
4775
+ class AbstractAsyncSubprocesses(BaseSubprocesses):
4776
+ @abc.abstractmethod
4777
+ async def check_call(
4778
+ self,
4779
+ *cmd: str,
4780
+ stdout: ta.Any = sys.stderr,
4781
+ **kwargs: ta.Any,
4782
+ ) -> None:
4783
+ raise NotImplementedError
4784
+
4785
+ @abc.abstractmethod
4786
+ async def check_output(
4787
+ self,
4788
+ *cmd: str,
4789
+ **kwargs: ta.Any,
4790
+ ) -> bytes:
4791
+ raise NotImplementedError
4746
4792
 
4747
- if isinstance(repo_subtrees, str):
4748
- raise TypeError(repo_subtrees)
4793
+ #
4749
4794
 
4750
- git_opts = [
4751
- '-c', 'advice.detachedHead=false',
4752
- ]
4795
+ async def check_output_str(
4796
+ self,
4797
+ *cmd: str,
4798
+ **kwargs: ta.Any,
4799
+ ) -> str:
4800
+ return (await self.check_output(*cmd, **kwargs)).decode().strip()
4753
4801
 
4754
- subprocess.check_call(
4755
- subprocess_maybe_shell_wrap_exec(
4756
- 'git',
4757
- *git_opts,
4758
- 'clone',
4759
- '-n',
4760
- '--depth=1',
4761
- '--filter=tree:0',
4762
- *(['-b', branch] if branch else []),
4763
- '--single-branch',
4764
- repo_url,
4765
- repo_dir,
4766
- ),
4767
- cwd=base_dir,
4768
- )
4802
+ #
4769
4803
 
4770
- rd = os.path.join(base_dir, repo_dir)
4771
- subprocess.check_call(
4772
- subprocess_maybe_shell_wrap_exec(
4773
- 'git',
4774
- *git_opts,
4775
- 'sparse-checkout',
4776
- 'set',
4777
- '--no-cone',
4778
- *repo_subtrees,
4779
- ),
4780
- cwd=rd,
4781
- )
4804
+ async def try_call(
4805
+ self,
4806
+ *cmd: str,
4807
+ **kwargs: ta.Any,
4808
+ ) -> bool:
4809
+ if isinstance(await self.async_try_fn(self.check_call, *cmd, **kwargs), Exception):
4810
+ return False
4811
+ else:
4812
+ return True
4782
4813
 
4783
- subprocess.check_call(
4784
- subprocess_maybe_shell_wrap_exec(
4785
- 'git',
4786
- *git_opts,
4787
- 'checkout',
4788
- *([rev] if rev else []),
4789
- ),
4790
- cwd=rd,
4791
- )
4814
+ async def try_output(
4815
+ self,
4816
+ *cmd: str,
4817
+ **kwargs: ta.Any,
4818
+ ) -> ta.Optional[bytes]:
4819
+ if isinstance(ret := await self.async_try_fn(self.check_output, *cmd, **kwargs), Exception):
4820
+ return None
4821
+ else:
4822
+ return ret
4823
+
4824
+ async def try_output_str(
4825
+ self,
4826
+ *cmd: str,
4827
+ **kwargs: ta.Any,
4828
+ ) -> ta.Optional[str]:
4829
+ if (ret := await self.try_output(*cmd, **kwargs)) is None:
4830
+ return None
4831
+ else:
4832
+ return ret.decode().strip()
4833
+
4834
+
4835
+ ########################################
4836
+ # ../../git/revisions.py
4792
4837
 
4793
4838
 
4794
4839
  def get_git_revision(
4795
4840
  *,
4796
4841
  cwd: ta.Optional[str] = None,
4797
4842
  ) -> ta.Optional[str]:
4798
- subprocess.check_output(subprocess_maybe_shell_wrap_exec('git', '--version'))
4843
+ subprocesses.check_output('git', '--version')
4799
4844
 
4800
4845
  if cwd is None:
4801
4846
  cwd = os.getcwd()
4802
4847
 
4803
4848
  if subprocess.run( # noqa
4804
- subprocess_maybe_shell_wrap_exec(
4805
- 'git',
4806
- 'rev-parse',
4807
- '--is-inside-work-tree',
4808
- ),
4809
- stdout=subprocess.PIPE,
4810
- stderr=subprocess.PIPE,
4849
+ subprocess_maybe_shell_wrap_exec(
4850
+ 'git',
4851
+ 'rev-parse',
4852
+ '--is-inside-work-tree',
4853
+ ),
4854
+ stdout=subprocess.PIPE,
4855
+ stderr=subprocess.PIPE,
4811
4856
  ).returncode:
4812
4857
  return None
4813
4858
 
4814
- has_untracked = bool(subprocess.check_output(subprocess_maybe_shell_wrap_exec(
4859
+ has_untracked = bool(subprocesses.check_output(
4815
4860
  'git',
4816
4861
  'ls-files',
4817
4862
  '.',
4818
4863
  '--exclude-standard',
4819
4864
  '--others',
4820
- ), cwd=cwd).decode().strip())
4865
+ cwd=cwd,
4866
+ ).decode().strip())
4821
4867
 
4822
- dirty_rev = subprocess.check_output(subprocess_maybe_shell_wrap_exec(
4868
+ dirty_rev = subprocesses.check_output(
4823
4869
  'git',
4824
4870
  'describe',
4825
4871
  '--match=NeVeRmAtCh',
4826
4872
  '--always',
4827
4873
  '--abbrev=40',
4828
4874
  '--dirty',
4829
- ), cwd=cwd).decode().strip()
4830
-
4831
- return dirty_rev + ('-untracked' if has_untracked else '')
4832
-
4833
-
4834
- ##
4835
-
4836
-
4837
- _GIT_STATUS_LINE_ESCAPE_CODES: ta.Mapping[str, str] = {
4838
- '\\': '\\',
4839
- '"': '"',
4840
- 'n': '\n',
4841
- 't': '\t',
4842
- }
4843
-
4844
-
4845
- def yield_git_status_line_fields(l: str) -> ta.Iterator[str]:
4846
- def find_any(chars: str, start: int = 0) -> int:
4847
- ret = -1
4848
- for c in chars:
4849
- if (found := l.find(c, start)) >= 0 and (ret < 0 or ret > found):
4850
- ret = found
4851
- return ret
4852
-
4853
- p = 0
4854
- while True:
4855
- if l[p] == '"':
4856
- p += 1
4857
- s = []
4858
- while (n := find_any('\\"', p)) > 0:
4859
- if (c := l[n]) == '\\':
4860
- s.append(l[p:n])
4861
- s.append(_GIT_STATUS_LINE_ESCAPE_CODES[l[n + 1]])
4862
- p = n + 2
4863
- elif c == '"':
4864
- s.append(l[p:n])
4865
- p = n
4866
- break
4867
- else:
4868
- raise ValueError(l)
4869
-
4870
- if l[p] != '"':
4871
- raise ValueError(l)
4872
-
4873
- yield ''.join(s)
4874
-
4875
- p += 1
4876
- if p == len(l):
4877
- return
4878
- elif l[p] != ' ':
4879
- raise ValueError(l)
4880
-
4881
- p += 1
4882
-
4883
- else:
4884
- if (e := l.find(' ', p)) < 0:
4885
- yield l[p:]
4886
- return
4887
-
4888
- yield l[p:e]
4889
- p = e + 1
4890
-
4891
-
4892
- """
4893
- When merge is occurring and was successful, or outside of a merge situation, X shows the status of the index and Y shows
4894
- the status of the working tree:
4895
- -------------------------------------------------
4896
- X Y Meaning
4897
- -------------------------------------------------
4898
- [AMD] not updated
4899
- M [ MTD] updated in index
4900
- T [ MTD] type changed in index
4901
- A [ MTD] added to index
4902
- D deleted from index
4903
- R [ MTD] renamed in index
4904
- C [ MTD] copied in index
4905
- [MTARC] index and work tree matches
4906
- [ MTARC] M work tree changed since index
4907
- [ MTARC] T type changed in work tree since index
4908
- [ MTARC] D deleted in work tree
4909
- R renamed in work tree
4910
- C copied in work tree
4911
-
4912
- When merge conflict has occurred and has not yet been resolved, X and Y show the state introduced by each head of the
4913
- merge, relative to the common ancestor:
4914
- -------------------------------------------------
4915
- X Y Meaning
4916
- -------------------------------------------------
4917
- D D unmerged, both deleted
4918
- A U unmerged, added by us
4919
- U D unmerged, deleted by them
4920
- U A unmerged, added by them
4921
- D U unmerged, deleted by us
4922
- A A unmerged, both added
4923
- U U unmerged, both modified
4924
-
4925
- When path is untracked, X and Y are always the same, since they are unknown to the index:
4926
- -------------------------------------------------
4927
- X Y Meaning
4928
- -------------------------------------------------
4929
- ? ? untracked
4930
- ! ! ignored
4931
-
4932
- Submodules have more state and instead report
4933
-
4934
- - M = the submodule has a different HEAD than recorded in the index
4935
- - m = the submodule has modified content
4936
- - ? = the submodule has untracked files
4937
-
4938
- This is since modified content or untracked files in a submodule cannot be added via git add in the superproject to
4939
- prepare a commit. m and ? are applied recursively. For example if a nested submodule in a submodule contains an
4940
- untracked file, this is reported as ? as well.
4941
- """ # noqa
4942
-
4943
-
4944
- class GitStatusState(enum.Enum):
4945
- UNMODIFIED = ' '
4946
- MODIFIED = 'M'
4947
- FILE_TYPE_CHANGED = 'T'
4948
- ADDED = 'A'
4949
- DELETED = 'D'
4950
- RENAMED = 'R'
4951
- COPIED = 'C'
4952
- UPDATED_BUT_UNMERGED = 'U'
4953
- UNTRACKED = '?'
4954
- IGNORED = '!'
4955
- SUBMODULE_MODIFIED_CONTENT = 'm'
4956
-
4957
-
4958
- _UNMERGED_GIT_STATUS_STATES: ta.FrozenSet[GitStatusState] = frozenset([
4959
- GitStatusState.UPDATED_BUT_UNMERGED,
4960
- ])
4961
-
4962
- _UNMERGED_GIT_STATUS_STATE_PAIRS: ta.FrozenSet[ta.Tuple[GitStatusState, GitStatusState]] = frozenset([
4963
- (GitStatusState.ADDED, GitStatusState.ADDED),
4964
- (GitStatusState.DELETED, GitStatusState.DELETED),
4965
- ])
4966
-
4967
-
4968
- @dc.dataclass(frozen=True)
4969
- class GitStatusItem:
4970
- x: GitStatusState
4971
- y: GitStatusState
4972
-
4973
- a: str
4974
- b: ta.Optional[str]
4975
-
4976
- @property
4977
- def is_unmerged(self) -> bool:
4978
- return (
4979
- self.x in _UNMERGED_GIT_STATUS_STATE_PAIRS or
4980
- self.y in _UNMERGED_GIT_STATUS_STATE_PAIRS or
4981
- (self.x, self.y) in _UNMERGED_GIT_STATUS_STATE_PAIRS
4982
- )
4983
-
4984
- def __repr__(self) -> str:
4985
- return (
4986
- f'{self.__class__.__name__}('
4987
- f'x={self.x.name}, '
4988
- f'y={self.y.name}, '
4989
- f'a={self.a!r}' +
4990
- (f', b={self.b!r}' if self.b is not None else '') +
4991
- ')'
4992
- )
4993
-
4994
-
4995
- def parse_git_status_line(l: str) -> GitStatusItem:
4996
- if len(l) < 3 or l[2] != ' ':
4997
- raise ValueError(l)
4998
- x, y = l[0], l[1]
4999
-
5000
- fields = list(yield_git_status_line_fields(l[3:]))
5001
- if len(fields) == 1:
5002
- a, b = fields[0], None
5003
- elif len(fields) == 3:
5004
- check.state(fields[1] == '->', l)
5005
- a, b = fields[0], fields[2]
5006
- else:
5007
- raise ValueError(l)
5008
-
5009
- return GitStatusItem(
5010
- GitStatusState(x),
5011
- GitStatusState(y),
5012
- a,
5013
- b,
5014
- )
5015
-
5016
-
5017
- class GitStatus(ta.Sequence[GitStatusItem]):
5018
- def __init__(self, lines: ta.Iterable[GitStatusItem]) -> None:
5019
- super().__init__()
5020
-
5021
- self._lst = list(lines)
5022
-
5023
- by_x: ta.Dict[GitStatusState, list[GitStatusItem]] = {}
5024
- by_y: ta.Dict[GitStatusState, list[GitStatusItem]] = {}
5025
-
5026
- by_a: ta.Dict[str, GitStatusItem] = {}
5027
- by_b: ta.Dict[str, GitStatusItem] = {}
5028
-
5029
- for l in self._lst:
5030
- by_x.setdefault(l.x, []).append(l)
5031
- by_y.setdefault(l.y, []).append(l)
5032
-
5033
- if l.a in by_a:
5034
- raise KeyError(l.a)
5035
- by_a[l.a] = l
5036
-
5037
- if l.b is not None:
5038
- if l.b in by_b:
5039
- raise KeyError(l.b)
5040
- by_b[l.b] = l
5041
-
5042
- self._by_x = by_x
5043
- self._by_y = by_y
5044
-
5045
- self._by_a = by_a
5046
- self._by_b = by_b
5047
-
5048
- self._has_unmerged = any(l.is_unmerged for l in self)
5049
-
5050
- #
5051
-
5052
- def __iter__(self) -> ta.Iterator[GitStatusItem]:
5053
- return iter(self._lst)
5054
-
5055
- def __getitem__(self, index):
5056
- return self._lst[index]
5057
-
5058
- def __len__(self) -> int:
5059
- return len(self._lst)
5060
-
5061
- #
5062
-
5063
- @property
5064
- def by_x(self) -> ta.Mapping[GitStatusState, ta.Sequence[GitStatusItem]]:
5065
- return self._by_x
5066
-
5067
- @property
5068
- def by_y(self) -> ta.Mapping[GitStatusState, ta.Sequence[GitStatusItem]]:
5069
- return self._by_y
5070
-
5071
- @property
5072
- def by_a(self) -> ta.Mapping[str, GitStatusItem]:
5073
- return self._by_a
5074
-
5075
- @property
5076
- def by_b(self) -> ta.Mapping[str, GitStatusItem]:
5077
- return self._by_b
5078
-
5079
- #
5080
-
5081
- @property
5082
- def has_unmerged(self) -> bool:
5083
- return self._has_unmerged
5084
-
5085
- @property
5086
- def has_staged(self) -> bool:
5087
- return any(l.x != GitStatusState.UNMODIFIED for l in self._lst)
5088
-
5089
- @property
5090
- def has_dirty(self) -> bool:
5091
- return any(l.y != GitStatusState.UNMODIFIED for l in self._lst)
5092
-
5093
-
5094
- def parse_git_status(s: str) -> GitStatus:
5095
- return GitStatus(parse_git_status_line(l) for l in s.splitlines())
5096
-
5097
-
5098
- def get_git_status(
5099
- *,
5100
- cwd: ta.Optional[str] = None,
5101
- ignore_submodules: bool = False,
5102
- verbose: bool = False,
5103
- ) -> GitStatus:
5104
- if cwd is None:
5105
- cwd = os.getcwd()
5106
-
5107
- proc = subprocess.run( # type: ignore
5108
- subprocess_maybe_shell_wrap_exec(
5109
- 'git',
5110
- 'status',
5111
- '--porcelain=v1',
5112
- *(['--ignore-submodules'] if ignore_submodules else []),
5113
- ),
5114
4875
  cwd=cwd,
5115
- stdout=subprocess.PIPE,
5116
- **(dict(stderr=subprocess.PIPE) if not verbose else {}),
5117
- check=True,
5118
- )
4876
+ ).decode().strip()
5119
4877
 
5120
- return parse_git_status(proc.stdout.decode()) # noqa
4878
+ return dirty_rev + ('-untracked' if has_untracked else '')
5121
4879
 
5122
4880
 
5123
4881
  ########################################
5124
- # ../../../omlish/lite/asyncio/subprocesses.py
4882
+ # ../../../omlish/asyncs/asyncio/subprocesses.py
5125
4883
 
5126
4884
 
5127
4885
  ##
@@ -5132,6 +4890,8 @@ class AsyncioProcessCommunicator:
5132
4890
  self,
5133
4891
  proc: asyncio.subprocess.Process,
5134
4892
  loop: ta.Optional[ta.Any] = None,
4893
+ *,
4894
+ log: ta.Optional[logging.Logger] = None,
5135
4895
  ) -> None:
5136
4896
  super().__init__()
5137
4897
 
@@ -5140,6 +4900,7 @@ class AsyncioProcessCommunicator:
5140
4900
 
5141
4901
  self._proc = proc
5142
4902
  self._loop = loop
4903
+ self._log = log
5143
4904
 
5144
4905
  self._transport: asyncio.base_subprocess.BaseSubprocessTransport = check.isinstance(
5145
4906
  proc._transport, # type: ignore # noqa
@@ -5155,19 +4916,19 @@ class AsyncioProcessCommunicator:
5155
4916
  try:
5156
4917
  if input is not None:
5157
4918
  stdin.write(input)
5158
- if self._debug:
5159
- log.debug('%r communicate: feed stdin (%s bytes)', self, len(input))
4919
+ if self._debug and self._log is not None:
4920
+ self._log.debug('%r communicate: feed stdin (%s bytes)', self, len(input))
5160
4921
 
5161
4922
  await stdin.drain()
5162
4923
 
5163
4924
  except (BrokenPipeError, ConnectionResetError) as exc:
5164
4925
  # communicate() ignores BrokenPipeError and ConnectionResetError. write() and drain() can raise these
5165
4926
  # exceptions.
5166
- if self._debug:
5167
- log.debug('%r communicate: stdin got %r', self, exc)
4927
+ if self._debug and self._log is not None:
4928
+ self._log.debug('%r communicate: stdin got %r', self, exc)
5168
4929
 
5169
- if self._debug:
5170
- log.debug('%r communicate: close stdin', self)
4930
+ if self._debug and self._log is not None:
4931
+ self._log.debug('%r communicate: close stdin', self)
5171
4932
 
5172
4933
  stdin.close()
5173
4934
 
@@ -5183,15 +4944,15 @@ class AsyncioProcessCommunicator:
5183
4944
  check.equal(fd, 1)
5184
4945
  stream = check.not_none(self._proc.stdout)
5185
4946
 
5186
- if self._debug:
4947
+ if self._debug and self._log is not None:
5187
4948
  name = 'stdout' if fd == 1 else 'stderr'
5188
- log.debug('%r communicate: read %s', self, name)
4949
+ self._log.debug('%r communicate: read %s', self, name)
5189
4950
 
5190
4951
  output = await stream.read()
5191
4952
 
5192
- if self._debug:
4953
+ if self._debug and self._log is not None:
5193
4954
  name = 'stdout' if fd == 1 else 'stderr'
5194
- log.debug('%r communicate: close %s', self, name)
4955
+ self._log.debug('%r communicate: close %s', self, name)
5195
4956
 
5196
4957
  transport.close()
5197
4958
 
@@ -5240,7 +5001,7 @@ class AsyncioProcessCommunicator:
5240
5001
  ##
5241
5002
 
5242
5003
 
5243
- class AsyncioSubprocesses(AbstractSubprocesses):
5004
+ class AsyncioSubprocesses(AbstractAsyncSubprocesses):
5244
5005
  async def communicate(
5245
5006
  self,
5246
5007
  proc: asyncio.subprocess.Process,
@@ -5337,45 +5098,6 @@ class AsyncioSubprocesses(AbstractSubprocesses):
5337
5098
  with self.prepare_and_wrap(*cmd, stdout=subprocess.PIPE, check=True, **kwargs) as (cmd, kwargs): # noqa
5338
5099
  return check.not_none((await self.run(*cmd, **kwargs)).stdout)
5339
5100
 
5340
- async def check_output_str(
5341
- self,
5342
- *cmd: str,
5343
- **kwargs: ta.Any,
5344
- ) -> str:
5345
- return (await self.check_output(*cmd, **kwargs)).decode().strip()
5346
-
5347
- #
5348
-
5349
- async def try_call(
5350
- self,
5351
- *cmd: str,
5352
- **kwargs: ta.Any,
5353
- ) -> bool:
5354
- if isinstance(await self.async_try_fn(self.check_call, *cmd, **kwargs), Exception):
5355
- return False
5356
- else:
5357
- return True
5358
-
5359
- async def try_output(
5360
- self,
5361
- *cmd: str,
5362
- **kwargs: ta.Any,
5363
- ) -> ta.Optional[bytes]:
5364
- if isinstance(ret := await self.async_try_fn(self.check_output, *cmd, **kwargs), Exception):
5365
- return None
5366
- else:
5367
- return ret
5368
-
5369
- async def try_output_str(
5370
- self,
5371
- *cmd: str,
5372
- **kwargs: ta.Any,
5373
- ) -> ta.Optional[str]:
5374
- if (ret := await self.try_output(*cmd, **kwargs)) is None:
5375
- return None
5376
- else:
5377
- return ret.decode().strip()
5378
-
5379
5101
 
5380
5102
  asyncio_subprocesses = AsyncioSubprocesses()
5381
5103
 
@@ -7215,7 +6937,7 @@ class PyprojectCli(ArgparseCli):
7215
6937
 
7216
6938
 
7217
6939
  async def _async_main(argv: ta.Optional[ta.Sequence[str]] = None) -> None:
7218
- check_runtime_version()
6940
+ check_lite_runtime_version()
7219
6941
  configure_standard_logging()
7220
6942
 
7221
6943
  await PyprojectCli(argv).async_cli_run()