Commits

Anonymous committed dfeb894

it works

  • Participants
  • Parent commits 43ab36b

Comments (0)

Files changed (7)

nbt2yaml/__init__.py

 from parse import parse_nbt, dump_nbt
-from yamlgen import to_yaml
+from yamlgen import parse_yaml, dump_yaml
 import argparse
 import sys
-from nbt2yaml import parse_nbt, to_yaml
+from nbt2yaml import parse_nbt, dump_yaml, parse_yaml, dump_nbt
 import tempfile
 import shutil
 import StringIO
     except KeyError:
         sys.exit("Environment variable EDITOR is not set")
 
-    edit_file = tempfile.NamedTemporaryFile(delete=False)
-    edit_file.write(to_yaml(struct))
+    edit_file = tempfile.NamedTemporaryFile(delete=False, suffix='.yml')
+    edit_file.write(dump_yaml(struct))
     edit_file.close()
     edit_filename = edit_file.name
     os.system("%s %s" % (editor, edit_filename))
 
+
+    edit_file = open(edit_filename, 'rb')
+    new_struct = parse_yaml(edit_file)
+    edit_file.close()
+    os.remove(edit_filename)
+
+    if new_struct == struct:
+        sys.stderr.write("No changes made\n")
+        sys.exit()
+
     save_counter = 0
     while True:
         if save_counter == 0:
             break
         save_counter += 1
 
-    shutil.copy(options.filename, savefile)
-    #shutil.move(output, options.filename)
-    os.remove(edit_filename)
+    shutil.move(options.filename, savefile)
+    sys.stderr.write("Saving old file as %s\n" % savefile)
+    write_file = open(options.filename, 'wb')
+    sys.stderr.write("Writing %s\n" % options.filename)
+    dump_nbt(new_struct, write_file, gzipped=not options.no_gzip)
+    write_file.close()
 
 def nbt2yaml():
     parser = argparse.ArgumentParser(description="Dump an nbt file or stream to yaml.")
-    parser.add_argument("-f", "--file", type=str, help="Filename.  If omitted, file is read from stdin.")
+    parser.add_argument("filename", type=str, help="Filename.  Specify as '-' to read from stdin.")
     parser.add_argument("-n", "--no-gzip", 
                         action="store_true", 
                         help="Don't use gzip"
                         )
     options = parser.parse_args()
-    if options.file:
-        input_ = open(options.file, 'rb')
+    if options.filename == '-':
+        input_ = StringIO.StringIO(sys.stdin.read())
     else:
-        input_ = StringIO.StringIO(sys.stdin.read())
+        input_ = open(options.filename, 'rb')
+
     struct = parse_nbt(input_, gzipped=not options.no_gzip)
-    print to_yaml(struct)
+    print dump_yaml(struct)
 
 def yaml2nbt():
     parser = argparse.ArgumentParser(description="Dump a yaml file or stream to nbt.")
-    parser.add_argument("-f", "--file", type=str, help="Filename.  If omitted, file is read from stdin.")
+    parser.add_argument("filename", type=str, help="Filename.  Specify as '-' to read from stdin.")
     parser.add_argument("-n", "--no-gzip", 
                         action="store_true", 
                         help="Don't use gzip"
                         )
-    raise NotImplementedError()
+    options = parser.parse_args()
+    if options.filename == '-':
+        input_ = StringIO.StringIO(sys.stdin.read())
+    else:
+        input_ = open(options.filename, 'rb')
+
+    struct = parse_yaml(input_)
+    dump_nbt(struct, sys.stdout)

nbt2yaml/yamlgen.py

 import yaml
 from nbt2yaml import parse
 
