• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

idlesign / makeapp / 18238566457

04 Oct 2025 02:48AM UTC coverage: 88.648% (-1.5%) from 90.121%
18238566457

push

github

idlesign
Fix commit message leading spaces.

1 of 1 new or added line in 1 file covered. (100.0%)

26 existing lines in 3 files now uncovered.

859 of 969 relevant lines covered (88.65%)

3.55 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.82
/src/makeapp/utils.py
1
import configparser
4✔
2
import fileinput
4✔
3
import logging
4✔
4
import os
4✔
5
import shutil
4✔
6
import sys
4✔
7
import tempfile
4✔
8
from collections.abc import Generator
4✔
9
from configparser import ConfigParser
4✔
10
from contextlib import contextmanager
4✔
11
from pathlib import Path
4✔
12
from subprocess import PIPE, STDOUT, Popen
4✔
13
from textwrap import indent
4✔
14

15
from .exceptions import CommandError
4✔
16

17
LOG = logging.getLogger(__name__)
4✔
18
PYTHON_VERSION = sys.version_info
4✔
19

20

21
def configure_logging(
4✔
22
        level: int | None = None,
23
        logger: logging.Logger | None = None,
24
        format: str = '%(message)s'
25
):
26
    """Switches on logging at a given level. For a given logger or globally.
27

28
    :param level:
29
    :param logger:
30
    :param format:
31

32
    """
33
    logging.basicConfig(format=format, level=level if level else None)
4✔
34
    logger and logger.setLevel(level or logging.INFO)
4✔
35

36

37
def get_user_dir() -> Path:
4✔
38
    """Returns the user's home directory."""
39
    return Path(os.path.expanduser('~'))
4✔
40

41

42
def read_ini(fpath: Path) -> ConfigParser:
4✔
43
    """Read a .ini file.
44

45
    :param fpath:
46
    """
47
    cfg = configparser.ConfigParser()
×
48
    cfg.read(f'{fpath}')
×
49
    return cfg
×
50

51

52
@contextmanager
4✔
53
def chdir(target_path):
4✔
54
    """Context manager.
55
     
56
    Temporarily switches the current working directory.
57
    
58
    """
59
    curr_dir = os.getcwd()
4✔
60
    os.chdir(target_path)
4✔
61

62
    try:
4✔
63
        yield
4✔
64

65
    finally:
66
        os.chdir(curr_dir)
4✔
67

68

69
@contextmanager
4✔
70
def temp_dir() -> Generator[str, None, None]:
4✔
71
    """Context manager to temporarily create a directory."""
72

73
    dir_tmp = tempfile.mkdtemp(prefix='makeapp_')
×
74

75
    try:
×
76
        yield dir_tmp
×
77

78
    finally:
79
        shutil.rmtree(dir_tmp, ignore_errors=True)
×
80

81

82
def replace_infile(filepath: str, pairs: dict[str, str]):
4✔
83
    """Replaces some term by another in file contents.
84

85
    :param filepath:
86
    :param pairs: search -> replace.
87

88
    """
89
    with fileinput.input(files=filepath, inplace=True) as f:
×
90

91
        for line in f:
×
92

93
            for search, replace in pairs.items():
×
94
                line = line.replace(search, replace)
×
95

96
            sys.stdout.write(line)
×
97

98

99
def check_command(command: str, *, hint: str):
4✔
100
    """Checks whether a command is available.
101
    If not - raises an exception.
102

103
    :param command:
104
    :param hint:
105

106
    """
107
    try:
4✔
108
        run_command(f'type {command}')
4✔
109

110
    except CommandError:
×
111
        raise CommandError(
×
112
            f"Failed to execute '{command}' command. "
113
            f"Check {hint} is installed and available.")
114

115

116
def run_command(command: str, *, err_msg: str = '', env: dict | None = None, capture: bool = True) -> list[str]:
4✔
117
    """Runs a command in a shell process.
118

119
    Returns a list of strings gathered from a command.
120

121
    :param command:
122
    :param err_msg: Message to show on error.
123
    :param env: Environment variables to use.
124
    :param capture: Capture stdout and stderr and return as lines.
125

126
    :raises: CommandError
127

128
    """
129
    if env:
4✔
UNCOV
130
        env = {**os.environ, **env}
×
131

132
    LOG.debug(f'Run command: {command} ...')
4✔
133
    kwargs = {}
4✔
134

135
    if capture:
4✔
136
        kwargs = {'stdout': PIPE, 'stderr': STDOUT}
4✔
137

138
    prc = Popen(command, shell=True, universal_newlines=True, env=env, **kwargs)
4✔
139
    out, _ = prc.communicate()
4✔
140

141
    if out:
4✔
142
        LOG.debug(indent(out, prefix="    "))
4✔
143
        data = [stripped for item in out.splitlines() if (stripped := item.strip())]
4✔
144

145
    else:
146
        data = []
4✔
147

148
    if prc.returncode:
4✔
UNCOV
149
        raise CommandError(err_msg or f"Command `{command}` failed: %s" % '\n'.join(data))
×
150

151
    return data
4✔
152

153

154
class Ruff:
4✔
155
    """Ruff wrapper."""
156

157
    @classmethod
4✔
158
    def _run(cls, cmd: str) -> list[str]:
4✔
UNCOV
159
        return run_command(f'ruff {cmd}', capture=False)
×
160

161
    @classmethod
4✔
162
    def check(cls, fix: bool = True) -> list[str]:
4✔
UNCOV
163
        return cls._run(f'check{" --fix" if fix else ""}')
×
164

165

166
class Uv:
4✔
167
    """Uv wrapper."""
168

169
    @classmethod
4✔
170
    def _run(cls, cmd: str) -> list[str]:
4✔
171
        return run_command(f'uv {cmd}', capture=False)
4✔
172

173
    @classmethod
4✔
174
    def upgrade(cls) -> list[str]:
4✔
UNCOV
175
        return cls._run('self update')
×
176

177
    @classmethod
4✔
178
    def tool_install(cls, name: str) -> list[str]:
4✔
UNCOV
179
        return cls._run(f'tool install {name}')
×
180

181
    @classmethod
4✔
182
    def tool_upgrade(cls, name: str) -> list[str]:
4✔
UNCOV
183
        return cls._run(f'tool upgrade {name} --reinstall')
×
184

185
    @classmethod
4✔
186
    def sync(cls) -> list[str]:
4✔
187
        return cls._run('sync')
4✔
188

189
    @classmethod
4✔
190
    def install(cls):
4✔
UNCOV
191
        return run_command('curl -LsSf https://astral.sh/uv/install.sh | sh', capture=False)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc