From 47aebb16f15fe6f8ce29d8c6b105f10d8d64c295 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Mon, 24 Sep 2012 14:12:07 -0400 Subject: more pylint checks --- src/lib/Bcfg2/Server/Plugins/Bundler.py | 96 ++++++------- src/lib/Bcfg2/Server/Plugins/GroupPatterns.py | 51 ++++--- src/lib/Bcfg2/Server/Plugins/Ohai.py | 18 +-- src/sbin/bcfg2-server | 9 +- src/sbin/bcfg2-yum-helper | 34 +++-- testsuite/Testsrc/testmisc.py | 187 +++++++++++++++----------- testsuite/pylintrc.conf | 4 +- 7 files changed, 234 insertions(+), 165 deletions(-) diff --git a/src/lib/Bcfg2/Server/Plugins/Bundler.py b/src/lib/Bcfg2/Server/Plugins/Bundler.py index 65914c371..15f8031ca 100644 --- a/src/lib/Bcfg2/Server/Plugins/Bundler.py +++ b/src/lib/Bcfg2/Server/Plugins/Bundler.py @@ -14,22 +14,27 @@ import Bcfg2.Server.Lint try: import genshi.template.base import Bcfg2.Server.Plugins.TGenshi - have_genshi = True -except: - have_genshi = False + HAS_GENSHI = True +except ImportError: + HAS_GENSHI = False class BundleFile(Bcfg2.Server.Plugin.StructFile): + """ Representation of a bundle XML file """ def get_xml_value(self, metadata): + """ get the XML data that applies to the given client """ bundlename = os.path.splitext(os.path.basename(self.name))[0] bundle = lxml.etree.Element('Bundle', name=bundlename) - [bundle.append(copy.copy(item)) for item in self.Match(metadata)] + for item in self.Match(metadata): + bundle.append(copy.copy(item)) return bundle -if have_genshi: +if HAS_GENSHI: class BundleTemplateFile(Bcfg2.Server.Plugins.TGenshi.TemplateFile, Bcfg2.Server.Plugin.StructFile): + """ Representation of a Genshi-templated bundle XML file """ + def __init__(self, name, specific, encoding): Bcfg2.Server.Plugins.TGenshi.TemplateFile.__init__(self, name, specific, @@ -38,54 +43,43 @@ if have_genshi: self.logger = logging.getLogger(name) def get_xml_value(self, metadata): + """ get the rendered XML data that applies to the given + client """ if not hasattr(self, 'template'): self.logger.error("No parsed template information for %s" % self.name) raise Bcfg2.Server.Plugin.PluginExecutionError - try: - stream = self.template.generate(metadata=metadata).filter( - Bcfg2.Server.Plugins.TGenshi.removecomment) - data = lxml.etree.XML(stream.render('xml', - strip_whitespace=False), - parser=Bcfg2.Server.XMLParser) - bundlename = os.path.splitext(os.path.basename(self.name))[0] - bundle = lxml.etree.Element('Bundle', name=bundlename) - for item in self.Match(metadata, data): - bundle.append(copy.deepcopy(item)) - return bundle - except LookupError: - lerror = sys.exc_info()[1] - self.logger.error('Genshi lookup error: %s' % lerror) - except genshi.template.TemplateError: - terror = sys.exc_info()[1] - self.logger.error('Genshi template error: %s' % terror) - raise - except genshi.input.ParseError: - perror = sys.exc_info()[1] - self.logger.error('Genshi parse error: %s' % perror) - raise - - def Match(self, metadata, xdata): + stream = self.template.generate(metadata=metadata).filter( + Bcfg2.Server.Plugins.TGenshi.removecomment) + data = lxml.etree.XML(stream.render('xml', + strip_whitespace=False), + parser=Bcfg2.Server.XMLParser) + bundlename = os.path.splitext(os.path.basename(self.name))[0] + bundle = lxml.etree.Element('Bundle', name=bundlename) + for item in self.Match(metadata, data): + bundle.append(copy.deepcopy(item)) + return bundle + + def Match(self, metadata, xdata): # pylint: disable=W0221 """Return matching fragments of parsed template.""" rv = [] for child in xdata.getchildren(): rv.extend(self._match(child, metadata)) - self.logger.debug("File %s got %d match(es)" % (self.name, len(rv))) + self.logger.debug("File %s got %d match(es)" % (self.name, + len(rv))) return rv - class SGenshiTemplateFile(BundleTemplateFile): - # provided for backwards compat + """ provided for backwards compat with the deprecated SGenshi + plugin """ pass class Bundler(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.Structure, Bcfg2.Server.Plugin.XMLDirectoryBacked): - """The bundler creates dependent clauses based on the - bundle/translation scheme from Bcfg1. - """ - name = 'Bundler' + """ The bundler creates dependent clauses based on the + bundle/translation scheme from Bcfg1. """ __author__ = 'bcfg-dev@mcs.anl.gov' patterns = re.compile('^(?P.*)\.(xml|genshi)$') @@ -103,17 +97,22 @@ class Bundler(Bcfg2.Server.Plugin.Plugin, raise Bcfg2.Server.Plugin.PluginInitError def template_dispatch(self, name, _): + """ Add the correct child entry type to Bundler depending on + whether the XML file in question is a plain XML file or a + templated bundle """ bundle = lxml.etree.parse(name, parser=Bcfg2.Server.XMLParser) nsmap = bundle.getroot().nsmap if (name.endswith('.genshi') or ('py' in nsmap and nsmap['py'] == 'http://genshi.edgewall.org/')): - if have_genshi: + if HAS_GENSHI: spec = Bcfg2.Server.Plugin.Specificity() return BundleTemplateFile(name, spec, self.encoding) else: - raise Bcfg2.Server.Plugin.PluginExecutionError("Genshi not available: %s" % name) + raise Bcfg2.Server.Plugin.PluginExecutionError("Genshi not " + "available: %s" + % name) else: return BundleFile(name, self.fam) @@ -123,8 +122,9 @@ class Bundler(Bcfg2.Server.Plugin.Plugin, bundle_entries = {} for key, item in self.entries.items(): - bundle_entries.setdefault(self.patterns.match(os.path.basename(key)).group('name'), - []).append(item) + bundle_entries.setdefault( + self.patterns.match(os.path.basename(key)).group('name'), + []).append(item) for bundlename in metadata.bundles: try: @@ -136,10 +136,9 @@ class Bundler(Bcfg2.Server.Plugin.Plugin, try: bundleset.append(entries[0].get_xml_value(metadata)) except genshi.template.base.TemplateError: - t = sys.exc_info()[1] - self.logger.error("Bundler: Failed to template genshi bundle %s" - % bundlename) - self.logger.error(t) + err = sys.exc_info()[1] + self.logger.error("Bundler: Failed to render templated bundle " + "%s: %s" % (bundlename, err)) except: self.logger.error("Bundler: Unexpected bundler error for %s" % bundlename, exc_info=1) @@ -148,19 +147,20 @@ class Bundler(Bcfg2.Server.Plugin.Plugin, class BundlerLint(Bcfg2.Server.Lint.ServerPlugin): """ Perform various bundle checks """ + def Run(self): """ run plugin """ self.missing_bundles() for bundle in self.core.plugins['Bundler'].entries.values(): if (self.HandlesFile(bundle.name) and - (not have_genshi or + (not HAS_GENSHI or not isinstance(bundle, BundleTemplateFile))): - self.bundle_names(bundle) + self.bundle_names(bundle) @classmethod def Errors(cls): - return {"bundle-not-found":"error", - "inconsistent-bundle-name":"warning"} + return {"bundle-not-found": "error", + "inconsistent-bundle-name": "warning"} def missing_bundles(self): """ find bundles listed in Metadata but not implemented in Bundler """ diff --git a/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py index 955a46c6c..6f0695bc3 100644 --- a/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py +++ b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py @@ -1,12 +1,16 @@ +""" set group membership based on client hostnames """ + import os import re import sys import logging -import lxml.etree import Bcfg2.Server.Lint import Bcfg2.Server.Plugin + class PackedDigitRange(object): + """ Helper object for NameRange entries """ + def __init__(self, digit_range): self.sparse = list() self.ranges = list() @@ -17,6 +21,7 @@ class PackedDigitRange(object): self.sparse.append(int(item)) def includes(self, other): + """ return True if other is included in this range """ iother = int(other) if iother in self.sparse: return True @@ -27,7 +32,7 @@ class PackedDigitRange(object): class PatternMap(object): - range_finder = r'\[\[[\d\-,]+\]\]' + """ Handler for a single pattern or range """ def __init__(self, pattern, rangestr, groups): self.pattern = pattern @@ -39,10 +44,11 @@ class PatternMap(object): elif rangestr != None: if '\\' in rangestr: raise Exception("Backslashes are not allowed in NameRanges") + range_finder = r'\[\[[\d\-,]+\]\]' self.process = self.process_range - self.re = re.compile('^' + re.sub(self.range_finder, '(\d+)', + self.re = re.compile('^' + re.sub(range_finder, '(\d+)', rangestr)) - dmatcher = re.compile(re.sub(self.range_finder, + dmatcher = re.compile(re.sub(range_finder, r'\[\[([\d\-,]+)\]\]', rangestr)) self.dranges = [PackedDigitRange(x) @@ -51,16 +57,18 @@ class PatternMap(object): raise Exception("No pattern or range given") def process_range(self, name): + """ match the given hostname against a range-based NameRange """ match = self.re.match(name) if not match: return None digits = match.groups() - for i in range(len(digits)): - if not self.dranges[i].includes(digits[i]): + for grp in range(len(digits)): + if not self.dranges[grp].includes(digits[grp]): return None return self.groups def process_re(self, name): + """ match the given hostname against a regex-based NamePattern """ match = self.re.search(name) if not match: return None @@ -79,6 +87,7 @@ class PatternMap(object): class PatternFile(Bcfg2.Server.Plugin.XMLFileBacked): + """ representation of GroupPatterns config.xml """ __identifier__ = None def __init__(self, filename, core=None): @@ -107,27 +116,29 @@ class PatternFile(Bcfg2.Server.Plugin.XMLFileBacked): for range_ent in entry.findall('NameRange'): rng = range_ent.text self.patterns.append(PatternMap(None, rng, groups)) - except: - self.logger.error("GroupPatterns: Failed to initialize pattern " - "%s" % entry.get('pattern')) + except: # pylint: disable=W0702 + self.logger.error("GroupPatterns: Failed to initialize " + "pattern %s" % entry.get('pattern')) def process_patterns(self, hostname): + """ return a list of groups that should be added to the given + client based on patterns that match the hostname """ ret = [] for pattern in self.patterns: try: - gn = pattern.process(hostname) - if gn is not None: - ret.extend(gn) - except: - self.logger.error("GroupPatterns: Failed to process pattern %s " - "for %s" % (pattern.pattern, hostname), + grpname = pattern.process(hostname) + if grpname is not None: + ret.extend(grpname) + except: # pylint: disable=W0702 + self.logger.error("GroupPatterns: Failed to process pattern " + "%s for %s" % (pattern.pattern, hostname), exc_info=1) return ret class GroupPatterns(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.Connector): - name = "GroupPatterns" + """ set group membership based on client hostnames """ def __init__(self, core, datastore): Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore) @@ -140,8 +151,9 @@ class GroupPatterns(Bcfg2.Server.Plugin.Plugin, class GroupPatternsLint(Bcfg2.Server.Lint.ServerPlugin): + """ bcfg2-lint plugin for GroupPatterns """ + def Run(self): - """ run plugin """ cfg = self.core.plugins['GroupPatterns'].config for entry in cfg.xdata.xpath('//GroupPattern'): groups = [g.text for g in entry.findall('Group')] @@ -150,9 +162,10 @@ class GroupPatternsLint(Bcfg2.Server.Lint.ServerPlugin): @classmethod def Errors(cls): - return {"pattern-fails-to-initialize":"error"} + return {"pattern-fails-to-initialize": "error"} def check(self, entry, groups, ptype="NamePattern"): + """ Check a single pattern for validity """ if ptype == "NamePattern": pmap = lambda p: PatternMap(p, None, groups) else: @@ -162,7 +175,7 @@ class GroupPatternsLint(Bcfg2.Server.Lint.ServerPlugin): pat = el.text try: pmap(pat) - except: + except: # pylint: disable=W0702 err = sys.exc_info()[1] self.LintError("pattern-fails-to-initialize", "Failed to initialize %s %s for %s: %s" % diff --git a/src/lib/Bcfg2/Server/Plugins/Ohai.py b/src/lib/Bcfg2/Server/Plugins/Ohai.py index 052597f84..fbb46f004 100644 --- a/src/lib/Bcfg2/Server/Plugins/Ohai.py +++ b/src/lib/Bcfg2/Server/Plugins/Ohai.py @@ -1,6 +1,9 @@ +"""The Ohai plugin is used to detect information about the client +operating system using ohai +(http://wiki.opscode.com/display/chef/Ohai) """ + import lxml.etree import os -import logging import Bcfg2.Server.Plugin # pylint: disable=F0401 @@ -10,10 +13,7 @@ except ImportError: import simplejson as json # pylint: enable=F0401 -logger = logging.getLogger('Bcfg2.Plugins.Ohai') - - -probecode = """#!/bin/sh +PROBECODE = """#!/bin/sh export PATH=$PATH:/sbin:/usr/sbin @@ -27,6 +27,8 @@ fi class OhaiCache(object): + """ Storage for Ohai output on the local filesystem so that the + output can be used by bcfg2-info, etc. """ def __init__(self, dirname): self.dirname = dirname self.cache = dict() @@ -68,14 +70,14 @@ class Ohai(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.Connector.__init__(self) self.probe = lxml.etree.Element('probe', name='Ohai', source='Ohai', interpreter='/bin/sh') - self.probe.text = probecode + self.probe.text = PROBECODE try: os.stat(self.data) - except: + except OSError: os.makedirs(self.data) self.cache = OhaiCache(self.data) - def GetProbes(self, meta, force=False): + def GetProbes(self, _): return [self.probe] def ReceiveData(self, meta, datalist): diff --git a/src/sbin/bcfg2-server b/src/sbin/bcfg2-server index fe16866cf..8322edeaa 100755 --- a/src/sbin/bcfg2-server +++ b/src/sbin/bcfg2-server @@ -9,9 +9,9 @@ import Bcfg2.Logger import Bcfg2.Options from Bcfg2.Server.Core import CoreInitError -logger = logging.getLogger('bcfg2-server') +LOGGER = logging.getLogger('bcfg2-server') -if __name__ == '__main__': +def main(): optinfo = dict() optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS) @@ -41,9 +41,12 @@ if __name__ == '__main__': core.run() except CoreInitError: msg = sys.exc_info()[1] - logger.error(msg) + LOGGER.error(msg) sys.exit(1) except KeyboardInterrupt: sys.exit(1) sys.exit(0) + +if __name__ == '__main__': + sys.exit(main()) diff --git a/src/sbin/bcfg2-yum-helper b/src/sbin/bcfg2-yum-helper index 859ec36b6..ba6f30406 100755 --- a/src/sbin/bcfg2-yum-helper +++ b/src/sbin/bcfg2-yum-helper @@ -48,6 +48,8 @@ def pkg_to_tuple(package): def pkgtup_to_string(package): + """ given a package tuple, return a human-readable string + describing the package """ if package[3] in ['auto', 'any']: return package[0] @@ -61,31 +63,37 @@ def pkgtup_to_string(package): class DepSolver(object): + """ Yum dependency solver """ + def __init__(self, cfgfile, verbose=1): self.cfgfile = cfgfile self.yumbase = yum.YumBase() - # pylint: disable=E1121 + # pylint: disable=E1121,W0212 try: self.yumbase.preconf.debuglevel = verbose self.yumbase.preconf.fn = cfgfile self.yumbase._getConfig() except AttributeError: self.yumbase._getConfig(cfgfile, debuglevel=verbose) - # pylint: enable=E1121 + # pylint: enable=E1121,W0212 self.logger = get_logger(verbose) + self._groups = None def get_groups(self): - try: + """ getter for the groups property """ + if self._groups is not None: return self._groups - except AttributeError: + else: return ["noarch"] def set_groups(self, groups): + """ setter for the groups property """ self._groups = set(groups).union(["noarch"]) groups = property(get_groups, set_groups) def get_package_object(self, pkgtup, silent=False): + """ given a package tuple, get a yum package object """ try: matches = yum.packageSack.packagesNewestByName( self.yumbase.pkgSack.searchPkgTuple(pkgtup)) @@ -107,6 +115,7 @@ class DepSolver(object): return None def get_group(self, group, ptype="default"): + """ Resolve a package group name into a list of packages """ if group.startswith("@"): group = group[1:] @@ -136,6 +145,8 @@ class DepSolver(object): return [] def _filter_arch(self, packages): + """ filter packages in the given list that do not have an + architecture in the list of groups for this client """ matching = [] for pkg in packages: if pkg.arch in self.groups: @@ -163,6 +174,8 @@ class DepSolver(object): return str(package) def complete(self, packagelist): + """ resolve dependencies and generate a complete package list + from the given list of initial packages """ packages = set() unknown = set() for pkg in packagelist: @@ -170,18 +183,18 @@ class DepSolver(object): pkgtup = pkg else: pkgtup = (pkg, None, None, None, None) - po = self.get_package_object(pkgtup) - if not po: + pkgobj = self.get_package_object(pkgtup) + if not pkgobj: self.logger.debug("Unknown package %s" % self.get_package_name(pkg)) unknown.add(pkg) else: - if self.yumbase.tsInfo.exists(pkgtup=po.pkgtup): + if self.yumbase.tsInfo.exists(pkgtup=pkgobj.pkgtup): self.logger.debug("%s added to transaction multiple times" - % po) + % pkgobj) else: - self.logger.debug("Adding %s to transaction" % po) - self.yumbase.tsInfo.addInstall(po) + self.logger.debug("Adding %s to transaction" % pkgobj) + self.yumbase.tsInfo.addInstall(pkgobj) self.yumbase.resolveDeps() for txmbr in self.yumbase.tsInfo: @@ -189,6 +202,7 @@ class DepSolver(object): return list(packages), list(unknown) def clean_cache(self): + """ clean the yum cache """ for mdtype in ["Headers", "Packages", "Sqlite", "Metadata", "ExpireCache"]: # for reasons that are entirely obvious, all of the yum diff --git a/testsuite/Testsrc/testmisc.py b/testsuite/Testsrc/testmisc.py index 3ea80310e..41a91caff 100644 --- a/testsuite/Testsrc/testmisc.py +++ b/testsuite/Testsrc/testmisc.py @@ -29,91 +29,135 @@ srcpath = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", rcfile = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "pylintrc.conf")) +# test for pylint existence +try: + Popen(['pylint'], stdout=PIPE, stderr=STDOUT).wait() + HAS_PYLINT = True +except OSError: + HAS_PYLINT = False -class TestPylint(Bcfg2TestCase): - # right now, too many things fail pylint miserably to just test - # everything, or even to do a blacklist, so we just whitelist the - # things we do want to do a full check on and only check most - # stuff for errors and fatal errors. This is a dict of - # => . - # is relative to src/ - whitelist = { - "lib/Bcfg2/Server": ["Lint", - "Plugin", - "BuiltinCore.py", - "CherryPyCore.py", - "Core.py"], - "lib/Bcfg2/Server/Plugins": ["PuppetENC.py", - "Rules.py", - "DBStats.py", - "Trigger.py", - "Defaults.py", - "Probes.py", - "TemplateHelper.py", - "Guppy.py", - "FileProbes.py", - "ServiceCompat.py", - "Properties.py", - "SEModules.py", - "Darcs.py", - "Git.py", - "Hg.py", - "Cvs.py", - "Fossil.py", - "Svn.py", - "Svn2.py", - "Bzr.py", - "Cfg", - "Packages"], - "lib/Bcfg2/Client/Tools": ["POSIX"], - } +# perform a full range of code checks on the listed files. +full_checks = { + "lib/Bcfg2/Server": ["Lint", + "Plugin", + "BuiltinCore.py", + "CherryPyCore.py", + "Core.py"], + "lib/Bcfg2/Server/Plugins": ["Bundler.py", + "Bzr.py", + "Cfg", + "Cvs.py", + "DBStats.py", + "Darcs.py", + "Defaults.py", + "FileProbes.py", + "Fossil.py", + "Git.py", + "GroupPatterns.py", + "Guppy.py", + "Hg.py", + "Ohai.py", + "Packages", + "Probes.py", + "Properties.py", + "PuppetENC.py", + "Rules.py", + "SEModules.py", + "ServiceCompat.py", + "Svn.py", + "Svn2.py", + "TemplateHelper.py", + "Trigger.py", + ], + "lib/Bcfg2/Client/Tools": ["POSIX"], + } + +# perform full code checks on the listed executables +sbin_checks = { + "sbin": ["bcfg2-server", "bcfg2-yum-helper"] + } + +# perform limited, django-safe checks on the listed files +django_checks = { + "lib/Bcfg2/Server": ["Reports", "models.py"] + } + +# perform no checks at all on the listed files +no_checks = { + "lib/Bcfg2/Client/Tools": ["APT.py", "RPMng.py", "rpmtools.py"], + "lib/Bcfg2/Server": ["Snapshots", "Hostbase"] + } + + +class TestPylint(Bcfg2TestCase): pylint_cmd = ["pylint", "--rcfile", rcfile] # regex to find errors and fatal errors error_re = re.compile(r':\d+:\s+\[[EF]\d{4}') - @skipIf(not os.path.exists(srcpath), "%s does not exist" % srcpath) - @skipIf(not os.path.exists(rcfile), "%s does not exist" % rcfile) - def test_pylint_full(self): + # build the blacklist + blacklist = [] + for parent, modules in no_checks.items(): + blacklist.extend([os.path.join(srcpath, parent, m) for m in modules]) + + def _get_paths(self, pathlist): paths = [] - for parent, modules in self.whitelist.items(): + for parent, modules in pathlist.items(): paths.extend([os.path.join(srcpath, parent, m) for m in modules]) - args = self.pylint_cmd + paths - try: - pylint = Popen(args, stdout=PIPE, stderr=STDOUT) - print(pylint.communicate()[0]) - rv = pylint.wait() - except OSError: - if can_skip: - return skip("pylint not found") - else: - print("pylint not found") - return - self.assertEqual(rv, 0) + return list(set(paths) - set(self.blacklist)) + + @skipIf(not os.path.exists(srcpath), "%s does not exist" % srcpath) + @skipIf(not os.path.exists(rcfile), "%s does not exist" % rcfile) + @skipUnless(HAS_PYLINT, "pylint not found, skipping") + def test_lib_full(self): + self._pylint_full(self._get_paths(full_checks)) + + @skipIf(not os.path.exists(srcpath), "%s does not exist" % srcpath) + @skipIf(not os.path.exists(rcfile), "%s does not exist" % rcfile) + @skipUnless(HAS_PYLINT, "pylint not found, skipping") + def test_sbin_full(self): + self._pylint_full(self._get_paths(sbin_checks), + extra_args=["--module-rgx", + "[a-z_-][a-z0-9_-]*$"]) + + def _pylint_full(self, paths, extra_args=None): + """ test select files for all pylint errors """ + if extra_args is None: + extra_args = [] + args = self.pylint_cmd + extra_args + \ + ["-f", "parseable"] + \ + [os.path.join(srcpath, p) for p in paths] + pylint = Popen(args, stdout=PIPE, stderr=STDOUT) + print(pylint.communicate()[0]) + self.assertEqual(pylint.wait(), 0) + @skipIf(not os.path.exists(srcpath), "%s does not exist" % srcpath) + @skipIf(not os.path.exists(rcfile), "%s does not exist" % rcfile) + @skipUnless(HAS_PYLINT, "pylint not found, skipping") def test_sbin_errors(self): - return self._pylint_errors(glob.glob("sbin/*")) + flist = list(set(os.path.join(srcpath, p) + for p in glob.glob("sbin/*")) - set(self.blacklist)) + return self._pylint_errors(flist) @skipUnless(HAS_DJANGO, "Django not found, skipping") + @skipIf(not os.path.exists(srcpath), "%s does not exist" % srcpath) + @skipIf(not os.path.exists(rcfile), "%s does not exist" % rcfile) + @skipUnless(HAS_PYLINT, "pylint not found, skipping") def test_django_errors(self): - return self._pylint_errors(["lib/Bcfg2/Server/Reports", - "lib/Bcfg2/Server/models.py"], + return self._pylint_errors(self._get_paths(django_checks), extra_args=["-d", "E1101"]) + @skipIf(not os.path.exists(srcpath), "%s does not exist" % srcpath) + @skipIf(not os.path.exists(rcfile), "%s does not exist" % rcfile) + @skipUnless(HAS_PYLINT, "pylint not found, skipping") def test_lib_errors(self): - # we ignore stuff that uses django (Reports, Hostbase, - # models.py) or that is deprecated and raises lots of errors - # (Snapshots, Hostbase), or that just raises a lot of errors - # (APT.py, RPMng.py, rpmtools.py). Reports is tested by - # test_django_errors - ignore = ["models.py", "APT.py", "RPMng.py", "rpmtools.py", - "Snapshots", "Reports", "Hostbase"] + ignore = [] + for fname_list in django_checks.values() + no_checks.values(): + ignore.extend(fname_list) return self._pylint_errors(["lib/Bcfg2"], extra_args=["--ignore", ",".join(ignore)]) - @skipIf(not os.path.exists(srcpath), "%s does not exist" % srcpath) - @skipIf(not os.path.exists(rcfile), "%s does not exist" % rcfile) def _pylint_errors(self, paths, extra_args=None): """ test all files for fatals and errors """ if extra_args is None: @@ -121,16 +165,9 @@ class TestPylint(Bcfg2TestCase): args = self.pylint_cmd + extra_args + \ ["-f", "parseable", "-d", "R0801,E1103"] + \ [os.path.join(srcpath, p) for p in paths] - try: - pylint = Popen(args, stdout=PIPE, stderr=STDOUT) - output = pylint.communicate()[0] - rv = pylint.wait() - except OSError: - if can_skip: - return skip("pylint not found") - else: - print("pylint not found") - return + pylint = Popen(args, stdout=PIPE, stderr=STDOUT) + output = pylint.communicate()[0] + rv = pylint.wait() for line in output.splitlines(): #print line diff --git a/testsuite/pylintrc.conf b/testsuite/pylintrc.conf index 014a94de5..2c56a3e81 100644 --- a/testsuite/pylintrc.conf +++ b/testsuite/pylintrc.conf @@ -169,14 +169,14 @@ variable-rgx=[a-z_][a-z0-9_]{2,30}$ inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ # Good variable names which should always be accepted, separated by a comma -good-names=_,rv,el,fd,ca +good-names=_,rv,el,fd,ca,re # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata # Regular expression which should only match functions or classes name which do # not require a docstring -no-docstring-rgx=__.*__ +no-docstring-rgx=__.*__|main [IMPORTS] -- cgit v1.2.3-1-g7c22