[
  {
    "path": ".gitignore",
    "content": "*.pyc\n*.pyo\n.cache/\n*.egg-info/\n__pycache__/\nbuild/\ndist/\ndocs/_build/\n"
  },
  {
    "path": "LICENSE",
    "content": "This project is licensed under the ISC license, reproduced below.\n\nCopyright (c) 2012, Lexy \"eevee\" Munroe <eevee.camel@veekun.com>\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND\nFITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include LICENSE\ninclude README.txt\n"
  },
  {
    "path": "README.txt",
    "content": "Camel\n=====\n\nCamel is a library that lets you describe how to serialize your objects to\nYAML — and refuses to serialize them if you don't.\n\nQuick example:\n\n.. code-block:: python\n\n    from camel import Camel, CamelRegistry\n\n    class DieRoll(tuple):\n        def __new__(cls, a, b):\n            return tuple.__new__(cls, [a, b])\n\n        def __repr__(self):\n            return \"DieRoll(%s,%s)\" % self\n\n    reg = CamelRegistry()\n\n    @reg.dumper(DieRoll, u'roll', version=None)\n    def _dump_dice(data):\n        return u\"{}d{}\".format(*data)\n\n    @reg.loader(u'roll', version=None)\n    def _load_dice(data, version):\n        a, _, b = data.partition(u'd')\n        return DieRoll(int(a), int(b))\n\n    value = DieRoll(3, 6)\n    camel = Camel([reg])\n    print(camel.dump(value))\n\n    # !roll 3d6\n    # ...\n\nDocs: http://camel.readthedocs.org/en/latest/\n\nGitHub: https://github.com/eevee/camel\n"
  },
  {
    "path": "camel/__init__.py",
    "content": "# encoding: utf8\n# TODO\n# i kinda need for pokedex purposes:\n# - consider creating an @inherited_dumper?  would need a way to pattern-match on the tag or something though, yikes.  can this be done in a more controlled way, like you only get to pick a suffix to add to the tag?  or is it useful in some other way to be able to pattern match on tags?\n# - easy way to explicitly set the representer style (maybe a decorator arg?)\n# - document tag_prefix!  or was i thinking about the design of it?  should you be able to override the tag prefix when adding to the camel?\n#   - tag_prefix=YAML_TAG_PREFIX is different from passing it as the actual tag name and i don't get why -- maybe because the default tag prefix is !\n# - test and document inherit arg to @dumper\n# - comment, test, document add_registry and tag_shorthand\n#\n# - expose the plain scalar parser things\n#\n# - support %TAG directives some nice reasonable way\n# - support the attrs module, if installed, somehow\n# - consider using (optionally?) ruamel.yaml, which roundtrips comments, merges, anchors, ...\n# - DWIM formatting: block style except for very short sequences (if at all?), quotey style for long text...\n# - make dumper/loader work on methods?  ehh\n# - \"raw\" loaders and dumpers that get to deal with raw yaml nodes?\n\n\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\nimport base64\nimport collections\nimport functools\nfrom io import StringIO\nimport types\n\nimport yaml\n\ntry:\n    from yaml import CSafeDumper as SafeDumper\n    from yaml import CSafeLoader as SafeLoader\nexcept ImportError:\n    from yaml import SafeDumper\n    from yaml import SafeLoader\n\n\nYAML_TAG_PREFIX = 'tag:yaml.org,2002:'\n\n_str = type('')\n_bytes = type(b'')\n_long = type(18446744073709551617)  # 2**64 + 1\n\n\nclass CamelDumper(SafeDumper):\n    \"\"\"Subclass of yaml's `SafeDumper` that scopes representers to the\n    instance, rather than to the particular class, because damn.\n    \"\"\"\n    def __init__(self, *args, **kwargs):\n        # TODO this isn't quite good enough; pyyaml still escapes anything\n        # outside the BMP\n        kwargs.setdefault('allow_unicode', True)\n        super(CamelDumper, self).__init__(*args, **kwargs)\n        self.yaml_representers = SafeDumper.yaml_representers.copy()\n        self.yaml_multi_representers = SafeDumper.yaml_multi_representers.copy()\n\n        # Always dump bytes as binary, even on Python 2\n        self.add_representer(bytes, CamelDumper.represent_binary)\n\n    def represent_binary(self, data):\n        # This is copy-pasted, because it only exists in pyyaml in python 3 (?!)\n        if hasattr(base64, 'encodebytes'):\n            data = base64.encodebytes(data).decode('ascii')\n        else:\n            data = base64.encodestring(data).decode('ascii')\n        return self.represent_scalar(\n            YAML_TAG_PREFIX + 'binary', data, style='|')\n\n    def add_representer(self, data_type, representer):\n        self.yaml_representers[data_type] = representer\n\n    def add_multi_representer(self, data_type, representer):\n        self.yaml_multi_representers[data_type] = representer\n\n\nclass CamelLoader(SafeLoader):\n    \"\"\"Subclass of yaml's `SafeLoader` that scopes constructors to the\n    instance, rather than to the particular class, because damn.\n    \"\"\"\n    def __init__(self, *args, **kwargs):\n        super(CamelLoader, self).__init__(*args, **kwargs)\n        self.yaml_constructors = SafeLoader.yaml_constructors.copy()\n        self.yaml_multi_constructors = SafeLoader.yaml_multi_constructors.copy()\n        self.yaml_implicit_resolvers = SafeLoader.yaml_implicit_resolvers.copy()\n\n    def add_constructor(self, data_type, constructor):\n        self.yaml_constructors[data_type] = constructor\n\n    def add_multi_constructor(self, data_type, constructor):\n        self.yaml_multi_constructors[data_type] = constructor\n\n    def add_implicit_resolver(self, tag, regexp, first):\n        if first is None:\n            first = [None]\n        for ch in first:\n            self.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp))\n\n    def add_path_resolver(self, *args, **kwargs):\n        # This API is non-trivial and claims to be experimental and unstable\n        raise NotImplementedError\n\n\nclass Camel(object):\n    \"\"\"Class responsible for doing the actual dumping to and loading from YAML.\n    \"\"\"\n    def __init__(self, registries=()):\n        self.registries = collections.OrderedDict()\n        self.version_locks = {}  # class => version\n\n        self.add_registry(STANDARD_TYPES)\n        for registry in registries:\n            self.add_registry(registry)\n\n    def add_registry(self, registry, tag_prefix=None, tag_shorthand=None):\n        self.registries[registry] = (\n            tag_prefix or registry.tag_prefix,\n            tag_shorthand or registry.tag_shorthand,\n        )\n\n    def lock_version(self, cls, version):\n        self.version_locks[cls] = version\n\n    def make_dumper(self, stream):\n        tag_shorthands = {}\n        for registry, (prefix, shorthand) in self.registries.items():\n            if shorthand is None:\n                continue\n            if shorthand in tag_shorthands:\n                raise ValueError(\n                    \"Conflicting tag shorthands: {!r} is short for both {!r} and {!r}\"\n                    .format(shorthand, tag_shorthands[shorthand], prefix))\n            tag_shorthands[shorthand] = prefix\n\n        dumper = CamelDumper(stream, default_flow_style=False, tags=tag_shorthands)\n        for registry in self.registries:\n            registry.inject_dumpers(dumper, version_locks=self.version_locks)\n        return dumper\n\n    def dump(self, data):\n        stream = StringIO()\n        dumper = self.make_dumper(stream)\n        dumper.open()\n        dumper.represent(data)\n        dumper.close()\n        return stream.getvalue()\n\n    def make_loader(self, stream):\n        loader = CamelLoader(stream)\n        for registry in self.registries:\n            registry.inject_loaders(loader)\n        return loader\n\n    def load(self, data):\n        stream = StringIO(data)\n        loader = self.make_loader(stream)\n        obj = loader.get_data()\n        if loader.check_node():\n            raise RuntimeError(\"Multiple documents found in stream; use load_all\")\n        return obj\n\n    def load_first(self, data):\n        stream = StringIO(data)\n        loader = self.make_loader(stream)\n        return loader.get_data()\n\n    def load_all(self, data):\n        stream = StringIO(data)\n        loader = self.make_loader(stream)\n        while loader.check_node():\n            yield loader.get_data()\n\n\nclass DuplicateVersion(ValueError):\n    pass\n\n\nclass CamelRegistry(object):\n    frozen = False\n\n    def __init__(self, tag_prefix='!', tag_shorthand=None):\n        self.tag_prefix = tag_prefix\n        self.tag_shorthand = tag_shorthand\n\n        # type => {version => function)\n        self.dumpers = collections.defaultdict(dict)\n        self.multi_dumpers = collections.defaultdict(dict)\n        # base tag => {version => function}\n        self.loaders = collections.defaultdict(dict)\n\n    def freeze(self):\n        self.frozen = True\n\n    # Dumping\n\n    def _check_tag(self, tag):\n        # Good a place as any, I suppose\n        if self.frozen:\n            raise RuntimeError(\"Can't add to a frozen registry\")\n\n        if ';' in tag:\n            raise ValueError(\n                \"Tags may not contain semicolons: {0!r}\".format(tag))\n\n    def dumper(self, cls, tag, version, inherit=False):\n        self._check_tag(tag)\n\n        if inherit:\n            store_in = self.multi_dumpers\n        else:\n            store_in = self.dumpers\n\n        if version in store_in[cls]:\n            raise DuplicateVersion\n\n        tag = self.tag_prefix + tag\n\n        if version is None:\n            full_tag = tag\n        elif isinstance(version, (int, _long)) and version > 0:\n            full_tag = \"{0};{1}\".format(tag, version)\n        else:\n            raise TypeError(\n                \"Expected None or a positive integer version; \"\n                \"got {0!r} instead\".format(version))\n\n        def decorator(f):\n            store_in[cls][version] = functools.partial(\n                self.run_representer, f, full_tag)\n            return f\n\n        return decorator\n\n    def run_representer(self, representer, tag, dumper, data):\n        canon_value = representer(data)\n        # Note that we /do not/ support subclasses of the built-in types here,\n        # to avoid complications from returning types that have their own\n        # custom representers\n        canon_type = type(canon_value)\n        # TODO this gives no control over flow_style, style, and implicit.  do\n        # we intend to figure it out ourselves?\n        if canon_type is dict:\n            return dumper.represent_mapping(tag, canon_value, flow_style=False)\n        elif canon_type is collections.OrderedDict:\n            # pyyaml tries to sort the items of a dict, which defeats the point\n            # of returning an OrderedDict.  Luckily, it only does this if the\n            # value it gets has an 'items' method; otherwise it skips the\n            # sorting and iterates the value directly, assuming it'll get\n            # key/value pairs.  So pass in the dict's items iterator.\n            return dumper.represent_mapping(tag, canon_value.items(), flow_style=False)\n        elif canon_type in (tuple, list):\n            return dumper.represent_sequence(tag, canon_value, flow_style=False)\n        elif canon_type in (int, _long, float, bool, _str, type(None)):\n            return dumper.represent_scalar(tag, canon_value)\n        else:\n            raise TypeError(\n                \"Representers must return native YAML types, but the representer \"\n                \"for {!r} returned {!r}, which is of type {!r}\"\n                .format(data, canon_value, canon_type))\n\n    def inject_dumpers(self, dumper, version_locks=None):\n        if not version_locks:\n            version_locks = {}\n\n        for add_method, dumpers in [\n                (dumper.add_representer, self.dumpers),\n                (dumper.add_multi_representer, self.multi_dumpers),\n            ]:\n            for cls, versions in dumpers.items():\n                version = version_locks.get(cls, max)\n                if versions and version is max:\n                    if None in versions:\n                        representer = versions[None]\n                    else:\n                        representer = versions[max(versions)]\n                elif version in versions:\n                    representer = versions[version]\n                else:\n                    raise KeyError(\n                        \"Don't know how to dump version {0!r} of type {1!r}\"\n                        .format(version, cls))\n                add_method(cls, representer)\n\n    # Loading\n    # TODO implement \"upgrader\", which upgrades from one version to another\n\n    def loader(self, tag, version):\n        self._check_tag(tag)\n\n        if version in self.loaders[tag]:\n            raise DuplicateVersion\n\n        tag = self.tag_prefix + tag\n\n        def decorator(f):\n            self.loaders[tag][version] = functools.partial(\n                self.run_constructor, f, version)\n            return f\n\n        return decorator\n\n    def run_constructor(self, constructor, version, *yaml_args):\n        # Two args for add_constructor, three for add_multi_constructor\n        if len(yaml_args) == 3:\n            loader, suffix, node = yaml_args\n            version = int(suffix)\n        else:\n            loader, node = yaml_args\n\n        if isinstance(node, yaml.ScalarNode):\n            data = loader.construct_scalar(node)\n        elif isinstance(node, yaml.SequenceNode):\n            data = loader.construct_sequence(node, deep=True)\n        elif isinstance(node, yaml.MappingNode):\n            data = loader.construct_mapping(node, deep=True)\n        else:\n            raise TypeError(\"Not a primitive node: {!r}\".format(node))\n        return constructor(data, version)\n\n    def inject_loaders(self, loader):\n        for tag, versions in self.loaders.items():\n            # \"all\" loader overrides everything\n            if all in versions:\n                if None in versions:\n                    loader.add_constructor(tag, versions[None])\n                else:\n                    loader.add_constructor(tag, versions[all])\n                loader.add_multi_constructor(tag + \";\", versions[all])\n                continue\n\n            # Otherwise, add each constructor individually\n            for version, constructor in versions.items():\n                if version is None:\n                    loader.add_constructor(tag, constructor)\n                elif version is any:\n                    loader.add_multi_constructor(tag + \";\", versions[any])\n                    if None not in versions:\n                        loader.add_constructor(tag, versions[any])\n                else:\n                    full_tag = \"{0};{1}\".format(tag, version)\n                    loader.add_constructor(full_tag, constructor)\n\n\n# YAML's \"language-independent types\" — not builtins, but supported with\n# standard !! tags.  Most of them are built into pyyaml, but OrderedDict is\n# curiously overlooked.  Loaded first by default into every Camel object.\n# Ref: http://yaml.org/type/\n# TODO pyyaml supports tags like !!python/list; do we care?\nSTANDARD_TYPES = CamelRegistry(tag_prefix=YAML_TAG_PREFIX)\n\n\n@STANDARD_TYPES.dumper(frozenset, 'set', version=None)\ndef _dump_frozenset(data):\n    return dict.fromkeys(data)\n\n\n@STANDARD_TYPES.dumper(collections.OrderedDict, 'omap', version=None)\ndef _dump_ordered_dict(data):\n    pairs = []\n    for key, value in data.items():\n        pairs.append({key: value})\n    return pairs\n\n\n@STANDARD_TYPES.loader('omap', version=None)\ndef _load_ordered_dict(data, version):\n    return collections.OrderedDict(\n        pair for datum in data for (pair,) in [datum.items()]\n    )\n\n\n# Extra Python types that don't have native YAML equivalents, but that PyYAML\n# supports with !!python/foo tags.  Dumping them isn't supported by default,\n# but loading them is, since there's no good reason for it not to be.\n# A couple of these dumpers override builtin type support.  For example, tuples\n# are dumped as lists by default, but this registry will dump them as\n# !!python/tuple.\nPYTHON_TYPES = CamelRegistry(tag_prefix=YAML_TAG_PREFIX)\n\n\n@PYTHON_TYPES.dumper(tuple, 'python/tuple', version=None)\ndef _dump_tuple(data):\n    return list(data)\n\n\n@STANDARD_TYPES.loader('python/tuple', version=None)\ndef _load_tuple(data, version):\n    return tuple(data)\n\n\n@PYTHON_TYPES.dumper(complex, 'python/complex', version=None)\ndef _dump_complex(data):\n    ret = repr(data)\n    if str is bytes:\n        ret = ret.decode('ascii')\n    # Complex numbers become (1+2j), but the parens are superfluous\n    if ret[0] == '(' and ret[-1] == ')':\n        return ret[1:-1]\n    else:\n        return ret\n\n\n@STANDARD_TYPES.loader('python/complex', version=None)\ndef _load_complex(data, version):\n    return complex(data)\n\n\n@PYTHON_TYPES.dumper(frozenset, 'python/frozenset', version=None)\ndef _dump_frozenset(data):\n    try:\n        return list(sorted(data))\n    except TypeError:\n        return list(data)\n\n\n@STANDARD_TYPES.loader('python/frozenset', version=None)\ndef _load_frozenset(data, version):\n    return frozenset(data)\n\n\nif hasattr(types, 'SimpleNamespace'):\n    @PYTHON_TYPES.dumper(types.SimpleNamespace, 'python/namespace', version=None)\n    def _dump_simple_namespace(data):\n        return data.__dict__\n\n\n    @STANDARD_TYPES.loader('python/namespace', version=None)\n    def _load_simple_namespace(data, version):\n        return types.SimpleNamespace(**data)\n\n\nSTANDARD_TYPES.freeze()\nPYTHON_TYPES.freeze()\n"
  },
  {
    "path": "camel/tests/__init__.py",
    "content": ""
  },
  {
    "path": "camel/tests/test_docs.py",
    "content": "\"\"\"Make sure the documentation examples actually, uh, work.\"\"\"\nfrom __future__ import unicode_literals\nimport textwrap\n\n\ndef test_docs_table_v1():\n    class Table(object):\n        def __init__(self, size):\n            self.size = size\n\n        def __repr__(self):\n            return \"<Table {self.size!r}>\".format(self=self)\n\n    from camel import CamelRegistry\n    my_types = CamelRegistry()\n\n    @my_types.dumper(Table, 'table', version=1)\n    def _dump_table(table):\n        return {\n            'size': table.size,\n        }\n\n    @my_types.loader('table', version=1)\n    def _load_table(data, version):\n        return Table(data[\"size\"])\n\n    from camel import Camel\n    table = Table(25)\n    assert Camel([my_types]).dump(table) == \"!table;1\\nsize: 25\\n\"\n\n    data = {'chairs': [], 'tables': [Table(25), Table(36)]}\n    assert Camel([my_types]).dump(data) == textwrap.dedent(\"\"\"\n        chairs: []\n        tables:\n        - !table;1\n          size: 25\n        - !table;1\n          size: 36\n    \"\"\").lstrip()\n\n    table, = Camel([my_types]).load(\"[!table;1 {size: 100}]\")\n    assert isinstance(table, Table)\n    assert table.size == 100\n\n\ndef test_docs_table_v2():\n    # Tables can be rectangles now!\n    class Table(object):\n        def __init__(self, height, width):\n            self.height = height\n            self.width = width\n\n        def __repr__(self):\n            return \"<Table {self.height!r}x{self.width!r}>\".format(self=self)\n\n    from camel import Camel, CamelRegistry\n    my_types = CamelRegistry()\n\n    @my_types.dumper(Table, 'table', version=2)\n    def _dump_table_v2(table):\n        return {\n            'height': table.height,\n            'width': table.width,\n        }\n\n    @my_types.loader('table', version=2)\n    def _load_table_v2(data, version):\n        return Table(data[\"height\"], data[\"width\"])\n\n    @my_types.loader('table', version=1)\n    def _load_table_v1(data, version):\n        edge = data[\"size\"] ** 0.5\n        return Table(edge, edge)\n\n    table = Table(7, 10)\n    assert Camel([my_types]).dump(table) == textwrap.dedent(\"\"\"\n        !table;2\n        height: 7\n        width: 10\n    \"\"\").lstrip()\n\n    @my_types.dumper(Table, 'table', version=1)\n    def _dump_table_v1(table):\n        return {\n            # not really, but the best we can manage\n            'size': table.height * table.width,\n            }\n\n    camel = Camel([my_types])\n    camel.lock_version(Table, 1)\n    assert camel.dump(Table(5, 7)) == \"!table;1\\nsize: 35\\n\"\n\n\ndef test_docs_deleted():\n    class DummyData(object):\n        def __init__(self, data):\n            self.data = data\n\n    from camel import Camel, CamelRegistry\n    my_types = CamelRegistry()\n\n    @my_types.loader('deleted-type', version=all)\n    def _load_deleted_type(data, version):\n        return DummyData(data)\n\n    camel = Camel([my_types])\n    assert isinstance(camel.load(\"\"\"!deleted-type;4 foo\"\"\"), DummyData)\n\n\ndef test_docs_table_any():\n    class Table(object):\n        def __init__(self, height, width):\n            self.height = height\n            self.width = width\n\n        def __repr__(self):\n            return \"<Table {self.height!r}x{self.width!r}>\".format(self=self)\n\n    from camel import Camel, CamelRegistry\n    my_types = CamelRegistry()\n\n    @my_types.loader('table', version=any)\n    def _load_table(data, version):\n        if 'size' in data:\n            # version 1\n            edge = data['size'] ** 0.5\n            return Table(edge, edge)\n        else:\n            # version 2?)\n            return Table(data['height'], data['width'])\n\n    camel = Camel([my_types])\n    table1, table2 = camel.load(\n        \"[!table;1 {size: 49}, !table;2 {height: 5, width: 9}]\")\n\n    assert table1.height == 7\n    assert table1.width == 7\n    assert table2.height == 5\n    assert table2.width == 9\n"
  },
  {
    "path": "camel/tests/test_general.py",
    "content": "# encoding: utf8\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\nimport collections\nimport datetime\n\nimport pytest\n\nfrom camel import Camel, CamelRegistry, PYTHON_TYPES\n\n\n# Round-trips for simple values of built-in types\n@pytest.mark.parametrize(('value', 'expected_serialization'), [\n    # TODO the trailing ... for non-container values is kinda weird\n    (None, \"null\\n...\\n\"),\n    ('ⓤⓝⓘⓒⓞⓓⓔ', \"ⓤⓝⓘⓒⓞⓓⓔ\\n...\\n\"),\n    (b'bytes', \"!!binary |\\n  Ynl0ZXM=\\n\"),\n    (True, \"true\\n...\\n\"),\n    (133, \"133\\n...\\n\"),\n    # long, for python 2\n    (2**133, \"10889035741470030830827987437816582766592\\n...\\n\"),\n    (3.52, \"3.52\\n...\\n\"),\n    ([1, 2, 'three'], \"- 1\\n- 2\\n- three\\n\"),\n    ({'x': 7, 'y': 8, 'z': 9}, \"x: 7\\ny: 8\\nz: 9\\n\"),\n    # TODO this should use ? notation\n    (set(\"qvx\"), \"!!set\\nq: null\\nv: null\\nx: null\\n\"),\n    (datetime.date(2015, 10, 21), \"2015-10-21\\n...\\n\"),\n    (datetime.datetime(2015, 10, 21, 4, 29), \"2015-10-21 04:29:00\\n...\\n\"),\n    # TODO case with timezone...  unfortunately can't preserve the whole thing\n    (collections.OrderedDict([('a', 1), ('b', 2), ('c', 3)]), \"!!omap\\n- a: 1\\n- b: 2\\n- c: 3\\n\"),\n])\ndef test_basic_roundtrip(value, expected_serialization):\n    camel = Camel()\n    dumped = camel.dump(value)\n    assert dumped == expected_serialization\n    assert camel.load(dumped) == value\n\n\ndef test_tuple_roundtrip():\n    # By default, tuples become lists\n    value = (4, 3, 2)\n    camel = Camel()\n    dumped = camel.dump(value)\n    # TODO short list like this should be flow style?\n    assert dumped == \"- 4\\n- 3\\n- 2\\n\"\n    assert camel.load(dumped) == list(value)\n\n\ndef test_frozenset_roundtrip():\n    # By default, frozensets become sets\n    value = frozenset((4, 3, 2))\n    camel = Camel()\n    dumped = camel.dump(value)\n    # TODO this should use ? notation\n    assert dumped == \"!!set\\n2: null\\n3: null\\n4: null\\n\"\n    assert camel.load(dumped) == set(value)\n\n\n# Round-trips for built-in Python types with custom representations\n@pytest.mark.parametrize(('value', 'expected_serialization'), [\n    ((4, 3, 2), \"!!python/tuple\\n- 4\\n- 3\\n- 2\\n\"),\n    (5 + 12j, \"!!python/complex 5+12j\\n...\\n\"),\n    (2j, \"!!python/complex 2j\\n...\\n\"),\n    (frozenset((4, 3, 2)), \"!!python/frozenset\\n- 2\\n- 3\\n- 4\\n\"),\n])\ndef test_python_roundtrip(value, expected_serialization):\n    camel = Camel([PYTHON_TYPES])\n    dumped = camel.dump(value)\n    assert dumped == expected_serialization\n\n    # Should be able to load them without the python types\n    vanilla_camel = Camel()\n    assert vanilla_camel.load(dumped) == value\n\n\n# -----------------------------------------------------------------------------\n# Simple custom type\n\nclass DieRoll(tuple):\n    def __new__(cls, a, b):\n        return tuple.__new__(cls, [a, b])\n\n    def __repr__(self):\n        return \"DieRoll(%s,%s)\" % self\n\n\n# Dump/load as a compact string\nreg = CamelRegistry()\n\n\n@reg.dumper(DieRoll, 'roll', version=None)\ndef dump_dice(data):\n    return \"{}d{}\".format(*data)\n\n\n@reg.loader('roll', version=None)\ndef load_dice(data, version):\n    # TODO enforce incoming data is a string?\n    a, _, b = data.partition('d')\n    return DieRoll(int(a), int(b))\n\n\ndef test_dieroll():\n    value = DieRoll(3, 6)\n    camel = Camel([reg])\n    dumped = camel.dump(value)\n    assert dumped == '!roll 3d6\\n...\\n'\n    assert camel.load(dumped) == value\n\n\n# Dump/load as a dict\nreg2 = CamelRegistry()\n\n\n@reg2.dumper(DieRoll, 'roll', version=None)\ndef dump_dice(data):\n    return collections.OrderedDict([\n        # NOTE: These are deliberately arranged in reverse alphabetical order,\n        # to ensure that we avoid pyyaml's default behavior of sorting a\n        # dict-like when dumping a mapping\n        ('numdice', data[0]),\n        ('faces', data[1]),\n    ])\n\n\n@reg2.loader('roll', version=None)\ndef load_dice(data, version):\n    # TODO enforce data is a dict?\n    # TODO enforce data contains only these keys?\n    return DieRoll(\n        int(data['numdice']),\n        int(data['faces']),\n    )\n\n\ndef test_dieroll2():\n    value = DieRoll(3, 6)\n    camel = Camel([reg2])\n    dumped = camel.dump(value)\n    assert dumped == '!roll\\nnumdice: 3\\nfaces: 6\\n'\n    assert camel.load(dumped) == value\n"
  },
  {
    "path": "docs/Makefile",
    "content": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD   = sphinx-build2\nPAPER         =\nBUILDDIR      = _build\n\n# User-friendly check for sphinx-build2\nifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)\n$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)\nendif\n\n# Internal variables.\nPAPEROPT_a4     = -D latex_paper_size=a4\nPAPEROPT_letter = -D latex_paper_size=letter\nALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .\n# the i18n builder cannot share the environment and doctrees with the others\nI18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .\n\n.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext\n\nhelp:\n\t@echo \"Please use \\`make <target>' where <target> is one of\"\n\t@echo \"  html       to make standalone HTML files\"\n\t@echo \"  dirhtml    to make HTML files named index.html in directories\"\n\t@echo \"  singlehtml to make a single large HTML file\"\n\t@echo \"  pickle     to make pickle files\"\n\t@echo \"  json       to make JSON files\"\n\t@echo \"  htmlhelp   to make HTML files and a HTML help project\"\n\t@echo \"  qthelp     to make HTML files and a qthelp project\"\n\t@echo \"  devhelp    to make HTML files and a Devhelp project\"\n\t@echo \"  epub       to make an epub\"\n\t@echo \"  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\"\n\t@echo \"  latexpdf   to make LaTeX files and run them through pdflatex\"\n\t@echo \"  latexpdfja to make LaTeX files and run them through platex/dvipdfmx\"\n\t@echo \"  text       to make text files\"\n\t@echo \"  man        to make manual pages\"\n\t@echo \"  texinfo    to make Texinfo files\"\n\t@echo \"  info       to make Texinfo files and run them through makeinfo\"\n\t@echo \"  gettext    to make PO message catalogs\"\n\t@echo \"  changes    to make an overview of all changed/added/deprecated items\"\n\t@echo \"  xml        to make Docutils-native XML files\"\n\t@echo \"  pseudoxml  to make pseudoxml-XML files for display purposes\"\n\t@echo \"  linkcheck  to check all external links for integrity\"\n\t@echo \"  doctest    to run all doctests embedded in the documentation (if enabled)\"\n\nclean:\n\trm -rf $(BUILDDIR)/*\n\nhtml:\n\t$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/html.\"\n\ndirhtml:\n\t$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml\n\t@echo\n\t@echo \"Build finished. The HTML pages are in $(BUILDDIR)/dirhtml.\"\n\nsinglehtml:\n\t$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml\n\t@echo\n\t@echo \"Build finished. The HTML page is in $(BUILDDIR)/singlehtml.\"\n\npickle:\n\t$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle\n\t@echo\n\t@echo \"Build finished; now you can process the pickle files.\"\n\njson:\n\t$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json\n\t@echo\n\t@echo \"Build finished; now you can process the JSON files.\"\n\nhtmlhelp:\n\t$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp\n\t@echo\n\t@echo \"Build finished; now you can run HTML Help Workshop with the\" \\\n\t      \".hhp project file in $(BUILDDIR)/htmlhelp.\"\n\nqthelp:\n\t$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp\n\t@echo\n\t@echo \"Build finished; now you can run \"qcollectiongenerator\" with the\" \\\n\t      \".qhcp project file in $(BUILDDIR)/qthelp, like this:\"\n\t@echo \"# qcollectiongenerator $(BUILDDIR)/qthelp/Camel.qhcp\"\n\t@echo \"To view the help file:\"\n\t@echo \"# assistant -collectionFile $(BUILDDIR)/qthelp/Camel.qhc\"\n\ndevhelp:\n\t$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp\n\t@echo\n\t@echo \"Build finished.\"\n\t@echo \"To view the help file:\"\n\t@echo \"# mkdir -p $$HOME/.local/share/devhelp/Camel\"\n\t@echo \"# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Camel\"\n\t@echo \"# devhelp\"\n\nepub:\n\t$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub\n\t@echo\n\t@echo \"Build finished. The epub file is in $(BUILDDIR)/epub.\"\n\nlatex:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo\n\t@echo \"Build finished; the LaTeX files are in $(BUILDDIR)/latex.\"\n\t@echo \"Run \\`make' in that directory to run these through (pdf)latex\" \\\n\t      \"(use \\`make latexpdf' here to do that automatically).\"\n\nlatexpdf:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through pdflatex...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\nlatexpdfja:\n\t$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex\n\t@echo \"Running LaTeX files through platex and dvipdfmx...\"\n\t$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja\n\t@echo \"pdflatex finished; the PDF files are in $(BUILDDIR)/latex.\"\n\ntext:\n\t$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text\n\t@echo\n\t@echo \"Build finished. The text files are in $(BUILDDIR)/text.\"\n\nman:\n\t$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man\n\t@echo\n\t@echo \"Build finished. The manual pages are in $(BUILDDIR)/man.\"\n\ntexinfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo\n\t@echo \"Build finished. The Texinfo files are in $(BUILDDIR)/texinfo.\"\n\t@echo \"Run \\`make' in that directory to run these through makeinfo\" \\\n\t      \"(use \\`make info' here to do that automatically).\"\n\ninfo:\n\t$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo\n\t@echo \"Running Texinfo files through makeinfo...\"\n\tmake -C $(BUILDDIR)/texinfo info\n\t@echo \"makeinfo finished; the Info files are in $(BUILDDIR)/texinfo.\"\n\ngettext:\n\t$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale\n\t@echo\n\t@echo \"Build finished. The message catalogs are in $(BUILDDIR)/locale.\"\n\nchanges:\n\t$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes\n\t@echo\n\t@echo \"The overview file is in $(BUILDDIR)/changes.\"\n\nlinkcheck:\n\t$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck\n\t@echo\n\t@echo \"Link check complete; look for any errors in the above output \" \\\n\t      \"or in $(BUILDDIR)/linkcheck/output.txt.\"\n\ndoctest:\n\t$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest\n\t@echo \"Testing of doctests in the sources finished, look at the \" \\\n\t      \"results in $(BUILDDIR)/doctest/output.txt.\"\n\nxml:\n\t$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml\n\t@echo\n\t@echo \"Build finished. The XML files are in $(BUILDDIR)/xml.\"\n\npseudoxml:\n\t$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml\n\t@echo\n\t@echo \"Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml.\"\n"
  },
  {
    "path": "docs/api.rst",
    "content": "API reference\n=============\n\n.. automodule:: camel\n   :members:\n   :undoc-members:\n"
  },
  {
    "path": "docs/camel.rst",
    "content": "Camel overview\n==============\n\nCamel is intended as a replacement for libraries like :py:mod:`pickle` or\n`PyYAML`_, which automagically serialize any type they come across.  That seems\nconvenient at first, but in any large or long-lived application, the benefits\nare soon outweighed by the costs:\n\n.. _PyYAML: http://pyyaml.org/\n\n* You can't move, rename, or delete any types that are encoded in a pickle.\n\n* Even private implementation details of your class are encoded in a pickle by\n  default, which means you can't change them either.\n\n* Because pickle's behavior is recursive, it can be difficult to know which\n  types are pickled.\n\n* Because pickle's behavior is recursive, you may inadvertently pickle far more\n  data than necessary, if your objects have caches or reified properties.  In\n  extreme cases you may pickle configuration that's no longer correct when the\n  pickle is loaded.\n\n* Since pickles aren't part of your codebase and are rarely covered by tests,\n  you may not know you've broken pickles until your code hits production...  or\n  much later.\n\n* Pickle in particular is very opaque, even when using the ASCII format.  It's\n  easy to end up with a needlessly large pickle by accident and have no\n  visibility into what's being stored in it, or to break loading a large pickle\n  and be unable to recover gracefully or even tell where the problem is.\n\n* Automagically serialized data is hard enough to load back into your *own*\n  application.  Loading it anywhere else is effectively out of the question.\n\nIt's certainly possible to whip pickle or PyYAML into shape manually by writing\n``__reduce__`` or representer functions, but their default behavior is still\nautomagic, so you can't be sure you didn't miss something.  Also, nobody\nactually does it, so merely knowing it's possible doesn't help much.\n\n\nCamel's philosophy\n------------------\n\n    Explicit is better than implicit.\n\n    Complex is better than complicated.\n\n    Readability counts.\n\n    If the implementation is hard to explain, it's a bad idea.\n\n    *In the face of ambiguity, refuse the temptation to guess.*\n\n    — `The Zen of Python`_\n\n    .. _The Zen of Python: https://www.python.org/dev/peps/pep-0020/\n\nSerialization is hard.  We can't hide that difficulty, only delay it for a\nwhile.  And it *will* catch up with you.\n\nA few people in the Python community have been rallying against pickle and its\nilk for a while, but when asked for alternatives, all we can do is mumble\nsomething about writing functions.  Well, that's not very helpful.\n\nCamel forces you to write all your own serialization code, then wires it all\ntogether for you.  It's backed by YAML, which is ostensibly easy for humans to\nread — and has explicit support for custom types.  Hopefully, after using\nCamel, you'll discover you've been tricked into making a library of every type\nyou serialize, the YAML name you give it, and exactly how it's formatted.  All\nof this lives in your codebase, so someone refactoring a class will easily\nstumble upon its serialization code.  Why, you could even use this knowledge to\nload your types into an application written in a different language, or turn\nthem into a documented format!\n\n\nLet's see some code already\n---------------------------\n\nLet's!\n\nHere's the Table example from `a talk Alex Gaynor gave at PyCon US 2014`_.\nInitially we have some square tables.\n\n.. _a talk Alex Gaynor gave at PyCon US 2014: https://www.youtube.com/watch?v=7KnfGDajDQw&t=1292\n\n.. code-block:: python\n\n    class Table(object):\n        def __init__(self, size):\n            self.size = size\n\n        def __repr__(self):\n            return \"<Table {self.size!r}>\".format(self=self)\n\nWe want to be able to serialize these, so we write a *dumper* and a\ncorresponding *loader* function.  We'll also need a *registry* to store these\nfunctions::\n\n    from camel import CamelRegistry\n    my_types = CamelRegistry()\n\n    @my_types.dumper(Table, 'table', version=1)\n    def _dump_table(table):\n        return dict(\n            size=table.size,\n        )\n\n    @my_types.loader('table', version=1)\n    def _load_table(data, version):\n        return Table(data[\"size\"])\n\n.. note:: This example is intended for Python 3.  With Python 2,\n   ``dict(size=...)`` will create a \"size\" key that's a :py:class:`bytes`,\n   which will be serialized as ``!!binary``.  It will still work, but it'll be\n   ugly, and won't interop with Python 3.  If you're still on Python 2, you\n   should definitely use dict literals with :py:class:`unicode` keys.\n\nNow we just give this registry to a :py:class:`Camel` object and ask it to dump\nfor us::\n\n    from camel import Camel\n    table = Table(25)\n    print(Camel([my_types]).dump(table))\n\n.. code-block:: yaml\n\n    !table;1\n    size: 25\n\nUnlike the simple example given in the talk, we can also dump arbitrary\nstructures containing Tables with no extra effort::\n\n    data = dict(chairs=[], tables=[Table(25), Table(36)])\n    print(Camel([my_types]).dump(data))\n\n.. code-block:: yaml\n\n    chairs: []\n    tables:\n    - !table;1\n      size: 25\n    - !table;1\n      size: 36\n\nAnd load them back in::\n\n    print(Camel([my_types]).load(\"[!table;1 {size: 100}]\"))\n\n.. code-block:: python\n\n    [<Table 100>]\n\nVersioning\n..........\n\nAs you can see, all serialized Tables are tagged as ``!table;1``.  The\n``table`` part is the argument we gave to ``@dumper`` and ``@loader``, and the\n``1`` is the version number.\n\nVersion numbers mean that when the time comes to change your class, you don't\nhave anything to worry about.  Just write a new loader and dumper with a higher\nversion number, and fix the old loader to work with the new code::\n\n    # Tables can be rectangles now!\n    class Table(object):\n        def __init__(self, height, width):\n            self.height = height\n            self.width = width\n\n        def __repr__(self):\n            return \"<Table {self.height!r}x{self.width!r}>\".format(self=self)\n\n    @my_types.dumper(Table, 'table', version=2)\n    def _dump_table_v2(table):\n        return dict(\n            height=table.height,\n            width=table.width,\n        )\n\n    @my_types.loader('table', version=2)\n    def _load_table_v2(data, version):\n        return Table(data[\"height\"], data[\"width\"])\n\n    @my_types.loader('table', version=1)\n    def _load_table_v1(data, version):\n        edge = data[\"size\"] ** 0.5\n        return Table(edge, edge)\n\n    table = Table(7, 10)\n    print(Camel([my_types]).dump(table))\n\n.. code-block:: yaml\n\n    !table;2\n    height: 7\n    width: 10\n\n\nMore on versions\n----------------\n\nVersions are expected to be positive integers, presumably starting at 1.\nWhenever your class changes, you have two options:\n\n1. Fix the dumper and loader to preserve the old format but work with the new\n   internals.\n2. Failing that, write new dumpers and loaders and bump the version.\n\nOne of the advantages of Camel is that your serialization code is nothing more\nthan functions returning Python structures, so it's very easily tested.  Even\nif you end up with dozens of versions, you can write test cases for each\nwithout ever dealing with YAML at all.\n\nYou might be wondering whether there's any point to having more than one\nversion of a dumper function.  By default, only the dumper with the highest\nversion for a type is used.  But it's possible you may want to stay\nbackwards-compatible with other code — perhaps an older version of your\napplication or library — and thus retain the ability to write out older\nformats.  You can do this with :py:meth:`Camel.lock_version`::\n\n    @my_types.dumper(Table, 'table', version=1)\n    def _dump_table_v1(table):\n        return dict(\n            # not really, but the best we can manage\n            size=table.height * table.width,\n        )\n\n    camel = Camel([my_types])\n    camel.lock_version(Table, 1)\n    print(camel.dump(Table(5, 7)))\n\n.. code-block:: yaml\n\n    !table;1\n    size: 35\n\nObviously you might lose some information when round-tripping through an old\nformat, but sometimes it's necessary until you can fix old code.\n\nNote that version locking only applies to dumping, not to loading.  For\nloading, there are a couple special versions you can use.\n\nLet's say you delete an old class whose information is no longer useful.  While\ncleaning up all references to it, you discover it has Camel dumpers and\nloaders.  What about all your existing data?  No problem!  Just use a version\nof ``all`` and return a dummy object::\n\n    class DummyData(object):\n        def __init__(self, data):\n            self.data = data\n\n    @my_types.loader('deleted-type', version=all)\n    def _load_deleted_type(data, version):\n        return DummyData(data)\n\n``all`` overrides *all* other loader versions (hence the name).  You might\ninstead want to use ``any``, which is a fallback for when the version isn't\nrecognized::\n\n    @my_types.loader('table', version=any)\n    def _load_table(data, version):\n        if 'size' in data:\n            # version 1\n            edge = data['size'] ** 0.5\n            return Table(edge, edge)\n        else:\n            # version 2?\n            return Table(data['height'], data['width'])\n\nVersions must still be integers; a non-integer version will cause an immediate\nparse error.\n\nGoing versionless\n.................\n\nYou might be thinking that the version numbers everywhere are an eyesore, and\nyour data would be much prettier if it only used ``!table``.\n\nWell, yes, it would.  But you'd lose your ability to bump the version, so you'd\nhave to be *very very sure* that your chosen format can be adapted to any\npossible future changes to your class.\n\nIf you are, in fact, *very very sure*, then you can use a version of ``None``.\nThis is treated like an *infinite* version number, so it will always be used\nwhen dumping (unless overridden by a version lock).\n\nSimilarly, an unversioned tag will look for a loader with a ``None`` version,\nthen fall back to ``all`` or ``any``.  The order versions are checked for is\nthus:\n\n* ``None``, if appropriate\n* ``all``\n* Numeric version, if appropriate\n* ``any``\n\nThere are deliberately no examples of unversioned tags here.  Designing an\nunversioned format requires some care, and a trivial documentation example\ncan't do it justice.\n\n\nSupported types\n---------------\n\nBy default, Camel knows how to load and dump all types in the `YAML type\nregistry`_ to their Python equivalents, which are as follows.\n\n.. _YAML type registry: http://yaml.org/type/\n\n===============     ========================================\nYAML tag            Python type\n===============     ========================================\n``!!binary``        :py:class:`bytes`\n``!!bool``          :py:class:`bool`\n``!!float``         :py:class:`float`\n``!!int``           :py:class:`int` (or :py:class:`long` on Python 2)\n``!!map``           :py:class:`dict`\n``!!merge``         —\n``!!null``          :py:class:`NoneType`\n``!!omap``          :py:class:`collections.OrderedDict`\n``!!seq``           :py:class:`list` or :py:class:`tuple` (dump only)\n``!!set``           :py:class:`set` or :py:class:`frozenset` (dump only)\n``!!str``           :py:class:`str` (:py:class:`unicode` on Python 2)\n``!!timestamp``     :py:class:`datetime.date` or :py:class:`datetime.datetime` as appropriate\n===============     ========================================\n\n.. note:: PyYAML tries to guess whether a bytestring is \"really\" a string on\n   Python 2, but Camel does not.  Serializing *any* bytestring produces an ugly\n   base64-encoded ``!!binary`` representation.\n\n   This is a **feature**.\n\n.. note:: A dumper function must return a value that can be expressed in YAML\n   without a tag — that is, any of the above Python types *except*\n   :py:class:`bytes`, :py:class:`set`/:py:class:`frozenset`, and\n   :py:class:`datetime.date`/:py:class:`datetime.datetime`.  (Of course, if the\n   value is a container, its contents can be anything and will be serialized\n   recursively.)\n\n   If a dumper returns a :py:class:`collections.OrderedDict`, it will be\n   serialized like a plain dict, but the order of its keys will be preserved.\n\nThe following additional types are loaded by default, but **not dumped**.  If\nyou want to dump these types, you can use the existing ``camel.PYTHON_TYPES``\nregistry.\n\n======================  =====================================\nYAML tag                Python type\n======================  =====================================\n``!!python/complex``    :py:class:`complex`\n``!!python/frozenset``  :py:class:`frozenset`\n``!!python/namespace``  :py:class:`types.SimpleNamespace` (Python 3.3+)\n``!!python/tuple``      :py:class:`tuple`\n======================  =====================================\n\n\nOther design notes\n------------------\n\n* Camel will automatically use the C extension if available, and fall back to a\n  Python implementation otherwise.  The PyYAML documentation says it doesn't\n  have this behavior because there are some slight differences between the\n  implementations, but fails to explain what they are.\n\n* :py:meth:`Camel.load` is safe by default.  There is no calling of arbitrary\n  functions or execution of arbitrary code just from loading data.  There is no\n  \"dangerous\" mode.  PyYAML's ``!!python/object`` and similar tags are not\n  supported.  (Unless you write your own loaders for them, of course.)\n\n* There is no \"OO\" interface, where dumpers or loaders can be written as\n  methods with special names.  That approach forces a class to have only a\n  single representation, and more importantly litters your class with junk\n  unrelated to the class itself.  Consider this a cheap implementation of\n  traits.  You can fairly easily build support for this in your application if\n  you really *really* want it.\n\n* Yes, you may have to write a lot of boring code like this::\n\n    @my_types.dumper(SomeType, 'sometype')\n    def _dump_sometype(data):\n        return dict(\n            foo=data.foo,\n            bar=data.bar,\n            baz=data.baz,\n            ...\n        )\n\n  I strongly encourage you *not* to do this automatically using introspection,\n  which would defeat the point of using Camel.  If it's painful, step back and\n  consider whether you really need to be serializing as much as you are, or\n  whether your classes need to be so large.\n\n* There's no guarantee that the data you get will actually be in the correct\n  format for that version.  YAML is meant for human beings, after all, and\n  human beings make mistakes.  If you're concerned about this, you could\n  combine Camel with something like the `Colander`_ library.\n\n  .. _Colander: http://docs.pylonsproject.org/projects/colander/en/latest/\n\n\nKnown issues\n------------\n\nCamel is a fairly simple wrapper around `PyYAML`_, and inherits many of its\nproblems.  Only YAML 1.1 is supported, not 1.2, so a handful of syntactic edge\ncases may not parse correctly.  Loading and dumping are certainly slower and\nmore memory-intensive than pickle or JSON.  Unicode handling is slightly\nclumsy.  Python-specific types use tags starting with ``!!``, which is supposed\nfor be for YAML's types only.\n\n.. _PyYAML: http://pyyaml.org/\n\nFormatting and comments are not preserved during a round-trip load and dump.\nThe `ruamel.yaml`_ library is a fork of PyYAML that solves this problem, but it\nonly works when using the pure-Python implementation, which would hurt Camel's\nperformance even more.  Opinions welcome.\n\n.. _ruamel.yaml: https://pypi.python.org/pypi/ruamel.yaml\n\nPyYAML has several features that aren't exposed in Camel yet: dumpers that work\non subclasses, loaders that work on all tags with a given prefix, and parsers\nfor plain scalars in custom formats.\n"
  },
  {
    "path": "docs/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# Camel documentation build configuration file, created by\n# sphinx-quickstart2 on Fri May  8 11:39:04 2015.\n#\n# This file is execfile()d with the current directory set to its\n# containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\nimport sys\nimport os\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\nsys.path.insert(0, os.path.abspath('..'))\n\n# -- General configuration ------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx.ext.autodoc',\n    'sphinx.ext.intersphinx',\n]\nintersphinx_mapping = {\n    'python': ('https://docs.python.org/3', None),\n  }\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix of source filenames.\nsource_suffix = '.rst'\n\n# The encoding of source files.\n#source_encoding = 'utf-8-sig'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = u'Camel'\ncopyright = u'2015, Eevee (Lexy Munroe)'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = '0.1'\n# The full version, including alpha/beta/rc tags.\nrelease = '0.1.2'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#language = None\n\n# There are two options for replacing |today|: either, you set today to some\n# non-false value, then it is used:\n#today = ''\n# Else, today_fmt is used as the format for a strftime call.\n#today_fmt = '%B %d, %Y'\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\nexclude_patterns = ['_build']\n\n# The reST default role (used for this markup: `text`) to use for all\n# documents.\n#default_role = None\n\n# If true, '()' will be appended to :func: etc. cross-reference text.\n#add_function_parentheses = True\n\n# If true, the current module name will be prepended to all description\n# unit titles (such as .. function::).\n#add_module_names = True\n\n# If true, sectionauthor and moduleauthor directives will be shown in the\n# output. They are ignored by default.\n#show_authors = False\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# A list of ignored prefixes for module index sorting.\n#modindex_common_prefix = []\n\n# If true, keep warnings as \"system message\" paragraphs in the built documents.\n#keep_warnings = False\n\n\n# -- Options for HTML output ----------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\nhtml_theme = 'classic'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#html_theme_options = {}\n\n# Add any paths that contain custom themes here, relative to this directory.\n#html_theme_path = []\n\n# The name for this set of Sphinx documents.  If None, it defaults to\n# \"<project> v<release> documentation\".\n#html_title = None\n\n# A shorter title for the navigation bar.  Default is the same as html_title.\n#html_short_title = None\n\n# The name of an image file (relative to this directory) to place at the top\n# of the sidebar.\n#html_logo = None\n\n# The name of an image file (within the static path) to use as favicon of the\n# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32\n# pixels large.\n#html_favicon = None\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n# Add any extra paths that contain custom files (such as robots.txt or\n# .htaccess) here, relative to this directory. These files are copied\n# directly to the root of the documentation.\n#html_extra_path = []\n\n# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,\n# using the given strftime format.\n#html_last_updated_fmt = '%b %d, %Y'\n\n# If true, SmartyPants will be used to convert quotes and dashes to\n# typographically correct entities.\n#html_use_smartypants = True\n\n# Custom sidebar templates, maps document names to template names.\n#html_sidebars = {}\n\n# Additional templates that should be rendered to pages, maps page names to\n# template names.\n#html_additional_pages = {}\n\n# If false, no module index is generated.\n#html_domain_indices = True\n\n# If false, no index is generated.\n#html_use_index = True\n\n# If true, the index is split into individual pages for each letter.\n#html_split_index = False\n\n# If true, links to the reST sources are added to the pages.\n#html_show_sourcelink = True\n\n# If true, \"Created using Sphinx\" is shown in the HTML footer. Default is True.\n#html_show_sphinx = True\n\n# If true, \"(C) Copyright ...\" is shown in the HTML footer. Default is True.\n#html_show_copyright = True\n\n# If true, an OpenSearch description file will be output, and all pages will\n# contain a <link> tag referring to it.  The value of this option must be the\n# base URL from which the finished HTML is served.\n#html_use_opensearch = ''\n\n# This is the file name suffix for HTML files (e.g. \".xhtml\").\n#html_file_suffix = None\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'Cameldoc'\n\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements = {\n# The paper size ('letterpaper' or 'a4paper').\n#'papersize': 'letterpaper',\n\n# The font size ('10pt', '11pt' or '12pt').\n#'pointsize': '10pt',\n\n# Additional stuff for the LaTeX preamble.\n#'preamble': '',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n  ('index', 'Camel.tex', u'Camel Documentation',\n   u'Eevee (Alex Munroe)', 'manual'),\n]\n\n# The name of an image file (relative to this directory) to place at the top of\n# the title page.\n#latex_logo = None\n\n# For \"manual\" documents, if this is true, then toplevel headings are parts,\n# not chapters.\n#latex_use_parts = False\n\n# If true, show page references after internal links.\n#latex_show_pagerefs = False\n\n# If true, show URL addresses after external links.\n#latex_show_urls = False\n\n# Documents to append as an appendix to all manuals.\n#latex_appendices = []\n\n# If false, no module index is generated.\n#latex_domain_indices = True\n\n\n# -- Options for manual page output ---------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    ('index', 'camel', u'Camel Documentation',\n     [u'Eevee (Lexy Munroe)'], 1)\n]\n\n# If true, show URL addresses after external links.\n#man_show_urls = False\n\n\n# -- Options for Texinfo output -------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n  ('index', 'Camel', u'Camel Documentation',\n   u'Eevee (Lexy Munroe)', 'Camel', 'One line description of project.',\n   'Miscellaneous'),\n]\n\n# Documents to append as an appendix to all manuals.\n#texinfo_appendices = []\n\n# If false, no module index is generated.\n#texinfo_domain_indices = True\n\n# How to display URL addresses: 'footnote', 'no', or 'inline'.\n#texinfo_show_urls = 'footnote'\n\n# If true, do not generate a @detailmenu in the \"Top\" node's menu.\n#texinfo_no_detailmenu = False\n"
  },
  {
    "path": "docs/index.rst",
    "content": ".. Camel documentation master file, created by\n   sphinx-quickstart2 on Fri May  8 11:39:04 2015.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\nWelcome to Camel's documentation!\n=================================\n\nCamel is a Python serialization library that forces you to explicitly describe\nhow to dump or load your types.  It's good for you, just like eating your\nvegetables.\n\nContents:\n\n.. toctree::\n   :maxdepth: 2\n\n   camel\n   yamlref\n   api\n"
  },
  {
    "path": "docs/make.bat",
    "content": "@ECHO OFF\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sphinx-build2\r\n)\r\nset BUILDDIR=_build\r\nset ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .\r\nset I18NSPHINXOPTS=%SPHINXOPTS% .\r\nif NOT \"%PAPER%\" == \"\" (\r\n\tset ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%\r\n\tset I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%\r\n)\r\n\r\nif \"%1\" == \"\" goto help\r\n\r\nif \"%1\" == \"help\" (\r\n\t:help\r\n\techo.Please use `make ^<target^>` where ^<target^> is one of\r\n\techo.  html       to make standalone HTML files\r\n\techo.  dirhtml    to make HTML files named index.html in directories\r\n\techo.  singlehtml to make a single large HTML file\r\n\techo.  pickle     to make pickle files\r\n\techo.  json       to make JSON files\r\n\techo.  htmlhelp   to make HTML files and a HTML help project\r\n\techo.  qthelp     to make HTML files and a qthelp project\r\n\techo.  devhelp    to make HTML files and a Devhelp project\r\n\techo.  epub       to make an epub\r\n\techo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter\r\n\techo.  text       to make text files\r\n\techo.  man        to make manual pages\r\n\techo.  texinfo    to make Texinfo files\r\n\techo.  gettext    to make PO message catalogs\r\n\techo.  changes    to make an overview over all changed/added/deprecated items\r\n\techo.  xml        to make Docutils-native XML files\r\n\techo.  pseudoxml  to make pseudoxml-XML files for display purposes\r\n\techo.  linkcheck  to check all external links for integrity\r\n\techo.  doctest    to run all doctests embedded in the documentation if enabled\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"clean\" (\r\n\tfor /d %%i in (%BUILDDIR%\\*) do rmdir /q /s %%i\r\n\tdel /q /s %BUILDDIR%\\*\r\n\tgoto end\r\n)\r\n\r\n\r\n%SPHINXBUILD% 2> nul\r\nif errorlevel 9009 (\r\n\techo.\r\n\techo.The 'sphinx-build2' command was not found. Make sure you have Sphinx\r\n\techo.installed, then set the SPHINXBUILD environment variable to point\r\n\techo.to the full path of the 'sphinx-build2' executable. Alternatively you\r\n\techo.may add the Sphinx directory to PATH.\r\n\techo.\r\n\techo.If you don't have Sphinx installed, grab it from\r\n\techo.http://sphinx-doc.org/\r\n\texit /b 1\r\n)\r\n\r\nif \"%1\" == \"html\" (\r\n\t%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished. The HTML pages are in %BUILDDIR%/html.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"dirhtml\" (\r\n\t%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"singlehtml\" (\r\n\t%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"pickle\" (\r\n\t%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished; now you can process the pickle files.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"json\" (\r\n\t%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished; now you can process the JSON files.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"htmlhelp\" (\r\n\t%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished; now you can run HTML Help Workshop with the ^\r\n.hhp project file in %BUILDDIR%/htmlhelp.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"qthelp\" (\r\n\t%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished; now you can run \"qcollectiongenerator\" with the ^\r\n.qhcp project file in %BUILDDIR%/qthelp, like this:\r\n\techo.^> qcollectiongenerator %BUILDDIR%\\qthelp\\Camel.qhcp\r\n\techo.To view the help file:\r\n\techo.^> assistant -collectionFile %BUILDDIR%\\qthelp\\Camel.ghc\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"devhelp\" (\r\n\t%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"epub\" (\r\n\t%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished. The epub file is in %BUILDDIR%/epub.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"latex\" (\r\n\t%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished; the LaTeX files are in %BUILDDIR%/latex.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"latexpdf\" (\r\n\t%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex\r\n\tcd %BUILDDIR%/latex\r\n\tmake all-pdf\r\n\tcd %BUILDDIR%/..\r\n\techo.\r\n\techo.Build finished; the PDF files are in %BUILDDIR%/latex.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"latexpdfja\" (\r\n\t%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex\r\n\tcd %BUILDDIR%/latex\r\n\tmake all-pdf-ja\r\n\tcd %BUILDDIR%/..\r\n\techo.\r\n\techo.Build finished; the PDF files are in %BUILDDIR%/latex.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"text\" (\r\n\t%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished. The text files are in %BUILDDIR%/text.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"man\" (\r\n\t%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished. The manual pages are in %BUILDDIR%/man.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"texinfo\" (\r\n\t%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"gettext\" (\r\n\t%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished. The message catalogs are in %BUILDDIR%/locale.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"changes\" (\r\n\t%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.The overview file is in %BUILDDIR%/changes.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"linkcheck\" (\r\n\t%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Link check complete; look for any errors in the above output ^\r\nor in %BUILDDIR%/linkcheck/output.txt.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"doctest\" (\r\n\t%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Testing of doctests in the sources finished, look at the ^\r\nresults in %BUILDDIR%/doctest/output.txt.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"xml\" (\r\n\t%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished. The XML files are in %BUILDDIR%/xml.\r\n\tgoto end\r\n)\r\n\r\nif \"%1\" == \"pseudoxml\" (\r\n\t%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml\r\n\tif errorlevel 1 exit /b 1\r\n\techo.\r\n\techo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.\r\n\tgoto end\r\n)\r\n\r\n:end\r\n"
  },
  {
    "path": "docs/yamlref.rst",
    "content": "Brief YAML reference\n====================\n\nThere is no official YAML reference guide.  The YAML website only offers the\n`YAML specification`_, which is a dense and thorny tome clearly aimed at\nimplementers.  I suspect this has greatly hampered YAML's popularity.\n\n.. _YAML specification: http://www.yaml.org/spec/1.2/spec.html\n\nIn the hopes of improving this situation, here is a very quick YAML overview\nthat should describe the language almost entirely.  Hopefully it's useful\nwhether or not you use Camel.\n\n\nOverall structure and design\n----------------------------\n\nAs I see it, YAML has two primary goals: to support encoding any arbitrary data\nstructure; and to be easily read and written by humans.  If only the spec\nshared that last goal.\n\nHuman-readability means that much of YAML's syntax is optional, wherever it\nwould be unambiguous and easier on a human.  The trade-off is more complexity\nin parsers and emitters.\n\nHere's an example document, configuration for some hypothetical app:\n\n.. code-block:: yaml\n\n    database:\n        username: admin\n        password: foobar  # TODO get prod passwords out of config\n        socket: /var/tmp/database.sock\n        options: {use_utf8: true}\n    memcached:\n        host: 10.0.0.99\n    workers:\n      - host: 10.0.0.101\n        port: 2301\n      - host: 10.0.0.102\n        port: 2302\n\nYAML often has more than one way to express the same data, leaving a human free\nto use whichever is most convenient.  More convenient syntax tends to be more\ncontextual or whitespace-sensitive.  In the above document, you can see that\nindenting is enough to make a nested mapping.  Integers and booleans are\nautomatically distinguished from unquoted strings, as well.\n\n\nGeneral syntax\n--------------\n\nAs of 1.2, YAML is a strict superset of JSON.  Any valid JSON can be parsed in\nthe same structure with a YAML 1.2 parser.\n\nYAML is designed around Unicode, not bytes, and its syntax assumes Unicode\ninput.  There is no syntactic mechanism for giving a character encoding; the\nparser is expected to recognize BOMs for UTF-8, UTF-16, and UTF-32, but\notherwise a byte stream is assumed to be UTF-8.\n\nThe only vertical whitespace characters are U+000A LINE FEED and U+000D\nCARRIAGE RETURN.  The only horizontal whitespace characters are U+0009 TAB and\nU+0020 SPACE.  Other control characters are not allowed anywhere.  Otherwise,\nanything goes.\n\nYAML operates on *streams*, which can contain multiple distinct structures,\neach parsed individually.  Each structure is called a *document*.\n\nA document begins with ``---`` and ends with ``...``.  Both are optional,\nthough a ``...`` can only be followed by directives or ``---``.  You don't see\nmultiple documents very often, but it's a very useful feature for sending\nintermittent chunks of data over a single network connection.  With JSON you'd\nusually put each chunk on its own line and delimit with newlines; YAML has\nsupport built in.\n\nDocuments may be preceded by *directives*, in which case the ``---`` is\nrequired to indicate the end of the directives.  Directives are a ``%``\nfollowed by an identifier and some parameters.  (This is how directives are\ndistinguished from a bare document without ``---``, so the first non-blank\nnon-comment line of a document can't start with a ``%``.)\n\nThere are only two directives at the moment: ``%YAML`` specifies the YAML\nversion of the document, and ``%TAG`` is used for tag shorthand, described\nin :ref:`yamlref-more-tags`.  Use of directives is, again, fairly uncommon.\n\n*Comments* may appear anywhere.  ``#`` begins a comment, and it runs until the\nend of the line.  In most cases, comments are whitespace: they don't affect\nindentation level, they can appear between any two tokens, and a comment on its\nown line is the same as a blank line.  The few exceptions are not too\nsurprising; for example, you can't have a comment between the key and colon in\n``key:``.\n\nA YAML document is a graph of values, called *nodes*.  See\n:ref:`yamlref-kinds`.\n\nNodes may be prefixed with up to two properties: a *tag* and an *anchor*.\nOrder doesn't matter, and both are optional.  Properties can be given to any\nvalue, regardless of kind or style.\n\nTags\n....\n\nTags are prefixed with ``!`` and describe the *type* of a node.  This allows\nfor adding new types without having to extend the syntax or mingle type\ninformation with data.  Omitting the tag leaves the type to the parser's\ndiscretion; usually that means you'll get lists, dicts, strings, numbers, and\nother simple types.\n\nYou'll probably only see tags in two forms:\n\n* ``!foo`` is a \"local\" tag, used for some custom type that's specific to the\n  document.\n\n* ``!!bar`` is a built-in YAML type from the `YAML tag repository`_.  Most of\n  these are inferred from plain data — ``!!seq`` for sequences, ``!!int`` for\n  numbers, and so on — but a few don't have dedicated syntax and have to be\n  given explicitly.\n\n  For example, ``!!binary`` is used for representing arbitrary binary data\n  encoded as base64.  So ``!!binary aGVsbG8=`` would parse as the bytestring\n  ``hello``.\n\n.. _YAML tag repository: http://yaml.org/type/\n\nThere's much more to tags, most of which is rarely used in practice.  See\n:ref:`yamlref-more-tags`.\n\nAnchors\n.......\n\nThe other node property is the *anchor*, which is how YAML can store recursive\ndata structures.  Anchor names are prefixed with ``&`` and can't contain\nwhitespace, brackets, braces, or commas.\n\nAn *alias node* is an anchor name prefixed with ``*``, and indicates that the\nnode with that anchor name should occur in both places.  (Alias nodes can't\nhave properties themselves; the properties of the anchored node are used.)\nFor example, you might share configuration::\n\n    host1:\n        &common-host\n        os: linux\n        arch: x86_64\n    host2: *common-host\n\nOr serialize a list that contains itself::\n\n    &me [*me]\n\n.. note:: This is **not** a copy.  The exact same value is reused.\n\nAnchor names act somewhat like variable assignments: at any point in the\ndocument, the parser only knows about the anchors it's seen so far, and a\nsecond anchor with the same name takes precedence.  This means that aliases\ncannot refer to anchors that appear later in the document.\n\nAnchor names aren't intended to carry information, which unfortunately means\nthat most YAML parsers throw them away, and re-serializing a document will get\nyou anchor names like ``ANCHOR1``.\n\n\n.. _yamlref-kinds:\n\nKinds of value\n--------------\n\nValues come in one of three *kinds*, which reflect the general \"shape\" of\nthe data.  Scalars are individual values; sequences are ordered collections;\nmappings are unordered associations.  Each can be written in either a\nwhitespace-sensitive *block style* or a more compact and explicit *flow style*.\n\nScalars\n.......\n\nMost values in a YAML document will be *plain scalars*.  They're defined by\nexclusion: if it's not anything else, it's a plain scalar.  Technically, they\ncan only be flow style, so they're really \"plain flow scalar style\" scalars.\n\nPlain scalars are the most flexible kind of value, and may resolve to a variety\nof types from the `YAML tag repository`_:\n\n* Integers become, well, integers (``!!int``).  Leading ``0``, ``0b``, and\n  ``0x`` are recognized as octal, binary, and hexadecimal.  ``_`` is allowed,\n  and ignored.  Curiously, ``:`` is allowed and treated as a base 60 delimiter,\n  so you can write a time as ``1:59`` and it'll be loaded as the number of\n  seconds, 119.\n\n* Floats become floats (``!!float``).  Scientific notation using ``e`` is also\n  recognized.  As with integers, ``_`` is ignored and ``:`` indicates base 60,\n  though only the last component can have a fractional part.  Positive\n  infinity, negative infinity, and not-a-number are recognized with a leading\n  dot: ``.inf``, ``-.inf``, and ``.nan``.\n\n* ``true`` and ``false`` become booleans (``!!bool``).  ``y``, ``n``, ``yes``, ``no``,\n  ``on``, and ``off`` are allowed as synonyms.  Uppercase and title case are\n  also recognized.\n\n* ``~`` and ``null`` become nulls (``!!null``), which is ``None`` in Python.  A\n  completely empty value also becomes null.\n\n* ISO8601 dates are recognized (``!!timestamp``), with whitespace allowed\n  between the date and time.  The time is also optional, and defaults to\n  midnight UTC.\n\n* ``=`` is a special value (``!!value``) used as a key in mappings.  I've never\n  seen it actually used, and the thing it does is nonsense in many languages\n  anyway, so don't worry about it.  Just remember you can't use ``=`` as a\n  plain string.\n\n* ``<<`` is another special value (``!!merge``) used as a key in mappings.\n  This one is actually kind of useful; it's described below in\n  :ref:`yamlref-merge-keys`.\n\n.. note:: The YAML spec has a notion of *schemas*, sets of types which are\n   recognized.  The recommended schema is \"core\", which doesn't actually\n   require ``!!timestamp`` support.  I think the idea is to avoid requiring\n   support for types that may not exist natively — a Perl YAML parser can't\n   reasonably handle ``!!timestamp`` out of the box, because Perl has no\n   built-in timestamp type.  So while you could technically run into a parser\n   that doesn't support floats (the \"failsafe\" schema only does strings!), it\n   probably won't come as a surprise.\n\nOtherwise, it's a string.  Well.  Probably.  As part of tag resolution (see\n:ref:`yamlref-more-tags`), an application is allowed to parse plain scalars\nhowever it wants; you might add logic that parses ``1..5`` as a range type, or\nyou might recognize keywords and replace them with special objects.  But if\nyou're doing any of that, you're hopefully aware of it.\n\nBetween the above parsing and conflicts with the rest of YAML's syntax, for a\nplain scalar to be a string, it must meet these restrictions:\n\n* It must not be ``true``, ``false``, ``yes``, ``no``, ``y``, ``n``, ``on``,\n  ``off``, ``null``, or any of those words in uppercase or title case, which\n  would all be parsed as booleans or nulls.\n\n* It must not be ``~``, which is null.  If it's a mapping key, it must not be\n  ``=`` or ``<<``, which are special key values.\n\n* It must not be something that looks like a number or timestamp.  I wouldn't\n  bet on anything that consists exclusively of digits, dashes, underscores, and\n  colons.\n\n* The first character must not be any of: ``[`` ``]`` ``{`` ``}`` ``,`` ``#``\n  ``&`` ``*`` ``!`` ``|`` ``>`` ``'`` ``\"`` ``%`` ``@`` `````.  All of these\n  are YAML syntax for some other kind of construct.\n\n* If the first character is ``?``, ``:``, or ``-``, the next character must not\n  be whitespace.  Otherwise it'll be parsed as a block mapping or sequence.\n\n* It must not contain `` #`` or ``: ``, which would be parsed as a comment or a\n  key.  A hash not preceded by space or a colon not followed by space is fine.\n\n* If the string is inside a flow collection (i.e., inside ``[...]`` or\n  ``{...}``), it must not contain any of ``[`` ``]`` ``{`` ``}`` ``,``, which\n  would all be parsed as part of the collection syntax.\n\n* Leading and trailing whitespace are ignored.\n\n* If the string is broken across lines, then the newline and any adjacent\n  whitespace are collapsed into a single space.\n\nThat actually leaves you fairly wide open; the biggest restriction is on the\nfirst character.  You can have spaces, you can wrap across lines, you can\ninclude whatever (non-control) Unicode you want.\n\nIf you need explicit strings, you have some other options.\n\n\nStrings\n```````\n\nYAML has lots of ways to write explicit strings.  Aside from plain scalars,\nthere are two other *flow scalar styles*.\n\nSingle-quoted strings are surrounded by ``'``.  Single quotes may be escaped as\n``''``, but otherwise no escaping is done at all.  You may wrap over multiple\nlines, but the newline and any surrounding whitespace becomes a single space.\nA line containing only whitespace becomes a newline.\n\nDouble-quoted strings are surrounded by ``\"``.  Backslash escapes are recognized:\n\n==============      ======\nSequence            Result\n==============      ======\n``\\0``              U+0000 NULL\n``\\a``              U+0007 BELL\n``\\b``              U+0008 BACKSPACE\n``\\t``              U+0009 CHARACTER TABULATION\n``\\n``              U+000A LINE FEED\n``\\v``              U+000B LINE TABULATION\n``\\f``              U+000C FORM FEED\n``\\r``              U+000D CARRIAGE RETURN\n``\\e``              U+001B ESCAPE\n``\\\"``              U+0022 QUOTATION MARK\n``\\/``              U+002F SOLIDUS\n``\\\\``              U+005C REVERSE SOLIDUS\n``\\N``              U+0085 NEXT LINE\n``\\_``              U+00A0 NO-BREAK SPACE\n``\\L``              U+2028 LINE SEPARATOR\n``\\P``              U+2029 PARAGRAPH SEPARATOR\n``\\xNN``            Unicode character ``NN``\n``\\uNNNN``          Unicode character ``NNNN``\n``\\UNNNNNNNN``      Unicode character ``NNNNNNNN``\n==============      ======\n\nAs usual, you may wrap a double-quoted string across multiple lines, but the\nnewline and any surrounding whitespace becomes a single space.  As with\nsingle-quoted strings, a line containing only whitespace becomes a newline.\nYou can escape spaces and tabs to protect them from being thrown away.  You\ncan also escape a newline to preserve any trailing whitespace on that line, but\nthrow away the newline and any leading whitespace on the next line.\n\nThese rules are weird, so here's a contrived example::\n\n    \"line  \\\n        one\n\n        line two\\n\\\n    \\ \\ line three\\nline four\\n\n    line five\n    \"\n\nWhich becomes::\n\n    line  one\n    line two\n      line three\n    line four\n     line five\n\nRight, well, I hope that clears that up.\n\nThere are also two *block scalar styles*, both consisting of a header followed by an\nindented block.  The header is usually just a single character, indicating\nwhich block style to use.\n\n``|`` indicates *literal style*, which preserves all newlines in the indented\nblock.  ``>`` indicates *folded style*, which performs the same line folding as\nwith quoted strings.  Escaped characters are not recognized in either style.\nIndentation, the initial newline, and any leading blank lines are always\nignored.\n\nSo to represent this string::\n\n    This is paragraph one.\n\n    This is paragraph two.\n\nYou could use either literal style::\n\n    |\n        This is paragraph one.\n\n        This is paragraph two.\n\nOr folded style::\n\n    >\n        This is\n        paragraph one.\n\n\n        This\n        is paragraph\n        two.\n\nObviously folded style is more useful if you have paragraphs with longer lines.\nNote that there are two blank lines between paragraphs in folded style; a\nsingle blank line would be parsed as a single newline.\n\nThe header has some other features, but I've never seen them used.  It consists\nof up to three parts, with no intervening whitespace.\n\n1. The character indicating which block style to use.\n2. Optionally, the indentation level of the indented block, relative to its\n   parent.  You only need this if the first line of the block starts with a\n   space, because the space would be interpreted as indentation.\n3. Optionally, a \"chomping\" indicator.  The default behavior is to include the\n   final newline as part of the string, but ignore any subsequent empty lines.\n   You can use ``-`` here to ignore the final newline as well, or use ``+`` to\n   preserve all trailing whitespace verbatim.\n\nYou can put a comment on the same line as the header, but a comment on the next\nline would be interpreted as part of the indented block.  You can also put a\ntag or an anchor before the header, as with any other node.\n\n\nSequences\n.........\n\nSequences are ordered collections, with type ``!!seq``.  They're pretty simple.\n\nFlow style is a comma-delimited list in square brackets, just like JSON:\n``[one, two, 3]``.  A trailing comma is allowed, and whitespace is generally\nignored.  The contents must also be written in flow style.\n\nBlock style is written like a bulleted list::\n\n    - one\n    - two\n    - 3\n    - a plain scalar that's\n      wrapped across multiple lines\n\nIndentation determines where each element ends, and where the entire sequence\nends.\n\nOther blocks may be nested without intervening newlines::\n\n    - - one one\n      - one two\n    - - two one\n      - two two\n\n\nMappings\n........\n\nMappings are unordered, er, mappings, with type ``!!map``.  The keys must be\nunique, but may be of any type.  Also, they're unordered.\n\nDid I mention that mappings are **unordered**?  The order of the keys in the\ndocument is irrelevant and arbitrary.  If you need order, you need a sequence.\n\nFlow style looks unsurprisingly like JSON: ``{x: 1, y: 2}``.  Again, a trailing\ncomma is allowed, and whitespace doesn't matter.\n\nAs a special case, inside a sequence, you can write a single-pair mapping\nwithout the braces.  So ``[a: b, c: d, e: f]`` is a sequence containing three\nmappings.  This is allowed in block sequences too, and is used for the ordered\nmapping type ``!!omap``.\n\nBlock style is actually a little funny.  The canonical form is a little\nsurprising::\n\n    ? x\n    : 1\n    ? y\n    : 2\n\n``?`` introduces a key, and ``:`` introduces a value.  You very rarely see this\nform, because the ``?`` is optional as long as the key and colon are all on one\nline (to avoid ambiguity) and the key is no more than 1024 characters long (to\navoid needing infinite lookahead).\n\nSo that's more commonly written like this::\n\n    x: 1\n    y: 2\n\nThe explicit ``?`` syntax is more useful for complex keys.  For example, it's\nthe only way to use block styles in the key::\n\n    ? >\n        If a train leaves Denver at 5:00 PM traveling at 90 MPH, and another\n        train leaves New York City at 10:00 PM traveling at 80 MPH, by how many\n        minutes are you going to miss your connection?\n    : Depends whether we're on Daylight Saving Time or not.\n\nOther than the syntactic restrictions, an implicit key isn't special in any way\nand can also be of any type::\n\n    true: false\n    null: null\n    up: down\n    [0, 1]: [1, 0]\n\nIt's fairly uncommon to see anything but strings as keys, though, since\nlanguages often don't support it.  Python can't have lists and dicts as dict\nkeys; Perl 5 and JavaScript only support string keys; and so on.\n\nUnlike sequences, you may **not** nest another block inside a block mapping on\nthe same line.  This is invalid::\n\n    one: two: buckle my shoe\n\nBut this is fine::\n\n    - one: 1\n      two: 2\n    - three: 3\n      four: 4\n\nYou can also nest a block sequence without indenting::\n\n    foods:\n    - burger\n    - fries\n    drinks:\n    - soda\n    - iced tea\n\nOne slight syntactic wrinkle: in either style, the colon must be followed by\nwhitespace.  ``foo:bar`` is a single string, remember.  (For JSON's sake, the\nwhitespace can be omitted if the colon immediately follows a flow sequence, a\nflow mapping, or a quoted string.)\n\n.. _yamlref-merge-keys:\n\nMerge keys\n``````````\n\nThese are written ``<<`` and have type ``!!merge``.  A merge key should have\nanother mapping (or sequence of mappings) as its value.  Each mapping is merged\ninto the containing mapping, with any existing keys left alone.  The actual\n``<<`` key is never shown to the application.\n\nThis is generally used in conjunction with anchors to share default values::\n\n    defaults: &DEFAULTS\n        use-tls: true\n        verify-host: true\n    host1:\n        <<: *DEFAULTS\n        hostname: example.com\n    host2:\n        <<: *DEFAULTS\n        hostname: example2.com\n    host3:\n        <<: *DEFAULTS\n        hostname: example3.com\n        # we have a really, really good reason for doing this, really\n        verify-host: false\n\n\n.. _yamlref-more-tags:\n\nMore on tags\n------------\n\n``!!str`` is actually an illusion.\n\nTag names are actually URIs, using UTF-8 percent-encoding.  YAML suggests using\nthe ``tag:`` scheme and your domain name to help keep tags globally unique; for\nexample, the string tag is really ``tag:yaml.org,2002:str``.  (Domain names can\nchange hands over time, hence the inclusion of a year.)\n\nThat's quite a mouthful, and wouldn't be recognized as a tag anyway, because\ntags have to start with ``!``.  So tags are written in shorthand with a prefix,\nlike ``!foo!bar``.  The ``!foo!`` is a *named tag handle* that expands to a\ngiven prefix, kind of like XML namespacing.  Named tag handles must be defined\nby a ``%TAG`` directive before the document::\n\n    %TAG !foo! tag:example.com,2015:app/\n\nA tag of ``!foo!bar`` would then resolve to ``tag:example.com,2015:app/bar``.\n\nI've never seen ``%TAG`` used in practice.  Instead, everyone uses the two\nspecial tag handles.\n\n* The *primary tag handle* is ``!``, which by default expands to ``!``.  So\n  ``!bar`` just resolves to ``!bar``, a *local tag*, specific to the document\n  and not expected to be unique.\n\n* The *secondary tag handle* is ``!!``, which by default expands to\n  ``tag:yaml.org,2002:``, the prefix YAML uses for its own built-in types.  So\n  ``!!bar`` resolves to ``tag:yaml.org,2002:bar``, and the tag for a string\n  would more commonly be written as ``!!str``.  Defining new tags that use\n  ``!!`` is impolite.\n\nBoth special handles can be reassigned with ``%TAG``, just like any other\nhandle.  An important (and confusing) point here is that the **resolved** name\ndetermines whether or not a tag is local; how it's written is irrelevant.\nYou're free to do this::\n\n    %TAG !foo! !foo-types/\n\nNow ``!foo!bar`` is shorthand for ``!foo-types/bar``, which is a local tag.\nYou can also do the reverse::\n\n  %TAG ! tag:example.com,2015:legacy-types/\n\nWhich would make ``!bar`` a global tag!  This is deliberate, as a quick way to\nconvert an entire document from local tags to global tags.\n\nYou can reassign ``!!``, too.  But let's not.\n\nTags can also be written *verbatim* as ``!<foo>``, in which case ``foo`` is\ntaken to be the resolved final name of the tag, ignoring ``%TAG`` and any other\nresolution mechanism.  This is the only way to write a global tag without using\n``%TAG``, since tags must start with a ``!``.\n\nEvery node has a tag, whether it's given one explicitly or not.  Nodes without\nexplicit tags are given one of two special *non-specific* tags: ``!`` for\nquoted and folded scalars; or ``?`` for sequences, mappings, and plain scalars.\n\nThe ``?`` tag tells the application to do *tag resolution*.  Technically, this\nmeans the application can do any kind of arbitrary inspection to figure out the\ntype of the node.  In practice, it just means that scalars are inspected to see\nwhether they're booleans, integers, floats, whatever else, or just strings.\n\nThe ``!`` tag forces a node to be interpreted as a basic built-in type, based\non its kind: ``!!str``, ``!!seq``, or ``!!map``.  You can explicitly give the\n``!`` tag to a node if you want, for example writing ``! true`` or ``! 133`` to\nforce parsing as strings.  Or you could use quotes.  Just saying.\n"
  },
  {
    "path": "setup.py",
    "content": "from setuptools import find_packages, setup\nfrom io import open\n\n\nsetup(\n    name='camel',\n    version='0.1.2',\n    description=\"Python serialization for adults\",\n    long_description=open('README.txt', encoding='utf8').read(),\n    url=\"https://github.com/eevee/camel\",\n    author=\"Eevee (Lexy Munroe)\",\n    author_email=\"eevee.camel@veekun.com\",\n    classifiers=[\n        'Development Status :: 2 - Pre-Alpha',\n        'Intended Audience :: Developers',\n        'License :: OSI Approved :: ISC License (ISCL)',\n        'Programming Language :: Python :: 2',\n        'Programming Language :: Python :: 2.7',\n        'Programming Language :: Python :: 3',\n        'Programming Language :: Python :: 3.2',\n        'Programming Language :: Python :: 3.3',\n        'Programming Language :: Python :: 3.4',\n        'Programming Language :: Python :: 3.5',\n    ],\n    packages=find_packages(),\n    install_requires=['pyyaml'],\n    tests_require=['pytest'],\n)\n"
  }
]