How I Replaced 12 Python Packages with Just the Standard Library

How I Replaced 12 Python Packages with Just the Standard Library

My "Write-Up" Submission for the Zero Dependency Hackathon 2026


🚀 Introduction

Every Python developer knows the feeling. You want to scan your code for security vulnerabilities, so you reach for a collection of packages:

pip install bandit pylint flake8 safety requests colorama click pydantic loguru jinja2 python-dotenv tqdm

That's 12 packages with a combined 200+ million weekly downloads.

But installing these tools can also introduce a growing dependency tree. For example, bandit can bring in packages such as pbr, stevedore, and PyYAML. Similarly, pylint relies on packages such as astroid, isort, and tomlkit.

Before long, a simple security or code-quality setup can become a collection of dependencies that needs to be installed, maintained, and kept compatible.

And sometimes, the setup itself becomes enough of a barrier that developers simply don't use these tools.

So I asked myself: What if I could build a security scanner that doesn't install ANY packages?

🎯 What I Actually Built

The result was Code Auditor Pro — a zero-dependency security scanner designed to analyze source code and identify common security, quality, and style issues.

The scanner looks for:

  • 🔴 Hardcoded Secrets — Passwords, API keys, and tokens
  • 🔴 SQL Injection — Unsafe database queries
  • 🔴 Dangerous Functionseval, exec, os.system, pickle.load
  • 🟡 Bad Practices — Bare except: and missing docstrings
  • 🔵 Style Issues — Lines longer than 100 characters and bad indentation

One file. 800+ lines. 0 dependencies. 100% standard library.

📦 The Packages I Replaced with the Standard Library

Instead of depending on external packages, I explored what Python already provides through its standard library.

1. bandit / pylint / flake8 — Security & Code Analysis

The Packages: I would normally use bandit for security scanning, pylint for code quality, and flake8 for style checking.

My Replacement: ast + re

import ast
import re

# AST (Abstract Syntax Tree) parses code structure
tree = ast.parse(code)

# I walk through every node in the tree
for node in ast.walk(tree):
    if isinstance(node, ast.FunctionDef):
        complexity = self._calculate_complexity(node)

        if complexity > 10:
            # Add issue: "Function has high complexity"

    if isinstance(node, ast.ExceptHandler) and node.type is None:
        # Add issue: "Bare except detected"

Why it works: Python's built-in ast module gives me direct access to the structure of Python source code. It allows me to inspect functions, classes, imports, exceptions, and other syntax elements.

The re module complements this by handling pattern matching for things such as hardcoded secrets and suspicious functions.

2. click / typer — Command-Line Interface

The Package: I would normally use click or typer to create command-line interfaces.

My Replacement: argparse

import argparse

parser = argparse.ArgumentParser(description="Code Auditor Pro")

parser.add_argument('--port',
                    type=int,
                    default=8080,
                    help='Port to run on')

parser.add_argument('--scan',
                    help='Scan a file')

parser.add_argument('--compare',
                    nargs=2,
                    help='Compare two files')

parser.add_argument('--export',
                    help='Export report')

args = parser.parse_args()

Why it works: argparse is part of Python's standard library and provides argument parsing, flags, help messages, and command-line options without requiring another installation.

3. loguru — Logging

The Package: I would normally use loguru for structured and convenient logging.

My Replacement: logging

import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

logger = logging.getLogger(__name__)

Why it works: Python's built-in logging module supports log levels, handlers, formatters, and configurable logging behavior.

4. colorama — Terminal Colors

The Package: I would normally use colorama for colored terminal output.

My Replacement: ANSI escape codes

COLORS = {
    'GREEN': '\033[92m',
    'RED': '\033[91m',
    'YELLOW': '\033[93m',
    'BLUE': '\033[94m',
    'CYAN': '\033[96m',
    'RESET': '\033[0m',
    'BOLD': '\033[1m'
}

def color_text(text, color):
    return f"{COLORS.get(color, '')}{text}{COLORS['RESET']}"

Why it works: ANSI escape sequences can be used to control terminal text formatting without installing an additional package.

5. pydantic — Data Validation

The Package: I would normally use pydantic for data validation and structured models.

My Replacement: dataclasses

from dataclasses import dataclass

@dataclass
class CodeIssue:
    file: str
    line: int
    severity: str
    category: str
    message: str
    code: str
    fix: str = ""
    language: str = "python"

Why it works: dataclasses provide a clean way to represent structured data using Python's own standard library.

6. requests — HTTP Server

