or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdcomplexity.mdhalstead.mdindex.mdmaintainability.mdraw-metrics.mdvisitors.md

complexity.mddocs/

0

# Cyclomatic Complexity Analysis

1

2

McCabe's cyclomatic complexity analysis for measuring code complexity and maintainability. Provides complexity scoring with A-F ranking system, detailed block-level analysis for functions, methods, and classes, and utilities for processing and sorting results.

3

4

## Capabilities

5

6

### Code Analysis Functions

7

8

Main functions for analyzing cyclomatic complexity from source code or AST nodes.

9

10

```python { .api }

11

def cc_visit(code, **kwargs):

12

"""

13

Visit the given code with ComplexityVisitor.

14

15

Parameters:

16

- code (str): Python source code to analyze

17

- **kwargs: Keyword arguments passed to ComplexityVisitor

18

- to_method (bool): Whether to treat top-level functions as methods

19

- classname (str): Class name for method analysis

20

- off (bool): Whether to include decorator complexity

21

- no_assert (bool): Whether to exclude assert statements from complexity

22

23

Returns:

24

list: List of Function and Class objects representing code blocks

25

"""

26

27

def cc_visit_ast(ast_node, **kwargs):

28

"""

29

Visit the AST node with ComplexityVisitor.

30

31

Parameters:

32

- ast_node: Python AST node to analyze

33

- **kwargs: Keyword arguments passed to ComplexityVisitor

34

35

Returns:

36

list: List of Function and Class objects representing code blocks

37

"""

38

```

39

40

### Complexity Ranking and Utilities

41

42

Functions for ranking complexity scores and processing analysis results.

43

44

```python { .api }

45

def cc_rank(cc):

46

"""

47

Rank the complexity score from A to F.

48

49

Ranking system:

50

- A (1-5): Low risk - simple block

51

- B (6-10): Low risk - well structured and stable block

52

- C (11-20): Moderate risk - slightly complex block

53

- D (21-30): More than moderate risk - more complex block

54

- E (31-40): High risk - complex block, alarming

55

- F (41+): Very high risk - error-prone, unstable block

56

57

Parameters:

58

- cc (int): Complexity score (must be non-negative)

59

60

Returns:

61

str: Single letter grade (A-F)

62

63

Raises:

64

ValueError: If complexity is negative

65

"""

66

67

def average_complexity(blocks):

68

"""

69

Compute the average cyclomatic complexity from blocks.

70

71

Parameters:

72

- blocks (list): List of Function or Class objects

73

74

Returns:

75

float: Average complexity score, or 0 if blocks is empty

76

"""

77

78

def sorted_results(blocks, order=SCORE):

79

"""

80

Sort blocks by complexity with specified ordering.

81

82

Parameters:

83

- blocks (list): List of Function or Class objects

84

- order (function): Sorting function, one of:

85

- SCORE: Sort by complexity score (descending) - default

86

- LINES: Sort by line number (ascending)

87

- ALPHA: Sort alphabetically by name (ascending)

88

89

Returns:

90

list: Sorted list of blocks

91

"""

92

93

def add_inner_blocks(blocks):

94

"""

95

Process blocks by adding closures and inner classes as top-level blocks.

96

Flattens nested functions and inner classes into the main block list.

97

98

Parameters:

99

- blocks (list): List of Function or Class objects

100

101

Returns:

102

list: Expanded list with nested blocks promoted to top-level

103

"""

104

```

105

106

### Sorting Constants

107

108

Predefined sorting functions for use with sorted_results().

109

110

```python { .api }

111

# Sort by complexity score (descending)

112

SCORE = lambda block: -GET_COMPLEXITY(block)

113

114

# Sort by line number (ascending)

115

LINES = lambda block: block.lineno

116

117

# Sort alphabetically by name (ascending)

118

ALPHA = lambda block: block.name

119

```

120

121

## Usage Examples

122

123

### Basic Complexity Analysis

124

125

