2014-10-15 23:22:54 +00:00
# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# Make coding more python3-ish
from __future__ import ( absolute_import , division , print_function )
__metaclass__ = type
2015-10-26 21:23:09 +00:00
from yaml . constructor import Constructor , ConstructorError
from yaml . nodes import MappingNode
2015-04-02 19:37:02 +00:00
from ansible . parsing . yaml . objects import AnsibleMapping , AnsibleSequence , AnsibleUnicode
2016-02-04 15:58:50 +00:00
from ansible . vars . unsafe_proxy import wrap_var
2014-10-07 20:04:42 +00:00
2015-10-26 21:23:09 +00:00
try :
from __main__ import display
except ImportError :
from ansible . utils . display import Display
display = Display ( )
2014-10-07 20:04:42 +00:00
class AnsibleConstructor ( Constructor ) :
2014-11-05 14:00:00 +00:00
def __init__ ( self , file_name = None ) :
self . _ansible_file_name = file_name
super ( AnsibleConstructor , self ) . __init__ ( )
2014-10-07 20:04:42 +00:00
def construct_yaml_map ( self , node ) :
data = AnsibleMapping ( )
yield data
value = self . construct_mapping ( node )
data . update ( value )
2015-04-01 23:25:37 +00:00
data . ansible_pos = self . _node_position_info ( node )
2014-10-07 20:04:42 +00:00
def construct_mapping ( self , node , deep = False ) :
2015-10-27 21:19:39 +00:00
# Most of this is from yaml.constructor.SafeConstructor. We replicate
# it here so that we can warn users when they have duplicate dict keys
# (pyyaml silently allows overwriting keys)
2015-10-26 21:23:09 +00:00
if not isinstance ( node , MappingNode ) :
raise ConstructorError ( None , None ,
" expected a mapping node, but found %s " % node . id ,
node . start_mark )
self . flatten_mapping ( node )
mapping = AnsibleMapping ( )
2015-10-27 21:19:39 +00:00
# Add our extra information to the returned value
mapping . ansible_pos = self . _node_position_info ( node )
2015-10-26 21:23:09 +00:00
for key_node , value_node in node . value :
key = self . construct_object ( key_node , deep = deep )
try :
hash ( key )
except TypeError as exc :
raise ConstructorError ( " while constructing a mapping " , node . start_mark ,
" found unacceptable key ( %s ) " % exc , key_node . start_mark )
2014-11-05 14:00:00 +00:00
2015-10-26 21:23:09 +00:00
if key in mapping :
2016-02-26 16:59:49 +00:00
display . warning ( u ' While constructing a mapping from {1} , line {2} , column {3} , found a duplicate dict key ( {0} ). Using last defined value only. ' . format ( key , * mapping . ansible_pos ) )
2015-10-26 21:23:09 +00:00
value = self . construct_object ( value_node , deep = deep )
mapping [ key ] = value
return mapping
2014-10-07 20:04:42 +00:00
2016-02-04 15:58:50 +00:00
def construct_yaml_str ( self , node , unsafe = False ) :
2015-03-31 04:48:28 +00:00
# Override the default string handling function
# to always return unicode objects
value = self . construct_scalar ( node )
2015-04-01 23:25:37 +00:00
ret = AnsibleUnicode ( value )
2015-03-31 04:48:28 +00:00
2015-04-01 23:25:37 +00:00
ret . ansible_pos = self . _node_position_info ( node )
2015-03-31 04:48:28 +00:00
2016-02-04 15:58:50 +00:00
if unsafe :
ret = wrap_var ( ret )
2015-04-01 21:54:22 +00:00
return ret
2015-03-31 04:48:28 +00:00
2015-04-02 19:37:02 +00:00
def construct_yaml_seq ( self , node ) :
data = AnsibleSequence ( )
yield data
data . extend ( self . construct_sequence ( node ) )
data . ansible_pos = self . _node_position_info ( node )
2016-02-04 15:58:50 +00:00
def construct_yaml_unsafe ( self , node ) :
return self . construct_yaml_str ( node , unsafe = True )
2015-04-01 23:25:37 +00:00
def _node_position_info ( self , node ) :
# the line number where the previous token has ended (plus empty lines)
2015-04-10 11:01:18 +00:00
# Add one so that the first line is line 1 rather than line 0
2015-04-01 23:25:37 +00:00
column = node . start_mark . column + 1
line = node . start_mark . line + 1
# in some cases, we may have pre-read the data and then
# passed it to the load() call for YAML, in which case we
# want to override the default datasource (which would be
# '<string>') to the actual filename we read in
datasource = self . _ansible_file_name or node . start_mark . name
return ( datasource , line , column )
2014-10-07 20:04:42 +00:00
AnsibleConstructor . add_constructor (
u ' tag:yaml.org,2002:map ' ,
AnsibleConstructor . construct_yaml_map )
AnsibleConstructor . add_constructor (
u ' tag:yaml.org,2002:python/dict ' ,
AnsibleConstructor . construct_yaml_map )
2015-03-31 04:48:28 +00:00
AnsibleConstructor . add_constructor (
u ' tag:yaml.org,2002:str ' ,
AnsibleConstructor . construct_yaml_str )
AnsibleConstructor . add_constructor (
u ' tag:yaml.org,2002:python/unicode ' ,
AnsibleConstructor . construct_yaml_str )
2015-04-02 19:37:02 +00:00
AnsibleConstructor . add_constructor (
u ' tag:yaml.org,2002:seq ' ,
AnsibleConstructor . construct_yaml_seq )
2016-02-04 15:58:50 +00:00
AnsibleConstructor . add_constructor (
u ' !unsafe ' ,
AnsibleConstructor . construct_yaml_unsafe )