Introduction
Welcome to my first blog post! In this project, I tackled an exciting coding challenge from John Cricket. The challenge was to create a custom wc
command-line tool named ccwc
using Python and the Typer library. This tool mimics the functionality of the wc
command in Linux, providing counts of bytes, lines, words, and characters in a file.
Learnings and Key Takeaways
Before diving into the implementation, here are the key learnings and takeaways from this project:
CLI Tool Development: Building command-line tools with Typer is efficient and straightforward.
File Handling in Python: Understanding how to read and process files and standard input in Python.
Project Configuration: Using
pyproject.toml
to manage project metadata and dependencies.Error Handling: Importance of providing user-friendly error messages.
Tech Stack
Language: Python
Library: Typer
Why Python? It's simple, readable, and powerful for scripting and automation.
Why Typer? It makes building command-line interfaces straightforward and efficient.
Project Structure
Here's the project structure for ccwc
:
ccwc/
├── ccwc/
│ ├── __init__.py
│ ├── main.py
├── tests/
│ ├── test_ccwc.py
├── test.txt
├── pyproject.toml
├── README.md
Setting Up Typer and Basic Structure
First, let's set up Typer and create the basic structure of our command-line tool:
import typer
import sys
from typing import List
app = typer.Typer()
Counting Bytes
The following function counts the bytes in a file:
def count_bytes(file_path: str = None):
if file_path:
with open(file_path, 'rb') as file:
content = file.read()
else:
content = sys.stdin.buffer.read()
byte_count = len(content)
return byte_count
Counting Lines
To count lines in a file, we use:
def count_lines(file_path: str = None):
if file_path:
with open(file_path, 'r') as file:
lines = file.readlines()
else:
lines = sys.stdin.read().splitlines()
line_count = len(lines)
return line_count
Counting Words
The function to count words is:
def count_words(file_path: str = None):
if file_path:
with open(file_path, 'r') as file:
content = file.read()
else:
content = sys.stdin.read()
words = content.split()
word_count = len(words)
return word_count
Counting Characters
For counting characters, we use:
def count_chars(file_path: str = None):
if file_path:
with open(file_path, 'r', encoding='utf-8') as file:
content = file.read()
else:
content = sys.stdin.read()
char_count = len(content)
return char_count
Main Command and Options
Setting up the main command and handling multiple options with Typer:
@app.command()
def main(
files: List[str] = typer.Argument(None, help="The files to process"),
c: bool = typer.Option(False, "--bytes", "-c", help="Print the byte counts"),
l: bool = typer.Option(False, "--lines", "-l", help="Print the newline counts"),
w: bool = typer.Option(False, "--words", "-w", help="Print the word counts"),
m: bool = typer.Option(False, "--chars", "-m", help="Print the character counts"),
version: bool = typer.Option(False, "--version", help="Show the version and exit")
):
if version:
typer.echo("ccwc version 0.1.0")
raise typer.Exit()
if not files:
files = [None]
for file in files:
if c:
byte_count = count_bytes(file)
print(f"{byte_count:8} {'-' if file is None else file}")
elif l:
line_count = count_lines(file)
print(f"{line_count:8} {'-' if file is None else file}")
elif w:
word_count = count_words(file)
print(f"{word_count:8} {'-' if file is None else file}")
elif m:
char_count = count_chars(file)
print(f"{char_count:8} {'-' if file is None else file}")
else:
line_count = count_lines(file)
word_count = count_words(file)
byte_count = count_bytes(file)
print(f"{line_count:8} {word_count:8} {byte_count:8} {'-' if file is None else file}")
if __name__ == "__main__":
app()
Understandingpyproject.toml
The pyproject.toml
file is crucial for configuring your project. Here's a brief overview:
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "ccwc"
version = "0.1.0"
description = "A wc-like tool for counting lines, words, bytes, and characters in files"
readme = "README.md"
license = {text = "MIT"}
authors = [
{name = "Your Name", email = "your.email@example.com"}
]
dependencies = [
"typer[all]>=0.4.0"
]
[project.scripts]
ccwc = "ccwc.main:app"
[build-system]: Specifies the tools required to build the project.
[project]: Contains metadata about the project like name, version, description, authors, and dependencies.
[project.scripts]: Defines command-line scripts for the project.
Installing and Testing
To install and test your tool:
Create a Virtual Environment:
python -m venv venv source venv/bin/activate # On macOS and Linux # venv\Scripts\activate # On Windows
Install the Package:
pip install --upgrade pip setuptools wheel pip install .
Verify Installation:
codeccwc --help ccwc --version ccwc -c test.txt ccwc -l test.txt ccwc -w test.txt ccwc -m test.txt ccwc test.txt cat test.txt | ccwc -l
Future Enhancements
Future Enhancements:
Add more options and functionalities, such as excluding specific file types or including subdirectories.
Improve error handling to provide more user-friendly messages.
Optimize performance for handling large files more efficiently.
Implement additional features like parallel processing for multiple files.
Conclusion
This project was a fantastic learning experience, and I'm excited to share it with you all. Whether you're a beginner looking to enhance your Python skills or an experienced developer seeking new insights, I hope you find this post helpful.
Thank you for reading! If you have any questions or suggestions, please leave a comment below.
Watch the YouTube Video:Creating a Custom wc
Command-Line Tool with Python and Typer
GitHub Repository:https://github.com/Nik-Jain/cc_ccwc
Don't forget to like, subscribe, and share your thoughts!