The Package: I would normally use requests for HTTP communication.

My Replacement: http.server + socketserver

import http.server
import socketserver

class Handler(http.server.SimpleHTTPRequestHandler):

    def do_GET(self):

        if self.path == '/':

            self.send_response(200)

            self.send_header(
                'Content-type',
                'text/html'
            )

            self.end_headers()

            self.wfile.write(
                HTML_TEMPLATE.encode()
            )

    def do_POST(self):

        if self.path == '/api/scan-code':

            # Process scan request
            # Return JSON response

Why it works: Python's http.server provides the basic functionality needed to serve HTML and handle HTTP requests without introducing a web framework.

7. jinja2 — HTML Templates

The Package: I would normally use jinja2 for HTML templating.

My Replacement: F-strings

HTML_TEMPLATE = f"""
<!DOCTYPE html>

<html>

<head>
    <title>Code Auditor Pro</title>

    <style>
        /* CSS here */
    </style>
</head>

<body>

    <h1>🔍 Code Auditor</h1>

    <div>Score: {score}</div>
    <div>Grade: {grade}</div>

</body>

</html>
"""

Why it works: For a relatively simple application, Python f-strings are enough to generate dynamic HTML without introducing a separate template engine.

8. python-dotenv — Configuration

The Package: I would normally use python-dotenv for configuration through environment files.

My Replacement: json + os.environ

import json
import os

# Load config from JSON file

with open('config.json', 'r') as f:
    config = json.load(f)

# Or read environment variables directly

port = int(
    os.environ.get('PORT', 8080)
)

debug = (
    os.environ.get('DEBUG', 'false')
    .lower() == 'true'
)

Why it works: Python already provides tools for reading JSON configuration files and environment variables.

9. tqdm — Progress Bars

The Package: I would normally use tqdm for progress indicators.

My Replacement: Manual progress using sys.stdout

import sys
import time

def show_progress(current, total):

    bar_width = 50

    progress = int(
        bar_width * current / total
    )

    bar = (
        '█' * progress
        + '░' * (bar_width - progress)
    )

    sys.stdout.write(
        f'\r[{bar}] {current}/{total}'
    )

    sys.stdout.flush()

Why it works: A progress bar can be implemented using normal terminal output and carriage returns. No dedicated progress-bar package is required.

10. pathlib — File Path Handling

The Package: I initially considered pathlib as something that needed replacement.

The Discovery: pathlib is already part of Python's standard library.

from pathlib import Path
import os

# Path handling with built-in pathlib

project_path = Path(
    "D:/zerodependency/code-auditor"
)

for py_file in project_path.rglob("*.py"):
    print(f"Found: {py_file}")

Why it works: pathlib has been part of Python's standard library since Python 3.4. This means there is no third-party dependency to install.

💀 The Hardest Parts of Building Without Dependencies

1. AST Is Powerful, But Limited

Python's built-in ast module gives me detailed information about source-code structure. However, static analysis still has limitations.

  • What is actually imported versus what is only referenced
  • Runtime behavior versus static code structure
  • Different ways of importing the same functionality

Because of this, I had to build my own import-resolution logic rather than relying on an existing package.

2. The "Eval" Problem

Regex is useful, but it can also create false positives. For example, cursor.execute(query) contains the letters exec, even though it is not a call to Python's exec() function.

# Had to add this check:

if 'cursor.execute' in line and func == 'exec':
    continue  # This is not actually exec()

I had to carefully refine the detection logic so that the scanner could distinguish between genuinely dangerous functions and harmless text.

3. Finding Secrets Without a Pattern Database

Hardcoded secrets can often be detected using regular expressions. The challenge was creating the detection patterns myself.

secrets = [

    (
        r'password\s*=\s*["\']([^"\']+)["\']',
        "CRITICAL",
        "Hardcoded password"
    ),

    (
        r'api_key\s*=\s*["\']([^"\']+)["\']',
        "CRITICAL",
        "Hardcoded API key"
    ),

    (
        r'sk-\w{20,}',
        "CRITICAL",
        "OpenAI API Key"
    ),

    (
        r'ghp_\w{20,}',
        "CRITICAL",
        "GitHub Token"
    ),

    (
        r'AIzaSy\w{20,}',
        "CRITICAL",
        "Google API Key"
    ),

    (
        r'AWS[A-Z0-9]{16,}',
        "CRITICAL",
        "AWS Access Key"
    ),

    # ... 15+ patterns total
]

4. Cross-Platform Issues

