or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/pypi-flake8-comprehensions

A flake8 plugin to help you write better list/set/dict comprehensions.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/flake8-comprehensions@3.16.x

To install, run

npx @tessl/cli install tessl/pypi-flake8-comprehensions@3.16.0

0

# flake8-comprehensions

1

2

A flake8 plugin that helps you write better list, set, and dict comprehensions in Python. The plugin implements over 20 specific rules (C400-C420) that detect unnecessary generators, redundant comprehensions, inefficient patterns, and suboptimal code constructs when working with Python's comprehension syntax.

3

4

## Package Information

5

6

- **Package Name**: flake8-comprehensions

7

- **Package Type**: pypi

8

- **Language**: Python

9

- **Installation**: `pip install flake8-comprehensions`

10

- **Requirements**: Python 3.9+, flake8 >=3 (!=3.2)

11

12

## Core Imports

13

14

This is a flake8 plugin, so it's not typically imported directly in code. Instead, it integrates automatically with flake8:

15

16

```python

17

# The plugin is automatically loaded by flake8

18

# No direct import needed in your code

19

```

20

21

For direct usage (advanced/testing):

22

23

```python

24

from flake8_comprehensions import ComprehensionChecker

25

import ast

26

```

27

28

## Basic Usage

29

30

### As a flake8 Plugin (Normal Usage)

31

32

After installation, the plugin automatically integrates with flake8 and detects comprehension optimization opportunities:

33

34

```bash

35

# Install the plugin

36

pip install flake8-comprehensions

37

38

# Run flake8 - C4xx rules are automatically active

39

flake8 your_code.py

40

41

# Example output:

42

# your_code.py:5:8: C400 Unnecessary generator - rewrite as a list comprehension.

43

# your_code.py:12:15: C403 Unnecessary list comprehension - rewrite as a set comprehension.

44

```

45

46

Configure in setup.cfg or pyproject.toml:

47

48

```ini

49

[flake8]

50

select = C4 # Enable only comprehension rules

51

# or

52

ignore = C415,C416 # Disable specific rules

53

```

54

55

### Direct API Usage (Advanced)

56

57

```python

58

import ast

59

from flake8_comprehensions import ComprehensionChecker

60

61

# Parse Python code

62

code = "foo = list(x + 1 for x in range(10))"

63

tree = ast.parse(code)

64

65

# Create checker instance

66

checker = ComprehensionChecker(tree)

67

68

# Run analysis

69

for violation in checker.run():

70

line, col, message, checker_type = violation

71

print(f"Line {line}, Col {col}: {message}")

72

```

73

74

## Architecture

75

76

The plugin follows flake8's standard plugin architecture and uses Python's AST (Abstract Syntax Tree) for code analysis:

77

78

### Plugin Integration

79

- **Entry Point Registration**: The plugin registers with flake8 via Python's entry point system under `flake8.extension` with code prefix `C4`

80

- **Instantiation**: For each Python file, flake8 creates a `ComprehensionChecker` instance, passing the parsed AST

81

- **Analysis**: flake8 calls the `run()` method which yields tuples of `(line, column, message, checker_type)`

82

- **Reporting**: flake8 formats and reports violations according to user configuration

83

84

### AST Analysis Pattern

85

- **Tree Walking**: Uses `ast.walk()` to traverse all nodes in the Python AST

86

- **Pattern Matching**: Identifies specific AST node patterns (e.g., `ast.Call` nodes with generators)

87

- **Rule Logic**: Each C4xx rule corresponds to specific combinations of AST node types and attributes

88

- **Violation Detection**: When a problematic pattern is found, yields structured violation data

89

90

### Error Code Organization

91

- **C400-C402**: Generator to comprehension transformations

92

- **C403-C404**: List comprehension to other comprehension types

93

- **C405-C420**: Various literal, constructor, and optimization patterns

94

95

This architecture enables static analysis of Python code without execution, providing fast and reliable detection of comprehension optimization opportunities.

96

97

## Capabilities

98

99

### ComprehensionChecker Class

100

101

