Skip to content

Commit a19790a

Browse files
Merge pull request #1 from Adwaith-Rajesh/target
Target
2 parents 0af9760 + 4bb5b78 commit a19790a

File tree

6 files changed

+94
-17
lines changed

6 files changed

+94
-17
lines changed

.pre-commit-config.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ repos:
1818
rev: v2.0.4
1919
hooks:
2020
- id: autopep8
21+
args: ["--max-line-length", "120", "--in-place"]
2122
- repo: https://github.com/asottile/reorder_python_imports
2223
rev: v3.10.0
2324
hooks:

buildme/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
from buildme.core import CommandRunner # noqa: F401
2+
from buildme.core import target # noqa: F401
3+
from buildme.core import TargetData # noqa: F401
24
from buildme.hfuncs import * # noqa: F401 F403

buildme/cli.py

+23-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
import argparse
2+
import sys
3+
from itertools import compress
4+
from typing import Any
5+
6+
from buildme.core import _check_target_exists
7+
from buildme.core import _exec_target
28

39

410
def _get_buildme_file_contents(filepath: str) -> str:
@@ -10,6 +16,10 @@ def _create_build_script_code(old_code: str, targets: list[str], usr_opt_var_nam
1016
return old_code + ''.join(f'\n{t}({usr_opt_var_name})' for t in targets)
1117

1218

19+
def _check_target_exists_map(targets: list[str]) -> list[bool]:
20+
return [_check_target_exists(name) for name in targets]
21+
22+
1323
def main() -> int:
1424
parser = argparse.ArgumentParser()
1525
user_arg_parser = argparse.ArgumentParser()
@@ -25,18 +35,19 @@ def main() -> int:
2535
user_arg_parser.add_argument(a.split('=')[0], type=str)
2636

2737
usr_known_args, _ = user_arg_parser.parse_known_args()
28-
usr_known_args_var_name = f'{usr_known_args=}'.split(
29-
'=')[0] # gets the var name as string
30-
31-
new_buildme_code = _create_build_script_code(
32-
_get_buildme_file_contents(args.path),
33-
args.targets,
34-
usr_known_args_var_name
35-
)
36-
37-
# the dangerous part
38-
exec(new_buildme_code, {
39-
usr_known_args_var_name: usr_known_args, **globals()})
38+
# gets the var name as string
39+
usr_known_args_var_name = f'{usr_known_args=}'.split('=')[0] # noqa: F841
40+
41+
target_globals: dict[str, Any] = {}
42+
exec(_get_buildme_file_contents(args.path), target_globals)
43+
44+
if not all(invalid_map := _check_target_exists_map(targets=args.targets)):
45+
print('unknown target(s): ' + ' '.join(compress(args.targets, [not i for i in invalid_map])),
46+
file=sys.stderr)
47+
return 1
48+
49+
for t in args.targets:
50+
_exec_target(t, usr_known_args, target_globals)
4051

4152
return 0
4253

buildme/core.py

+51-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import shlex
2+
import sys
3+
from argparse import Namespace
4+
from functools import wraps
25
from subprocess import Popen
6+
from typing import Any
7+
from typing import Callable
8+
from typing import NamedTuple
39

410

511
class CommandRunner:
@@ -11,7 +17,7 @@ def __init__(self, shell: bool = False, print_cmd: bool = True,
1117
self.print_cmd_sep = print_cmd_sep
1218

1319
def run(self, cmd: str) -> int:
14-
if self.print_cmd:
20+
if self.print_cmd_sep:
1521
print('=' * 80)
1622

1723
if self.print_cmd:
@@ -20,15 +26,56 @@ def run(self, cmd: str) -> int:
2026
p = Popen(args=shlex.split(cmd), shell=self.shell)
2127
p.wait()
2228

23-
if self.print_cmd:
29+
if self.print_cmd_sep:
2430
print('=' * 80)
2531

2632
if self.exit_non_zero and p.returncode != 0:
27-
print(
28-
f'Last command exited with non zero exit code. [{p.returncode}]')
33+
print(f'Last command exited with non zero exit code. [{p.returncode}]')
2934
exit(1)
3035

3136
return p.returncode
3237

3338
def set_exit_non_zero(self, val: bool) -> None:
3439
self.exit_non_zero = val
40+
41+
42+
class TargetData(NamedTuple):
43+
name: str
44+
depends: list[str] = []
45+
46+
47+
TargetFuncType = Callable[[Namespace, TargetData], None]
48+
WrapTargetFuncType = Callable[[Namespace], None]
49+
50+
_targets = {}
51+
52+
53+
def target(depends: list[str] = []) -> Callable[[TargetFuncType], WrapTargetFuncType]:
54+
def target_dec(fn: TargetFuncType) -> WrapTargetFuncType:
55+
_targets[fn.__name__] = TargetData(name=fn.__name__, depends=depends)
56+
57+
@wraps(fn)
58+
def target_wrap(opts: Namespace) -> None:
59+
fn(opts, _targets[fn.__name__])
60+
return target_wrap
61+
return target_dec
62+
63+
64+
def _get_target_data(name: str) -> TargetData | None:
65+
return _targets.get(name, None)
66+
67+
68+
def _check_target_exists(name: str) -> bool: return name in _targets
69+
70+
71+
def _exec_target(name: str, opts: Namespace, target_globals: dict[str, Any]) -> None:
72+
if name not in _targets:
73+
print(f'unknown target: {name}', file=sys.stderr)
74+
exit(1)
75+
76+
if callable(fn := target_globals[name]):
77+
t_data = _get_target_data(name)
78+
if t_data:
79+
for d in t_data.depends:
80+
_exec_target(d, opts, target_globals)
81+
fn(opts)

buildme/hfuncs.py

+16
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
# helper funcs
2+
import glob
3+
import shutil
24
from pathlib import Path
35

46

@@ -10,4 +12,18 @@ def touch(filepath: str) -> None:
1012
Path(filepath).touch()
1113

1214

15+
def rmdir(path: str) -> None:
16+
if Path(path).is_dir():
17+
shutil.rmtree(path)
18+
19+
20+
def rm(path: str) -> None:
21+
Path(path).unlink(missing_ok=True)
22+
23+
24+
def get_files_in_dir(dir_pattern: str, recurse: bool = False,
25+
include_hidden: bool = False) -> list[str]:
26+
return glob.glob(dir_pattern, recursive=recurse, include_hidden=include_hidden)
27+
28+
1329
create_file = touch

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = buildme
3-
version = 0.1.0
3+
version = 0.2.0
44
author = Adwaith Rajesh
55
author_email = adwaithrajesh3180@gmail.com
66
description = A simple command runner disguised as a build system.

0 commit comments

Comments
 (0)