2025, Oct 16 08:31
xml.etree के साथ JUnit‑शैली XML बनाते समय int एट्रिब्यूट से होने वाली TypeError का समाधान
Python xml.etree.ElementTree में JUnit‑स्टाइल XML लिखते समय int एट्रिब्यूट से होने वाली TypeError का कारण, न्यूनतम उदाहरण और स्ट्रिंग कास्टिंग से समाधान.
Python में xml.etree के साथ JUnit-शैली का XML बनाते समय, एक दिखने में साधारण-सी बात पूरी सीरियलाइज़ेशन को तोड़ सकती है: ऐट्रिब्यूट के मान स्ट्रिंग होने चाहिए। अगर किसी ऐट्रिब्यूट का मान int है, तो write चरण TypeError के साथ फेल हो जाता है। नीचे उसी फेल्योर मोड का सटीक उदाहरण और वह न्यूनतम समाधान दिया है जो XML जनरेशन को बहाल करता है।
समस्या की रूपरेखा
उद्देश्य ऐसा XML स्ट्रक्चर बनाना है:
<?xml version="1.0" encoding="UTF-8"?>
<testsuites tests="65" failures="0" disabled="0" errors="0" name="AllTests">
  <testsuite name="Tests" tests="65" failures="0" disabled="0" skipped="0" errors="0">
    <testcase name="TestInit" file="" status="run" result="completed" />
    <testcase name="TestAlways" file="" status="run" result="completed" />
  </testsuite>
  ...
</testsuites>
यहाँ एक Python स्निपेट है जो इन-मेमेंरी ऑब्जेक्ट्स से XML बनाता है और फिर उसे डिस्क पर लिखता है। यही पैटर्न फेल्योर पैदा करता है।
import xml.etree.cElementTree as ET
class CaseNode:
    def __init__(self, label, outcome):
        self.name = label
        self.result = outcome
    def __str__(self):
        return f"Test Case: {self.name}, Status: {self.result}"
class SuiteNode:
    def __init__(self, title):
        self.name = title
        self.test_case_list = []
    def __str__(self):
        return f"Test Suite: {self.name}"
def run():
    print("Hello from html-test-report-generator!")
    suites = build_html_report()
    for s in suites:
        print(s)
        for c in s.test_case_list:
            print(c)
        print()
    write_xml_report(suites)
def build_html_report():
    # SuiteNode ऑब्जेक्ट्स की सूची बनाता है और उसे कॉलर को वापस देता है
    pass
def write_xml_report(suites):
    root_suites = ET.Element(
        "testsuites",
        tests=len(suites),          # <-- समस्या: int ऐट्रिब्यूट
        failures="0",
        disabled="0",
        errors="0",
        name="AllTests",
    )
    for s in suites:
        suite_node = ET.SubElement(
            root_suites,
            "testsuite",
            name=s.name,
            tests=len(s.test_case_list),  # <-- समस्या: int ऐट्रिब्यूट
            failures="0",
            disabled="0",
            skipped="0",
            errors="0",
        )
        for c in s.test_case_list:
            ET.SubElement(
                suite_node,
                "testcase",
                name=c.name,
                file="",
                line="",
                status="run",
                result=c.result,
            )
    tree = ET.ElementTree(root_suites)
    tree.write("filename.xml")
यह कोड चलाने पर सीरियलाइज़ेशन के वक्त यह अपवाद आता है:
TypeError: cannot serialize 23 (type int)
कभी-कभी इसके साथ ऐसा संदेश भी दिख सकता है:
AttributeError: 'str' object has no attribute 'write'
दूसरा संदेश इसलिए आता है क्योंकि write ऑपरेशन एक अवैध ट्री को सीरियलाइज़ करने की कोशिश करते हुए अंदरूनी हैंडलिंग से होकर ऊपर तक बबल होता है। असली समस्या TypeError है: ElementTree किसी integer ऐट्रिब्यूट को सीरियलाइज़ करने से इनकार करता है।
असल में गड़बड़ी कहाँ है
xml.etree में एलिमेंट के ऐट्रिब्यूट्स स्ट्रिंग होते हैं। जब आप किसी ऐट्रिब्यूट के रूप में Python का int पास करते हैं (जैसे, tests=len(...)), तो सीरियलाइज़र उसे अपने आप कन्वर्ट नहीं करता और TypeError: cannot serialize 23 (type int) उठाता है। फेल्योर write के समय होता है, जब ElementTree इन-मेमेंरी स्ट्रक्चर को बाइट्स में फ्लैटन करता है। क्योंकि ट्री अपनी ही नियमावली के अनुसार वैध नहीं है, सीरियलाइज़ेशन रुक जाता है।
इसे आप उसी गड़बड़ी को दोहराने वाले एक छोटे से उदाहरण से देख सकते हैं:
import xml.etree.cElementTree as ET
root = ET.Element("testsuites", tests=23)  # int ऐट्रिब्यूट
ET.ElementTree(root).write("filename.xml")  # TypeError उठता है
और स्ट्रिंग फोर्स करने पर यह सफल होता है:
import xml.etree.cElementTree as ET
root = ET.Element("testsuites", tests=str(23))  # स्ट्रिंग ऐट्रिब्यूट
ET.ElementTree(root).write("filename.xml")      # ठीक
XML जनरेशन को ठीक करना
सुधार सीधा है: Element या SubElement को देने से पहले न्यूमेरिक ऐट्रिब्यूट्स को स्ट्रिंग में बदलें। मूल फ्लो में, इसका मतलब tests ऐट्रिब्यूट को दो जगह सुधारना है।
import xml.etree.cElementTree as ET
class CaseNode:
    def __init__(self, label, outcome):
        self.name = label
        self.result = outcome
    def __str__(self):
        return f"Test Case: {self.name}, Status: {self.result}"
class SuiteNode:
    def __init__(self, title):
        self.name = title
        self.test_case_list = []
    def __str__(self):
        return f"Test Suite: {self.name}"
def run():
    print("Hello from html-test-report-generator!")
    suites = build_html_report()
    for s in suites:
        print(s)
        for c in s.test_case_list:
            print(c)
        print()
    write_xml_report(suites)
def build_html_report():
    # SuiteNode ऑब्जेक्ट्स की सूची बनाता है और उसे कॉलर को वापस देता है
    pass
def write_xml_report(suites):
    root_suites = ET.Element(
        "testsuites",
        tests=str(len(suites)),     # स्ट्रिंग में बदलें
        failures="0",
        disabled="0",
        errors="0",
        name="AllTests",
    )
    for s in suites:
        suite_node = ET.SubElement(
            root_suites,
            "testsuite",
            name=s.name,
            tests=str(len(s.test_case_list)),  # स्ट्रिंग में बदलें
            failures="0",
            disabled="0",
            skipped="0",
            errors="0",
        )
        for c in s.test_case_list:
            ET.SubElement(
                suite_node,
                "testcase",
                name=c.name,
                file="",
                line="",
                status="run",
                result=c.result,
            )
    tree = ET.ElementTree(root_suites)
    tree.write("filename.xml")
काम करते-करते स्ट्रक्चर की जाँच कैसे करें
अगर आप ट्री के बनते समय उसका ढांचा देखना चाहते हैं, तो बीच-बीच में उसे प्रिंट करें। हर Element या SubElement बनाने के बाद testsuites.dump() कॉल करें ताकि ऐट्रिब्यूट्स और नेस्टिंग दिख सके। साथ ही जिन मानों से ऐट्रिब्यूट बन रहे हैं, उनके आसपास print(), print(type(...)) और print(len(...)) लगाएँ। यह हल्की-फुल्की “print डिबगिंग” जल्द पता लगा देती है कि कहाँ कोई non-string ऐट्रिब्यूट में घुस गया है।
यह बारीकी क्यों अहम है
सीरियलाइज़ेशन की सीमा पर XML टूलिंग डेटा टाइप्स को लेकर सख्त होती है। ElementTree में ऐट्रिब्यूट्स साधारण स्ट्रिंग होते हैं; वे अपने आप कन्वर्ट नहीं होते। int जैसे छोटे असंगतियाँ ऐट्रिब्यूट में आने पर ऐसे कन्फ्यूज़िंग स्टैक ट्रेस दे सकती हैं जो कॉल चेन में ऊपर कहीं write फेल्योर का संकेत देती हैं। यह पहचानना कि सीरियलाइज़र int ऐट्रिब्यूट को ठुकरा रहा है, आपको स्रोत पर ही सुधार करने देता है और भटकाव से बचाता है।
मुख्य बातें
xml.etree से XML बनाते समय, जिन ऐट्रिब्यूट्स को न्यूमेरिक मान दे रहे हैं, उन्हें स्पष्ट रूप से स्ट्रिंग में कास्ट करें। यह हर निर्माण बिंदु पर करें, जैसे ET.Element(...) और ET.SubElement(...). यदि सीरियलाइज़ेशन फेल हो, तो एक छोटे उदाहरण तक सीमित करें और जल्दी से ऐट्रिब्यूट टाइप्स की पुष्टि print डिबगिंग से या stdout पर एलिमेंट ट्री डंप करके करें। इससे XML पाइपलाइन अनुमानित रहती है और आउटपुट डाउनस्ट्रीम टूल्स के अनुरूप बनता है।