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

idlesign / makeapp / 25419139842

06 May 2026 06:01AM UTC coverage: 88.838% (-0.1%) from 88.971%
25419139842

Pull #9

github

web-flow
Merge 9707532c7 into 6c41965aa
Pull Request #9: Add windows support

11 of 15 new or added lines in 4 files covered. (73.33%)

1 existing line in 1 file now uncovered.

963 of 1084 relevant lines covered (88.84%)

3.55 hits per line

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

73.68
/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 temp_dir() -> Generator[str, None, None]:
4✔
54
    """Context manager to temporarily create a directory."""
55

56
    dir_tmp = tempfile.mkdtemp(prefix='makeapp_')
×
57

58
    try:
×
59
        yield dir_tmp
×
60

61
    finally:
62
        shutil.rmtree(dir_tmp, ignore_errors=True)
×
63

64

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

68
    :param filepath:
69
    :param pairs: search -> replace.
70

71
    """
72
    with fileinput.input(files=filepath, inplace=True) as f:
×
73

74
        for line in f:
×
75

76
            for search, replace in pairs.items():
×
77
                line = line.replace(search, replace)
×
78

79
            sys.stdout.write(line)
×
80

81

82
def check_command(command: str, *, hint: str):
4✔
83
    """Checks whether a command is available.
84
    If not - raises an exception.
85

86
    :param command:
87
    :param hint:
88

89
    """
90
    if shutil.which(command) is None:
4✔
UNCOV
91
        raise CommandError(
×
92
            f"Failed to execute '{command}' command. "
93
            f"Check {hint} is installed and available.")
94

95

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

99
    Returns a list of strings gathered from a command.
100

101
    :param command:
102
    :param err_msg: Message to show on error.
103
    :param env: Environment variables to use.
104
    :param capture: Capture stdout and stderr and return as lines.
105

106
    :raises: CommandError
107

108
    """
109
    if env:
4✔
110
        env = {**os.environ, **env}
4✔
111

112
    LOG.debug(f'Run command: {command} ...')
4✔
113
    kwargs = {}
4✔
114

115
    if capture:
4✔
116
        kwargs = {'stdout': PIPE, 'stderr': STDOUT}
4✔
117

118
    prc = Popen(command, shell=True, universal_newlines=True, env=env, **kwargs)
4✔
119
    out, _ = prc.communicate()
4✔
120

121
    if out:
4✔
122
        LOG.debug(indent(out, prefix="    "))
4✔
123
        data = [stripped for item in out.splitlines() if (stripped := item.strip())]
4✔
124

125
    else:
126
        data = []
4✔
127

128
    if prc.returncode:
4✔
129
        raise CommandError(err_msg or f"Command `{command}` failed: %s" % '\n'.join(data))
3✔
130

131
    return data
4✔
132

133

134
class Ruff:
4✔
135
    """Ruff wrapper."""
136

137
    @classmethod
4✔
138
    def _run(cls, cmd: str) -> list[str]:
4✔
139
        return run_command(f'ruff {cmd}', capture=False)
×
140

141
    @classmethod
4✔
142
    def check(cls, *, fix: bool = True) -> list[str]:
4✔
143
        return cls._run(f'check{" --fix" if fix else ""}')
×
144

145

146
class MkDocs:
4✔
147
    """MkDocs wrapper."""
148

149
    @classmethod
4✔
150
    def _run(cls, cmd: str) -> list[str]:
4✔
151
        return run_command(f'mkdocs {cmd}', capture=False)
×
152

153
    @classmethod
4✔
154
    def serve(cls) -> list[str]:
4✔
155
        return cls._run('serve -o')
×
156

157
    @classmethod
4✔
158
    def build(cls) -> list[str]:
4✔
159
        return cls._run('build')
×
160

161

162
class Uv:
4✔
163
    """Uv wrapper."""
164

165
    @classmethod
4✔
166
    def exec(cls, cmd: str, env: dict | None = None) -> list[str]:
4✔
167
        return run_command(f'uv {cmd}', env=env, capture=False)
4✔
168

169
    @classmethod
4✔
170
    def upgrade(cls) -> list[str]:
4✔
171
        return cls.exec('self update')
×
172

173
    @classmethod
4✔
174
    def tool_install(cls, name: str) -> list[str]:
4✔
175
        return cls.exec(f'tool install {name}')
×
176

177
    @classmethod
4✔
178
    def tool_upgrade(cls, name: str) -> list[str]:
4✔
179
        return cls.exec(f'tool upgrade {name} --reinstall')
×
180

181
    @classmethod
4✔
182
    def sync(cls) -> list[str]:
4✔
183
        return cls.exec('sync')
4✔
184

185
    @classmethod
4✔
186
    def install(cls):
4✔
NEW
187
        if sys.platform == 'win32':
×
NEW
188
            cmd = 'powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"'
×
189
        else:
NEW
190
            cmd = 'curl -LsSf https://astral.sh/uv/install.sh | sh'
×
NEW
191
        return run_command(cmd, 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