initial chkin of pylint
this is a pylint wrapper for Trove's tox tests. This commit includes a basic infrastructure for running pylint through tox. It also fixes some very obvious import errors that are flagged by the tool. One is to handle missing imports for _ and _LE. There is one instance where an exception is being raised but trove.common.exception isn't imported, and another where an exception is being incorrectly thrown. A short readme is also provided. Change-Id: I0a38f5efde3cb491f1f6c27f6c6500ab29987968 Partial-Bug: #1621636
This commit is contained in:
parent
92a78ea5e5
commit
a0bc0dcb7d
7
pylintrc
Normal file
7
pylintrc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# pylintrc
|
||||||
|
#
|
||||||
|
# For trove we use the defaults, this file is just to shut up an
|
||||||
|
# annoying error message from pylint.
|
||||||
|
#
|
||||||
|
# Don't set pylint options here.
|
||||||
|
#
|
@ -28,3 +28,5 @@ cassandra-driver!=3.6.0,>=2.1.4 # Apache-2.0
|
|||||||
pycrypto>=2.6 # Public Domain
|
pycrypto>=2.6 # Public Domain
|
||||||
couchdb>=0.8 # Apache-2.0
|
couchdb>=0.8 # Apache-2.0
|
||||||
os-testr>=0.7.0 # Apache-2.0
|
os-testr>=0.7.0 # Apache-2.0
|
||||||
|
astroid<1.4.0 # LGPLv2.1 # breaks pylint 1.4.4
|
||||||
|
pylint==1.4.5 # GPLv2
|
||||||
|
167
tools/trove-pylint.README
Normal file
167
tools/trove-pylint.README
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
trove-pylint
|
||||||
|
------------
|
||||||
|
|
||||||
|
trove-pylint.py is a wrapper around pylint which allows for some
|
||||||
|
custom processing relevant to the trove source tree, and suitable to
|
||||||
|
run as a CI job for trove.
|
||||||
|
|
||||||
|
The purpose is to perform a lint check on the code and detect obvious
|
||||||
|
(lintable) errors and fix them.
|
||||||
|
|
||||||
|
How trove-pylint works
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
trove-pylint is driven by a configuration file which is by default,
|
||||||
|
located in tools/trove-pylint.config. This file is a json dump of the
|
||||||
|
configuration. A default configuraiton file looks like this.
|
||||||
|
|
||||||
|
{
|
||||||
|
"include": ["*.py"],
|
||||||
|
"folder": "trove",
|
||||||
|
"options": ["--rcfile=./pylintrc", "-E"],
|
||||||
|
"ignored_files": ['trove/tests'],
|
||||||
|
"ignored_codes": [],
|
||||||
|
"ignored_messages": [],
|
||||||
|
"ignored_file_codes": [],
|
||||||
|
"ignored_file_messages": [],
|
||||||
|
"ignored_file_code_messages": [],
|
||||||
|
"always_error_messages": [
|
||||||
|
"Undefined variable '_'",
|
||||||
|
"Undefined variable '_LE'",
|
||||||
|
"Undefined variable '_LI'",
|
||||||
|
"Undefined variable '_LW'",
|
||||||
|
"Undefined variable '_LC'"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
include
|
||||||
|
-------
|
||||||
|
|
||||||
|
Provide a list of match specs (passed to fnmatch.fnmatch). The
|
||||||
|
default is only "*.py".
|
||||||
|
|
||||||
|
folder
|
||||||
|
------
|
||||||
|
|
||||||
|
Provide the name of the top level folder to lint. This is a single
|
||||||
|
value.
|
||||||
|
|
||||||
|
options
|
||||||
|
-------
|
||||||
|
|
||||||
|
These are the pylint launch options. The default is to specify an
|
||||||
|
rcfile and only errors. Specifying the rcfile is required, and the
|
||||||
|
file is a dummy, to suppress an annoying warning.
|
||||||
|
|
||||||
|
ignored_files
|
||||||
|
-------------
|
||||||
|
|
||||||
|
This is a list of paths that we wish to ignore. When a file is
|
||||||
|
considered for linting, if the path name begins with any of these
|
||||||
|
provided prefixes, the file will not be linted. We ignore the
|
||||||
|
tests directory because it has a high instance of false positives.
|
||||||
|
|
||||||
|
ignored_codes, ignored_messages, ignored_file_codes,
|
||||||
|
ignored_file_messages, and ignored_file_code_messages
|
||||||
|
-----------------------------------------------------
|
||||||
|
|
||||||
|
These settings identify specific failures that are to be
|
||||||
|
ignored. Each is a list, some are lists of single elements, others
|
||||||
|
are lists of lists.
|
||||||
|
|
||||||
|
ignored_codes, and ignored_messages are lists of single elements
|
||||||
|
that are to be ignored. You could specify either the code name, or
|
||||||
|
the code numeric representation. You must specify the exact
|
||||||
|
message.
|
||||||
|
|
||||||
|
ignored_file_codes and ignored_file_messages are lists of lists
|
||||||
|
where each element is a code and a message.
|
||||||
|
|
||||||
|
ignored_file_code_messages is a list of lists where each element
|
||||||
|
consists of a filename, an errorcode, a message, a line number and
|
||||||
|
a function name.
|
||||||
|
|
||||||
|
always_error_messages
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
This is a list of messages which have a low chance of false
|
||||||
|
positives, which are always flagged as errors.
|
||||||
|
|
||||||
|
Using trove-pylint
|
||||||
|
------------------
|
||||||
|
|
||||||
|
You can check your code for errors by simply running:
|
||||||
|
|
||||||
|
tox -e pylint
|
||||||
|
|
||||||
|
The equivalent result can be obtained by running the command:
|
||||||
|
|
||||||
|
tools/trove-pylint.py
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
tools/trove-pylint.py check
|
||||||
|
|
||||||
|
|
||||||
|
For example, here is the result from such a run.
|
||||||
|
|
||||||
|
$ tools/trove-pylint.py check
|
||||||
|
ERROR: trove/common/extensions.py 575: E1003 bad-super-call, \
|
||||||
|
TroveExtensionMiddleware.__init__: Bad first argument \
|
||||||
|
'ExtensionMiddleware' given to super()
|
||||||
|
Check failed. 367 files processed, 1 had errors, 1 errors recorded.
|
||||||
|
|
||||||
|
I wish to ignore this error and keep going. To do this, I rebuild the
|
||||||
|
list of errors to ignore as follows.
|
||||||
|
|
||||||
|
$ tools/trove-pylint.py rebuild
|
||||||
|
Rebuild completed. 367 files processed, 177 exceptions recorded.
|
||||||
|
|
||||||
|
This caused the tool to add the following two things to the config file.
|
||||||
|
|
||||||
|
[
|
||||||
|
"trove/common/extensions.py",
|
||||||
|
"E1003",
|
||||||
|
"Bad first argument 'ExtensionMiddleware' given to super()",
|
||||||
|
"575",
|
||||||
|
"TroveExtensionMiddleware.__init__"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"trove/common/extensions.py",
|
||||||
|
"bad-super-call",
|
||||||
|
"Bad first argument 'ExtensionMiddleware' given to super()",
|
||||||
|
"575",
|
||||||
|
"TroveExtensionMiddleware.__init__"
|
||||||
|
],
|
||||||
|
|
||||||
|
With that done, I can recheck as shown below.
|
||||||
|
|
||||||
|
$ tools/trove-pylint.py check
|
||||||
|
Check succeeded. 367 files processed
|
||||||
|
|
||||||
|
You can review the errors that are being currently ignored by reading
|
||||||
|
the file tools/trove-pylint.config.
|
||||||
|
|
||||||
|
If you want to fix some of these errors, identify the configuration(s)
|
||||||
|
that are causing those errors to be ignored and re-run the check. Once
|
||||||
|
you see that the errors are in fact being reported by the tool, go
|
||||||
|
ahead and fix the problem(s) and retest.
|
||||||
|
|
||||||
|
Known issues
|
||||||
|
------------
|
||||||
|
|
||||||
|
1. The tool appears to be very sensitive to the version(s) of pylint
|
||||||
|
and astroid. In testing, I've found that if the version of either of
|
||||||
|
these changes, you could either have a failure of the tool (exceptions
|
||||||
|
thrown, ...) or a different set of errors reported.
|
||||||
|
|
||||||
|
Currently, test-requirements.txt sets these versions in this way.
|
||||||
|
|
||||||
|
astroid<1.4.0 # LGPLv2.1 # breaks pylint 1.4.4
|
||||||
|
pylint==1.4.5 # GPLv2
|
||||||
|
|
||||||
|
If you run the tool on your machine and find that there are no errors,
|
||||||
|
but find that either the CI generates errors, or that the tool run
|
||||||
|
through tox generates errors, check what versions of astroid and
|
||||||
|
pylint are being run in each configuration.
|
||||||
|
|
2504
tools/trove-pylint.config
Normal file
2504
tools/trove-pylint.config
Normal file
File diff suppressed because it is too large
Load Diff
339
tools/trove-pylint.py
Executable file
339
tools/trove-pylint.py
Executable file
@ -0,0 +1,339 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# Copyright 2016 Tesora, Inc.
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import fnmatch
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from pylint import lint
|
||||||
|
from pylint.reporters import text
|
||||||
|
from six.moves import cStringIO as csio
|
||||||
|
|
||||||
|
DEFAULT_CONFIG_FILE = "tools/trove-pylint.config"
|
||||||
|
DEFAULT_IGNORED_FILES = ['trove/tests']
|
||||||
|
DEFAULT_IGNORED_CODES = []
|
||||||
|
DEFAULT_IGNORED_MESSAGES = []
|
||||||
|
DEFAULT_ALWAYS_ERROR = [
|
||||||
|
"Undefined variable '_'",
|
||||||
|
"Undefined variable '_LE'",
|
||||||
|
"Undefined variable '_LI'",
|
||||||
|
"Undefined variable '_LW'",
|
||||||
|
"Undefined variable '_LC'"]
|
||||||
|
|
||||||
|
MODE_CHECK = "check"
|
||||||
|
MODE_REBUILD = "rebuild"
|
||||||
|
|
||||||
|
class Config(object):
|
||||||
|
def __init__(self, filename=DEFAULT_CONFIG_FILE):
|
||||||
|
|
||||||
|
self.default_config = {
|
||||||
|
"include": ["*.py"],
|
||||||
|
"folder": "trove",
|
||||||
|
"options": ["--rcfile=./pylintrc", "-E"],
|
||||||
|
"ignored_files": DEFAULT_IGNORED_FILES,
|
||||||
|
"ignored_codes": DEFAULT_IGNORED_CODES,
|
||||||
|
"ignored_messages": DEFAULT_IGNORED_MESSAGES,
|
||||||
|
"ignored_file_codes": [],
|
||||||
|
"ignored_file_messages": [],
|
||||||
|
"ignored_file_code_messages": [],
|
||||||
|
"always_error_messages": DEFAULT_ALWAYS_ERROR
|
||||||
|
}
|
||||||
|
|
||||||
|
self.config = self.default_config
|
||||||
|
|
||||||
|
def save(self, filename=DEFAULT_CONFIG_FILE):
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
os.rename(filename, "%s~" % filename)
|
||||||
|
|
||||||
|
with open(filename, 'w') as fp:
|
||||||
|
json.dump(self.config, fp, encoding="utf-8",
|
||||||
|
indent=2, separators=(',', ': '))
|
||||||
|
|
||||||
|
def load(self, filename=DEFAULT_CONFIG_FILE):
|
||||||
|
self.config = self.default_config
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(filename) as fp:
|
||||||
|
_c = json.load(fp, encoding="utf-8")
|
||||||
|
|
||||||
|
self.config = _c
|
||||||
|
except Exception:
|
||||||
|
print("An error occured loading configuration, using default.")
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get(self, attribute):
|
||||||
|
return self.config[attribute]
|
||||||
|
|
||||||
|
def is_file_ignored(self, f):
|
||||||
|
if any(f.startswith(i)
|
||||||
|
for i in self.config['ignored_files']):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_file_included(self, f):
|
||||||
|
if any(fnmatch.fnmatch(f, wc) for wc in self.config['include']):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_always_error(self, message):
|
||||||
|
if message in self.config['always_error_messages']:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def ignore(self, filename, code, codename, message):
|
||||||
|
# the high priority checks
|
||||||
|
if self.is_file_ignored(filename):
|
||||||
|
return True
|
||||||
|
|
||||||
|
# never ignore messages
|
||||||
|
if self.is_always_error(message):
|
||||||
|
return False
|
||||||
|
|
||||||
|
if code in self.config['ignored_codes']:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if codename in self.config['ignored_codes']:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if message and any(message.startswith(ignore_message)
|
||||||
|
for ignore_message
|
||||||
|
in self.config['ignored_messages']):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if filename and message and (
|
||||||
|
[filename, message] in self.config['ignored_file_messages']):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if filename and code and (
|
||||||
|
[filename, code] in self.config['ignored_file_codes']):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if filename and codename and (
|
||||||
|
[filename, codename] in self.config['ignored_file_codes']):
|
||||||
|
return True
|
||||||
|
|
||||||
|
fcm_ignore1 = [filename, codename, message]
|
||||||
|
fcm_ignore2 = [filename, codename, message]
|
||||||
|
for fcm in self.config['ignored_file_code_messages']:
|
||||||
|
if fcm_ignore1 == [fcm[0], fcm[1], fcm[2]]:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if fcm_ignore2 == [fcm[0], fcm[1], fcm[2]]:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def ignore_code(self, c):
|
||||||
|
_c = set(self.config['ignored_codes'])
|
||||||
|
_c.add(c)
|
||||||
|
self.config['ignored_codes'] = list(_c)
|
||||||
|
|
||||||
|
def ignore_files(self, f):
|
||||||
|
_c = set(self.config['ignored_files'])
|
||||||
|
_c.add(f)
|
||||||
|
self.config['ignored_files'] = list(_c)
|
||||||
|
|
||||||
|
def ignore_message(self, m):
|
||||||
|
_c = set(self.config['ignored_messages'])
|
||||||
|
_c.add(m)
|
||||||
|
self.config['ignored_messages'] = list(_c)
|
||||||
|
|
||||||
|
def ignore_file_code(self, f, c):
|
||||||
|
_c = set(self.config['ignored_file_codes'])
|
||||||
|
_c.add((f, c))
|
||||||
|
self.config['ignored_file_codes'] = list(_c)
|
||||||
|
|
||||||
|
def ignore_file_message(self, f, m):
|
||||||
|
_c = set(self.config['ignored_file_messages'])
|
||||||
|
_c.add((f, m))
|
||||||
|
self.config['ignored_file_messages'] = list(_c)
|
||||||
|
|
||||||
|
def ignore_file_code_message(self, f, c, m, l, fn):
|
||||||
|
_c = set(self.config['ignored_file_code_messages'])
|
||||||
|
_c.add((f, c, m, l, fn))
|
||||||
|
self.config['ignored_file_code_messages'] = list(_c)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) == 1 or sys.argv[1] == "check":
|
||||||
|
return check()
|
||||||
|
elif sys.argv[1] == "rebuild":
|
||||||
|
return rebuild()
|
||||||
|
elif sys.argv[1] == "initialize":
|
||||||
|
return initialize()
|
||||||
|
else:
|
||||||
|
return usage()
|
||||||
|
|
||||||
|
def usage():
|
||||||
|
print("Usage: %s [check|rebuild]" % sys.argv[0])
|
||||||
|
print("\tUse this tool to perform a lint check of the trove project.")
|
||||||
|
print("\t check: perform the lint check.")
|
||||||
|
print("\t rebuild: rebuild the list of exceptions to ignore.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
class LintRunner(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.config = Config()
|
||||||
|
self.idline = re.compile("^[*]* Module .*")
|
||||||
|
self.detail = re.compile("(\S+):(\d+): \[(\S+)\((\S+)\), (\S+)?] (.*)")
|
||||||
|
|
||||||
|
def dolint(self, filename):
|
||||||
|
exceptions = set()
|
||||||
|
|
||||||
|
buffer = csio()
|
||||||
|
reporter = text.ParseableTextReporter(output=buffer)
|
||||||
|
options = list(self.config.get('options'))
|
||||||
|
options.append(filename)
|
||||||
|
lint.Run(options, reporter=reporter, exit=False)
|
||||||
|
|
||||||
|
output = buffer.getvalue()
|
||||||
|
buffer.close()
|
||||||
|
|
||||||
|
for line in output.splitlines():
|
||||||
|
if self.idline.match(line):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self.detail.match(line):
|
||||||
|
mo = self.detail.search(line)
|
||||||
|
tokens = mo.groups()
|
||||||
|
fn = tokens[0]
|
||||||
|
ln = tokens[1]
|
||||||
|
code = tokens[2]
|
||||||
|
codename = tokens[3]
|
||||||
|
func = tokens[4]
|
||||||
|
message = tokens[5]
|
||||||
|
|
||||||
|
if not self.config.ignore(fn, code, codename, message):
|
||||||
|
exceptions.add((fn, ln, code, codename, func, message))
|
||||||
|
|
||||||
|
return exceptions
|
||||||
|
|
||||||
|
def process(self, mode=MODE_CHECK):
|
||||||
|
files_processed = 0
|
||||||
|
files_with_errors = 0
|
||||||
|
errors_recorded = 0
|
||||||
|
exceptions_recorded = 0
|
||||||
|
|
||||||
|
for (root, dirs, files) in os.walk(self.config.get('folder')):
|
||||||
|
# if we shouldn't even bother about this part of the
|
||||||
|
# directory structure, we can punt quietly
|
||||||
|
if self.config.is_file_ignored(root):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# since we are walking top down, let's clean up the dirs
|
||||||
|
# that we will walk by eliminating any dirs that will
|
||||||
|
# end up getting ignored
|
||||||
|
for d in dirs:
|
||||||
|
p = os.path.join(root, d)
|
||||||
|
if self.config.is_file_ignored(p):
|
||||||
|
dirs.remove(d)
|
||||||
|
|
||||||
|
# check if we can ignore the file and process if not
|
||||||
|
for f in files:
|
||||||
|
p = os.path.join(root, f)
|
||||||
|
if self.config.is_file_ignored(p):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not self.config.is_file_included(f):
|
||||||
|
continue
|
||||||
|
|
||||||
|
files_processed += 1
|
||||||
|
exceptions = self.dolint(p)
|
||||||
|
file_had_errors = 0
|
||||||
|
|
||||||
|
for e in exceptions:
|
||||||
|
# what we do with this exception depents on the
|
||||||
|
# kind of exception, and the mode
|
||||||
|
if self.config.is_always_error(e[5]):
|
||||||
|
print("ERROR: %s %s: %s %s, %s: %s" %
|
||||||
|
(e[0], e[1], e[2], e[3], e[4], e[5]))
|
||||||
|
errors_recorded += 1
|
||||||
|
file_had_errors += 1
|
||||||
|
elif mode == MODE_REBUILD:
|
||||||
|
# parameters to ignore_file_code_message are
|
||||||
|
# filename, code, message, linenumber, and function
|
||||||
|
self.config.ignore_file_code_message(e[0], e[2], e[-1], e[1], e[4])
|
||||||
|
self.config.ignore_file_code_message(e[0], e[3], e[-1], e[1], e[4])
|
||||||
|
exceptions_recorded += 1
|
||||||
|
elif mode == MODE_CHECK:
|
||||||
|
print("ERROR: %s %s: %s %s, %s: %s" %
|
||||||
|
(e[0], e[1], e[2], e[3], e[4], e[5]))
|
||||||
|
errors_recorded += 1
|
||||||
|
file_had_errors += 1
|
||||||
|
|
||||||
|
|
||||||
|
if file_had_errors:
|
||||||
|
files_with_errors += 1
|
||||||
|
|
||||||
|
return (files_processed, files_with_errors, errors_recorded,
|
||||||
|
exceptions_recorded)
|
||||||
|
|
||||||
|
def rebuild(self):
|
||||||
|
self.initialize()
|
||||||
|
(files_processed,
|
||||||
|
files_with_errors,
|
||||||
|
errors_recorded,
|
||||||
|
exceptions_recorded) = self.process(mode=MODE_REBUILD)
|
||||||
|
|
||||||
|
if files_with_errors > 0:
|
||||||
|
print("Rebuild failed. %s files processed, %s had errors, "
|
||||||
|
"%s errors recorded." % (
|
||||||
|
files_processed, files_with_errors, errors_recorded))
|
||||||
|
|
||||||
|
return 1
|
||||||
|
|
||||||
|
self.config.save()
|
||||||
|
print("Rebuild completed. %s files processed, %s exceptions recorded." %
|
||||||
|
(files_processed, exceptions_recorded))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def check(self):
|
||||||
|
self.config.load()
|
||||||
|
(files_processed,
|
||||||
|
files_with_errors,
|
||||||
|
errors_recorded,
|
||||||
|
exceptions_recorded) = self.process(mode=MODE_CHECK)
|
||||||
|
|
||||||
|
if files_with_errors > 0:
|
||||||
|
print("Check failed. %s files processed, %s had errors, "
|
||||||
|
"%s errors recorded." % (
|
||||||
|
files_processed, files_with_errors, errors_recorded))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print("Check succeeded. %s files processed" % files_processed)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def initialize(self):
|
||||||
|
self.config.save()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def check():
|
||||||
|
exit(LintRunner().check())
|
||||||
|
|
||||||
|
def rebuild():
|
||||||
|
exit(LintRunner().rebuild())
|
||||||
|
|
||||||
|
def initialize():
|
||||||
|
exit(LintRunner().initialize())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
8
tox.ini
8
tox.ini
@ -1,5 +1,5 @@
|
|||||||
[tox]
|
[tox]
|
||||||
envlist = py{27,34,35},pep8,apiexamples,cover,api-ref,releasenotes,bandit,fakemodetests
|
envlist = py{27,34,35},pep8,apiexamples,cover,api-ref,releasenotes,bandit,fakemodetests,pylint
|
||||||
minversion = 1.6
|
minversion = 1.6
|
||||||
skipsdist = True
|
skipsdist = True
|
||||||
|
|
||||||
@ -91,3 +91,9 @@ commands = bandit -r trove -n5 -x tests
|
|||||||
|
|
||||||
[testenv:install-guide]
|
[testenv:install-guide]
|
||||||
commands = sphinx-build -a -E -W -d install-guide/build/doctrees -b html install-guide/source install-guide/build/html
|
commands = sphinx-build -a -E -W -d install-guide/build/doctrees -b html install-guide/source install-guide/build/html
|
||||||
|
|
||||||
|
[testenv:pylint]
|
||||||
|
deps = -r{toxinidir}/requirements.txt
|
||||||
|
-r{toxinidir}/test-requirements.txt
|
||||||
|
commands = {[testenv]commands}
|
||||||
|
python tools/trove-pylint.py check
|
||||||
|
@ -23,6 +23,7 @@ from oslo_log import log as logging
|
|||||||
from oslo_middleware import cors
|
from oslo_middleware import cors
|
||||||
from osprofiler import opts as profiler
|
from osprofiler import opts as profiler
|
||||||
|
|
||||||
|
from trove.common.i18n import _
|
||||||
from trove.version import version_info as version
|
from trove.version import version_info as version
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import sys
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from trove.common.i18n import _
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
@ -19,6 +19,7 @@ from oslo_log import log as logging
|
|||||||
|
|
||||||
from trove.common import cfg
|
from trove.common import cfg
|
||||||
from trove.common import exception
|
from trove.common import exception
|
||||||
|
from trove.common.i18n import _
|
||||||
from trove.common.remote import create_nova_client
|
from trove.common.remote import create_nova_client
|
||||||
from trove.common import utils
|
from trove.common import utils
|
||||||
from trove.db import get_db_api
|
from trove.db import get_db_api
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from trove.common.i18n import _
|
||||||
from trove.guestagent.common import operating_system
|
from trove.guestagent.common import operating_system
|
||||||
from trove.guestagent.datastore.galera_common import service as galera_service
|
from trove.guestagent.datastore.galera_common import service as galera_service
|
||||||
from trove.guestagent.datastore.mysql_common import service as mysql_service
|
from trove.guestagent.datastore.mysql_common import service as mysql_service
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
from trove.common.i18n import _
|
||||||
from trove.guestagent.datastore.mysql_common import service
|
from trove.guestagent.datastore.mysql_common import service
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
import psycopg2
|
import psycopg2
|
||||||
|
|
||||||
from trove.common import exception
|
from trove.common import exception
|
||||||
|
from trove.common.i18n import _
|
||||||
|
|
||||||
PG_ADMIN = 'os_admin'
|
PG_ADMIN = 'os_admin'
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import psycopg2
|
import psycopg2
|
||||||
|
|
||||||
|
from trove.common.i18n import _
|
||||||
from trove.common import instance
|
from trove.common import instance
|
||||||
from trove.common import utils
|
from trove.common import utils
|
||||||
from trove.guestagent.datastore.experimental.postgresql import pgutil
|
from trove.guestagent.datastore.experimental.postgresql import pgutil
|
||||||
|
@ -21,6 +21,7 @@ import time
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from trove.common import exception
|
from trove.common import exception
|
||||||
|
from trove.common.i18n import _
|
||||||
from trove.common import utils
|
from trove.common import utils
|
||||||
from trove.guestagent.common import operating_system
|
from trove.guestagent.common import operating_system
|
||||||
from trove.guestagent.datastore.experimental.couchbase import service
|
from trove.guestagent.datastore.experimental.couchbase import service
|
||||||
|
@ -20,7 +20,6 @@ from eventlet.green import subprocess
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from trove.common import cfg
|
from trove.common import cfg
|
||||||
from trove.common import exception
|
|
||||||
from trove.common.i18n import _
|
from trove.common.i18n import _
|
||||||
from trove.common import stream_codecs
|
from trove.common import stream_codecs
|
||||||
from trove.guestagent.common import operating_system
|
from trove.guestagent.common import operating_system
|
||||||
@ -89,7 +88,7 @@ class PgDump(base.RestoreRunner):
|
|||||||
for message in err.splitlines(False):
|
for message in err.splitlines(False):
|
||||||
if not any(regex.match(message)
|
if not any(regex.match(message)
|
||||||
for regex in self.IGNORED_ERROR_PATTERNS):
|
for regex in self.IGNORED_ERROR_PATTERNS):
|
||||||
raise exception(message)
|
raise Exception(message)
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user