Skip to content

k3handy

Action-CI Documentation Status Package

Handy alias of mostly used functions. A collection of utility shortcuts for common operations.

k3handy is a component of pykit3 project: a python3 toolkit set.

Installation

pip install k3handy

Quick Start

from k3handy import fread, fwrite, cmdx, dd

# File operations
fwrite('/tmp/hello.txt', 'Hello World')
content = fread('/tmp/hello.txt')

# Command execution
cmdx('ls', '-la')

# Debug output
dd('debug message')

API Reference

k3handy

k3handy is collection of mostly used utilities.

CmdFlag

Bases: str, Enum

Command execution flags for cmdf() and related functions.

Flags control subprocess behavior and return value formatting. Can be used individually or combined in lists.

Examples:

>>> cmdf("ls", flag=CmdFlag.RAISE)
>>> cmdf("git", "status", flag=[CmdFlag.RAISE, CmdFlag.STDOUT])
>>> cmdf("echo", "hi", flag=CMD_RAISE_ONELINE)
Source code in k3handy/cmdutil.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class CmdFlag(str, Enum):
    """Command execution flags for cmdf() and related functions.

    Flags control subprocess behavior and return value formatting.
    Can be used individually or combined in lists.

    Examples:
        >>> cmdf("ls", flag=CmdFlag.RAISE)
        >>> cmdf("git", "status", flag=[CmdFlag.RAISE, CmdFlag.STDOUT])
        >>> cmdf("echo", "hi", flag=CMD_RAISE_ONELINE)
    """

    RAISE = "raise"  # Raise CalledProcessError if return code != 0
    TTY = "tty"  # Start subprocess in a tty
    NONE = "none"  # Return None if return code != 0
    PASS = "pass"  # Don't capture stdin/stdout/stderr
    STDOUT = "stdout"  # Return stdout as list[str]
    ONELINE = "oneline"  # Return first line of stdout

cmd0(cmd, *arguments, **options)

Alias to k3proc.command() with check=True

Returns:

Name Type Description
str str

first line of stdout.

Source code in k3handy/cmdutil.py
168
169
170
171
172
173
174
175
176
177
178
179
180
def cmd0(cmd: str | Sequence[str], *arguments: str, **options: Any) -> str:
    """
    Alias to k3proc.command() with ``check=True``

    Returns:
        str: first line of stdout.
    """
    dd("cmd0:", cmd, arguments, options)
    _, out, _ = cmdx(cmd, *arguments, **options)
    dd("cmd0: out:", out)
    if len(out) > 0:
        return out[0]
    return ""

cmdf(cmd, *arguments, flag='', **options)

Alias to k3proc.command(). Behavior is specified with flag

Parameters:

Name Type Description Default
cmd str | Sequence[str]

the path to executable

required
arguments str

command arguments

()
flag CmdFlagType

Execution flags (use CmdFlag enum recommended, strings also supported)

Individual flags (use CmdFlag enum): - CmdFlag.RAISE: raise CalledProcessError if return code != 0 - CmdFlag.TTY: start subprocess in a tty - CmdFlag.NONE: return None if return code != 0 - CmdFlag.PASS: don't capture stdin/stdout/stderr - CmdFlag.STDOUT: return stdout as list[str] - CmdFlag.ONELINE: return first line of stdout

Preset combinations: - CMD_RAISE_STDOUT: [CmdFlag.RAISE, CmdFlag.STDOUT] - CMD_RAISE_ONELINE: [CmdFlag.RAISE, CmdFlag.ONELINE] - CMD_NONE_ONELINE: [CmdFlag.NONE, CmdFlag.ONELINE]

String forms also supported: 'raise', 'tty', 'none', 'pass', 'stdout', 'oneline'

.. deprecated:: Single-letter flags are deprecated, use CmdFlag enum or full names instead: - 'x' or ('raise',): raise CalledProcessError if return code != 0 - 't' or ('tty',): start subprocess in a tty - 'n' or ('none',): return None if return code != 0 - 'p' or ('pass',): don't capture stdin/stdout/stderr - 'o' or ('stdout',): return stdout as list[str] - '0' or ('oneline',): return first line of stdout

''
options Any

other options passed to k3proc.command()

{}

Returns:

Type Description
None | list[str] | str | tuple[int, list[str], list[str]]

Varies based on flags: - With ONELINE: str (first line of stdout) - With STDOUT: list[str] (all stdout lines) - With NONE: None if error, otherwise normal return - Default: tuple[int, list[str], list[str]] (code, stdout, stderr)

Examples:

Using enum constants (recommended):

>>> cmdf("ls", "-la", flag=CmdFlag.RAISE)
>>> cmdf("git", "status", flag=[CmdFlag.RAISE, CmdFlag.STDOUT])
>>> branch = cmdf("git", "branch", "--show-current", flag=CMD_RAISE_ONELINE)

String forms (also supported):

>>> cmdf("ls", flag="raise")
>>> cmdf("ls", flag=["raise", "stdout"])
Source code in k3handy/cmdutil.py
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
def cmdf(
    cmd: str | Sequence[str],
    *arguments: str,
    flag: CmdFlagType = "",
    **options: Any,
) -> None | list[str] | str | tuple[int, list[str], list[str]]:
    """
    Alias to k3proc.command(). Behavior is specified with ``flag``

    Args:
        cmd: the path to executable

        arguments: command arguments

        flag: Execution flags (use CmdFlag enum recommended, strings also supported)

            Individual flags (use CmdFlag enum):
                - CmdFlag.RAISE: raise CalledProcessError if return code != 0
                - CmdFlag.TTY: start subprocess in a tty
                - CmdFlag.NONE: return None if return code != 0
                - CmdFlag.PASS: don't capture stdin/stdout/stderr
                - CmdFlag.STDOUT: return stdout as list[str]
                - CmdFlag.ONELINE: return first line of stdout

            Preset combinations:
                - CMD_RAISE_STDOUT: [CmdFlag.RAISE, CmdFlag.STDOUT]
                - CMD_RAISE_ONELINE: [CmdFlag.RAISE, CmdFlag.ONELINE]
                - CMD_NONE_ONELINE: [CmdFlag.NONE, CmdFlag.ONELINE]

            String forms also supported: 'raise', 'tty', 'none', 'pass', 'stdout', 'oneline'

            .. deprecated::
                Single-letter flags are deprecated, use CmdFlag enum or full names instead:
                    - 'x' or ('raise',): raise CalledProcessError if return code != 0
                    - 't' or ('tty',): start subprocess in a tty
                    - 'n' or ('none',): return None if return code != 0
                    - 'p' or ('pass',): don't capture stdin/stdout/stderr
                    - 'o' or ('stdout',): return stdout as list[str]
                    - '0' or ('oneline',): return first line of stdout

        options: other options passed to k3proc.command()

    Returns:
        Varies based on flags:
            - With ONELINE: str (first line of stdout)
            - With STDOUT: list[str] (all stdout lines)
            - With NONE: None if error, otherwise normal return
            - Default: tuple[int, list[str], list[str]] (code, stdout, stderr)

    Examples:
        Using enum constants (recommended):

        >>> cmdf("ls", "-la", flag=CmdFlag.RAISE)
        >>> cmdf("git", "status", flag=[CmdFlag.RAISE, CmdFlag.STDOUT])
        >>> branch = cmdf("git", "branch", "--show-current", flag=CMD_RAISE_ONELINE)

        String forms (also supported):

        >>> cmdf("ls", flag="raise")
        >>> cmdf("ls", flag=["raise", "stdout"])
    """
    dd("cmdf:", cmd, arguments, options)
    dd("flag:", flag)
    flag = parse_flag(flag)

    if "raise" in flag:
        options["check"] = True
    if "tty" in flag:
        options["tty"] = True
    if "pass" in flag:
        options["capture"] = False

    code, out, err = command(cmd, *arguments, **options)

    # reaching here means there is no check of exception
    if code != 0 and "none" in flag:
        return None

    out_lines = out.splitlines() if isinstance(out, str) else out.decode().splitlines()
    err_lines = err.splitlines() if isinstance(err, str) else err.decode().splitlines()

    if "stdout" in flag:
        dd("cmdf: out:", out_lines)
        return out_lines

    if "oneline" in flag:
        dd("cmdf: out:", out_lines)
        if len(out_lines) > 0:
            return out_lines[0]
        return ""

    return code, out_lines, err_lines

cmdout(cmd, *arguments, **options)

Alias to k3proc.command() with check=True.

Returns:

Name Type Description
list list[str]

stdout in lines of str.

Source code in k3handy/cmdutil.py
183
184
185
186
187
188
189
190
191
192
193
194
def cmdout(cmd: str | Sequence[str], *arguments: str, **options: Any) -> list[str]:
    """
    Alias to k3proc.command() with ``check=True``.

    Returns:
        list: stdout in lines of str.
    """

    dd("cmdout:", cmd, arguments, options)
    _, out, _ = cmdx(cmd, *arguments, **options)
    dd("cmdout: out:", out)
    return out

cmdpass(cmd, *arguments, **options)

Alias to k3proc.command() with check=True capture=False. It just passes stdout and stderr to calling process.

Returns:

Type Description
(int, list, list)

exit code and empty stdout and stderr.

Source code in k3handy/cmdutil.py
228
229
230
231
232
233
234
235
236
237
238
239
def cmdpass(cmd: str | Sequence[str], *arguments: str, **options: Any) -> tuple[int, list[str], list[str]]:
    """
    Alias to k3proc.command() with ``check=True`` ``capture=False``.
    It just passes stdout and stderr to calling process.

    Returns:
        (int, list, list): exit code and empty stdout and stderr.
    """
    # interactive mode, delegate stdin to sub proc
    dd("cmdpass:", cmd, arguments, options)
    options["capture"] = False
    return cmdx(cmd, *arguments, **options)

cmdtty(cmd, *arguments, **options)

Alias to k3proc.command() with check=True tty=True. As if the command is run in a tty.

Returns:

Type Description
(int, list, list)

exit code, stdout and stderr in lines of str.

Source code in k3handy/cmdutil.py
214
215
216
217
218
219
220
221
222
223
224
225
def cmdtty(cmd: str | Sequence[str], *arguments: str, **options: Any) -> tuple[int, list[str], list[str]]:
    """
    Alias to k3proc.command() with ``check=True`` ``tty=True``.
    As if the command is run in a tty.

    Returns:
        (int, list, list): exit code, stdout and stderr in lines of str.
    """

    dd("cmdtty:", cmd, arguments, options)
    options["tty"] = True
    return cmdx(cmd, *arguments, **options)

cmdx(cmd, *arguments, **options)

Alias to k3proc.command() with check=True.

Returns:

Type Description
(int, list, list)

exit code, stdout and stderr in lines of str.

Source code in k3handy/cmdutil.py
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
def cmdx(cmd: str | Sequence[str], *arguments: str, **options: Any) -> tuple[int, list[str], list[str]]:
    """
    Alias to k3proc.command() with ``check=True``.

    Returns:
        (int, list, list): exit code, stdout and stderr in lines of str.
    """
    dd("cmdx:", cmd, arguments, options)
    ddstack()

    options["check"] = True
    code, out, err = command(cmd, *arguments, **options)
    out_lines = out.splitlines() if isinstance(out, str) else out.decode().splitlines()
    err_lines = err.splitlines() if isinstance(err, str) else err.decode().splitlines()
    return code, out_lines, err_lines

dd(*msg)

Alias to logger.debug()

Source code in k3handy/cmdutil.py
49
50
51
52
53
54
55
def dd(*msg: Any) -> None:
    """
    Alias to logger.debug()
    """
    msg_strs = [str(x) for x in msg]
    msg_str = " ".join(msg_strs)
    logger.debug(msg_str, **ddstack_kwarg)

ddstack(*msg)

Log calling stack in logging.DEBUG level.

Source code in k3handy/cmdutil.py
58
59
60
61
62
63
64
65
66
67
68
69
70
71
def ddstack(*msg: Any) -> None:
    """
    Log calling stack in logging.DEBUG level.
    """

    if logger.isEnabledFor(logging.DEBUG):
        stack = inspect.stack()[1:]
        for i, (frame, path, ln, func, lines, xx) in enumerate(stack):
            #  python -c "xxx" does not have a line
            if lines is None:
                line_str = ""
            else:
                line_str = lines[0].strip()
            logger.debug("stack: %d %s %s", ln, func, line_str, **ddstack_kwarg)

display(stdout, stderr=None)

Output to stdout and stderr. - display(1, "foo") write to stdout. - display(1, ["foo", "bar"]) write multilines to stdout. - display(1, ("foo", "bar")) write multilines to stdout. - display(("foo", "bar"), ["woo"]) write multilines to stdout and stderr. - display(None, ["woo"]) write multilines to stderr.

Source code in k3handy/__init__.py
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def display(
    stdout: int | str | Sequence[str] | None,
    stderr: str | Sequence[str] | None = None,
) -> None:
    """
    Output to stdout and stderr.
    - ``display(1, "foo")`` write to stdout.
    - ``display(1, ["foo", "bar"])`` write multilines to stdout.
    - ``display(1, ("foo", "bar"))`` write multilines to stdout.
    - ``display(("foo", "bar"), ["woo"])`` write multilines to stdout and stderr.
    - ``display(None, ["woo"])`` write multilines to stderr.

    """

    if isinstance(stdout, int):
        fd = stdout
        line = stderr

        if isinstance(line, (list, tuple)):
            lines = line
            for ln in lines:
                display(fd, ln)
            return

        os.write(fd, to_bytes(line))
        os.write(fd, b"\n")
        return

    if stdout is not None:
        display(1, stdout)

    if stderr is not None:
        display(2, stderr)