```python

126

from radon.complexity import cc_visit, cc_rank

127

128

code = '''

129

def simple_function():

130

return True

131

132

def complex_function(x, y, z):

133

if x > 0:

134

if y > 0:

135

if z > 0:

136

return x + y + z

137

else:

138

return x + y

139

else:

140

return x

141

else:

142

return 0

143

'''

144

145

# Analyze complexity

146

blocks = cc_visit(code)

147

148

for block in blocks:

149

rank = cc_rank(block.complexity)

150

print(f"{block.name}: {block.complexity} ({rank})")

151

# Output:

152

# simple_function: 1 (A)

153

# complex_function: 4 (A)

154

```

155

156

### Processing and Sorting Results

157

158

```python

159

from radon.complexity import cc_visit, sorted_results, average_complexity, SCORE, LINES, ALPHA

160

161

code = '''

162

class Calculator:

163

def add(self, a, b):

164

return a + b

165

166

def complex_divide(self, a, b):

167

if b == 0:

168

raise ValueError("Division by zero")

169

elif isinstance(a, str) or isinstance(b, str):

170

raise TypeError("String division not supported")

171

else:

172

return a / b

173

174

def utility_function():

175

pass

176

'''

177

178

blocks = cc_visit(code)

179

180

# Sort by complexity (default)

181

by_complexity = sorted_results(blocks, SCORE)

182

print("By complexity:")

183

for block in by_complexity:

184

print(f" {block.name}: {block.complexity}")

185

186

# Sort by line number

187

by_lines = sorted_results(blocks, LINES)

188

print("By line number:")

189

for block in by_lines:

190

print(f" Line {block.lineno}: {block.name}")

191

192

# Sort alphabetically

193

by_name = sorted_results(blocks, ALPHA)

194

print("By name:")

195

for block in by_name:

196

print(f" {block.name}: {block.complexity}")

197

198

# Calculate average complexity

199

avg = average_complexity(blocks)

200

print(f"Average complexity: {avg:.2f}")

201

```

202

203

### Analyzing Methods vs Functions

204

205

```python

206

from radon.complexity import cc_visit

207

208

# Analyze as standalone functions

209

blocks_as_functions = cc_visit(class_code, to_method=False)

210

211

# Analyze as methods within a class context

212

blocks_as_methods = cc_visit(class_code, to_method=True, classname="MyClass")

213

214

for block in blocks_as_methods:

215

if block.is_method:

216

print(f"Method {block.fullname}: {block.complexity}")

217

else:

218

print(f"Function {block.name}: {block.complexity}")

219

```

220

221

### Handling Nested Functions and Classes

222

223

```python

224

from radon.complexity import cc_visit, add_inner_blocks

225

226

code_with_nested = '''

227

class OuterClass:

228

def method(self):

229

def inner_function():

230

return True

231

return inner_function()

232

233

class InnerClass:

234

def inner_method(self):

235

pass

236

'''

237

238

# Get blocks including nested structures

239

blocks = cc_visit(code_with_nested)

240

241

# Flatten nested blocks to top level

242

all_blocks = add_inner_blocks(blocks)

243

244

print("All blocks (including nested):")

245

for block in all_blocks:

246

print(f" {block.name}: {block.complexity}")

247

```

248

249

## Error Handling

250

251

The complexity analysis functions handle various error conditions:

252

253

- **Invalid complexity scores**: `cc_rank()` raises `ValueError` for negative complexity values

254

- **Empty block lists**: `average_complexity()` returns 0 for empty lists

255

- **Malformed code**: AST parsing errors are propagated from underlying Python ast module

256

- **Invalid sorting functions**: TypeError if order parameter is not callable

257

258

## Integration with CLI

259

260

The complexity module integrates with radon's command-line interface:

261

262

```bash

263

# Command-line equivalent of cc_visit()

264

radon cc path/to/code.py

265

266

# With ranking and complexity display

267

radon cc --show-complexity --min A --max F path/to/code.py

268

269

# JSON output for programmatic processing

270

radon cc --json path/to/code.py

271

```