Skip to content

Commit 57494e2

Browse files
authored
Add support for type variable name convention check (#235)
Add N808 for type variable name convention check
1 parent f0edf20 commit 57494e2

File tree

3 files changed

+119
-0
lines changed

3 files changed

+119
-0
lines changed

README.rst

+4
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ These error codes are emitted:
5454
+---------+-----------------------------------------------------------------+
5555
| _`N807` | function name should not start and end with '__' |
5656
+---------+-----------------------------------------------------------------+
57+
| _`N808` | type variable names should use CapWords convention and an |
58+
| | optional suffix '_co' or '_contra' (`type variable names`_) |
59+
+---------+-----------------------------------------------------------------+
5760
| _`N811` | constant imported as non constant (`constants`_) |
5861
+---------+-----------------------------------------------------------------+
5962
| _`N812` | lowercase imported as non-lowercase |
@@ -80,6 +83,7 @@ These error codes are emitted:
8083
.. _function names: https://www.python.org/dev/peps/pep-0008/#function-and-variable-names
8184
.. _function arguments: https://www.python.org/dev/peps/pep-0008/#function-and-method-arguments
8285
.. _method names: https://www.python.org/dev/peps/pep-0008/#method-names-and-instance-variables
86+
.. _type variable names: https://peps.python.org/pep-0008/#type-variable-names
8387

8488
Options
8589
-------

src/pep8ext_naming.py

+35
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ class ClassNameCheck(BaseASTCheck):
254254
Classes for internal use have a leading underscore in addition.
255255
"""
256256
N801 = "class name '{name}' should use CapWords convention"
257+
N808 = "type variable name '{name}' should use CapWords convention and an optional suffix '_co' or '_contra'"
257258
N818 = "exception name '{name}' should be named with an Error suffix"
258259

259260
@classmethod
@@ -275,6 +276,40 @@ def superclass_names(cls, name, parents: Iterable, _names=None):
275276
names.update(cls.superclass_names(base.id, parents, names))
276277
return names
277278

279+
def visit_module(self, node, parents: Iterable, ignore=None):
280+
for body in node.body:
281+
try:
282+
if len(body.targets) != 1:
283+
continue
284+
name = body.targets[0].id
285+
func_name = body.value.func.id
286+
args = [a.value for a in body.value.args]
287+
keywords = {kw.arg: kw.value.value for kw in body.value.keywords}
288+
except AttributeError:
289+
continue
290+
291+
if func_name != "TypeVar" or _ignored(name, ignore):
292+
continue
293+
294+
if len(args) == 0 or args[0] != name:
295+
yield self.err(body, 'N808', name=name)
296+
297+
if not name[:1].isupper():
298+
yield self.err(body, 'N808', name=name)
299+
300+
parts = name.split('_')
301+
if len(parts) > 2:
302+
yield self.err(body, 'N808', name=name)
303+
304+
suffix = parts[-1] if len(parts) > 1 else ''
305+
if suffix and suffix != 'co' and suffix != 'contra':
306+
yield self.err(body, 'N808', name=name)
307+
elif keywords.get('covariant') and suffix != 'co':
308+
yield self.err(body, 'N808', name=name)
309+
elif keywords.get('contravariant') and suffix != 'contra':
310+
yield self.err(body, 'N808', name=name)
311+
312+
278313
def visit_classdef(self, node, parents: Iterable, ignore=None):
279314
name = node.name
280315
if _ignored(name, ignore):

testsuite/N808.py

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from typing import TypeVar
2+
3+
#: Okay
4+
Ok = TypeVar("Ok")
5+
6+
#: N808
7+
notok = TypeVar("notok")
8+
9+
#: N808
10+
notok_co = TypeVar("notok_co")
11+
12+
#: Okay(--ignore-names=notok)
13+
notok = TypeVar("notok")
14+
15+
#: N808:1:1(--ignore-names=*OK)
16+
notok = TypeVar("notok")
17+
18+
#: Okay
19+
Ok_co = TypeVar("Ok_co", covariant=True)
20+
21+
#: Okay
22+
Ok_contra = TypeVar("Ok_contra", contravariant=True)
23+
24+
#: N808
25+
Ok__contra = TypeVar("Ok__contra", contravariant=True)
26+
27+
#: N808
28+
Var_contra = TypeVar("Var_contra", covariant=True)
29+
30+
#: N808
31+
Var_co = TypeVar("Var_co", contravariant=True)
32+
33+
#: N808
34+
Var = TypeVar("Var", covariant=True)
35+
36+
#: Okay
37+
Ok_co = TypeVar("Ok_co")
38+
39+
#: Okay
40+
Ok_contra = TypeVar("Ok_contra")
41+
42+
#: Okay
43+
Good = TypeVar("Good")
44+
45+
#: N808
46+
__NotGood = TypeVar("__NotGood")
47+
48+
#: N808
49+
__NotGood__ = TypeVar("__NotGood__")
50+
51+
#: N808
52+
NotGood__ = TypeVar("NotGood__")
53+
54+
#: Okay
55+
A = TypeVar("A")
56+
57+
#: Okay
58+
A_contra = TypeVar("A_contra")
59+
60+
#: N808
61+
A = TypeVar("B")
62+
63+
#: N808
64+
A_contra = TypeVar("B")
65+
66+
#: N808
67+
A_contra = TypeVar("B_contra")
68+
69+
#: N808
70+
A_contra = TypeVar("A_Contra")
71+
72+
#: N808
73+
A_contra = TypeVar()
74+
75+
76+
# Make sure other function calls do not get checked
77+
#: Okay
78+
t = str('something')
79+
t = TypeVar
80+

0 commit comments

Comments
 (0)