Return with non-zero return value when internal program errors occurred.

This commit is contained in:
Bastian Kleineidam 2011-12-14 22:54:26 +01:00
parent 00003517b1
commit 21532a70ec
17 changed files with 806 additions and 361 deletions

View file

@ -7,6 +7,8 @@ Fixes:
Closes: SF bug #3438553
- checking: Do not remove whitespace inside URLs given on the
commandline or GUI. Only remove whitespace at the start and end.
- cmdline: Return with non-zero exit value when internal program
errors occurred.
Changes:
- gui: Display all options in one dialog instead of tabbed panes.

990
doc/de.po

File diff suppressed because it is too large Load diff

View file

@ -89,11 +89,13 @@ Definieren Sie einen regulären Ausdruck der eine Warnung ausgibt falls er
auf den Inhalt einer geprüften URL zutrifft. Dies gilt nur für gültige
Seiten deren Inhalt wir bekommen können.
.br
Use this to check for pages that contain some form of error, for example
"This page has moved" or "Oracle Application error".
Benutzen Sie dies, um nach Seiten zu suchen, welche bestimmte Fehler
enthalten, zum Beispiel "Diese Seite ist umgezogen" oder "Oracle
Applikationsfehler".
.br
Note that multiple values can be combined in the regular expression, for
example "(This page has moved|Oracle Application error)".
Man beachte, dass mehrere Werte in dem regulären Ausdruck kombiniert
werden können, zum Beispiel "(Diese Seite ist umgezogen|Oracle
Applikationsfehler)".
.br
Siehe Abschnitt \fBREGULAR EXPRESSIONS\fP für weitere Infos.
.TP
@ -495,13 +497,17 @@ Proxy\-Server kontaktiert werden
\fBLC_MESSAGES\fP, \fBLANG\fP, \fBLANGUAGE\fP \- gibt Ausgabesprache an
.
.SH RÜCKGABEWERT
Der Rückgabewert ist nicht Null falls
Der Rückgabewert ist 2 falls
.IP \(bu
ein Programmfehler aufgetreten ist.
.PP
Der Rückgabewert ist 1 falls
.IP \(bu
ungültige Verknüpfungen gefunden wurden oder
.IP \(bu
Warnungen gefunden wurden und Warnungen aktiviert sind
.IP \(bu
ein Programmfehler aufgetreten ist.
.PP
Sonst ist der Rückgabewert Null.
.
.SH LIMITIERUNGEN
LinkChecker benutzt Hauptspeicher für jede zu prüfende URL, die in der

View file

@ -504,13 +504,17 @@ same as the host of the user browsing your pages.
\fBLC_MESSAGES\fP, \fBLANG\fP, \fBLANGUAGE\fP - specify output language
.
.SH RETURN VALUE
The return value is non-zero when
The return value is 2 when
.IP \(bu
a program error occurred.
.PP
The return value is 1 when
.IP \(bu
invalid links were found or
.IP \(bu
link warnings were found and warnings are enabled
.IP \(bu
a program error occurred.
.PP
Else the return value is zero.
.
.SH LIMITATIONS
LinkChecker consumes memory for each queued URL to check. With thousands

View file

@ -7,7 +7,7 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2011-10-19 22:32+0300\n"
"POT-Creation-Date: 2011-12-14 22:36+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -1318,38 +1318,48 @@ msgstr ""
#. type: Plain text
#: en/linkchecker.1:508
msgid "The return value is non-zero when"
msgid "The return value is 2 when"
msgstr ""
#. type: IP
#: en/linkchecker.1:508 en/linkchecker.1:510 en/linkchecker.1:512
#: en/linkchecker.1:508 en/linkchecker.1:512 en/linkchecker.1:514
#, no-wrap
msgid "\\(bu"
msgstr ""
#. type: Plain text
#: en/linkchecker.1:510
msgid "invalid links were found or"
msgid "a program error occurred."
msgstr ""
#. type: Plain text
#: en/linkchecker.1:512
msgid "link warnings were found and warnings are enabled"
msgid "The return value is 1 when"
msgstr ""
#. type: Plain text
#: en/linkchecker.1:514
msgid "a program error occurred."
msgid "invalid links were found or"
msgstr ""
#. type: Plain text
#: en/linkchecker.1:516
msgid "link warnings were found and warnings are enabled"
msgstr ""
#. type: Plain text
#: en/linkchecker.1:518
msgid "Else the return value is zero."
msgstr ""
#. type: SH
#: en/linkchecker.1:515
#: en/linkchecker.1:519
#, no-wrap
msgid "LIMITATIONS"
msgstr ""
#. type: Plain text
#: en/linkchecker.1:519
#: en/linkchecker.1:523
msgid ""
"LinkChecker consumes memory for each queued URL to check. With thousands of "
"queued URLs the amount of consumed memory can become quite large. This might "
@ -1357,70 +1367,70 @@ msgid ""
msgstr ""
#. type: SH
#: en/linkchecker.1:520
#: en/linkchecker.1:524
#, no-wrap
msgid "FILES"
msgstr ""
#. type: Plain text
#: en/linkchecker.1:522
#: en/linkchecker.1:526
msgid "B<~/.linkchecker/linkcheckerrc> - default configuration file"
msgstr ""
#. type: Plain text
#: en/linkchecker.1:524
#: en/linkchecker.1:528
msgid "B<~/.linkchecker/blacklist> - default blacklist logger output filename"
msgstr ""
#. type: Plain text
#: en/linkchecker.1:526
#: en/linkchecker.1:530
msgid "B<linkchecker-out.>I<TYPE> - default logger file output name"
msgstr ""
#. type: Plain text
#: en/linkchecker.1:528
#: en/linkchecker.1:532
msgid ""
"B<http://docs.python.org/library/codecs.html#standard-encodings> - valid "
"output encodings"
msgstr ""
#. type: Plain text
#: en/linkchecker.1:530
#: en/linkchecker.1:534
msgid ""
"B<http://docs.python.org/howto/regex.html> - regular expression "
"documentation"
msgstr ""
#. type: SH
#: en/linkchecker.1:531 en/linkcheckerrc.5:514 en/linkchecker-gui.1:11
#: en/linkchecker.1:535 en/linkcheckerrc.5:514 en/linkchecker-gui.1:11
#, no-wrap
msgid "SEE ALSO"
msgstr ""
#. type: Plain text
#: en/linkchecker.1:533
#: en/linkchecker.1:537
msgid "B<linkcheckerrc>(5)"
msgstr ""
#. type: SH
#: en/linkchecker.1:534 en/linkcheckerrc.5:517 en/linkchecker-gui.1:14
#: en/linkchecker.1:538 en/linkcheckerrc.5:517 en/linkchecker-gui.1:14
#, no-wrap
msgid "AUTHOR"
msgstr ""
#. type: Plain text
#: en/linkchecker.1:536 en/linkcheckerrc.5:519 en/linkchecker-gui.1:16
#: en/linkchecker.1:540 en/linkcheckerrc.5:519 en/linkchecker-gui.1:16
msgid "Bastian Kleineidam E<lt>calvin@users.sourceforge.netE<gt>"
msgstr ""
#. type: SH
#: en/linkchecker.1:537 en/linkcheckerrc.5:520 en/linkchecker-gui.1:17
#: en/linkchecker.1:541 en/linkcheckerrc.5:520 en/linkchecker-gui.1:17
#, no-wrap
msgid "COPYRIGHT"
msgstr ""
#. type: Plain text
#: en/linkchecker.1:538 en/linkcheckerrc.5:521
#: en/linkchecker.1:542 en/linkcheckerrc.5:521
msgid "Copyright \\(co 2000-2011 Bastian Kleineidam"
msgstr ""

View file

@ -148,6 +148,7 @@ def check_urls (aggregate):
# from badly-programmed libraries that raise all kinds of strange
# exceptions.
console.internal_error()
aggregate.logger.log_internal_error()
abort(aggregate)
# Not catched exceptions at this point are SystemExit and GeneratorExit,
# and both should be handled by the calling layer.

View file

@ -33,14 +33,13 @@ def check_url (urlqueue, logger):
urlqueue.task_done(url_data)
class Checker (task.CheckedTask):
class Checker (task.LoggedCheckedTask):
"""URL check thread."""
def __init__ (self, urlqueue, logger):
"""Store URL queue and logger."""
super(Checker, self).__init__()
super(Checker, self).__init__(logger)
self.urlqueue = urlqueue
self.logger = logger
self.origname = self.getName()
def run_checked (self):

View file

@ -16,7 +16,7 @@
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""Cleanup task."""
import time
from . import task
from . import task, console
class Cleanup (task.CheckedTask):
@ -34,3 +34,7 @@ class Cleanup (task.CheckedTask):
# clean every 15 seconds
while not self.stopped(15):
self.connections.remove_expired()
def internal_error (self):
"""Print internal error to console."""
console.internal_error()

View file

@ -73,3 +73,9 @@ class Logger (object):
transport = url_data.to_wire()
for log in self.logs:
log.log_filter_url(transport, do_print)
@synchronized(_lock)
def log_internal_error (self):
"""Document that an internal error occurred."""
for logger in self.logs:
logger.log_internal_error()

View file

@ -19,14 +19,13 @@ import time
from . import task
class Status (task.CheckedTask):
class Status (task.LoggedCheckedTask):
"""Status thread."""
def __init__ (self, urlqueue, logger, wait_seconds):
"""Store urlqueue object."""
super(Status, self).__init__()
super(Status, self).__init__(logger)
self.urlqueue = urlqueue
self.logger = logger
self.wait_seconds = wait_seconds
def run_checked (self):

View file

@ -31,9 +31,27 @@ class CheckedTask (threader.StoppableThread):
log.warn(LOG_CHECK, "interrupt did not reach the main thread")
thread.interrupt_main()
except Exception:
console.internal_error()
self.internal_error()
@notimplemented
def run_checked (self):
"""Overload in subclass."""
pass
@notimplemented
def internal_error (self):
"""Overload in subclass."""
pass
class LoggedCheckedTask (CheckedTask):
"""URL check task with a logger instance and internal error handling."""
def __init__ (self, logger):
super(CheckedTask, self).__init__()
self.logger = logger
def internal_error (self):
"""Log an internal error on console and the logger."""
console.internal_error()
self.logger.log_internal_error()

View file

@ -2,7 +2,7 @@
# Form implementation generated from reading ui file 'ui/options.ui'
#
# Created: Tue Dec 13 07:09:29 2011
# Created: Wed Dec 14 22:41:04 2011
# by: PyQt4 UI code generator 4.8.6
#
# WARNING! All changes made in this file will be lost!
@ -23,7 +23,7 @@ class Ui_Options(object):
self.verticalLayout_3 = QtGui.QVBoxLayout(Options)
self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3"))
self.groupBox_2 = QtGui.QGroupBox(Options)
self.groupBox_2.setTitle(_("Scanning options"))
self.groupBox_2.setTitle(_("Checking options"))
self.groupBox_2.setObjectName(_fromUtf8("groupBox_2"))
self.verticalLayout = QtGui.QVBoxLayout(self.groupBox_2)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))

View file

@ -23,7 +23,7 @@
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Scanning options</string>
<string>Checking options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>

View file

@ -70,16 +70,18 @@ class LogStatistics (object):
def reset (self):
"""Reset all log statistics to default values."""
# number of logged urls
# number of logged URLs
self.number = 0
# number of encountered errors
# number of encountered URL errors
self.errors = 0
# number of errors that were printed
# number of URL errors that were printed
self.errors_printed = 0
# number of warnings
# number of URL warnings
self.warnings = 0
# number of warnings that were printed
# number of URL warnings that were printed
self.warnings_printed = 0
# number of internal errors
self.internal_errors = 0
self.domains = set()
self.link_types = ContentTypes.copy()
self.max_url_length = 0
@ -120,6 +122,10 @@ class LogStatistics (object):
# calculate running average
self.avg_url_length += (l - self.avg_url_length) / self.avg_number
def log_internal_error (self):
"""Increase internal error count."""
self.internal_errors += 1
class Logger (object):
"""
@ -273,8 +279,7 @@ class Logger (object):
self.start_fileoutput()
if self.fd is None:
# Happens when aborting threads times out
log.warn(LOG_CHECK,
"writing to unitialized or closed file")
log.warn(LOG_CHECK, "writing to unitialized or closed file")
else:
self.fd.write(s, **args)
@ -389,7 +394,13 @@ class Logger (object):
except (IOError, AttributeError):
pass
# note: don't confuse URL loggers with application logs above
def log_internal_error (self):
"""Indicate that an internal error occurred in the program."""
log.warn(LOG_CHECK, "internal error occurred")
self.stats.log_internal_error()
# the standard URL logger implementations
from .text import TextLogger
from .html import HtmlLogger
from .gml import GMLLogger
@ -402,7 +413,7 @@ from .customxml import CustomXMLLogger
from .none import NoneLogger
# default link logger classes
# default URL logger classes
Loggers = {
"text": TextLogger,
"html": HtmlLogger,

View file

@ -283,6 +283,11 @@ class HtmlLogger (Logger):
(self.stats.errors - self.stats.errors_printed))
self.writeln(u".")
self.writeln(u"<br>")
num = self.stats.internal_errors
if num:
self.write(_n("There was %(num)d internal error.",
"There were %(num)d internal errors.", num) % {"num": num})
self.writeln(u"<br>")
self.stoptime = time.time()
duration = self.stoptime - self.starttime
self.writeln(_("Stopped checking at %(time)s (%(duration)s)") %

View file

@ -220,6 +220,10 @@ class TextLogger (Logger):
self.write(_(" (%d duplicates not printed)") %
(self.stats.errors - self.stats.errors_printed))
self.writeln(u".")
num = self.stats.internal_errors
if num:
self.writeln(_n("There was %(num)d internal error.",
"There were %(num)d internal errors.", num) % {"num": num})
self.stoptime = time.time()
duration = self.stoptime - self.starttime
self.writeln(_("Stopped checking at %(time)s (%(duration)s)") %

View file

@ -800,8 +800,10 @@ if do_profile:
else:
check_urls(aggregate)
# if printed errors or warnings are encountered, exit with non-zero status
stats = config['logger'].stats
if stats.errors_printed or \
(stats.warnings_printed and config['warnings']):
# on internal errors, exit with status 2
if stats.internal_errors:
sys.exit(2)
# on errors or printed warnings, exit with status 1
if stats.errors or (stats.warnings_printed and config['warnings']):
sys.exit(1)