1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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
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
|
# This file is not mean to be used!!!
# ONLY FOR INTERNAL DEBUG
# wraps each method in a derived class of:
# - VersionSourceInterface
# - BuildHookInterface
# etc.
# wraps this:
# site-packages\pyproject_hooks\_impl.py ->
# def _call_hook(self, hook_name, kwargs):
# ...
# from hatch_ci._support import tracer
# with tracer.decorator(_in_proc_script_path)() as script:
# change the OUTPUT below
from __future__ import annotations
import functools
import inspect
import io
import json
import os
import sys
from pathlib import Path
from typing import Any
OUTPUT = None # eg. r"C:\Users\antonio\Projects\github\hatch-ci\NOTES.txt"
def is_scm(path: Path) -> str:
return "<git>" if (Path(path) / ".git").exists() else "<plain>"
def jformat(data: Any, pre: str) -> str:
return json.dumps(data, indent=2, sort_keys=True).replace(
"\n", f"\n{' ' * len(pre)}"
)
class Tracer:
def __init__(self, path: Path | str | None = None):
self.path = Path(path) if path else None
def decorator(self, fn):
if fn.__name__ == "_in_proc_script_path":
return self.wraps__in_proc_script_path(fn)
else:
return self.wraps_interface(fn)
def append(self, txt):
if not self.path:
return
with self.path.open("a") as fp:
fp.write(txt.getvalue() if hasattr(txt, "getvalue") else txt)
def wraps__in_proc_script_path(self, fn):
# this is mean to wrap the _in_proc_script_path function
@functools.wraps(fn)
def _fn(*args, **kwargs):
# ok, looking to wrap _call_hook in _impl.py
frames = [
f
for f in inspect.getouterframes(inspect.currentframe())
if "_impl.py" in f.filename and f.function == "_call_hook"
]
if len(frames) != 1:
raise RuntimeError("cannot find _call_hook in _impl.py frame")
variables = frames[0].frame.f_locals
if not self.path:
fp = io.StringIO()
print("", file=fp)
print(f"[pid={os.getpid()}]", file=fp)
print(f"<hook> '{variables['hook_name']}'", file=fp)
scm = (
"<git>"
if (Path(variables["self"].source_dir) / ".git").exists()
else "<plain>"
)
print(
f" cwd (self.source_dir): {variables['self'].source_dir} {scm}",
file=fp,
)
print(f" kwargs: {variables['kwargs']}", file=fp)
print(f" extra_environ: {variables['extra_environ']}", file=fp)
print(" input.json:", file=fp)
pre = " input.json: "
print(f"{pre}{jformat(variables['hook_input'], pre=pre)}", file=fp)
self.append(fp)
return fn(*args, **kwargs)
return _fn
def wraps_interface(self, method):
# a method decorator
# you should wrap methods of subclasses of:
# - VersionSourceInterface
# - BuildHookInterface
# etc.
@functools.wraps(method)
def _fn(slf, *args, **kwargs):
if self.path:
fp = io.StringIO()
print("", file=fp)
print(f"[pid={os.getpid()}] {sys.argv=}", file=fp)
print(
f"!callback! '{method.__name__}' {slf.__class__.__name__}", file=fp
)
print(f" self.root: {slf.root} {is_scm(slf.root)}", file=fp)
print(f" config: {jformat(slf.config, ' config: ')}", file=fp)
self.append(fp)
return method(slf, *args, **kwargs)
return _fn if self.path else method
tracer = Tracer() # r"C:\Users\antonio\Projects\github\hatch-ci\NOTES.txt")
|