One of the hackathon requirements was that the project should build using a single command.

The command worked locally:

py -3.12 main.py

But testing on different systems exposed additional problems. File paths, ports, and Python versions could behave differently.

I had to:

  • Add allow_reuse_address = True to the server
  • Support a configurable --port argument
  • Handle cross-platform file paths
  • Ensure Python 3.8+ compatibility

No dependency would have solved these problems. Testing on different systems was the real solution.

5. Replacing the HTML Template

I wanted the application to have a useful and attractive interface. Normally, I might reach for React or Jinja2.

Instead, everything had to be created using pure strings:

HTML_TEMPLATE = """<!DOCTYPE html>

<html>

<head>

    <style>
        /* 150 lines of pure CSS */
    </style>

</head>

<body>

    <!-- 100 lines of HTML -->

    <script>
        // 200 lines of vanilla JavaScript
    </script>

</body>

</html>
"""

No frameworks. No CDNs. No external assets. Just one file containing everything.

6. Multi-Language Support

I also added support for JavaScript and Go. This meant creating language-specific scanning logic.

def scan_javascript(self, code, filename):

    # JavaScript patterns
    # 'const password = "admin123"' → detected!
    # 'eval(cmd)' → detected!


def scan_go(self, code, filename):

    # Go patterns
    # 'password := "admin123"' → detected!
    # 'unsafe.' → detected!

7. Language-Specific Fix Suggestions

Detecting an issue is only half the job. The suggested fix should also make sense for the language being scanned.

# Python fix:

import os

PASSWORD = os.environ.get("PASSWORD")


# JavaScript fix:

const PASSWORD = process.env.PASSWORD;


# Go fix:

import "os"

password := os.Getenv("PASSWORD")

📊 The Cost of Replacing These Packages

Building these replacements took time, but it also showed me how much functionality can be achieved using Python's built-in capabilities.

Package Replacement Time Spent
bandit / pylint / flake8 ast + re 6 hours
click argparse 1 hour
loguru logging 30 minutes
colorama ANSI codes 30 minutes
pydantic dataclasses 1 hour
requests http.server 2 hours
jinja2 F-strings 1 hour
python-dotenv json + os.environ 30 minutes
tqdm Manual sys.stdout 1 hour
pathlib Built-in pathlib 0
Total 10+ substitutions ~13.5 hours

🏆 The Reward

The project achieved the full 16/16 bonus points.

Bonus Points How I Got It
Single File +5 Everything in main.py
Reproducible Build +5 Pure Python, deterministic output
Package Killer +3 Replaces bandit, pylint, and flake8
STDLIB Log +3 10+ standard-library substitutions documented

🎯 Key Takeaways

What I Learned

  1. The standard library is more powerful than you think. Python already provides a surprising amount of functionality.
  2. Dependencies are a convenience, not always a necessity. Understanding the standard library can help you build many tools without additional packages.
  3. Cross-platform issues are real. Testing on different systems is essential.
  4. Writing your own code gives you control. You understand what is happening underneath the abstractions.
  5. The "one command" rule is harder than it looks. Making a project reliable across environments takes real effort.

Why This Matters

  • Security: Fewer third-party dependencies can reduce dependency-related attack surface.
  • Speed: No dependency installation step.
  • Portability: The project can run wherever the required Python version is available.
  • Reliability: Fewer dependency conflicts and version constraints.
  • Learning: You gain a better understanding of what libraries are doing underneath.

📊 Project Stats

800+ Lines of Code
12+ Standard Library Modules
10+ Package Substitutions
16/16 Bonus Points
3 Languages Supported
0 Dependencies

Languages: Python, JavaScript, Go

Time Spent: ~13.5 hours

💡 Final Thoughts

Building a security scanner without any packages felt like the most unnatural thing I've ever done.

Every time I needed to implement something, my muscle memory wanted to reach for pip install.

But forcing myself to work with only the standard library changed the way I think about Python. Instead of immediately searching for a package, I started asking what Python itself could already do.

The hackathon rule of having an empty dependency manifest wasn't simply a restriction. It became an opportunity to understand the tools and abstractions I normally take for granted.

Building without dependencies forces you to ask: "How does this actually work?"

And the answer is:

It works differently than you expected.

🙏 Special Thanks

  • My Team: Diya Karmakar, Krishna Popat, Shyam Makvana, Saurabh Singh Rathore
"Every dependency is a stranger. This time, invite none."
— Zero Dependency Hackathon Motto

Comments

Popular posts from this blog