pabs(*args)

Alias to os.path.abspath() and os.path.normpath()

pabs('a').startswith(os.path.abspath(".")) True

pabs('a')[-2:] '/a'

Source code in k3handy/path.py
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def pabs(*args: str) -> str:
    """
    Alias to os.path.abspath() and os.path.normpath()

    >>> pabs('a').startswith(os.path.abspath("."))
    True

    >>> pabs('a')[-2:]
    '/a'


    """
    p = os.path.abspath(pjoin(*args))
    return os.path.normpath(p)

parse_flag(*flags)

Convert short form flag into tuple form, e.g.: parse_flag('x0') output: ('raise', 'oneline')

'-x' will remove flag 'x'. parse_flag('x0-x') output ('online', )

parse_flag(['raise', 'oneline', '-raise']) outputs ('oneline', )

parse_flag(['raise', 'oneline', '-raise'], 't') outputs ('oneline', 'tty', )

.. deprecated:: Single-letter flags ('x', 't', 'n', 'p', 'o', '0') are deprecated. Use full names ('raise', 'tty', 'none', 'pass', 'stdout', 'oneline') instead.

Source code in k3handy/cmdutil.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
def parse_flag(*flags: str | CmdFlag | Sequence[str | CmdFlag]) -> tuple[str, ...]:
    """
    Convert short form flag into tuple form, e.g.:
    parse_flag('x0') output: ('raise', 'oneline')

    '-x' will remove flag 'x'.
    parse_flag('x0-x') output ('online', )

    parse_flag(['raise', 'oneline', '-raise']) outputs ('oneline', )

    parse_flag(['raise', 'oneline', '-raise'], 't') outputs ('oneline', 'tty', )

    .. deprecated::
        Single-letter flags ('x', 't', 'n', 'p', 'o', '0') are deprecated.
        Use full names ('raise', 'tty', 'none', 'pass', 'stdout', 'oneline') instead.

    """

    expanded: list[str] = []
    for flag in flags:
        f = expand_flag(flag)
        expanded.extend(f)

    #  reduce

    res: dict[str, bool] = {}
    for key in expanded:
        if key.startswith("-"):
            key = key[1:]
            if key in res:
                del res[key]
        else:
            res[key] = True

    result = tuple(res.keys())

    return result

pjoin(*args)

Alias to os.path.join()

pjoin("a", "b") 'a/b'

pjoin("a", "b", "c") 'a/b/c'

Source code in k3handy/path.py
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def pjoin(*args: str) -> str:
    """
    Alias to os.path.join()

    >>> pjoin("a", "b")
    'a/b'

    >>> pjoin("a", "b", "c")
    'a/b/c'

    """
    return os.path.join(*args)

prebase(base, *pseg)

Rebaase path pseg on to base and returns the absolute path.

prebase("/a", "b") '/a/b'

prebase("/a", "b", "c") '/a/b/c'

prebase("/a", "/b") '/b'

prebase("/a", "b/../c") '/a/c'

prebase("/a") '/a'

repr(prebase("a", None)) 'None'

prebase(None, "/b") '/b'

Args:

base: base path, aka the left part.

*pseg: segments to append to ``base``.

Returns:

Name Type Description
str str | None

the path joined

Source code in k3handy/path.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
def prebase(base: str | None, *pseg: str | None) -> str | None:
    """
    Rebaase path pseg on to base and returns the absolute path.

    >>> prebase("/a", "b")
    '/a/b'

    >>> prebase("/a", "b", "c")
    '/a/b/c'

    >>> prebase("/a", "/b")
    '/b'

    >>> prebase("/a", "b/../c")
    '/a/c'

    >>> prebase("/a")
    '/a'

    >>> repr(prebase("a", None))
    'None'

    >>> prebase(None, "/b")
    '/b'

    Args:

        base: base path, aka the left part.

        *pseg: segments to append to ``base``.

    Returns:
        str: the path joined
    """

    for p in pseg:
        if p is None:
            return None

        if os.path.isabs(p):
            base = p

        if base is None:
            base = pabs(p)

        base = pabs(base, p)

    return base

License

The MIT License (MIT) - Copyright (c) 2015 Zhang Yanpo (张炎泼)