Python difflib: อ่านการเปลี่ยนบรรทัดด้วย opcode ก่อนทำ review
เวลา config เปลี่ยนเพียงหนึ่งบรรทัด การพิมพ์ไฟล์ก่อนและหลังออกมาทั้งก้อนทำให้คนตรวจหลงทางครับ difflib.SequenceMatcher ช่วยบอกว่าแต่ละช่วง เท่าเดิม, เพิ่ม, ลบ หรือ แทนที่ โดยไม่ต้องเขียนตัวเทียบเอง เหมือนวางกระดาษสองแผ่นบนโต๊ะแล้วชี้เฉพาะแถบที่ขยับ
get_opcodes() คืน tuple (tag, i1, i2, j1, j2) ช่วง index แบบปลายไม่รวมของข้อมูลเก่าและใหม่ เราจึงสร้าง review summary ที่ยังอ้างกลับไปยังต้นฉบับได้ ตัวอย่างนี้ปิด autojunk เพื่อให้พฤติกรรมบนข้อมูลสั้นอ่านง่าย และตรวจทั้งกรณีเพิ่มกับแทนที่จริง
import unittest
from difflib import SequenceMatcher
def changes(before: list[str], after: list[str]):
matcher = SequenceMatcher(None, before, after, autojunk=False)
return [
(tag, before[i1:i2], after[j1:j2])
for tag, i1, i2, j1, j2 in matcher.get_opcodes()
if tag != "equal"
]
class DiffTests(unittest.TestCase):
def test_insert_and_replace(self):
old = ["host=local", "retry=2"]
new = ["host=local", "retry=3", "timeout=5"]
self.assertEqual(changes(old, new), [
("replace", ["retry=2"], ["retry=3", "timeout=5"])
])
def test_equal_inputs(self):
self.assertEqual(changes(["same"], ["same"]), [])
if __name__ == "__main__":
unittest.main()
อ่านผลอย่างไร
ตัว matcher อาจรวมการแก้บรรทัดและการเพิ่มบรรทัดข้างกันเป็น replace เดียว นี่คือการจัดกลุ่มเพื่อแสดง diff ไม่ใช่ความหมายเชิงธุรกิจของ config หากต้องบอกว่า retry เปลี่ยนและ timeout เพิ่ม ต้อง parse key แล้วเทียบ key แยกอีกชั้น การรีวิวไฟล์ JSON, YAML หรือ policy สำคัญไม่ควรใช้ text diff เป็นตัวตัดสินความถูกต้องเพียงตัวเดียว
ratio() ให้ตัวเลขความคล้าย แต่ไม่ใช่หลักฐานว่าเนื้อหาปลอดภัยหรือทำงานเหมือนเดิม บรรทัดที่เปลี่ยนจาก enabled=false เป็น enabled=true อาจมีคะแนนสูงมากแต่เปลี่ยนพฤติกรรมทั้งหมด สำหรับการแจ้งเตือน ให้แสดง opcode และค่าจริงแก่ผู้รีวิว พร้อมทดสอบ schema หรือ unit test ที่เกี่ยวข้อง
ถ้าข้อมูลใหญ่มากหรือเป็นลำดับที่มีสมาชิกซ้ำจำนวนมาก ให้ benchmark กับข้อมูลจริงก่อน เพราะ matcher ออกแบบเพื่อ diff ที่มนุษย์อ่านง่าย มิใช่ตัวพิสูจน์ความเท่ากันที่เร็วที่สุด และอย่าคัดลอกข้อมูลลับลง report โดยไม่ redaction ครับ
Recap
- ใช้
get_opcodes()เพื่อหาช่วงที่แก้และเก็บ index กลับต้นฉบับ - ทดสอบกรณีเท่ากัน, เพิ่ม, ลบ และแทนที่
- แยก text diff ออกจากการตรวจ semantic ของข้อมูล
- ไม่ใช้ similarity score แทน code review หรือ test