From 04cb827911d945d401dedc5cc6aee2452c9c47cd Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Mon, 27 Jun 2016 23:19:53 +0200 Subject: [PATCH 1/7] Started work on using pylint for code analysing. --- tests/utils/test_pylint.py | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/utils/test_pylint.py diff --git a/tests/utils/test_pylint.py b/tests/utils/test_pylint.py new file mode 100644 index 000000000..cf09a387a --- /dev/null +++ b/tests/utils/test_pylint.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2016 OpenLP Developers # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +Package to test for proper bzr tags. +""" +import os +import logging +from unittest import TestCase + +from pylint import epylint as lint +import multiprocessing + +log = logging.getLogger(__name__) + + +class TestPylint(TestCase): + + def test_pylint(self): + """ + Test for pylint errors + """ + # GIVEN: The openlp base folder + path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..', 'openlp')) + + # WHEN: Running pylint + (pylint_stdout, pylint_stderr) = lint.py_run('{path} --disable=all --enable=missing-format-argument-key,unused-format-string-argument -r n -j {cpu_count}'.format(path='openlp', cpu_count=multiprocessing.cpu_count()), return_std=True) + stdout = pylint_stdout.read() + stderr = pylint_stderr.read() + log.debug(stdout) + log.debug(stderr) + + # THEN: The output should be empty + self.assertEqual(stdout, '', 'PyLint should find no errors') From 668f10a14b52131da74e9d198486438cf063778d Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Tue, 28 Jun 2016 22:44:50 +0200 Subject: [PATCH 2/7] more pylint --- tests/utils/test_pylint.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/tests/utils/test_pylint.py b/tests/utils/test_pylint.py index cf09a387a..6f98e9a33 100644 --- a/tests/utils/test_pylint.py +++ b/tests/utils/test_pylint.py @@ -27,9 +27,6 @@ import logging from unittest import TestCase from pylint import epylint as lint -import multiprocessing - -log = logging.getLogger(__name__) class TestPylint(TestCase): @@ -39,14 +36,18 @@ class TestPylint(TestCase): Test for pylint errors """ # GIVEN: The openlp base folder - path = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..', 'openlp')) + enabled_checks = 'missing-format-argument-key,unused-format-string-argument' + disabled_checks = 'all' - # WHEN: Running pylint - (pylint_stdout, pylint_stderr) = lint.py_run('{path} --disable=all --enable=missing-format-argument-key,unused-format-string-argument -r n -j {cpu_count}'.format(path='openlp', cpu_count=multiprocessing.cpu_count()), return_std=True) + # WHEN: Running pylint + (pylint_stdout, pylint_stderr) = \ + lint.py_run('{path} --disable={disabled} --enable={enabled} --reports=no'.format(path='openlp', + disabled=disabled_checks, + enabled=enabled_checks), + return_std=True) stdout = pylint_stdout.read() stderr = pylint_stderr.read() - log.debug(stdout) - log.debug(stderr) + print(stdout) # THEN: The output should be empty - self.assertEqual(stdout, '', 'PyLint should find no errors') + self.assertTrue(stdout == '', 'PyLint should find no errors') From 1cf078be76b903a72e09f22b15f426ecafe02e98 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Fri, 1 Jul 2016 23:14:24 +0200 Subject: [PATCH 3/7] Add pylintrc file --- pylintrc | 379 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 379 insertions(+) create mode 100644 pylintrc diff --git a/pylintrc b/pylintrc new file mode 100644 index 000000000..367695e13 --- /dev/null +++ b/pylintrc @@ -0,0 +1,379 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=vlc.py + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=4 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Allow optimization of some AST trees. This will activate a peephole AST +# optimizer, which will apply various small optimizations. For instance, it can +# be used to obtain the result of joining multiple strings with the addition +# operator. Joining a lot of strings can lead to a maximum recursion error in +# Pylint and this flag can prevent that. It has one side effect, the resulting +# AST will be different than the one from reality. +optimize-ast=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time. See also the "--disable" option for examples. +#enable= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +disable=reload-builtin,too-many-locals,no-name-in-module,hex-method,bad-builtin,old-raise-syntax,bad-continuation,reduce-builtin,unicode-builtin,unused-wildcard-import,apply-builtin,no-member,filter-builtin-not-iterating,execfile-builtin,import-star-module-level,input-builtin,duplicate-code,old-division,print-statement,cmp-method,fixme,no-absolute-import,cmp-builtin,no-self-use,too-many-nested-blocks,standarderror-builtin,long-builtin,raising-string,delslice-method,metaclass-assignment,coerce-builtin,old-octal-literal,basestring-builtin,xrange-builtin,line-too-long,suppressed-message,unused-variable,round-builtin,raw_input-builtin,too-many-instance-attributes,unused-argument,next-method-called,oct-method,attribute-defined-outside-init,too-many-public-methods,too-many-statements,logging-format-interpolation,map-builtin-not-iterating,buffer-builtin,parameter-unpacking,range-builtin-not-iterating,intern-builtin,wrong-import-position,coerce-method,useless-suppression,using-cmp-argument,dict-view-method,backtick,old-ne-operator,missing-docstring,setslice-method,access-member-before-definition,long-suffix,too-few-public-methods,file-builtin,zip-builtin-not-iterating,invalid-name,getslice-method,unpacking-in-except,too-many-arguments,dict-iter-method,unichr-builtin,too-many-lines,too-many-branches,wildcard-import,indexing-exception,nonzero-method + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". +files-output=no + +# Tells whether to display a full report or only the messages +reports=no + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: en_ZA (myspell), en_US +# (myspell), en_GB (myspell), en_AU (myspell), da_DK (myspell), en (aspell), +# en_CA (aspell). +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[BASIC] + +# List of builtins function names that should not be used, separated by a comma +bad-functions=map,filter + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for attribute names +attr-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for variable names +variable-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for argument names +argument-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for function names +function-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for method names +method-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + + +[ELIF] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=100 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_$|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# List of classes names for which member attributes should not be checked +# (useful for classes with attributes dynamically set). This supports can work +# with qualified names. +ignored-classes= + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=optparse + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception From 8aa917c89c1a5e406682f3cd2eeb9717f71f4dc4 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Fri, 1 Jul 2016 23:17:20 +0200 Subject: [PATCH 4/7] A bunch of fixes suggested by pylint. --- openlp/core/common/actions.py | 2 +- openlp/core/common/db.py | 5 ++-- openlp/core/common/languagemanager.py | 2 +- openlp/core/common/settings.py | 2 +- openlp/core/common/uistrings.py | 2 +- openlp/core/common/versionchecker.py | 4 +-- openlp/core/lib/__init__.py | 2 +- openlp/core/lib/db.py | 4 +-- openlp/core/lib/htmlbuilder.py | 4 +-- openlp/core/lib/imagemanager.py | 2 +- openlp/core/lib/pluginmanager.py | 1 - openlp/core/lib/projector/db.py | 25 ++++++++--------- openlp/core/lib/projector/pjlink1.py | 28 +++++++++---------- openlp/core/lib/renderer.py | 2 +- openlp/core/lib/serviceitem.py | 2 +- openlp/core/lib/theme.py | 7 ++--- openlp/core/lib/webpagereader.py | 3 +- openlp/core/ui/exceptionform.py | 3 +- openlp/core/ui/firsttimeform.py | 4 +-- openlp/core/ui/formattingtagcontroller.py | 2 +- openlp/core/ui/lib/spelltextedit.py | 2 +- openlp/core/ui/maindisplay.py | 8 +++--- openlp/core/ui/mainwindow.py | 1 - openlp/core/ui/media/__init__.py | 4 +-- openlp/core/ui/media/mediacontroller.py | 5 ++-- openlp/core/ui/media/playertab.py | 4 ++- openlp/core/ui/media/vlcplayer.py | 3 +- openlp/core/ui/media/webkitplayer.py | 4 +-- openlp/core/ui/projector/sourceselectform.py | 14 +++++----- openlp/core/ui/projector/tab.py | 2 +- openlp/core/ui/servicemanager.py | 4 +-- openlp/core/ui/slidecontroller.py | 1 - openlp/core/ui/themeform.py | 4 +-- openlp/core/ui/thememanager.py | 2 +- .../presentations/lib/messagelistener.py | 2 +- .../presentations/lib/pdfcontroller.py | 6 ++-- .../presentations/lib/pptviewcontroller.py | 4 +-- .../presentations/presentationplugin.py | 2 +- .../openlp_core_lib/test_serviceitem.py | 2 -- tests/utils/test_pylint.py | 9 ++++-- 40 files changed, 92 insertions(+), 97 deletions(-) diff --git a/openlp/core/common/actions.py b/openlp/core/common/actions.py index d22ef8fd1..5e5dd2e05 100644 --- a/openlp/core/common/actions.py +++ b/openlp/core/common/actions.py @@ -138,7 +138,7 @@ class CategoryList(object): for category in self.categories: if category.name == key: return category - raise KeyError('Category "{keY}" does not exist.'.format(key=key)) + raise KeyError('Category "{key}" does not exist.'.format(key=key)) def __len__(self): """ diff --git a/openlp/core/common/db.py b/openlp/core/common/db.py index 1e18167ab..1fd7a6521 100644 --- a/openlp/core/common/db.py +++ b/openlp/core/common/db.py @@ -22,11 +22,12 @@ """ The :mod:`db` module provides helper functions for database related methods. """ -import sqlalchemy import logging - from copy import deepcopy +import sqlalchemy + + log = logging.getLogger(__name__) diff --git a/openlp/core/common/languagemanager.py b/openlp/core/common/languagemanager.py index 58262ffb5..099e84af6 100644 --- a/openlp/core/common/languagemanager.py +++ b/openlp/core/common/languagemanager.py @@ -168,7 +168,7 @@ def format_time(text, local_time): """ return local_time.strftime(match.group()) - return re.sub('\%[a-zA-Z]', match_formatting, text) + return re.sub(r'\%[a-zA-Z]', match_formatting, text) def get_locale_key(string): diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index d4e114c74..a812e856b 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -26,7 +26,7 @@ import datetime import logging import os -from PyQt5 import QtCore, QtGui, QtWidgets +from PyQt5 import QtCore, QtGui from openlp.core.common import ThemeLevel, SlideLimits, UiStrings, is_win, is_linux diff --git a/openlp/core/common/uistrings.py b/openlp/core/common/uistrings.py index 91db10fcf..dccc3bdb4 100644 --- a/openlp/core/common/uistrings.py +++ b/openlp/core/common/uistrings.py @@ -68,7 +68,7 @@ class UiStrings(object): self.Default = translate('OpenLP.Ui', 'Default') self.DefaultColor = translate('OpenLP.Ui', 'Default Color:') self.DefaultServiceName = translate('OpenLP.Ui', 'Service %Y-%m-%d %H-%M', - 'This may not contain any of the following characters: /\\?*|<>\[\]":+\n' + 'This may not contain any of the following characters: /\\?*|<>[]":+\n' 'See http://docs.python.org/library/datetime' '.html#strftime-strptime-behavior for more information.') self.Delete = translate('OpenLP.Ui', '&Delete') diff --git a/openlp/core/common/versionchecker.py b/openlp/core/common/versionchecker.py index fb706968b..25479884f 100644 --- a/openlp/core/common/versionchecker.py +++ b/openlp/core/common/versionchecker.py @@ -10,10 +10,10 @@ from datetime import datetime from distutils.version import LooseVersion from subprocess import Popen, PIPE -from openlp.core.common import AppLocation, Settings - from PyQt5 import QtCore +from openlp.core.common import AppLocation, Settings + log = logging.getLogger(__name__) APPLICATION_VERSION = {} diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index a7e01bd24..fed6df05c 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -95,7 +95,7 @@ def get_text_file_string(text_file): content = None try: file_handle = open(text_file, 'r', encoding='utf-8') - if not file_handle.read(3) == '\xEF\xBB\xBF': + if file_handle.read(3) != '\xEF\xBB\xBF': # no BOM was found file_handle.seek(0) content = file_handle.read() diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 3decb0a3b..d77432181 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -178,9 +178,9 @@ def upgrade_db(url, upgrade): version_meta = Metadata.populate(key='version', value=int(upgrade.__version__)) session.commit() upgrade_version = upgrade.__version__ - version_meta = int(version_meta.value) + version = int(version_meta.value) session.close() - return version_meta, upgrade_version + return version, upgrade_version def delete_database(plugin_name, db_file_name=None): diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index 6f2fee68c..456925d5a 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -389,8 +389,8 @@ is the function which has to be called from outside. The generated and returned """ import logging -from PyQt5 import QtWebKit from string import Template +from PyQt5 import QtWebKit from openlp.core.common import Settings from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, VerticalType, HorizontalType @@ -647,7 +647,7 @@ def webkit_version(): webkit_ver = float(QtWebKit.qWebKitVersion()) log.debug('Webkit version = {version}'.format(version=webkit_ver)) except AttributeError: - webkit_ver = 0 + webkit_ver = 0.0 return webkit_ver diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py index 1c25fca25..7e0cd3212 100644 --- a/openlp/core/lib/imagemanager.py +++ b/openlp/core/lib/imagemanager.py @@ -272,7 +272,7 @@ class ImageManager(QtCore.QObject): Add image to cache if it is not already there. """ log.debug('add_image {path}'.format(path=path)) - if not (path, source, width, height) in self._cache: + if (path, source, width, height) not in self._cache: image = Image(path, source, background, width, height) self._cache[(path, source, width, height)] = image self._conversion_queue.put((image.priority, image.secondary_priority, image)) diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index 4eb4c2f01..98f49a2c6 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -23,7 +23,6 @@ Provide plugin management """ import os -import sys import imp from openlp.core.lib import Plugin, PluginStatus diff --git a/openlp/core/lib/projector/db.py b/openlp/core/lib/projector/db.py index 98778e695..9d223b0e1 100644 --- a/openlp/core/lib/projector/db.py +++ b/openlp/core/lib/projector/db.py @@ -40,13 +40,12 @@ log.debug('projector.lib.db module loaded') from sqlalchemy import Column, ForeignKey, Integer, MetaData, String, and_ from sqlalchemy.ext.declarative import declarative_base, declared_attr -from sqlalchemy.orm import backref, relationship +from sqlalchemy.orm import relationship from openlp.core.lib.db import Manager, init_db, init_url from openlp.core.lib.projector.constants import PJLINK_DEFAULT_CODES -metadata = MetaData() -Base = declarative_base(metadata) +Base = declarative_base(MetaData()) class CommonBase(object): @@ -54,8 +53,8 @@ class CommonBase(object): Base class to automate table name and ID column. """ @declared_attr - def __tablename__(cls): - return cls.__name__.lower() + def __tablename__(self): + return self.__name__.lower() id = Column(Integer, primary_key=True) @@ -257,7 +256,7 @@ class ProjectorDB(Manager): projector = self.get_object_filtered(Projector, Projector.id == dbid) if projector is None: # Not found - log.warn('get_projector_by_id() did not find {data}'.format(data=id)) + log.warning('get_projector_by_id() did not find {data}'.format(data=id)) return None log.debug('get_projectorby_id() returning 1 entry for "{entry}" id="{data}"'.format(entry=dbid, data=projector.id)) @@ -290,7 +289,7 @@ class ProjectorDB(Manager): projector = self.get_object_filtered(Projector, Projector.ip == ip) if projector is None: # Not found - log.warn('get_projector_by_ip() did not find {ip}'.format(ip=ip)) + log.warning('get_projector_by_ip() did not find {ip}'.format(ip=ip)) return None log.debug('get_projectorby_ip() returning 1 entry for "{ip}" id="{data}"'.format(ip=ip, data=projector.id)) @@ -307,7 +306,7 @@ class ProjectorDB(Manager): projector = self.get_object_filtered(Projector, Projector.name == name) if projector is None: # Not found - log.warn('get_projector_by_name() did not find "{name}"'.format(name=name)) + log.warning('get_projector_by_name() did not find "{name}"'.format(name=name)) return None log.debug('get_projector_by_name() returning one entry for "{name}" id="{data}"'.format(name=name, data=projector.id)) @@ -324,7 +323,7 @@ class ProjectorDB(Manager): """ old_projector = self.get_object_filtered(Projector, Projector.ip == projector.ip) if old_projector is not None: - log.warn('add_new() skipping entry ip="{ip}" (Already saved)'.format(ip=old_projector.ip)) + log.warning('add_new() skipping entry ip="{ip}" (Already saved)'.format(ip=old_projector.ip)) return False log.debug('add_new() saving new entry') log.debug('ip="{ip}", name="{name}", location="{location}"'.format(ip=projector.ip, @@ -408,10 +407,10 @@ class ProjectorDB(Manager): :param source: ProjectorSource id :returns: ProjetorSource instance or None """ - source_entry = self.get_object_filtered(ProjetorSource, ProjectorSource.id == source) + source_entry = self.get_object_filtered(ProjectorSource, ProjectorSource.id == source) if source_entry is None: # Not found - log.warn('get_source_by_id() did not find "{source}"'.format(source=source)) + log.warning('get_source_by_id() did not find "{source}"'.format(source=source)) return None log.debug('get_source_by_id() returning one entry for "{source}""'.format(source=source)) return source_entry @@ -430,8 +429,8 @@ class ProjectorDB(Manager): if source_entry is None: # Not found - log.warn('get_source_by_id() not found') - log.warn('code="{code}" projector_id="{data}"'.format(code=code, data=projector_id)) + log.warning('get_source_by_id() not found') + log.warning('code="{code}" projector_id="{data}"'.format(code=code, data=projector_id)) return None log.debug('get_source_by_id() returning one entry') log.debug('code="{code}" projector_id="{data}"'.format(code=code, data=projector_id)) diff --git a/openlp/core/lib/projector/pjlink1.py b/openlp/core/lib/projector/pjlink1.py index ce06b3625..834b589d8 100644 --- a/openlp/core/lib/projector/pjlink1.py +++ b/openlp/core/lib/projector/pjlink1.py @@ -310,10 +310,10 @@ class PJLink1(QTcpSocket): read = self.readLine(self.maxSize) dontcare = self.readLine(self.maxSize) # Clean out the trailing \r\n if read is None: - log.warn('({ip}) read is None - socket error?'.format(ip=self.ip)) + log.warning('({ip}) read is None - socket error?'.format(ip=self.ip)) return elif len(read) < 8: - log.warn('({ip}) Not enough data read)'.format(ip=self.ip)) + log.warning('({ip}) Not enough data read)'.format(ip=self.ip)) return data = decode(read, 'ascii') # Possibility of extraneous data on input when reading. @@ -402,7 +402,7 @@ class PJLink1(QTcpSocket): self.projectorReceivedData.emit() return elif '=' not in data: - log.warn('({ip}) get_data(): Invalid packet received'.format(ip=self.ip)) + log.warning('({ip}) get_data(): Invalid packet received'.format(ip=self.ip)) self.send_busy = False self.projectorReceivedData.emit() return @@ -410,15 +410,15 @@ class PJLink1(QTcpSocket): try: (prefix, class_, cmd, data) = (data_split[0][0], data_split[0][1], data_split[0][2:], data_split[1]) except ValueError as e: - log.warn('({ip}) get_data(): Invalid packet - expected header + command + data'.format(ip=self.ip)) - log.warn('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in.strip())) + log.warning('({ip}) get_data(): Invalid packet - expected header + command + data'.format(ip=self.ip)) + log.warning('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in.strip())) self.change_status(E_INVALID_DATA) self.send_busy = False self.projectorReceivedData.emit() return if not (self.pjlink_class in PJLINK_VALID_CMD and cmd in PJLINK_VALID_CMD[self.pjlink_class]): - log.warn('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd)) + log.warning('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd)) self.send_busy = False self.projectorReceivedData.emit() return @@ -461,7 +461,7 @@ class PJLink1(QTcpSocket): :param queue: Option to force add to queue rather than sending directly """ if self.state() != self.ConnectedState: - log.warn('({ip}) send_command(): Not connected - returning'.format(ip=self.ip)) + log.warning('({ip}) send_command(): Not connected - returning'.format(ip=self.ip)) self.send_queue = [] return self.projectorNetwork.emit(S_NETWORK_SENDING) @@ -577,7 +577,7 @@ class PJLink1(QTcpSocket): if cmd in self.PJLINK1_FUNC: self.PJLINK1_FUNC[cmd](data) else: - log.warn('({ip}) Invalid command {data}'.format(ip=self.ip, data=cmd)) + log.warning('({ip}) Invalid command {data}'.format(ip=self.ip, data=cmd)) self.send_busy = False self.projectorReceivedData.emit() @@ -596,7 +596,7 @@ class PJLink1(QTcpSocket): fill = {'Hours': int(data_dict[0]), 'On': False if data_dict[1] == '0' else True} except ValueError: # In case of invalid entry - log.warn('({ip}) process_lamp(): Invalid data "{data}"'.format(ip=self.ip, data=data)) + log.warning('({ip}) process_lamp(): Invalid data "{data}"'.format(ip=self.ip, data=data)) return lamps.append(fill) data_dict.pop(0) # Remove lamp hours @@ -623,7 +623,7 @@ class PJLink1(QTcpSocket): self.send_command('INST') else: # Log unknown status response - log.warn('({ip}) Unknown power response: {data}'.format(ip=self.ip, data=data)) + log.warning('({ip}) Unknown power response: {data}'.format(ip=self.ip, data=data)) return def process_avmt(self, data): @@ -648,7 +648,7 @@ class PJLink1(QTcpSocket): shutter = True mute = True else: - log.warn('({ip}) Unknown shutter response: {data}'.format(ip=self.ip, data=data)) + log.warning('({ip}) Unknown shutter response: {data}'.format(ip=self.ip, data=data)) update_icons = shutter != self.shutter update_icons = update_icons or mute != self.mute self.shutter = shutter @@ -797,7 +797,7 @@ class PJLink1(QTcpSocket): Initiate connection to projector. """ if self.state() == self.ConnectedState: - log.warn('({ip}) connect_to_host(): Already connected - returning'.format(ip=self.ip)) + log.warning('({ip}) connect_to_host(): Already connected - returning'.format(ip=self.ip)) return self.change_status(S_CONNECTING) self.connectToHost(self.ip, self.port if type(self.port) is int else int(self.port)) @@ -809,9 +809,9 @@ class PJLink1(QTcpSocket): """ if abort or self.state() != self.ConnectedState: if abort: - log.warn('({ip}) disconnect_from_host(): Aborting connection'.format(ip=self.ip)) + log.warning('({ip}) disconnect_from_host(): Aborting connection'.format(ip=self.ip)) else: - log.warn('({ip}) disconnect_from_host(): Not connected - returning'.format(ip=self.ip)) + log.warning('({ip}) disconnect_from_host(): Not connected - returning'.format(ip=self.ip)) self.reset_information() self.disconnectFromHost() try: diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py index 0d233a9c4..48d1cd05b 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/lib/renderer.py @@ -531,7 +531,7 @@ def words_split(line): :param line: Line to be split """ # this parse we are to be wordy - return re.split('\s+', line) + return re.split(r'\s+', line) def get_start_tags(raw_text): diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index c0a819390..1344bfeea 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -34,7 +34,7 @@ import ntpath from PyQt5 import QtGui from openlp.core.common import RegistryProperties, Settings, translate, AppLocation, md5_hash -from openlp.core.lib import ImageSource, build_icon, clean_tags, expand_tags, create_thumb +from openlp.core.lib import ImageSource, build_icon, clean_tags, expand_tags log = logging.getLogger(__name__) diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 4e84d353b..e24613aa6 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -23,7 +23,6 @@ Provide the theme XML and handling functions for OpenLP v2 themes. """ import os -import re import logging import json @@ -477,12 +476,12 @@ class ThemeXML(object): if element == 'weight': element = 'bold' if value == 'Normal': - value = False + ret_value = False else: - value = True + ret_value = True if element == 'proportion': element = 'size' - return False, master, element, value + return False, master, element, ret_value def _create_attr(self, master, element, value): """ diff --git a/openlp/core/lib/webpagereader.py b/openlp/core/lib/webpagereader.py index 260ef1556..52c98bbaf 100644 --- a/openlp/core/lib/webpagereader.py +++ b/openlp/core/lib/webpagereader.py @@ -179,5 +179,4 @@ def get_web_page(url, header=None, update_openlp=False): return page -__all__ = ['get_application_version', 'check_latest_version', - 'get_web_page'] +__all__ = ['get_web_page'] diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index 216780584..7e97cb796 100644 --- a/openlp/core/ui/exceptionform.py +++ b/openlp/core/ui/exceptionform.py @@ -32,8 +32,6 @@ import sqlalchemy from PyQt5 import Qt, QtCore, QtGui, QtWebKit, QtWidgets from lxml import etree -from openlp.core.common import RegistryProperties, is_linux - try: import migrate MIGRATE_VERSION = getattr(migrate, '__version__', '< 0.7') @@ -74,6 +72,7 @@ except ImportError: from openlp.core.common import Settings, UiStrings, translate from openlp.core.common.versionchecker import get_application_version +from openlp.core.common import RegistryProperties, is_linux from .exceptiondialog import Ui_ExceptionDialog diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index 9ae2e0898..a0eec54a4 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -666,14 +666,14 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties): if missed_files: file_list = '' for entry in missed_files: - file_list += '{text}
'.format(text=entry) + file_list += '{text}
'.format(text=entry) msg = QtWidgets.QMessageBox() msg.setIcon(QtWidgets.QMessageBox.Warning) msg.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'Network Error')) msg.setText(translate('OpenLP.FirstTimeWizard', 'Unable to download some files')) msg.setInformativeText(translate('OpenLP.FirstTimeWizard', 'The following files were not able to be ' - 'downloaded:
{text}'.format(text=file_list))) + 'downloaded:
{text}'.format(text=file_list))) msg.setStandardButtons(msg.Ok) ans = msg.exec() return True diff --git a/openlp/core/ui/formattingtagcontroller.py b/openlp/core/ui/formattingtagcontroller.py index 161930cb6..5a0511842 100644 --- a/openlp/core/ui/formattingtagcontroller.py +++ b/openlp/core/ui/formattingtagcontroller.py @@ -84,7 +84,7 @@ class FormattingTagController(object): 'desc': desc, 'start tag': '{{{tag}}}'.format(tag=tag), 'start html': start_html, - 'end tag': '{/{tag}}}'.format(tag=tag), + 'end tag': '{{{tag}}}'.format(tag=tag), 'end html': end_html, 'protected': False, 'temporary': False diff --git a/openlp/core/ui/lib/spelltextedit.py b/openlp/core/ui/lib/spelltextedit.py index 8b6b552be..5fd983128 100644 --- a/openlp/core/ui/lib/spelltextedit.py +++ b/openlp/core/ui/lib/spelltextedit.py @@ -164,7 +164,7 @@ class Highlighter(QtGui.QSyntaxHighlighter): """ Provides a text highlighter for pointing out spelling errors in text. """ - WORDS = '(?iu)[\w\']+' + WORDS = r'(?iu)[\w\']+' def __init__(self, *args): """ diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 00f4be7ca..ff5acb975 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -33,7 +33,7 @@ import html import logging import os -from PyQt5 import QtCore, QtWidgets, QtWebKit, QtWebKitWidgets, QtOpenGL, QtGui, QtMultimedia +from PyQt5 import QtCore, QtWidgets, QtWebKit, QtWebKitWidgets, QtGui, QtMultimedia from openlp.core.common import AppLocation, Registry, RegistryProperties, OpenLPMixin, Settings, translate,\ is_macosx, is_win @@ -468,9 +468,9 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties): self.service_item.theme_data.background_filename, ImageSource.Theme) if image_path: image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin) - html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes, - plugins=self.plugin_manager.plugins) - self.web_view.setHtml(html) + created_html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes, + plugins=self.plugin_manager.plugins) + self.web_view.setHtml(created_html) if service_item.foot_text: self.footer(service_item.foot_text) # if was hidden keep it hidden diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index ccd12727c..63048eac5 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -46,7 +46,6 @@ from openlp.core.ui.firsttimeform import FirstTimeForm from openlp.core.ui.media import MediaController from openlp.core.ui.printserviceform import PrintServiceForm from openlp.core.ui.projector.manager import ProjectorManager -from openlp.core.ui.lib.toolbar import OpenLPToolbar from openlp.core.ui.lib.dockwidget import OpenLPDockWidget from openlp.core.ui.lib.mediadockmanager import MediaDockManager diff --git a/openlp/core/ui/media/__init__.py b/openlp/core/ui/media/__init__.py index 248aca6f2..b51391583 100644 --- a/openlp/core/ui/media/__init__.py +++ b/openlp/core/ui/media/__init__.py @@ -24,10 +24,10 @@ The :mod:`~openlp.core.ui.media` module contains classes and objects for media p """ import logging -from openlp.core.common import Settings - from PyQt5 import QtCore +from openlp.core.common import Settings + log = logging.getLogger(__name__ + '.__init__') diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index 021ea5281..d404ee02e 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -38,7 +38,6 @@ from openlp.core.ui.media.mediaplayer import MediaPlayer from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players,\ parse_optical_path from openlp.core.ui.lib.toolbar import OpenLPToolbar -from openlp.core.ui.lib.dockwidget import OpenLPDockWidget log = logging.getLogger(__name__) @@ -175,7 +174,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): log.debug('_check_available_media_players') controller_dir = os.path.join(AppLocation.get_directory(AppLocation.AppDir), 'core', 'ui', 'media') for filename in os.listdir(controller_dir): - if filename.endswith('player.py') and not filename == 'mediaplayer.py': + if filename.endswith('player.py') and filename != 'mediaplayer.py': path = os.path.join(controller_dir, filename) if os.path.isfile(path): module_name = 'openlp.core.ui.media.' + os.path.splitext(filename)[0] @@ -554,7 +553,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties): default_player = [used_players[0]] if service_item.processor and service_item.processor != UiStrings().Automatic: # check to see if the player is usable else use the default one. - if not service_item.processor.lower() in used_players: + if service_item.processor.lower() not in used_players: used_players = default_player else: used_players = [service_item.processor.lower()] diff --git a/openlp/core/ui/media/playertab.py b/openlp/core/ui/media/playertab.py index 1fca21450..c76fc3300 100644 --- a/openlp/core/ui/media/playertab.py +++ b/openlp/core/ui/media/playertab.py @@ -224,9 +224,11 @@ class PlayerTab(SettingsTab): self.settings_form.register_post_process('mediaitem_media_rebuild') self.settings_form.register_post_process('config_screen_changed') - def post_set_up(self): + def post_set_up(self, post_update=False): """ Late setup for players as the MediaController has to be initialised first. + + :param post_update: Indicates if called before or after updates. """ for key, player in self.media_players.items(): player = self.media_players[key] diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index 9c2110e22..48b0602fe 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -112,7 +112,6 @@ def get_vlc(): # This needs to happen on module load and not in get_vlc(), otherwise it can cause crashes on some DE on some setups # (reported on Gnome3, Unity, Cinnamon, all GTK+ based) when using native filedialogs... if is_linux() and 'nose' not in sys.argv[0] and get_vlc(): - import ctypes try: try: x11 = ctypes.cdll.LoadLibrary('libX11.so.6') @@ -233,7 +232,7 @@ class VlcPlayer(MediaPlayer): """ vlc = get_vlc() start = datetime.now() - while not media_state == display.vlc_media.get_state(): + while media_state != display.vlc_media.get_state(): if display.vlc_media.get_state() == vlc.State.Error: return False self.application.process_events() diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index 19221ace0..5cd982951 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -22,10 +22,10 @@ """ The :mod:`~openlp.core.ui.media.webkit` module contains our WebKit video player """ -from PyQt5 import QtGui, QtWebKitWidgets - import logging +from PyQt5 import QtGui, QtWebKitWidgets + from openlp.core.common import Settings from openlp.core.lib import translate from openlp.core.ui.media import MediaState diff --git a/openlp/core/ui/projector/sourceselectform.py b/openlp/core/ui/projector/sourceselectform.py index 7d73f6a5a..5ee1ac403 100644 --- a/openlp/core/ui/projector/sourceselectform.py +++ b/openlp/core/ui/projector/sourceselectform.py @@ -141,23 +141,23 @@ def Build_Tab(group, source_key, default, projector, projectordb, edit=False): return widget, button_count, buttonchecked -def set_button_tooltip(bar): +def set_button_tooltip(button_bar): """ Set the toolip for the standard buttons used - :param bar: QDialogButtonBar instance to update + :param button_bar: QDialogButtonBar instance to update """ - for button in bar.buttons(): - if bar.standardButton(button) == QDialogButtonBox.Cancel: + for button in button_bar.buttons(): + if button_bar.standardButton(button) == QDialogButtonBox.Cancel: button.setToolTip(translate('OpenLP.SourceSelectForm', 'Ignoring current changes and return to OpenLP')) - elif bar.standardButton(button) == QDialogButtonBox.Reset: + elif button_bar.standardButton(button) == QDialogButtonBox.Reset: button.setToolTip(translate('OpenLP.SourceSelectForm', 'Delete all user-defined text and revert to PJLink default text')) - elif bar.standardButton(button) == QDialogButtonBox.Discard: + elif button_bar.standardButton(button) == QDialogButtonBox.Discard: button.setToolTip(translate('OpenLP.SourceSelectForm', 'Discard changes and reset to previous user-defined text')) - elif bar.standardButton(button) == QDialogButtonBox.Ok: + elif button_bar.standardButton(button) == QDialogButtonBox.Ok: button.setToolTip(translate('OpenLP.SourceSelectForm', 'Save changes and return to OpenLP')) else: diff --git a/openlp/core/ui/projector/tab.py b/openlp/core/ui/projector/tab.py index 1b87209c0..ff4bdee17 100644 --- a/openlp/core/ui/projector/tab.py +++ b/openlp/core/ui/projector/tab.py @@ -133,7 +133,7 @@ class ProjectorTab(SettingsTab): settings.setValue('socket timeout', self.socket_timeout_spin_box.value()) settings.setValue('poll time', self.socket_poll_spin_box.value()) settings.setValue('source dialog type', self.dialog_type_combo_box.currentIndex()) - settings.endGroup + settings.endGroup() def on_dialog_type_combo_box_changed(self): self.dialog_type = self.dialog_type_combo_box.currentIndex() diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 907cb49a6..aa31dfbf8 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -774,7 +774,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa else: critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.')) self.log_error('File contains no service data') - except (IOError, NameError, zipfile.BadZipfile): + except (IOError, NameError): self.log_exception('Problem loading service file {name}'.format(name=file_name)) critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File could not be opened because it is corrupt.')) @@ -1327,7 +1327,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa """ The theme may have changed in the settings dialog so make sure the theme combo box is in the correct state. """ - visible = not self.renderer.theme_level == ThemeLevel.Global + visible = self.renderer.theme_level != ThemeLevel.Global self.toolbar.actions['theme_combo_box'].setVisible(visible) self.toolbar.actions['theme_label'].setVisible(visible) self.regenerate_service_items() diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 9379bb96f..8b5ce5e34 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -37,7 +37,6 @@ from openlp.core.lib import ItemCapabilities, ServiceItem, ImageSource, ServiceI build_html from openlp.core.lib.ui import create_action from openlp.core.ui.lib.toolbar import OpenLPToolbar -from openlp.core.ui.lib.dockwidget import OpenLPDockWidget from openlp.core.ui.lib.listpreviewwidget import ListPreviewWidget from openlp.core.ui import HideMode, MainDisplay, Display, DisplayControllerType diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index 475bfc0b7..a0ab5f45b 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -249,7 +249,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): NOTE the font_main_override is the inverse of the check box value """ if self.update_theme_allowed: - self.theme.font_main_override = not (value == QtCore.Qt.Checked) + self.theme.font_main_override = (value != QtCore.Qt.Checked) def on_footer_position_check_box_state_changed(self, value): """ @@ -257,7 +257,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties): NOTE the font_footer_override is the inverse of the check box value """ if self.update_theme_allowed: - self.theme.font_footer_override = not (value == QtCore.Qt.Checked) + self.theme.font_footer_override = (value != QtCore.Qt.Checked) def exec(self, edit=False): """ diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 70ca9fd88..341a8061e 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -583,7 +583,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ThemeManage out_file.write(theme_zip.read(name)) out_file.close() except (IOError, zipfile.BadZipfile): - self.log_exception('Importing theme from zip failed {name|'.format(name=file_name)) + self.log_exception('Importing theme from zip failed {name}'.format(name=file_name)) raise ValidationError except ValidationError: critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'), diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index 992ba9b5c..097b9dc38 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -28,7 +28,7 @@ from PyQt5 import QtCore from openlp.core.common import Registry from openlp.core.ui import HideMode -from openlp.core.lib import ServiceItemContext, ServiceItem +from openlp.core.lib import ServiceItemContext from openlp.plugins.presentations.lib.pdfcontroller import PDF_CONTROLLER_FILETYPES log = logging.getLogger(__name__) diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index dd67031bd..6045d5326 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -80,7 +80,7 @@ class PdfController(PresentationController): found_mutool = re.search('usage: mutool.*', decoded_line, re.IGNORECASE) if found_mutool: # Test that mutool contains mudraw - if re.search('draw\s+--\s+convert document.*', runlog.decode(), re.IGNORECASE | re.MULTILINE): + if re.search(r'draw\s+--\s+convert document.*', runlog.decode(), re.IGNORECASE | re.MULTILINE): program_type = 'mutool' break found_gs = re.search('GPL Ghostscript.*', decoded_line, re.IGNORECASE) @@ -215,8 +215,8 @@ class PdfDocument(PresentationDocument): height = 0.0 for line in runlog.splitlines(): try: - width = float(re.search('.*Size: x: (\d+\.?\d*), y: \d+.*', line.decode()).group(1)) - height = float(re.search('.*Size: x: \d+\.?\d*, y: (\d+\.?\d*).*', line.decode()).group(1)) + width = float(re.search(r'.*Size: x: (\d+\.?\d*), y: \d+.*', line.decode()).group(1)) + height = float(re.search(r'.*Size: x: \d+\.?\d*, y: (\d+\.?\d*).*', line.decode()).group(1)) break except AttributeError: continue diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index 54d8f5170..43eb69454 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -181,13 +181,13 @@ class PptviewDocument(PresentationDocument): index = -1 list_to_add = None # check if it is a slide - match = re.search("slides/slide(.+)\.xml", zip_info.filename) + match = re.search(r'slides/slide(.+)\.xml', zip_info.filename) if match: index = int(match.group(1)) - 1 node_type = 'ctrTitle' list_to_add = titles # or a note - match = re.search("notesSlides/notesSlide(.+)\.xml", zip_info.filename) + match = re.search(r'notesSlides/notesSlide(.+)\.xml', zip_info.filename) if match: index = int(match.group(1)) - 1 node_type = 'body' diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index dc0614086..ca0ecba82 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -124,7 +124,7 @@ class PresentationPlugin(Plugin): log.debug('check_pre_conditions') controller_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), 'presentations', 'lib') for filename in os.listdir(controller_dir): - if filename.endswith('controller.py') and not filename == 'presentationcontroller.py': + if filename.endswith('controller.py') and filename != 'presentationcontroller.py': path = os.path.join(controller_dir, filename) if os.path.isfile(path): module_name = 'openlp.plugins.presentations.lib.' + os.path.splitext(filename)[0] diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core_lib/test_serviceitem.py index e1c73b3ca..d18a4d049 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core_lib/test_serviceitem.py @@ -112,7 +112,6 @@ class TestServiceItem(TestCase): # WHEN: adding an image from a saved Service and mocked exists line = convert_file_service_item(TEST_PATH, 'serviceitem_image_1.osj') with patch('openlp.core.ui.servicemanager.os.path.exists') as mocked_exists,\ - patch('openlp.core.lib.serviceitem.create_thumb') as mocked_create_thumb,\ patch('openlp.core.lib.serviceitem.AppLocation.get_section_data_path') as \ mocked_get_section_data_path: mocked_exists.return_value = True @@ -164,7 +163,6 @@ class TestServiceItem(TestCase): line2 = convert_file_service_item(TEST_PATH, 'serviceitem_image_2.osj', 1) with patch('openlp.core.ui.servicemanager.os.path.exists') as mocked_exists, \ - patch('openlp.core.lib.serviceitem.create_thumb') as mocked_create_thumb, \ patch('openlp.core.lib.serviceitem.AppLocation.get_section_data_path') as \ mocked_get_section_data_path: mocked_exists.return_value = True diff --git a/tests/utils/test_pylint.py b/tests/utils/test_pylint.py index 6f98e9a33..6db43464e 100644 --- a/tests/utils/test_pylint.py +++ b/tests/utils/test_pylint.py @@ -27,6 +27,7 @@ import logging from unittest import TestCase from pylint import epylint as lint +from pylint.__pkginfo__ import pylint_version class TestPylint(TestCase): @@ -37,17 +38,19 @@ class TestPylint(TestCase): """ # GIVEN: The openlp base folder enabled_checks = 'missing-format-argument-key,unused-format-string-argument' - disabled_checks = 'all' + #disabled_checks = 'all' + disabled_checks = '' # WHEN: Running pylint (pylint_stdout, pylint_stderr) = \ lint.py_run('{path} --disable={disabled} --enable={enabled} --reports=no'.format(path='openlp', disabled=disabled_checks, enabled=enabled_checks), - return_std=True) + return_std=True, script='pylint3') stdout = pylint_stdout.read() stderr = pylint_stderr.read() print(stdout) + print(stderr) # THEN: The output should be empty - self.assertTrue(stdout == '', 'PyLint should find no errors') + self.assertTrue(stdout == 's', 'PyLint should find no errors') From 9294aadae9eb25a2a11396e4c6cb1836a8410e29 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Tue, 5 Jul 2016 22:30:33 +0200 Subject: [PATCH 5/7] Changes to pylint test --- tests/utils/test_pylint.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/tests/utils/test_pylint.py b/tests/utils/test_pylint.py index 6db43464e..d4e744e55 100644 --- a/tests/utils/test_pylint.py +++ b/tests/utils/test_pylint.py @@ -24,11 +24,14 @@ Package to test for proper bzr tags. """ import os import logging -from unittest import TestCase - -from pylint import epylint as lint -from pylint.__pkginfo__ import pylint_version +import platform +from unittest import TestCase, SkipTest +try: + from pylint import epylint as lint + from pylint.__pkginfo__ import version +except ImportError: + raise SkipTest('pylint not installed - skipping tests using pylint.') class TestPylint(TestCase): @@ -36,17 +39,20 @@ class TestPylint(TestCase): """ Test for pylint errors """ - # GIVEN: The openlp base folder + # GIVEN: Some checks to disable and enable, and the pylint script + disabled_checks = 'no-member,import-error,no-name-in-module' enabled_checks = 'missing-format-argument-key,unused-format-string-argument' - #disabled_checks = 'all' - disabled_checks = '' + if 'arch' in platform.dist()[0].lower(): + pylint_script = 'pylint' + else: + pylint_script = 'pylint3' # WHEN: Running pylint (pylint_stdout, pylint_stderr) = \ - lint.py_run('{path} --disable={disabled} --enable={enabled} --reports=no'.format(path='openlp', - disabled=disabled_checks, - enabled=enabled_checks), - return_std=True, script='pylint3') + lint.py_run('openlp --errors-only --disable={disabled} --enable={enabled} --reports=no --output-format=parseable'.format( + disabled=disabled_checks, + enabled=enabled_checks), + return_std=True, script=pylint_script) stdout = pylint_stdout.read() stderr = pylint_stderr.read() print(stdout) From 99e6ab6657eb1dec65d9a31aca04619214798d24 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Tue, 5 Jul 2016 22:31:29 +0200 Subject: [PATCH 6/7] Fix various issues as suggested by pylint --- openlp/core/lib/theme.py | 1 + openlp/plugins/bibles/lib/manager.py | 2 +- openlp/plugins/bibles/lib/opensong.py | 2 +- openlp/plugins/songs/lib/importers/cclifile.py | 1 + openlp/plugins/songs/lib/importers/dreambeam.py | 4 ++-- openlp/plugins/songs/lib/importers/easyworship.py | 3 +++ openlp/plugins/songs/lib/importers/openlyrics.py | 2 +- openlp/plugins/songs/lib/importers/presentationmanager.py | 1 + pylintrc | 2 +- 9 files changed, 12 insertions(+), 6 deletions(-) diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index e24613aa6..fc20037e7 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -164,6 +164,7 @@ class ThemeXML(object): jsn = get_text_file_string(json_file) jsn = json.loads(jsn) self.expand_json(jsn) + self.background_filename = None def expand_json(self, var, prev=None): """ diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index d1465475d..1c55222f2 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -370,7 +370,7 @@ class BibleManager(RegistryProperties): """ log.debug('save_meta data {bible}, {version}, {copyright}, {perms}'.format(bible=bible, version=version, - cr=copyright, + copyright=copyright, perms=permissions)) self.db_cache[bible].save_meta('name', version) self.db_cache[bible].save_meta('copyright', copyright) diff --git a/openlp/plugins/bibles/lib/opensong.py b/openlp/plugins/bibles/lib/opensong.py index af3ef64c0..9c01a1fe0 100644 --- a/openlp/plugins/bibles/lib/opensong.py +++ b/openlp/plugins/bibles/lib/opensong.py @@ -117,7 +117,7 @@ class OpenSongBible(BibleDB): if len(verse_parts) > 1: number = int(verse_parts[0]) except TypeError: - log.warning('Illegal verse number: {verse:d}'.format(verse.attrib['n'])) + log.warning('Illegal verse number: {verse:d}'.format(verse=verse.attrib['n'])) verse_number = number else: verse_number += 1 diff --git a/openlp/plugins/songs/lib/importers/cclifile.py b/openlp/plugins/songs/lib/importers/cclifile.py index 800509ab2..b17f78129 100644 --- a/openlp/plugins/songs/lib/importers/cclifile.py +++ b/openlp/plugins/songs/lib/importers/cclifile.py @@ -255,6 +255,7 @@ class CCLIFileImport(SongImport): song_author = '' verse_start = False for line in text_list: + verse_type= 'v' clean_line = line.strip() if not clean_line: if line_number == 0: diff --git a/openlp/plugins/songs/lib/importers/dreambeam.py b/openlp/plugins/songs/lib/importers/dreambeam.py index 9371adc56..8c435bfd5 100644 --- a/openlp/plugins/songs/lib/importers/dreambeam.py +++ b/openlp/plugins/songs/lib/importers/dreambeam.py @@ -124,8 +124,8 @@ class DreamBeamImport(SongImport): if hasattr(song_xml, 'Sequence'): for lyrics_sequence_item in (song_xml.Sequence.iterchildren()): item = lyrics_sequence_item.get('Type')[:1] - self.verse_order_list.append("{item}{number}".format(item=item), - lyrics_sequence_item.get('Number')) + number = lyrics_sequence_item.get('Number') + self.verse_order_list.append("{item}{number}".format(item=item, number=number)) if hasattr(song_xml, 'Notes'): self.comments = str(song_xml.Notes.text) else: diff --git a/openlp/plugins/songs/lib/importers/easyworship.py b/openlp/plugins/songs/lib/importers/easyworship.py index cdffe9292..9db30838f 100644 --- a/openlp/plugins/songs/lib/importers/easyworship.py +++ b/openlp/plugins/songs/lib/importers/easyworship.py @@ -27,6 +27,7 @@ import os import struct import re import zlib +import logging from openlp.core.lib import translate from openlp.plugins.songs.lib import VerseType @@ -38,6 +39,8 @@ SLIDE_BREAK_REGEX = re.compile(r'\n *?\n[\n ]*') NUMBER_REGEX = re.compile(r'[0-9]+') NOTE_REGEX = re.compile(r'\(.*?\)') +log = logging.getLogger(__name__) + class FieldDescEntry: def __init__(self, name, field_type, size): diff --git a/openlp/plugins/songs/lib/importers/openlyrics.py b/openlp/plugins/songs/lib/importers/openlyrics.py index 84b667ce4..fcbe8fbfb 100644 --- a/openlp/plugins/songs/lib/importers/openlyrics.py +++ b/openlp/plugins/songs/lib/importers/openlyrics.py @@ -67,7 +67,7 @@ class OpenLyricsImport(SongImport): xml = etree.tostring(parsed_file).decode() self.open_lyrics.xml_to_song(xml) except etree.XMLSyntaxError: - log.exception('XML syntax error in file {path}'.format(file_path)) + log.exception('XML syntax error in file {path}'.format(path=file_path)) self.log_error(file_path, SongStrings.XMLSyntaxError) except OpenLyricsError as exception: log.exception('OpenLyricsException {error:d} in file {name}: {text}'.format(error=exception.type, diff --git a/openlp/plugins/songs/lib/importers/presentationmanager.py b/openlp/plugins/songs/lib/importers/presentationmanager.py index 34a94dec2..f10bd0ed6 100644 --- a/openlp/plugins/songs/lib/importers/presentationmanager.py +++ b/openlp/plugins/songs/lib/importers/presentationmanager.py @@ -30,6 +30,7 @@ import re import chardet from lxml import objectify, etree +from openlp.core.common import translate from openlp.core.ui.lib.wizard import WizardStrings from .songimport import SongImport diff --git a/pylintrc b/pylintrc index 367695e13..92d83ffaa 100644 --- a/pylintrc +++ b/pylintrc @@ -19,7 +19,7 @@ persistent=yes load-plugins= # Use multiple processes to speed up Pylint. -jobs=4 +#jobs=4 # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. From 1992a513394a8e661824fbdb8f0c821219535906 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Wed, 6 Jul 2016 21:48:57 +0200 Subject: [PATCH 7/7] More pylint-fixes --- openlp/core/lib/db.py | 30 +++++------ openlp/plugins/bibles/lib/osis.py | 2 - openlp/plugins/bibles/lib/zefania.py | 2 - .../plugins/songs/lib/importers/cclifile.py | 2 +- tests/utils/test_pylint.py | 53 ++++++++++++++++--- 5 files changed, 62 insertions(+), 27 deletions(-) diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index d77432181..f42c3b5fc 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -122,6 +122,21 @@ def get_upgrade_op(session): return Operations(context) +class BaseModel(object): + """ + BaseModel provides a base object with a set of generic functions + """ + @classmethod + def populate(cls, **kwargs): + """ + Creates an instance of a class and populates it, returning the instance + """ + instance = cls() + for key, value in kwargs.items(): + instance.__setattr__(key, value) + return instance + + def upgrade_db(url, upgrade): """ Upgrade a database. @@ -197,21 +212,6 @@ def delete_database(plugin_name, db_file_name=None): return delete_file(db_file_path) -class BaseModel(object): - """ - BaseModel provides a base object with a set of generic functions - """ - @classmethod - def populate(cls, **kwargs): - """ - Creates an instance of a class and populates it, returning the instance - """ - instance = cls() - for key, value in kwargs.items(): - instance.__setattr__(key, value) - return instance - - class Manager(object): """ Provide generic object persistence management diff --git a/openlp/plugins/bibles/lib/osis.py b/openlp/plugins/bibles/lib/osis.py index c0a3bec81..85aea70b3 100644 --- a/openlp/plugins/bibles/lib/osis.py +++ b/openlp/plugins/bibles/lib/osis.py @@ -126,8 +126,6 @@ class OSISBible(BibleDB): # Remove div-tags in the book etree.strip_tags(book, ('{http://www.bibletechnologies.net/2003/OSIS/namespace}div')) book_ref_id = self.get_book_ref_id_by_name(book.get('osisID'), num_books, language_id) - if not book_ref_id: - book_ref_id = self.get_book_ref_id_by_localised_name(book.get('osisID')) if not book_ref_id: log.error('Importing books from "{name}" failed'.format(name=self.filename)) return False diff --git a/openlp/plugins/bibles/lib/zefania.py b/openlp/plugins/bibles/lib/zefania.py index 0ea96a2a3..482d98107 100644 --- a/openlp/plugins/bibles/lib/zefania.py +++ b/openlp/plugins/bibles/lib/zefania.py @@ -86,8 +86,6 @@ class ZefaniaBible(BibleDB): continue if bname: book_ref_id = self.get_book_ref_id_by_name(bname, num_books, language_id) - if not book_ref_id: - book_ref_id = self.get_book_ref_id_by_localised_name(bname) else: log.debug('Could not find a name, will use number, basically a guess.') book_ref_id = int(bnumber) diff --git a/openlp/plugins/songs/lib/importers/cclifile.py b/openlp/plugins/songs/lib/importers/cclifile.py index b17f78129..58ceefebc 100644 --- a/openlp/plugins/songs/lib/importers/cclifile.py +++ b/openlp/plugins/songs/lib/importers/cclifile.py @@ -255,7 +255,7 @@ class CCLIFileImport(SongImport): song_author = '' verse_start = False for line in text_list: - verse_type= 'v' + verse_type = 'v' clean_line = line.strip() if not clean_line: if line_number == 0: diff --git a/tests/utils/test_pylint.py b/tests/utils/test_pylint.py index d4e744e55..dc6c83909 100644 --- a/tests/utils/test_pylint.py +++ b/tests/utils/test_pylint.py @@ -33,6 +33,13 @@ try: except ImportError: raise SkipTest('pylint not installed - skipping tests using pylint.') +from openlp.core.common import is_win + +TOLERATED_ERRORS = {'registryproperties.py': ['access-member-before-definition'], + 'opensong.py': ['no-name-in-module'], + 'maindisplay.py': ['no-name-in-module']} + + class TestPylint(TestCase): def test_pylint(self): @@ -40,23 +47,55 @@ class TestPylint(TestCase): Test for pylint errors """ # GIVEN: Some checks to disable and enable, and the pylint script - disabled_checks = 'no-member,import-error,no-name-in-module' + disabled_checks = 'import-error,no-member' enabled_checks = 'missing-format-argument-key,unused-format-string-argument' - if 'arch' in platform.dist()[0].lower(): + if is_win() or 'arch' in platform.dist()[0].lower(): pylint_script = 'pylint' else: pylint_script = 'pylint3' # WHEN: Running pylint (pylint_stdout, pylint_stderr) = \ - lint.py_run('openlp --errors-only --disable={disabled} --enable={enabled} --reports=no --output-format=parseable'.format( - disabled=disabled_checks, - enabled=enabled_checks), + lint.py_run('openlp --errors-only --disable={disabled} --enable={enabled} ' + '--reports=no --output-format=parseable'.format(disabled=disabled_checks, + enabled=enabled_checks), return_std=True, script=pylint_script) stdout = pylint_stdout.read() stderr = pylint_stderr.read() - print(stdout) + filtered_stdout = self._filter_tolerated_errors(stdout) + print(filtered_stdout) print(stderr) # THEN: The output should be empty - self.assertTrue(stdout == 's', 'PyLint should find no errors') + self.assertTrue(filtered_stdout == '', 'PyLint should find no errors') + + def _filter_tolerated_errors(self, pylint_output): + """ + Filter out errors we tolerate. + """ + filtered_output = '' + for line in pylint_output.splitlines(): + # Filter out module info lines + if line.startswith('**'): + continue + # Filter out undefined-variable error releated to WindowsError + elif 'undefined-variable' in line and 'WindowsError' in line: + continue + # Filter out PyQt related errors + elif ('no-name-in-module' in line or 'no-member' in line) and 'PyQt5' in line: + continue + elif self._is_line_tolerated(line): + continue + else: + filtered_output += line + '\n' + return filtered_output.strip() + + def _is_line_tolerated(self, line): + """ + Check if line constains a tolerated error + """ + for filename in TOLERATED_ERRORS: + for tolerated_error in TOLERATED_ERRORS[filename]: + if filename in line and tolerated_error in line: + return True + return False