cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
444 lines
12 KiB
Python
444 lines
12 KiB
Python
import sys
|
|
import inspect
|
|
from collections import OrderedDict
|
|
|
|
|
|
class TracebackFancy:
|
|
def __init__(self, traceback):
|
|
self.t = traceback
|
|
|
|
def getFrame(self):
|
|
return FrameFancy(self.t.tb_frame)
|
|
|
|
def getLineNumber(self):
|
|
return self.t.tb_lineno if self.t is not None else None
|
|
|
|
def getNext(self):
|
|
return TracebackFancy(self.t.tb_next)
|
|
|
|
def __str__(self):
|
|
if self.t is None:
|
|
return ""
|
|
str_self = "%s @ %s" % (self.getFrame().getName(), self.getLineNumber())
|
|
return str_self + "\n" + self.getNext().__str__()
|
|
|
|
|
|
class ExceptionFancy:
|
|
def __init__(self, frame):
|
|
self.etraceback = frame.f_exc_traceback
|
|
self.etype = frame.exc_type
|
|
self.evalue = frame.f_exc_value
|
|
|
|
def __init__(self, tb, ty, va):
|
|
self.etraceback = tb
|
|
self.etype = ty
|
|
self.evalue = va
|
|
|
|
def getTraceback(self):
|
|
return TracebackFancy(self.etraceback)
|
|
|
|
def __nonzero__(self):
|
|
return (
|
|
self.etraceback is not None
|
|
or self.etype is not None
|
|
or self.evalue is not None
|
|
)
|
|
|
|
def getType(self):
|
|
return str(self.etype)
|
|
|
|
def getValue(self):
|
|
return self.evalue
|
|
|
|
|
|
class CodeFancy:
|
|
def __init__(self, code):
|
|
self.c = code
|
|
|
|
def getArgCount(self):
|
|
return self.c.co_argcount if self.c is not None else 0
|
|
|
|
def getFilename(self):
|
|
return self.c.co_filename if self.c is not None else ""
|
|
|
|
def getVariables(self):
|
|
return self.c.co_varnames if self.c is not None else []
|
|
|
|
def getName(self):
|
|
return self.c.co_name if self.c is not None else ""
|
|
|
|
def getFileName(self):
|
|
return self.c.co_filename if self.c is not None else ""
|
|
|
|
|
|
class ArgsFancy:
|
|
def __init__(self, frame, arginfo):
|
|
self.f = frame
|
|
self.a = arginfo
|
|
|
|
def __str__(self):
|
|
args, varargs, kwargs = self.getArgs(), self.getVarArgs(), self.getKWArgs()
|
|
ret = ""
|
|
count = 0
|
|
size = len(args)
|
|
for arg in args:
|
|
ret = ret + ("%s = %s" % (arg, args[arg]))
|
|
count = count + 1
|
|
if count < size:
|
|
ret = ret + ", "
|
|
if varargs:
|
|
if size > 0:
|
|
ret = ret + " "
|
|
ret = ret + "varargs are " + str(varargs)
|
|
if kwargs:
|
|
if size > 0:
|
|
ret = ret + " "
|
|
ret = ret + "kwargs are " + str(kwargs)
|
|
return ret
|
|
|
|
def getNumArgs(wantVarargs=False, wantKWArgs=False):
|
|
args, varargs, keywords, values = self.a
|
|
size = len(args)
|
|
if varargs and wantVarargs:
|
|
size = size + len(self.getVarArgs())
|
|
if keywords and wantKWArgs:
|
|
size = size + len(self.getKWArgs())
|
|
return size
|
|
|
|
def getArgs(self):
|
|
args, _, _, values = self.a
|
|
argWValues = OrderedDict()
|
|
for arg in args:
|
|
argWValues[arg] = values[arg]
|
|
return argWValues
|
|
|
|
def getVarArgs(self):
|
|
_, vargs, _, _ = self.a
|
|
if vargs:
|
|
return self.f.f_locals[vargs]
|
|
return ()
|
|
|
|
def getKWArgs(self):
|
|
_, _, kwargs, _ = self.a
|
|
if kwargs:
|
|
return self.f.f_locals[kwargs]
|
|
return {}
|
|
|
|
|
|
class FrameFancy:
|
|
def __init__(self, frame):
|
|
self.f = frame
|
|
|
|
def getCaller(self):
|
|
return FrameFancy(self.f.f_back)
|
|
|
|
def getLineNumber(self):
|
|
return self.f.f_lineno if self.f is not None else 0
|
|
|
|
def getCodeInformation(self):
|
|
return CodeFancy(self.f.f_code) if self.f is not None else None
|
|
|
|
def getExceptionInfo(self):
|
|
return ExceptionFancy(self.f) if self.f is not None else None
|
|
|
|
def getName(self):
|
|
return self.getCodeInformation().getName() if self.f is not None else ""
|
|
|
|
def getFileName(self):
|
|
return self.getCodeInformation().getFileName() if self.f is not None else ""
|
|
|
|
def getLocals(self):
|
|
return self.f.f_locals if self.f is not None else {}
|
|
|
|
def getArgumentInfo(self):
|
|
return (
|
|
ArgsFancy(self.f, inspect.getargvalues(self.f))
|
|
if self.f is not None
|
|
else None
|
|
)
|
|
|
|
|
|
class TracerClass:
|
|
def callEvent(self, frame):
|
|
pass
|
|
|
|
def lineEvent(self, frame):
|
|
pass
|
|
|
|
def returnEvent(self, frame, retval):
|
|
pass
|
|
|
|
def exceptionEvent(self, frame, exception, value, traceback):
|
|
pass
|
|
|
|
def cCallEvent(self, frame, cfunct):
|
|
pass
|
|
|
|
def cReturnEvent(self, frame, cfunct):
|
|
pass
|
|
|
|
def cExceptionEvent(self, frame, cfunct):
|
|
pass
|
|
|
|
|
|
tracer_impl = TracerClass()
|
|
|
|
|
|
def the_tracer_entrypoint(frame, event, args):
|
|
if tracer_impl is None:
|
|
return None
|
|
if event == "call":
|
|
call_retval = tracer_impl.callEvent(FrameFancy(frame))
|
|
if not call_retval:
|
|
return None
|
|
return the_tracer_entrypoint
|
|
elif event == "line":
|
|
line_retval = tracer_impl.lineEvent(FrameFancy(frame))
|
|
if not line_retval:
|
|
return None
|
|
return the_tracer_entrypoint
|
|
elif event == "return":
|
|
tracer_impl.returnEvent(FrameFancy(frame), args)
|
|
elif event == "exception":
|
|
exty, exva, extb = args
|
|
exception_retval = tracer_impl.exceptionEvent(
|
|
FrameFancy(frame), ExceptionFancy(extb, exty, exva)
|
|
)
|
|
if not exception_retval:
|
|
return None
|
|
return the_tracer_entrypoint
|
|
elif event == "c_call":
|
|
tracer_impl.cCallEvent(FrameFancy(frame), args)
|
|
elif event == "c_return":
|
|
tracer_impl.cReturnEvent(FrameFancy(frame), args)
|
|
elif event == "c_exception":
|
|
tracer_impl.cExceptionEvent(FrameFancy(frame), args)
|
|
return None
|
|
|
|
|
|
def enable(t=None):
|
|
global tracer_impl
|
|
if t:
|
|
tracer_impl = t
|
|
sys.settrace(the_tracer_entrypoint)
|
|
|
|
|
|
def disable():
|
|
sys.settrace(None)
|
|
|
|
|
|
class LoggingTracer:
|
|
def callEvent(self, frame):
|
|
print(
|
|
"call "
|
|
+ frame.getName()
|
|
+ " from "
|
|
+ frame.getCaller().getName()
|
|
+ " @ "
|
|
+ str(frame.getCaller().getLineNumber())
|
|
+ " args are "
|
|
+ str(frame.getArgumentInfo())
|
|
)
|
|
|
|
def lineEvent(self, frame):
|
|
print(
|
|
"running "
|
|
+ frame.getName()
|
|
+ " @ "
|
|
+ str(frame.getLineNumber())
|
|
+ " locals are "
|
|
+ str(frame.getLocals())
|
|
+ " in "
|
|
+ frame.getFileName()
|
|
)
|
|
|
|
def returnEvent(self, frame, retval):
|
|
print(
|
|
"return from "
|
|
+ frame.getName()
|
|
+ " value is "
|
|
+ str(retval)
|
|
+ " locals are "
|
|
+ str(frame.getLocals())
|
|
)
|
|
|
|
def exceptionEvent(self, frame, exception):
|
|
print(
|
|
"exception %s %s raised from %s @ %s"
|
|
% (
|
|
exception.getType(),
|
|
str(exception.getValue()),
|
|
frame.getName(),
|
|
frame.getLineNumber(),
|
|
)
|
|
)
|
|
print("tb: " + str(exception.getTraceback()))
|
|
|
|
|
|
# the same functionality as LoggingTracer, but with a little more
|
|
# lldb-specific smarts
|
|
|
|
|
|
class LLDBAwareTracer:
|
|
def callEvent(self, frame):
|
|
if frame.getName() == "<module>":
|
|
return
|
|
if frame.getName() == "run_one_line":
|
|
print(
|
|
"call run_one_line(%s)"
|
|
% (frame.getArgumentInfo().getArgs()["input_string"])
|
|
)
|
|
return
|
|
if "Python.framework" in frame.getFileName():
|
|
print("call into Python at " + frame.getName())
|
|
return
|
|
if (
|
|
frame.getName() == "__init__"
|
|
and frame.getCaller().getName() == "run_one_line"
|
|
and frame.getCaller().getLineNumber() == 101
|
|
):
|
|
return False
|
|
strout = "call " + frame.getName()
|
|
if frame.getCaller().getFileName() == "":
|
|
strout += " from LLDB - args are "
|
|
args = frame.getArgumentInfo().getArgs()
|
|
for arg in args:
|
|
if arg == "dict" or arg == "internal_dict":
|
|
continue
|
|
strout = strout + ("%s = %s " % (arg, args[arg]))
|
|
else:
|
|
strout += (
|
|
" from "
|
|
+ frame.getCaller().getName()
|
|
+ " @ "
|
|
+ str(frame.getCaller().getLineNumber())
|
|
+ " args are "
|
|
+ str(frame.getArgumentInfo())
|
|
)
|
|
print(strout)
|
|
|
|
def lineEvent(self, frame):
|
|
if frame.getName() == "<module>":
|
|
return
|
|
if frame.getName() == "run_one_line":
|
|
print(
|
|
"running run_one_line(%s) @ %s"
|
|
% (
|
|
frame.getArgumentInfo().getArgs()["input_string"],
|
|
frame.getLineNumber(),
|
|
)
|
|
)
|
|
return
|
|
if "Python.framework" in frame.getFileName():
|
|
print(
|
|
"running into Python at "
|
|
+ frame.getName()
|
|
+ " @ "
|
|
+ str(frame.getLineNumber())
|
|
)
|
|
return
|
|
strout = (
|
|
"running "
|
|
+ frame.getName()
|
|
+ " @ "
|
|
+ str(frame.getLineNumber())
|
|
+ " locals are "
|
|
)
|
|
if frame.getCaller().getFileName() == "":
|
|
locals = frame.getLocals()
|
|
for local in locals:
|
|
if local == "dict" or local == "internal_dict":
|
|
continue
|
|
strout = strout + ("%s = %s " % (local, locals[local]))
|
|
else:
|
|
strout = strout + str(frame.getLocals())
|
|
strout = strout + " in " + frame.getFileName()
|
|
print(strout)
|
|
|
|
def returnEvent(self, frame, retval):
|
|
if frame.getName() == "<module>":
|
|
return
|
|
if frame.getName() == "run_one_line":
|
|
print(
|
|
"return from run_one_line(%s) return value is %s"
|
|
% (frame.getArgumentInfo().getArgs()["input_string"], retval)
|
|
)
|
|
return
|
|
if "Python.framework" in frame.getFileName():
|
|
print(
|
|
"return from Python at "
|
|
+ frame.getName()
|
|
+ " return value is "
|
|
+ str(retval)
|
|
)
|
|
return
|
|
strout = (
|
|
"return from "
|
|
+ frame.getName()
|
|
+ " return value is "
|
|
+ str(retval)
|
|
+ " locals are "
|
|
)
|
|
if frame.getCaller().getFileName() == "":
|
|
locals = frame.getLocals()
|
|
for local in locals:
|
|
if local == "dict" or local == "internal_dict":
|
|
continue
|
|
strout = strout + ("%s = %s " % (local, locals[local]))
|
|
else:
|
|
strout = strout + str(frame.getLocals())
|
|
strout = strout + " in " + frame.getFileName()
|
|
print(strout)
|
|
|
|
def exceptionEvent(self, frame, exception):
|
|
if frame.getName() == "<module>":
|
|
return
|
|
print(
|
|
"exception %s %s raised from %s @ %s"
|
|
% (
|
|
exception.getType(),
|
|
str(exception.getValue()),
|
|
frame.getName(),
|
|
frame.getLineNumber(),
|
|
)
|
|
)
|
|
print("tb: " + str(exception.getTraceback()))
|
|
|
|
|
|
def f(x, y=None):
|
|
if x > 0:
|
|
return 2 + f(x - 2)
|
|
return 35
|
|
|
|
|
|
def g(x):
|
|
return 1.134 / x
|
|
|
|
|
|
def print_keyword_args(**kwargs):
|
|
# kwargs is a dict of the keyword args passed to the function
|
|
for key, value in kwargs.items():
|
|
print("%s = %s" % (key, value))
|
|
|
|
|
|
def total(initial=5, *numbers, **keywords):
|
|
count = initial
|
|
for number in numbers:
|
|
count += number
|
|
for key in keywords:
|
|
count += keywords[key]
|
|
return count
|
|
|
|
|
|
if __name__ == "__main__":
|
|
enable(LoggingTracer())
|
|
f(5)
|
|
f(5, 1)
|
|
print_keyword_args(first_name="John", last_name="Doe")
|
|
total(10, 1, 2, 3, vegetables=50, fruits=100)
|
|
try:
|
|
g(0)
|
|
except:
|
|
pass
|
|
disable()
|