itd-scanner
A production-ready static analysis engine for detecting Idiomatic Technical Debt (ITD), vectorization bypass, and performance antipatterns in Data Science Python (Pandas, NumPy, SciPy, and PyTorch).
What is Idiomatic Technical Debt (ITD)?
Modern LLMs and procedural programmers often generate functionally correct data science code that bypasses high-performance vectorized C/Fortran primitives. Instead, they produce slow, memory-intensive Python loops, accumulator variables, and reinvented algorithms.
Traditional linters (pylint, flake8, perflint) focus on PEP 8 style or general Python idioms and miss vectorized data science smells. itd-scanner inspects the Abstract Syntax Tree (AST) to pinpoint exactly where vectorized libraries are being bypassed.
The 5 Core ITD Antipatterns
| Smell | Antipattern (What It Detects) | Idiomatic Alternative |
|---|---|---|
| Collection Choker | Row-by-row iteration over DataFrames/Arrays (.iterrows(), .itertuples(), for i in range(len(arr)): arr[i]) |
Vectorized operations, .apply(), or array indexing |
| Math Looper | Scalar accumulator math (total += arr[i] or total += x * y) inside loops |
np.sum(), df['col'].sum(), or vectorized ufuncs |
| String Masher | Quadratic string concatenation (s += ...) inside iterative loops |
str.join(), f-strings, or pd.Series.str |
| RAM Hog | Wasteful list comprehensions materialized inside reductions (sum([...]), set([...])) |
Generator expressions sum(...) or set comprehensions {...} |
| DIY Wheel | Reinventing built-ins (d[k] = d.get(k, 0) + 1, linear search loops, manual min/max/unique) |
collections.Counter(), pd.value_counts(), in, set() |
Quickstart & CLI
Installation
# Install core package from PyPI
pip install itd-scanner
# Or install with research baseline linters (dslinter, perflint, pandas-vet)
pip install "itd-scanner[baselines]"
CLI Usage
Scan any Python file directly from your terminal:
# Scan for ITD smells with ANSI colored reporting
itd-scanner my_script.py
# Include Radon software metrics (Cyclomatic Complexity, SLOC, Halstead Volume, MI)
itd-scanner my_script.py --metrics
# Output structured JSON for CI/CD pipelines
itd-scanner my_script.py --json
Terminal Preview
=================================================================
ITD-Scanner Report: analysis.py
=================================================================
Idiomatic Technical Debt (ITD) Smells:
Collection Choker : [DETECTED]
Math Looper : [CLEAN]
String Masher : [CLEAN]
RAM Hog : [DETECTED]
DIY Wheel : [CLEAN]
Detailed Findings (2):
Line 14 [Collection Choker]: Row-by-row DataFrame iteration using .iterrows() instead of vectorized operations.
Line 28 [RAM Hog]: Wasteful list comprehension materialized inside 'sum()'; use a generator expression or vectorized call.
Software Complexity & Maintainability (Radon):
Lines of Code (LOC) : 34
Source Lines (SLOC) : 22
Cyclomatic Complexity (CC): 4
Maintainability Index : 84.12/100
Halstead Volume : 312.45 bits
Halstead Difficulty : 8.50
=================================================================
Status: 2 ITD Smells Found
=================================================================
Python API
You can easily embed itd-scanner into custom analysis pipelines, linters, or research benchmarks:
from itd_scanner import scan_code, scan_file
code = """
import pandas as pd
total = 0
for idx, row in df.iterrows():
total += row['price']
"""
# Simple boolean smell flags
flags = scan_code(code)
print(flags)
# Output:
# {
# 'Collection Choker': True,
# 'Math Looper': True,
# 'String Masher': False,
# 'RAM Hog': False,
# 'DIY Wheel': False
# }
# Detailed report with AST line numbers and Radon software metrics
report = scan_code(code, return_details=True, include_metrics=True)
print(report["findings"])
# [
# {'smell': 'Collection Choker', 'line': 5, 'message': 'Row-by-row DataFrame iteration...'},
# {'smell': 'Math Looper', 'line': 6, 'message': 'Accumulator math inside loop...'}
# ]
print(report["metrics"]["cyclomatic_complexity"]) # CC
Antipattern Examples & Fixes
1. Collection Choker
# ❌ Antipattern (Iterating row-by-row)
for idx, row in df.iterrows():
df.loc[idx, 'total'] = row['a'] + row['b']
# ✅ Idiomatic (Vectorized column addition)
df['total'] = df['a'] + df['b']
2. Math Looper
# ❌ Antipattern (Pure Python loop math)
total = 0
for i in range(len(arr)):
total += arr[i] * 2
# ✅ Idiomatic (NumPy vectorized ufunc)
total = np.sum(arr * 2)
3. DIY Wheel (Frequency Counting)
# ❌ Antipattern (Manual dict accumulation)
counts = {}
for item in items:
counts[item] = counts.get(item, 0) + 1
# ✅ Idiomatic (Standard library Counter / Pandas)
from collections import Counter
counts = Counter(items)
Research & Empirical Study
This library is part of an academic research project at King Fahd University of Petroleum & Minerals (KFUPM) analyzing code generated by Large Language Models across the DS-1000 benchmark.
Citation
If you use itd-scanner in academic research, please cite:
@misc{khalid2026itdscanner,
author = {Abdullah Khalid},
title = {itd-scanner: Static Analysis Engine for Idiomatic Technical Debt in Data Science Python},
year = {2026},
publisher = {GitHub},
howpublished = {\url{https://github.com/Abdullah-k0de/itd-scanner}}
}
License
This project is licensed under the MIT License.
Release files for itd-scanner 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| itd_scanner-0.1.0.tar.gz | 18.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| itd_scanner-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 33.8 kB
Release files / itd_scanner-0.1.0.tar.gz
| Download URL | itd_scanner-0.1.0.tar.gz |
|---|---|
| Size | 18.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d07789f3f65056581322214659cb192fd75fa0b16f577c2e84f125c5f262561f
|
|
BLAKE2b-256 checksum How to use checksums |
b06e9f7ee6c21b1cdbcec7cff1932f202237658f03f3cbaefdb74bc064fcc398
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.6
|
Release files / itd_scanner-0.1.0-py3-none-any.whl
| Download URL | itd_scanner-0.1.0-py3-none-any.whl |
|---|---|
| Size | 15.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fc6c784546406233257e545a4d6369c955bbb2118f4ffa1517d8fcb725a7a1f0
|
|
BLAKE2b-256 checksum How to use checksums |
dba376215a9f7e4c4de1eb485a371fafb7aabcfb6d7cb59af619fb102b63192b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.6
|