summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Bundler.py96
-rw-r--r--src/lib/Bcfg2/Server/Plugins/GroupPatterns.py51
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Ohai.py18
-rwxr-xr-xsrc/sbin/bcfg2-server9
-rwxr-xr-xsrc/sbin/bcfg2-yum-helper34
-rw-r--r--testsuite/Testsrc/testmisc.py187
-rw-r--r--testsuite/pylintrc.conf4
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<name>.*)\.(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
- # <directory> => <file globs within that directory>. <directory>
- # 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]