Merge pull request #759 from zielmicha/stdmsg
Write tracebacks to stderr instead of stdout.
This commit is contained in:
commit
259d205ff6
8 changed files with 84 additions and 41 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -63,6 +63,8 @@ examples/cross_calculator/android/tags
|
||||||
/examples/allany
|
/examples/allany
|
||||||
/examples/cairoex
|
/examples/cairoex
|
||||||
/examples/cgiex
|
/examples/cgiex
|
||||||
|
/examples/cgi/cgi_stacktrace
|
||||||
|
/examples/cgi/example
|
||||||
/examples/curlex
|
/examples/curlex
|
||||||
/examples/debugging
|
/examples/debugging
|
||||||
/examples/docstrings
|
/examples/docstrings
|
||||||
|
|
|
||||||
11
examples/cgi/cgi_server.py
Normal file
11
examples/cgi/cgi_server.py
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
import BaseHTTPServer
|
||||||
|
import CGIHTTPServer
|
||||||
|
|
||||||
|
server = BaseHTTPServer.HTTPServer
|
||||||
|
handler = CGIHTTPServer.CGIHTTPRequestHandler
|
||||||
|
server_address = ('localhost', 8008)
|
||||||
|
handler.cgi_directories = ['/']
|
||||||
|
|
||||||
|
httpd = server(server_address, handler)
|
||||||
|
httpd.serve_forever()
|
||||||
5
examples/cgi/cgi_stacktrace.nim
Normal file
5
examples/cgi/cgi_stacktrace.nim
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import cgi
|
||||||
|
cgi.setStackTraceStdout()
|
||||||
|
|
||||||
|
var a: string = nil
|
||||||
|
a.add "foobar"
|
||||||
7
examples/cgi/example.nim
Normal file
7
examples/cgi/example.nim
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import cgi
|
||||||
|
|
||||||
|
write(stdout, "Content-type: text/html\n\n")
|
||||||
|
write(stdout, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n")
|
||||||
|
write(stdout, "<html><head><title>Test</title></head><body>\n")
|
||||||
|
write(stdout, "Hello!")
|
||||||
|
writeln(stdout, "</body></html>")
|
||||||
|
|
@ -342,16 +342,35 @@ proc writeContentType*() =
|
||||||
##
|
##
|
||||||
## .. code-block:: Nimrod
|
## .. code-block:: Nimrod
|
||||||
## write(stdout, "Content-type: text/html\n\n")
|
## write(stdout, "Content-type: text/html\n\n")
|
||||||
##
|
|
||||||
## It also modifies the debug stack traces so that they contain
|
|
||||||
## ``<br />`` and are easily readable in a browser.
|
|
||||||
write(stdout, "Content-type: text/html\n\n")
|
write(stdout, "Content-type: text/html\n\n")
|
||||||
system.stackTraceNewLine = "<br />\n"
|
|
||||||
|
|
||||||
proc setStackTraceNewLine*() =
|
proc resetForStacktrace() =
|
||||||
## Modifies the debug stack traces so that they contain
|
stdout.write """<!--: spam
|
||||||
## ``<br />`` and are easily readable in a browser.
|
Content-Type: text/html
|
||||||
system.stackTraceNewLine = "<br />\n"
|
|
||||||
|
<body bgcolor=#f0f0f8><font color=#f0f0f8 size=-5> -->
|
||||||
|
<body bgcolor=#f0f0f8><font color=#f0f0f8 size=-5> --> -->
|
||||||
|
</font> </font> </font> </script> </object> </blockquote> </pre>
|
||||||
|
</table> </table> </table> </table> </table> </font> </font> </font>
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc writeErrorMessage*(data: string) =
|
||||||
|
## Tries to reset browser state and writes `data` to stdout in
|
||||||
|
## <plaintext> tag.
|
||||||
|
resetForStacktrace()
|
||||||
|
# We use <plaintext> here, instead of escaping, so stacktrace can
|
||||||
|
# be understood by human looking at source.
|
||||||
|
stdout.write("<plaintext>\n")
|
||||||
|
stdout.write(data)
|
||||||
|
|
||||||
|
proc setStackTraceStdout*() =
|
||||||
|
## Makes Nimrod output stacktraces to stdout, instead of server log.
|
||||||
|
errorMessageWriter = writeErrorMessage
|
||||||
|
|
||||||
|
proc setStackTraceNewLine*() {.deprecated.} =
|
||||||
|
## Makes Nimrod output stacktraces to stdout, instead of server log.
|
||||||
|
## Depracated alias for setStackTraceStdout.
|
||||||
|
setStackTraceStdout()
|
||||||
|
|
||||||
proc setCookie*(name, value: string) =
|
proc setCookie*(name, value: string) =
|
||||||
## Sets a cookie.
|
## Sets a cookie.
|
||||||
|
|
@ -374,4 +393,3 @@ when isMainModule:
|
||||||
const test1 = "abc\L+def xyz"
|
const test1 = "abc\L+def xyz"
|
||||||
assert UrlEncode(test1) == "abc%0A%2Bdef+xyz"
|
assert UrlEncode(test1) == "abc%0A%2Bdef+xyz"
|
||||||
assert UrlDecode(UrlEncode(test1)) == test1
|
assert UrlDecode(UrlEncode(test1)) == test1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1951,15 +1951,13 @@ when not defined(JS): #and not defined(NimrodVM):
|
||||||
## The standard output stream.
|
## The standard output stream.
|
||||||
stderr* {.importc: "stderr", header: "<stdio.h>".}: TFile
|
stderr* {.importc: "stderr", header: "<stdio.h>".}: TFile
|
||||||
## The standard error stream.
|
## The standard error stream.
|
||||||
##
|
|
||||||
## Note: In my opinion, this should not be used -- the concept of a
|
when defined(useStdoutAsStdmsg):
|
||||||
## separate error stream is a design flaw of UNIX. A separate *message
|
template stdmsg*: TFile = stdout
|
||||||
## stream* is a good idea, but since it is named ``stderr`` there are few
|
else:
|
||||||
## programs out there that distinguish properly between ``stdout`` and
|
template stdmsg*: TFile = stderr
|
||||||
## ``stderr``. So, that's what you get if you don't name your variables
|
## Template which expands to either stdout or stderr depending on
|
||||||
## appropriately. It also annoys people if redirection
|
## `useStdoutAsStdmsg` compile-time switch.
|
||||||
## via ``>output.txt`` does not work because the program writes
|
|
||||||
## to ``stderr``.
|
|
||||||
|
|
||||||
proc Open*(f: var TFile, filename: string,
|
proc Open*(f: var TFile, filename: string,
|
||||||
mode: TFileMode = fmRead, bufSize: int = -1): Bool {.tags: [].}
|
mode: TFileMode = fmRead, bufSize: int = -1): Bool {.tags: [].}
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,13 @@
|
||||||
# use the heap (and nor exceptions) do not include the GC or memory allocator.
|
# use the heap (and nor exceptions) do not include the GC or memory allocator.
|
||||||
|
|
||||||
var
|
var
|
||||||
stackTraceNewLine*: string ## undocumented feature; it is replaced by ``<br>``
|
errorMessageWriter*: (proc(msg: string): void {.tags: [FWriteIO].})
|
||||||
## for CGI applications
|
## Function that will be called
|
||||||
|
## instead of stdmsg.write when printing stacktrace.
|
||||||
template stackTraceNL: expr =
|
## Unstable API.
|
||||||
(if IsNil(stackTraceNewLine): "\n" else: stackTraceNewLine)
|
|
||||||
|
|
||||||
when not defined(windows) or not defined(guiapp):
|
when not defined(windows) or not defined(guiapp):
|
||||||
proc writeToStdErr(msg: CString) = write(stdout, msg)
|
proc writeToStdErr(msg: CString) = write(stdmsg, msg)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {.
|
proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {.
|
||||||
|
|
@ -27,6 +26,12 @@ else:
|
||||||
proc writeToStdErr(msg: CString) =
|
proc writeToStdErr(msg: CString) =
|
||||||
discard MessageBoxA(0, msg, nil, 0)
|
discard MessageBoxA(0, msg, nil, 0)
|
||||||
|
|
||||||
|
proc showErrorMessage(data: cstring) =
|
||||||
|
if errorMessageWriter != nil:
|
||||||
|
errorMessageWriter($data)
|
||||||
|
else:
|
||||||
|
writeToStdErr(data)
|
||||||
|
|
||||||
proc chckIndx(i, a, b: int): int {.inline, compilerproc.}
|
proc chckIndx(i, a, b: int): int {.inline, compilerproc.}
|
||||||
proc chckRange(i, a, b: int): int {.inline, compilerproc.}
|
proc chckRange(i, a, b: int): int {.inline, compilerproc.}
|
||||||
proc chckRangeF(x, a, b: float): float {.inline, compilerproc.}
|
proc chckRangeF(x, a, b: float): float {.inline, compilerproc.}
|
||||||
|
|
@ -111,7 +116,7 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
|
||||||
add(s, tempDlInfo.dli_sname)
|
add(s, tempDlInfo.dli_sname)
|
||||||
else:
|
else:
|
||||||
add(s, '?')
|
add(s, '?')
|
||||||
add(s, stackTraceNL)
|
add(s, "\n")
|
||||||
else:
|
else:
|
||||||
if dlresult != 0 and tempDlInfo.dli_sname != nil and
|
if dlresult != 0 and tempDlInfo.dli_sname != nil and
|
||||||
c_strcmp(tempDlInfo.dli_sname, "signalHandler") == 0'i32:
|
c_strcmp(tempDlInfo.dli_sname, "signalHandler") == 0'i32:
|
||||||
|
|
@ -172,21 +177,18 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
|
||||||
add(s, ')')
|
add(s, ')')
|
||||||
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
|
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
|
||||||
add(s, tempFrames[j].procname)
|
add(s, tempFrames[j].procname)
|
||||||
add(s, stackTraceNL)
|
add(s, "\n")
|
||||||
|
|
||||||
when hasSomeStackTrace:
|
when hasSomeStackTrace:
|
||||||
proc rawWriteStackTrace(s: var string) =
|
proc rawWriteStackTrace(s: var string) =
|
||||||
when nimrodStackTrace:
|
when nimrodStackTrace:
|
||||||
if framePtr == nil:
|
if framePtr == nil:
|
||||||
add(s, "No stack traceback available")
|
add(s, "No stack traceback available\n")
|
||||||
add(s, stackTraceNL)
|
|
||||||
else:
|
else:
|
||||||
add(s, "Traceback (most recent call last)")
|
add(s, "Traceback (most recent call last)\n")
|
||||||
add(s, stackTraceNL)
|
|
||||||
auxWriteStackTrace(framePtr, s)
|
auxWriteStackTrace(framePtr, s)
|
||||||
elif defined(nativeStackTrace) and nativeStackTraceSupported:
|
elif defined(nativeStackTrace) and nativeStackTraceSupported:
|
||||||
add(s, "Traceback from system (most recent call last)")
|
add(s, "Traceback from system (most recent call last)\n")
|
||||||
add(s, stackTraceNL)
|
|
||||||
auxWriteStackTraceWithBacktrace(s)
|
auxWriteStackTraceWithBacktrace(s)
|
||||||
else:
|
else:
|
||||||
add(s, "No stack traceback available\n")
|
add(s, "No stack traceback available\n")
|
||||||
|
|
@ -207,7 +209,7 @@ proc raiseExceptionAux(e: ref E_Base) =
|
||||||
pushCurrentException(e)
|
pushCurrentException(e)
|
||||||
c_longjmp(excHandler.context, 1)
|
c_longjmp(excHandler.context, 1)
|
||||||
elif e[] of EOutOfMemory:
|
elif e[] of EOutOfMemory:
|
||||||
writeToStdErr(e.name)
|
showErrorMessage(e.name)
|
||||||
quitOrDebug()
|
quitOrDebug()
|
||||||
else:
|
else:
|
||||||
when hasSomeStackTrace:
|
when hasSomeStackTrace:
|
||||||
|
|
@ -219,7 +221,7 @@ proc raiseExceptionAux(e: ref E_Base) =
|
||||||
add(buf, " [")
|
add(buf, " [")
|
||||||
add(buf, $e.name)
|
add(buf, $e.name)
|
||||||
add(buf, "]\n")
|
add(buf, "]\n")
|
||||||
writeToStdErr(buf)
|
showErrorMessage(buf)
|
||||||
else:
|
else:
|
||||||
# ugly, but avoids heap allocations :-)
|
# ugly, but avoids heap allocations :-)
|
||||||
template xadd(buf, s, slen: expr) =
|
template xadd(buf, s, slen: expr) =
|
||||||
|
|
@ -235,7 +237,7 @@ proc raiseExceptionAux(e: ref E_Base) =
|
||||||
add(buf, " [")
|
add(buf, " [")
|
||||||
xadd(buf, e.name, c_strlen(e.name))
|
xadd(buf, e.name, c_strlen(e.name))
|
||||||
add(buf, "]\n")
|
add(buf, "]\n")
|
||||||
writeToStdErr(buf)
|
showErrorMessage(buf)
|
||||||
quitOrDebug()
|
quitOrDebug()
|
||||||
|
|
||||||
proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} =
|
proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} =
|
||||||
|
|
@ -255,9 +257,9 @@ proc WriteStackTrace() =
|
||||||
when hasSomeStackTrace:
|
when hasSomeStackTrace:
|
||||||
var s = ""
|
var s = ""
|
||||||
rawWriteStackTrace(s)
|
rawWriteStackTrace(s)
|
||||||
writeToStdErr(s)
|
showErrorMessage(s)
|
||||||
else:
|
else:
|
||||||
writeToStdErr("No stack traceback available\n")
|
showErrorMessage("No stack traceback available\n")
|
||||||
|
|
||||||
proc getStackTrace(): string =
|
proc getStackTrace(): string =
|
||||||
when hasSomeStackTrace:
|
when hasSomeStackTrace:
|
||||||
|
|
@ -298,13 +300,13 @@ when not defined(noSignalHandler):
|
||||||
var buf = newStringOfCap(2000)
|
var buf = newStringOfCap(2000)
|
||||||
rawWriteStackTrace(buf)
|
rawWriteStackTrace(buf)
|
||||||
processSignal(sig, buf.add) # nice hu? currying a la nimrod :-)
|
processSignal(sig, buf.add) # nice hu? currying a la nimrod :-)
|
||||||
writeToStdErr(buf)
|
showErrorMessage(buf)
|
||||||
GC_enable()
|
GC_enable()
|
||||||
else:
|
else:
|
||||||
var msg: cstring
|
var msg: cstring
|
||||||
template asgn(y: expr) = msg = y
|
template asgn(y: expr) = msg = y
|
||||||
processSignal(sig, asgn)
|
processSignal(sig, asgn)
|
||||||
writeToStdErr(msg)
|
showErrorMessage(msg)
|
||||||
when defined(endb): dbgAborting = True
|
when defined(endb): dbgAborting = True
|
||||||
quit(1) # always quit when SIGABRT
|
quit(1) # always quit when SIGABRT
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ Changes affecting backwards compatibility
|
||||||
using the new ``OSLastError`` proc.
|
using the new ``OSLastError`` proc.
|
||||||
- ``os.parentDir`` now returns "" if there is no parent dir.
|
- ``os.parentDir`` now returns "" if there is no parent dir.
|
||||||
|
|
||||||
|
|
||||||
Compiler Additions
|
Compiler Additions
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|
@ -41,7 +40,8 @@ Compiler Additions
|
||||||
over the generated code.
|
over the generated code.
|
||||||
- The compiler now supports a ``computedGoto`` pragma to support very fast
|
- The compiler now supports a ``computedGoto`` pragma to support very fast
|
||||||
dispatching for interpreters and the like.
|
dispatching for interpreters and the like.
|
||||||
|
- In CGI scripts stacktraces are shown user only if cgi.setStackTraceStdout
|
||||||
|
is used.
|
||||||
|
|
||||||
Language Additions
|
Language Additions
|
||||||
------------------
|
------------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue