mirror of
https://github.com/Hopiu/linkchecker.git
synced 2026-05-02 03:44:43 +00:00
Return with non-zero return value when internal program errors occurred.
This commit is contained in:
parent
00003517b1
commit
21532a70ec
17 changed files with 806 additions and 361 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)") %
|
||||
|
|
|
|||
|
|
@ -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)") %
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue