# ToDo: # # - Figure out Bless/Blessed # - Document the STACK and CONFIGURATION package YAML; use 5.005003; use strict; use warnings; use Carp; use YAML::Base -base; $YAML::VERSION = '0.99_01'; @YAML::EXPORT = qw'Dump Load'; @YAML::EXPORT_OK = qw'DumpFile LoadFile freeze thaw'; field dumper_class => -init => '$YAML::DumperClass || "YAML::Dumper"'; field dumper => -init => '$self->create("dumper")'; field loader_class => -init => '$YAML::LoaderClass || "YAML::Loader"'; field loader => -init => '$self->create("loader")'; field resolver_class => -init => '$YAML::ResolverClass || "YAML::Resolver"'; field resolver => -init => '$self->create("resolver")'; sub Dump { return YAML->new->dumper->dump(@_); } sub Load { return YAML->new->loader->load(@_); } { no warnings 'once'; *YAML::freeze = \ &Dump; *YAML::thaw = \ &Load; } sub DumpFile { my $OUT; my $filename = shift; if (ref $filename eq 'GLOB') { $OUT = $filename; } else { my $mode = '>'; if ($filename =~ /^\s*(>{1,2})\s*(.*)$/) { ($mode, $filename) = ($1, $2); } open $OUT, $mode, $filename or YAML::Base->die('YAML_DUMP_ERR_FILE_OUTPUT', $filename, $!); } local $/ = "\n"; # reset special to "sane" print $OUT Dump(@_); } sub LoadFile { my $IN; my $filename = shift; if (ref $filename eq 'GLOB') { $IN = $filename; } else { open $IN, $filename or YAML::Base->die('YAML_LOAD_ERR_FILE_INPUT', $filename, $!); } return Load(do { local $/; <$IN> }); } # my $global = {}; # sub Bless { # require YAML::Dumper::Base; # YAML::Dumper::Base::bless($global, @_) # } # sub Blessed { # require YAML::Dumper::Base; # YAML::Dumper::Base::blessed($global, @_) # } # sub global_object { $global } 1; __END__ from error import * from tokens import * from events import * from nodes import * from loader import * # 'BaseLoader', 'SafeLoader', 'Loader' from dumper import * ## I think it might be a little clearer to actually name the symbols ## being imported rather than using '*'. ## ## For Perl, I'm creating the -all import flag to have '*' semantics. try: from cyaml import * except ImportError: pass ## Try to load cyaml which has alternate Loader and Dumper classes that use ## libyaml. def scan(stream, Loader=Loader): """ Scan a YAML stream and produce scanning tokens. """ loader = Loader(stream) while loader.check_token(): yield loader.get_token() ## This looks like a class method to me. No self. The symbol 'Loader' ## here is used in two different contexts: as a local variable and as ## the global default. ## This method also uses a Python generator. Need to figure out how I ## want to translate that to Perl. ## ## ... return a closure for iteration. Example: ## ## my $scan = YAML::scan($stream); ## while (my $token = $scan->()) { ... ## Also thinking that these top level functions should be class methods ## instead. (YAML->scan($stream)). Then at least YAML.pm is subclassable. def parse(stream, Loader=Loader): """ Parse a YAML stream and produce parsing events. """ loader = Loader(stream) while loader.check_event(): yield loader.get_event() def compose(stream, Loader=Loader): """ Parse the first YAML document in a stream and produce the corresponding representation tree. """ loader = Loader(stream) if loader.check_node(): return loader.get_node() def compose_all(stream, Loader=Loader): """ Parse all YAML documents in a stream and produce corresponsing representation trees. """ loader = Loader(stream) while loader.check_node(): yield loader.get_node() def load_all(stream, Loader=Loader): """ Parse all YAML documents in a stream and produce corresponding Python objects. """ loader = Loader(stream) while loader.check_data(): yield loader.get_data() def load(stream, Loader=Loader): """ Parse the first YAML document in a stream and produce the corresponding Python object. """ loader = Loader(stream) if loader.check_data(): return loader.get_data() def safe_load_all(stream): """ Parse all YAML documents in a stream and produce corresponding Python objects. Resolve only basic YAML tags. """ return load_all(stream, SafeLoader) def safe_load(stream): """ Parse the first YAML document in a stream and produce the corresponding Python object. Resolve only basic YAML tags. """ return load(stream, SafeLoader) def emit(events, stream=None, Dumper=Dumper, canonical=None, indent=None, width=None, allow_unicode=None, line_break=None): """ Emit YAML parsing events into a stream. If stream is None, return the produced string instead. """ getvalue = None if stream is None: try: from cStringIO import StringIO except ImportError: from StringIO import StringIO stream = StringIO() getvalue = stream.getvalue dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, allow_unicode=allow_unicode, line_break=line_break) for event in events: dumper.emit(event) if getvalue: return getvalue() def serialize_all(nodes, stream=None, Dumper=Dumper, canonical=None, indent=None, width=None, allow_unicode=None, line_break=None, encoding='utf-8', explicit_start=None, explicit_end=None, version=None, tags=None): """ Serialize a sequence of representation trees into a YAML stream. If stream is None, return the produced string instead. """ getvalue = None if stream is None: try: from cStringIO import StringIO except ImportError: from StringIO import StringIO stream = StringIO() getvalue = stream.getvalue dumper = Dumper(stream, canonical=canonical, indent=indent, width=width, allow_unicode=allow_unicode, line_break=line_break, encoding=encoding, version=version, tags=tags, explicit_start=explicit_start, explicit_end=explicit_end) dumper.open() for node in nodes: dumper.serialize(node) dumper.close() if getvalue: return getvalue() def serialize(node, stream=None, Dumper=Dumper, **kwds): """ Serialize a representation tree into a YAML stream. If stream is None, return the produced string instead. """ return serialize_all([node], stream, Dumper=Dumper, **kwds) def dump_all(documents, stream=None, Dumper=Dumper, default_style=None, default_flow_style=None, canonical=None, indent=None, width=None, allow_unicode=None, line_break=None, encoding='utf-8', explicit_start=None, explicit_end=None, version=None, tags=None): """ Serialize a sequence of Python objects into a YAML stream. If stream is None, return the produced string instead. """ getvalue = None if stream is None: try: from cStringIO import StringIO except ImportError: from StringIO import StringIO stream = StringIO() getvalue = stream.getvalue dumper = Dumper(stream, default_style=default_style, default_flow_style=default_flow_style, canonical=canonical, indent=indent, width=width, allow_unicode=allow_unicode, line_break=line_break, encoding=encoding, version=version, tags=tags, explicit_start=explicit_start, explicit_end=explicit_end) dumper.open() for data in documents: dumper.represent(data) dumper.close() if getvalue: return getvalue() def dump(data, stream=None, Dumper=Dumper, **kwds): """ Serialize a Python object into a YAML stream. If stream is None, return the produced string instead. """ return dump_all([data], stream, Dumper=Dumper, **kwds) def safe_dump_all(documents, stream=None, **kwds): """ Serialize a sequence of Python objects into a YAML stream. Produce only basic YAML tags. If stream is None, return the produced string instead. """ return dump_all(documents, stream, Dumper=SafeDumper, **kwds) def safe_dump(data, stream=None, **kwds): """ Serialize a Python object into a YAML stream. Produce only basic YAML tags. If stream is None, return the produced string instead. """ return dump_all([data], stream, Dumper=SafeDumper, **kwds) def add_implicit_resolver(tag, regexp, first=None, Loader=Loader, Dumper=Dumper): """ Add an implicit scalar detector. If an implicit scalar value matches the given regexp, the corresponding tag is assigned to the scalar. first is a sequence of possible initial characters or None. """ Loader.add_implicit_resolver(tag, regexp, first) Dumper.add_implicit_resolver(tag, regexp, first) def add_path_resolver(tag, path, kind=None, Loader=Loader, Dumper=Dumper): """ Add a path based resolver for the given tag. A path is a list of keys that forms a path to a node in the representation tree. Keys can be string values, integers, or None. """ Loader.add_path_resolver(tag, path, kind) Dumper.add_path_resolver(tag, path, kind) def add_constructor(tag, constructor, Loader=Loader): """ Add a constructor for the given tag. Constructor is a function that accepts a Loader instance and a node object and produces the corresponding Python object. """ Loader.add_constructor(tag, constructor) def add_multi_constructor(tag_prefix, multi_constructor, Loader=Loader): """ Add a multi-constructor for the given tag prefix. Multi-constructor is called for a node if its tag starts with tag_prefix. Multi-constructor accepts a Loader instance, a tag suffix, and a node object and produces the corresponding Python object. """ Loader.add_multi_constructor(tag_prefix, multi_constructor) def add_representer(data_type, representer, Dumper=Dumper): """ Add a representer for the given type. Representer is a function accepting a Dumper instance and an instance of the given data type and producing the corresponding representation node. """ Dumper.add_representer(data_type, representer) def add_multi_representer(data_type, multi_representer, Dumper=Dumper): """ Add a representer for the given type. Multi-representer is a function accepting a Dumper instance and an instance of the given data type or subtype and producing the corresponding representation node. """ Dumper.add_multi_representer(data_type, multi_representer) class YAMLObjectMetaclass(type): """ The metaclass for YAMLObject. """ def __init__(cls, name, bases, kwds): super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds) if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None: cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml) cls.yaml_dumper.add_representer(cls, cls.to_yaml) class YAMLObject(object): """ An object that can dump itself to a YAML stream and load itself from a YAML stream. """ __metaclass__ = YAMLObjectMetaclass yaml_loader = Loader yaml_dumper = Dumper yaml_tag = None yaml_flow_style = None def from_yaml(cls, loader, node): """ Convert a representation node to a Python object. """ return loader.construct_yaml_object(node, cls) from_yaml = classmethod(from_yaml) def to_yaml(cls, dumper, data): """ Convert a Python object to a representation node. """ return dumper.represent_yaml_object(cls.yaml_tag, data, cls, flow_style=cls.yaml_flow_style) to_yaml = classmethod(to_yaml) =head1 NAME YAML - YAML Ain't Markup Language (tm) =head1 AUTHOR Ingy döt Net =head1 SYNOPSIS use YAML; # Save a list of (possibly complex) data structures to a YAML string my $yaml_string = Dump(@data_structures); # Restore (deep copies of) those data structures from the YAML string my @cloned_structures = Load($yaml_string); # YAML Debugging die Dump(@data_structures); # Reading a YAML config file my $config = YAML::LoadFile('./config.yaml'); # Save a dump to a file (or stream) YAML::Dump =head1 DESCRIPTION YAML is a data serialization language that uses printable unicode characters to store data structures in a very readable, editable and human friendly fashion. YAML is programming language agnostic; it stores data in such a way that is possible to share data structures between programs written in various languages that have YAML support. This currently includes popular languages such as Perl, Python, Ruby, PHP, JavaScript, Java and Haskell. YAML has a simple but complete data model that is similar to Perl's internal data model. It consists Mappings, Sequences and Scalars (think hashes, arrays and scalars) combined with Tags for typing (think bless) and an Anchor/Alias referencing facility (think references in Perl). As Perl folk know, this minimal model is complete enough to represent almost any other data structures. This documentation discusses the YAML.pm simple Load/Dump API, and then goes on to tell about how all the YAML::* framework modules fit together. For specific information on a framework module, see the documentation for that module. =head1 YAML.pm API YAML exports its two main functions C and C and provides two more non-exported functions C and C. These functions are just syntax sugar coating the more flexible and object-oriented underlying APIs. =head2 Dump() The Dump() function takes a list of values and returns their YAML serialization as a YAML stream in a single scalar. my $yaml = YAML::Dump($value1, $value2, ... ); Since YAML streams can support multiple documents, each input value is serialized as a separate document in the stream. Dump() will serialize plain scalars, in addition to hash and array references. This is different than Storable::freeze(), which only takes one argument and demands that it be a reference. =head2 Load() The Load() function is the exact inverse of the Dump() function. It takes a single scalar consisting of a YAML stream, and return a list of zero or more values, one for each document in the stream. my ($value1, $value2, ...) = YAML::Load($yaml); =head2 DumpFile() The DumpFile() function does the exact same thing as Dump(), except that it takes an extra first argument, which is a file name or an IO handle, telling YAML.pm where to send the data. DumpFile() returns a true value on success. YAML::DumpFile('./file.yaml', $value1, $value2, ... ); To concatenate the result onto an existing file, specify a mode flag before the file name: YAML::DumpFile('>> ./file.yaml', $value1, $value2, ... ); =head2 LoadFile() The LoadFile() function does the exact same thing as Load(), except that its single input argument is a file name or an IO handle, telling YAML.pm where to read the YAML stream from. LoadFile returns the same thing as Load(). my ($value1, $value2, ...) = YAML::LoadFile('./file.yaml'); =head2 freeze() and thaw() YAML::freeze() is an alias of YAML::Dump(), and YAML::thaw() is an alias of YAML::Load(). This is merely a convenience for modules like POE that honor a freeze/thaw API (like Storable). These functions are exportable, but are not exported by default. =head1 CONFIGURATION ... describe the $YAML::* variables ... for Dumper and Loader =head1 IMPLEMENTATION This implementation of YAML.pm is largely based on the excellent code from Kirill Simonov's PyYaml (http://pyyaml.org) project. It is comprised of a many layered and functionally mirrored stack of object oriented modules under the Dump/Load API. See below for details. =head1 THE YAML STACK YAML.pm has a deceivingly simple API. Simply C your data somewhere and later C it back in. That's how every serialization module works, more or less. However, under the hood, both in YAML theory and in YAML.pm, there is a stack of processors for both Dumping and Loading. What does this mean to you? It means "Control". It means that you can do streaming processing of YAML without loading data all the way into Perl data structures. It means that you can load YAML into a Generic YAML Graph, that is truly Perl independent. It means that you can swap out any of the parts of the stack, with alternate processors that meet you specific needs. =head2 The Dumper Stack See the documentation of the modules listed below for more specifics. =over =item YAML::Dumper YAML::Dumper is the top level of the Dumper Stack. =item YAML::Representer =item YAML::Serializer =item YAML::Emitter =item YAML::Writer =back =head2 The Loader Stack =over =item YAML::Loader YAML::Loader is the top level of the Loader Stack. =item YAML::Constructor =item YAML::Composer =item YAML::Parser =item YAML::Scanner =item YAML::Reader =back =head2 The Resolver =head1 ysh - The YAML Shell The YAML distribution ships with a script called 'ysh', the YAML shell. ysh provides a simple, interactive way to play with YAML. If you type in Perl code, it displays the result in YAML. If you type in YAML it turns it into Perl code. To run ysh, (assuming you installed it along with YAML.pm) simply type: ysh [options] Please read the C documentation for the full details. There are lots of options. =head1 CREDIT The YAML serialization language is credited to years of tireless effort from Oren Ben-Kiki, Clark Evans, the late Brian Ingerson (RIP), and the wonderfully brilliant citizens of the YAML community. YAML's users are a lucky lot of stiffs to have Why The Lucky Stiff save them from years of darkness with his creation of libsyck, the original C library for YAML, which has been bound to almost every language that boasts YAML support. Why also single handedly tricked the Ruby community into adopting YAML for just about everything. Thanks Why. Not enough can be said about the contributions of Kirill Simonov. Python thanks him for finally delivering a solid PyYaml implementation, during which he helped identify many bugs in the YAML specification. After that he went on to write libyaml which promises to be the cornerstone of YAML processing. Ola Bini, in addition to bringing YAML support to Java, has ported PyYaml to Ruby, giving Ruby its first YAML 1.1 compliant toolset. =head1 REFERENCES * http://yaml.com =head1 COPYRIGHT Copyright (c) 2005, 2006. Ingy döt Net. All rights reserved. Copyright (c) 2001, 2002, 2005. Brian Ingerson. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See L =cut