mirror of
https://github.com/avrdudes/avrdude.git
synced 2026-09-29 04:18:54 +03:00
Implement Python callback for avrdude_message2()
Doing so requires quite a bit of C preprocessing, since a va_list from C cannot be passed to a Python function directly. Also, using stdout or stderr is not necessarily going to be the correct target for a Python program, so instead, convert the FILE* arguments to names to pass them to Python. By now, the '\v' feature of the message system (conditional newline) is ignored here.
This commit is contained in:
165
src/libavrdude.i
165
src/libavrdude.i
@@ -34,21 +34,17 @@ int ovsigck;
|
||||
const char *partdesc = "";
|
||||
const char *pgmid = "";
|
||||
|
||||
int avrdude_message(int msglvl, const char *format, ...)
|
||||
{
|
||||
int rc = 0;
|
||||
va_list ap;
|
||||
if (verbose >= msglvl) {
|
||||
va_start(ap, format);
|
||||
rc = vfprintf(stderr, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
return rc;
|
||||
static PyObject *msg_cb = NULL;
|
||||
|
||||
void set_msg_callback(PyObject *PyFunc) {
|
||||
if (PyFunc == Py_None)
|
||||
msg_cb = NULL;
|
||||
else
|
||||
msg_cb = PyFunc;
|
||||
}
|
||||
|
||||
|
||||
// This is the version from main.c
|
||||
// Should be handled by something overridable from Python
|
||||
// We cannot pass va_args to Python, so pre-process the message here
|
||||
// in C code.
|
||||
int avrdude_message2(FILE *fp, int lno, const char *file,
|
||||
const char *func, int msgmode, int msglvl,
|
||||
const char *format, ...)
|
||||
@@ -56,97 +52,51 @@ int avrdude_message2(FILE *fp, int lno, const char *file,
|
||||
int rc = 0;
|
||||
va_list ap;
|
||||
|
||||
static struct { // Memorise whether last print ended at beginning of line
|
||||
FILE *fp;
|
||||
int bol; // Are we at the beginning of a line for this fp stream?
|
||||
} bols[5+1]; // Cater for up to 5 different FILE pointers plus one catch-all
|
||||
const char *target = fp == stderr? "stderr": "stdout";
|
||||
|
||||
size_t bi = 0; // bi is index to bols[] array
|
||||
for(bi=0; bi < sizeof bols/sizeof*bols -1; bi++) { // Note the -1, so bi is valid after loop
|
||||
if(!bols[bi].fp) { // First free space
|
||||
bols[bi].fp = fp; // Insert fp in first free space
|
||||
bols[bi].bol = 1; // Assume beginning of line on first use
|
||||
}
|
||||
if(bols[bi].fp == fp)
|
||||
break;
|
||||
}
|
||||
|
||||
if(msglvl <= MSG_ERROR) // Serious error? Free progress bars (if any)
|
||||
if (msglvl <= MSG_ERROR) // Serious error? Free progress bars (if any)
|
||||
report_progress(1, -1, NULL);
|
||||
|
||||
if(msgmode & MSG2_FLUSH) {
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
if (msgmode & MSG2_FLUSH) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
// Reduce effective verbosity level by number of -q above one when printing to stderr
|
||||
if ((quell_progress < 2 || fp != stderr? verbose: verbose+1-quell_progress) >= msglvl) {
|
||||
if(msgmode & MSG2_PROGNAME) {
|
||||
if(!bols[bi].bol)
|
||||
fprintf(fp, "\n");
|
||||
fprintf(fp, "%s", progname);
|
||||
if(verbose >= MSG_NOTICE && (msgmode & MSG2_FUNCTION))
|
||||
fprintf(fp, " %s()", func);
|
||||
if(verbose >= MSG_DEBUG && (msgmode & MSG2_FILELINE)) {
|
||||
const char *pr = strrchr(file, '/'); // Only print basename
|
||||
#if defined (WIN32)
|
||||
if(!pr)
|
||||
pr = strrchr(file, '\\');
|
||||
#endif
|
||||
pr = pr? pr+1: file;
|
||||
fprintf(fp, " [%s:%d]", pr, lno);
|
||||
}
|
||||
if(msgmode & MSG2_TYPE)
|
||||
fprintf(fp, " level %d", msglvl);
|
||||
fprintf(fp, ": ");
|
||||
bols[bi].bol = 0;
|
||||
} else if(msgmode & MSG2_INDENT1) {
|
||||
fprintf(fp, "%*s", (int) strlen(progname)+1, "");
|
||||
bols[bi].bol = 0;
|
||||
} else if(msgmode & MSG2_INDENT2) {
|
||||
fprintf(fp, "%*s", (int) strlen(progname)+2, "");
|
||||
bols[bi].bol = 0;
|
||||
}
|
||||
|
||||
// Vertical tab at start of format string is a conditional new line
|
||||
if(*format == '\v') {
|
||||
format++;
|
||||
if(!bols[bi].bol) {
|
||||
fprintf(fp, "\n");
|
||||
bols[bi].bol = 1;
|
||||
}
|
||||
}
|
||||
// Vertical tab at start of format string is a conditional new line
|
||||
// This feature is ignored here by now, but skip '\v'
|
||||
if (*format == '\v')
|
||||
format++;
|
||||
|
||||
// Figure out whether this print will leave us at beginning of line
|
||||
// Determine required size first
|
||||
va_start(ap, format);
|
||||
rc = vsnprintf(NULL, 0, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
// Determine required size first
|
||||
va_start(ap, format);
|
||||
rc = vsnprintf(NULL, 0, format, ap);
|
||||
va_end(ap);
|
||||
if (rc < 0) // Some errror?
|
||||
return 0;
|
||||
|
||||
if(rc < 0) // Some errror?
|
||||
return 0;
|
||||
rc++; // Accommodate terminating nul
|
||||
char *p = cfg_malloc(__func__, rc);
|
||||
va_start(ap, format);
|
||||
rc = vsnprintf(p, rc, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
rc++; // Accommodate terminating nul
|
||||
char *p = cfg_malloc(__func__, rc);
|
||||
va_start(ap, format);
|
||||
rc = vsnprintf(p, rc, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if(rc < 0) {
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(*p) {
|
||||
fprintf(fp, "%s", p); // Finally: print!
|
||||
bols[bi].bol = p[strlen(p)-1] == '\n';
|
||||
}
|
||||
free(p);
|
||||
if (rc < 0) {
|
||||
free(p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(msgmode & MSG2_FLUSH)
|
||||
fflush(fp);
|
||||
if (*p) {
|
||||
if (msg_cb) {
|
||||
PyObject *result =
|
||||
PyObject_CallFunction(msg_cb, "(sissiis)", target, lno, file, func, msgmode, msglvl, p);
|
||||
Py_XDECREF(result);
|
||||
} else {
|
||||
fprintf(stderr, "Would call msg callback here\n");
|
||||
}
|
||||
free(p);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -189,6 +139,39 @@ typedef struct avrmem AVRMEM;
|
||||
typedef struct programmer_t PROGRAMMER;
|
||||
typedef void pgm_initpgm(PROGRAMMER*);
|
||||
|
||||
enum msglvl {
|
||||
MSG_EXT_ERROR = (-3), // OS-type error, no -v option, can be suppressed with -qqqqq
|
||||
MSG_ERROR = (-2), // Avrdude error, no -v option, can be suppressed with -qqqq
|
||||
MSG_WARNING = (-1), // Warning, no -v option, can be suppressed with -qqq
|
||||
MSG_INFO = 0, // Commentary, no -v option, can be suppressed with -qq
|
||||
MSG_NOTICE = 1, // Displayed with -v
|
||||
MSG_NOTICE2 = 2, // Displayed with -vv
|
||||
MSG_DEBUG = 3, // Displayed with -vvv
|
||||
MSG_TRACE = 4, // Displayed with -vvvv, show trace communication
|
||||
MSG_TRACE2 = 5, // Displayed with -vvvvv
|
||||
};
|
||||
|
||||
enum msgmode {
|
||||
MSG2_PROGNAME = 1, // Start by printing progname
|
||||
MSG2_FUNCTION = 2, // Print calling function (1st arg) after progname if >= notice
|
||||
MSG2_FILELINE = 4, // Print source file and line number after function if >= debug
|
||||
MSG2_TYPE = 8, // Print message type after function or progname
|
||||
MSG2_INDENT1 = 16, // Start by printing indentation of progname+1 blanks
|
||||
MSG2_INDENT2 = 32, // Start by printing indentation of progname+2 blanks
|
||||
MSG2_FLUSH = 64, // Flush before and after printing
|
||||
};
|
||||
|
||||
// Function to record a callback
|
||||
%typemap(in) PyObject *PyFunc {
|
||||
// calling with None removes previous callback
|
||||
if ($input != Py_None && !PyCallable_Check($input)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Need a callable object!");
|
||||
return NULL;
|
||||
}
|
||||
$1 = $input;
|
||||
}
|
||||
void set_msg_callback(PyObject *PyFunc);
|
||||
|
||||
// These things are read from config file(s), and must be considered
|
||||
// read-only by any program. Most internals are only relevant for
|
||||
// libavrdude itself, so only map things to the Python level that are
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
# getavr("m128")
|
||||
|
||||
# ad.set_msg_callback(msg_callback)
|
||||
# ad.cvar.verbose=2
|
||||
# p = ad.locate_part(ad.cvar.part_list, 'm168pb')
|
||||
# ad.avr_initmem(p)
|
||||
# pgm = start_programmer('xplainedmini', 'usb', p)
|
||||
@@ -141,3 +143,38 @@ def stop_programmer(g):
|
||||
g.disable()
|
||||
g.close()
|
||||
g.teardown()
|
||||
|
||||
def message_type(msglvl: int):
|
||||
tnames = ('OS error', 'error', 'warning', 'info', 'notice',
|
||||
'notice2', 'debug', 'trace', 'trace2')
|
||||
msglvl -= ad.MSG_EXT_ERROR # rebase to 0
|
||||
if msglvl > len(tnames):
|
||||
return 'unknown msglvl'
|
||||
else:
|
||||
return tnames(msglvl)
|
||||
|
||||
# rough equivalent of avrdude_message2()
|
||||
# first argument is either "stdout" or "stderr"
|
||||
#
|
||||
# install callback with ad.set_msg_callback(msg_callback)
|
||||
def msg_callback(target: str, lno: int, fname: str, func: str,
|
||||
msgmode: int, msglvl: int, msg: str):
|
||||
if ad.cvar.verbose >= msglvl:
|
||||
s = ""
|
||||
if msgmode & ad.MSG2_PROGNAME:
|
||||
s += ad.cvar.progname
|
||||
if ad.cvar.verbose >= ad.MSG_NOTICE and (msgmode & ad.MSG2_FUNCTION) != 0:
|
||||
s += " " + func + "()"
|
||||
if ad.cvar.verbose >= ad.MSG_DEBUG and (msgmode & ad.MSG2_FILELINE) != 0:
|
||||
n = os.path.basename(fname)
|
||||
s += f" [{n}:{lno}]"
|
||||
if (msgmode & ad.MSG2_TYPE) != 0:
|
||||
s += " " + message_type(msglvl)
|
||||
s += ": "
|
||||
elif (msgmode & ad.MSG2_INDENT1) != 0:
|
||||
s = (len(ad.cvar.progname) + 1) * ' '
|
||||
elif (msgmode & ad.MSG2_INDENT2) != 0:
|
||||
s = (len(ad.cvar.progname) + 2) * ' '
|
||||
s += msg
|
||||
print(s, end='')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user