2025, Oct 15 15:32

ruamel.yaml में YAML anchors, aliases और merge key को बिना बदले कैसे रखें

जानें कैसे ruamel.yaml से YAML anchors, aliases और merge key को बिना बदले अपडेट करें: single-item sequence की दिक्कत, सही anchoring और merges के उपाय.

YAML के anchors और aliases डुप्लिकेशन से बचने का एक शक्तिशाली तरीका हैं, लेकिन जब कोई टूल उन्हें अनपेक्षित रूप से फिर से लिख दे, तो वे नाज़ुक हो सकते हैं। अगर आप merges और anchors पर निर्भर किसी दस्तावेज़ को अपडेट करने के लिए ruamel.yaml का इस्तेमाल करते हैं, तो आपको id001 जैसे स्वतः-जनित alias नाम दिख सकते हैं या anchors अलग-अलग नोड्स पर खिसक सकते हैं। यहां समझिए कि यह क्यों होता है और दस्तावेज़ बदलते हुए भी anchors को बिना छेड़े कैसे रखा जाए।

समस्या की रूपरेखा

मान लीजिए आपको YAML कॉन्फ़िग में बदलाव करना है, लेकिन टिप्पणियाँ और संरचना जस की तस रखनी है। नीचे दिया गया Python स्निपेट clients मैपिंग में एक नया client जोड़ता है:

import ruamel.yaml
parser = ruamel.yaml.YAML()
parser.preserve_quotes = True
parser.indent(mapping=2, sequence=4, offset=2)
config_text = """---
# Some config file..
# This is the main clients config
clients:
  foobar1:
    name: foo Bar 1
    auth: false
    count: 1290
  barboz:
    name: BarraBoz
    auth: true
    count: 19
uids_default: &uids_default
  - attribute_email: mail
    datafile: vars1
uids:
  - <<: *uids_default
    skip: true
  - <<: *uids_default
    issuer: http://foo.bar
"""
new_client = {"adfs": 124423423, "123": "adsfadsfasdf", "name": "Dirk Van helst"}
doc = parser.load(config_text)
doc["clients"].insert(len(doc["clients"]), "vanhelst", new_client)
with open("/tmp/out.yml", "w") as out_f:
    parser.dump(doc, out_f)

एंट्री जुड़ जाती है और टिप्पणियाँ भी बनी रहती हैं, लेकिन आउटपुट में स्वतः-निर्मित aliases आ जाते हैं और anchors खिसक जाते हैं—जैसे मूल anchor को id001 से बदल देना और उसे sequence से निकालकर उसके भीतर वाले mapping पर ले जाना।

असल में गड़बड़ी कहाँ होती है

यहाँ एक anchor और दो aliases हैं। anchor एक ऐसी sequence से जुड़ा है जिसमें ठीक एक mapping है। उसी sequence को बाद में << merge key के मान के रूप में इस्तेमाल किया जाता है। ruamel.yaml जब merges को संरक्षित करता है, तो वह single‑element sequence को flatten कर देता है; नतीजतन anchor sequence से हटकर उसके अंदर वाली mapping पर चला जाता है। यह व्यवहार merge key के उस हैंडलिंग से आता है जो anchored sequences के aliases को सही ढंग से नहीं संभालती। अगर sequence में दो तत्व होते, तो merge प्रविष्टि merge key के बाद aliases की सूची के रूप में दिखाई देती, जैसे:

  - <<: [*id0001, *id0002]

मौजूदा स्थिति में, single-item sequence flatten हो जाती है और ऐसा लगता है कि anchor “छेड़ा” गया है।

समाधान

यदि आप उस वैकल्पिक सूची का उपयोग नहीं कर रहे हैं, तो uids_default के चारों ओर लगी अनावश्यक sequence हटा दें। mapping को सीधे anchor करें और merge को उसी mapping की ओर निर्देशित रखें। यह बदलाव करने पर ruamel.yaml अपेक्षित रूप से anchors और aliases को जस का तस रखता है।

import sys
import ruamel.yaml
parser = ruamel.yaml.YAML()
parser.preserve_quotes = True
parser.indent(mapping=2, sequence=4, offset=2)
fixed_yaml = """# Some config file..
# This is the main clients config
clients:
  foobar1:
    name: foo Bar 1
    auth: false
    count: 1290
  barboz:
    name: BarraBoz
    auth: true
    count: 19
uids_default: &uids_default
    attribute_email: mail
    datafile: vars1
uids:
  - <<: *uids_default
    skip: true
  - <<: *uids_default
    issuer: http://foo.bar
"""
new_client = {"adfs": 124423423, "123": "adsfadsfasdf", "name": "Dirk Van helst"}
doc = parser.load(fixed_yaml)
doc["clients"].insert(len(doc["clients"]), "vanhelst", new_client)
parser.dump(doc, sys.stdout)

नतीजे में बना YAML anchors और merges को अक्षुण्ण रखता है:

# Some config file..
# This is the main clients config
clients:
  foobar1:
    name: foo Bar 1
    auth: false
    count: 1290
  barboz:
    name: BarraBoz
    auth: true
    count: 19
  vanhelst:
    adfs: 124423423
    '123': adsfadsfasdf
    name: Dirk Van helst
uids_default: &uids_default
  attribute_email: mail
  datafile: vars1
uids:
  - <<: *uids_default
    skip: true
  - <<: *uids_default
    issuer: http://foo.bar

यदि आपको कई mappings को merge करना हो, तो हर mapping को अलग‑अलग anchor दें और << merge key के मान के रूप में aliases की sequence रखें।

यह क्यों मायने रखता है

YAML anchors, aliases और merge key पर आधारित कॉन्फ़िग अक्सर ऑटोमेशन, CI पाइपलाइनों और उस कोड में जाते हैं जो स्थिर संरचना पर निर्भर होता है। anchors का चुपचाप id001‑शैली के aliases में बदल जाना या उनका इधर‑उधर हो जाना diffs को शोरगुल भरा और नाज़ुक बना देता है, और कभी‑कभी उन डाउनस्ट्रीम टूल्स को भ्रमित कर देता है जो एक तय संरचना की उम्मीद करते हैं। single‑element sequences के बजाय anchored mappings पर merges को टिकाए रखना ऐसे rewrite से बचाता है।

टिप्पणियाँ और आगे की दिशा

anchored sequences के aliases के लिए ruamel.yaml की merge हैंडलिंग में यह व्यवहार एक बग माना गया है। इसे अगले रिलीज़ (> 0.18.14) में ठीक किया जाना चाहिए। साथ ही, फ़ाइल एक्सटेंशन पर ध्यान दें: आप अभी भी .yml उपयोग कर रहे हैं; yaml.org पर YAML FAQ देखें और उसकी सिफारिश का पालन करें।

निष्कर्ष

ruamel.yaml के साथ merges का उपयोग करते समय, single‑item sequence को anchor करके उसी को merge करने से बचें। mapping को सीधे anchor करें ताकि merge key किसी सूची नहीं, बल्कि mapping की ओर इशारा करे। यदि कई mappings को merge करना हो, तो अलग‑अलग anchors का उपयोग करें और << के बाद aliases की sequence दें। इससे anchors स्थिर रहते हैं, टिप्पणियाँ सुरक्षित रहती हैं और serialisation के दौरान ruamel.yaml id001‑शैली के aliases जोड़ने से बचता है।

यह लेख StackOverflow के एक प्रश्न (लेखक: Dick Visser) और Anthon के उत्तर पर आधारित है।