The main plugin class that analyzes Python AST for comprehension optimization opportunities.

102

103

```python { .api }

104

class ComprehensionChecker:

105

"""

106

Flake8 plugin to help you write better list/set/dict comprehensions.

107

"""

108

109

name: str # "flake8-comprehensions"

110

version: str # Plugin version from package metadata

111

messages: dict[str, str] # Error message templates for all C4xx rules

112

tree: ast.AST # Python AST tree to analyze

113

114

def __init__(self, tree: ast.AST) -> None:

115

"""Initialize checker with AST tree."""

116

117

def run(self) -> Generator[tuple[int, int, str, type[Any]]]:

118

"""

119

Main analysis method that yields flake8 violations.

120

121

Yields:

122

tuple[int, int, str, type]: (line_number, column_offset, message, checker_type)

123

"""

124

```

125

126

### Helper Functions

127

128

Utility functions used internally by the checker:

129

130

```python { .api }

131

def has_star_args(call_node: ast.Call) -> bool:

132

"""Check if AST Call node has starred arguments (*args)."""

133

134

def has_double_star_args(call_node: ast.Call) -> bool:

135

"""Check if AST Call node has double-starred arguments (**kwargs)."""

136

```

137

138

### Constants

139

140

```python { .api }

141

comp_type: dict[type[ast.AST], str]

142

# Maps AST comprehension node types to string names

143

# {ast.DictComp: "dict", ast.ListComp: "list", ast.SetComp: "set"}

144

```

145

146

## Error Rules Reference

147

148

The plugin implements 20 specific rules for comprehension optimization:

149

150

### Generator to Comprehension Rules (C400-C402)

151

- **C400**: Unnecessary generator - rewrite as list comprehension

152

- `list(f(x) for x in foo)``[f(x) for x in foo]`

153

- **C401**: Unnecessary generator - rewrite as set comprehension

154

- `set(f(x) for x in foo)``{f(x) for x in foo}`

155

- **C402**: Unnecessary generator - rewrite as dict comprehension

156

- `dict((x, f(x)) for x in foo)``{x: f(x) for x in foo}`

157

158

### List to Other Comprehension Rules (C403-C404)

159

- **C403**: Unnecessary list comprehension - rewrite as set comprehension

160

- `set([f(x) for x in foo])``{f(x) for x in foo}`

161

- **C404**: Unnecessary list comprehension - rewrite as dict comprehension

162

- `dict([(x, f(x)) for x in foo])``{x: f(x) for x in foo}`

163

164

### Literal Optimization Rules (C405-C406)

165

- **C405**: Unnecessary list/tuple literal - rewrite as set literal

166

- `set([1, 2])``{1, 2}`, `set((1, 2))``{1, 2}`

167

- **C406**: Unnecessary list/tuple literal - rewrite as dict literal

168

- `dict([(1, 2)])``{1: 2}`, `dict(((1, 2),))``{1: 2}`

169

170

### Empty Constructor Rules (C408)

171

- **C408**: Unnecessary call - rewrite as literal

172

- `dict()``{}`, `list()``[]`, `tuple()``()`

173

174

### Redundant Constructor Rules (C409-C411)

175

- **C409**: Unnecessary tuple passed to tuple() - remove outer call or rewrite as literal

176

- `tuple((1, 2))``(1, 2)`, `tuple([1, 2])``(1, 2)`

177

- **C410**: Unnecessary list passed to list() - remove outer call or rewrite as literal

178

- `list([1, 2])``[1, 2]`, `list((1, 2))``[1, 2]`

179

- **C411**: Unnecessary list call - remove outer call to list()

180

- `list([f(x) for x in foo])``[f(x) for x in foo]`

181

182

### Function Call Optimization Rules (C413-C415)

183

- **C413**: Unnecessary list/reversed call around sorted()

184

- `list(sorted([2, 3, 1]))``sorted([2, 3, 1])`

185

- `reversed(sorted([2, 3, 1]))``sorted([2, 3, 1], reverse=True)`

186

- **C414**: Unnecessary call within other constructor calls

