omdev 0.0.0.dev155__py3-none-any.whl → 0.0.0.dev157__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.
@@ -1823,8 +1823,8 @@ class _CachedNullary(_AbstractCachedNullary):
1823
1823
  return self._value
1824
1824
 
1825
1825
 
1826
- def cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
1827
- return _CachedNullary(fn)
1826
+ def cached_nullary(fn: CallableT) -> CallableT:
1827
+ return _CachedNullary(fn) # type: ignore
1828
1828
 
1829
1829
 
1830
1830
  def static_init(fn: CallableT) -> CallableT:
@@ -3316,6 +3316,8 @@ def _get_argparse_arg_ann_kwargs(ann: ta.Any) -> ta.Mapping[str, ta.Any]:
3316
3316
  return {'action': 'store_true'}
3317
3317
  elif ann is list:
3318
3318
  return {'action': 'append'}
3319
+ elif is_optional_alias(ann):
3320
+ return _get_argparse_arg_ann_kwargs(get_optional_alias_arg(ann))
3319
3321
  else:
3320
3322
  raise TypeError(ann)
3321
3323
 
@@ -3756,6 +3758,7 @@ TODO:
3756
3758
  - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
3757
3759
  - namedtuple
3758
3760
  - literals
3761
+ - newtypes?
3759
3762
  """
3760
3763
 
3761
3764
 
@@ -4498,168 +4501,222 @@ SUBPROCESS_CHANNEL_OPTION_VALUES: ta.Mapping[SubprocessChannelOption, int] = {
4498
4501
  _SUBPROCESS_SHELL_WRAP_EXECS = False
4499
4502
 
4500
4503
 
4501
- def subprocess_shell_wrap_exec(*args: str) -> ta.Tuple[str, ...]:
4502
- return ('sh', '-c', ' '.join(map(shlex.quote, args)))
4504
+ def subprocess_shell_wrap_exec(*cmd: str) -> ta.Tuple[str, ...]:
4505
+ return ('sh', '-c', ' '.join(map(shlex.quote, cmd)))
4503
4506
 
4504
4507
 
4505
- def subprocess_maybe_shell_wrap_exec(*args: str) -> ta.Tuple[str, ...]:
4508
+ def subprocess_maybe_shell_wrap_exec(*cmd: str) -> ta.Tuple[str, ...]:
4506
4509
  if _SUBPROCESS_SHELL_WRAP_EXECS or is_debugger_attached():
4507
- return subprocess_shell_wrap_exec(*args)
4510
+ return subprocess_shell_wrap_exec(*cmd)
4508
4511
  else:
4509
- return args
4512
+ return cmd
4510
4513
 
4511
4514
 
4512
- def prepare_subprocess_invocation(
4513
- *args: str,
4514
- env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
4515
- extra_env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
4516
- quiet: bool = False,
4517
- shell: bool = False,
4518
- **kwargs: ta.Any,
4519
- ) -> ta.Tuple[ta.Tuple[ta.Any, ...], ta.Dict[str, ta.Any]]:
4520
- log.debug('prepare_subprocess_invocation: args=%r', args)
4521
- if extra_env:
4522
- log.debug('prepare_subprocess_invocation: extra_env=%r', extra_env)
4523
-
4524
- if extra_env:
4525
- env = {**(env if env is not None else os.environ), **extra_env}
4515
+ ##
4526
4516
 
4527
- if quiet and 'stderr' not in kwargs:
4528
- if not log.isEnabledFor(logging.DEBUG):
4529
- kwargs['stderr'] = subprocess.DEVNULL
4530
4517
 
4531
- if not shell:
4532
- args = subprocess_maybe_shell_wrap_exec(*args)
4518
+ def subprocess_close(
4519
+ proc: subprocess.Popen,
4520
+ timeout: ta.Optional[float] = None,
4521
+ ) -> None:
4522
+ # TODO: terminate, sleep, kill
4523
+ if proc.stdout:
4524
+ proc.stdout.close()
4525
+ if proc.stderr:
4526
+ proc.stderr.close()
4527
+ if proc.stdin:
4528
+ proc.stdin.close()
4533
4529
 
4534
- return args, dict(
4535
- env=env,
4536
- shell=shell,
4537
- **kwargs,
4538
- )
4530
+ proc.wait(timeout)
4539
4531
 
4540
4532
 
4541
4533
  ##
4542
4534
 
4543
4535
 
4544
- @contextlib.contextmanager
4545
- def subprocess_common_context(*args: ta.Any, **kwargs: ta.Any) -> ta.Iterator[None]:
4546
- start_time = time.time()
4547
- try:
4548
- log.debug('subprocess_common_context.try: args=%r', args)
4549
- yield
4536
+ class AbstractSubprocesses(abc.ABC): # noqa
4537
+ DEFAULT_LOGGER: ta.ClassVar[ta.Optional[logging.Logger]] = log
4550
4538
 
4551
- except Exception as exc: # noqa
4552
- log.debug('subprocess_common_context.except: exc=%r', exc)
4553
- raise
4539
+ def __init__(
4540
+ self,
4541
+ *,
4542
+ log: ta.Optional[logging.Logger] = None,
4543
+ try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
4544
+ ) -> None:
4545
+ super().__init__()
4554
4546
 
4555
- finally:
4556
- end_time = time.time()
4557
- elapsed_s = end_time - start_time
4558
- log.debug('subprocess_common_context.finally: elapsed_s=%f args=%r', elapsed_s, args)
4547
+ self._log = log if log is not None else self.DEFAULT_LOGGER
4548
+ self._try_exceptions = try_exceptions if try_exceptions is not None else self.DEFAULT_TRY_EXCEPTIONS
4559
4549
 
4550
+ #
4560
4551
 
4561
- ##
4552
+ def prepare_args(
4553
+ self,
4554
+ *cmd: str,
4555
+ env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
4556
+ extra_env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
4557
+ quiet: bool = False,
4558
+ shell: bool = False,
4559
+ **kwargs: ta.Any,
4560
+ ) -> ta.Tuple[ta.Tuple[ta.Any, ...], ta.Dict[str, ta.Any]]:
4561
+ if self._log:
4562
+ self._log.debug('Subprocesses.prepare_args: cmd=%r', cmd)
4563
+ if extra_env:
4564
+ self._log.debug('Subprocesses.prepare_args: extra_env=%r', extra_env)
4562
4565
 
4566
+ if extra_env:
4567
+ env = {**(env if env is not None else os.environ), **extra_env}
4563
4568
 
4564
- def subprocess_check_call(
4565
- *args: str,
4566
- stdout: ta.Any = sys.stderr,
4567
- **kwargs: ta.Any,
4568
- ) -> None:
4569
- args, kwargs = prepare_subprocess_invocation(*args, stdout=stdout, **kwargs)
4570
- with subprocess_common_context(*args, **kwargs):
4571
- return subprocess.check_call(args, **kwargs) # type: ignore
4569
+ if quiet and 'stderr' not in kwargs:
4570
+ if self._log and not self._log.isEnabledFor(logging.DEBUG):
4571
+ kwargs['stderr'] = subprocess.DEVNULL
4572
4572
 
4573
+ if not shell:
4574
+ cmd = subprocess_maybe_shell_wrap_exec(*cmd)
4573
4575
 
4574
- def subprocess_check_output(
4575
- *args: str,
4576
- **kwargs: ta.Any,
4577
- ) -> bytes:
4578
- args, kwargs = prepare_subprocess_invocation(*args, **kwargs)
4579
- with subprocess_common_context(*args, **kwargs):
4580
- return subprocess.check_output(args, **kwargs)
4576
+ return cmd, dict(
4577
+ env=env,
4578
+ shell=shell,
4579
+ **kwargs,
4580
+ )
4581
4581
 
4582
+ @contextlib.contextmanager
4583
+ def wrap_call(self, *cmd: ta.Any, **kwargs: ta.Any) -> ta.Iterator[None]:
4584
+ start_time = time.time()
4585
+ try:
4586
+ if self._log:
4587
+ self._log.debug('Subprocesses.wrap_call.try: cmd=%r', cmd)
4588
+ yield
4582
4589
 
4583
- def subprocess_check_output_str(*args: str, **kwargs: ta.Any) -> str:
4584
- return subprocess_check_output(*args, **kwargs).decode().strip()
4590
+ except Exception as exc: # noqa
4591
+ if self._log:
4592
+ self._log.debug('Subprocesses.wrap_call.except: exc=%r', exc)
4593
+ raise
4585
4594
 
4595
+ finally:
4596
+ end_time = time.time()
4597
+ elapsed_s = end_time - start_time
4598
+ if self._log:
4599
+ self._log.debug('sSubprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
4586
4600
 
4587
- ##
4601
+ @contextlib.contextmanager
4602
+ def prepare_and_wrap(
4603
+ self,
4604
+ *cmd: ta.Any,
4605
+ **kwargs: ta.Any,
4606
+ ) -> ta.Iterator[ta.Tuple[
4607
+ ta.Tuple[ta.Any, ...],
4608
+ ta.Dict[str, ta.Any],
4609
+ ]]:
4610
+ cmd, kwargs = self.prepare_args(*cmd, **kwargs)
4611
+ with self.wrap_call(*cmd, **kwargs):
4612
+ yield cmd, kwargs
4588
4613
 
4614
+ #
4589
4615
 
4590
- DEFAULT_SUBPROCESS_TRY_EXCEPTIONS: ta.Tuple[ta.Type[Exception], ...] = (
4591
- FileNotFoundError,
4592
- subprocess.CalledProcessError,
4593
- )
4616
+ DEFAULT_TRY_EXCEPTIONS: ta.Tuple[ta.Type[Exception], ...] = (
4617
+ FileNotFoundError,
4618
+ subprocess.CalledProcessError,
4619
+ )
4594
4620
 
4621
+ def try_fn(
4622
+ self,
4623
+ fn: ta.Callable[..., T],
4624
+ *cmd: str,
4625
+ try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
4626
+ **kwargs: ta.Any,
4627
+ ) -> ta.Union[T, Exception]:
4628
+ if try_exceptions is None:
4629
+ try_exceptions = self._try_exceptions
4595
4630
 
4596
- def _subprocess_try_run(
4597
- fn: ta.Callable[..., T],
4598
- *args: ta.Any,
4599
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
4600
- **kwargs: ta.Any,
4601
- ) -> ta.Union[T, Exception]:
4602
- try:
4603
- return fn(*args, **kwargs)
4604
- except try_exceptions as e: # noqa
4605
- if log.isEnabledFor(logging.DEBUG):
4606
- log.exception('command failed')
4607
- return e
4631
+ try:
4632
+ return fn(*cmd, **kwargs)
4608
4633
 
4634
+ except try_exceptions as e: # noqa
4635
+ if self._log and self._log.isEnabledFor(logging.DEBUG):
4636
+ self._log.exception('command failed')
4637
+ return e
4609
4638
 
4610
- def subprocess_try_call(
4611
- *args: str,
4612
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
4613
- **kwargs: ta.Any,
4614
- ) -> bool:
4615
- if isinstance(_subprocess_try_run(
4616
- subprocess_check_call,
4617
- *args,
4618
- try_exceptions=try_exceptions,
4619
- **kwargs,
4620
- ), Exception):
4621
- return False
4622
- else:
4623
- return True
4639
+ async def async_try_fn(
4640
+ self,
4641
+ fn: ta.Callable[..., ta.Awaitable[T]],
4642
+ *cmd: ta.Any,
4643
+ try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
4644
+ **kwargs: ta.Any,
4645
+ ) -> ta.Union[T, Exception]:
4646
+ if try_exceptions is None:
4647
+ try_exceptions = self._try_exceptions
4624
4648
 
4649
+ try:
4650
+ return await fn(*cmd, **kwargs)
4625
4651
 
4626
- def subprocess_try_output(
4627
- *args: str,
4628
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
4629
- **kwargs: ta.Any,
4630
- ) -> ta.Optional[bytes]:
4631
- if isinstance(ret := _subprocess_try_run(
4632
- subprocess_check_output,
4633
- *args,
4634
- try_exceptions=try_exceptions,
4635
- **kwargs,
4636
- ), Exception):
4637
- return None
4638
- else:
4639
- return ret
4652
+ except try_exceptions as e: # noqa
4653
+ if self._log and self._log.isEnabledFor(logging.DEBUG):
4654
+ self._log.exception('command failed')
4655
+ return e
4640
4656
 
4641
4657
 
4642
- def subprocess_try_output_str(*args: str, **kwargs: ta.Any) -> ta.Optional[str]:
4643
- out = subprocess_try_output(*args, **kwargs)
4644
- return out.decode().strip() if out is not None else None
4658
+ ##
4645
4659
 
4646
4660
 
4647
- ##
4661
+ class Subprocesses(AbstractSubprocesses):
4662
+ def check_call(
4663
+ self,
4664
+ *cmd: str,
4665
+ stdout: ta.Any = sys.stderr,
4666
+ **kwargs: ta.Any,
4667
+ ) -> None:
4668
+ with self.prepare_and_wrap(*cmd, stdout=stdout, **kwargs) as (cmd, kwargs): # noqa
4669
+ subprocess.check_call(cmd, **kwargs)
4648
4670
 
4671
+ def check_output(
4672
+ self,
4673
+ *cmd: str,
4674
+ **kwargs: ta.Any,
4675
+ ) -> bytes:
4676
+ with self.prepare_and_wrap(*cmd, **kwargs) as (cmd, kwargs): # noqa
4677
+ return subprocess.check_output(cmd, **kwargs)
4649
4678
 
4650
- def subprocess_close(
4651
- proc: subprocess.Popen,
4652
- timeout: ta.Optional[float] = None,
4653
- ) -> None:
4654
- # TODO: terminate, sleep, kill
4655
- if proc.stdout:
4656
- proc.stdout.close()
4657
- if proc.stderr:
4658
- proc.stderr.close()
4659
- if proc.stdin:
4660
- proc.stdin.close()
4679
+ def check_output_str(
4680
+ self,
4681
+ *cmd: str,
4682
+ **kwargs: ta.Any,
4683
+ ) -> str:
4684
+ return self.check_output(*cmd, **kwargs).decode().strip()
4661
4685
 
4662
- proc.wait(timeout)
4686
+ #
4687
+
4688
+ def try_call(
4689
+ self,
4690
+ *cmd: str,
4691
+ **kwargs: ta.Any,
4692
+ ) -> bool:
4693
+ if isinstance(self.try_fn(self.check_call, *cmd, **kwargs), Exception):
4694
+ return False
4695
+ else:
4696
+ return True
4697
+
4698
+ def try_output(
4699
+ self,
4700
+ *cmd: str,
4701
+ **kwargs: ta.Any,
4702
+ ) -> ta.Optional[bytes]:
4703
+ if isinstance(ret := self.try_fn(self.check_output, *cmd, **kwargs), Exception):
4704
+ return None
4705
+ else:
4706
+ return ret
4707
+
4708
+ def try_output_str(
4709
+ self,
4710
+ *cmd: str,
4711
+ **kwargs: ta.Any,
4712
+ ) -> ta.Optional[str]:
4713
+ if (ret := self.try_output(*cmd, **kwargs)) is None:
4714
+ return None
4715
+ else:
4716
+ return ret.decode().strip()
4717
+
4718
+
4719
+ subprocesses = Subprocesses()
4663
4720
 
4664
4721
 
4665
4722
  ########################################
@@ -5070,43 +5127,6 @@ def get_git_status(
5070
5127
  ##
5071
5128
 
5072
5129
 
5073
- @contextlib.asynccontextmanager
5074
- async def asyncio_subprocess_popen(
5075
- *cmd: str,
5076
- shell: bool = False,
5077
- timeout: ta.Optional[float] = None,
5078
- **kwargs: ta.Any,
5079
- ) -> ta.AsyncGenerator[asyncio.subprocess.Process, None]:
5080
- fac: ta.Any
5081
- if shell:
5082
- fac = functools.partial(
5083
- asyncio.create_subprocess_shell,
5084
- check.single(cmd),
5085
- )
5086
- else:
5087
- fac = functools.partial(
5088
- asyncio.create_subprocess_exec,
5089
- *cmd,
5090
- )
5091
-
5092
- with subprocess_common_context(
5093
- *cmd,
5094
- shell=shell,
5095
- timeout=timeout,
5096
- **kwargs,
5097
- ):
5098
- proc: asyncio.subprocess.Process
5099
- proc = await fac(**kwargs)
5100
- try:
5101
- yield proc
5102
-
5103
- finally:
5104
- await asyncio_maybe_timeout(proc.wait(), timeout)
5105
-
5106
-
5107
- ##
5108
-
5109
-
5110
5130
  class AsyncioProcessCommunicator:
5111
5131
  def __init__(
5112
5132
  self,
@@ -5217,148 +5237,147 @@ class AsyncioProcessCommunicator:
5217
5237
  return await asyncio_maybe_timeout(self._communicate(input), timeout)
5218
5238
 
5219
5239
 
5220
- async def asyncio_subprocess_communicate(
5221
- proc: asyncio.subprocess.Process,
5222
- input: ta.Any = None, # noqa
5223
- timeout: ta.Optional[float] = None,
5224
- ) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
5225
- return await AsyncioProcessCommunicator(proc).communicate(input, timeout) # noqa
5226
-
5227
-
5228
- @dc.dataclass(frozen=True)
5229
- class AsyncioSubprocessOutput:
5230
- proc: asyncio.subprocess.Process
5231
- stdout: ta.Optional[bytes]
5232
- stderr: ta.Optional[bytes]
5233
-
5234
-
5235
- async def asyncio_subprocess_run(
5236
- *args: str,
5237
- input: ta.Any = None, # noqa
5238
- timeout: ta.Optional[float] = None,
5239
- check: bool = False, # noqa
5240
- capture_output: ta.Optional[bool] = None,
5241
- **kwargs: ta.Any,
5242
- ) -> AsyncioSubprocessOutput:
5243
- if capture_output:
5244
- kwargs.setdefault('stdout', subprocess.PIPE)
5245
- kwargs.setdefault('stderr', subprocess.PIPE)
5246
-
5247
- args, kwargs = prepare_subprocess_invocation(*args, **kwargs)
5248
-
5249
- proc: asyncio.subprocess.Process
5250
- async with asyncio_subprocess_popen(*args, **kwargs) as proc:
5251
- stdout, stderr = await asyncio_subprocess_communicate(proc, input, timeout)
5240
+ ##
5252
5241
 
5253
- if check and proc.returncode:
5254
- raise subprocess.CalledProcessError(
5255
- proc.returncode,
5256
- args,
5257
- output=stdout,
5258
- stderr=stderr,
5259
- )
5260
5242
 
5261
- return AsyncioSubprocessOutput(
5262
- proc,
5263
- stdout,
5264
- stderr,
5265
- )
5243
+ class AsyncioSubprocesses(AbstractSubprocesses):
5244
+ async def communicate(
5245
+ self,
5246
+ proc: asyncio.subprocess.Process,
5247
+ input: ta.Any = None, # noqa
5248
+ timeout: ta.Optional[float] = None,
5249
+ ) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
5250
+ return await AsyncioProcessCommunicator(proc).communicate(input, timeout) # noqa
5266
5251
 
5252
+ #
5267
5253
 
5268
- ##
5254
+ @contextlib.asynccontextmanager
5255
+ async def popen(
5256
+ self,
5257
+ *cmd: str,
5258
+ shell: bool = False,
5259
+ timeout: ta.Optional[float] = None,
5260
+ **kwargs: ta.Any,
5261
+ ) -> ta.AsyncGenerator[asyncio.subprocess.Process, None]:
5262
+ fac: ta.Any
5263
+ if shell:
5264
+ fac = functools.partial(
5265
+ asyncio.create_subprocess_shell,
5266
+ check.single(cmd),
5267
+ )
5268
+ else:
5269
+ fac = functools.partial(
5270
+ asyncio.create_subprocess_exec,
5271
+ *cmd,
5272
+ )
5269
5273
 
5274
+ with self.prepare_and_wrap( *cmd, shell=shell, **kwargs) as (cmd, kwargs): # noqa
5275
+ proc: asyncio.subprocess.Process = await fac(**kwargs)
5276
+ try:
5277
+ yield proc
5270
5278
 
5271
- async def asyncio_subprocess_check_call(
5272
- *args: str,
5273
- stdout: ta.Any = sys.stderr,
5274
- input: ta.Any = None, # noqa
5275
- timeout: ta.Optional[float] = None,
5276
- **kwargs: ta.Any,
5277
- ) -> None:
5278
- await asyncio_subprocess_run(
5279
- *args,
5280
- stdout=stdout,
5281
- input=input,
5282
- timeout=timeout,
5283
- check=True,
5284
- **kwargs,
5285
- )
5279
+ finally:
5280
+ await asyncio_maybe_timeout(proc.wait(), timeout)
5286
5281
 
5282
+ #
5287
5283
 
5288
- async def asyncio_subprocess_check_output(
5289
- *args: str,
5290
- input: ta.Any = None, # noqa
5291
- timeout: ta.Optional[float] = None,
5292
- **kwargs: ta.Any,
5293
- ) -> bytes:
5294
- out = await asyncio_subprocess_run(
5295
- *args,
5296
- stdout=asyncio.subprocess.PIPE,
5297
- input=input,
5298
- timeout=timeout,
5299
- check=True,
5300
- **kwargs,
5301
- )
5284
+ @dc.dataclass(frozen=True)
5285
+ class RunOutput:
5286
+ proc: asyncio.subprocess.Process
5287
+ stdout: ta.Optional[bytes]
5288
+ stderr: ta.Optional[bytes]
5302
5289
 
5303
- return check.not_none(out.stdout)
5290
+ async def run(
5291
+ self,
5292
+ *cmd: str,
5293
+ input: ta.Any = None, # noqa
5294
+ timeout: ta.Optional[float] = None,
5295
+ check: bool = False, # noqa
5296
+ capture_output: ta.Optional[bool] = None,
5297
+ **kwargs: ta.Any,
5298
+ ) -> RunOutput:
5299
+ if capture_output:
5300
+ kwargs.setdefault('stdout', subprocess.PIPE)
5301
+ kwargs.setdefault('stderr', subprocess.PIPE)
5304
5302
 
5303
+ proc: asyncio.subprocess.Process
5304
+ async with self.popen(*cmd, **kwargs) as proc:
5305
+ stdout, stderr = await self.communicate(proc, input, timeout)
5306
+
5307
+ if check and proc.returncode:
5308
+ raise subprocess.CalledProcessError(
5309
+ proc.returncode,
5310
+ cmd,
5311
+ output=stdout,
5312
+ stderr=stderr,
5313
+ )
5305
5314
 
5306
- async def asyncio_subprocess_check_output_str(*args: str, **kwargs: ta.Any) -> str:
5307
- return (await asyncio_subprocess_check_output(*args, **kwargs)).decode().strip()
5315
+ return self.RunOutput(
5316
+ proc,
5317
+ stdout,
5318
+ stderr,
5319
+ )
5308
5320
 
5321
+ #
5309
5322
 
5310
- ##
5323
+ async def check_call(
5324
+ self,
5325
+ *cmd: str,
5326
+ stdout: ta.Any = sys.stderr,
5327
+ **kwargs: ta.Any,
5328
+ ) -> None:
5329
+ with self.prepare_and_wrap(*cmd, stdout=stdout, check=True, **kwargs) as (cmd, kwargs): # noqa
5330
+ await self.run(*cmd, **kwargs)
5311
5331
 
5332
+ async def check_output(
5333
+ self,
5334
+ *cmd: str,
5335
+ **kwargs: ta.Any,
5336
+ ) -> bytes:
5337
+ with self.prepare_and_wrap(*cmd, stdout=subprocess.PIPE, check=True, **kwargs) as (cmd, kwargs): # noqa
5338
+ return check.not_none((await self.run(*cmd, **kwargs)).stdout)
5312
5339
 
5313
- async def _asyncio_subprocess_try_run(
5314
- fn: ta.Callable[..., ta.Awaitable[T]],
5315
- *args: ta.Any,
5316
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
5317
- **kwargs: ta.Any,
5318
- ) -> ta.Union[T, Exception]:
5319
- try:
5320
- return await fn(*args, **kwargs)
5321
- except try_exceptions as e: # noqa
5322
- if log.isEnabledFor(logging.DEBUG):
5323
- log.exception('command failed')
5324
- return e
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()
5325
5346
 
5347
+ #
5326
5348
 
5327
- async def asyncio_subprocess_try_call(
5328
- *args: str,
5329
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
5330
- **kwargs: ta.Any,
5331
- ) -> bool:
5332
- if isinstance(await _asyncio_subprocess_try_run(
5333
- asyncio_subprocess_check_call,
5334
- *args,
5335
- try_exceptions=try_exceptions,
5336
- **kwargs,
5337
- ), Exception):
5338
- return False
5339
- else:
5340
- return True
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
5341
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
5342
5368
 
5343
- async def asyncio_subprocess_try_output(
5344
- *args: str,
5345
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
5346
- **kwargs: ta.Any,
5347
- ) -> ta.Optional[bytes]:
5348
- if isinstance(ret := await _asyncio_subprocess_try_run(
5349
- asyncio_subprocess_check_output,
5350
- *args,
5351
- try_exceptions=try_exceptions,
5352
- **kwargs,
5353
- ), Exception):
5354
- return None
5355
- else:
5356
- return ret
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()
5357
5378
 
5358
5379
 
5359
- async def asyncio_subprocess_try_output_str(*args: str, **kwargs: ta.Any) -> ta.Optional[str]:
5360
- out = await asyncio_subprocess_try_output(*args, **kwargs)
5361
- return out.decode().strip() if out is not None else None
5380
+ asyncio_subprocesses = AsyncioSubprocesses()
5362
5381
 
5363
5382
 
5364
5383
  ########################################
@@ -5435,7 +5454,7 @@ class InterpInspector:
5435
5454
  return cls._build_inspection(sys.executable, eval(cls._INSPECTION_CODE)) # noqa
5436
5455
 
5437
5456
  async def _inspect(self, exe: str) -> InterpInspection:
5438
- output = await asyncio_subprocess_check_output(exe, '-c', f'print({self._INSPECTION_CODE})', quiet=True)
5457
+ output = await asyncio_subprocesses.check_output(exe, '-c', f'print({self._INSPECTION_CODE})', quiet=True)
5439
5458
  return self._build_inspection(exe, output.decode())
5440
5459
 
5441
5460
  async def inspect(self, exe: str) -> ta.Optional[InterpInspection]:
@@ -5836,7 +5855,7 @@ class BasePyprojectPackageGenerator(abc.ABC):
5836
5855
  output_dir: ta.Optional[str] = None,
5837
5856
  opts: BuildOpts = BuildOpts(),
5838
5857
  ) -> None:
5839
- subprocess_check_call(
5858
+ subprocesses.check_call(
5840
5859
  sys.executable,
5841
5860
  '-m',
5842
5861
  'build',
@@ -5852,14 +5871,14 @@ class BasePyprojectPackageGenerator(abc.ABC):
5852
5871
  for fn in os.listdir(dist_dir):
5853
5872
  tmp_dir = tempfile.mkdtemp()
5854
5873
 
5855
- subprocess_check_call(
5874
+ subprocesses.check_call(
5856
5875
  sys.executable,
5857
5876
  '-m', 'venv',
5858
5877
  'test-install',
5859
5878
  cwd=tmp_dir,
5860
5879
  )
5861
5880
 
5862
- subprocess_check_call(
5881
+ subprocesses.check_call(
5863
5882
  os.path.join(tmp_dir, 'test-install', 'bin', 'python3'),
5864
5883
  '-m', 'pip',
5865
5884
  'install',
@@ -6232,7 +6251,7 @@ class Pyenv:
6232
6251
  return self._root_kw
6233
6252
 
6234
6253
  if shutil.which('pyenv'):
6235
- return await asyncio_subprocess_check_output_str('pyenv', 'root')
6254
+ return await asyncio_subprocesses.check_output_str('pyenv', 'root')
6236
6255
 
6237
6256
  d = os.path.expanduser('~/.pyenv')
6238
6257
  if os.path.isdir(d) and os.path.isfile(os.path.join(d, 'bin', 'pyenv')):
@@ -6261,7 +6280,7 @@ class Pyenv:
6261
6280
  if await self.root() is None:
6262
6281
  return []
6263
6282
  ret = []
6264
- s = await asyncio_subprocess_check_output_str(await self.exe(), 'install', '--list')
6283
+ s = await asyncio_subprocesses.check_output_str(await self.exe(), 'install', '--list')
6265
6284
  for l in s.splitlines():
6266
6285
  if not l.startswith(' '):
6267
6286
  continue
@@ -6276,7 +6295,7 @@ class Pyenv:
6276
6295
  return False
6277
6296
  if not os.path.isdir(os.path.join(root, '.git')):
6278
6297
  return False
6279
- await asyncio_subprocess_check_call('git', 'pull', cwd=root)
6298
+ await asyncio_subprocesses.check_call('git', 'pull', cwd=root)
6280
6299
  return True
6281
6300
 
6282
6301
 
@@ -6367,7 +6386,7 @@ class DarwinPyenvInstallOpts(PyenvInstallOptsProvider):
6367
6386
  cflags = []
6368
6387
  ldflags = []
6369
6388
  for dep in self.BREW_DEPS:
6370
- dep_prefix = await asyncio_subprocess_check_output_str('brew', '--prefix', dep)
6389
+ dep_prefix = await asyncio_subprocesses.check_output_str('brew', '--prefix', dep)
6371
6390
  cflags.append(f'-I{dep_prefix}/include')
6372
6391
  ldflags.append(f'-L{dep_prefix}/lib')
6373
6392
  return PyenvInstallOpts(
@@ -6377,11 +6396,11 @@ class DarwinPyenvInstallOpts(PyenvInstallOptsProvider):
6377
6396
 
6378
6397
  @async_cached_nullary
6379
6398
  async def brew_tcl_opts(self) -> PyenvInstallOpts:
6380
- if await asyncio_subprocess_try_output('brew', '--prefix', 'tcl-tk') is None:
6399
+ if await asyncio_subprocesses.try_output('brew', '--prefix', 'tcl-tk') is None:
6381
6400
  return PyenvInstallOpts()
6382
6401
 
6383
- tcl_tk_prefix = await asyncio_subprocess_check_output_str('brew', '--prefix', 'tcl-tk')
6384
- tcl_tk_ver_str = await asyncio_subprocess_check_output_str('brew', 'ls', '--versions', 'tcl-tk')
6402
+ tcl_tk_prefix = await asyncio_subprocesses.check_output_str('brew', '--prefix', 'tcl-tk')
6403
+ tcl_tk_ver_str = await asyncio_subprocesses.check_output_str('brew', 'ls', '--versions', 'tcl-tk')
6385
6404
  tcl_tk_ver = '.'.join(tcl_tk_ver_str.split()[1].split('.')[:2])
6386
6405
 
6387
6406
  return PyenvInstallOpts(conf_opts=[
@@ -6489,6 +6508,7 @@ class PyenvVersionInstaller:
6489
6508
  self._version,
6490
6509
  ]
6491
6510
 
6511
+ full_args: ta.List[str]
6492
6512
  if self._given_install_name is not None:
6493
6513
  full_args = [
6494
6514
  os.path.join(check.not_none(await self._pyenv.root()), 'plugins', 'python-build', 'bin', 'python-build'), # noqa
@@ -6502,7 +6522,7 @@ class PyenvVersionInstaller:
6502
6522
  *conf_args,
6503
6523
  ]
6504
6524
 
6505
- await asyncio_subprocess_check_call(
6525
+ await asyncio_subprocesses.check_call(
6506
6526
  *full_args,
6507
6527
  env=env,
6508
6528
  )
@@ -6897,12 +6917,12 @@ class Venv:
6897
6917
  return False
6898
6918
 
6899
6919
  log.info('Using interpreter %s', (ie := await self.interp_exe()))
6900
- await asyncio_subprocess_check_call(ie, '-m', 'venv', dn)
6920
+ await asyncio_subprocesses.check_call(ie, '-m', 'venv', dn)
6901
6921
 
6902
6922
  ve = self.exe()
6903
6923
  uv = self._cfg.use_uv
6904
6924
 
6905
- await asyncio_subprocess_check_call(
6925
+ await asyncio_subprocesses.check_call(
6906
6926
  ve,
6907
6927
  '-m', 'pip',
6908
6928
  'install', '-v', '--upgrade',
@@ -6920,7 +6940,7 @@ class Venv:
6920
6940
  # Caused by: Failed to download distribution due to network timeout. Try increasing UV_HTTP_TIMEOUT (current value: 30s). # noqa
6921
6941
  # UV_CONCURRENT_DOWNLOADS=4 UV_HTTP_TIMEOUT=3600
6922
6942
 
6923
- await asyncio_subprocess_check_call(
6943
+ await asyncio_subprocesses.check_call(
6924
6944
  ve,
6925
6945
  '-m',
6926
6946
  *(['uv'] if uv else []),
@@ -7074,7 +7094,7 @@ class PyprojectCli(ArgparseCli):
7074
7094
  else:
7075
7095
  docker_env[e] = os.environ.get(e, '')
7076
7096
 
7077
- await asyncio_subprocess_check_call(
7097
+ await asyncio_subprocesses.check_call(
7078
7098
  'docker',
7079
7099
  'compose',
7080
7100
  '-f', 'docker/compose.yml',
@@ -7125,7 +7145,7 @@ class PyprojectCli(ArgparseCli):
7125
7145
 
7126
7146
  elif cmd == 'test':
7127
7147
  await venv.create()
7128
- await asyncio_subprocess_check_call(venv.exe(), '-m', 'pytest', *(self.args.args or []), *venv.srcs())
7148
+ await asyncio_subprocesses.check_call(venv.exe(), '-m', 'pytest', *(self.args.args or []), *venv.srcs())
7129
7149
 
7130
7150
  else:
7131
7151
  raise Exception(f'unknown subcommand: {cmd}')