diff options
Diffstat (limited to 'pylint/checkers/logging.py')
-rw-r--r-- | pylint/checkers/logging.py | 45 |
1 files changed, 31 insertions, 14 deletions
diff --git a/pylint/checkers/logging.py b/pylint/checkers/logging.py index 486c52dbd..be031a931 100644 --- a/pylint/checkers/logging.py +++ b/pylint/checkers/logging.py @@ -7,6 +7,7 @@ """checker for use of Python logging """ +import string import six @@ -56,15 +57,15 @@ MSGS = { } -CHECKED_CONVENIENCE_FUNCTIONS = set([ - 'critical', 'debug', 'error', 'exception', 'fatal', 'info', 'warn', - 'warning']) +CHECKED_CONVENIENCE_FUNCTIONS = {'critical', 'debug', 'error', 'exception', + 'fatal', 'info', 'warn', 'warning'} -def is_method_call(callfunc_node, types=(), methods=()): - """Determines if a CallFunc node represents a method call. + +def is_method_call(func, types=(), methods=()): + """Determines if a BoundMethod node represents a method call. Args: - callfunc_node (astroid.CallFunc): The CallFunc AST node to check. + func (astroid.BoundMethod): The BoundMethod AST node to check. types (Optional[String]): Optional sequence of caller type names to restrict check. methods (Optional[String]): Optional sequence of method names to restrict check. @@ -72,16 +73,12 @@ def is_method_call(callfunc_node, types=(), methods=()): bool: true if the node represents a method call for the given type and method names, False otherwise. """ - if not isinstance(callfunc_node, astroid.Call): - return False - func = utils.safe_infer(callfunc_node.func) return (isinstance(func, astroid.BoundMethod) and isinstance(func.bound, astroid.Instance) and (func.bound.name in types if types else True) and (func.name in methods if methods else True)) - class LoggingChecker(checkers.BaseChecker): """Checks use of the logging module.""" @@ -183,15 +180,18 @@ class LoggingChecker(checkers.BaseChecker): elif isinstance(node.args[format_pos], astroid.Const): self._check_format_string(node, format_pos) - def _check_call_func(self, callfunc_node): + def _check_call_func(self, node): """Checks that function call is not format_string.format(). Args: - callfunc_node (astroid.node_classes.NodeNG): + node (astroid.node_classes.CallFunc): CallFunc AST node to be checked. """ - if is_method_call(callfunc_node, ('str', 'unicode'), ('format',)): - self.add_message('logging-format-interpolation', node=callfunc_node) + func = utils.safe_infer(node.func) + types = ('str', 'unicode') + methods = ('format',) + if is_method_call(func, types, methods) and not is_complex_format_str(func.bound): + self.add_message('logging-format-interpolation', node=node) def _check_format_string(self, node, format_arg): """Checks that format string tokens match the supplied arguments. @@ -232,6 +232,23 @@ class LoggingChecker(checkers.BaseChecker): self.add_message('logging-too-few-args', node=node) +def is_complex_format_str(node): + """ Checks if node represents a string with complex formatting specs. + + Args: + node (astroid.node_classes.NodeNG): AST node to check + Returns: + bool: True if inferred string uses complex formatting, False otherwise + """ + inferred = utils.safe_infer(node) + if inferred is None or not isinstance(inferred.value, six.string_types): + return True + for _, _, format_spec, _ in string.Formatter().parse(inferred.value): + if format_spec: + return True + return False + + def _count_supplied_tokens(args): """Counts the number of tokens in an args list. |