omdev 0.0.0.dev154__py3-none-any.whl → 0.0.0.dev156__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.

@@ -1852,8 +1852,6 @@ def async_cached_nullary(fn): # ta.Callable[..., T]) -> ta.Callable[..., T]:
1852
1852
  """
1853
1853
  TODO:
1854
1854
  - def maybe(v: lang.Maybe[T])
1855
- - patch / override lite.check ?
1856
- - checker interface?
1857
1855
  """
1858
1856
 
1859
1857
 
@@ -3756,7 +3754,8 @@ def configure_standard_logging(
3756
3754
  """
3757
3755
  TODO:
3758
3756
  - pickle stdlib objs? have to pin to 3.8 pickle protocol, will be cross-version
3759
- - nonstrict toggle
3757
+ - namedtuple
3758
+ - literals
3760
3759
  """
3761
3760
 
3762
3761
 
@@ -4046,14 +4045,18 @@ class ObjMarshalerManager:
4046
4045
  ) -> ObjMarshaler:
4047
4046
  if isinstance(ty, type):
4048
4047
  if abc.ABC in ty.__bases__:
4049
- return PolymorphicObjMarshaler.of([ # type: ignore
4048
+ impls = [ity for ity in deep_subclasses(ty) if abc.ABC not in ity.__bases__] # type: ignore
4049
+ if all(ity.__qualname__.endswith(ty.__name__) for ity in impls):
4050
+ ins = {ity: snake_case(ity.__qualname__[:-len(ty.__name__)]) for ity in impls}
4051
+ else:
4052
+ ins = {ity: ity.__qualname__ for ity in impls}
4053
+ return PolymorphicObjMarshaler.of([
4050
4054
  PolymorphicObjMarshaler.Impl(
4051
4055
  ity,
4052
- ity.__qualname__,
4056
+ itn,
4053
4057
  rec(ity),
4054
4058
  )
4055
- for ity in deep_subclasses(ty)
4056
- if abc.ABC not in ity.__bases__
4059
+ for ity, itn in ins.items()
4057
4060
  ])
4058
4061
 
4059
4062
  if issubclass(ty, enum.Enum):
@@ -4495,168 +4498,222 @@ SUBPROCESS_CHANNEL_OPTION_VALUES: ta.Mapping[SubprocessChannelOption, int] = {
4495
4498
  _SUBPROCESS_SHELL_WRAP_EXECS = False
4496
4499
 
4497
4500
 
4498
- def subprocess_shell_wrap_exec(*args: str) -> ta.Tuple[str, ...]:
4499
- return ('sh', '-c', ' '.join(map(shlex.quote, args)))
4501
+ def subprocess_shell_wrap_exec(*cmd: str) -> ta.Tuple[str, ...]:
4502
+ return ('sh', '-c', ' '.join(map(shlex.quote, cmd)))
4500
4503
 
4501
4504
 
4502
- def subprocess_maybe_shell_wrap_exec(*args: str) -> ta.Tuple[str, ...]:
4505
+ def subprocess_maybe_shell_wrap_exec(*cmd: str) -> ta.Tuple[str, ...]:
4503
4506
  if _SUBPROCESS_SHELL_WRAP_EXECS or is_debugger_attached():
4504
- return subprocess_shell_wrap_exec(*args)
4507
+ return subprocess_shell_wrap_exec(*cmd)
4505
4508
  else:
4506
- return args
4507
-
4509
+ return cmd
4508
4510
 
4509
- def prepare_subprocess_invocation(
4510
- *args: str,
4511
- env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
4512
- extra_env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
4513
- quiet: bool = False,
4514
- shell: bool = False,
4515
- **kwargs: ta.Any,
4516
- ) -> ta.Tuple[ta.Tuple[ta.Any, ...], ta.Dict[str, ta.Any]]:
4517
- log.debug('prepare_subprocess_invocation: args=%r', args)
4518
- if extra_env:
4519
- log.debug('prepare_subprocess_invocation: extra_env=%r', extra_env)
4520
4511
 
4521
- if extra_env:
4522
- env = {**(env if env is not None else os.environ), **extra_env}
4512
+ ##
4523
4513
 
4524
- if quiet and 'stderr' not in kwargs:
4525
- if not log.isEnabledFor(logging.DEBUG):
4526
- kwargs['stderr'] = subprocess.DEVNULL
4527
4514
 
4528
- if not shell:
4529
- args = subprocess_maybe_shell_wrap_exec(*args)
4515
+ def subprocess_close(
4516
+ proc: subprocess.Popen,
4517
+ timeout: ta.Optional[float] = None,
4518
+ ) -> None:
4519
+ # TODO: terminate, sleep, kill
4520
+ if proc.stdout:
4521
+ proc.stdout.close()
4522
+ if proc.stderr:
4523
+ proc.stderr.close()
4524
+ if proc.stdin:
4525
+ proc.stdin.close()
4530
4526
 
4531
- return args, dict(
4532
- env=env,
4533
- shell=shell,
4534
- **kwargs,
4535
- )
4527
+ proc.wait(timeout)
4536
4528
 
4537
4529
 
4538
4530
  ##
4539
4531
 
4540
4532
 
4541
- @contextlib.contextmanager
4542
- def subprocess_common_context(*args: ta.Any, **kwargs: ta.Any) -> ta.Iterator[None]:
4543
- start_time = time.time()
4544
- try:
4545
- log.debug('subprocess_common_context.try: args=%r', args)
4546
- yield
4533
+ class AbstractSubprocesses(abc.ABC): # noqa
4534
+ DEFAULT_LOGGER: ta.ClassVar[ta.Optional[logging.Logger]] = log
4547
4535
 
4548
- except Exception as exc: # noqa
4549
- log.debug('subprocess_common_context.except: exc=%r', exc)
4550
- raise
4536
+ def __init__(
4537
+ self,
4538
+ *,
4539
+ log: ta.Optional[logging.Logger] = None,
4540
+ try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
4541
+ ) -> None:
4542
+ super().__init__()
4551
4543
 
4552
- finally:
4553
- end_time = time.time()
4554
- elapsed_s = end_time - start_time
4555
- log.debug('subprocess_common_context.finally: elapsed_s=%f args=%r', elapsed_s, args)
4544
+ self._log = log if log is not None else self.DEFAULT_LOGGER
4545
+ self._try_exceptions = try_exceptions if try_exceptions is not None else self.DEFAULT_TRY_EXCEPTIONS
4556
4546
 
4547
+ #
4557
4548
 
4558
- ##
4549
+ def prepare_args(
4550
+ self,
4551
+ *cmd: str,
4552
+ env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
4553
+ extra_env: ta.Optional[ta.Mapping[str, ta.Any]] = None,
4554
+ quiet: bool = False,
4555
+ shell: bool = False,
4556
+ **kwargs: ta.Any,
4557
+ ) -> ta.Tuple[ta.Tuple[ta.Any, ...], ta.Dict[str, ta.Any]]:
4558
+ if self._log:
4559
+ self._log.debug('Subprocesses.prepare_args: cmd=%r', cmd)
4560
+ if extra_env:
4561
+ self._log.debug('Subprocesses.prepare_args: extra_env=%r', extra_env)
4559
4562
 
4563
+ if extra_env:
4564
+ env = {**(env if env is not None else os.environ), **extra_env}
4560
4565
 
4561
- def subprocess_check_call(
4562
- *args: str,
4563
- stdout: ta.Any = sys.stderr,
4564
- **kwargs: ta.Any,
4565
- ) -> None:
4566
- args, kwargs = prepare_subprocess_invocation(*args, stdout=stdout, **kwargs)
4567
- with subprocess_common_context(*args, **kwargs):
4568
- return subprocess.check_call(args, **kwargs) # type: ignore
4566
+ if quiet and 'stderr' not in kwargs:
4567
+ if self._log and not self._log.isEnabledFor(logging.DEBUG):
4568
+ kwargs['stderr'] = subprocess.DEVNULL
4569
4569
 
4570
+ if not shell:
4571
+ cmd = subprocess_maybe_shell_wrap_exec(*cmd)
4570
4572
 
4571
- def subprocess_check_output(
4572
- *args: str,
4573
- **kwargs: ta.Any,
4574
- ) -> bytes:
4575
- args, kwargs = prepare_subprocess_invocation(*args, **kwargs)
4576
- with subprocess_common_context(*args, **kwargs):
4577
- return subprocess.check_output(args, **kwargs)
4573
+ return cmd, dict(
4574
+ env=env,
4575
+ shell=shell,
4576
+ **kwargs,
4577
+ )
4578
4578
 
4579
+ @contextlib.contextmanager
4580
+ def wrap_call(self, *cmd: ta.Any, **kwargs: ta.Any) -> ta.Iterator[None]:
4581
+ start_time = time.time()
4582
+ try:
4583
+ if self._log:
4584
+ self._log.debug('Subprocesses.wrap_call.try: cmd=%r', cmd)
4585
+ yield
4579
4586
 
4580
- def subprocess_check_output_str(*args: str, **kwargs: ta.Any) -> str:
4581
- return subprocess_check_output(*args, **kwargs).decode().strip()
4587
+ except Exception as exc: # noqa
4588
+ if self._log:
4589
+ self._log.debug('Subprocesses.wrap_call.except: exc=%r', exc)
4590
+ raise
4582
4591
 
4592
+ finally:
4593
+ end_time = time.time()
4594
+ elapsed_s = end_time - start_time
4595
+ if self._log:
4596
+ self._log.debug('sSubprocesses.wrap_call.finally: elapsed_s=%f cmd=%r', elapsed_s, cmd)
4583
4597
 
4584
- ##
4598
+ @contextlib.contextmanager
4599
+ def prepare_and_wrap(
4600
+ self,
4601
+ *cmd: ta.Any,
4602
+ **kwargs: ta.Any,
4603
+ ) -> ta.Iterator[ta.Tuple[
4604
+ ta.Tuple[ta.Any, ...],
4605
+ ta.Dict[str, ta.Any],
4606
+ ]]:
4607
+ cmd, kwargs = self.prepare_args(*cmd, **kwargs)
4608
+ with self.wrap_call(*cmd, **kwargs):
4609
+ yield cmd, kwargs
4585
4610
 
4611
+ #
4586
4612
 
4587
- DEFAULT_SUBPROCESS_TRY_EXCEPTIONS: ta.Tuple[ta.Type[Exception], ...] = (
4588
- FileNotFoundError,
4589
- subprocess.CalledProcessError,
4590
- )
4613
+ DEFAULT_TRY_EXCEPTIONS: ta.Tuple[ta.Type[Exception], ...] = (
4614
+ FileNotFoundError,
4615
+ subprocess.CalledProcessError,
4616
+ )
4591
4617
 
4618
+ def try_fn(
4619
+ self,
4620
+ fn: ta.Callable[..., T],
4621
+ *cmd: str,
4622
+ try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
4623
+ **kwargs: ta.Any,
4624
+ ) -> ta.Union[T, Exception]:
4625
+ if try_exceptions is None:
4626
+ try_exceptions = self._try_exceptions
4592
4627
 
4593
- def _subprocess_try_run(
4594
- fn: ta.Callable[..., T],
4595
- *args: ta.Any,
4596
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
4597
- **kwargs: ta.Any,
4598
- ) -> ta.Union[T, Exception]:
4599
- try:
4600
- return fn(*args, **kwargs)
4601
- except try_exceptions as e: # noqa
4602
- if log.isEnabledFor(logging.DEBUG):
4603
- log.exception('command failed')
4604
- return e
4628
+ try:
4629
+ return fn(*cmd, **kwargs)
4605
4630
 
4631
+ except try_exceptions as e: # noqa
4632
+ if self._log and self._log.isEnabledFor(logging.DEBUG):
4633
+ self._log.exception('command failed')
4634
+ return e
4606
4635
 
4607
- def subprocess_try_call(
4608
- *args: str,
4609
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
4610
- **kwargs: ta.Any,
4611
- ) -> bool:
4612
- if isinstance(_subprocess_try_run(
4613
- subprocess_check_call,
4614
- *args,
4615
- try_exceptions=try_exceptions,
4616
- **kwargs,
4617
- ), Exception):
4618
- return False
4619
- else:
4620
- return True
4636
+ async def async_try_fn(
4637
+ self,
4638
+ fn: ta.Callable[..., ta.Awaitable[T]],
4639
+ *cmd: ta.Any,
4640
+ try_exceptions: ta.Optional[ta.Tuple[ta.Type[Exception], ...]] = None,
4641
+ **kwargs: ta.Any,
4642
+ ) -> ta.Union[T, Exception]:
4643
+ if try_exceptions is None:
4644
+ try_exceptions = self._try_exceptions
4621
4645
 
4646
+ try:
4647
+ return await fn(*cmd, **kwargs)
4622
4648
 
4623
- def subprocess_try_output(
4624
- *args: str,
4625
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
4626
- **kwargs: ta.Any,
4627
- ) -> ta.Optional[bytes]:
4628
- if isinstance(ret := _subprocess_try_run(
4629
- subprocess_check_output,
4630
- *args,
4631
- try_exceptions=try_exceptions,
4632
- **kwargs,
4633
- ), Exception):
4634
- return None
4635
- else:
4636
- return ret
4649
+ except try_exceptions as e: # noqa
4650
+ if self._log and self._log.isEnabledFor(logging.DEBUG):
4651
+ self._log.exception('command failed')
4652
+ return e
4637
4653
 
4638
4654
 
4639
- def subprocess_try_output_str(*args: str, **kwargs: ta.Any) -> ta.Optional[str]:
4640
- out = subprocess_try_output(*args, **kwargs)
4641
- return out.decode().strip() if out is not None else None
4655
+ ##
4642
4656
 
4643
4657
 
4644
- ##
4658
+ class Subprocesses(AbstractSubprocesses):
4659
+ def check_call(
4660
+ self,
4661
+ *cmd: str,
4662
+ stdout: ta.Any = sys.stderr,
4663
+ **kwargs: ta.Any,
4664
+ ) -> None:
4665
+ with self.prepare_and_wrap(*cmd, stdout=stdout, **kwargs) as (cmd, kwargs): # noqa
4666
+ subprocess.check_call(cmd, **kwargs)
4645
4667
 
4668
+ def check_output(
4669
+ self,
4670
+ *cmd: str,
4671
+ **kwargs: ta.Any,
4672
+ ) -> bytes:
4673
+ with self.prepare_and_wrap(*cmd, **kwargs) as (cmd, kwargs): # noqa
4674
+ return subprocess.check_output(cmd, **kwargs)
4646
4675
 
4647
- def subprocess_close(
4648
- proc: subprocess.Popen,
4649
- timeout: ta.Optional[float] = None,
4650
- ) -> None:
4651
- # TODO: terminate, sleep, kill
4652
- if proc.stdout:
4653
- proc.stdout.close()
4654
- if proc.stderr:
4655
- proc.stderr.close()
4656
- if proc.stdin:
4657
- proc.stdin.close()
4676
+ def check_output_str(
4677
+ self,
4678
+ *cmd: str,
4679
+ **kwargs: ta.Any,
4680
+ ) -> str:
4681
+ return self.check_output(*cmd, **kwargs).decode().strip()
4658
4682
 
4659
- proc.wait(timeout)
4683
+ #
4684
+
4685
+ def try_call(
4686
+ self,
4687
+ *cmd: str,
4688
+ **kwargs: ta.Any,
4689
+ ) -> bool:
4690
+ if isinstance(self.try_fn(self.check_call, *cmd, **kwargs), Exception):
4691
+ return False
4692
+ else:
4693
+ return True
4694
+
4695
+ def try_output(
4696
+ self,
4697
+ *cmd: str,
4698
+ **kwargs: ta.Any,
4699
+ ) -> ta.Optional[bytes]:
4700
+ if isinstance(ret := self.try_fn(self.check_output, *cmd, **kwargs), Exception):
4701
+ return None
4702
+ else:
4703
+ return ret
4704
+
4705
+ def try_output_str(
4706
+ self,
4707
+ *cmd: str,
4708
+ **kwargs: ta.Any,
4709
+ ) -> ta.Optional[str]:
4710
+ if (ret := self.try_output(*cmd, **kwargs)) is None:
4711
+ return None
4712
+ else:
4713
+ return ret.decode().strip()
4714
+
4715
+
4716
+ subprocesses = Subprocesses()
4660
4717
 
4661
4718
 
4662
4719
  ########################################
@@ -5067,43 +5124,6 @@ def get_git_status(
5067
5124
  ##
5068
5125
 
5069
5126
 
5070
- @contextlib.asynccontextmanager
5071
- async def asyncio_subprocess_popen(
5072
- *cmd: str,
5073
- shell: bool = False,
5074
- timeout: ta.Optional[float] = None,
5075
- **kwargs: ta.Any,
5076
- ) -> ta.AsyncGenerator[asyncio.subprocess.Process, None]:
5077
- fac: ta.Any
5078
- if shell:
5079
- fac = functools.partial(
5080
- asyncio.create_subprocess_shell,
5081
- check.single(cmd),
5082
- )
5083
- else:
5084
- fac = functools.partial(
5085
- asyncio.create_subprocess_exec,
5086
- *cmd,
5087
- )
5088
-
5089
- with subprocess_common_context(
5090
- *cmd,
5091
- shell=shell,
5092
- timeout=timeout,
5093
- **kwargs,
5094
- ):
5095
- proc: asyncio.subprocess.Process
5096
- proc = await fac(**kwargs)
5097
- try:
5098
- yield proc
5099
-
5100
- finally:
5101
- await asyncio_maybe_timeout(proc.wait(), timeout)
5102
-
5103
-
5104
- ##
5105
-
5106
-
5107
5127
  class AsyncioProcessCommunicator:
5108
5128
  def __init__(
5109
5129
  self,
@@ -5214,137 +5234,147 @@ class AsyncioProcessCommunicator:
5214
5234
  return await asyncio_maybe_timeout(self._communicate(input), timeout)
5215
5235
 
5216
5236
 
5217
- async def asyncio_subprocess_communicate(
5218
- proc: asyncio.subprocess.Process,
5219
- input: ta.Any = None, # noqa
5220
- timeout: ta.Optional[float] = None,
5221
- ) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
5222
- return await AsyncioProcessCommunicator(proc).communicate(input, timeout) # noqa
5223
-
5224
-
5225
- async def asyncio_subprocess_run(
5226
- *args: str,
5227
- input: ta.Any = None, # noqa
5228
- timeout: ta.Optional[float] = None,
5229
- check: bool = False, # noqa
5230
- capture_output: ta.Optional[bool] = None,
5231
- **kwargs: ta.Any,
5232
- ) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
5233
- if capture_output:
5234
- kwargs.setdefault('stdout', subprocess.PIPE)
5235
- kwargs.setdefault('stderr', subprocess.PIPE)
5236
-
5237
- args, kwargs = prepare_subprocess_invocation(*args, **kwargs)
5238
-
5239
- proc: asyncio.subprocess.Process
5240
- async with asyncio_subprocess_popen(*args, **kwargs) as proc:
5241
- stdout, stderr = await asyncio_subprocess_communicate(proc, input, timeout)
5237
+ ##
5242
5238
 
5243
- if check and proc.returncode:
5244
- raise subprocess.CalledProcessError(
5245
- proc.returncode,
5246
- args,
5247
- output=stdout,
5248
- stderr=stderr,
5249
- )
5250
5239
 
5251
- return stdout, stderr
5240
+ class AsyncioSubprocesses(AbstractSubprocesses):
5241
+ async def communicate(
5242
+ self,
5243
+ proc: asyncio.subprocess.Process,
5244
+ input: ta.Any = None, # noqa
5245
+ timeout: ta.Optional[float] = None,
5246
+ ) -> ta.Tuple[ta.Optional[bytes], ta.Optional[bytes]]:
5247
+ return await AsyncioProcessCommunicator(proc).communicate(input, timeout) # noqa
5252
5248
 
5249
+ #
5253
5250
 
5254
- ##
5251
+ @contextlib.asynccontextmanager
5252
+ async def popen(
5253
+ self,
5254
+ *cmd: str,
5255
+ shell: bool = False,
5256
+ timeout: ta.Optional[float] = None,
5257
+ **kwargs: ta.Any,
5258
+ ) -> ta.AsyncGenerator[asyncio.subprocess.Process, None]:
5259
+ fac: ta.Any
5260
+ if shell:
5261
+ fac = functools.partial(
5262
+ asyncio.create_subprocess_shell,
5263
+ check.single(cmd),
5264
+ )
5265
+ else:
5266
+ fac = functools.partial(
5267
+ asyncio.create_subprocess_exec,
5268
+ *cmd,
5269
+ )
5255
5270
 
5271
+ with self.prepare_and_wrap( *cmd, shell=shell, **kwargs) as (cmd, kwargs): # noqa
5272
+ proc: asyncio.subprocess.Process = await fac(**kwargs)
5273
+ try:
5274
+ yield proc
5256
5275
 
5257
- async def asyncio_subprocess_check_call(
5258
- *args: str,
5259
- stdout: ta.Any = sys.stderr,
5260
- input: ta.Any = None, # noqa
5261
- timeout: ta.Optional[float] = None,
5262
- **kwargs: ta.Any,
5263
- ) -> None:
5264
- _, _ = await asyncio_subprocess_run(
5265
- *args,
5266
- stdout=stdout,
5267
- input=input,
5268
- timeout=timeout,
5269
- check=True,
5270
- **kwargs,
5271
- )
5276
+ finally:
5277
+ await asyncio_maybe_timeout(proc.wait(), timeout)
5272
5278
 
5279
+ #
5273
5280
 
5274
- async def asyncio_subprocess_check_output(
5275
- *args: str,
5276
- input: ta.Any = None, # noqa
5277
- timeout: ta.Optional[float] = None,
5278
- **kwargs: ta.Any,
5279
- ) -> bytes:
5280
- stdout, stderr = await asyncio_subprocess_run(
5281
- *args,
5282
- stdout=asyncio.subprocess.PIPE,
5283
- input=input,
5284
- timeout=timeout,
5285
- check=True,
5286
- **kwargs,
5287
- )
5281
+ @dc.dataclass(frozen=True)
5282
+ class RunOutput:
5283
+ proc: asyncio.subprocess.Process
5284
+ stdout: ta.Optional[bytes]
5285
+ stderr: ta.Optional[bytes]
5288
5286
 
5289
- return check.not_none(stdout)
5287
+ async def run(
5288
+ self,
5289
+ *cmd: str,
5290
+ input: ta.Any = None, # noqa
5291
+ timeout: ta.Optional[float] = None,
5292
+ check: bool = False, # noqa
5293
+ capture_output: ta.Optional[bool] = None,
5294
+ **kwargs: ta.Any,
5295
+ ) -> RunOutput:
5296
+ if capture_output:
5297
+ kwargs.setdefault('stdout', subprocess.PIPE)
5298
+ kwargs.setdefault('stderr', subprocess.PIPE)
5290
5299
 
5300
+ proc: asyncio.subprocess.Process
5301
+ async with self.popen(*cmd, **kwargs) as proc:
5302
+ stdout, stderr = await self.communicate(proc, input, timeout)
5303
+
5304
+ if check and proc.returncode:
5305
+ raise subprocess.CalledProcessError(
5306
+ proc.returncode,
5307
+ cmd,
5308
+ output=stdout,
5309
+ stderr=stderr,
5310
+ )
5291
5311
 
5292
- async def asyncio_subprocess_check_output_str(*args: str, **kwargs: ta.Any) -> str:
5293
- return (await asyncio_subprocess_check_output(*args, **kwargs)).decode().strip()
5312
+ return self.RunOutput(
5313
+ proc,
5314
+ stdout,
5315
+ stderr,
5316
+ )
5294
5317
 
5318
+ #
5295
5319
 
5296
- ##
5320
+ async def check_call(
5321
+ self,
5322
+ *cmd: str,
5323
+ stdout: ta.Any = sys.stderr,
5324
+ **kwargs: ta.Any,
5325
+ ) -> None:
5326
+ with self.prepare_and_wrap(*cmd, stdout=stdout, check=True, **kwargs) as (cmd, kwargs): # noqa
5327
+ await self.run(*cmd, **kwargs)
5297
5328
 
5329
+ async def check_output(
5330
+ self,
5331
+ *cmd: str,
5332
+ **kwargs: ta.Any,
5333
+ ) -> bytes:
5334
+ with self.prepare_and_wrap(*cmd, stdout=subprocess.PIPE, check=True, **kwargs) as (cmd, kwargs): # noqa
5335
+ return check.not_none((await self.run(*cmd, **kwargs)).stdout)
5298
5336
 
5299
- async def _asyncio_subprocess_try_run(
5300
- fn: ta.Callable[..., ta.Awaitable[T]],
5301
- *args: ta.Any,
5302
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
5303
- **kwargs: ta.Any,
5304
- ) -> ta.Union[T, Exception]:
5305
- try:
5306
- return await fn(*args, **kwargs)
5307
- except try_exceptions as e: # noqa
5308
- if log.isEnabledFor(logging.DEBUG):
5309
- log.exception('command failed')
5310
- return e
5337
+ async def check_output_str(
5338
+ self,
5339
+ *cmd: str,
5340
+ **kwargs: ta.Any,
5341
+ ) -> str:
5342
+ return (await self.check_output(*cmd, **kwargs)).decode().strip()
5311
5343
 
5344
+ #
5312
5345
 
5313
- async def asyncio_subprocess_try_call(
5314
- *args: str,
5315
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
5316
- **kwargs: ta.Any,
5317
- ) -> bool:
5318
- if isinstance(await _asyncio_subprocess_try_run(
5319
- asyncio_subprocess_check_call,
5320
- *args,
5321
- try_exceptions=try_exceptions,
5322
- **kwargs,
5323
- ), Exception):
5324
- return False
5325
- else:
5326
- return True
5346
+ async def try_call(
5347
+ self,
5348
+ *cmd: str,
5349
+ **kwargs: ta.Any,
5350
+ ) -> bool:
5351
+ if isinstance(await self.async_try_fn(self.check_call, *cmd, **kwargs), Exception):
5352
+ return False
5353
+ else:
5354
+ return True
5327
5355
 
5356
+ async def try_output(
5357
+ self,
5358
+ *cmd: str,
5359
+ **kwargs: ta.Any,
5360
+ ) -> ta.Optional[bytes]:
5361
+ if isinstance(ret := await self.async_try_fn(self.check_output, *cmd, **kwargs), Exception):
5362
+ return None
5363
+ else:
5364
+ return ret
5328
5365
 
5329
- async def asyncio_subprocess_try_output(
5330
- *args: str,
5331
- try_exceptions: ta.Tuple[ta.Type[Exception], ...] = DEFAULT_SUBPROCESS_TRY_EXCEPTIONS,
5332
- **kwargs: ta.Any,
5333
- ) -> ta.Optional[bytes]:
5334
- if isinstance(ret := await _asyncio_subprocess_try_run(
5335
- asyncio_subprocess_check_output,
5336
- *args,
5337
- try_exceptions=try_exceptions,
5338
- **kwargs,
5339
- ), Exception):
5340
- return None
5341
- else:
5342
- return ret
5366
+ async def try_output_str(
5367
+ self,
5368
+ *cmd: str,
5369
+ **kwargs: ta.Any,
5370
+ ) -> ta.Optional[str]:
5371
+ if (ret := await self.try_output(*cmd, **kwargs)) is None:
5372
+ return None
5373
+ else:
5374
+ return ret.decode().strip()
5343
5375
 
5344
5376
 
5345
- async def asyncio_subprocess_try_output_str(*args: str, **kwargs: ta.Any) -> ta.Optional[str]:
5346
- out = await asyncio_subprocess_try_output(*args, **kwargs)
5347
- return out.decode().strip() if out is not None else None
5377
+ asyncio_subprocesses = AsyncioSubprocesses()
5348
5378
 
5349
5379
 
5350
5380
  ########################################
@@ -5421,7 +5451,7 @@ class InterpInspector:
5421
5451
  return cls._build_inspection(sys.executable, eval(cls._INSPECTION_CODE)) # noqa
5422
5452
 
5423
5453
  async def _inspect(self, exe: str) -> InterpInspection:
5424
- output = await asyncio_subprocess_check_output(exe, '-c', f'print({self._INSPECTION_CODE})', quiet=True)
5454
+ output = await asyncio_subprocesses.check_output(exe, '-c', f'print({self._INSPECTION_CODE})', quiet=True)
5425
5455
  return self._build_inspection(exe, output.decode())
5426
5456
 
5427
5457
  async def inspect(self, exe: str) -> ta.Optional[InterpInspection]:
@@ -5822,7 +5852,7 @@ class BasePyprojectPackageGenerator(abc.ABC):
5822
5852
  output_dir: ta.Optional[str] = None,
5823
5853
  opts: BuildOpts = BuildOpts(),
5824
5854
  ) -> None:
5825
- subprocess_check_call(
5855
+ subprocesses.check_call(
5826
5856
  sys.executable,
5827
5857
  '-m',
5828
5858
  'build',
@@ -5838,14 +5868,14 @@ class BasePyprojectPackageGenerator(abc.ABC):
5838
5868
  for fn in os.listdir(dist_dir):
5839
5869
  tmp_dir = tempfile.mkdtemp()
5840
5870
 
5841
- subprocess_check_call(
5871
+ subprocesses.check_call(
5842
5872
  sys.executable,
5843
5873
  '-m', 'venv',
5844
5874
  'test-install',
5845
5875
  cwd=tmp_dir,
5846
5876
  )
5847
5877
 
5848
- subprocess_check_call(
5878
+ subprocesses.check_call(
5849
5879
  os.path.join(tmp_dir, 'test-install', 'bin', 'python3'),
5850
5880
  '-m', 'pip',
5851
5881
  'install',
@@ -6218,7 +6248,7 @@ class Pyenv:
6218
6248
  return self._root_kw
6219
6249
 
6220
6250
  if shutil.which('pyenv'):
6221
- return await asyncio_subprocess_check_output_str('pyenv', 'root')
6251
+ return await asyncio_subprocesses.check_output_str('pyenv', 'root')
6222
6252
 
6223
6253
  d = os.path.expanduser('~/.pyenv')
6224
6254
  if os.path.isdir(d) and os.path.isfile(os.path.join(d, 'bin', 'pyenv')):
@@ -6247,7 +6277,7 @@ class Pyenv:
6247
6277
  if await self.root() is None:
6248
6278
  return []
6249
6279
  ret = []
6250
- s = await asyncio_subprocess_check_output_str(await self.exe(), 'install', '--list')
6280
+ s = await asyncio_subprocesses.check_output_str(await self.exe(), 'install', '--list')
6251
6281
  for l in s.splitlines():
6252
6282
  if not l.startswith(' '):
6253
6283
  continue
@@ -6262,7 +6292,7 @@ class Pyenv:
6262
6292
  return False
6263
6293
  if not os.path.isdir(os.path.join(root, '.git')):
6264
6294
  return False
6265
- await asyncio_subprocess_check_call('git', 'pull', cwd=root)
6295
+ await asyncio_subprocesses.check_call('git', 'pull', cwd=root)
6266
6296
  return True
6267
6297
 
6268
6298
 
@@ -6353,7 +6383,7 @@ class DarwinPyenvInstallOpts(PyenvInstallOptsProvider):
6353
6383
  cflags = []
6354
6384
  ldflags = []
6355
6385
  for dep in self.BREW_DEPS:
6356
- dep_prefix = await asyncio_subprocess_check_output_str('brew', '--prefix', dep)
6386
+ dep_prefix = await asyncio_subprocesses.check_output_str('brew', '--prefix', dep)
6357
6387
  cflags.append(f'-I{dep_prefix}/include')
6358
6388
  ldflags.append(f'-L{dep_prefix}/lib')
6359
6389
  return PyenvInstallOpts(
@@ -6363,11 +6393,11 @@ class DarwinPyenvInstallOpts(PyenvInstallOptsProvider):
6363
6393
 
6364
6394
  @async_cached_nullary
6365
6395
  async def brew_tcl_opts(self) -> PyenvInstallOpts:
6366
- if await asyncio_subprocess_try_output('brew', '--prefix', 'tcl-tk') is None:
6396
+ if await asyncio_subprocesses.try_output('brew', '--prefix', 'tcl-tk') is None:
6367
6397
  return PyenvInstallOpts()
6368
6398
 
6369
- tcl_tk_prefix = await asyncio_subprocess_check_output_str('brew', '--prefix', 'tcl-tk')
6370
- tcl_tk_ver_str = await asyncio_subprocess_check_output_str('brew', 'ls', '--versions', 'tcl-tk')
6399
+ tcl_tk_prefix = await asyncio_subprocesses.check_output_str('brew', '--prefix', 'tcl-tk')
6400
+ tcl_tk_ver_str = await asyncio_subprocesses.check_output_str('brew', 'ls', '--versions', 'tcl-tk')
6371
6401
  tcl_tk_ver = '.'.join(tcl_tk_ver_str.split()[1].split('.')[:2])
6372
6402
 
6373
6403
  return PyenvInstallOpts(conf_opts=[
@@ -6488,7 +6518,7 @@ class PyenvVersionInstaller:
6488
6518
  *conf_args,
6489
6519
  ]
6490
6520
 
6491
- await asyncio_subprocess_check_call(
6521
+ await asyncio_subprocesses.check_call(
6492
6522
  *full_args,
6493
6523
  env=env,
6494
6524
  )
@@ -6883,12 +6913,12 @@ class Venv:
6883
6913
  return False
6884
6914
 
6885
6915
  log.info('Using interpreter %s', (ie := await self.interp_exe()))
6886
- await asyncio_subprocess_check_call(ie, '-m', 'venv', dn)
6916
+ await asyncio_subprocesses.check_call(ie, '-m', 'venv', dn)
6887
6917
 
6888
6918
  ve = self.exe()
6889
6919
  uv = self._cfg.use_uv
6890
6920
 
6891
- await asyncio_subprocess_check_call(
6921
+ await asyncio_subprocesses.check_call(
6892
6922
  ve,
6893
6923
  '-m', 'pip',
6894
6924
  'install', '-v', '--upgrade',
@@ -6906,7 +6936,7 @@ class Venv:
6906
6936
  # Caused by: Failed to download distribution due to network timeout. Try increasing UV_HTTP_TIMEOUT (current value: 30s). # noqa
6907
6937
  # UV_CONCURRENT_DOWNLOADS=4 UV_HTTP_TIMEOUT=3600
6908
6938
 
6909
- await asyncio_subprocess_check_call(
6939
+ await asyncio_subprocesses.check_call(
6910
6940
  ve,
6911
6941
  '-m',
6912
6942
  *(['uv'] if uv else []),
@@ -7060,7 +7090,7 @@ class PyprojectCli(ArgparseCli):
7060
7090
  else:
7061
7091
  docker_env[e] = os.environ.get(e, '')
7062
7092
 
7063
- await asyncio_subprocess_check_call(
7093
+ await asyncio_subprocesses.check_call(
7064
7094
  'docker',
7065
7095
  'compose',
7066
7096
  '-f', 'docker/compose.yml',
@@ -7111,7 +7141,7 @@ class PyprojectCli(ArgparseCli):
7111
7141
 
7112
7142
  elif cmd == 'test':
7113
7143
  await venv.create()
7114
- await asyncio_subprocess_check_call(venv.exe(), '-m', 'pytest', *(self.args.args or []), *venv.srcs())
7144
+ await asyncio_subprocesses.check_call(venv.exe(), '-m', 'pytest', *(self.args.args or []), *venv.srcs())
7115
7145
 
7116
7146
  else:
7117
7147
  raise Exception(f'unknown subcommand: {cmd}')