+explicit_types = parse.TAG_Short, parse.TAG_Long, parse.TAG_Double, parse.TAG_Byte, parse.TAG_Byte_Array
 class ForceType(object):
     """Represent a data value with an explicit type.
     
         self.value = value
 
 def _type_representer(dumper, struct):
-    return dumper.represent_scalar(u'!%s' % struct.type, repr(struct.value), style='""')
+    if struct.type is parse.TAG_Byte_Array:
+        representation = struct.value
+    else:
+        representation = repr(struct.value)
+    return dumper.represent_scalar(u'!%s' % struct.type.name, representation, style='""')
 
 yaml.add_representer(ForceType, _type_representer)
 
+def _type_constructor(type_):
+    def _constructor(loader, node):
+        value = loader.construct_scalar(node)
+        return ForceType(type_, value)
+    return _constructor
+
+for type_ in explicit_types:
+    yaml.add_constructor(u'!%s' % type_.name, _type_constructor(type_))
+
 def yaml_serialize(struct):
     tag, name, data = struct.type, struct.name, struct.data
     name = name.encode('utf-8')
         return [yaml_serialize(s) for s in value]
     elif type_ is parse.TAG_String:
         return value.encode('utf-8')
-    elif type_ in (parse.TAG_Short, parse.TAG_Long, parse.TAG_Double, parse.TAG_Byte):
-        return ForceType(type_.name, value)
+    elif type_ in explicit_types:
+        return ForceType(type_, value)
     elif type_ is parse.TAG_List:
         element_type, data = value
         return [_value_as_yaml(element_type, d) for d in data]
     else:
         return value
 
-def to_yaml(struct, canonical=False, default_flow_style=False):
+def _yaml_as_value(type_, value):
+    if type_ is parse.TAG_Compound:
+        return [yaml_deserialize(s) for s in value]
+    elif type_ is parse.TAG_String:
+        return value.decode('utf-8')
+    elif type_ in explicit_types:
+        if type_ in (parse.TAG_Long,):
+            return long(value.value)
+        elif type_ in (parse.TAG_Int, parse.TAG_Short, parse.TAG_Byte):
+            return int(value.value)
+        elif type_ in (parse.TAG_Float, parse.TAG_Double):
+            return float(value.value)
+        else:
+            return value.value
+    elif type_ is parse.TAG_List:
+        ltype = _type_from_yaml(value[0])
+        return (ltype, [_yaml_as_value(ltype, s) for s in value])
+    else:
+        return value
+
+def _type_from_yaml(data):
+    if isinstance(data, list):
+        if isinstance(data[0], dict):
+            type_ = parse.TAG_Compound
+        else:
+            type_ = parse.TAG_List
+    elif isinstance(data, ForceType):
+        type_ = data.type
+    elif type(data) in canned_types:
+        type_ = canned_types[type(data)]
+    else:
+        raise ValueError("Can't determine type for element: %r" % (data))
+    return type_
+
+canned_types = {
+    str:parse.TAG_String,
+    float:parse.TAG_Float,
+    int:parse.TAG_Int,
+}
+
+def yaml_deserialize(struct):
+    name, data = struct.items()[0]
+    type_ = _type_from_yaml(data)
+
+    return parse.Tag._tuple(type_, name.decode('utf-8'), _yaml_as_value(type_, data))
+
+def dump_yaml(struct, canonical=False, default_flow_style=False):
     return yaml.dump(
                 yaml_serialize(struct), 
                 default_flow_style=default_flow_style, 
                 canonical=canonical)
+
+
+def parse_yaml(stream):
+    struct = yaml.load(stream)
+    return yaml_deserialize(struct)

tests/files/bigtest.yml

+Level:
+- longTest: !long "9223372036854775807L"
+- shortTest: !short "32767"
+- stringTest: !!python/str "HELLO WORLD THIS IS A TEST STRING \xC5\xC4\xD6!"
+- floatTest: 0.4982314705848694
+- intTest: 2147483647
+- nested compound test:
+  - ham:
+    - name: Hampus
+    - value: 0.75
+  - egg:
+    - name: Eggbert
+    - value: 0.5
+- listTest (long):
+  - !long "11"
+  - !long "12"
+  - !long "13"
+  - !long "14"
+  - !long "15"
+- listTest (compound):
+  - - name: 'Compound tag #0'
+    - created-on: !long "1264099775885L"
+  - - name: 'Compound tag #1'
+    - created-on: !long "1264099775885L"
+- byteTest: !byte "127"
+- byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...)): !byte_array "\0\
+    >\"\x10\b\n\x16,L\x12F \x04VNP\\\x0E.X(\x02J802>T\x10:\nH,\x1A\x12\x14 6V\x1C\
+    P*\x0E`XZ\x02\x188b2\fTB:<H^\x1AD\x14R6$\x1C\x1E*@`&Z4\x18\x06b\0\f\"B\b<\x16\
+    ^LDFR\x04$N\x1E\\@.&(4J\x060\0>\"\x10\b\n\x16,L\x12F \x04VNP\\\x0E.X(\x02J802>T\x10\
+    :\nH,\x1A\x12\x14 6V\x1CP*\x0E`XZ\x02\x188b2\fTB:<H^\x1AD\x14R6$\x1C\x1E*@`&Z4\x18\
+    \x06b\0\f\"B\b<\x16^LDFR\x04$N\x1E\\@.&(4J\x060\0>\"\x10\b\n\x16,L\x12F \x04VNP\\\
+    \x0E.X(\x02J802>T\x10:\nH,\x1A\x12\x14 6V\x1CP*\x0E`XZ\x02\x188b2\fTB:<H^\x1A\
+    D\x14R6$\x1C\x1E*@`&Z4\x18\x06b\0\f\"B\b<\x16^LDFR\x04$N\x1E\\@.&(4J\x060\0>\"\
+    \x10\b\n\x16,L\x12F \x04VNP\\\x0E.X(\x02J802>T\x10:\nH,\x1A\x12\x14 6V\x1CP*\x0E\
+    `XZ\x02\x188b2\fTB:<H^\x1AD\x14R6$\x1C\x1E*@`&Z4\x18\x06b\0\f\"B\b<\x16^LDFR\x04\
+    $N\x1E\\@.&(4J\x060\0>\"\x10\b\n\x16,L\x12F \x04VNP\\\x0E.X(\x02J802>T\x10:\n\
+    H,\x1A\x12\x14 6V\x1CP*\x0E`XZ\x02\x188b2\fTB:<H^\x1AD\x14R6$\x1C\x1E*@`&Z4\x18\
+    \x06b\0\f\"B\b<\x16^LDFR\x04$N\x1E\\@.&(4J\x060\0>\"\x10\b\n\x16,L\x12F \x04VNP\\\
+    \x0E.X(\x02J802>T\x10:\nH,\x1A\x12\x14 6V\x1CP*\x0E`XZ\x02\x188b2\fTB:<H^\x1A\
+    D\x14R6$\x1C\x1E*@`&Z4\x18\x06b\0\f\"B\b<\x16^LDFR\x04$N\x1E\\@.&(4J\x060\0>\"\
+    \x10\b\n\x16,L\x12F \x04VNP\\\x0E.X(\x02J802>T\x10:\nH,\x1A\x12\x14 6V\x1CP*\x0E\
+    `XZ\x02\x188b2\fTB:<H^\x1AD\x14R6$\x1C\x1E*@`&Z4\x18\x06b\0\f\"B\b<\x16^LDFR\x04\
+    $N\x1E\\@.&(4J\x060\0>\"\x10\b\n\x16,L\x12F \x04VNP\\\x0E.X(\x02J802>T\x10:\n\
+    H,\x1A\x12\x14 6V\x1CP*\x0E`XZ\x02\x188b2\fTB:<H^\x1AD\x14R6$\x1C\x1E*@`&Z4\x18\
+    \x06b\0\f\"B\b<\x16^LDFR\x04$N\x1E\\@.&(4J\x060\0>\"\x10\b\n\x16,L\x12F \x04VNP\\\
+    \x0E.X(\x02J802>T\x10:\nH,\x1A\x12\x14 6V\x1CP*\x0E`XZ\x02\x188b2\fTB:<H^\x1A\
+    D\x14R6$\x1C\x1E*@`&Z4\x18\x06b\0\f\"B\b<\x16^LDFR\x04$N\x1E\\@.&(4J\x060\0>\"\
+    \x10\b\n\x16,L\x12F \x04VNP\\\x0E.X(\x02J802>T\x10:\nH,\x1A\x12\x14 6V\x1CP*\x0E\
+    `XZ\x02\x188b2\fTB:<H^\x1AD\x14R6$\x1C\x1E*@`&Z4\x18\x06b\0\f\"B\b<\x16^LDFR\x04\
+    $N\x1E\\@.&(4J\x060"
+- doubleTest: !double "0.4931287132182315"

tests/files/test.yml

+hello world:
+- name: Bananrama

tests/test_dump_yaml.py

 import unittest
-from nbt2yaml import parse_nbt, to_yaml
+from nbt2yaml import parse_nbt, dump_yaml
 from tests import datafile, eq_
 
 class ToYamlTest(unittest.TestCase):
     def test_basic(self):
         data = parse_nbt(datafile("test.nbt"))
-        eq_(to_yaml(data),
+        eq_(dump_yaml(data),
 """hello world:
 - name: Bananrama
 """)
 
     def test_large(self):
         data = parse_nbt(datafile("bigtest.nbt"))
-        eq_(to_yaml(data),
+        eq_(dump_yaml(data),
 r"""Level:
 - longTest: !long "9223372036854775807L"
 - shortTest: !short "32767"
   - - name: 'Compound tag #1'
     - created-on: !long "1264099775885L"
 - byteTest: !byte "127"
-- byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...)): "\0\
+- byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...)): !byte_array "\0\
     >\"\x10\b\n\x16,L\x12F \x04VNP\\\x0E.X(\x02J802>T\x10:\nH,\x1A\x12\x14 6V\x1C\
     P*\x0E`XZ\x02\x188b2\fTB:<H^\x1AD\x14R6$\x1C\x1E*@`&Z4\x18\x06b\0\f\"B\b<\x16\
     ^LDFR\x04$N\x1E\\@.&(4J\x060\0>\"\x10\b\n\x16,L\x12F \x04VNP\\\x0E.X(\x02J802>T\x10\

tests/test_parse_yaml.py

+import unittest
+from nbt2yaml import parse_nbt, dump_yaml, parse_yaml, dump_nbt
+from tests import datafile, eq_
+
+class FromYamlTest(unittest.TestCase):
+    def test_basic(self):
+        data = parse_yaml(datafile("test.yml"))
+
+        eq_(data, parse_nbt(datafile("test.nbt")))
+
+    def test_large(self):
+        data = parse_yaml(datafile("bigtest.yml"))
+
+        eq_(data, parse_nbt(datafile("bigtest.nbt")))