* [gentoo-commits] proj/portage:repoman commit in: repoman/pym/repoman/modules/linechecks/
@ 2017-07-15 2:08 Brian Dolbec
0 siblings, 0 replies; 2+ messages in thread
From: Brian Dolbec @ 2017-07-15 2:08 UTC (permalink / raw
To: gentoo-commits
commit: c51d1a7b8b3daf6db294913c3f8a7f14aeeb0d32
Author: Brian Dolbec <dolsen <AT> gentoo <DOT> org>
AuthorDate: Sat Jul 15 00:15:22 2017 +0000
Commit: Brian Dolbec <dolsen <AT> gentoo <DOT> org>
CommitDate: Sat Jul 15 02:08:27 2017 +0000
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=c51d1a7b
repoman: Initial creation of a new linechecks sub module plugin system
This new module system will be for splitting the multicheck module
checks into a fully configurable, plugable system.
repoman/pym/repoman/modules/linechecks/__init__.py | 1 +
repoman/pym/repoman/modules/linechecks/base.py | 101 +++++++++++++++
repoman/pym/repoman/modules/linechecks/config.py | 94 ++++++++++++++
.../pym/repoman/modules/linechecks/controller.py | 140 +++++++++++++++++++++
4 files changed, 336 insertions(+)
diff --git a/repoman/pym/repoman/modules/linechecks/__init__.py b/repoman/pym/repoman/modules/linechecks/__init__.py
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/repoman/pym/repoman/modules/linechecks/__init__.py
@@ -0,0 +1 @@
+
diff --git a/repoman/pym/repoman/modules/linechecks/base.py b/repoman/pym/repoman/modules/linechecks/base.py
new file mode 100644
index 000000000..4e3d6f0b4
--- /dev/null
+++ b/repoman/pym/repoman/modules/linechecks/base.py
@@ -0,0 +1,101 @@
+
+import logging
+import re
+
+
+class LineCheck(object):
+ """Run a check on a line of an ebuild."""
+ """A regular expression to determine whether to ignore the line"""
+ ignore_line = False
+ """True if lines containing nothing more than comments with optional
+ leading whitespace should be ignored"""
+ ignore_comment = True
+
+ def __init__(self, errors):
+ self.errors = errors
+
+ def new(self, pkg):
+ pass
+
+ def check_eapi(self, eapi):
+ """Returns if check should be run in the given EAPI (default: True)"""
+ return True
+
+ def check(self, num, line):
+ """Run the check on line and return error if there is one"""
+ if self.re.match(line):
+ return self.errors[self.error]
+
+ def end(self):
+ pass
+
+
+class InheritEclass(LineCheck):
+ """
+ Base class for checking for missing inherits, as well as excess inherits.
+
+ Args:
+ eclass: Set to the name of your eclass.
+ funcs: A tuple of functions that this eclass provides.
+ comprehensive: Is the list of functions complete?
+ exempt_eclasses: If these eclasses are inherited, disable the missing
+ inherit check.
+ """
+
+ def __init__(
+ self, eclass, eclass_eapi_functions, errors, funcs=None, comprehensive=False,
+ exempt_eclasses=None, ignore_missing=False, **kwargs):
+ self._eclass = eclass
+ self._comprehensive = comprehensive
+ self._exempt_eclasses = exempt_eclasses
+ self._ignore_missing = ignore_missing
+ self.errors = errors
+ inherit_re = eclass
+ self._eclass_eapi_functions = eclass_eapi_functions
+ self._inherit_re = re.compile(
+ r'^(\s*|.*[|&]\s*)\binherit\s(.*\s)?%s(\s|$)' % inherit_re)
+ # Match when the function is preceded only by leading whitespace, a
+ # shell operator such as (, {, |, ||, or &&, or optional variable
+ # setting(s). This prevents false positives in things like elog
+ # messages, as reported in bug #413285.
+ logging.debug("InheritEclass, eclass: %s, funcs: %s", eclass, funcs)
+ self._func_re = re.compile(
+ r'(^|[|&{(])\s*(\w+=.*)?\b(' + r'|'.join(funcs) + r')\b')
+
+ def new(self, pkg):
+ self.repoman_check_name = 'inherit.missing'
+ # We can't use pkg.inherited because that tells us all the eclasses that
+ # have been inherited and not just the ones we inherit directly.
+ self._inherit = False
+ self._func_call = False
+ if self._exempt_eclasses is not None:
+ inherited = pkg.inherited
+ self._disabled = any(x in inherited for x in self._exempt_eclasses)
+ else:
+ self._disabled = False
+ self._eapi = pkg.eapi
+
+ def check(self, num, line):
+ if not self._inherit:
+ self._inherit = self._inherit_re.match(line)
+ if not self._inherit:
+ if self._disabled or self._ignore_missing:
+ return
+ s = self._func_re.search(line)
+ if s is not None:
+ func_name = s.group(3)
+ eapi_func = self._eclass_eapi_functions.get(func_name)
+ if eapi_func is None or not eapi_func(self._eapi):
+ self._func_call = True
+ return (
+ '%s.eclass is not inherited, '
+ 'but "%s" found at line: %s' %
+ (self._eclass, func_name, '%d'))
+ elif not self._func_call:
+ self._func_call = self._func_re.search(line)
+
+ def end(self):
+ if not self._disabled and self._comprehensive and self._inherit \
+ and not self._func_call:
+ self.repoman_check_name = 'inherit.unused'
+ yield 'no function called from %s.eclass; please drop' % self._eclass
diff --git a/repoman/pym/repoman/modules/linechecks/config.py b/repoman/pym/repoman/modules/linechecks/config.py
new file mode 100644
index 000000000..0044afe79
--- /dev/null
+++ b/repoman/pym/repoman/modules/linechecks/config.py
@@ -0,0 +1,94 @@
+# -*- coding:utf-8 -*-
+# repoman: Checks
+# Copyright 2007-2017 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+"""This module contains functions used in Repoman to ascertain the quality
+and correctness of an ebuild."""
+
+from __future__ import unicode_literals
+
+import collections
+import logging
+import os
+import yaml
+from copy import deepcopy
+
+from portage.util import stack_lists
+from repoman.config import load_config
+
+
+def merge(dict1, dict2):
+ ''' Return a new dictionary by merging two dictionaries recursively. '''
+
+ result = deepcopy(dict1)
+
+ for key, value in dict2.items():
+ if isinstance(value, collections.Mapping):
+ result[key] = merge(result.get(key, {}), value)
+ else:
+ result[key] = deepcopy(dict2[key])
+
+ return result
+
+
+class LineChecksConfig(object):
+ '''Holds our LineChecks configuration data and operation functions'''
+
+ def __init__(self, repo_settings):
+ '''Class init
+
+ @param repo_settings: RepoSettings instance
+ @param configpaths: ordered list of filepaths to load
+ '''
+ self.repo_settings = repo_settings
+ self.infopaths = [os.path.join(path, 'linechecks.yaml') for path in self.repo_settings.masters_list]
+ logging.debug("LineChecksConfig; configpaths: %s", self.infopaths)
+ self.info_config = None
+ self._config = None
+ self.usex_supported_eapis = None
+ self.in_iuse_supported_eapis = None
+ self.get_libdir_supported_eapis = None
+ self.eclass_eapi_functions = {}
+ self.eclass_export_functions = None
+ self.eclass_info = {}
+ self.eclass_info_experimental_inherit = {}
+ self.errors = {}
+ self.load_checks_info()
+
+
+ def load_checks_info(self, infopaths=None):
+ '''load the config files in order
+
+ @param configpaths: ordered list of filepaths to load
+ '''
+ if infopaths:
+ self.infopaths = infopaths
+ elif not self.infopaths:
+ logging.error("LineChecksConfig; Error: No linechecks.yaml files defined")
+ configs = load_config(self.infopaths, 'yaml')
+ if configs == {}:
+ logging.error("LineChecksConfig: Failed to load a valid 'linechecks.yaml' file at paths: %s", self.infopaths)
+ return False
+ logging.debug("LineChecksConfig: linechecks.yaml configs: %s", configs)
+ self.info_config = configs
+
+ self.errors = self.info_config['errors']
+ self.usex_supported_eapis = self.info_config['usex_supported_eapis']
+ self.in_iuse_supported_eapis = self.info_config['in_iuse_supported_eapis']
+ self.eclass_info_experimental_inherit = self.info_config['eclass_info_experimental_inherit']
+ self.get_libdir_supported_eapis = self.in_iuse_supported_eapis
+ self.eclass_eapi_functions = {
+ "usex": lambda eapi: eapi not in self.usex_supported_eapis,
+ "in_iuse": lambda eapi: eapi not in self.in_iuse_supported_eapis,
+ "get_libdir": lambda eapi: eapi not in self.get_libdir_supported_eapis,
+ }
+
+ # eclasses that export ${ECLASS}_src_(compile|configure|install)
+ self.eclass_export_functions = self.info_config['eclass_export_functions']
+
+ self.eclass_info_experimental_inherit = self.info_config['eclass_info_experimental_inherit']
+ # These are "eclasses are the whole ebuild" type thing.
+ self.eclass_info_experimental_inherit['eutils']['exempt_eclasses'] = self.eclass_export_functions
+ self.eclass_info_experimental_inherit['multilib']['exempt_eclasses'] = self.eclass_export_functions + [
+ 'autotools', 'libtool', 'multilib-minimal']
diff --git a/repoman/pym/repoman/modules/linechecks/controller.py b/repoman/pym/repoman/modules/linechecks/controller.py
new file mode 100644
index 000000000..5c7f5c924
--- /dev/null
+++ b/repoman/pym/repoman/modules/linechecks/controller.py
@@ -0,0 +1,140 @@
+
+import logging
+import operator
+import os
+import re
+
+from portage.module import Modules
+from repoman.modules.linechecks.base import InheritEclass
+from repoman.modules.linechecks.config import LineChecksConfig
+
+MODULES_PATH = os.path.dirname(__file__)
+# initial development debug info
+logging.debug("LineChecks module path: %s", MODULES_PATH)
+
+
+class LineCheckController(object):
+ '''Initializes and runs the LineCheck checks'''
+
+ def __init__(self, repo_settings, linechecks):
+ '''Class init
+
+ @param repo_settings: RepoSettings instance
+ '''
+ self.repo_settings = repo_settings
+ self.linechecks = linechecks
+ self.config = LineChecksConfig(repo_settings)
+
+ self.controller = Modules(path=MODULES_PATH, namepath="repoman.modules.linechecks")
+ logging.debug("LineCheckController; module_names: %s", self.controller.module_names)
+
+ self._constant_checks = None
+
+ self._here_doc_re = re.compile(r'.*<<[-]?(\w+)\s*(>\s*\S+\s*)?$')
+ self._ignore_comment_re = re.compile(r'^\s*#')
+ self._continuation_re = re.compile(r'(\\)*$')
+
+ def checks_init(self, experimental_inherit=False):
+ '''Initialize the main variables
+
+ @param experimental_inherit boolean
+ '''
+ if not experimental_inherit:
+ # Emulate the old eprefixify.defined and inherit.autotools checks.
+ self._eclass_info = self.config.eclass_info
+ else:
+ self._eclass_info = self.config.eclass_info_experimental_inherit
+
+ self._constant_checks = []
+ logging.debug("LineCheckController; modules: %s", self.linechecks)
+ # Add in the pluggable modules
+ for mod in self.linechecks:
+ mod_class = self.controller.get_class(mod)
+ logging.debug("LineCheckController; module_name: %s, class: %s", mod, mod_class.__name__)
+ self._constant_checks.append(mod_class(self.config.errors))
+ # Add in the InheritEclass checks
+ logging.debug("LineCheckController; eclass_info.items(): %s", list(self.config.eclass_info))
+ for k, kwargs in self.config.eclass_info.items():
+ logging.debug("LineCheckController; k: %s, kwargs: %s", k, kwargs)
+ self._constant_checks.append(
+ InheritEclass(
+ k,
+ self.config.eclass_eapi_functions,
+ self.config.errors,
+ **kwargs
+ )
+ )
+
+
+ def run_checks(self, contents, pkg):
+ '''Run the configured linechecks
+
+ @param contents: the ebjuild contents to check
+ @param pkg: the package being checked
+ '''
+ if self._constant_checks is None:
+ self.checks_init()
+ checks = self._constant_checks
+ here_doc_delim = None
+ multiline = None
+
+ for lc in checks:
+ lc.new(pkg)
+
+ multinum = 0
+ for num, line in enumerate(contents):
+
+ # Check if we're inside a here-document.
+ if here_doc_delim is not None:
+ if here_doc_delim.match(line):
+ here_doc_delim = None
+ if here_doc_delim is None:
+ here_doc = self._here_doc_re.match(line)
+ if here_doc is not None:
+ here_doc_delim = re.compile(r'^\s*%s$' % here_doc.group(1))
+ if here_doc_delim is not None:
+ continue
+
+ # Unroll multiline escaped strings so that we can check things:
+ # inherit foo bar \
+ # moo \
+ # cow
+ # This will merge these lines like so:
+ # inherit foo bar moo cow
+ # A line ending with an even number of backslashes does not count,
+ # because the last backslash is escaped. Therefore, search for an
+ # odd number of backslashes.
+ line_escaped = operator.sub(*self._continuation_re.search(line).span()) % 2 == 1
+ if multiline:
+ # Chop off the \ and \n bytes from the previous line.
+ multiline = multiline[:-2] + line
+ if not line_escaped:
+ line = multiline
+ num = multinum
+ multiline = None
+ else:
+ continue
+ else:
+ if line_escaped:
+ multinum = num
+ multiline = line
+ continue
+
+ if not line.endswith("#nowarn\n"):
+ # Finally we have a full line to parse.
+ is_comment = self._ignore_comment_re.match(line) is not None
+ for lc in checks:
+ if is_comment and lc.ignore_comment:
+ continue
+ if lc.check_eapi(pkg.eapi):
+ ignore = lc.ignore_line
+ if not ignore or not ignore.match(line):
+ e = lc.check(num, line)
+ if e:
+ yield lc.repoman_check_name, e % (num + 1)
+
+ for lc in checks:
+ i = lc.end()
+ if i is not None:
+ for e in i:
+ yield lc.repoman_check_name, e
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [gentoo-commits] proj/portage:repoman commit in: repoman/pym/repoman/modules/linechecks/
@ 2017-07-15 2:29 Brian Dolbec
0 siblings, 0 replies; 2+ messages in thread
From: Brian Dolbec @ 2017-07-15 2:29 UTC (permalink / raw
To: gentoo-commits
commit: b6e8cfe29d860d018bb386cdfbbcde6cb2edaadc
Author: Brian Dolbec <dolsen <AT> gentoo <DOT> org>
AuthorDate: Sat Jul 15 00:15:22 2017 +0000
Commit: Brian Dolbec <dolsen <AT> gentoo <DOT> org>
CommitDate: Sat Jul 15 02:25:44 2017 +0000
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=b6e8cfe2
repoman: Initial creation of a new linechecks sub module plugin system
This new module system will be for splitting the multicheck module
checks into a fully configurable, plugable system.
repoman/pym/repoman/modules/linechecks/__init__.py | 1 +
repoman/pym/repoman/modules/linechecks/base.py | 101 +++++++++++++++
repoman/pym/repoman/modules/linechecks/config.py | 94 ++++++++++++++
.../pym/repoman/modules/linechecks/controller.py | 140 +++++++++++++++++++++
4 files changed, 336 insertions(+)
diff --git a/repoman/pym/repoman/modules/linechecks/__init__.py b/repoman/pym/repoman/modules/linechecks/__init__.py
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/repoman/pym/repoman/modules/linechecks/__init__.py
@@ -0,0 +1 @@
+
diff --git a/repoman/pym/repoman/modules/linechecks/base.py b/repoman/pym/repoman/modules/linechecks/base.py
new file mode 100644
index 000000000..4e3d6f0b4
--- /dev/null
+++ b/repoman/pym/repoman/modules/linechecks/base.py
@@ -0,0 +1,101 @@
+
+import logging
+import re
+
+
+class LineCheck(object):
+ """Run a check on a line of an ebuild."""
+ """A regular expression to determine whether to ignore the line"""
+ ignore_line = False
+ """True if lines containing nothing more than comments with optional
+ leading whitespace should be ignored"""
+ ignore_comment = True
+
+ def __init__(self, errors):
+ self.errors = errors
+
+ def new(self, pkg):
+ pass
+
+ def check_eapi(self, eapi):
+ """Returns if check should be run in the given EAPI (default: True)"""
+ return True
+
+ def check(self, num, line):
+ """Run the check on line and return error if there is one"""
+ if self.re.match(line):
+ return self.errors[self.error]
+
+ def end(self):
+ pass
+
+
+class InheritEclass(LineCheck):
+ """
+ Base class for checking for missing inherits, as well as excess inherits.
+
+ Args:
+ eclass: Set to the name of your eclass.
+ funcs: A tuple of functions that this eclass provides.
+ comprehensive: Is the list of functions complete?
+ exempt_eclasses: If these eclasses are inherited, disable the missing
+ inherit check.
+ """
+
+ def __init__(
+ self, eclass, eclass_eapi_functions, errors, funcs=None, comprehensive=False,
+ exempt_eclasses=None, ignore_missing=False, **kwargs):
+ self._eclass = eclass
+ self._comprehensive = comprehensive
+ self._exempt_eclasses = exempt_eclasses
+ self._ignore_missing = ignore_missing
+ self.errors = errors
+ inherit_re = eclass
+ self._eclass_eapi_functions = eclass_eapi_functions
+ self._inherit_re = re.compile(
+ r'^(\s*|.*[|&]\s*)\binherit\s(.*\s)?%s(\s|$)' % inherit_re)
+ # Match when the function is preceded only by leading whitespace, a
+ # shell operator such as (, {, |, ||, or &&, or optional variable
+ # setting(s). This prevents false positives in things like elog
+ # messages, as reported in bug #413285.
+ logging.debug("InheritEclass, eclass: %s, funcs: %s", eclass, funcs)
+ self._func_re = re.compile(
+ r'(^|[|&{(])\s*(\w+=.*)?\b(' + r'|'.join(funcs) + r')\b')
+
+ def new(self, pkg):
+ self.repoman_check_name = 'inherit.missing'
+ # We can't use pkg.inherited because that tells us all the eclasses that
+ # have been inherited and not just the ones we inherit directly.
+ self._inherit = False
+ self._func_call = False
+ if self._exempt_eclasses is not None:
+ inherited = pkg.inherited
+ self._disabled = any(x in inherited for x in self._exempt_eclasses)
+ else:
+ self._disabled = False
+ self._eapi = pkg.eapi
+
+ def check(self, num, line):
+ if not self._inherit:
+ self._inherit = self._inherit_re.match(line)
+ if not self._inherit:
+ if self._disabled or self._ignore_missing:
+ return
+ s = self._func_re.search(line)
+ if s is not None:
+ func_name = s.group(3)
+ eapi_func = self._eclass_eapi_functions.get(func_name)
+ if eapi_func is None or not eapi_func(self._eapi):
+ self._func_call = True
+ return (
+ '%s.eclass is not inherited, '
+ 'but "%s" found at line: %s' %
+ (self._eclass, func_name, '%d'))
+ elif not self._func_call:
+ self._func_call = self._func_re.search(line)
+
+ def end(self):
+ if not self._disabled and self._comprehensive and self._inherit \
+ and not self._func_call:
+ self.repoman_check_name = 'inherit.unused'
+ yield 'no function called from %s.eclass; please drop' % self._eclass
diff --git a/repoman/pym/repoman/modules/linechecks/config.py b/repoman/pym/repoman/modules/linechecks/config.py
new file mode 100644
index 000000000..0044afe79
--- /dev/null
+++ b/repoman/pym/repoman/modules/linechecks/config.py
@@ -0,0 +1,94 @@
+# -*- coding:utf-8 -*-
+# repoman: Checks
+# Copyright 2007-2017 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+"""This module contains functions used in Repoman to ascertain the quality
+and correctness of an ebuild."""
+
+from __future__ import unicode_literals
+
+import collections
+import logging
+import os
+import yaml
+from copy import deepcopy
+
+from portage.util import stack_lists
+from repoman.config import load_config
+
+
+def merge(dict1, dict2):
+ ''' Return a new dictionary by merging two dictionaries recursively. '''
+
+ result = deepcopy(dict1)
+
+ for key, value in dict2.items():
+ if isinstance(value, collections.Mapping):
+ result[key] = merge(result.get(key, {}), value)
+ else:
+ result[key] = deepcopy(dict2[key])
+
+ return result
+
+
+class LineChecksConfig(object):
+ '''Holds our LineChecks configuration data and operation functions'''
+
+ def __init__(self, repo_settings):
+ '''Class init
+
+ @param repo_settings: RepoSettings instance
+ @param configpaths: ordered list of filepaths to load
+ '''
+ self.repo_settings = repo_settings
+ self.infopaths = [os.path.join(path, 'linechecks.yaml') for path in self.repo_settings.masters_list]
+ logging.debug("LineChecksConfig; configpaths: %s", self.infopaths)
+ self.info_config = None
+ self._config = None
+ self.usex_supported_eapis = None
+ self.in_iuse_supported_eapis = None
+ self.get_libdir_supported_eapis = None
+ self.eclass_eapi_functions = {}
+ self.eclass_export_functions = None
+ self.eclass_info = {}
+ self.eclass_info_experimental_inherit = {}
+ self.errors = {}
+ self.load_checks_info()
+
+
+ def load_checks_info(self, infopaths=None):
+ '''load the config files in order
+
+ @param configpaths: ordered list of filepaths to load
+ '''
+ if infopaths:
+ self.infopaths = infopaths
+ elif not self.infopaths:
+ logging.error("LineChecksConfig; Error: No linechecks.yaml files defined")
+ configs = load_config(self.infopaths, 'yaml')
+ if configs == {}:
+ logging.error("LineChecksConfig: Failed to load a valid 'linechecks.yaml' file at paths: %s", self.infopaths)
+ return False
+ logging.debug("LineChecksConfig: linechecks.yaml configs: %s", configs)
+ self.info_config = configs
+
+ self.errors = self.info_config['errors']
+ self.usex_supported_eapis = self.info_config['usex_supported_eapis']
+ self.in_iuse_supported_eapis = self.info_config['in_iuse_supported_eapis']
+ self.eclass_info_experimental_inherit = self.info_config['eclass_info_experimental_inherit']
+ self.get_libdir_supported_eapis = self.in_iuse_supported_eapis
+ self.eclass_eapi_functions = {
+ "usex": lambda eapi: eapi not in self.usex_supported_eapis,
+ "in_iuse": lambda eapi: eapi not in self.in_iuse_supported_eapis,
+ "get_libdir": lambda eapi: eapi not in self.get_libdir_supported_eapis,
+ }
+
+ # eclasses that export ${ECLASS}_src_(compile|configure|install)
+ self.eclass_export_functions = self.info_config['eclass_export_functions']
+
+ self.eclass_info_experimental_inherit = self.info_config['eclass_info_experimental_inherit']
+ # These are "eclasses are the whole ebuild" type thing.
+ self.eclass_info_experimental_inherit['eutils']['exempt_eclasses'] = self.eclass_export_functions
+ self.eclass_info_experimental_inherit['multilib']['exempt_eclasses'] = self.eclass_export_functions + [
+ 'autotools', 'libtool', 'multilib-minimal']
diff --git a/repoman/pym/repoman/modules/linechecks/controller.py b/repoman/pym/repoman/modules/linechecks/controller.py
new file mode 100644
index 000000000..5c7f5c924
--- /dev/null
+++ b/repoman/pym/repoman/modules/linechecks/controller.py
@@ -0,0 +1,140 @@
+
+import logging
+import operator
+import os
+import re
+
+from portage.module import Modules
+from repoman.modules.linechecks.base import InheritEclass
+from repoman.modules.linechecks.config import LineChecksConfig
+
+MODULES_PATH = os.path.dirname(__file__)
+# initial development debug info
+logging.debug("LineChecks module path: %s", MODULES_PATH)
+
+
+class LineCheckController(object):
+ '''Initializes and runs the LineCheck checks'''
+
+ def __init__(self, repo_settings, linechecks):
+ '''Class init
+
+ @param repo_settings: RepoSettings instance
+ '''
+ self.repo_settings = repo_settings
+ self.linechecks = linechecks
+ self.config = LineChecksConfig(repo_settings)
+
+ self.controller = Modules(path=MODULES_PATH, namepath="repoman.modules.linechecks")
+ logging.debug("LineCheckController; module_names: %s", self.controller.module_names)
+
+ self._constant_checks = None
+
+ self._here_doc_re = re.compile(r'.*<<[-]?(\w+)\s*(>\s*\S+\s*)?$')
+ self._ignore_comment_re = re.compile(r'^\s*#')
+ self._continuation_re = re.compile(r'(\\)*$')
+
+ def checks_init(self, experimental_inherit=False):
+ '''Initialize the main variables
+
+ @param experimental_inherit boolean
+ '''
+ if not experimental_inherit:
+ # Emulate the old eprefixify.defined and inherit.autotools checks.
+ self._eclass_info = self.config.eclass_info
+ else:
+ self._eclass_info = self.config.eclass_info_experimental_inherit
+
+ self._constant_checks = []
+ logging.debug("LineCheckController; modules: %s", self.linechecks)
+ # Add in the pluggable modules
+ for mod in self.linechecks:
+ mod_class = self.controller.get_class(mod)
+ logging.debug("LineCheckController; module_name: %s, class: %s", mod, mod_class.__name__)
+ self._constant_checks.append(mod_class(self.config.errors))
+ # Add in the InheritEclass checks
+ logging.debug("LineCheckController; eclass_info.items(): %s", list(self.config.eclass_info))
+ for k, kwargs in self.config.eclass_info.items():
+ logging.debug("LineCheckController; k: %s, kwargs: %s", k, kwargs)
+ self._constant_checks.append(
+ InheritEclass(
+ k,
+ self.config.eclass_eapi_functions,
+ self.config.errors,
+ **kwargs
+ )
+ )
+
+
+ def run_checks(self, contents, pkg):
+ '''Run the configured linechecks
+
+ @param contents: the ebjuild contents to check
+ @param pkg: the package being checked
+ '''
+ if self._constant_checks is None:
+ self.checks_init()
+ checks = self._constant_checks
+ here_doc_delim = None
+ multiline = None
+
+ for lc in checks:
+ lc.new(pkg)
+
+ multinum = 0
+ for num, line in enumerate(contents):
+
+ # Check if we're inside a here-document.
+ if here_doc_delim is not None:
+ if here_doc_delim.match(line):
+ here_doc_delim = None
+ if here_doc_delim is None:
+ here_doc = self._here_doc_re.match(line)
+ if here_doc is not None:
+ here_doc_delim = re.compile(r'^\s*%s$' % here_doc.group(1))
+ if here_doc_delim is not None:
+ continue
+
+ # Unroll multiline escaped strings so that we can check things:
+ # inherit foo bar \
+ # moo \
+ # cow
+ # This will merge these lines like so:
+ # inherit foo bar moo cow
+ # A line ending with an even number of backslashes does not count,
+ # because the last backslash is escaped. Therefore, search for an
+ # odd number of backslashes.
+ line_escaped = operator.sub(*self._continuation_re.search(line).span()) % 2 == 1
+ if multiline:
+ # Chop off the \ and \n bytes from the previous line.
+ multiline = multiline[:-2] + line
+ if not line_escaped:
+ line = multiline
+ num = multinum
+ multiline = None
+ else:
+ continue
+ else:
+ if line_escaped:
+ multinum = num
+ multiline = line
+ continue
+
+ if not line.endswith("#nowarn\n"):
+ # Finally we have a full line to parse.
+ is_comment = self._ignore_comment_re.match(line) is not None
+ for lc in checks:
+ if is_comment and lc.ignore_comment:
+ continue
+ if lc.check_eapi(pkg.eapi):
+ ignore = lc.ignore_line
+ if not ignore or not ignore.match(line):
+ e = lc.check(num, line)
+ if e:
+ yield lc.repoman_check_name, e % (num + 1)
+
+ for lc in checks:
+ i = lc.end()
+ if i is not None:
+ for e in i:
+ yield lc.repoman_check_name, e
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2017-07-15 2:29 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-07-15 2:29 [gentoo-commits] proj/portage:repoman commit in: repoman/pym/repoman/modules/linechecks/ Brian Dolbec
-- strict thread matches above, loose matches on Subject: below --
2017-07-15 2:08 Brian Dolbec
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox