verboselib.cli.utils._wrap_writer()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 5
dl 0
loc 7
rs 10
c 0
b 0
f 0
cc 1
nop 1
1
import functools
2
import os
3
import subprocess
4
import sys
5
6
if sys.version_info >= (3, 9):
7
  from collections.abc import Callable
8
9
  List  = list
10
  Tuple = tuple
11
12
else:
13
  from typing import Callable
14
  from typing import List
15
  from typing import Tuple
16
17
from typing import Optional
18
19
20
ERROR_CODE = -1
21
22
23
def find_executable(
24
  name:    str,
25
  path:    Optional[str]=None,
26
  pathext: Optional[str]=None,
27
) -> Optional[str]:
28
29
  if path is None:
30
    path = os.environ.get("PATH", "").split(os.pathsep)
31
32
  if isinstance(path, str):
33
    path = [path, ]
34
35
  # check if there are path extensions for Windows executables
36
  if pathext is None:
37
    pathext = os.environ.get("PATHEXT", ".COM;.EXE;.BAT;.CMD")
38
    pathext = pathext.split(os.pathsep)
39
40
  # don't use extensions if the command ends with one of them
41
  for ext in pathext:
42
    if name.endswith(ext):
43
      pathext = ["", ]
44
      break
45
46
  # check if we find the command on PATH
47
  for p in path:
48
    f = os.path.join(p, name)
49
    if os.path.isfile(f):
50
      return f
51
52
    for ext in pathext:
53
      fext = f + ext
54
      if os.path.isfile(fext):
55
        return fext
56
57
  return None
58
59
60
def popen_wrapper(args: List[str]) -> Tuple[str, str, int]:
61
  """
62
  Friendly wrapper for Popen.
63
64
  Returns stdout output, stderr output and OS status code.
65
66
  """
67
  is_windows = (os.name == "nt")
68
  try:
69
    p = subprocess.Popen(
70
      args,
71
      shell=False,
72
      stdout=subprocess.PIPE,
73
      stderr=subprocess.PIPE,
74
      close_fds=(not is_windows),
75
    )
76
  except OSError as e:
77
    raise OSError(f"failed to execute '{args[0]}'") from e
78
  else:
79
    output, errors = p.communicate()
80
    return (
81
      output.decode("utf-8"),
82
      errors.decode("utf-8"),
83
      p.returncode
84
    )
85
86
87
def halt() -> None:
88
  sys.exit(ERROR_CODE)
89
90
91
def show_usage_error_and_halt() -> None:
92
  print_err("run the command with -h/--help flag for usage information")
93
  halt()
94
95
96
def _wrap_writer(writer: Callable[[str], None]) -> Callable[[str], None]:
97
98
  @functools.wraps(writer)
99
  def wrapped(s: str) -> None:
100
    writer(f"{s}\n")
101
102
  return wrapped
103
104
105
print_out = _wrap_writer(sys.stdout.write)
106
print_err = _wrap_writer(sys.stderr.write)
107