Skip to content

Commit 21f4d8e

Browse files
authored
Handle positional-only args in class methods (fixes #157) (#158)
1 parent c636077 commit 21f4d8e

File tree

3 files changed

+125
-1
lines changed

3 files changed

+125
-1
lines changed

bugbear.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ def check_for_b902(self, node):
487487
expected_first_args = B902.self
488488
kind = "instance"
489489

490-
args = node.args.args
490+
args = getattr(node.args, "posonlyargs", []) + node.args.args
491491
vararg = node.args.vararg
492492
kwarg = node.args.kwarg
493493
kwonlyargs = node.args.kwonlyargs

tests/b902_py38.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
def not_a_method(arg1, /):
2+
...
3+
4+
5+
class NoWarnings:
6+
def __init__(self, /):
7+
def not_a_method_either(arg1, /):
8+
...
9+
10+
def __new__(cls, /, *args, **kwargs):
11+
...
12+
13+
def method(self, arg1, /, *, yeah):
14+
...
15+
16+
async def async_method(self, arg1, /, *, yeah):
17+
...
18+
19+
@classmethod
20+
def someclassmethod(cls, arg1, with_default=None, /):
21+
...
22+
23+
@staticmethod
24+
def not_a_problem(arg1, /):
25+
...
26+
27+
28+
class Warnings:
29+
def __init__(i_am_special, /):
30+
...
31+
32+
def almost_a_class_method(cls, arg1, /):
33+
...
34+
35+
def almost_a_static_method():
36+
...
37+
38+
@classmethod
39+
def wat(self, i_like_confusing_people, /):
40+
...
41+
42+
def i_am_strange(*args, **kwargs):
43+
self = args[0]
44+
45+
def defaults_anyone(self=None, /):
46+
...
47+
48+
def invalid_kwargs_only(**kwargs):
49+
...
50+
51+
def invalid_keyword_only(*, self):
52+
...
53+
54+
async def async_invalid_keyword_only(*, self):
55+
...
56+
57+
58+
class Meta(type):
59+
def __init__(cls, name, bases, d, /):
60+
...
61+
62+
@classmethod
63+
def __prepare__(metacls, name, bases, /):
64+
return {}
65+
66+
67+
class OtherMeta(type):
68+
def __init__(self, name, bases, d, /):
69+
...
70+
71+
@classmethod
72+
def __prepare__(cls, name, bases, /):
73+
return {}
74+
75+
@classmethod
76+
def first_arg_mcs_allowed(mcs, value, /):
77+
...
78+
79+
80+
def type_factory():
81+
return object
82+
83+
84+
class CrazyBases(Warnings, type_factory(), metaclass=type):
85+
def __init__(self):
86+
...
87+
88+
89+
class RuntimeError("This is not a base"):
90+
def __init__(self):
91+
...
92+
93+
94+
class ImplicitClassMethods:
95+
def __new__(cls, /, *args, **kwargs):
96+
...
97+
98+
def __init_subclass__(cls, /, *args, **kwargs):
99+
...
100+
101+
def __class_getitem__(cls, key, /):
102+
...

tests/test_bugbear.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from pathlib import Path
44
import site
55
import subprocess
6+
import sys
67
import unittest
78

89
from hypothesis import HealthCheck, given, settings
@@ -262,6 +263,27 @@ def test_b902(self):
262263
),
263264
)
264265

266+
@unittest.skipIf(sys.version_info < (3, 8), "requires 3.8+")
267+
def test_b902_py38(self):
268+
filename = Path(__file__).absolute().parent / "b902_py38.py"
269+
bbc = BugBearChecker(filename=str(filename))
270+
errors = list(bbc.run())
271+
self.assertEqual(
272+
errors,
273+
self.errors(
274+
B902(29, 17, vars=("'i_am_special'", "instance", "self")),
275+
B902(32, 30, vars=("'cls'", "instance", "self")),
276+
B902(35, 4, vars=("(none)", "instance", "self")),
277+
B902(39, 12, vars=("'self'", "class", "cls")),
278+
B902(42, 22, vars=("*args", "instance", "self")),
279+
B902(48, 30, vars=("**kwargs", "instance", "self")),
280+
B902(51, 32, vars=("*, self", "instance", "self")),
281+
B902(54, 44, vars=("*, self", "instance", "self")),
282+
B902(68, 17, vars=("'self'", "metaclass instance", "cls")),
283+
B902(72, 20, vars=("'cls'", "metaclass class", "metacls")),
284+
),
285+
)
286+
265287
def test_b903(self):
266288
filename = Path(__file__).absolute().parent / "b903.py"
267289
bbc = BugBearChecker(filename=str(filename))

0 commit comments

Comments
 (0)