From 775867626df1a5a2b36dd73c7e27f3a679b4a427 Mon Sep 17 00:00:00 2001 From: Ben Webber Date: Thu, 30 Nov 2017 18:27:16 +0000 Subject: [PATCH 1/2] check-builtin-literals: Ignore function attribute calls --- README.md | 1 + pre_commit_hooks/check_builtin_literals.py | 5 +++++ testing/resources/builtin_constructors.py | 11 +++++++++++ tests/check_builtin_literals_test.py | 9 ++++++++- 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ff596ac..d717137 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Add this to your `.pre-commit-config.yaml` - `check-ast` - Simply check whether files parse as valid python. - `check-builtin-literals` - Require literal syntax when initializing empty or zero Python builtin types. - Allows calling constructors with positional arguments (e.g., `list('abc')`). + - Allows calling constructors from the `builtins` (`__builtin__`) namespace (`builtins.list()`). - Ignore this requirement for specific builtin types with `--ignore=type1,type2,…`. - Forbid `dict` keyword syntax with `--no-allow-dict-kwargs`. - `check-byte-order-marker` - Forbid files which have a UTF-8 byte-order marker diff --git a/pre_commit_hooks/check_builtin_literals.py b/pre_commit_hooks/check_builtin_literals.py index 1213288..c4ac969 100644 --- a/pre_commit_hooks/check_builtin_literals.py +++ b/pre_commit_hooks/check_builtin_literals.py @@ -30,6 +30,11 @@ class BuiltinTypeVisitor(ast.NodeVisitor): return self.allow_dict_kwargs and (getattr(node, 'kwargs', None) or getattr(node, 'keywords', None)) def visit_Call(self, node): + if isinstance(node.func, ast.Attribute): + # Ignore functions that are object attributes (`foo.bar()`). + # Assume that if the user calls `builtins.list()`, they know what + # they're doing. + return if node.func.id not in set(BUILTIN_TYPES).difference(self.ignore): return if node.func.id == 'dict' and self._check_dict_call(node): diff --git a/testing/resources/builtin_constructors.py b/testing/resources/builtin_constructors.py index 3fab056..720bd2b 100644 --- a/testing/resources/builtin_constructors.py +++ b/testing/resources/builtin_constructors.py @@ -1,3 +1,6 @@ +# flake8 checks imports; `builtins` does not exist in Python 2. +# flake8: noqa + c1 = complex() d1 = dict() f1 = float() @@ -5,3 +8,11 @@ i1 = int() l1 = list() s1 = str() t1 = tuple() + +c2 = builtins.complex() +d2 = builtins.dict() +f2 = builtins.float() +i2 = builtins.int() +l2 = builtins.list() +s2 = builtins.str() +t2 = builtins.tuple() diff --git a/tests/check_builtin_literals_test.py b/tests/check_builtin_literals_test.py index a38e522..13f896a 100644 --- a/tests/check_builtin_literals_test.py +++ b/tests/check_builtin_literals_test.py @@ -21,31 +21,36 @@ def visitor(): ("complex()", [BuiltinTypeCall('complex', 1, 0)]), ("complex(0, 0)", []), ("complex('0+0j')", []), + ('builtins.complex()', []), # float ("0.0", []), ("float()", [BuiltinTypeCall('float', 1, 0)]), ("float('0.0')", []), + ('builtins.float()', []), # int ("0", []), ("int()", [BuiltinTypeCall('int', 1, 0)]), ("int('0')", []), + ('builtins.int()', []), # list ("[]", []), ("list()", [BuiltinTypeCall('list', 1, 0)]), ("list('abc')", []), ("list([c for c in 'abc'])", []), ("list(c for c in 'abc')", []), + ('builtins.list()', []), # str ("''", []), ("str()", [BuiltinTypeCall('str', 1, 0)]), ("str('0')", []), - ("[]", []), + ('builtins.str()', []), # tuple ("()", []), ("tuple()", [BuiltinTypeCall('tuple', 1, 0)]), ("tuple('abc')", []), ("tuple([c for c in 'abc'])", []), ("tuple(c for c in 'abc')", []), + ('builtins.tuple()', []), ], ) def test_non_dict_exprs(visitor, expression, calls): @@ -62,6 +67,7 @@ def test_non_dict_exprs(visitor, expression, calls): ("dict(**{'a': 1, 'b': 2, 'c': 3})", []), ("dict([(k, v) for k, v in [('a', 1), ('b', 2), ('c', 3)]])", []), ("dict((k, v) for k, v in [('a', 1), ('b', 2), ('c', 3)])", []), + ('builtins.dict()', []), ], ) def test_dict_allow_kwargs_exprs(visitor, expression, calls): @@ -75,6 +81,7 @@ def test_dict_allow_kwargs_exprs(visitor, expression, calls): ("dict()", [BuiltinTypeCall('dict', 1, 0)]), ("dict(a=1, b=2, c=3)", [BuiltinTypeCall('dict', 1, 0)]), ("dict(**{'a': 1, 'b': 2, 'c': 3})", [BuiltinTypeCall('dict', 1, 0)]), + ('builtins.dict()', []), ], ) def test_dict_no_allow_kwargs_exprs(expression, calls): From d889840b883231141e020a3f5bf8785f5bfa8d70 Mon Sep 17 00:00:00 2001 From: Ben Webber Date: Fri, 1 Dec 2017 03:04:46 +0000 Subject: [PATCH 2/2] check-builtin-literals: Appease flake8 with six --- testing/resources/builtin_constructors.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/testing/resources/builtin_constructors.py b/testing/resources/builtin_constructors.py index 720bd2b..174a9e8 100644 --- a/testing/resources/builtin_constructors.py +++ b/testing/resources/builtin_constructors.py @@ -1,5 +1,4 @@ -# flake8 checks imports; `builtins` does not exist in Python 2. -# flake8: noqa +from six.moves import builtins c1 = complex() d1 = dict()