How to read a python tuple using PyYAML?
<p>I have the following YAML file named <code>input.yaml</code>:</p>

<pre class="lang-yaml prettyprint-override"><code>cities:
  1: [0,0]
  2: [4,0]
  3: [0,4]
  4: [4,4]
  5: [2,2]
  6: [6,2]
highways:
  - [1,2]
  - [1,3]
  - [1,5]
  - [2,4]
  - [3,4]
  - [5,4]
start: 1
end: 4
</code></pre>

<p>I'm loading it using PyYAML and printing the result as follows:</p>

<pre class="lang-py prettyprint-override"><code>import yaml

f = open("input.yaml", "r")
data = yaml.load(f)
f.close()

print(data)
</code></pre>

<p>The result is the following data structure:</p>

<pre class="lang-py prettyprint-override"><code>{ 'cities': { 1: [0, 0]
            , 2: [4, 0]
            , 3: [0, 4]
            , 4: [4, 4]
            , 5: [2, 2]
            , 6: [6, 2]
            }
, 'highways': [ [1, 2]
              , [1, 3]
              , [1, 5]
              , [2, 4]
              , [3, 4]
              , [5, 4]
              ]
, 'start': 1
, 'end': 4
}
</code></pre>

<p>As you can see, each city and highway is represented as a list. However, I want them to be represented as a tuple. Hence, I manually convert them into tuples using comprehensions:</p>

<pre class="lang-py prettyprint-override"><code>import yaml

f = open("input.yaml", "r")
data = yaml.load(f)
f.close()

data["cities"] = {k: tuple(v) for k, v in data["cities"].items()}
data["highways"] = [tuple(v) for v in data["highways"]]

print(data)
</code></pre>

<p>However, this seems like a hack. Is there some way to instruct PyYAML to directly read them as tuples instead of lists?</p>

<p>Depending on where your YAML input comes from your "hack" is a good solution, especially if you would use <code>yaml.safe_load()</code> instead of the unsafe <code>yaml.load()</code>. If only the "leaf" sequences in your YAML file need to be tuples you can do Â¹:</p>

<pre><code>import pprint
import ruamel.yaml
from ruamel.yaml.constructor import SafeConstructor


def construct_yaml_tuple(self, node):
    seq = self.construct_sequence(node)
    # only make "leaf sequences" into tuples, you can add dict 
    # and other types as necessary
    if seq and isinstance(seq[0], (list, tuple)):
        return seq
    return tuple(seq)

SafeConstructor.add_constructor(
    u'tag:yaml.org,2002:seq',
    construct_yaml_tuple)

with open('input.yaml') as fp:
    data = ruamel.yaml.safe_load(fp)
pprint.pprint(data, width=24)
</code></pre>

<p>which prints:</p>

<pre><code>{'cities': {1: (0, 0),
            2: (4, 0),
            3: (0, 4),
            4: (4, 4),
            5: (2, 2),
            6: (6, 2)},
 'end': 4,
 'highways': [(1, 2),
              (1, 3),
              (1, 5),
              (2, 4),
              (3, 4),
              (5, 4)],
 'start': 1}
</code></pre>

<p>if you then need to process more material where sequence need to be "normal" lists again, use:</p>

<pre><code>SafeConstructor.add_constructor(
    u'tag:yaml.org,2002:seq',
    SafeConstructor.construct_yaml_seq)
</code></pre>

<hr>

<p>Â¹ <sub>This was done using <a href="https://pypi.python.org/pypi/ruamel.yaml" rel="nofollow">ruamel.yaml</a> a YAML 1.2 parser, of which I am the author. You should be able to do same with the older PyYAML if you only ever need to support YAML 1.1 and/or cannot upgrade for some reason</sub></p>

python yaml pyyaml