สร้าง CLI Python จัดการ Task ด้วย Context Manager พร้อม Test และ Logging ง่ายๆ
สวัสดีครับ เพื่อนๆ โปรแกรมเมอร์ทุกคน
วันนี้ผมจะมาแนะนำวิธีสร้าง CLI (Command Line Interface) ง่ายๆ ด้วย Python นะครับ ให้เราสามารถจัดการงานบางอย่างได้ แล้วก็ยังมีการ Test เพื่อความชัวร์ รวมถึงการทำ Logging เพื่อดูว่ามีใครมาทำอะไรกับระบบของเราบ้าง ซึ่งอันนี้สำคัญกับเรื่อง Compliance มากๆ เลยนะครับ
1. โครงสร้าง CLI พื้นฐาน
มาเริ่มจากโครงสร้าง CLI ง่ายๆ ก่อนนะครับ ผมใช้ argparse เข้ามาช่วยแบบนี้นะครับ
# main.py
import argparse
def add_task(task_name):
print(f"Adding task: {task_name}")
# เดี๋ยวเราจะมาเพิ่มโค้ดบันทึกลงไฟล์ทีหลังครับ
def list_tasks():
print("Listing all tasks...")
# เดี๋ยวเราจะมาเพิ่มโค้ดอ่านจากไฟล์ทีหลังครับ
def main():
parser = argparse.ArgumentParser(description="Simple Task Manager CLI.")
subparsers = parser.add_subparsers(dest="command")
# Add command
add_parser = subparsers.add_parser("add", help="Add a new task.")
add_parser.add_argument("task", type=str, help="The name of the task.")
add_parser.set_defaults(func=lambda args: add_task(args.task))
# List command
list_parser = subparsers.add_parser("list", help="List all tasks.")
list_parser.set_defaults(func=lambda args: list_tasks())
args = parser.parse_args()
if args.command:
args.func(args)
else:
parser.print_help()
if __name__ == "__main__":
main()
จากนั้นก็รันแบบนี้นะครับ python main.py add "ซื้อของ" หรือ python main.py list
2. Context Manager สำหรับจัดการไฟล์อย่างปลอดภัย
ทีนี้เราจะมาทำให้การอ่าน-เขียนไฟล์ของเราปลอดภัยมากขึ้น ด้วยการใช้ Context Manager นะครับ ซึ่งมันจะช่วยให้เรามั่นใจได้ว่าไฟล์จะถูกเปิดและปิดอย่างถูกต้องเสมอครับ แม้จะมี Error เกิดขึ้นก็ตาม ผมสร้างคลาส TaskFileHandler แบบนี้นะครับ
# task_manager.py
import json
import os
class TaskFileHandler:
def __init__(self, filename="tasks.json"):
self.filename = filename
def __enter__(self):
if not os.path.exists(self.filename):
with open(self.filename, 'w') as f:
json.dump([], f)
with open(self.filename, 'r+') as f:
self.file = f
try:
self.data = json.load(f)
except json.JSONDecodeError:
self.data = [] # กรณีไฟล์ว่างเปล่าหรือไม่ถูกต้อง
return self.data
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is None: # ถ้าไม่มี error
self.file.seek(0)
json.dump(self.data, self.file, indent=4)
self.file.truncate()
self.file.close()
วิธีนำไปใช้ใน main.py นะครับ:
# ใน main.py: อัปเดตฟังก์ชัน add_task และ list_tasks
from task_manager import TaskFileHandler # เพิ่มบรรทัดนี้
def add_task(task_name):
with TaskFileHandler() as tasks:
tasks.append({"name": task_name, "completed": False})
print(f"Task '{task_name}' added successfully.")
def list_tasks():
with TaskFileHandler() as tasks:
if not tasks:
print("No tasks found.")
return
print("Your tasks:")
for i, task in enumerate(tasks):
status = "[DONE]" if task.get("completed", False) else ""
print(f"{i+1}. {task['name']} {status}")
แบบนี้ เราก็จะมั่นใจได้ว่าไฟล์จะถูกจัดการอย่างถูกต้องเสมอครับ
3. Unit Testing เบื้องต้น เพื่อความมั่นใจ
เพื่อให้แน่ใจว่า Context Manager ของเราทำงานถูกต้อง ผมก็เขียน Unit Test ง่ายๆ ด้วย unittest แบบนี้นะครับ
# test_task_manager.py
import unittest
from unittest.mock import patch, mock_open
from task_manager import TaskFileHandler
import json
import os
class TestTaskFileHandler(unittest.TestCase):
def setUp(self):
# สร้างไฟล์จำลองเพื่อ Test
self.test_filename = "test_tasks.json"
if os.path.exists(self.test_filename):
os.remove(self.test_filename)
def tearDown(self):
# ลบไฟล์จำลองหลัง Test
if os.path.exists(self.test_filename):
os.remove(self.test_filename)
def test_initial_file_creation(self):
with TaskFileHandler(self.test_filename) as tasks:
self.assertEqual(tasks, [])
# ตรวจสอบว่าไฟล์ถูกสร้างและมี []
with open(self.test_filename, 'r') as f:
self.assertEqual(json.load(f), [])
def test_add_task_to_file(self):
with TaskFileHandler(self.test_filename) as tasks:
tasks.append({"name": "Task 1", "completed": False})
# เปิดไฟล์อีกครั้งเพื่อยืนยันว่าข้อมูลถูกบันทึก
with TaskFileHandler(self.test_filename) as tasks_read:
self.assertEqual(len(tasks_read), 1)
self.assertEqual(tasks_read[0]["name"], "Task 1")
def test_empty_or_corrupt_file_handling(self):
# สร้างไฟล์เปล่า
with open(self.test_filename, 'w') as f:
f.write("")
with TaskFileHandler(self.test_filename) as tasks:
self.assertEqual(tasks, []) # ควรคืนค่าเป็น list เปล่า
if __name__ == '__main__':
unittest.main()
จากนั้นก็รัน python -m unittest test_task_manager.py นะครับ เพื่อดูผลการ Test ของเรา
4. Logging เพื่อ Compliance และการตรวจสอบ
สุดท้าย เรื่อง Logging นี่สำคัญมากนะครับ โดยเฉพาะเรื่อง Compliance เราต้องรู้ว่ามีใครมา Add หรือ Modify Task บ้าง ผมใช้ logging module แบบนี้เพื่อเก็บ Log การกระทำต่างๆ นะครับ
# logger_config.py
import logging
def setup_logging():
logging.basicConfig(
filename='task_manager.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
# สำหรับให้แสดงใน console ด้วยนะครับ
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
logging.getLogger().addHandler(console_handler)
# ใน main.py ของเรานะครับ (เพิ่มส่วนนี้)
# import logging
# from logger_config import setup_logging
#
# setup_logging()
# logger = logging.getLogger(__name__)
#
# def add_task(task_name):
# with TaskFileHandler() as tasks:
# tasks.append({"name": task_name, "completed": False})
# logger.info(f"Task '{task_name}' added by user.") # เพิ่มบรรทัดนี้
#
# def list_tasks():
# with TaskFileHandler() as tasks:
# if not tasks:
# logger.info("No tasks found for user listing.") # เพิ่มบรรทัดนี้
# print("No tasks found.")
# return
# logger.info("Tasks listed by user.") # เพิ่มบรรทัดนี้
# print("Your tasks:")
# for i, task in enumerate(tasks):
# status = "[DONE]" if task.get("completed", False) else ""
# print(f"{i+1}. {task['name']} {status}")
เมื่อรันโปรแกรมแล้ว ไฟล์ task_manager.log ก็จะมีบันทึกการกระทำต่างๆ ของผู้ใช้งาน แบบนี้ช่วยให้เราตรวจสอบย้อนหลังได้สบายเลยครับ เรื่องความปลอดภัยและการ Audit นี่สำคัญมากๆ เลยนะครับ
เป็นไงบ้างครับ เพื่อนๆ? จากตัวอย่างนี้ เราก็ได้เห็นแล้วนะครับว่าการสร้าง CLI Tool ง่ายๆ ด้วย Python พร้อมกับ Context Manager, Unit Test และ Logging นี่ไม่ได้ยากเลยใช่ไหมครับ แล้วก็ยังช่วยให้โค้ดของเรามีคุณภาพมากขึ้น จัดการได้ง่ายขึ้น และตอบโจทย์เรื่อง Compliance ได้อีกด้วยนะครับ ลองเอาไปปรับใช้กับโปรเจกต์ของเพื่อนๆ ดูนะครับ ผมหวังว่าบทความนี้จะเป็นประโยชน์กับเพื่อนๆ ทุกคนนะครับ
อ้างอิง: * Python argparse documentation * Python contextlib (for context managers) * Python unittest documentation * Python logging documentation