187

- `list(list(iterable))``list(iterable)`

188

- `set(sorted(iterable))``set(iterable)`

189

- **C415**: Unnecessary subscript reversal within functions

190

- `set(iterable[::-1])``set(iterable)`

191

- `reversed(iterable[::-1])``iterable`

192

193

### Identity Comprehension Rules (C416, C420)

194

- **C416**: Unnecessary comprehension - rewrite using constructor

195

- `[x for x in iterable]``list(iterable)`

196

- `{x for x in iterable}``set(iterable)`

197

- `{a: b for a, b in iterable}``dict(iterable)`

198

- **C420**: Unnecessary dict comprehension - rewrite using dict.fromkeys()

199

- `{x: 1 for x in iterable}``dict.fromkeys(iterable, 1)`

200

- `{x: None for x in iterable}``dict.fromkeys(iterable)`

201

202

### Lambda and Map Rules (C417)

203

- **C417**: Unnecessary map usage - rewrite using comprehension

204

- `map(lambda x: x + 1, iterable)``(x + 1 for x in iterable)`

205

- `list(map(lambda x: x * 2, nums))``[x * 2 for x in nums]`

206

207

### Dict Constructor Rules (C418)

208

- **C418**: Unnecessary dict/dict comprehension passed to dict()

209

- `dict({})``{}`, `dict({"a": 1})``{"a": 1}`

210

211

### Short-circuiting Rules (C419)

212

- **C419**: Unnecessary list comprehension in any/all() prevents short-circuiting

213

- `all([condition(x) for x in iterable])``all(condition(x) for x in iterable)`

214

- `any([condition(x) for x in iterable])``any(condition(x) for x in iterable)`

215

216

## Plugin Integration

217

218

The plugin integrates with flake8 through Python's entry point system:

219

220

```toml

221

# pyproject.toml entry point configuration

222

[project.entry-points."flake8.extension"]

223

C4 = "flake8_comprehensions:ComprehensionChecker"

224

```

225

226

This registers the plugin with flake8 to handle all C4xx rule codes. The plugin follows flake8's standard interface:

227

228

1. **Instantiation**: flake8 creates a `ComprehensionChecker` instance for each Python file, passing the parsed AST

229

2. **Analysis**: flake8 calls the `run()` method to get a generator of violations

230

3. **Reporting**: Each violation is a tuple of `(line, column, message, checker_type)`

231

232

## Usage Examples

233

234

### Common Patterns Detected

235

236

```python

237

# C400: Generator to list comprehension

238

# Bad

239

numbers = list(x * 2 for x in range(10))

240

# Good

241

numbers = [x * 2 for x in range(10)]

242

243

# C403: List comprehension to set comprehension

244

# Bad

245

unique_squares = set([x**2 for x in numbers])

246

# Good

247

unique_squares = {x**2 for x in numbers}

248

249

# C411: Unnecessary list() around list comprehension

250

# Bad

251

processed = list([process(x) for x in items])

252

# Good

253

processed = [process(x) for x in items]

254

255

# C417: Map with lambda to comprehension

256

# Bad

257

doubled = list(map(lambda x: x * 2, numbers))

258

# Good

259

doubled = [x * 2 for x in numbers]

260

261

# C419: List comprehension in any/all

262

# Bad - builds entire list before checking

263

valid = all([is_valid(item) for item in items])

264

# Good - stops at first False

265

valid = all(is_valid(item) for item in items)

266

```

267

268

### Integration with Development Workflow

269

270

```bash

271

# In pre-commit hooks

272

repos:

273

- repo: https://github.com/PyCQA/flake8

274

rev: 6.0.0

275

hooks:

276

- id: flake8

277

additional_dependencies: [flake8-comprehensions]

278

279

# In CI/CD pipelines

280

flake8 --select=C4 src/

281

# Returns exit code 1 if any C4xx violations found

282

283

# With specific rule configuration

284

flake8 --ignore=C416,C419 src/ # Ignore specific rules

285

flake8 --select=C400,C401,C402 src/ # Only generator rules

286

```