pax_global_header00006660000000000000000000000064131265263440014520gustar00rootroot0000000000000052 comment=aa5d604871c7dd8f2c98dedcbb3836c88b13b29b Serpent-serpent-1.23/000077500000000000000000000000001312652634400145435ustar00rootroot00000000000000Serpent-serpent-1.23/.gitignore000066400000000000000000000015111312652634400165310ustar00rootroot00000000000000*.py[cod] # C extensions *.so # Packages *.idea *.egg *.egg-info dist build eggs parts bin target var sdist develop-eggs .installed.cfg lib lib64 # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) [Bb]in/ [Oo]bj/ build target # mstest test results TestResults TEST-*.xml TestResult.xml ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates # Build results [Dd]ebug/ [Rr]elease/ x64/ *_i.c *_p.c *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.log *.vspscc *.vssscc .builds *.nupkg # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject # java *.class Serpent-serpent-1.23/.pylintrc000066400000000000000000000002001312652634400164000ustar00rootroot00000000000000[MESSAGES CONTROL] disable=missing-docstring [BASIC] include-naming-hint=yes max-line-length=120 good-names=i,j,k,x,y,z,t,ex,_ Serpent-serpent-1.23/.travis.yml000066400000000000000000000004471312652634400166610ustar00rootroot00000000000000language: python python: - "2.7" - "3.3" - "3.4" - "3.5" - "3.6" - "pypy" - "pypy3" # Use fast travis build infrastructure explicitly sudo: false # Installation installs dependencies install: - pip install pytz - pip install . script: cd tests && python -bb test_serpent.py Serpent-serpent-1.23/LICENSE000066400000000000000000000020561312652634400155530ustar00rootroot00000000000000MIT License Copyright (c) 2017 Irmen de Jong Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Serpent-serpent-1.23/Makefile000066400000000000000000000022121312652634400162000ustar00rootroot00000000000000.PHONY: all dist install upload clean test check all: @echo "targets include sdist, wheel, upload, install, clean, test" dist: python setup.py sdist bdist_wheel @echo "Look in the dist/ directory" upload: python setup.py sdist bdist_wheel upload install: python setup.py install test: cd tests && (PYTHONPATH=.. python -s test_serpent.py) check: flake8 --exclude .tox --ignore E501 clean: @echo "Removing tox dirs, logfiles, .pyo/.pyc files..." rm -rf .tox find . -name __pycache__ -print0 | xargs -0 rm -rf find . -name \*_log -print0 | xargs -0 rm -f find . -name \*.log -print0 | xargs -0 rm -f find . -name \*.pyo -print0 | xargs -0 rm -f find . -name \*.pyc -print0 | xargs -0 rm -f find . -name \*.class -print0 | xargs -0 rm -f find . -name \*.DS_Store -print0 | xargs -0 rm -f find . -name TEST-*.xml -print0 | xargs -0 rm -f find . -name TestResult.xml -print0 | xargs -0 rm -f rm -f MANIFEST rm -rf build rm -rf dotnet/Serpent/obj dotnet/Serpent.Test/obj rm -rf dotnet/Serpent/bin dotnet/Serpent.Test/bin find . -name '.#*' -print0 | xargs -0 rm -f find . -name '#*#' -print0 | xargs -0 rm -f @echo "clean!" Serpent-serpent-1.23/README.md000066400000000000000000000112201312652634400160160ustar00rootroot00000000000000Serpent serialization library (Python/.NET/Java) ================================================ [![saythanks](https://img.shields.io/badge/say-thanks-ff69b4.svg)](https://saythanks.io/to/irmen) [![Build Status](https://travis-ci.org/irmen/Serpent.svg?branch=master)](https://travis-ci.org/irmen/Serpent) [![Latest Version](https://img.shields.io/pypi/v/Serpent.svg)](https://pypi.python.org/pypi/Serpent/) [![Maven Central](https://img.shields.io/maven-central/v/net.razorvine/serpent.svg)](http://search.maven.org/#search|ga|1|g%3A%22net.razorvine%22%20AND%20a%3A%22serpent%22) [![NuGet](https://img.shields.io/nuget/v/Razorvine.Serpent.svg)](https://www.nuget.org/packages/Razorvine.Serpent/) [![Anaconda-Server Badge](https://anaconda.org/conda-forge/serpent/badges/version.svg)](https://anaconda.org/conda-forge/serpent) Serpent provides ast.literal_eval() compatible object tree serialization. It serializes an object tree into bytes (utf-8 encoded string) that can be decoded and then passed as-is to ast.literal_eval() to rebuild it as the original object tree. As such it is safe to send serpent data to other machines over the network for instance (because only 'safe' literals are encoded). More info on Pypi: https://pypi.python.org/pypi/serpent Source code is on Github: https://github.com/irmen/Serpent Copyright by Irmen de Jong (irmen@razorvine.net) This software is released under the MIT software license. This license, including disclaimer, is available in the 'LICENSE' file. PYTHON ------ Package can be found on Pypi as 'serpent': https://pypi.python.org/pypi/serpent Example usage can be found in ./tests/example.py C#/.NET ------- Package is available on www.nuget.org as 'Razorvine.Serpent'. Full source code can be found in ./dotnet/ directory. Example usage can be found in ./dotnet/Serpent.Test/Example.cs JAVA ---- Maven-artefact is available on maven central, groupid 'net.razorvine' artifactid 'serpent'. Full source code can be found in ./java/ directory. Example usage can be found in ./java/test/SerpentExample.java Versions before 1.23 require Java 7 or Java 8 (JDK 1.7 or 1.8) to compile and run. Version 1.23 and later require Java 8 (JDK 1.8) at a minimum to compile and run. SOME MORE DETAILS ----------------- Compatible with Python 2.7+ (including 3.x), IronPython 2.7+, Jython 2.7+. Serpent handles several special Python types to make life easier: - str --> promoted to unicode (see below why this is) - bytes, bytearrays, memoryview, buffer --> string, base-64 (you'll have to manually un-base64 them though. Can use serpent.tobytes function) - uuid.UUID, datetime.{datetime, date, time, timespan} --> appropriate string/number - decimal.Decimal --> string (to not lose precision) - array.array typecode 'c'/'u' --> string/unicode - array.array other typecode --> list - Exception --> dict with some fields of the exception (message, args) - collections module types --> mostly equivalent primitive types or dict - enums --> the value of the enum (Python 3.4+) - all other types --> dict with the ``__getstate__`` or ``vars()`` of the object Notes: All str will be promoted to unicode. This is done because it is the default anyway for Python 3.x, and it solves the problem of the str/unicode difference between different Python versions. Also it means the serialized output doesn't have those problematic 'u' prefixes on strings. The serializer is not thread-safe. Make sure you're not making changes to the object tree that is being serialized, and don't use the same serializer in different threads. Because the serialized format is just valid Python source code, it can contain comments. Serpent does not add comments by itself apart from the single header line. Set literals are not supported on python <3.2 (``ast.literal_eval`` limitation). If you need Python < 3.2 compatibility, you'll have to use ``set_literals=False`` when serializing. Since version 1.6 serpent chooses this wisely for you by default, but you can still override it if needed. Floats +inf and -inf are handled via a trick, Float 'nan' cannot be handled and is represented by the special value: ``{'__class__':'float','value':'nan'}`` We chose not to encode it as just the string 'NaN' because that could cause memory issues when used in multiplications. Jython's ast module cannot properly parse some literal reprs of unicode strings. This is a known bug http://bugs.jython.org/issue2008 It seems to work when your server is Python 2.x but safest is perhaps to make sure your data to parse contains only ascii strings when dealing with Jython. Serpent checks for possible problems and will raise an error if it finds one, rather than continuing with string data that might be incorrect. Serpent-serpent-1.23/dotnet/000077500000000000000000000000001312652634400160405ustar00rootroot00000000000000Serpent-serpent-1.23/dotnet/Serpent.Test/000077500000000000000000000000001312652634400203765ustar00rootroot00000000000000Serpent-serpent-1.23/dotnet/Serpent.Test/CycleTest.cs000066400000000000000000000050541312652634400226300ustar00rootroot00000000000000/// /// Serpent, a Python literal expression serializer/deserializer /// (a.k.a. Python's ast.literal_eval in .NET) /// /// Copyright Irmen de Jong (irmen@razorvine.net) /// Software license: "MIT software license". See http://opensource.org/licenses/MIT /// using System; using System.Collections; using System.Collections.Generic; using NUnit.Framework; namespace Razorvine.Serpent.Test { [TestFixture] public class CycleTest { [Test] public void testTupleOk() { var ser = new Serializer(); var t = new int[] {1,2,3}; var d = new object[] {t,t,t}; var data = ser.Serialize(d); var parser = new Parser(); var ast = parser.Parse(data); } [Test] public void testListOk() { var ser = new Serializer(); var t = new List(); t.Add(1); t.Add(2); t.Add(3); var d = new List(); d.Add(t); d.Add(t); d.Add(t); var data = ser.Serialize(d); var parser = new Parser(); var ast = parser.Parse(data); } [Test] public void testDictOk() { var ser = new Serializer(); var t = new Hashtable(); t["a"] = 1; var d = new Hashtable(); d["x"] = t; d["y"] = t; d["z"] = t; var data = ser.Serialize(d); var parser = new Parser(); var ast = parser.Parse(data); } [Test] [ExpectedException(typeof(ArgumentException))] public void testListCycle() { var ser = new Serializer(); var d = new List(); d.Add(1); d.Add(2); d.Add(d); var data = ser.Serialize(d); } [Test] [ExpectedException(typeof(ArgumentException))] public void testDictCycle() { var ser = new Serializer(); var d = new Hashtable(); d["x"] = 1; d["y"] = 2; d["z"] = d; var data = ser.Serialize(d); } [Test] [ExpectedException(typeof(ArgumentException))] public void testClassCycle() { var ser = new Serializer(); var d = new SerializeTestClass(); d.x = 42; d.i = 99; d.s = "hello"; d.obj = d; var data = ser.Serialize(d); } [Test] public void testMaxLevel() { Serializer ser = new Serializer(); Assert.AreEqual(500, ser.MaximumLevel); Object[] array = new Object[] { "level1", new Object[] { "level2", new Object[] { "level3", new Object[] { "level 4" } } } }; ser.MaximumLevel = 4; ser.Serialize(array); // should work ser.MaximumLevel = 3; try { ser.Serialize(array); Assert.Fail("should fail"); } catch(ArgumentException x) { Assert.IsTrue(x.Message.Contains("too deep")); } } } } Serpent-serpent-1.23/dotnet/Serpent.Test/Example.cs000066400000000000000000000057501312652634400223270ustar00rootroot00000000000000using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Linq; using System.IO; using NUnit.Framework; using Razorvine.Serpent; namespace Razorvine.Serpent.Test { /// /// Example usage. /// [TestFixture] [Explicit("example")] public class Example { [Test] public void ExampleUsage() { Console.WriteLine("using serpent library version {0}", LibraryVersion.Version); var data = new Dictionary { {"tuple", new int[] { 1,2,3 } }, {"date", DateTime.Now}, {"set", new HashSet { "a", "b", "c" } }, {"class", new SampleClass() { name = "Sally", age = 26 }} }; // serialize data structure to bytes Serializer serpent = new Serializer(indent: true); byte[] ser = serpent.Serialize(data); // print it on the screen, but normally you'd store byte bytes in a file or transfer them across a network connection Console.WriteLine("Serialized:"); Console.WriteLine(Encoding.UTF8.GetString(ser)); // parse the serialized bytes back into an abstract syntax tree of the datastructure Parser parser = new Parser(); Ast ast = parser.Parse(ser); Console.WriteLine("\nParsed AST:"); Console.WriteLine(ast.Root.ToString()); // print debug representation DebugVisitor dv = new DebugVisitor(); ast.Accept(dv); Console.WriteLine("DEBUG string representation:"); Console.WriteLine(dv.ToString()); // turn the Ast into regular .net objects var dict = (IDictionary) ast.GetData(); // You can get the data out of the Ast manually as well, by using the supplied visitor: // var visitor = new ObjectifyVisitor(); // ast.Accept(visitor); // var dict = (IDictionary) visitor.GetObject(); // print the results Console.WriteLine("PARSED results:"); Console.Write("tuple items: "); object[] tuple = (object[]) dict["tuple"]; Console.WriteLine(string.Join(", ", tuple.Select(e=>e.ToString()).ToArray())); Console.WriteLine("date: {0}", dict["date"]); Console.Write("set items: "); HashSet set = (HashSet) dict["set"]; Console.WriteLine(string.Join(", ", set.Select(e=>e.ToString()).ToArray())); Console.WriteLine("class attributes:"); var clazz = (IDictionary) dict["class"]; // custom classes are serialized as dicts Console.WriteLine(" type: {0}", clazz["__class__"]); Console.WriteLine(" name: {0}", clazz["name"]); Console.WriteLine(" age: {0}", clazz["age"]); Console.WriteLine(""); // parse and print the example file ser=File.ReadAllBytes("testserpent.utf8.bin"); ast = parser.Parse(ser); dv = new DebugVisitor(); ast.Accept(dv); Console.WriteLine("DEBUG string representation of the test file:"); Console.WriteLine(dv.ToString()); } [Serializable] public class SampleClass { public int age {get;set;} public string name {get;set;} } } } Serpent-serpent-1.23/dotnet/Serpent.Test/ParserTest.cs000066400000000000000000000700061312652634400230240ustar00rootroot00000000000000/// /// Serpent, a Python literal expression serializer/deserializer /// (a.k.a. Python's ast.literal_eval in .NET) /// /// Copyright Irmen de Jong (irmen@razorvine.net) /// Software license: "MIT software license". See http://opensource.org/licenses/MIT /// using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using NUnit.Framework; namespace Razorvine.Serpent.Test { [TestFixture] public class ParserTest { [Test] public void TestBasic() { Parser p = new Parser(); Assert.IsNull(p.Parse((string)null).Root); Assert.IsNull(p.Parse("").Root); Assert.IsNotNull(p.Parse("# comment\n42\n").Root); } [Test] public void TestComments() { Parser p = new Parser(); Ast ast = p.Parse("[ 1, 2 ]"); // no header whatsoever var visitor = new ObjectifyVisitor(); ast.Accept(visitor); Object obj = visitor.GetObject(); Assert.AreEqual(new int[] {1,2}, obj); ast = p.Parse(@"# serpent utf-8 python2.7 [ 1, 2, # some comments here 3, 4] # more here # and here. "); visitor = new ObjectifyVisitor(); ast.Accept(visitor); obj = visitor.GetObject(); Assert.AreEqual(new int[] {1,2,3,4}, obj); } [Test] public void TestPrimitives() { Parser p = new Parser(); Assert.AreEqual(new Ast.IntegerNode(42), p.Parse("42").Root); Assert.AreEqual(new Ast.IntegerNode(-42), p.Parse("-42").Root); Assert.AreEqual(new Ast.DoubleNode(42.331), p.Parse("42.331").Root); Assert.AreEqual(new Ast.DoubleNode(-42.331), p.Parse("-42.331").Root); Assert.AreEqual(new Ast.DoubleNode(-1.2e19), p.Parse("-1.2e+19").Root); Assert.AreEqual(new Ast.DoubleNode(-1.2e19), p.Parse("-1.2e19").Root); Assert.AreEqual(new Ast.DoubleNode(0.0004), p.Parse("4e-4").Root); Assert.AreEqual(new Ast.DoubleNode(40000), p.Parse("4e4").Root); Assert.AreEqual(new Ast.BooleanNode(true), p.Parse("True").Root); Assert.AreEqual(new Ast.BooleanNode(false), p.Parse("False").Root); Assert.AreEqual(Ast.NoneNode.Instance, p.Parse("None").Root); // long ints Assert.AreEqual(new Ast.DecimalNode(123456789123456789123456789M), p.Parse("123456789123456789123456789").Root); Assert.AreNotEqual(new Ast.LongNode(52), p.Parse("52").Root); Assert.AreEqual(new Ast.LongNode(123456789123456789L), p.Parse("123456789123456789").Root); Assert.Throws(()=>p.Parse("123456789123456789123456789123456789")); // overflow } [Test] public void TestWeirdFloats() { Parser p = new Parser(); var d = (Ast.DoubleNode) p.Parse("1e30000").Root; Assert.IsTrue(double.IsPositiveInfinity(d.Value)); d = (Ast.DoubleNode) p.Parse("-1e30000").Root; Assert.IsTrue(double.IsNegativeInfinity(d.Value)); var tuple = (Ast.TupleNode) p.Parse("(1e30000,-1e30000,{'__class__':'float','value':'nan'})").Root; Assert.AreEqual(3, tuple.Elements.Count); d = (Ast.DoubleNode) tuple.Elements[0]; Assert.IsTrue(double.IsPositiveInfinity(d.Value)); d = (Ast.DoubleNode) tuple.Elements[1]; Assert.IsTrue(double.IsNegativeInfinity(d.Value)); d = (Ast.DoubleNode) tuple.Elements[2]; Assert.IsTrue(Double.IsNaN(d.Value)); var c = (Ast.ComplexNumberNode) p.Parse("(1e30000-1e30000j)").Root; Assert.IsTrue(double.IsPositiveInfinity(c.Real)); Assert.IsTrue(double.IsNegativeInfinity(c.Imaginary)); } [Test] public void TestFloatPrecision() { Parser p = new Parser(); Serializer serpent = new Serializer(); var ser = serpent.Serialize(1.2345678987654321); Ast.DoubleNode dv = (Ast.DoubleNode) p.Parse(ser).Root; Assert.AreEqual(1.2345678987654321.ToString(), dv.Value.ToString()); ser = serpent.Serialize(5555.12345678987656); dv = (Ast.DoubleNode) p.Parse(ser).Root; Assert.AreEqual(5555.12345678987656.ToString(), dv.Value.ToString()); ser = serpent.Serialize(98765432123456.12345678987656); dv = (Ast.DoubleNode) p.Parse(ser).Root; Assert.AreEqual(98765432123456.12345678987656.ToString(), dv.Value.ToString()); ser = serpent.Serialize(98765432123456.12345678987656e+44); dv = (Ast.DoubleNode) p.Parse(ser).Root; Assert.AreEqual((98765432123456.12345678987656e+44).ToString(), dv.Value.ToString()); ser = serpent.Serialize(-98765432123456.12345678987656e-44); dv = (Ast.DoubleNode) p.Parse(ser).Root; Assert.AreEqual((-98765432123456.12345678987656e-44).ToString(), dv.Value.ToString()); } [Test] public void TestEquality() { Ast.INode n1, n2; n1 = new Ast.IntegerNode(42); n2 = new Ast.IntegerNode(42); Assert.AreEqual(n1, n2); n2 = new Ast.IntegerNode(43); Assert.AreNotEqual(n1, n2); n1 = new Ast.StringNode("foo"); n2 = new Ast.StringNode("foo"); Assert.AreEqual(n1, n2); n2 = new Ast.StringNode("bar"); Assert.AreNotEqual(n1, n2); n1 = new Ast.ComplexNumberNode() { Real=1.1, Imaginary=2.2 }; n2 = new Ast.ComplexNumberNode() { Real=1.1, Imaginary=2.2 }; Assert.AreEqual(n1, n2); n2 = new Ast.ComplexNumberNode() { Real=1.1, Imaginary=3.3 }; Assert.AreNotEqual(n1, n2); n1=new Ast.KeyValueNode() { Key=new Ast.IntegerNode(42), Value=new Ast.IntegerNode(42) }; n2=new Ast.KeyValueNode() { Key=new Ast.IntegerNode(42), Value=new Ast.IntegerNode(42) }; Assert.AreEqual(n1, n2); n1=new Ast.KeyValueNode() { Key=new Ast.IntegerNode(43), Value=new Ast.IntegerNode(43) }; Assert.AreNotEqual(n1,n2); n1=Ast.NoneNode.Instance; n2=Ast.NoneNode.Instance; Assert.AreEqual(n1, n2); n2=new Ast.IntegerNode(42); Assert.AreNotEqual(n1, n2); n1=new Ast.DictNode() { Elements=new List() { new Ast.KeyValueNode() { Key=new Ast.IntegerNode(42), Value=new Ast.IntegerNode(42) } } }; n2=new Ast.DictNode() { Elements=new List() { new Ast.KeyValueNode() { Key=new Ast.IntegerNode(42), Value=new Ast.IntegerNode(42) } } }; Assert.AreEqual(n1, n2); n2=new Ast.DictNode() { Elements=new List() { new Ast.KeyValueNode() { Key=new Ast.IntegerNode(42), Value=new Ast.IntegerNode(43) } } }; Assert.AreNotEqual(n1, n2); n1=new Ast.ListNode() { Elements=new List() { new Ast.IntegerNode(42) } }; n2=new Ast.ListNode() { Elements=new List() { new Ast.IntegerNode(42) } }; Assert.AreEqual(n1,n2); n2=new Ast.ListNode() { Elements=new List() { new Ast.IntegerNode(43) } }; Assert.AreNotEqual(n1,n2); n1=new Ast.SetNode() { Elements=new List() { new Ast.IntegerNode(42) } }; n2=new Ast.SetNode() { Elements=new List() { new Ast.IntegerNode(42) } }; Assert.AreEqual(n1,n2); n2=new Ast.SetNode() { Elements=new List() { new Ast.IntegerNode(43) } }; Assert.AreNotEqual(n1,n2); n1=new Ast.TupleNode() { Elements=new List() { new Ast.IntegerNode(42) } }; n2=new Ast.TupleNode() { Elements=new List() { new Ast.IntegerNode(42) } }; Assert.AreEqual(n1,n2); n2=new Ast.TupleNode() { Elements=new List() { new Ast.IntegerNode(43) } }; Assert.AreNotEqual(n1,n2); } [Test] public void TestDictEquality() { Ast.DictNode dict1 = new Ast.DictNode(); Ast.KeyValueNode kv=new Ast.KeyValueNode() { Key=new Ast.StringNode("key1"), Value=new Ast.IntegerNode(42) }; dict1.Elements.Add(kv); kv=new Ast.KeyValueNode() { Key=new Ast.StringNode("key2"), Value=new Ast.IntegerNode(43) }; dict1.Elements.Add(kv); kv=new Ast.KeyValueNode() { Key=new Ast.StringNode("key3"), Value=new Ast.IntegerNode(44) }; dict1.Elements.Add(kv); Ast.DictNode dict2 = new Ast.DictNode(); kv=new Ast.KeyValueNode(){ Key=new Ast.StringNode("key2"), Value=new Ast.IntegerNode(43) }; dict2.Elements.Add(kv); kv=new Ast.KeyValueNode() { Key=new Ast.StringNode("key3"), Value=new Ast.IntegerNode(44) }; dict2.Elements.Add(kv); kv=new Ast.KeyValueNode() { Key=new Ast.StringNode("key1"), Value=new Ast.IntegerNode(42) }; dict2.Elements.Add(kv); Assert.AreEqual(dict1, dict2); kv=new Ast.KeyValueNode() { Key=new Ast.StringNode("key4"), Value=new Ast.IntegerNode(45) }; dict2.Elements.Add(kv); Assert.AreNotEqual(dict1, dict2); } [Test] public void TestSetEquality() { Ast.SetNode set1 = new Ast.SetNode(); set1.Elements.Add(new Ast.IntegerNode(1)); set1.Elements.Add(new Ast.IntegerNode(2)); set1.Elements.Add(new Ast.IntegerNode(3)); Ast.SetNode set2 = new Ast.SetNode(); set2.Elements.Add(new Ast.IntegerNode(2)); set2.Elements.Add(new Ast.IntegerNode(3)); set2.Elements.Add(new Ast.IntegerNode(1)); Assert.AreEqual(set1, set2); set2.Elements.Add(new Ast.IntegerNode(0)); Assert.AreNotEqual(set1, set2); } [Test] public void TestPrintSingle() { Parser p = new Parser(); // primitives Assert.AreEqual("42", p.Parse("42").Root.ToString()); Assert.AreEqual("-42.331", p.Parse("-42.331").Root.ToString()); Assert.AreEqual("-42.0", p.Parse("-42.0").Root.ToString()); Assert.AreEqual("-2E+20", p.Parse("-2E20").Root.ToString()); Assert.AreEqual("2.0", p.Parse("2.0").Root.ToString()); Assert.AreEqual("1.2E+19", p.Parse("1.2e19").Root.ToString()); Assert.AreEqual("True", p.Parse("True").Root.ToString()); Assert.AreEqual("'hello'", p.Parse("'hello'").Root.ToString()); Assert.AreEqual("'\\n'", p.Parse("'\n'").Root.ToString()); Assert.AreEqual("'\\''", p.Parse("'\\''").Root.ToString()); Assert.AreEqual("'\"'", p.Parse("'\\\"'").Root.ToString()); Assert.AreEqual("'\"'", p.Parse("'\"'").Root.ToString()); Assert.AreEqual("'\\\\'", p.Parse("'\\\\'").Root.ToString()); Assert.AreEqual("None", p.Parse("None").Root.ToString()); string ustr = "'\u20ac\u2603'"; Assert.AreEqual(ustr, p.Parse(ustr).Root.ToString()); // complex Assert.AreEqual("(0+2j)", p.Parse("2j").Root.ToString()); Assert.AreEqual("(-1.1-2.2j)", p.Parse("(-1.1-2.2j)").Root.ToString()); Assert.AreEqual("(1.1+2.2j)", p.Parse("(1.1+2.2j)").Root.ToString()); // long int Assert.AreEqual("123456789123456789123456789", p.Parse("123456789123456789123456789").Root.ToString()); } [Test] public void TestPrintSeq() { Parser p=new Parser(); //tuple Assert.AreEqual("()", p.Parse("()").Root.ToString()); Assert.AreEqual("(42,)", p.Parse("(42,)").Root.ToString()); Assert.AreEqual("(42,43)", p.Parse("(42,43)").Root.ToString()); // list Assert.AreEqual("[]", p.Parse("[]").Root.ToString()); Assert.AreEqual("[42]", p.Parse("[42]").Root.ToString()); Assert.AreEqual("[42,43]", p.Parse("[42,43]").Root.ToString()); // set Assert.AreEqual("{42}", p.Parse("{42}").Root.ToString()); Assert.AreEqual("{42,43}", p.Parse("{42,43,43,43}").Root.ToString()); // dict Assert.AreEqual("{}", p.Parse("{}").Root.ToString()); Assert.AreEqual("{'a':42}", p.Parse("{'a': 42}").Root.ToString()); Assert.AreEqual("{'a':42,'b':43}", p.Parse("{'a': 42, 'b': 43}").Root.ToString()); Assert.AreEqual("{'a':42,'b':45}", p.Parse("{'a': 42, 'b': 43, 'b': 44, 'b': 45}").Root.ToString()); } [Test] public void TestInvalidPrimitives() { Parser p = new Parser(); Assert.Throws(()=>p.Parse("1+2")); Assert.Throws(()=>p.Parse("1-2")); Assert.Throws(()=>p.Parse("1.1+2.2")); Assert.Throws(()=>p.Parse("1.1-2.2")); Assert.Throws(()=>p.Parse("True+2")); Assert.Throws(()=>p.Parse("False-2")); Assert.Throws(()=>p.Parse("3j+2")); Assert.Throws(()=>p.Parse("3j-2")); Assert.Throws(()=>p.Parse("None+2")); Assert.Throws(()=>p.Parse("None-2")); } [Test] public void TestComplex() { Parser p = new Parser(); var cplx = new Ast.ComplexNumberNode() { Real = 4.2, Imaginary = 3.2 }; var cplx2 = new Ast.ComplexNumberNode() { Real = 4.2, Imaginary = 99 }; Assert.AreNotEqual(cplx, cplx2); cplx2.Imaginary = 3.2; Assert.AreEqual(cplx, cplx2); Assert.AreEqual(cplx, p.Parse("(4.2+3.2j)").Root); cplx.Real = 0; Assert.AreEqual(cplx, p.Parse("(0+3.2j)").Root); Assert.AreEqual(cplx, p.Parse("3.2j").Root); Assert.AreEqual(cplx, p.Parse("+3.2j").Root); cplx.Imaginary = -3.2; Assert.AreEqual(cplx, p.Parse("-3.2j").Root); cplx.Real = -9.9; Assert.AreEqual(cplx, p.Parse("(-9.9-3.2j)").Root); cplx.Real = 2; cplx.Imaginary = 3; Assert.AreEqual(cplx, p.Parse("(2+3j)").Root); cplx.Imaginary = -3; Assert.AreEqual(cplx, p.Parse("(2-3j)").Root); cplx.Real = 0; Assert.AreEqual(cplx, p.Parse("-3j").Root); cplx.Real = -3.2e32; cplx.Imaginary = -9.9e44; Assert.AreEqual(cplx, p.Parse("(-3.2e32 -9.9e44j)").Root); Assert.AreEqual(cplx, p.Parse("(-3.2e+32 -9.9e+44j)").Root); Assert.AreEqual(cplx, p.Parse("(-3.2e32-9.9e44j)").Root); Assert.AreEqual(cplx, p.Parse("(-3.2e+32-9.9e+44j)").Root); cplx.Imaginary = 9.9e44; Assert.AreEqual(cplx, p.Parse("(-3.2e32+9.9e44j)").Root); Assert.AreEqual(cplx, p.Parse("(-3.2e+32+9.9e+44j)").Root); cplx.Real = -3.2e-32; cplx.Imaginary = -9.9e-44; Assert.AreEqual(cplx, p.Parse("(-3.2e-32-9.9e-44j)").Root); } [Test] public void TestComplexPrecision() { Parser p = new Parser(); Ast.ComplexNumberNode cv = (Ast.ComplexNumberNode)p.Parse("(98765432123456.12345678987656+665544332211.9998877665544j)").Root; Assert.AreEqual(98765432123456.12345678987656, cv.Real); Assert.AreEqual(665544332211.9998877665544, cv.Imaginary); cv = (Ast.ComplexNumberNode)p.Parse("(98765432123456.12345678987656-665544332211.9998877665544j)").Root; Assert.AreEqual(98765432123456.12345678987656, cv.Real); Assert.AreEqual(-665544332211.9998877665544, cv.Imaginary); cv = (Ast.ComplexNumberNode)p.Parse("(98765432123456.12345678987656e+33+665544332211.9998877665544e+44j)").Root; Assert.AreEqual(98765432123456.12345678987656e+33, cv.Real); Assert.AreEqual(665544332211.9998877665544e+44, cv.Imaginary); cv = (Ast.ComplexNumberNode)p.Parse("(-98765432123456.12345678987656e+33-665544332211.9998877665544e+44j)").Root; Assert.AreEqual(-98765432123456.12345678987656e+33, cv.Real); Assert.AreEqual(-665544332211.9998877665544e+44, cv.Imaginary); } [Test] public void TestPrimitivesStuffAtEnd() { Parser p = new Parser(); Assert.AreEqual(new Ast.IntegerNode(42), p.ParseSingle(new SeekableStringReader("42@"))); Assert.AreEqual(new Ast.DoubleNode(42.331), p.ParseSingle(new SeekableStringReader("42.331@"))); Assert.AreEqual(new Ast.BooleanNode(true), p.ParseSingle(new SeekableStringReader("True@"))); Assert.AreEqual(Ast.NoneNode.Instance, p.ParseSingle(new SeekableStringReader("None@"))); var cplx = new Ast.ComplexNumberNode() { Real = 4, Imaginary = 3 }; Assert.AreEqual(cplx, p.ParseSingle(new SeekableStringReader("(4+3j)@"))); cplx.Real=0; Assert.AreEqual(cplx, p.ParseSingle(new SeekableStringReader("3j@"))); } [Test] public void TestStrings() { Parser p = new Parser(); Assert.AreEqual(new Ast.StringNode("hello"), p.Parse("'hello'").Root); Assert.AreEqual(new Ast.StringNode("hello"), p.Parse("\"hello\"").Root); Assert.AreEqual(new Ast.StringNode("\\"), p.Parse("'\\\\'").Root); Assert.AreEqual(new Ast.StringNode("\\"), p.Parse("\"\\\\\"").Root); Assert.AreEqual(new Ast.StringNode("'"), p.Parse("\"'\"").Root); Assert.AreEqual(new Ast.StringNode("\""), p.Parse("'\"'").Root); Assert.AreEqual(new Ast.StringNode("tab\tnewline\n."), p.Parse("'tab\\tnewline\\n.'").Root); } [Test] public void TestUnicode() { Parser p = new Parser(); string str = "'\u20ac\u2603'"; Assert.AreEqual(0x20ac, str[1]); Assert.AreEqual(0x2603, str[2]); byte[] bytes = Encoding.UTF8.GetBytes(str); string value = "\u20ac\u2603"; Assert.AreEqual(new Ast.StringNode(value), p.Parse(str).Root); Assert.AreEqual(new Ast.StringNode(value), p.Parse(bytes).Root); } [Test] public void TestLongUnicodeRoundtrip() { Char[] chars64k = new Char[65536]; for(int i=0; i<=65535; ++i) chars64k[i]=(Char)i; String str64k= new String(chars64k); Serializer ser=new Serializer(); byte[] data = ser.Serialize(str64k); Assert.Greater(data.Length, chars64k.Length); Parser p=new Parser(); String result = (String)p.Parse(data).GetData(); Assert.AreEqual(str64k, result); } [Test] public void TestWhitespace() { Parser p = new Parser(); Assert.AreEqual(new Ast.IntegerNode(42), p.Parse(" 42 ").Root); Assert.AreEqual(new Ast.IntegerNode(42), p.Parse(" 42 ").Root); Assert.AreEqual(new Ast.IntegerNode(42), p.Parse("\t42\r\n").Root); Assert.AreEqual(new Ast.IntegerNode(42), p.Parse(" \t 42 \r \n ").Root); Assert.AreEqual(new Ast.StringNode(" string value "), p.Parse(" ' string value ' ").Root); Assert.Throws(()=>p.Parse(" ( 42 , ( 'x', 'y' ) ")); // missing tuple close ) Ast ast = p.Parse(" ( 42 , ( 'x', 'y' ) ) "); Ast.TupleNode tuple = (Ast.TupleNode) ast.Root; Assert.AreEqual(new Ast.IntegerNode(42), tuple.Elements[0]); tuple = (Ast.TupleNode) tuple.Elements[1]; Assert.AreEqual(new Ast.StringNode("x"), tuple.Elements[0]); Assert.AreEqual(new Ast.StringNode("y"), tuple.Elements[1]); p.Parse(" ( 52 , ) "); p.Parse(" [ 52 ] "); p.Parse(" { 'a' : 42 } "); p.Parse(" { 52 } "); } [Test] public void TestTuple() { Parser p = new Parser(); Ast.TupleNode tuple = new Ast.TupleNode(); Ast.TupleNode tuple2 = new Ast.TupleNode(); Assert.AreEqual(tuple, tuple2); tuple.Elements.Add(new Ast.IntegerNode(42)); tuple2.Elements.Add(new Ast.IntegerNode(99)); Assert.AreNotEqual(tuple, tuple2); tuple2.Elements.Clear(); tuple2.Elements.Add(new Ast.IntegerNode(42)); Assert.AreEqual(tuple, tuple2); tuple2.Elements.Add(new Ast.IntegerNode(43)); tuple2.Elements.Add(new Ast.IntegerNode(44)); Assert.AreNotEqual(tuple, tuple2); Assert.AreEqual(new Ast.TupleNode(), p.Parse("()").Root); Assert.AreEqual(tuple, p.Parse("(42,)").Root); Assert.AreEqual(tuple2, p.Parse("( 42,43, 44 )").Root); Assert.Throws(()=>p.Parse("(42,43]")); Assert.Throws(()=>p.Parse("()@")); Assert.Throws(()=>p.Parse("(42,43)@")); } [Test] public void TestList() { Parser p = new Parser(); Ast.ListNode list = new Ast.ListNode(); Ast.ListNode list2 = new Ast.ListNode(); Assert.AreEqual(list, list2); list.Elements.Add(new Ast.IntegerNode(42)); list2.Elements.Add(new Ast.IntegerNode(99)); Assert.AreNotEqual(list, list2); list2.Elements.Clear(); list2.Elements.Add(new Ast.IntegerNode(42)); Assert.AreEqual(list, list2); list2.Elements.Add(new Ast.IntegerNode(43)); list2.Elements.Add(new Ast.IntegerNode(44)); Assert.AreNotEqual(list, list2); Assert.AreEqual(new Ast.ListNode(), p.Parse("[]").Root); Assert.AreEqual(list, p.Parse("[42]").Root); Assert.AreEqual(list2, p.Parse("[ 42,43, 44 ]").Root); Assert.Throws(()=>p.Parse("[42,43}")); Assert.Throws(()=>p.Parse("[]@")); Assert.Throws(()=>p.Parse("[42,43]@")); } [Test] public void TestSet() { Parser p = new Parser(); Ast.SetNode set1 = new Ast.SetNode(); Ast.SetNode set2 = new Ast.SetNode(); Assert.AreEqual(set1, set2); set1.Elements.Add(new Ast.IntegerNode(42)); set2.Elements.Add(new Ast.IntegerNode(99)); Assert.AreNotEqual(set1, set2); set2.Elements.Clear(); set2.Elements.Add(new Ast.IntegerNode(42)); Assert.AreEqual(set1, set2); set2.Elements.Add(new Ast.IntegerNode(43)); set2.Elements.Add(new Ast.IntegerNode(44)); Assert.AreNotEqual(set1, set2); Assert.AreEqual(set1, p.Parse("{42}").Root); Assert.AreEqual(set2, p.Parse("{ 42,43, 44 }").Root); Assert.Throws(()=>p.Parse("{42,43]")); Assert.Throws(()=>p.Parse("{42,43}@")); set1 = p.Parse("{'first','second','third','fourth','fifth','second', 'first', 'third', 'third' }").Root as Ast.SetNode; Assert.AreEqual("'first'", set1.Elements[0].ToString()); Assert.AreEqual("'second'", set1.Elements[1].ToString()); Assert.AreEqual("'third'", set1.Elements[2].ToString()); Assert.AreEqual("'fourth'", set1.Elements[3].ToString()); Assert.AreEqual("'fifth'", set1.Elements[4].ToString()); Assert.AreEqual(5, set1.Elements.Count); } [Test] public void TestDict() { Parser p = new Parser(); Ast.DictNode dict1 = new Ast.DictNode(); Ast.DictNode dict2 = new Ast.DictNode(); Assert.AreEqual(dict1, dict2); Ast.KeyValueNode kv1 = new Ast.KeyValueNode { Key=new Ast.StringNode("key"), Value=new Ast.IntegerNode(42) }; Ast.KeyValueNode kv2 = new Ast.KeyValueNode { Key=new Ast.StringNode("key"), Value=new Ast.IntegerNode(99) }; Assert.AreNotEqual(kv1, kv2); kv2.Value = new Ast.IntegerNode(42); Assert.AreEqual(kv1, kv2); dict1.Elements.Add(new Ast.KeyValueNode { Key=new Ast.StringNode("key1"), Value=new Ast.IntegerNode(42) }); dict2.Elements.Add(new Ast.KeyValueNode { Key=new Ast.StringNode("key1"), Value=new Ast.IntegerNode(99) }); Assert.AreNotEqual(dict1, dict2); dict2.Elements.Clear(); dict2.Elements.Add(new Ast.KeyValueNode { Key=new Ast.StringNode("key1"), Value=new Ast.IntegerNode(42) }); Assert.AreEqual(dict1, dict2); dict2.Elements.Add(new Ast.KeyValueNode { Key=new Ast.StringNode("key2"), Value=new Ast.IntegerNode(43) }); dict2.Elements.Add(new Ast.KeyValueNode { Key=new Ast.StringNode("key3"), Value=new Ast.IntegerNode(44) }); Assert.AreNotEqual(dict1, dict2); Assert.AreEqual(new Ast.DictNode(), p.Parse("{}").Root); Assert.AreEqual(dict1, p.Parse("{'key1': 42}").Root); Assert.AreEqual(dict2, p.Parse("{'key1': 42, 'key2': 43, 'key3':44}").Root); Assert.Throws(()=>p.Parse("{'key': 42]")); Assert.Throws(()=>p.Parse("{}@")); Assert.Throws(()=>p.Parse("{'key': 42}@")); dict1 = p.Parse("{'a': 1, 'b': 2, 'c': 3, 'c': 4, 'c': 5, 'c': 6}").Root as Ast.DictNode; Assert.AreEqual("'a':1", dict1.Elements[0].ToString()); Assert.AreEqual("'b':2", dict1.Elements[1].ToString()); Assert.AreEqual("'c':6", dict1.Elements[2].ToString()); Assert.AreEqual(3, dict1.Elements.Count); } [Test] public void TestFile() { Parser p = new Parser(); byte[] ser=File.ReadAllBytes("testserpent.utf8.bin"); Ast ast = p.Parse(ser); string expr = ast.ToString(); Ast ast2 = p.Parse(expr); string expr2 = ast2.ToString(); Assert.AreEqual(expr, expr2); StringBuilder sb= new StringBuilder(); Walk(ast.Root, sb); string walk1 = sb.ToString(); sb= new StringBuilder(); Walk(ast2.Root, sb); string walk2 = sb.ToString(); Assert.AreEqual(walk1, walk2); // @TODO Assert.AreEqual(ast.Root, ast2.Root); ast = p.Parse(expr2); // @TODO Assert.AreEqual(ast.Root, ast2.Root); } [Test] [Ignore("can't yet get the ast to compare equal on mono")] public void TestAstEquals() { Parser p = new Parser (); byte[] ser = File.ReadAllBytes ("testserpent.utf8.bin"); Ast ast = p.Parse(ser); Ast ast2 = p.Parse(ser); Assert.AreEqual(ast.Root, ast2.Root); } public void Walk(Ast.INode node, StringBuilder sb) { if(node is Ast.SequenceNode) { sb.AppendLine(string.Format("{0} (seq)", node.GetType())); Ast.SequenceNode seq = (Ast.SequenceNode)node; foreach(Ast.INode child in seq.Elements) { Walk(child, sb); } } else sb.AppendLine(string.Format("{0} = {1}", node.GetType(), node.ToString())); } [Test] public void TestTrailingCommas() { Parser p = new Parser(); Ast.INode result; result = p.Parse("[1,2,3, ]").Root; result = p.Parse("[1,2,3 , ]").Root; result = p.Parse("[1,2,3,]").Root; Assert.AreEqual("[1,2,3]", result.ToString()); result = p.Parse("(1,2,3, )").Root; result = p.Parse("(1,2,3 , )").Root; result = p.Parse("(1,2,3,)").Root; Assert.AreEqual("(1,2,3)", result.ToString()); // for dict and set the asserts are a bit more complex // we cannot simply convert to string because the order of elts is undefined. result = p.Parse("{'a':1, 'b':2, 'c':3, }").Root; result = p.Parse("{'a':1, 'b':2, 'c':3 , }").Root; result = p.Parse("{'a':1, 'b':2, 'c':3,}").Root; Ast.DictNode dict = (Ast.DictNode) result; var items = dict.ElementsAsSet(); Assert.IsTrue(items.Contains(new Ast.KeyValueNode(new Ast.StringNode("a"), new Ast.IntegerNode(1)))); Assert.IsTrue(items.Contains(new Ast.KeyValueNode(new Ast.StringNode("b"), new Ast.IntegerNode(2)))); Assert.IsTrue(items.Contains(new Ast.KeyValueNode(new Ast.StringNode("c"), new Ast.IntegerNode(3)))); result = p.Parse("{1,2,3, }").Root; result = p.Parse("{1,2,3 , }").Root; result = p.Parse("{1,2,3,}").Root; Ast.SetNode set = (Ast.SetNode) result; items = set.ElementsAsSet(); Assert.IsTrue(items.Contains(new Ast.IntegerNode(1))); Assert.IsTrue(items.Contains(new Ast.IntegerNode(2))); Assert.IsTrue(items.Contains(new Ast.IntegerNode(3))); Assert.IsFalse(items.Contains(new Ast.IntegerNode(4))); } } [TestFixture] public class VisitorTest { [Test] public void TestObjectify() { Parser p = new Parser(); byte[] ser=File.ReadAllBytes("testserpent.utf8.bin"); Ast ast = p.Parse(ser); var visitor = new ObjectifyVisitor(); ast.Accept(visitor); object thing = visitor.GetObject(); IDictionary dict = (IDictionary) thing; Assert.AreEqual(11, dict.Count); IList list = dict["numbers"] as IList; Assert.AreEqual(4, list.Count); Assert.AreEqual(999.1234, list[1]); Assert.AreEqual(new ComplexNumber(-3, 8), list[3]); string euro = dict["unicode"] as string; Assert.AreEqual("\u20ac", euro); IDictionary exc = (IDictionary)dict["exc"]; object[] args = (object[]) exc["args"]; Assert.AreEqual("fault", args[0]); Assert.AreEqual("ZeroDivisionError", exc["__class__"]); } object ZerodivisionFromDict(IDictionary dict) { string classname = (string)dict["__class__"]; if(classname=="ZeroDivisionError") { object[] args = (object[]) dict["args"]; return new DivideByZeroException((string)args[0]); } return null; } [Test] public void TestObjectifyDictToClass() { Parser p = new Parser(); byte[] ser=File.ReadAllBytes("testserpent.utf8.bin"); Ast ast = p.Parse(ser); var visitor = new ObjectifyVisitor(ZerodivisionFromDict); ast.Accept(visitor); object thing = visitor.GetObject(); IDictionary dict = (IDictionary) thing; Assert.AreEqual(11, dict.Count); DivideByZeroException ex = (DivideByZeroException) dict["exc"]; Assert.AreEqual("fault", ex.Message); thing = ast.GetData(ZerodivisionFromDict); dict = (IDictionary) thing; Assert.AreEqual(11, dict.Count); ex = (DivideByZeroException) dict["exc"]; Assert.AreEqual("fault", ex.Message); } } }Serpent-serpent-1.23/dotnet/Serpent.Test/Properties/000077500000000000000000000000001312652634400225325ustar00rootroot00000000000000Serpent-serpent-1.23/dotnet/Serpent.Test/Properties/AssemblyInfo.cs000066400000000000000000000020431312652634400254530ustar00rootroot00000000000000#region Using directives using System; using System.Reflection; using System.Runtime.InteropServices; #endregion // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Serpent.Test")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Serpent.Test")] [assembly: AssemblyCopyright("Copyright Irmen de Jong")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // This sets the default COM visibility of types in the assembly to invisible. // If you need to expose a type to COM, use [ComVisible(true)] on that type. [assembly: ComVisible(false)] // The assembly version has following format : // // Major.Minor.Build.Revision // // You can specify all the values or you can use the default the Revision and // Build Numbers by using the '*' as shown below: [assembly: AssemblyVersion("1.0.*")] Serpent-serpent-1.23/dotnet/Serpent.Test/SerializeTest.cs000066400000000000000000000565161312652634400235310ustar00rootroot00000000000000/// /// Serpent, a Python literal expression serializer/deserializer /// (a.k.a. Python's ast.literal_eval in .NET) /// /// Copyright Irmen de Jong (irmen@razorvine.net) /// Software license: "MIT software license". See http://opensource.org/licenses/MIT /// using System; using System.Collections.Generic; using System.Text; using NUnit.Framework; using Hashtable = System.Collections.Hashtable; using IDictionary = System.Collections.IDictionary; namespace Razorvine.Serpent.Test { [TestFixture] public class SerializeTest { public byte[] strip_header(byte[] data) { int start=Array.IndexOf(data, (byte)10); // the newline after the header if(start<0) throw new ArgumentException("need header in string"); start++; byte[] result = new byte[data.Length-start]; Array.Copy(data, start, result, 0, data.Length-start); return result; } public byte[] B(string s) { return Encoding.UTF8.GetBytes(s); } public string S(byte[] b) { return Encoding.UTF8.GetString(b); } [Test] public void TestHeader() { Serializer ser = new Serializer(); byte[] data = ser.Serialize(null); Assert.AreEqual(35, data[0]); string strdata = S(data); Assert.AreEqual("# serpent utf-8 python3.2", strdata.Split('\n')[0]); ser.SetLiterals=false; data = ser.Serialize(null); strdata = S(data); Assert.AreEqual("# serpent utf-8 python2.6", strdata.Split('\n')[0]); data = B("# header\nfirst-line"); data = strip_header(data); Assert.AreEqual(B("first-line"), data); } [Test] public void TestStuff() { Serializer ser=new Serializer(); byte[] result = ser.Serialize("blerp"); result=strip_header(result); Assert.AreEqual(B("'blerp'"), result); result = ser.Serialize(new Guid("f1f8d00e-49a5-4662-ac1d-d5f0426ed293")); result=strip_header(result); Assert.AreEqual(B("'f1f8d00e-49a5-4662-ac1d-d5f0426ed293'"), result); result = ser.Serialize(123456789.987654321987654321987654321987654321m); result=strip_header(result); Assert.AreEqual(B("'123456789.98765432198765432199'"), result); } [Test] public void TestNull() { Serializer ser = new Serializer(); byte[] data = ser.Serialize(null); data=strip_header(data); Assert.AreEqual(B("None"),data); } [Test] public void TestStrings() { Serializer serpent = new Serializer(); byte[] ser = serpent.Serialize("hello"); byte[] data = strip_header(ser); Assert.AreEqual(B("'hello'"), data); ser = serpent.Serialize("quotes'\""); data = strip_header(ser); Assert.AreEqual(B("'quotes\\'\"'"), data); ser = serpent.Serialize("quotes2'"); data = strip_header(ser); Assert.AreEqual(B("\"quotes2'\""), data); } [Test] public void TestUnicodeEscapes() { Serializer serpent=new Serializer(); // regular escaped chars first byte[] ser = serpent.Serialize("\b\r\n\f\t \\"); byte[] data = strip_header(ser); // '\\x08\\r\\n\\x0c\\t \\\\' Assert.AreEqual(new byte[] {39, 92, 120, 48, 56, 92, 114, 92, 110, 92, 120, 48, 99, 92, 116, 32, 92, 92, 39}, data); // simple cases (chars < 0x80) ser = serpent.Serialize("\u0000\u0001\u001f\u007f"); data = strip_header(ser); // '\\x00\\x01\\x1f\\x7f' Assert.AreEqual(new byte[] {39, 92, 120, 48, 48, 92, 120, 48, 49, 92, 120, 49, 102, 92, 120, 55, 102, 39 }, data); // chars 0x80 .. 0xff ser = serpent.Serialize("\u0080\u0081\u00ff"); data = strip_header(ser); // '\\x80\\x81\xc3\xbf' (has some utf-8 encoded chars in it) Assert.AreEqual(new byte[] {39, 92, 120, 56, 48, 92, 120, 56, 49, 195, 191, 39}, data); // chars above 0xff ser = serpent.Serialize("\u0100\u20ac\u8899"); data = strip_header(ser); // '\xc4\x80\xe2\x82\xac\xe8\xa2\x99' (has some utf-8 encoded chars in it) Assert.AreEqual(new byte[] {39, 196, 128, 226, 130, 172, 232, 162, 153, 39}, data); // // some random high chars that are all printable in python and not escaped // ser = serpent.Serialize("\u0377\u082d\u10c5\u135d\uac00"); // data = strip_header(ser); // Console.WriteLine(S(data)); // XXX // // '\xcd\xb7\xe0\xa0\xad\xe1\x83\x85\xe1\x8d\x9d\xea\xb0\x80' (only a bunch of utf-8 encoded chars) // Assert.AreEqual(new byte[] {39, 205, 183, 224, 160, 173, 225, 131, 133, 225, 141, 157, 234, 176, 128, 39}, data); // some random high chars that are all non-printable in python and that are escaped ser = serpent.Serialize("\u0378\u082e\u10c6\u135c\uabff"); data = strip_header(ser); // '\\u0378\\u082e\\u10c6\\u135c\\uabff' Assert.AreEqual(new byte[] {39, 92, 117, 48, 51, 55, 56, 92, 117, 48, 56, 50, 101, 92, 117, 49, 48, 99, 54, 92, 117, 49, 51, 53, 99, 92, 117, 97, 98, 102, 102, 39}, data); } [Test] public void TestNumbers() { Serializer serpent = new Serializer(); byte[] ser = serpent.Serialize((int)12345); byte[] data = strip_header(ser); Assert.AreEqual(B("12345"), data); ser = serpent.Serialize((uint)12345); data = strip_header(ser); Assert.AreEqual(B("12345"), data); ser = serpent.Serialize((long)1234567891234567891L); data = strip_header(ser); Assert.AreEqual(B("1234567891234567891"), data); ser = serpent.Serialize((ulong)12345678912345678912L); data = strip_header(ser); Assert.AreEqual(B("12345678912345678912"), data); ser = serpent.Serialize(99.1234); data = strip_header(ser); Assert.AreEqual(B("99.1234"), data); ser = serpent.Serialize(1234.9999999999m); data = strip_header(ser); Assert.AreEqual(B("'1234.9999999999'"), data); ser = serpent.Serialize(123456789.987654321987654321987654321987654321m); data=strip_header(ser); Assert.AreEqual(B("'123456789.98765432198765432199'"), data); ComplexNumber cplx = new ComplexNumber(2.2, 3.3); ser = serpent.Serialize(cplx); data = strip_header(ser); Assert.AreEqual(B("(2.2+3.3j)"), data); cplx = new ComplexNumber(0, 3); ser = serpent.Serialize(cplx); data = strip_header(ser); Assert.AreEqual(B("(0+3j)"), data); cplx = new ComplexNumber(-2, -3); ser = serpent.Serialize(cplx); data = strip_header(ser); Assert.AreEqual(B("(-2-3j)"), data); } [Test] public void TestDoubleNanInf() { Serializer serpent = new Serializer(); var doubles = new object[] {double.PositiveInfinity, double.NegativeInfinity, double.NaN, float.PositiveInfinity, float.NegativeInfinity, float.NaN, new ComplexNumber(double.PositiveInfinity, 3.4)}; byte[] ser = serpent.Serialize(doubles); byte[] data = strip_header(ser); Assert.AreEqual("(1e30000,-1e30000,{'__class__':'float','value':'nan'},1e30000,-1e30000,{'__class__':'float','value':'nan'},(1e30000+3.4j))", S(data)); } [Test] public void TestBool() { Serializer serpent = new Serializer(); byte[] ser = serpent.Serialize(true); byte[] data = strip_header(ser); Assert.AreEqual(B("True"),data); ser = serpent.Serialize(false); data = strip_header(ser); Assert.AreEqual(B("False"),data); } [Test] public void TestList() { Serializer serpent = new Serializer(); IList list = new List(); // test empty list byte[] ser = strip_header(serpent.Serialize(list)); Assert.AreEqual("[]", S(ser)); serpent.Indent=true; ser = strip_header(serpent.Serialize(list)); Assert.AreEqual("[]", S(ser)); serpent.Indent=false; // test nonempty list list.Add(42); list.Add("Sally"); list.Add(16.5); ser = strip_header(serpent.Serialize(list)); Assert.AreEqual("[42,'Sally',16.5]", S(ser)); serpent.Indent=true; ser = strip_header(serpent.Serialize(list)); Assert.AreEqual("[\n 42,\n 'Sally',\n 16.5\n]", S(ser)); } [Test] public void TestSet() { // test with set literals Serializer serpent = new Serializer(); serpent.SetLiterals = true; HashSet set = new HashSet(); // test empty set byte[] ser = strip_header(serpent.Serialize(set)); Assert.AreEqual("()", S(ser)); // empty set is serialized as a tuple. serpent.Indent=true; ser = strip_header(serpent.Serialize(set)); Assert.AreEqual("()", S(ser)); // empty set is serialized as a tuple. serpent.Indent=false; // test nonempty set set.Add(42); set.Add("Sally"); set.Add(16.5); ser = strip_header(serpent.Serialize(set)); Assert.AreEqual("{42,'Sally',16.5}", S(ser)); serpent.Indent=true; ser = strip_header(serpent.Serialize(set)); Assert.AreEqual("{\n 42,\n 'Sally',\n 16.5\n}", S(ser)); // test no set literals serpent.Indent=false; serpent.SetLiterals=false; ser = strip_header(serpent.Serialize(set)); Assert.AreEqual("(42,'Sally',16.5)", S(ser)); // needs to be tuple now } [Test] public void TestDictionary() { Serializer serpent = new Serializer(); Parser p = new Parser(); // test empty dict IDictionary ht = new Hashtable(); byte[] ser = serpent.Serialize(ht); Assert.AreEqual(B("{}"), strip_header(ser)); string parsed = p.Parse(ser).Root.ToString(); Assert.AreEqual("{}", parsed); // empty dict with indentation serpent.Indent=true; ser = serpent.Serialize(ht); Assert.AreEqual(B("{}"), strip_header(ser)); parsed = p.Parse(ser).Root.ToString(); Assert.AreEqual("{}", parsed); // test dict with values serpent.Indent=false; ht = new Hashtable() { {42, "fortytwo"}, {"sixteen-and-half", 16.5}, {"name", "Sally"}, {"status", false} }; ser = serpent.Serialize(ht); Assert.AreEqual('}', ser[ser.Length-1]); Assert.AreNotEqual(',', ser[ser.Length-2]); parsed = p.Parse(ser).Root.ToString(); Assert.AreEqual(69, parsed.Length); // test indentation serpent.Indent=true; ser = serpent.Serialize(ht); Assert.AreEqual('}', ser[ser.Length-1]); Assert.AreEqual('\n', ser[ser.Length-2]); Assert.AreNotEqual(',', ser[ser.Length-3]); string ser_str = S(strip_header(ser)); Assert.IsTrue(ser_str.Contains("'name': 'Sally'")); Assert.IsTrue(ser_str.Contains("'status': False")); Assert.IsTrue(ser_str.Contains("42: 'fortytwo'")); Assert.IsTrue(ser_str.Contains("'sixteen-and-half': 16.5")); parsed = p.Parse(ser).Root.ToString(); Assert.AreEqual(69, parsed.Length); serpent.Indent=false; // generic Dictionary test IDictionary mydict = new Dictionary { { 1, "one" }, { 2, "two" }, }; ser = serpent.Serialize(mydict); ser_str = S(strip_header(ser)); Assert.IsTrue(ser_str=="{2:'two',1:'one'}" || ser_str=="{1:'one',2:'two'}"); } [Test] public void TestBytes() { Serializer serpent = new Serializer(indent: true); byte[] bytes = new byte[] { 97, 98, 99, 100, 101, 102 }; // abcdef byte[] ser = serpent.Serialize(bytes); Assert.AreEqual("{\n 'data': 'YWJjZGVm',\n 'encoding': 'base64'\n}", S(strip_header(ser))); Parser p = new Parser(); string parsed = p.Parse(ser).Root.ToString(); Assert.AreEqual(39, parsed.Length); var hashtable = new Hashtable { {"data", "YWJjZGVm"}, {"encoding", "base64"} }; byte[] bytes2 = Parser.ToBytes(hashtable); Assert.AreEqual(bytes, bytes2); var dict = new Dictionary { {"data", "YWJjZGVm"}, {"encoding", "base64"} }; bytes2 = Parser.ToBytes(dict); Assert.AreEqual(bytes, bytes2); var dict2 = new Dictionary { {"data", "YWJjZGVm"}, {"encoding", "base64"} }; bytes2 = Parser.ToBytes(dict2); Assert.AreEqual(bytes, bytes2); dict["encoding"] = "base99"; Assert.Throws(()=>Parser.ToBytes(dict)); dict.Clear(); Assert.Throws(()=>Parser.ToBytes(dict)); dict.Clear(); dict["data"] = "YWJjZGVm"; Assert.Throws(()=>Parser.ToBytes(dict)); dict.Clear(); dict["encoding"] = "base64"; Assert.Throws(()=>Parser.ToBytes(dict)); Assert.Throws(()=>Parser.ToBytes(12345)); Assert.Throws(()=>Parser.ToBytes(null)); } [Test] public void TestCollection() { ICollection intlist = new LinkedList(); intlist.Add(42); intlist.Add(43); Serializer serpent = new Serializer(); byte[] ser = serpent.Serialize(intlist); ser = strip_header(ser); Assert.AreEqual("[42,43]", S(ser)); ser=strip_header(serpent.Serialize(new int[] {42})); Assert.AreEqual("(42,)", S(ser)); ser=strip_header(serpent.Serialize(new int[] {42, 43})); Assert.AreEqual("(42,43)", S(ser)); serpent.Indent=true; ser = strip_header(serpent.Serialize(intlist)); Assert.AreEqual("[\n 42,\n 43\n]", S(ser)); ser=strip_header(serpent.Serialize(new int[] {42})); Assert.AreEqual("(\n 42,\n)", S(ser)); ser=strip_header(serpent.Serialize(new int[] {42, 43})); Assert.AreEqual("(\n 42,\n 43\n)", S(ser)); } [Test] public void TestIndentation() { var dict = new Dictionary(); var list = new List() { 1, 2, new string[] {"a", "b"} }; dict.Add("first", list); dict.Add("second", new Dictionary { {1, false} }); dict.Add("third", new HashSet { 3, 4} ); Serializer serpent = new Serializer(); serpent.Indent=true; byte[] ser = strip_header(serpent.Serialize(dict)); string txt=@"{ 'first': [ 1, 2, ( 'a', 'b' ) ], 'second': { 1: False }, 'third': { 3, 4 } }"; // bit of trickery to deal with Windows/Unix line ending differences txt = txt.Replace("\n","\r\n"); txt = txt.Replace("\r\r\n", "\r\n"); string ser_txt = S(ser); ser_txt = ser_txt.Replace("\n", "\r\n"); ser_txt = ser_txt.Replace("\r\r\n", "\r\n"); Assert.AreEqual(txt, ser_txt); } [Test] public void TestSorting() { Serializer serpent=new Serializer(); object data = new List { 3, 2, 1}; byte[] ser = strip_header(serpent.Serialize(data)); Assert.AreEqual("[3,2,1]", S(ser)); data = new int[] { 3,2,1 }; ser = strip_header(serpent.Serialize(data)); Assert.AreEqual("(3,2,1)", S(ser)); data = new HashSet { 42, "hi" }; serpent.Indent=true; ser = strip_header(serpent.Serialize(data)); Assert.IsTrue(S(ser)=="{\n 42,\n 'hi'\n}" || S(ser)=="{\n 'hi',\n 42\n}"); data = new Dictionary { {5, "five"}, {3, "three"}, {1, "one"}, {4, "four"}, {2, "two"} }; serpent.Indent=true; ser = strip_header(serpent.Serialize(data)); Assert.AreEqual("{\n 1: 'one',\n 2: 'two',\n 3: 'three',\n 4: 'four',\n 5: 'five'\n}", S(ser)); data = new HashSet { "x", "y", "z", "c", "b", "a" }; serpent.Indent=true; ser = strip_header(serpent.Serialize(data)); Assert.AreEqual("{\n 'a',\n 'b',\n 'c',\n 'x',\n 'y',\n 'z'\n}", S(ser)); } [Test] public void TestClass() { Serializer.RegisterClass(typeof(SerializeTestClass), null); Serializer serpent = new Serializer(indent: true); var obj = new SerializeTestClass() { i = 99, s = "hi", x = 42 }; byte[] ser = strip_header(serpent.Serialize(obj)); Assert.AreEqual("{\n '__class__': 'SerializeTestClass',\n 'i': 99,\n 'obj': None,\n 's': 'hi'\n}", S(ser)); } [Test] public void TestClass2() { Serializer.RegisterClass(typeof(SerializeTestClass), null); Serializer serpent = new Serializer(indent: true, namespaceInClassName: true); object obj = new SerializeTestClass() { i = 99, s = "hi", x = 42 }; byte[] ser = strip_header(serpent.Serialize(obj)); Assert.AreEqual("{\n '__class__': 'Razorvine.Serpent.Test.SerializeTestClass',\n 'i': 99,\n 'obj': None,\n 's': 'hi'\n}", S(ser)); } protected IDictionary testclassConverter(object obj) { SerializeTestClass o = (SerializeTestClass) obj; IDictionary result = new Hashtable(); result["__class@__"] = o.GetType().Name+"@"; result["i@"] = o.i; result["s@"] = o.s; result["x@"] = o.x; return result; } [Test] public void TestCustomClassDict() { Serializer.RegisterClass(typeof(SerializeTestClass), testclassConverter); Serializer serpent = new Serializer(indent: true); var obj = new SerializeTestClass() { i = 99, s = "hi", x = 42 }; byte[] ser = strip_header(serpent.Serialize(obj)); Assert.AreEqual("{\n '__class@__': 'SerializeTestClass@',\n 'i@': 99,\n 's@': 'hi',\n 'x@': 42\n}", S(ser)); } [Test] public void TestStruct() { Serializer serpent = new Serializer(indent: true); var obj2 = new SerializeTestStruct() { i = 99, s = "hi", x = 42 }; byte[] ser = strip_header(serpent.Serialize(obj2)); Assert.AreEqual("{\n '__class__': 'SerializeTestStruct',\n 'i': 99,\n 's': 'hi'\n}", S(ser)); } [Test] public void TestStruct2() { Serializer serpent = new Serializer(indent: true, namespaceInClassName: true); var obj2 = new SerializeTestStruct() { i = 99, s = "hi", x = 42 }; byte[] ser = strip_header(serpent.Serialize(obj2)); Assert.AreEqual("{\n '__class__': 'Razorvine.Serpent.Test.SerializeTestStruct',\n 'i': 99,\n 's': 'hi'\n}", S(ser)); } [Test] public void TestAnonymousClass() { Serializer serpent = new Serializer(indent: true); Object obj = new { Name="Harry", Age=33, Country="NL" }; byte[] ser = strip_header(serpent.Serialize(obj)); Assert.AreEqual("{\n 'Age': 33,\n 'Country': 'NL',\n 'Name': 'Harry'\n}", S(ser)); } [Test] public void TestDateTime() { Serializer serpent = new Serializer(); DateTime date = new DateTime(2013, 1, 20, 23, 59, 45, 999, DateTimeKind.Local); byte[] ser = strip_header(serpent.Serialize(date)); Assert.AreEqual("'2013-01-20T23:59:45.999'", S(ser)); date = new DateTime(2013, 1, 20, 23, 59, 45, 999, DateTimeKind.Utc); ser = strip_header(serpent.Serialize(date)); Assert.AreEqual("'2013-01-20T23:59:45.999'", S(ser)); date = new DateTime(2013, 1, 20, 23, 59, 45, 999, DateTimeKind.Unspecified); ser = strip_header(serpent.Serialize(date)); Assert.AreEqual("'2013-01-20T23:59:45.999'", S(ser)); date = new DateTime(2013, 1, 20, 23, 59, 45); ser = strip_header(serpent.Serialize(date)); Assert.AreEqual("'2013-01-20T23:59:45'", S(ser)); TimeSpan timespan = new TimeSpan(1, 10, 20, 30, 999); ser = strip_header(serpent.Serialize(timespan)); Assert.AreEqual("123630.999", S(ser)); } [Test] public void TestDateTimeOffset() { Serializer serpent = new Serializer(); DateTimeOffset date = new DateTimeOffset(2013, 1, 20, 23, 59, 45, 999, TimeSpan.FromHours(+2)); byte[] ser = strip_header(serpent.Serialize(date)); Assert.AreEqual("'2013-01-20T23:59:45.999+02:00'", S(ser)); date = new DateTimeOffset(2013, 5, 10, 13, 59, 45, TimeSpan.FromHours(+2)); ser = strip_header(serpent.Serialize(date)); Assert.AreEqual("'2013-05-10T13:59:45+02:00'", S(ser)); } [Test] public void TestException() { Exception x = new ApplicationException("errormessage"); Serializer serpent = new Serializer(indent:true); byte[] ser = strip_header(serpent.Serialize(x)); Assert.AreEqual("{\n '__class__': 'ApplicationException',\n '__exception__': True,\n 'args': (\n 'errormessage',\n ),\n 'attributes': {}\n}", S(ser)); x.Data["custom_attribute"]=999; ser = strip_header(serpent.Serialize(x)); Assert.AreEqual("{\n '__class__': 'ApplicationException',\n '__exception__': True,\n 'args': (\n 'errormessage',\n ),\n 'attributes': {\n 'custom_attribute': 999\n }\n}", S(ser)); } [Test] public void TestExceptionWithNamespace() { Exception x = new ApplicationException("errormessage"); Serializer serpent = new Serializer(indent:true, namespaceInClassName: true); byte[] ser = strip_header(serpent.Serialize(x)); Assert.AreEqual("{\n '__class__': 'System.ApplicationException',\n '__exception__': True,\n 'args': (\n 'errormessage',\n ),\n 'attributes': {}\n}", S(ser)); } enum FooType { Foobar, Jarjar } [Test] public void TestEnum() { FooType e = FooType.Jarjar; Serializer serpent = new Serializer(); byte[] ser = strip_header(serpent.Serialize(e)); Assert.AreEqual("'Jarjar'", S(ser)); } interface IBaseInterface {}; interface ISubInterface : IBaseInterface {}; class BaseClassWithInterface : IBaseInterface {}; class SubClassWithInterface : BaseClassWithInterface, ISubInterface {}; class BaseClass {}; class SubClass : BaseClass {}; abstract class AbstractBaseClass {}; class ConcreteSubClass : AbstractBaseClass {}; protected IDictionary AnyClassSerializer(object arg) { IDictionary result = new Hashtable(); result["(SUB)CLASS"] = arg.GetType().Name; return result; } [Test] public void testAbstractBaseClassHierarchyPickler() { ConcreteSubClass c = new ConcreteSubClass(); Serializer serpent = new Serializer(); serpent.Serialize(c); Serializer.RegisterClass(typeof(AbstractBaseClass), AnyClassSerializer); byte[] data = serpent.Serialize(c); Assert.AreEqual("{'(SUB)CLASS':'ConcreteSubClass'}", S(strip_header(data))); } [Test] public void TestInterfaceHierarchyPickler() { BaseClassWithInterface b = new BaseClassWithInterface(); SubClassWithInterface sub = new SubClassWithInterface(); Serializer serpent = new Serializer(); serpent.Serialize(b); serpent.Serialize(sub); Serializer.RegisterClass(typeof(IBaseInterface), AnyClassSerializer); byte[] data = serpent.Serialize(b); Assert.AreEqual("{'(SUB)CLASS':'BaseClassWithInterface'}", S(strip_header(data))); data = serpent.Serialize(sub); Assert.AreEqual("{'(SUB)CLASS':'SubClassWithInterface'}", S(strip_header(data))); } } public class SerializeTestClass { public int x; public string s {get; set;} public int i {get; set;} public object obj {get; set;} } public struct SerializeTestStruct { public int x; public string s {get; set;} public int i {get; set;} } } Serpent-serpent-1.23/dotnet/Serpent.Test/Serpent.Test.csproj000066400000000000000000000066101312652634400241610ustar00rootroot00000000000000 {44F33161-24F6-41AC-8097-0AF7B43F3786} Debug x86 Library Razorvine.Serpent.Test Razorvine.Serpent.Test Properties OnBuildSuccess False False 4 false 8.0.30703 2.0 v4.5 x86 bin\Debug\ true Full False True DEBUG;TRACE bin\Release\ false PdbOnly True False TRACE AnyCPU False Auto 4194304 4096 none none ..\lib\nunit.framework.dll Always {5A8E1B96-8C48-4A35-A3C4-0844486B2D47} Serpent Serpent-serpent-1.23/dotnet/Serpent.Test/SlowPerformanceTest.cs000066400000000000000000000031001312652634400246650ustar00rootroot00000000000000using System; using NUnit.Framework; namespace Razorvine.Serpent.Test { [TestFixture] public class SlowPerformanceTest { // tests some performance regressions when they occur [Test] [Ignore("number parse performance in long lists has been resolved")] public void TestManyFloats() { const int amount = 200000; double[] array = new double[amount]; for(int i=0; i using System; using System.Collections.Generic; using NUnit.Framework; namespace Razorvine.Serpent.Test { [TestFixture] public class StringreaderTest { [Test] public void TestStuff() { using(SeekableStringReader s=new SeekableStringReader("hello")) { Assert.AreEqual('h', s.Peek()); Assert.AreEqual("hel", s.Peek(3)); Assert.AreEqual('h', s.Read()); Assert.AreEqual("ell", s.Read(3)); Assert.AreEqual("o", s.Peek(999)); Assert.AreEqual("o", s.Read(999)); } using(SeekableStringReader s2 = new SeekableStringReader(" skip.\t\n\rwhitespace. ")) { s2.SkipWhitespace(); Assert.AreEqual("skip", s2.ReadUntil('.')); s2.SkipWhitespace(); Assert.AreEqual("whitespace", s2.ReadUntil('.')); s2.SkipWhitespace(); Assert.IsFalse(s2.HasMore()); Assert.Throws(()=>s2.Peek()); } } [Test] public void TestRead() { SeekableStringReader s = new SeekableStringReader("hello"); Assert.AreEqual('h', s.Read()); Assert.AreEqual('e', s.Read()); Assert.AreEqual("l", s.Read(1)); Assert.AreEqual("lo", s.Read(2)); Assert.Throws(()=>s.Read()); } [Test] public void TestRanges() { SeekableStringReader s = new SeekableStringReader("hello"); Assert.Throws(()=>s.Read(-1)); Assert.AreEqual("hello", s.Read(999)); Assert.Throws(()=>s.Read(1)); s.Rewind(int.MaxValue); Assert.IsTrue(s.HasMore()); Assert.AreEqual("hello", s.Peek(999)); Assert.IsTrue(s.HasMore()); } [Test] public void TestReadUntil() { SeekableStringReader s = new SeekableStringReader("hello there"); s.Read(); Assert.AreEqual("ello", s.ReadUntil(' ')); Assert.AreEqual('t', s.Peek()); Assert.Throws(()=>s.ReadUntil('x')); Assert.AreEqual("there", s.Rest()); Assert.Throws(()=>s.Rest()); s.Rewind(int.MaxValue); Assert.AreEqual("hell", s.ReadUntil('x', 'y', 'z', ' ', 'o')); Assert.Throws(()=>s.ReadUntil('x', 'y', '@')); } [Test] public void TestReadWhile() { SeekableStringReader s = new SeekableStringReader("123.456 foo"); Assert.AreEqual("123.456", s.ReadWhile("0123456789.")); Assert.AreEqual("", s.ReadWhile("@")); Assert.AreEqual(" ", s.ReadWhile(" ")); Assert.AreEqual("foo", s.Rest()); } [Test] public void TestBookmark() { SeekableStringReader s = new SeekableStringReader("hello"); s.Read(2); int bookmark = s.Bookmark(); Assert.AreEqual("ll", s.Read(2)); s.FlipBack(bookmark); Assert.AreEqual("ll", s.Read(2)); Assert.AreEqual("o", s.Read(999)); } [Test] public void TestNesting() { SeekableStringReader outer = new SeekableStringReader("hello!"); outer.Read(1); SeekableStringReader inner1 = new SeekableStringReader(outer); SeekableStringReader inner2 = new SeekableStringReader(outer); Assert.AreEqual("ell", inner1.Read(3)); Assert.AreEqual("el", inner2.Read(2)); Assert.AreEqual("o", inner1.Read(1)); Assert.AreEqual("l", inner2.Read(1)); Assert.AreEqual("e", outer.Read(1)); Assert.AreEqual("o", inner2.Read(1)); Assert.AreEqual("l", outer.Read(1)); outer.Sync(inner2); Assert.AreEqual("!", outer.Read(1)); } [Test] public void TestContext() { SeekableStringReader s = new SeekableStringReader("abcdefghijklmnopqrstuvwxyz"); s.Read(10); string left, right; s.Context(-1, 5, out left, out right); Assert.AreEqual("fghij", left); Assert.AreEqual("klmno", right); s.Context(-1, 12, out left, out right); Assert.AreEqual("abcdefghij", left); Assert.AreEqual("klmnopqrstuv", right); s.Read(13); s.Context(-1, 6, out left, out right); Assert.AreEqual("rstuvw", left); Assert.AreEqual("xyz", right); s.Context(5,4, out left, out right); Assert.AreEqual("bcde", left); Assert.AreEqual("fghi", right); } } }Serpent-serpent-1.23/dotnet/Serpent.Test/app.config000066400000000000000000000001161312652634400223430ustar00rootroot00000000000000 Serpent-serpent-1.23/dotnet/Serpent.Test/testserpent.utf8.bin000066400000000000000000000022361312652634400243400ustar00rootroot00000000000000# serpent utf-8 python3.3 { # serpent supports comments in the serialized data 'bytes': { 'data': 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==', 'encoding': 'base64' }, 'dates': [ # test some dates. '2013-01-26T03:32:39.007001', '23:59:45.999888', 500.0 ], 'dict': { 0: '0000', 1: '1111', 2: '2222', 3: '3333', 4: '4444', 5: '5555', 6: '6666', 7: '7777', 8: '8888', 9: '9999' }, 'exc': { '__class__': 'ZeroDivisionError', '__exception__': True, # this is an exception 'args': ( 'fault', ), 'attributes': {} }, 'list': [ 1, 2, 3, 4, 5, 6, 7, 8 ], 'numbers': [ 12345678901234567890123456, 999.1234, '1.99999999999999999991', (-3+8j) ], 'set': { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 'str': 'hello', 'tuple': ( 1, 2, 3, 4, 5, 6, 7, 8 ), 'unicode': '€', # the feared unicode! 'uuid': '57e12d82-511f-456b-ab65-f765144c6a90' }Serpent-serpent-1.23/dotnet/Serpent.sln000066400000000000000000000036721312652634400202060ustar00rootroot00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 # SharpDevelop 5.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serpent", "Serpent\Serpent.csproj", "{5A8E1B96-8C48-4A35-A3C4-0844486B2D47}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serpent.Test", "Serpent.Test\Serpent.Test.csproj", "{44F33161-24F6-41AC-8097-0AF7B43F3786}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Debug|Any CPU = Debug|Any CPU Release|x86 = Release|x86 Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5A8E1B96-8C48-4A35-A3C4-0844486B2D47}.Debug|x86.ActiveCfg = Debug|x86 {5A8E1B96-8C48-4A35-A3C4-0844486B2D47}.Debug|x86.Build.0 = Debug|x86 {5A8E1B96-8C48-4A35-A3C4-0844486B2D47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5A8E1B96-8C48-4A35-A3C4-0844486B2D47}.Debug|Any CPU.Build.0 = Debug|Any CPU {5A8E1B96-8C48-4A35-A3C4-0844486B2D47}.Release|x86.ActiveCfg = Release|x86 {5A8E1B96-8C48-4A35-A3C4-0844486B2D47}.Release|x86.Build.0 = Release|x86 {5A8E1B96-8C48-4A35-A3C4-0844486B2D47}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A8E1B96-8C48-4A35-A3C4-0844486B2D47}.Release|Any CPU.Build.0 = Release|Any CPU {44F33161-24F6-41AC-8097-0AF7B43F3786}.Debug|x86.ActiveCfg = Debug|x86 {44F33161-24F6-41AC-8097-0AF7B43F3786}.Debug|x86.Build.0 = Debug|x86 {44F33161-24F6-41AC-8097-0AF7B43F3786}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {44F33161-24F6-41AC-8097-0AF7B43F3786}.Debug|Any CPU.Build.0 = Debug|Any CPU {44F33161-24F6-41AC-8097-0AF7B43F3786}.Release|x86.ActiveCfg = Release|x86 {44F33161-24F6-41AC-8097-0AF7B43F3786}.Release|x86.Build.0 = Release|x86 {44F33161-24F6-41AC-8097-0AF7B43F3786}.Release|Any CPU.ActiveCfg = Release|Any CPU {44F33161-24F6-41AC-8097-0AF7B43F3786}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal Serpent-serpent-1.23/dotnet/Serpent/000077500000000000000000000000001312652634400174605ustar00rootroot00000000000000Serpent-serpent-1.23/dotnet/Serpent/Ast.cs000066400000000000000000000224331312652634400205420ustar00rootroot00000000000000/// /// Serpent, a Python literal expression serializer/deserializer /// (a.k.a. Python's ast.literal_eval in .NET) /// /// Copyright Irmen de Jong (irmen@razorvine.net) /// Software license: "MIT software license". See http://opensource.org/licenses/MIT /// using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Text; using System.Linq; namespace Razorvine.Serpent { /// /// Abstract syntax tree for the literal expression. This is what the parser returns. /// public class Ast { public INode Root; public override string ToString() { return "# serpent utf-8 .net\n" + Root.ToString(); } /// /// Get the actual parsed data as C# object(s). /// public object GetData() { var visitor = new ObjectifyVisitor(); Root.Accept(visitor); return visitor.GetObject(); } /// /// Get the actual parsed data as C# object(s). /// /// functin to convert dicts to actual instances for a class, /// instead of leaving them as dictionaries. Requires the __class__ key to be present /// in the dict node. If it returns null, the normal processing is done. public object GetData(Func dictToInstance) { var visitor = new ObjectifyVisitor(dictToInstance); Root.Accept(visitor); return visitor.GetObject(); } public interface INodeVisitor { void Visit(Ast.ComplexNumberNode complex); void Visit(Ast.DictNode dict); void Visit(Ast.ListNode list); void Visit(Ast.NoneNode none); void Visit(Ast.IntegerNode value); void Visit(Ast.LongNode value); void Visit(Ast.DoubleNode value); void Visit(Ast.BooleanNode value); void Visit(Ast.StringNode value); void Visit(Ast.DecimalNode value); void Visit(Ast.SetNode setnode); void Visit(Ast.TupleNode tuple); } /// /// Visitor pattern: visit all nodes in the Ast with the given visitor. /// public void Accept(INodeVisitor visitor) { Root.Accept(visitor); } public interface INode { string ToString(); bool Equals(object obj); void Accept(INodeVisitor visitor); } public abstract class PrimitiveNode : INode, IComparable> where T: IComparable { public T Value; public PrimitiveNode(T value) { this.Value=value; } public override int GetHashCode() { return Value!=null? Value.GetHashCode() : 0; } public override bool Equals(object obj) { return (obj is Ast.PrimitiveNode) && Equals(Value, ((Ast.PrimitiveNode)obj).Value); } public bool Equals(Ast.PrimitiveNode other) { return object.Equals(this.Value, other.Value); } public int CompareTo(PrimitiveNode other) { return Value.CompareTo(other.Value); } public override string ToString() { if(Value is string) { StringBuilder sb=new StringBuilder(); sb.Append("'"); foreach(char c in (Value as string)) { switch(c) { case '\\': sb.Append("\\\\"); break; case '\'': sb.Append("\\'"); break; case '\a': sb.Append("\\a"); break; case '\b': sb.Append("\\b"); break; case '\f': sb.Append("\\f"); break; case '\n': sb.Append("\\n"); break; case '\r': sb.Append("\\r"); break; case '\t': sb.Append("\\t"); break; case '\v': sb.Append("\\v"); break; default: sb.Append(c); break; } } sb.Append("'"); return sb.ToString(); } else if(Value is double || Value is float) { string d = Convert.ToString(Value, CultureInfo.InvariantCulture); if(d.IndexOfAny(new char[] {'.', 'e', 'E'})<=0) d+=".0"; return d; } else return Value.ToString(); } public abstract void Accept(Ast.INodeVisitor visitor); } public class IntegerNode: PrimitiveNode { public IntegerNode(int value) : base(value) { } public override void Accept(Ast.INodeVisitor visitor) { visitor.Visit(this); } } public class LongNode: PrimitiveNode { public LongNode(long value) : base(value) { } public override void Accept(Ast.INodeVisitor visitor) { visitor.Visit(this); } } public class DoubleNode: PrimitiveNode { public DoubleNode(double value) : base(value) { } public override void Accept(Ast.INodeVisitor visitor) { visitor.Visit(this); } } public class StringNode: PrimitiveNode { public StringNode(string value) : base(value) { } public override void Accept(Ast.INodeVisitor visitor) { visitor.Visit(this); } } public class DecimalNode: PrimitiveNode { public DecimalNode(decimal value) : base(value) { } public override void Accept(Ast.INodeVisitor visitor) { visitor.Visit(this); } } public class BooleanNode: PrimitiveNode { public BooleanNode(bool value) : base(value) { } public override void Accept(Ast.INodeVisitor visitor) { visitor.Visit(this); } } public struct ComplexNumberNode: INode { public double Real; public double Imaginary; public void Accept(INodeVisitor visitor) { visitor.Visit(this); } public override string ToString() { string strReal = Real.ToString(CultureInfo.InvariantCulture); string strImag = Imaginary.ToString(CultureInfo.InvariantCulture); if(Imaginary>=0) return string.Format("({0}+{1}j)", strReal, strImag); return string.Format("({0}{1}j)", strReal, strImag); } } public class NoneNode: INode { public static NoneNode Instance = new NoneNode(); private NoneNode() { } public override string ToString() { return "None"; } public void Accept(INodeVisitor visitor) { visitor.Visit(this); } } public abstract class SequenceNode: INode { public List Elements = new List(); public virtual char OpenChar {get { return '?'; }} public virtual char CloseChar {get { return '?'; }} public override int GetHashCode() { int hashCode = 0; unchecked { foreach(INode elt in Elements) hashCode += 1000000007 * elt.GetHashCode(); } return hashCode; } public override bool Equals(object obj) { Ast.SequenceNode other = obj as Ast.SequenceNode; if (other == null) return false; return Enumerable.SequenceEqual(Elements, other.Elements); } public override string ToString() { StringBuilder sb=new StringBuilder(); sb.Append(OpenChar); if(Elements != null) { foreach(var elt in Elements) { sb.Append(elt.ToString()); sb.Append(','); } } if(Elements.Count>0) sb.Remove(sb.Length-1, 1); // remove last comma sb.Append(CloseChar); return sb.ToString(); } public abstract void Accept(Ast.INodeVisitor visitor); } public class TupleNode : SequenceNode { public override string ToString() { StringBuilder sb=new StringBuilder(); sb.Append('('); if(Elements != null) { foreach(var elt in Elements) { sb.Append(elt.ToString()); sb.Append(","); } } if(Elements.Count>1) sb.Remove(sb.Length-1, 1); sb.Append(')'); return sb.ToString(); } public override void Accept(Ast.INodeVisitor visitor) { visitor.Visit(this); } } public class ListNode : SequenceNode { public override char OpenChar { get { return '['; } } public override char CloseChar { get { return ']'; } } public override void Accept(Ast.INodeVisitor visitor) { visitor.Visit(this); } } public abstract class UnorderedSequenceNode : SequenceNode { public override bool Equals(object obj) { if(!(obj is UnorderedSequenceNode)) return false; var set1 = ElementsAsSet(); var set2 = (obj as UnorderedSequenceNode).ElementsAsSet(); return set1.SetEquals(set2); } public override int GetHashCode() { return ElementsAsSet().GetHashCode(); } public HashSet ElementsAsSet() { var set = new HashSet(); foreach(INode kv in Elements) set.Add(kv); return set; } } public class SetNode : UnorderedSequenceNode { public override char OpenChar { get { return '{'; } } public override char CloseChar { get { return '}'; } } public override void Accept(Ast.INodeVisitor visitor) { visitor.Visit(this); } } public class DictNode : UnorderedSequenceNode { public override char OpenChar { get { return '{'; } } public override char CloseChar { get { return '}'; } } public override void Accept(Ast.INodeVisitor visitor) { visitor.Visit(this); } } public struct KeyValueNode : INode { public INode Key; public INode Value; public KeyValueNode(INode key, INode value) { this.Key = key; this.Value = value; } public override string ToString() { return string.Format("{0}:{1}", Key, Value); } public void Accept(INodeVisitor visitor) { throw new NotSupportedException("don't visit a keyvaluenode"); } } } } Serpent-serpent-1.23/dotnet/Serpent/ComplexNumber.cs000066400000000000000000000045631312652634400225770ustar00rootroot00000000000000/// /// Serpent, a Python literal expression serializer/deserializer /// (a.k.a. Python's ast.literal_eval in .NET) /// /// Copyright Irmen de Jong (irmen@razorvine.net) /// Software license: "MIT software license". See http://opensource.org/licenses/MIT /// using System; using System.Text; namespace Razorvine.Serpent { /// /// A Complex Number class. /// public class ComplexNumber { public double Real {get; private set;} public double Imaginary {get; private set;} public ComplexNumber(double r, double i) { Real=r; Imaginary=i; } public override string ToString() { StringBuilder sb=new StringBuilder(); sb.Append(Real); if(Imaginary>0) sb.Append('+'); return sb.Append(Imaginary).Append('i').ToString(); } public double Magnitude() { return Math.Sqrt(Real * Real + Imaginary * Imaginary); } public static ComplexNumber operator +(ComplexNumber c1, ComplexNumber c2) { return new ComplexNumber(c1.Real + c2.Real, c1.Imaginary + c2.Imaginary); } public static ComplexNumber operator -(ComplexNumber c1, ComplexNumber c2) { return new ComplexNumber(c1.Real - c2.Real, c1.Imaginary - c2.Imaginary); } public static ComplexNumber operator *(ComplexNumber c1, ComplexNumber c2) { return new ComplexNumber(c1.Real * c2.Real - c1.Imaginary * c2.Imaginary, c1.Real * c2.Imaginary + c1.Imaginary * c2.Real); } public static ComplexNumber operator /(ComplexNumber c1, ComplexNumber c2) { return new ComplexNumber((c1.Real * c2.Real + c1.Imaginary * c2.Imaginary) / (c2.Real * c2.Real + c2.Imaginary * c2.Imaginary), (c1.Imaginary * c2.Real - c1.Real * c2.Imaginary) / (c2.Real * c2.Real + c2.Imaginary * c2.Imaginary)); } #region Equals and GetHashCode implementation public override bool Equals(object obj) { if(!(obj is ComplexNumber)) return false; ComplexNumber other = (ComplexNumber) obj; return Real==other.Real && Imaginary==other.Imaginary; } public override int GetHashCode() { return (Real.GetHashCode()) ^ (Imaginary.GetHashCode()); } public static bool operator ==(ComplexNumber lhs, ComplexNumber rhs) { if (ReferenceEquals(lhs, rhs)) return true; if (ReferenceEquals(lhs, null) || ReferenceEquals(rhs, null)) return false; return lhs.Equals(rhs); } public static bool operator !=(ComplexNumber lhs, ComplexNumber rhs) { return !(lhs == rhs); } #endregion } }Serpent-serpent-1.23/dotnet/Serpent/DebugVisitor.cs000066400000000000000000000054721312652634400224250ustar00rootroot00000000000000/// /// Serpent, a Python literal expression serializer/deserializer /// (a.k.a. Python's ast.literal_eval in .NET) /// /// Copyright Irmen de Jong (irmen@razorvine.net) /// Software license: "MIT software license". See http://opensource.org/licenses/MIT /// using System; using System.Collections.Generic; using System.Text; namespace Razorvine.Serpent { /// /// Ast nodevisitor that prints out the Ast as a string for debugging purposes /// public class DebugVisitor: Ast.INodeVisitor { private StringBuilder result = new StringBuilder(); private int indent=0; public DebugVisitor() { } /// /// Get the debug string representation result. /// public override string ToString() { return result.ToString(); } protected void Indent() { for(int i=0; i /// Serpent, a Python literal expression serializer/deserializer /// (a.k.a. Python's ast.literal_eval in .NET) /// /// Copyright Irmen de Jong (irmen@razorvine.net) /// Software license: "MIT software license". See http://opensource.org/licenses/MIT /// using System; using System.Collections.Generic; using System.Collections; namespace Razorvine.Serpent { /// /// Ast nodevisitor that turns the AST into actual .NET objects (array, int, IDictionary, string, etc...) /// public class ObjectifyVisitor: Ast.INodeVisitor { private Stack generated = new Stack(); private Func dictToInstance = null; /// /// Create the visitor that converts AST in actual objects. /// public ObjectifyVisitor() { } /// /// Create the visitor that converts AST in actual objects. /// /// functin to convert dicts to actual instances for a class, /// instead of leaving them as dictionaries. Requires the __class__ key to be present /// in the dict node. If it returns null, the normal processing is done. public ObjectifyVisitor(Func dictToInstance) { this.dictToInstance = dictToInstance; } /// /// get the resulting object tree. /// public object GetObject() { return generated.Pop(); } public void Visit(Ast.ComplexNumberNode complex) { generated.Push(new ComplexNumber(complex.Real, complex.Imaginary)); } public void Visit(Ast.DictNode dict) { IDictionary obj = new Dictionary(dict.Elements.Count); foreach(Ast.KeyValueNode kv in dict.Elements) { kv.Key.Accept(this); object key = generated.Pop(); kv.Value.Accept(this); object value = generated.Pop(); obj[key] = value; } if(dictToInstance==null || !obj.Contains("__class__")) { generated.Push(obj); } else { object result = dictToInstance(obj); if(result==null) generated.Push(obj); else generated.Push(result); } } public void Visit(Ast.ListNode list) { IList obj = new List(list.Elements.Count); foreach(Ast.INode node in list.Elements) { node.Accept(this); obj.Add(generated.Pop()); } generated.Push(obj); } public void Visit(Ast.NoneNode none) { generated.Push(null); } public void Visit(Ast.IntegerNode value) { generated.Push(value.Value); } public void Visit(Ast.LongNode value) { generated.Push(value.Value); } public void Visit(Ast.DoubleNode value) { generated.Push(value.Value); } public void Visit(Ast.BooleanNode value) { generated.Push(value.Value); } public void Visit(Ast.StringNode value) { generated.Push(value.Value); } public void Visit(Ast.DecimalNode value) { generated.Push(value.Value); } public void Visit(Ast.SetNode setnode) { HashSet obj = new HashSet(); foreach(Ast.INode node in setnode.Elements) { node.Accept(this); obj.Add(generated.Pop()); } generated.Push(obj); } public void Visit(Ast.TupleNode tuple) { object[] array = new object[tuple.Elements.Count]; int index=0; foreach(Ast.INode node in tuple.Elements) { node.Accept(this); array[index++] = generated.Pop(); } generated.Push(array); } } } Serpent-serpent-1.23/dotnet/Serpent/ParseException.cs000066400000000000000000000012171312652634400227410ustar00rootroot00000000000000/// /// Serpent, a Python literal expression serializer/deserializer /// (a.k.a. Python's ast.literal_eval in .NET) /// /// Copyright Irmen de Jong (irmen@razorvine.net) /// Software license: "MIT software license". See http://opensource.org/licenses/MIT /// using System; namespace Razorvine.Serpent { /// /// A problem occurred during parsing. /// public class ParseException : Exception { public ParseException() { } public ParseException(string message) : base(message) { } public ParseException(string message, Exception innerException) : base(message, innerException) { } } }Serpent-serpent-1.23/dotnet/Serpent/Parser.cs000066400000000000000000000446471312652634400212620ustar00rootroot00000000000000/// /// Serpent, a Python literal expression serializer/deserializer /// (a.k.a. Python's ast.literal_eval in .NET) /// /// Copyright Irmen de Jong (irmen@razorvine.net) /// Software license: "MIT software license". See http://opensource.org/licenses/MIT /// using System; #if !(SILVERLIGHT || WINDOWS_PHONE || PORTABLE) using System.Collections; #endif using System.Collections.Generic; using System.Globalization; using System.Text; namespace Razorvine.Serpent { /// /// Parse a Python literal into an Ast (abstract syntax tree). /// public class Parser { /// /// Parse from a byte array (containing utf-8 encoded string with the Python literal expression in it) /// public Ast Parse(byte[] serialized) { return Parse(Encoding.UTF8.GetString(serialized, 0, serialized.Length)); } /// /// Parse from a string with the Python literal expression /// public Ast Parse(string expression) { Ast ast=new Ast(); if(string.IsNullOrEmpty(expression)) return ast; SeekableStringReader sr = new SeekableStringReader(expression); if(sr.Peek()=='#') sr.ReadUntil('\n'); // skip comment line try { ast.Root = ParseExpr(sr); sr.SkipWhitespace(); if(sr.HasMore()) throw new ParseException("garbage at end of expression"); return ast; } catch (ParseException x) { string faultLocation = ExtractFaultLocation(sr); throw new ParseException(x.Message + " (at position "+sr.Bookmark()+"; '"+faultLocation+"')", x); } } string ExtractFaultLocation(SeekableStringReader sr) { string left, right; sr.Context(-1, 20, out left, out right); return string.Format("...{0}>>><<<{1}...", left, right); } Ast.INode ParseExpr(SeekableStringReader sr) { // expr = [ ] single | compound [ ] . sr.SkipWhitespace(); if(!sr.HasMore()) throw new ParseException("unexpected end of line, missing expression or close/open character"); char c = sr.Peek(); Ast.INode node; if(c=='{' || c=='[' || c=='(') node = ParseCompound(sr); else node = ParseSingle(sr); sr.SkipWhitespace(); return node; } Ast.INode ParseCompound(SeekableStringReader sr) { // compound = tuple | dict | list | set . sr.SkipWhitespace(); switch(sr.Peek()) { case '[': return ParseList(sr); case '{': { int bm = sr.Bookmark(); try { return ParseSet(sr); } catch(ParseException) { sr.FlipBack(bm); return ParseDict(sr); } } case '(': // tricky case here, it can be a tuple but also a complex number: // if the last character before the closing parenthesis is a 'j', it is a complex number { int bm = sr.Bookmark(); string betweenparens = sr.ReadUntil(new char[]{')','\n'}).TrimEnd(); sr.FlipBack(bm); return betweenparens.EndsWith("j") ? (Ast.INode) ParseComplex(sr) : ParseTuple(sr); } default: throw new ParseException("invalid sequencetype char"); } } Ast.TupleNode ParseTuple(SeekableStringReader sr) { //tuple = tuple_empty | tuple_one | tuple_more //tuple_empty = '()' . //tuple_one = '(' expr ',' ')' . //tuple_more = '(' expr_list trailing_comma ')' . // trailing_comma = '' | ',' . sr.Read(); // ( sr.SkipWhitespace(); Ast.TupleNode tuple = new Ast.TupleNode(); if(sr.Peek() == ')') { sr.Read(); return tuple; // empty tuple } Ast.INode firstelement = ParseExpr(sr); if(sr.Peek() == ',') { sr.Read(); sr.SkipWhitespace(); if(sr.Read() == ')') { // tuple with just a single element tuple.Elements.Add(firstelement); return tuple; } sr.Rewind(1); // undo the thing that wasn't a ) } tuple.Elements = ParseExprList(sr); tuple.Elements.Insert(0, firstelement); // handle trailing comma if present sr.SkipWhitespace(); if(!sr.HasMore()) throw new ParseException("missing ')'"); if(sr.Peek() == ',') sr.Read(); if(!sr.HasMore()) throw new ParseException("missing ')'"); char closechar = sr.Read(); if(closechar==',') closechar = sr.Read(); if(closechar!=')') throw new ParseException("expected ')'"); return tuple; } List ParseExprList(SeekableStringReader sr) { //expr_list = expr { ',' expr } . List exprList = new List(); exprList.Add(ParseExpr(sr)); while(sr.HasMore() && sr.Peek() == ',') { sr.Read(); try { exprList.Add(ParseExpr(sr)); } catch (ParseException) { sr.Rewind(1); break; } } return exprList; } List ParseKeyValueList(SeekableStringReader sr) { //keyvalue_list = keyvalue { ',' keyvalue } . List kvs = new List(); kvs.Add(ParseKeyValue(sr)); while(sr.HasMore() && sr.Peek()==',') { sr.Read(); try { kvs.Add(ParseKeyValue(sr)); } catch (ParseException) { sr.Rewind(1); break; } } return kvs; } Ast.KeyValueNode ParseKeyValue(SeekableStringReader sr) { //keyvalue = expr ':' expr . Ast.INode key = ParseExpr(sr); if(sr.HasMore() && sr.Peek()==':') { sr.Read(); // : Ast.INode value = ParseExpr(sr); return new Ast.KeyValueNode { Key = key, Value = value }; } throw new ParseException("expected ':'"); } Ast.SetNode ParseSet(SeekableStringReader sr) { // set = '{' expr_list trailing_comma '}' . // trailing_comma = '' | ',' . sr.Read(); // { sr.SkipWhitespace(); Ast.SetNode setnode = new Ast.SetNode(); List elts = ParseExprList(sr); // handle trailing comma if present sr.SkipWhitespace(); if(!sr.HasMore()) throw new ParseException("missing '}'"); if(sr.Peek() == ',') sr.Read(); if(!sr.HasMore()) throw new ParseException("missing '}'"); char closechar = sr.Read(); if(closechar!='}') throw new ParseException("expected '}'"); // make sure it has set semantics (remove duplicate elements) HashSet h = new HashSet(elts); setnode.Elements = new List(h); return setnode; } Ast.ListNode ParseList(SeekableStringReader sr) { // list = list_empty | list_nonempty . // list_empty = '[]' . // list_nonempty = '[' expr_list trailing_comma ']' . // trailing_comma = '' | ',' . sr.Read(); // [ sr.SkipWhitespace(); Ast.ListNode list = new Ast.ListNode(); if(sr.Peek() == ']') { sr.Read(); return list; // empty list } list.Elements = ParseExprList(sr); // handle trailing comma if present sr.SkipWhitespace(); if(!sr.HasMore()) throw new ParseException("missing ']'"); if(sr.Peek() == ',') sr.Read(); if(!sr.HasMore()) throw new ParseException("missing ']'"); char closechar = sr.Read(); if(closechar!=']') throw new ParseException("expected ']'"); return list; } Ast.INode ParseDict(SeekableStringReader sr) { //dict = '{' keyvalue_list trailing_comma '}' . //keyvalue_list = keyvalue { ',' keyvalue } . //keyvalue = expr ':' expr . // trailing_comma = '' | ',' . sr.Read(); // { sr.SkipWhitespace(); Ast.DictNode dict = new Ast.DictNode(); if(sr.Peek() == '}') { sr.Read(); return dict; // empty dict } List elts = ParseKeyValueList(sr); // handle trailing comma if present sr.SkipWhitespace(); if(!sr.HasMore()) throw new ParseException("missing '}'"); if(sr.Peek() == ',') sr.Read(); if(!sr.HasMore()) throw new ParseException("missing '}'"); char closechar = sr.Read(); if(closechar!='}') throw new ParseException("expected '}'"); // make sure it has dict semantics (remove duplicate keys) Dictionary fixedDict = new Dictionary(elts.Count); foreach(Ast.KeyValueNode kv in elts) fixedDict[kv.Key] = kv.Value; foreach(var kv in fixedDict) { dict.Elements.Add(new Ast.KeyValueNode() { Key=kv.Key, Value=kv.Value }); } // SPECIAL CASE: {'__class__':'float','value':'nan'} ---> Double.NaN if(dict.Elements.Count==2) { if(dict.Elements.Contains(new Ast.KeyValueNode(new Ast.StringNode("__class__"), new Ast.StringNode("float")))) { if(dict.Elements.Contains(new Ast.KeyValueNode(new Ast.StringNode("value"), new Ast.StringNode("nan")))) { return new Ast.DoubleNode(Double.NaN); } } } return dict; } public Ast.INode ParseSingle(SeekableStringReader sr) { // single = int | float | complex | string | bool | none . sr.SkipWhitespace(); switch(sr.Peek()) { case 'N': return ParseNone(sr); case 'T': case 'F': return ParseBool(sr); case '\'': case '"': return ParseString(sr); } // int or float or complex. int bookmark = sr.Bookmark(); try { return ParseComplex(sr); } catch (ParseException) { sr.FlipBack(bookmark); try { return ParseFloat(sr); } catch (ParseException) { sr.FlipBack(bookmark); return ParseInt(sr); } } } const string IntegerChars = "-0123456789"; const string FloatChars = "-+.eE0123456789"; Ast.INode ParseInt(SeekableStringReader sr) { // int = ['-'] digitnonzero {digit} . string numberstr = sr.ReadWhile(IntegerChars); if(numberstr.Length==0) throw new ParseException("invalid int character"); try { try { return new Ast.IntegerNode(int.Parse(numberstr)); } catch (OverflowException) { // try long try { return new Ast.LongNode(long.Parse(numberstr)); } catch (OverflowException) { // try decimal, but it can still overflow because it's not arbitrary precision try { return new Ast.DecimalNode(decimal.Parse(numberstr)); } catch (OverflowException) { throw new ParseException("number too large"); } } } } catch (FormatException x) { throw new ParseException("invalid integer format", x); } } Ast.PrimitiveNode ParseFloat(SeekableStringReader sr) { string numberstr = sr.ReadWhile(FloatChars); if(numberstr.Length==0) throw new ParseException("invalid float character"); // little bit of a hack: // if the number doesn't contain a decimal point and no 'e'/'E', it is an integer instead. // in that case, we need to reject it as a float. if(numberstr.IndexOfAny(new char[] {'.','e','E'}) < 0) throw new ParseException("number is not a float (might be an integer though)"); try { return new Ast.DoubleNode(this.ParseDouble(numberstr)); } catch (FormatException x) { throw new ParseException("invalid float format", x); } } Ast.ComplexNumberNode ParseComplex(SeekableStringReader sr) { //complex = complextuple | imaginary . //imaginary = ['+' | '-' ] ( float | int ) 'j' . //complextuple = '(' ( float | int ) imaginary ')' . if(sr.Peek()=='(') { // complextuple sr.Read(); // ( string numberstr; if(sr.Peek()=='-' || sr.Peek()=='+') { // starts with a sign, read that first otherwise the readuntil will return immediately numberstr = sr.Read(1) + sr.ReadUntil(new char[] {'+', '-'}); } else { numberstr = sr.ReadUntil(new char[] {'+', '-'}); } sr.Rewind(1); // rewind the +/- // because we're a bit more cautious here with reading chars than in the float parser, // it can be that the parser now stopped directly after the 'e' in a number like "3.14e+20". // ("3.14e20" is fine) So, check if the last char is 'e' and if so, continue reading 0..9. if(numberstr.EndsWith("e", StringComparison.InvariantCultureIgnoreCase)) { // if the next symbol is + or -, accept it, then read the exponent integer if(sr.Peek()=='-' || sr.Peek()=='+') numberstr+=sr.Read(1); numberstr += sr.ReadWhile("0123456789"); } sr.SkipWhitespace(); double realpart; try { realpart = this.ParseDouble(numberstr); } catch (FormatException x) { throw new ParseException("invalid float format", x); } double imaginarypart = ParseImaginaryPart(sr); if(sr.Read()!=')') throw new ParseException("expected ) to end a complex number"); return new Ast.ComplexNumberNode() { Real = realpart, Imaginary = imaginarypart }; } else { // imaginary double imag = ParseImaginaryPart(sr); return new Ast.ComplexNumberNode() { Real=0, Imaginary=imag }; } } double ParseImaginaryPart(SeekableStringReader sr) { //imaginary = ['+' | '-' ] ( float | int ) 'j' . // string numberstr = sr.ReadUntil('j'); // try { // return this.ParseDouble(numberstr); // } catch(FormatException x) { // throw new ParseException("invalid float format", x); // } if(!sr.HasMore()) throw new ParseException("unexpected end of input string"); char sign_or_digit = sr.Peek(); if(sign_or_digit=='+') sr.Read(); // skip the '+' // now an int or float follows. double double_value; int bookmark = sr.Bookmark(); try { var float_part = ParseFloat(sr); double_value = float_part.Value; } catch (ParseException) { sr.FlipBack(bookmark); var integer_part = ParseInt(sr); var integerNode = integer_part as Ast.IntegerNode; if (integerNode != null) double_value = integerNode.Value; else { var longNode = integer_part as Ast.LongNode; if (longNode != null) double_value = longNode.Value; else { var decimalNode = integer_part as Ast.DecimalNode; if (decimalNode != null) double_value = Convert.ToDouble(decimalNode.Value); else throw new ParseException("not an integer for the imaginary part"); } } } // now a 'j' must follow! sr.SkipWhitespace(); try { char j_char = sr.Read(); if(j_char!='j') throw new ParseException("not an imaginary part"); } catch (IndexOutOfRangeException) { throw new ParseException("not an imaginary part"); } return double_value; } Ast.PrimitiveNode ParseString(SeekableStringReader sr) { char quotechar = sr.Read(); // ' or " StringBuilder sb = new StringBuilder(10); while(sr.HasMore()) { char c = sr.Read(); if(c=='\\') { // backslash unescape c = sr.Read(); switch(c) { case '\\': sb.Append('\\'); break; case '\'': sb.Append('\''); break; case '"': sb.Append('"'); break; case 'a': sb.Append('\a'); break; case 'b': sb.Append('\b'); break; case 'f': sb.Append('\f'); break; case 'n': sb.Append('\n'); break; case 'r': sb.Append('\r'); break; case 't': sb.Append('\t'); break; case 'v': sb.Append('\v'); break; case 'x': // "\x00" sb.Append((char)int.Parse(sr.Read(2), NumberStyles.HexNumber)); break; case 'u': // "\u0000" sb.Append((char)int.Parse(sr.Read(4), NumberStyles.HexNumber)); break; default: sb.Append(c); break; } } else if(c==quotechar) { // end of string return new Ast.StringNode(sb.ToString()); } else { sb.Append(c); } } throw new ParseException("unclosed string"); } Ast.PrimitiveNode ParseBool(SeekableStringReader sr) { // True,False string b = sr.ReadUntil('e'); if(b=="Tru") return new Ast.BooleanNode(true); if(b=="Fals") return new Ast.BooleanNode(false); throw new ParseException("expected bool, True or False"); } Ast.NoneNode ParseNone(SeekableStringReader sr) { // None string n = sr.ReadUntil('e'); if(n=="Non") return Ast.NoneNode.Instance; throw new ParseException("expected None"); } double ParseDouble(string numberstr) { // the number is possibly +Inf/-Inf, these are encoded as "1e30000" and "-1e30000" if(numberstr=="1e30000") return double.PositiveInfinity; if(numberstr=="-1e30000") return double.NegativeInfinity; return double.Parse(numberstr, CultureInfo.InvariantCulture); } /// /// Utility function to convert obj back to actual bytes if it is a serpent-encoded bytes dictionary /// (a IDictionary with base-64 encoded 'data' in it and 'encoding'='base64'). /// If obj is already a byte array, return obj unmodified. /// If it is something else, throw an ArgumentException /// public static byte[] ToBytes(object obj) { #if !(SILVERLIGHT || WINDOWS_PHONE || PORTABLE) Hashtable hashtable = obj as Hashtable; if(hashtable!=null) { string data = null; string encoding = null; if(hashtable.Contains("data")) data = (string)hashtable["data"]; if(hashtable.Contains("encoding")) encoding = (string)hashtable["encoding"]; if(data==null || "base64"!=encoding) { throw new ArgumentException("argument is neither bytearray nor serpent base64 encoded bytes dict"); } return Convert.FromBase64String(data); } #endif IDictionary dict = obj as IDictionary; if(dict!=null) { string data; string encoding; bool hasData = dict.TryGetValue("data", out data); bool hasEncoding = dict.TryGetValue("encoding", out encoding); if(!hasData || !hasEncoding || encoding!="base64") { throw new ArgumentException("argument is neither bytearray nor serpent base64 encoded bytes dict"); } return Convert.FromBase64String(data); } IDictionary dict2 = obj as IDictionary; if(dict2!=null) { object dataobj; object encodingobj; bool hasData = dict2.TryGetValue("data", out dataobj); bool hasEncoding = dict2.TryGetValue("encoding", out encodingobj); string data = (string)dataobj; string encoding = (string)encodingobj; if(!hasData || !hasEncoding || encoding!="base64") { throw new ArgumentException("argument is neither bytearray nor serpent base64 encoded bytes dict"); } return Convert.FromBase64String(data); } byte[] bytearray = obj as byte[]; if(bytearray!=null) { return bytearray; } throw new ArgumentException("argument is neither bytearray nor serpent base64 encoded bytes dict"); } } } Serpent-serpent-1.23/dotnet/Serpent/Properties/000077500000000000000000000000001312652634400216145ustar00rootroot00000000000000Serpent-serpent-1.23/dotnet/Serpent/Properties/AssemblyInfo.cs000066400000000000000000000026061312652634400245420ustar00rootroot00000000000000#region Using directives using System; using System.Reflection; using System.Runtime.InteropServices; #endregion // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("Razorvine.Serpent")] [assembly: AssemblyDescription(@"Serpent Python literal expression serialization. Serpent provides Python ast.literal_eval() compatible object tree serialization. It serializes an object tree into bytes that can be transferred to Python and decoded there (using ast.literal_eval()). It can ofcourse also deserialize such a Python expression itself, back into the equivalent .NET datatypes. More info for the Python version is on Pypi: https://pypi.python.org/pypi/serpent ")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Irmen de Jong")] [assembly: AssemblyProduct("Serpent Python literal expression serialization")] [assembly: AssemblyCopyright("Copyright Irmen de Jong")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // The assembly version has following format : // // Major.Minor.Build.Revision // // You can specify all the values or you can use the default the Revision and // Build Numbers by using the '*' as shown below: [assembly: AssemblyVersion("1.18.0.*")] [assembly: AssemblyFileVersion("1.18.0.0")] Serpent-serpent-1.23/dotnet/Serpent/SeekableStringReader.cs000066400000000000000000000112621312652634400240360ustar00rootroot00000000000000/// /// Serpent, a Python literal expression serializer/deserializer /// (a.k.a. Python's ast.literal_eval in .NET) /// /// Copyright Irmen de Jong (irmen@razorvine.net) /// Software license: "MIT software license". See http://opensource.org/licenses/MIT /// using System; using System.Collections.Generic; namespace Razorvine.Serpent { /// /// A special string reader that is suitable for the parser to read through /// the expression string. You can rewind it, set bookmarks to flip back to, etc. /// public class SeekableStringReader : IDisposable { private string str; private int cursor = 0; private int bookmark = -1; public SeekableStringReader(string str) { if(str==null) throw new ArgumentNullException("str"); this.str = str; } /// /// Make a nested reader with its own cursor and bookmark. /// The cursor starts at the same position as the parent. /// /// public SeekableStringReader(SeekableStringReader parent) { str = parent.str; cursor = parent.cursor; } /// /// Is there more to read? /// public bool HasMore() { return cursor /// What is the next character? /// public char Peek() { return str[cursor]; } /// /// What are the next characters that will be read? /// public string Peek(int count) { return str.Substring(cursor, Math.Min(count, str.Length-cursor)); } /// /// Read a single character. /// public char Read() { return str[cursor++]; } /// /// Read a number of characters. /// public string Read(int count) { if(count<0) throw new ParseException("use Rewind to seek back"); int safecount = Math.Min(count, str.Length-cursor); if(safecount==0 && count>0) throw new ParseException("no more data"); string result = str.Substring(cursor, safecount); cursor += safecount; return result; } /// /// Read everything until one of the sentinel(s), which must exist in the string. /// Sentinel char is read but not returned in the result. /// public string ReadUntil(params char[] sentinels) { int index = str.IndexOfAny(sentinels, cursor); if(index>=0) { string result = str.Substring(cursor, index-cursor); cursor = index+1; return result; } throw new ParseException("terminator not found"); } /// /// Read everything as long as the char occurs in the accepted characters. /// public string ReadWhile(string accepted) { int start = cursor; while(cursor < str.Length) { if(accepted.IndexOf(str[cursor])>=0) ++cursor; else break; } return str.Substring(start, cursor-start); } /// /// Read away any whitespace. /// If a comment follows ('# bla bla') read away that as well /// public void SkipWhitespace() { while(HasMore()) { char c=Read(); if(c=='#') { ReadUntil('\n'); return; } if(!Char.IsWhiteSpace(c)) { Rewind(1); return; } } } /// /// Returns the rest of the data until the end. /// public string Rest() { if(cursor>=str.Length) throw new ParseException("no more data"); string result=str.Substring(cursor); cursor = str.Length; return result; } /// /// Rewind a number of characters. /// public void Rewind(int count) { cursor = Math.Max(0, cursor-count); } /// /// Return a bookmark to rewind to later. /// public int Bookmark() { return cursor; } /// /// Flip back to previously set bookmark. /// public void FlipBack(int bookmark) { cursor = bookmark; } /// /// Sync the position and bookmark with the current position in another reader. /// public void Sync(SeekableStringReader inner) { bookmark = inner.bookmark; cursor = inner.cursor; } /// /// Extract a piece of context around the current cursor (if you set cursor to -1) /// or around a given position in the string (if you set cursor>=0). /// public void Context(int crsr, int width, out string left, out string right) { if(crsr<0) crsr=this.cursor; int leftStrt = Math.Max(0, crsr-width); int leftLen = crsr-leftStrt; int rightLen = Math.Min(width, str.Length-crsr); left = str.Substring(leftStrt, leftLen); right = str.Substring(crsr, rightLen); } public void Dispose() { this.str = null; } } } Serpent-serpent-1.23/dotnet/Serpent/Serializer.cs000066400000000000000000000351541312652634400221300ustar00rootroot00000000000000/// /// Serpent, a Python literal expression serializer/deserializer /// (a.k.a. Python's ast.literal_eval in .NET) /// /// Copyright Irmen de Jong (irmen@razorvine.net) /// Software license: "MIT software license". See http://opensource.org/licenses/MIT /// using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.Serialization; using System.Text; using System.Xml; namespace Razorvine.Serpent { /// /// Serialize an object tree to a byte stream. /// It is not thread-safe: make sure you're not making changes to the object tree that is being serialized. /// public class Serializer { /// /// indent output? /// public bool Indent; /// /// use set literals? /// public bool SetLiterals; /// /// include namespace prefix for classes that are serialized to dict? /// public bool NamespaceInClassName; /// /// The maximum nesting level of the object graphs that you want to serialize. /// This limit has been set to avoid troublesome stack overflow errors. /// (If it is reached, an IllegalArgumentException is thrown instead with a clear message) /// public int MaximumLevel = 500; // avoids stackoverflow errors private static IDictionary> classToDictRegistry = new Dictionary>(); /// /// Initialize the serializer. /// /// indent the output over multiple lines (default=false) /// use set-literals or not (set to False if you need compatibility with Python < 3.2) /// include namespace prefix for class names or only use the class name itself public Serializer(bool indent=false, bool setLiterals=true, bool namespaceInClassName=false) { this.Indent = indent; this.SetLiterals = setLiterals; this.NamespaceInClassName = namespaceInClassName; } /// /// Register a custom class-to-dict converter. /// public static void RegisterClass(Type clazz, Func converter) { classToDictRegistry[clazz] = converter; } /// /// Serialize the object tree to bytes. /// public byte[] Serialize(object obj) { using(StringWriter tw = new StringWriter()) { if(this.SetLiterals) tw.Write("# serpent utf-8 python3.2\n"); //set-literals require python 3.2+ to deserialize (ast.literal_eval limitation) else tw.Write("# serpent utf-8 python2.6\n"); Serialize(obj, tw, 0); tw.Flush(); return Encoding.UTF8.GetBytes(tw.ToString()); } } protected void Serialize(object obj, TextWriter tw, int level) { if(level>MaximumLevel) throw new ArgumentException("Object graph nesting too deep. Increase serializer.MaximumLevel if you think you need more."); // null -> None // hashtables/dictionaries -> dict // hashset -> set // array -> tuple // byte arrays --> base64 // any other icollection --> list // date/timespan/uuid/exception -> custom mapping // random class --> public properties to dict // primitive types --> simple mapping Type t = obj==null? null : obj.GetType(); if(obj==null) { tw.Write("None"); } else if(obj is string) { Serialize_string((string)obj, tw, level); } else if(t.IsPrimitive) { Serialize_primitive(obj, tw, level); } else if(obj is decimal) { Serialize_decimal((decimal)obj, tw, level); } else if(obj is Enum) { Serialize_string(obj.ToString(), tw, level); } else if(obj is IDictionary) { Serialize_dict((IDictionary)obj, tw, level); } else if(t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(HashSet<>))) { IEnumerable x = (IEnumerable) obj; List list = new List(); foreach(object elt in x) list.Add(elt); object[] setvalues = list.ToArray(); Serialize_set(setvalues, tw, level); } else if(obj is byte[]) { Serialize_bytes((byte[])obj, tw, level); } else if(obj is Array) { Serialize_tuple((ICollection) obj, tw, level); } else if(obj is ICollection) { Serialize_list((ICollection) obj, tw, level); } else if(obj is DateTimeOffset) { Serialize_datetimeoffset((DateTimeOffset)obj, tw, level); } else if(obj is DateTime) { Serialize_datetime((DateTime)obj, tw, level); } else if(obj is TimeSpan) { Serialize_timespan((TimeSpan)obj, tw, level); } else if(obj is Exception) { Serialize_exception((Exception)obj, tw, level); } else if(obj is Guid) { Serialize_guid((Guid) obj, tw, level); } else if(obj is ComplexNumber) { Serialize_complex((ComplexNumber) obj, tw, level); } else { Serialize_class(obj, tw, level); } } protected void Serialize_tuple(ICollection array, TextWriter tw, int level) { tw.Write("("); Serialize_sequence_elements(array, array.Count==1, tw, level+1); if(this.Indent && array.Count>0) tw.Write(string.Join(" ", new string[level+1])); tw.Write(")"); } protected void Serialize_list(ICollection list, TextWriter tw, int level) { tw.Write("["); Serialize_sequence_elements(list, false, tw, level+1); if(this.Indent && list.Count>0) tw.Write(string.Join(" ", new string[level+1])); tw.Write("]"); } protected int DictentryCompare(DictionaryEntry d1, DictionaryEntry d2) { IComparable c1 = d1.Key as IComparable; IComparable c2 = d2.Key as IComparable; if(c1==null) return 0; return c1.CompareTo(c2); } protected void Serialize_dict(IDictionary dict, TextWriter tw, int level) { if(dict.Count==0) { tw.Write("{}"); return; } int counter=0; if(this.Indent) { string innerindent = string.Join(" ", new string[level+2]); tw.Write("{\n"); DictionaryEntry[] entries = new DictionaryEntry[dict.Count]; dict.CopyTo(entries, 0); try { Array.Sort(entries, DictentryCompare); } catch (InvalidOperationException) { // ignore sorting of incomparable elements } catch (ArgumentException) { // ignore sorting of incomparable elements } foreach(DictionaryEntry x in entries) { tw.Write(innerindent); Serialize(x.Key, tw, level+1); tw.Write(": "); Serialize(x.Value, tw, level+1); counter++; if(counter0) { tw.Write("{"); if(this.Indent) { try { Array.Sort(set); } catch (InvalidOperationException) { // ignore sorting of incomparable elements. } catch (ArgumentException) { // ignore sorting of incomparable elements. } } Serialize_sequence_elements(set, false, tw, level+1); if(this.Indent) tw.Write(string.Join(" ", new string[level+1])); tw.Write("}"); } else { // empty set literal doesn't exist, replace with empty tuple Serialize_tuple(new object[0], tw, level+1); } } protected void Serialize_sequence_elements(ICollection elements, bool trailingComma, TextWriter tw, int level) { if(elements.Count==0) return; int count=0; if(this.Indent) { tw.Write("\n"); string innerindent = string.Join(" ", new string[level+1]); foreach(object e in elements) { tw.Write(innerindent); Serialize(e, tw, level); count++; if(count() { {"data", str}, {"encoding", "base64"} }; Serialize_dict(dict, tw, level); } // the repr translation table for characters 0x00-0xff private static readonly string[] repr_255; static Serializer() { repr_255=new String[256]; for(int c=0; c<32; ++c) { repr_255[c] = "\\x"+c.ToString("x2"); } for(int c=0x20; c<0x7f; ++c) { repr_255[c] = Convert.ToString((char)c); } for(int c=0x7f; c<=0xa0; ++c) { repr_255[c] = "\\x"+c.ToString("x2"); } for(int c=0xa1; c<=0xff; ++c) { repr_255[c] = Convert.ToString((char)c); } // odd ones out: repr_255['\t'] = "\\t"; repr_255['\n'] = "\\n"; repr_255['\r'] = "\\r"; repr_255['\\'] = "\\\\"; repr_255[0xad] = "\\xad"; } protected void Serialize_string(string str, TextWriter tw, int level) { // create a 'repr' string representation following the same escaping rules as python 3.x repr() does. StringBuilder b=new StringBuilder(str.Length*2); bool containsSingleQuote=false; bool containsQuote=false; foreach(char c in str) { containsSingleQuote |= c=='\''; containsQuote |= c=='"'; if(c<256) { // characters 0..255 via quick lookup table b.Append(repr_255[c]); } else { if(Char.IsLetterOrDigit(c) || Char.IsNumber(c) || Char.IsPunctuation(c) || Char.IsSymbol(c)) { b.Append(c); } else { b.Append("\\u"); b.Append(((int)c).ToString("x4")); } } } if(!containsSingleQuote) { b.Insert(0, '\''); b.Append('\''); tw.Write(b.ToString()); } else if (!containsQuote) { b.Insert(0, '"'); b.Append('"'); tw.Write(b.ToString()); } else { String str2 = b.ToString(); str2 = str2.Replace("'", "\\'"); tw.Write("'"); tw.Write(str2); tw.Write("'"); } } protected void Serialize_datetime(DateTime dt, TextWriter tw, int level) { string s = dt.Millisecond == 0 ? XmlConvert.ToString(dt, "yyyy-MM-ddTHH:mm:ss") : XmlConvert.ToString(dt, "yyyy-MM-ddTHH:mm:ss.fff"); Serialize_string(s, tw, level); } protected void Serialize_datetimeoffset(DateTimeOffset dto, TextWriter tw, int level) { string s = XmlConvert.ToString(dto); Serialize_string(s, tw, level); } protected void Serialize_timespan(TimeSpan span, TextWriter tw, int level) { Serialize_primitive(span.TotalSeconds, tw, level); } protected void Serialize_exception(Exception exc, TextWriter tw, int level) { IDictionary dict; Func converter = null; classToDictRegistry.TryGetValue(exc.GetType(), out converter); if(converter!=null) { // build a custom property dict from the object. dict = converter(exc); } else { string className; if(this.NamespaceInClassName) className = exc.GetType().FullName; else className = exc.GetType().Name; dict = new Dictionary { {"__class__", className}, {"__exception__", true}, {"args", new string[]{exc.Message} }, {"attributes", exc.Data} }; } Serialize_dict(dict, tw, level); } protected void Serialize_guid(Guid guid, TextWriter tw, int level) { // simple string representation of the guid Serialize_string(guid.ToString(), tw, level); } protected void Serialize_decimal(decimal dec, TextWriter tw, int level) { Serialize_string(dec.ToString(CultureInfo.InvariantCulture), tw, level); } protected void Serialize_primitive(object obj, TextWriter tw, int level) { if(obj is float) { float f = (float)obj; double d = (double)f; Serialize_primitive(d, tw, level); } else if(obj is double) { double d = (double) obj; if(double.IsPositiveInfinity(d)) { // output a literal expression that overflows the float and results in +/-INF tw.Write("1e30000"); } else if(double.IsNegativeInfinity(d)) { tw.Write("-1e30000"); } else if(double.IsNaN(d)) { // there's no literal expression for a float NaN... tw.Write("{'__class__':'float','value':'nan'}"); } else { tw.Write(Convert.ToString(obj, CultureInfo.InvariantCulture)); } } else { tw.Write(Convert.ToString(obj, CultureInfo.InvariantCulture)); } } protected void Serialize_complex(ComplexNumber cplx, TextWriter tw, int level) { tw.Write("("); Serialize_primitive(cplx.Real, tw, level); if(cplx.Imaginary>=0) tw.Write("+"); Serialize_primitive(cplx.Imaginary, tw, level); tw.Write("j)"); } protected void Serialize_class(object obj, TextWriter tw, int level) { Type obj_type = obj.GetType(); IDictionary dict; Func converter = GetCustomConverter(obj_type); if(converter!=null) { // build a custom property dict from the object. dict = converter(obj); } else { bool isAnonymousClass = obj_type.Name.StartsWith("<>"); dict = new Dictionary(); if(!isAnonymousClass) { // only provide the class name when it is not an anonymous class if(this.NamespaceInClassName) dict["__class__"] = obj_type.FullName; else dict["__class__"] = obj_type.Name; } PropertyInfo[] properties=obj_type.GetProperties(); foreach(var propinfo in properties) { if(propinfo.CanRead) { string name=propinfo.Name; try { dict[name]=propinfo.GetValue(obj, null); } catch (Exception x) { throw new SerializationException("cannot serialize a property:",x); } } } } Serialize_dict(dict, tw, level); } protected Func GetCustomConverter(Type obj_type) { Func converter; if(classToDictRegistry.TryGetValue(obj_type, out converter)) return converter; // exact match // check if there's a custom converter registered for an interface or abstract base class // that this object implements or inherits from. foreach(var x in classToDictRegistry) { if(x.Key.IsAssignableFrom(obj_type)) { return x.Value; } } return null; } } } Serpent-serpent-1.23/dotnet/Serpent/Serpent.csproj000066400000000000000000000062441312652634400223300ustar00rootroot00000000000000 {5A8E1B96-8C48-4A35-A3C4-0844486B2D47} Debug x86 Library Razorvine.Serpent Razorvine.Serpent Properties False False 4 false C:\Users\Irmen\AppData\Roaming\ICSharpCode/SharpDevelop4\Settings.SourceAnalysis 8.0.30703 2.0 v4.5 x86 bin\Debug\ true Full False True DEBUG;TRACE bin\Release\ false PdbOnly True False TRACE AnyCPU False Auto 4194304 4096 none none Serpent-serpent-1.23/dotnet/Serpent/Serpent.nuspec000066400000000000000000000027561312652634400223310ustar00rootroot00000000000000 Razorvine.Serpent 1.18.0.0 Razorvine.Serpent Irmen de Jong Irmen de Jong http://opensource.org/licenses/MIT https://github.com/irmen/Serpent false Serpent Python literal expression serialization. Serpent provides Python ast.literal_eval() compatible object tree serialization. It serializes an object tree into bytes that can be transferred to Python and decoded there (using ast.literal_eval()). It can ofcourse also deserialize such a Python expression itself, back into the equivalent .NET datatypes. More info for the Python version is on Pypi: https://pypi.python.org/pypi/serpent Serpent Python literal expression serialization CRITICAL FIX: rewrote serialization and parsing of strings containing chars above 255, this didn't work. Now mimics python's repr(str) form as closely as possible. Added Serializer.MaximumLevel to avoid too deep recursion resulting in stack overflow errors. Serializer.MaximumLevel decreased to 500. Copyright 2017 serialization python pyro Serpent-serpent-1.23/dotnet/Serpent/Version.cs000066400000000000000000000006201312652634400214320ustar00rootroot00000000000000/// /// Serpent, a Python literal expression serializer/deserializer /// (a.k.a. Python's ast.literal_eval in .NET) /// /// Copyright: Irmen de Jong (irmen@razorvine.net) /// Software license: "MIT software license". See http://opensource.org/licenses/MIT /// namespace Razorvine.Serpent { public static class LibraryVersion { public static string Version = "1.18"; } } Serpent-serpent-1.23/dotnet/build-dotnet-microsoft.cmd000066400000000000000000000004271312652634400231250ustar00rootroot00000000000000echo "Compiling .net source (with microsoft windows sdk msbuild)" msbuild /verbosity:minimal /p:Platform="Any CPU" /p:Configuration="Release" Serpent.sln /t:Rebuild if not exist build mkdir build copy Serpent\bin\Release\*.dll build\ copy Serpent\bin\Release\*.pdb build\ Serpent-serpent-1.23/dotnet/build-dotnet-mono.sh000077500000000000000000000004551312652634400217430ustar00rootroot00000000000000#!/bin/sh echo "Compiling .net source" if [ -d Serpent/bin ]; then rm -r Serpent/bin fi if [ -d Serpent.Test/bin ]; then rm -r Serpent.Test/bin fi xbuild /verbosity:minimal /property:Configuration=Release /property:Platform="Any CPU" Serpent.sln mkdir -p build cp Serpent/bin/Release/*.dll build Serpent-serpent-1.23/dotnet/lib/000077500000000000000000000000001312652634400166065ustar00rootroot00000000000000Serpent-serpent-1.23/dotnet/lib/nunit.framework.dll000077500000000000000000004400001312652634400224350ustar00rootroot00000000000000MZ@ !L!This program cannot be run in DOS mode. $PELaP!  " @@ @!O@`  H.text  `.rsrc@ @@.reloc `0@B!H) P a!~ JuYI^N2H8,y/!5 97QCxM$7.Ij'?w6VPE5i08^KޘJZ( o }*0R( ( o }{rpo  - {{o  Yo }*0 { +*( *( *F( }*0 { +**( *v( }o } *F( } *0 { +*j}{o } *0 {  +*&} *0 {  +*&} *0 {  +*&} *0 {  +*&} *0 {  +*&} *V( rp}*F( }*0 { +*V( rp}*F( }*0 { +**( *F( }*0 { +*&}*0 { +*&}*0 { +*&}**(*.(**(*.(*s }( {o *s }( {=o *s }( {>o *Vs }( *0Rs }( ( o rpo  -o  Yo {o *0 { +*Rrpr+p()*RrprGp()*RrprYp()*B=(-*( *0(4 }*0 (4 }*0$(4 }*F(4}*0o  8b{ -8Bo o rop( ,o rp( +-{8o o -8u?-{8 @( .B( .C( +- u= +e( -u>-u;- u=+ +0)( .D( +- u; -{( (! X {i:{+*z(8}}*(8}}}}*(8}}}}*0 ( {E,+{o +6{{{o +{{{o +o" } +{o# X o" -(9 + *6(?*0H (8Y[X }  +{%X =X  -*0K (8Y[jX ԍ}  +{%X HX j -*0a (8#@@[ Y[X#?Xi }  +{%X > X -*0Y (8"zD[ Y[X"?Xi }  +{%X I X -*B=(-*F( }*0 { +*Brp()*Brp()*( *( *( *( * * *0 +*( *0 {  +*&} *( *0,(  - }!+}!*0(  }!*0 (  }!*0$(  }!*0 {! +*0 {" +*B}"}#*0 {" +*0 {# +*0){-,{-J ,o$ + +*0 {- +*&}-*0 {$ +*j}${$o }%*0 {% +*B}%}$*0 {& +*&}&*0 {' +*&}'*0 {( +*&}(*0 {) +*&})*0 {* +*&}**0 {* +*&}**0 {+ +*&}+*0 {, +*&},*0 {, +*},{,,{,~% (& +}**F( }.*b( }/}.*0 {. +*0 {/ +*0 {0 +*&}0*.(*0V( ,+}2 +'u,+ -X {2i -*0 {1 +*&}1*0 {5 +*&}5*0){5,{5J ,o$ + +*0 {2 +*0 {3 +*&}3*0 {4 +*}4{4,{4~% (& +}3*0 {6 +*&}6*( *( *( *B=(-*(,(.rpo *(,(.rpo *BK(-*K(-(.rpo *F( }7*b( }8}7*0 {7 +*0 {8 +*r~9}:(' }<*~9}:(' }<}=*~9}:(' }<}=}>*&}?*0{; :( o o( };{;rpo -{;r po + -{;{;o Yo };{;rpo  - {;{;o  Yo };{; +*&};**oA*0oo +*0qo +*>{:oI*0#o {?, r)p() + +*0{{< E4+rKp(() +JrWp({=((* ++rkp({=({>((+ +*0E -rp +/u;-rp+rp (   (, +*0  oos +*0  oos +*0 ,o+ss +*0:{?  -s osos2 +*0 ( +*0:{?  -s osos2 +*0"{?, {?o +s +*0#{?, {?o +s +*0{?, {?o + +*.s9*0 rp +*(' *0F(}@( {@o -rp{@() rps- z*0G}:uM  -rp() rops- z{@o. i +*nr}poF{@oG*0( - o}A*0F(}B( {Bo -rp{B() rps- z*0}:uM  -rp() rops- z{Bo. t i -rp{B() rops- z}C{A{Co +*0[rp{Bo (/ oF{A -0{Aug - rpoF{Ao*>{CoI*0rp{B{A(* +*b(}D}E*0M}:-{D+ - +),{D+ - +{Do0 +*>{Eo1 *Brp(*VKr-p(*VKr7p(*0;}:u>, >(2 -uI, I(3 ++ +*:rCpo1 *(*j(}F}G*2(*0?}:{Fo-+{Go-+}H{H +*{ForKpoE{Go*0C {H YE+{Fo+{Go+ (+*2(*0*}:{Fo-{Go+ +*{ForSpoE{Go**(*.(*0c!u  - o"  +Do4 + o5  -o6 -uP -o7  +*%@06"}:u  -rYprops- zo +*V(h}M(*Z(h}M(*0#{Moj +*0#{Mom(o8 & +*0#{Mom(+o8 & +*0#{Mom(+o8 & +*0#{Mom(o8 & +*0#{Mom(+o8 & +*0$( {Moo +*0%{Ms +*0 ( +*:rpo1 *(*0&s9 o4 +go5 o: +o5 (- go6 -uP-o7 o; &o6 -uP-o7  + *$.Rx:rpo1 *(*z(}Nrp(*0X'o4 +o5 {N( - +o6 -uP -o7  +* -6nrpoF{NoG*z(}Orp(*0m({Ou,u+ -%to" {Oto"  - +%{O( o, o+ +*nr/poF{OoG*z(}PrKp(*0{P(o +*nr]poF{PoG*(}Q(rqp(*0)}T +*0$)(}Qo o }R +*0$)(+}Qo o }R +*0$)(+}Qo o }R +*0)}S +*05*  o4 8o5  -roprp(< (/ s= z{S-Ho {So> o? -roprp(< (/ s= z-M{Q o{T, +-[{T- +-= X o6 :uP  - o7 +*A0T{S -rpo1 +r poF{SoG{T - r9poH*0+rOps@ {S -rap{S(/ oA &{T - ripoA &{R -rp{R(/ oA &rpoA &o +*0^,s9 }U(' }Vo4 +o5 {Uo; &o6 -uP  - o7 *""D0-{UoB +*0$( {Voo +*0M. +0{UoC ( -{UoD  +X {UoB  - +*0O'o4 +o5 ( - +o6 -uP -o7  +* $-0 /s  +*0 /s  +*0 /sE +*0 /sF +*(' *F( }W*0-{WoG +*>(b( *F( }H *0{ ( o o -rpo (/ sI z ( o o -rpo (/ sI z{H   oJ +*F( }K *0{ ( o o -rpo (/ sI z ( o o -rpo (/ sI z{K   oL +*Z(}Z(*^(}Z(*00(}Z +*00(+}Z +*00(+}Z +*(' s!}[s&}\*0${]u/-{]u\+ +*0M{]o{]uS -(o({[o$}]*0;{]uS -({\o)}]o*0g1{[o#o {[o#o{[o#o -({[o% o({[o$*0B+{[o%{\o{[o"-{[o#o+ -*0U2( - rpsM z+{[o% {\o{[o" -{\o* +*VsN }^(' *0{^oO  +*03{^oP +*>{^oQ *03{^oR +*rsS }_(' }`*0{_oT  +*0{_oU +*r{_oV {`o*04{_oW o +*V(' s}a*F(' }a*0{ao o +*0{aotQ +*05{ao{as +*0{ao +**(+*.(,*0s(. +*0s(. +*0s(. +*0s(. +*0s(. +*0s(. +*05s(/ +*05r!p(9 +*05r/p(9 +*05r;p(9 +*05rKp(9 +*05s(/ +*05( (> +*0s(. +*0 (0 +*0sX (0 +*06s(0t5 +*07s(0t6 +*08s(0t7 +*09=s0(0tr +*0:=s6(0tt +*0;s(0t8 +*0<s(0tf +*0=s(0t@ +*0>s(0t +*0?s(0t +*0@s(0tg +*0As(0t +*09s0(0tr +*0Bs3(0ts +*0Bs3(0ts +*0:s6(0tt +*0Cs9(0tu +*0Cs9(0tu +*0Ds(0t +*0 D( s(0t +*0Es (0t +*0 E( s (0t +*0Es (0t +*0 E( s (0t +*0Fs(0t +*0 F( s(0t +*0Gs(0t +*0 G( s(0t +*0Hs(0tB +*0Is(0tC +*0)s(0tD +*0Js(0tA +*0Js(0tA +*0Ks(0tc +*0Ls(0t +*0Ls(0t +*0Ms(0t +*0Ms(0t +*0Ns(0t +*0Ns(0t +*0Os(0t +*0Os(0t +*0Ps(0t +*0Qs(0t +*0Rs(0t +*0SsZ (0t +*0 ( +*0 ( +*0 ( +*0 ( +*0 ( +*0 ( +*0 5( +*0 5( +*0 5( +*0 5( +*0 5( +*0 5( +*05( (| +*0 6s +*0 7s +*0 8s +*09=s0 +*0:=s6 +*0 ;s +*0 <s +*0 =s +*0 >s +*0 ?s +*0 @s +*0 As +*0 9s0 +*0 Bs3 +*0 Bs3 +*0 :s6 +*0 Cs9 +*0 Cs9 +*0 Ds +*0D( s +*0 Es  +*0E( s  +*0 Es  +*0E( s  +*0 Fs +*0F( s +*0 Gs +*0G( s +*0 Hs +*0 Is +*0 )s +*0 Js +*0 Js +*0 Ks +*0 Ls +*0 Ls +*0Ls1(3of +*0 Ms +*0 Ms +*0Ms1(3og +*0 Ns +*0 Ns +*0Ns1(3oi +*0 Os +*0 Os +*0Os1(3ok +*0 Ps +*0 Qs +*0 Rs +*0 SsZ +*(' *0 {b +*&}b*0 {c +*&}c*0 -{d +*0 -{e +*(' *Vo*oo)*(*0-(% }e}d*0 s +*f(}d }e*0 s +*(*0 s +*(*0 s +*(*F(}f*0{fs +*b(}d}e*0 +*(*0 {g +*0!-(}g% }e}d*0N(,(u`+ -{gso)+{go*so)*0!-(}h% }e}d*0N(,(u`+ -{hso)+{ho*so)*f(}dd}e*0C(,(u`+ -so)+o*so)*04o* o* oo)*0$-(uV-(+ ( X +*0$-(uV-(+ ( X +*(*0-(% }e}d*0 s +*0-(% }e}d*0 s +*0T{j :{:u; -0{it;s {k -o }j+9{is {l -{Mom{lo8 &}j{j +*&}j*f}l(}i*0K}k +*0}:(o +*>(o*0K(}l +*0K(+}l +*0K(+}l +*0K(}l +*0K(+}l +*6(*04( -riprps- z}m}n*0.{m +5{nY {n([ }:{Ao - +B{n1 {n+ - -([ }:{Ao +*0.{m +I{nY {n([ o}:{A{:o - U&{n1 {n+ - -([ o}:{A{:o +*, L 0.{m +S{nY {n([ q}:{Aqo - _&{n1 {n+ - -([ q}:{Aqo +*1%V {Aorp{m=() o1 *>{Ao*0!rp{m={A(* +*0_U}:u  -r5props- zo\ i}oo] i}p{o- {p+ +*:rpo1 *0OV{:u  - (+,oIrp{o={p=o^ *Z}o}p(*0EW{:u; -s +%{:u -s +s +*04}: -rprops- z(o +*>(o*(*(}r}ss{}t(}q*0@{toj +*0@}s +*0@{tol +*0-X{ro - rpsM zs}r +*0@{ro}r +*0@{ro}r +*0@{ro}r +*0@{ro}r +*0@{ro}r +*0@{ro}r +*0@{ro}r +*0@{ro}r +*0 @( +*0@{tom(o8 & +*0@{tom(+o8 & +*0@{tom(+o8 & +*0@{tom(o8 & +*0@{tom(+o8 & +*0%}:{t{q|roo +*^{q{:( *0{qoG{r, {ro+ -Nr poE{rooG{ro -r p{roo_ {toi - r poH*0u;,u;+ -t;t;( 8u,u+ -tt(8u,u+ -tt(+Zu,u+ -tt( +'{r -{roC+ oB*0Y{toi(R o o  --~u o ==o?+9~v o =o ==o?{toi{soD*0Zo` o`  -G{tono# H ~w o` HHo?+0~x o` Ho` Ho?*0[({tono"  :{tono# t} ({, {+ -{{X%( +I{ -r6 po1 {oK+rP po1 {oK*0(L u, u+ -!rj pto" =() (/ (L u, u+ -!rj pto" =() (/ (  -~y  o@+~z   o@*0\u u -+oa -+oa  ,+-6+!ob ob -X/+-{(O-'~{  (N o@+?{(O~|  (N (N o@*0]u ,oa + -(Ooc +su -to# +To4 +o5 Y% - -o6 -uP -o7  +*V+0[({tono"  -Z{tono# t} ({, {+ -{{X%( *0Qr pur pvr pwr pxrU pyr pzr p{r p|*0Eu;,u;+ - +#u-u+ - + +*0 ^s +*0 ^s +*0 ^sd +*0 ^se +*0 ^sf +*(' *F(}}*0{}oG  +*F(}~*0{~og +*05 ( o o , ( o o + +*0` ( o o -rpo (/ sI z ( o o -rpo (/ sI z*(*F(h }i *0&(j {i   ok +*F(h }l *0)(j {l   oJ  +*F(h }m *0)(j {m   oL  +*0_pp}}{d {d {Y_{f_`} {Y _{ f_`}{{Y(n +*0`qq}}{?d {?d !{Y_{f_`}!{Y _{ f_`}{{Y(o +*0ap}{ +*0bq}{ +*0cp}{ +*0dq}{ +**(' *J(}*nrw poF{oG*0C}:{, + - r psI z{Z{o +*J(}*nr poF{oG*0F}:{, + - r psI z{Z{o +*J(}*nr poF{oG*0C}:{, + - r psI z{Z{o +*J(}*nr# poF{oG*0F}:{, + - r psI z{Z{o +*>( (r *2o@*0eu -rKpo () 8ss o  + ot X ou -o oA &[ov &+.- ,ov &ob ow &Xoa -]ov &+ rO poA & Y% -rKpo () +*0ff:Rss  8)ox \0JEBSa}o\.+8 ; ( YE8rU poA &8r[ poA &8ra poA &+rrg poA &+drm poA &+Vrs poA &+Hry poA &+:r poA &+,r poA &+r p=oy &+ ov &+X o :o  + *0_gss [ov & +- - ,ov &=(< oA &X i -]ov &o +*0Rhu ,oa + =  + ob  ][ Y% -+*0i ss  -~o Y ~oA &o Y -,~o Y o oA &~oA &+# -oz oA &+oA &o +*0^ Po Po ({  -+>~o Y Y  - [Y({ P(PQP(PQ*0}jo o (| -+o( -+o(  +! ox  ox - +- X -o o -++*.r p*(' *0(V-(W+ +*09 -*u> - +uI - + +*0 :uB - 8uC - 8u - +}u= - +iu[ - +UuH - +Au\ - +-u@ - +u] - + +*0bu>-u>+ -(} (} (Y 8,uI-uI+ -(~ (~ (Z 8Po - r psM zu-u+ -( ( P([ 8u\-u\+ -( ( P(\ +xuH-uH+ -( ( P(] +Gu[-u[+ -( ( P(^ +( ( P(_ +*0@k(2 , (2 + - 8( -(2 - (2 + -( 8Po,~#+ -~>sQPo E/w8( 8Y( Po(}  +n# - ( +PY[( Po(} #Y@[ +&Po( (* +rprWps- z*09l(3 , (3 + - 8( -(3 - (3 + -( 8Po,~#+ -~>sQPo E0p8( 8Y( lPo(}  +f" - ( +LY[( Po(~ "B[ +&Po( () +rprWps- z*0mo ET8( 8o( s ( -( ( ( +t( +is ( - ( +I( ( l( l[( o(} #Y@[ +rprWps- z*0noES8( 8o( j-4Y+Y +r( +fj- ( +L( ( Y vlvl[( o(} #Y@[+rprWps- z*0oo EI8( 8o( j-Y(o  +_( +Tj- ( +;Yll[( o(} #Y@[ +rprWps- z*0poER8( 8o( -4Y+Y +q( +e- ( +L( ( Y vlvl[( o(} #Y@[+rprWps- z*0qo EH8( 8o( -Y(n  +^( +S- ( +;Yll[( o(} #Y@[ +rprWps- z*0;r(U,(U+ - rapsI z(V- (V+ -(} (} ( 8u-u+ -( ( ( 8u\-u\+ -( ( ( +~uH-uH+ -( ( ( +Ku[-u[+ -( ( ( +( ( ( +**(' *0 ssd +*0At- ,+ 8$- 8(U, (U+- (` 8u$-t$o 8u$-t$o e 8o o rp o -o = +Orp o -o =e + rpsI z *(' *0  - ,+ 8  - 8 (U, (U+ -  (` +b u - to +9 u$ - t$ o + rpsI z*(' *0 us{ +*0 { +*&}*0 { +*&}*0 v{ +*0 v{ +*0ws9 }- +-8, +-8( -8o o (s -o8you ,ou ,{+-tt(t8@u ,u +-t t (u8 u,u,u;, u;++-tt(x8u;,u;+-t;t;(w8u,u+-tt(z8Vu,u+-tt(y8 (U, (U+-(X8P,PouD+:PoD u),u)+-'))( ( ( 8uD,uD+-$DD( ( ( +B(p- (r+&(p- (r+ o0 +*0Cx(q  +  o - X i- +*0&o ,o a( o + +*0o  +*0To ~-|s +~(+~-}s +~(+ +*0Iya(   o o r!po   o K +*0]z{o: +o5 th o - +o6 -uP -o7  +*-;0O{oa oa  - +4 +ob ob  - +X  -(x +*0|o" o"  - 8o s o o,o+ - +bo o4 +%o5 o o (o - -o6 -uP -o7  +*Z40}o4 o4  8o6 o6  -+-8 3o5 o5 (o+-ds} } - o5 }}- o5 }{o + X 8H*01~{-+o( {-+o( o +*0}o4 o4  8o6 o6  -+-8 3o5 o5 (o+-ds} } - o5 }}- o5 }{o + X 8H*0]o o 3)o o ( -o o ( + - +o so o +*0  -8o   -r/pr]ps- zo   -r/props- zo   -ropr]ps- zo   -roprops- zo` o`   -8~B ~B s s o o o jo & o jo &j+s~o & ~o &+6  -{jXHo SX~  -~jXo`   :zo o +*. *Ns9 }(' *(' *~ \}(}*0} +*0} +*0?}:u;}{ - +{{o +*00rp({{-rp+rp(+ +*0g o -<~ +! ox -  X  i-~o$ s  +i oC t;  ,K rp( - rp( - +- oD +) oD -  Y% oD + X + oB -oB YoC t;rp( -oB YoD ~   ( ;( o t( -~   ( (/ +*0(  +*0b{o o  - +Eo (  - ++Yox ~ .1ox ~ ++ +*0{o o  - +a -{(  +Co {(  - +$Yox ~ .ox ~ + +*0J \/*.(*0(({( +*nrpoF{oG*.(*08 -rprops- z(({( +*nrSpoF{oG*.(*03~( ( {(-{(+ +*nripoF{oG*F(} *0O}:u  -$rp ( o (/ rops- z{  o +*0HrpoF{ o o rpo -{ o o +rpo1 *.(*0}:{Ao +*nrpoF{Ao*>{Ao*^(r'p(*0}}:u -rYprops- zto4 + o5 {Ao - + o6 - uP -o7  +*2)[nr/poF{Ao*^(rCp(*0}:u -rYprops- zto4 + o5 {Ao - + o6 - uP -o7  +*2,^nrMpoF{Ao*^(rap(*0}:u -rYprops- zto4 + o5 {Ao - + o6 - uP -o7  +*2,^nrkpoF{Ao*z(r{p(}*0}:u -rYprops- z to4 +o5 {Ao -X o6 -uP -o7 { +*5.c0e-{ E+rkpoF+1rpoF+#rp|(< rp( oF+{Ao*J(}*0i}: - rops zu }{ - o }{{ 4o  +*frp{(/ o1 *>{oI*0rp{() +*J(}*0}: - rops zu   -o { 4o  -rp{() r9ps- zo? }{A{o +*0Vrp{(/ oF{A -0{Aug - rpoF{Ao*>{oI*0rCp{{A(* +*  (} } s (+}Z*0}:{ ,{ , + -r pr]ps- z{Z{ o0{Z{ o+ +*rip{ { o^ **(1*.(2*0s(. +*0s(. +*0{ao  +*0 ( +*0 ( +*0 ( +*0oos +*0 ( +*0 ( +*0 ( +*0oos +*0 ,o+ss +*Z(' o}*0 s +*0{o +*0 { +*J(}*0}:{( +*nrpoF{oG*0X}: -s zs {o jo &{o  & *0Oj:rpo1 *zrKp{:o o o_ *0 rp +*Ns }(*0o}: -s zs o s }{o jo &{o  & & *A`kAfT:rpo1 *zrKp{:o o o_ *0 rp +*(*J(}*0} +*0.}:u; - +t;~% ( +*:rpo1 *(*Z(r7p(*0I}: - +0u; -rOprops- zt;~% ( +*:rpo1 *.(*0f}:u; - +K{ -$t;o( {o( o  +t;{o  +*04rpoF{oG{ - r poH*.(*0Z}:u; - +?{ -t;o( {o( o +t;{o +*0CrpoF{o=(Y(PoG{ - r poH*.(*0Z}:u; - +?{ -t;o( {o( o +t;{o +*04rpoF{oG{ - r poH*.(*05}:u;,t;{{-+( + +*04r)poF{oG{ - r poH*.(*0 { +*0u  - rIpo o () rops- z}o" }{ - +{A,{A{o+ +*: E (' *:{o&*0's }s! o +*0-{A -rpoF+ {Ao*0I{ -rpo1 +){A -{Ao+ {oI*0${A -rp + ( +*0Pu  -rprops- z}o" }{ +** 5 (' *:{o&*0's }s! o +*frUp( o1 *rp{o o {o o1 *(*0=s +*0=s +*2(*b(' }}*0 { +*0C{ -{,~+~sM z{(U - ~sM z*0 { +*0({s +*0({s +*0'({(} ( Ds +*0'({(} ( Ds +*0'({(} ( Ds +*0'({(} ( Ds +*0'({(} ( Ds +*0'({( ( Ds +*0{ +*~rprprsp*J(}*~{:, {:o +oI*^(rp(*0!}:,o {+ +*>{oG*.(*0?{:u  ( -!rpo o o o1 *^(rp(*0}:,{o + +*nrpoF{oG*.(*0$}:,o {o + +*nrpoF{oG*.(*0$}:,{o o + +*nrpoF{oG*.( *2( *2( *.( *2( *2( *.( *2( *2( *.( *2( *2( **(' *0~  +*:~X*2r+psz2rpsz0[(2 - ( + ->>((9+%>>(>o(9*08 - ~% +, i+ - ( sz*(+*:~% (+*08 - ~% +, i+ - ( sz*(.*:~% (.*08 - ~% +, i+ - ( sz*(1*:~% (1*08 - ~% +, i+ - ( sz*(4*:~% (4*2(9*2(9*07o ('o -s oo sz*Fo(<*Fo(<*07o ('o -s oo sz*Fo( +*Fo( +*07o ('o + -s oo sz*VK((9*VK((9*VK((9**(7*2(9*2(9*2(9*0& o"  (9 +*  0(G +*0~% (G +*0s (G +*0s (G +*0s ~% (G +*0( (J +*0( + +*0~% ( + +*0 ( s (G +*0 ( s (H +*0 ( s (I +*0s (G +*0s (H +*0s (I +*0"( s (G +*0!( s (H +*0 ( s (I +*Bs(9*Bs(9*Rs~% (9*VK((9*VK((9*VK((9*VK((9*VK((9*VK((9*VK((9*VK((9*VK((9*VK((9*VK((9*VK((9*V(oC(9*V(oC(9*V(oC(9*V(oC(9*V(oC(9*V(oC(9*B((9*B((9*B((9*B((9*B((9*B((9*V>((9*V>((9*V>((9*V((9*V((9*V((9*Bs(9*Bs(9*Bs(9*Bs(9*Bs(9*Bs(9*V(oI(9*V(oI(9*V(oI(9*V(oI(9*V(oI(9*V(oI(9*Bs(9*Bs(9*Bs(9*Vss(9*Vss(9*Vss(9*F((9*F((9*F((9*j( ((9*j( ((9*j( ((9*Z(o[(9*Z(o[(9*Z(o[(9*~(( o[(9*~(( o[(9*~(( o[(9*F((9*F((9*F((9*F((9*F((9*F((9*j( ((9*j( ((9*j( ((9*Z(oW(9*Z(oW(9*Z(oW(9*Z(oW(9*Z(oW(9*Z(oW(9*~(( oW(9*~(( oW(9*~(( oW(9*n==((9*n==((9*n==((9*nHH((9*nHH((9*nHH((9*n[[((9*n[[((9*n[[((9*n\\((9*n\\((9*n\\((9*n((9*n((9*n((9*:(**6(**6(**V( l(**R( l(**R( l(**F((9*F((9*F((9*=(=oM(9*=(=oM(9*=(=oM(9*H(HoM(9*H(HoM(9*H(HoM(9*[([oM(9*[([oM(9*[([oM(9*\(\oM(9*\(\oM(9*\(\oM(9*(oM(9*(oM(9*(oM(9*I(IoM(9*I(IoM(9*I(IoM(9*>(>oM(9*>(>oM(9*>(>oM(9*Z(oM(9*Z(oM(9*Z(oM(9*F((9*F((9*F((9*Z(oN(9*Z(oN(9*Z(oN(9*n==((9*n==((9*n==((9*n[[((9*n[[((9*n[[((9*nHH((9*nHH((9*nHH((9*n\\((9*n\\((9*n\\((9*n((9*n((9*n((9*n>>((9*n>>((9*n>>((9*nII((9*nII((9*nII((9*F((9*F((9*F((9*n==((9*n==((9*n==((9*n[[((9*n[[((9*n[[((9*nHH((9*nHH((9*nHH((9*n\\((9*n\\((9*n\\((9*n((9*n((9*n((9*n>>((9*n>>((9*n>>((9*nII((9*nII((9*nII((9*F((9*F((9*F((9*n==((9*n==((9*n==((9*n[[((9*n[[((9*n[[((9*nHH((9*nHH((9*nHH((9*n\\((9*n\\((9*n\\((9*n((9*n((9*n((9*n>>((9*n>>((9*n>>((9*nII((9*nII((9*nII((9*F((9*F((9*F((9*n==((9*n==((9*n==((9*n[[((9*n[[((9*n[[((9*nHH((9*nHH((9*nHH((9*n\\((9*n\\((9*n\\((9*n((9*n((9*n((9*n>>((9*n>>((9*n>>((9*nII((9*nII((9*nII((9*F((9*F((9*F((9*Fs(9*Fs(9*Fs(9**2(9*2(9*6(9*Fo(<*Fo(<*6(<*Fo( +*Fo( +*6( +*VK((9*VK((9*VK((9**(7*0 s +*(*2r+psz2rpsz2(W*2(W*01o o -s oo sz*Fo(Z*Fo(Z*01o o -s oo sz*Fo( +*Fo( +*01o o + -s oo sz*VK((W*VK((W*VK((W**(U*(' *2r+psz2rpszB~% (g*2(g*Zs s(9*>~% (j*.(j*nsss(9*>~% (m*.(m*Bs(9*>s(7*F~% (s*Bs(8*6(s*Fs(9*bs((9*B~% (v*2(v*Fs(9*Rss(7*F~% (|*Vss(8*6(|*Zss(9*vs(s(9*B~% (*2(*Zss(9*B~% (*2(*Fs(9*B~% (*2(*Zss(9*B~% (*2(*Zss(9*B~% (*2(*Fs(9*Bs(9*.(*>~% (*Vss(9*.(*>~% (*Bs(9*.(*>~% (*Zs((9*2(*B~% (*(' *0 Ls +*0 Js +*(' *2r+psz2rpsz*(' *Fs(9*2(*B~% (*0s s (*2(*B~% (*Zss(9*2(*B~% (*0s s (*2(*B~% (*Bs(9*.(*>~% (*Bs (*.(*>~% (*Vss(9*.(*>~% (*0s (*.(*>~% (*0L -rpr9ps- z -rprops- zo o (*0L -rpr9ps- z -rprops- zo o (*0P -rpr9ps- z -rprops- zo o ~% (*Fs(9*2(*B~% (*0L -rpr9ps- z -rprops- zo o (*0L -rpr9ps- z -rprops- zo o (*0P -rpr9ps- z -rprops- zo o ~% (*Zss(9*2(*B~% (*2r+psz2rpsz*(' *Fs(9*2(*B~% (*0Do o ( -o7  -o7 **22(*B~% (*0D( ( ( -o7  -o7 **22(*B~% (*Zss(9*2(*B~% (*0Do o ( -o7  -o7 **22(*B~% (*0D( ( ( -o7  -o7 **22(*B~% (*>#*(' *0s1(3 +*0s1(5 +*0s1(6 +*0s1(7 +*0s1(8 +*05s1(9 +*05r!p( +*05r/p( +*05r;p( +*05rKp( +*05s1(> +*05( ( +*0 Js +*(' *0s1(3 +*0s1(5 +*0 6s +*0 7s +*0 8s +*09=s0 +*0:=s6 +*0 ;s +*0 <s +*0 =s +*0 >s +*0 ?s +*0 @s +*0 As +*0 9s0 +*0 Bs3 +*0 Bs3 +*0 :s6 +*0 Cs9 +*0 Cs9 +*0 Ds +*0D( s +*0 Es  +*0E( s  +*0 Es  +*0E( s  +*0 Fs +*0F( s +*0 Gs +*0G( s +*0 Hs +*0 Is +*0 )s +*0 Ls +*0 Ms +*0 Ns +*0 Os +*0 Ps +*0 Qs +*0 Rs +*0 SsZ +*(' *(*0 s +*(' *F(' }*0s9 {o4 +Go5 o 4o -rMp(* sI zo? o; &o6 -uP-o7  + *Xm0-~o +*06~o t  -~s% o  +*0o ( +*>(( *.( *0'>  + o X  - +*01Y >  +o ZXX - + *0)=  +o X  - +*Vs s *2r+psz2rpszFs(9*2(#*B~% (#*Zss(9*2(&*B~% (&*Fs(9*2()*B~% ()*Zss(9*2(,*B~% (,*Fs(9*2(/*B~% (/*Zss(9*2(2*B~% (2*Zs((9*2(5*B~% (5*ns(s(9*2(8*B~% (8*Fs(9*2(;*B~% (;*Zss(9*2(>*B~% (>*(' *0,('  - }+}*0('  }*0 ('  }*0$('  }*0 { +*0 { +*0 { +*0 { +*0 { +*0 { +*0 { +*0 { +*0 { +*0 { +*0K(Q~o  -(Q~s9 o (Q~o t +*0'{ - s }{ +*0}} +*0}o } +*0} +*0} +*0} +*0(Po8 & +*0(Qo  +*0(Q=o  +*0(Q>o  +*0} +*0}} +*0} +*0}} +*.rp*F(' }*0rp( t s` +*0-{ -{sf}{ +*0-{ -{sj}{ +*0{rpo t; +*0{rpo t; +*F(' }*0{rpo u; +*0{r+po u; +*0{rGpo u  +*F(' }*0{rgpo  +*0B(k E + +  + + +*(' (o(q(s(u(w*0 { +*"}*0 { +*"}*0 { +*"}*0 { +*"}*0 { +*"}*0s1(5 +*0 Ls +*0Ls1(3of +*0 Ms +*0Ms1(3og +*0 Ns +*0Ns1(3oi +*0 Os +*0Os1(3ok +*(' *V~}(<*06~}(<,~% (& + - (?*0 -{ +*&}*0N -D+ rpo1 %Y -, i+ - ( o *J((*J((*N((*0o=~YY  - (Q(M(M(R~o1 oG - r poHo ( -(*>~o_ *>~o_ *>~o_ **oJ**oJ*0X -~o1 8:o ou  -t(8u; -t;(8u -t oK8uJ -J(8u> ->(8uI -I(+lu -(+Mu) -)(+.o o  -~o_ + ~o_ *0  o4 +K o5 %X -4X% -+-.rp+rpo1 oJ o6 - uP-o7 -~o1 +%- r po1 rpo1 * [h0No -~o1 8(oa =   +  ob Z% Y% -o  8 o5 - rpo1  +/-  ]+- rpo1 X -oJX +/-  ]+- rpo1 X - o6 :< uP  - o7 *d/0/~% (  -~o1 + ~o_ *R~Jo_ *0p(2 - ( + - o +Lrp( ( .o  -rp(/ o1 +rp(/ o1 *0p(3 - ( + - o  +Lrp( (  .o  -rp(/ o1 +rp(/ o1 *rp( (  rp(/ o1 *j~( (  o1 *v~o1 oo *.(*0l~o1 oG,o+ -?r poEooGo -r poo_ o *v~o1 oo *v~o1 oIo *rp-~XYXs  o *0Nrprp~o r prpr#prpr/prprprMprYprprKp*05s1s(/ +*05(o= +*0Du( ( +*0DL( ( +*0DT( ( +*0 s +*0D(oU +*0D( ( +*0E(oW +*0E( ( +*(' *BSJB v2.0.50727l0#~:#Strings#US@ #GUIDP #BlobW 3u  "4Q        ' 3 A K      8'2 ? Q   +  U    " r" ;$ $z$a$%e%%e%i& ,' ( _( (*e, - / #00I0d0}0000001=1|1\11\11 1 1 &2292 G ]2 d2 j2 2 2 2 222 2 3  3 3 *3 3- H3|3 3 3 3 344E- 04 B4'z4 4444445 5  5 5)6\1{6 6 ;7 J7 7 7 7 8 !8.8 Q8e%h8 8 8829 H9 m9W9 9W99 9 99-0@0S0g0|0 00000(%0('0)04/*040<041P042a03x0H50L: >0L>04C0D04 F04 G0 H0 I(0 J70 KI0 LU0 Oi0 Sw0!V0!`0.01070707 0470471047F047^07s9 9 @@ABDFF&F6FDFUHc IpM}MMNNNOP8QT Ud WvW X X YZ[ [  ^! _&+ a+D@b1Y bqk b~LfPfPfXfXfXfXfPgLgpgph'pi6LiEiQi\iomoqq }v}~ #%'( )>0L0\,0r,3,6,9< L U b e0g  hc ' 6 I [ u         ,$ DC  V g      @ @ @ @- > V  d  n } l p  l  l l  0)  0)  0) + 0)< 0 U 0 !c 0!p 0 %w 0HD 0 S 0 c 0  0  0  0  0  0 0  0 0  0  0- 0 ! 0 !" 0 B/ 0 `; fG jU 0 ma 0 xk 0 xv 0 x{ 0 0 8 8 R s 4V7V 7V7V7O  7   G G ` h G s %0464:4>C4VHVLVUa s xO  7s  G  ! !O s x  4! !OOU\ !h4!o!tyXOOs 4VVVDDN   NO5S:_MbyXyX444 O!B4!V4f4l4!tx~1 1 1 1 1 1 1 1 DN8>4BGNS1  !!)!17!466"4"""#1#+# 8#  #4 #O ####!K$UK$Y  $j$j4VxV$xV|xVwx$x$1$ 1$ 1$ O4VV%V%%4(H(Q(i(4VC 1( xO  s s Q7) QB) K)sT)Z)K)sK)su** * * *4V&V+V+VZV+V"+V*+V0+4V&1V+1V:+1VA+11H+46\+ 6i+ 6t+41+ 1+ 1+ 1+ 1+ 1+ 1+ 1+ 1+ 1+ 1, ,4.. _# _(H!e,`!_(h!_(p!_#!,!_(!_S!_#!Y!+S "A,$"[#0"u,H"#T",l"#x"^ "c ", "# "_( "_# "N, #_( #_# (#N, @#_( K#_# `#p,x#|##,###N,###_(#_##_(#_##_w$_}E$_n$_($_$$_(%_(&%_(;%_L%_(T%_|%_%_%_"%#'_$'_%'_((+(_,(_. )_1x)_4)_7M*_:^*_#;p*o,<*_#<*_#=*_(>*_(>*_(>*_(>>? @*@*A* B*_(B*,B+#B+_(C C C C YC A,C ,C ,C C C ,C+_CP+_Dx+_E+_G+ J+ J,J,K0, KH,K,,K, #K, YL,+SL, A,M,[#M-u,N-#N(-^O@-cOL- ,Pd-#Pp- ,Q--#Q-:R-E#R- S-P#S- T-\#T.N,U.#U$. ,V<.i#Vg._#Wy._-X.,Z.YZ.,Z. #Z._([._[X/,\p/#\|/,]/ #]/^/^/:^0E#^0,_,0i#_X09`p0?`z0_(a0_(a0_(a0_a0_(b0_(b0_(b0_Lb 1_#c21_-dL1,fd1YfGRf|1_(f1_f1_g1]i1,j2#j2dkkl2pm2wndo3dp$3,qT3,q3 q04rX4%t42v4@w4Hw 5QwT5Xw5Xx5^Rz5A3z5,z5_(z6_Sz\6k{6d|6_}6_~L7k7dK8d\8,8_8k8d 9_(9_(09_(H9k9d9_(9_9_9k:dD:d:_:k:d:_( ;_;;k ;_(;_<(<,T<,<,<,<,#=e0,=p6L= d=ds=_(|= T>dc>_(k>_> ?d?_H^_(>^>^_(@_@$_ RB_ FB__C_$D`kD5`dEH`,*Fh`,2G`,?H`,LI`,TJ`_gK`_oM8akPapQbwR\cdScdTc,UckU3ddVDddWd_(Xd RX ekXLedY\e_(Zde_Ze}[e }[e}[e&[,f-}\Pf6}\tfB}\fK}\fU}\fa}\gm}\(g~}\Lg\hg,]g,^g,_g,`h,aDhkbuhdchdd4ie_F?k@dAdB _FC$kDdEܚ_FFkGdH_FIěkJ`dK|_LkNHdO_#P̝kQAdR[dSl,T_TkVDdWdX,Y_ Yk[d\Ƞ_(]Ӡ_]]@^Q^ ^R^@^\`x+b#7d%fԡ%+h%j #7l02CnY_op$Lp,qGRq_qԢkrdsktdudv,wӣ_(wkwdxdy,zǤ_(zϤ_#z^{k{:d|I_(}Q_(}hk}d~̥_#إkLd_#kdO_#\kħd_#kTd_F$nktpdd<,lkp#d=di_(t}%}__ث %)%(@?%X6-BܬKUDaxm~I%A3_S.dN_Shkd_Sd_SkCd__Slkd_Sįkd_#_)_6_#B_O_\_#h_u__#___!(9'E_(%Ӱ%%t%%d&&&ı & & &$hs&Ȳ&Ӳ&&&& ?& Q& d&& &3 &> ̳&J &X %&` ;&f Q&k \"&i"&v"&  u ȴ   $ D h   е(& (& $(& !L(& "l(& &(& )(& +ܶ(& . (& 08.& 1I.& 4Z.& 6oHX 7H` :Hf <;&X =Ƿ;&` @ݷ;&f BMX C M` FMf H5B&X IKB&` LaB&f NwJ& OJ& RJ& TR& UϸR& XR& ZC [ C ^C `.\& a?\& dP\& fac&# gwc&+ jc&1 lc&6 mc&C pϹc&N rX s` vxf y)o |:v ~Kt&X at&` wt&t&f t&o t&v Ϻ&X &` &&X &` .&D&| V& h& z& & & ˻&| & & & 0& P& p&| & & &| & ʼ& ܼ& & & -&| D& [& r&| & & & ׽& &  3 O k    ۾   / K g   ʿ ؿ' . = #J '&V *8_ .J1\' 3}' 7' :' <' @' C"' EC' Id' L' N' R' U' W ' [*' ^K'f `l'o d'v g'| i' m' p'V r('_ v?'yV'V {h'_ z''V '_ '$' $'  $' %$' A$' ]$' y$' $' $' $' $' $' !$' =$' Y$' u$'| $' $' $'f $'o $'v $' /$' A$' S8' o8' 8' 8' 8' 8' 8' 8' 38' O8' k8' 8' 8' 8' 8' 8'| 8' /8' K8'f g8'o 8'v 8' 8' 8' =' =' ! =' $)=' &E=' *a=' -}=' /=' 3=' 6=' 8=' < =' ?%=' AA=' E]=' Hy='| J=' N=' Q='f S='o W='v Z!=' \3=' `E=' cWL' esL' iL' lL' nL' rL' uL' wL' {7L' ~SL' oL' L' L' L' L' L'| L' 3L' OL'f kL'o L'v L' L' L'    A3`' $`' 1`' ?`' Q`' c`' q`' `' `'& `'4 `'< `'#`'B g'L _(%t%*&7&D& & & && &3 &>  &J I&X _&` u&f &k _(%t%k'T k'\ k'e 'v 'o  'f %'v 5'o A'f Rp bx t  " & *'p /' 1' 4'p 8'x :' =+' @9' DP' Hn'p M' O' R V X [ _ a d'p h' j&' m='p qN' s[' vmf z~o }v t&f t&o t&v 'f 'o 'v ' ' ' ._(8' P' h_(p%t}%_(     `  ' #' 0' D' j' w'`  & - X ` t& t&& t&- t&X 2t&` >t&P' ' ' \' n' {'` ( ( <( ( ( (` %t%_(3 > G $O \ g   )` :'3 Q'> ^'G !p'O #'\ ''g *' ,h' 0u'` 3_(5A35q 5q 5q 5q 550"w 6P+~ 7l~ 76~ 7B~ 7 7 8 8_(98( 9$q :@q :\a :tj :s :} : : : : :, :D :\ :t ; < = >  ?@(A/ B4/CX6Cp6"DADA"EP)EP1F _8F$_@GHlGG`yOHxWI I]JdKkLrMyN O8 PQ_(RY_(Rdg'R|_(S_S"THu(Ud(U(V_(W_W( X(YX(\A3_%t_%a c  g ` j l p` s" u4 yA` |R ~i v`   `   ` ( ( (` !( =( J(` [S# mS# zS#`   ` _(__(_T_    Y A, , ,, D \ ,t(%$ ,L 4h(4)4)4);)C )K4SP4t*)S*)4A3_b)u)4p~),),_e,),$K_`))_ *#*,(*C3*L),c>*#lK*,T*#]*i*#q   ]]@dXd|kk_(_(_Y0k<}Ff#v## ,6`+,#7,<A,A,M,FX,Kn,Q|,F|, |,X ,F , ,A3,~ B~ ,`,`,`@ -fX/ x/66"_(\_(d8(ت_(8(sg-u-------GG`-----------ootot---06:06:0-####-##-##-##-0a-------x-otot---------------x------.-oty-.U.U...#BBVU..U..sU.U.U..U.-&.U1.-<.>.@.1..U.U.U.U.U.@.U.@.UUUU-otB.I.P.B.Y.`.#c.y`.y#y`.`.#y##t.t.##t.t.t.##--#######-U.BBVU.U..U..U.$U...U|..U.U|..U|..U..U.1...U|.<.>.<.>.<.>.<.>.<.>.<.>.<.>.<.>.<.>...----.U.U.U.U-.-..-#UUtU...#.U.1..:...1.....U..U /...UtUtUtUtUtUtUtUtU<.>.<.>.<.>.t--<.>.t////<.>.<.>.t<.>.t<.>.t<.>.<.>.t<.>.<.>.5g6U+#8#!/&/,/&/,/&/,/+#8#.+#8#.+#8#.#U.U..2/U.2/U.2/U.2/U.U..U..##U.y#@.@.U.U..U..U.U.U.U.U.t.U.U...U...$$$.U..U.U.U...A/G/L/..A/G/L/..A/G/L/..A/G/L/<.>.tB.I.P.B.Y.T/V/T/V/UX/.-.-..-..-..-.U^/U^/.U^/.-.i/.i/..i/.-U^/U^/.U^/.-n/.-n/.n/x/#U^/U^/.U^/.-^/x/.-^/x/.^/x/x/.-x/.x/x/.-x/.x/x/.-x/.x/x/.-x/.x/x/.-x/.x/x/.-x/.x/n/.-n/.n/n/.-n/.n/n/.-n/.n/n/.-n/.n/}/.-}/.}/}/.-}/.}/}/.-}/.}/}/.-}/.}//.-/.//.-/.//.-/./1..-1..1./.-/./1..-1..1./.-/.//.-/./U.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UUX/.-UX/.UX/UX/.-UX/.UX/U.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.Uot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otot.-ot.otU.-U.UU#U#.U#.-.i/.i/..i/.-U#U#.U^/.-n/.-n/.n/x/#H(T/V/T/V/U^/U^/.U^/.-.i/.i/..i/.-U^/U^/.U^/.-n/.-n/.n/x/#T/V/T/V/1.1..1..-1.1..1..-1.1..1..-UUU.U.U.-U.-UU.U.-UUU.U.U.-U.-UU.U.-1.U1.U.1.U.-1.U1.U.1.U.-////.//.-////.//.-1..-1..1.1..-1..1.1..-1..1.1..-1..1.//T/V/T/V/U.-U.UU.-U.UU.-U.UU.-U.U/.-/.//.-/.//.-/.//.-/.//U.-/U./U/U.-/U./U/U.-/U./U/U.-/U./UT/V/T/V/U.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.U/t.##UH(/-/06:06:0T/V/T/V/U.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.UU.-U.Ut.U.-t.U.t.Ut.U.-t.U.t.U--otot-Y.g-u-s/#/#/#GGL/L/L//I.//-----t.t.--..-#UUtU...#.U.1..:/.@.////#t#U. |#/yz |Q_(Y_#a_(i_#q_#y_#_#_#_#_#_#_#_#__(_ _(1,1Y9e,H+'Ma),_(i52y?2YQ2^,a&da|2j)2r12x23 3^_(23,:3":3-:34:3Aa_wia3u3%kq3#c&Ic&q3y3y33(52_(352,_wa3"3)_#{C2"4<_D_9La_#L\ dL_#l_(ll+4llt_(tt+4tt|__#_ I4O4X4q3q3=+Jg4p4\3s_V__%0 _(N %44__1__(a4Ya4{{#484='f 5t5t1'51051951C51L51T51]5e5%4Ie5I%4%_p5545Q25% 55A%%" 5'5'%95H5M5SA5X5]5\!5a5t)5%tI5!5!5!5a%d_(a6a6Ya69a69_!7_!7.a7Ha5Pi+7zi247%HY7h7I3y7),77!_7J!77!7717_Q, 88 $#?9(*Hu3^_#a3e##_(a_(I_(;8E8_(_S;8E8~8aS#:3Q6,q8=Q8,!8!8!8! 9!9!(9a&kq_(Q_#Q_Q_y_(__(?%_#)99Y91219y9Y_Y9Y9Y_(1_(i9q8#q8(a:+3q3A~8!q3FII_&;@EJp;t@xE(;,@0E;@EJ;@E;<Y@p;t@xE|J"',;@EJ.S".cI.kI.@.sZ.{c.+8.3I.Kg.#I.2.;M.C\C!c[#c!#C2c2#!C!c 3Z#D @c#Ccd @d @AS@aS@{@U3cS@S@)S@S@S@!S@AS@ +G@+i2G2i6 @D7 @@@#OS@OS@Y @Z @dZ @Z @\ @$] @D^ @^ @d` @` @b @c @c @d @e!e @df @$g @g @h @di @$j @j @k @dl @$m @m @n @do @$p @p @q @dr @Ds @`s@s@s@Dt @t@t@t@$u @vI vI$v @@vI`vIvIvIw @$x @yI yI$y @@yI`yIyIyIz @${ @$| @`|I|I|I} @ }I@}I`}I$~ @D @`III I@I`Id @ @`IIIĂ @ I@I`ID @`III @ I@I`IĆ @ @ @$ @`!D @`!d @ @ @ď @ @ @$ @!D @d @ @ @`!Ę @ @ @$ @D @d @ @ G@i @ġ @ @ @ !$ @D @d @ @ @Ī @ @ @S@S@S@ S@$ @@S@`S@S@S@S@S@ \@yD @`@d @ @ @ij @ @ @ķ @ @ @d @ļ @ @ @d @ @d @$ @$ @ @ @ @ @ @ @ @ @$ @ @D @ @ @ @ @ @$ @$ @ @ @d @D @d @ @ @D @d @ @ @ @ @ @ @ @$ @D @d @ @ @ @ @ @ @ @SW(-Y(<KS_hrx~0JR]ag #)/5;ACNUay*EPYkz->bn~Wcs+EQow*4=O $-,  !"#$.%1-8/:>>D?E@FAMBNCOEQGR\Sp]t`ucwfygzvy|~n00i00)05n?0Y0Y000Y000. 8?i0Q00Zbk0. 8z?(0i005n0Q0ZbY0k00i000(. k0F0i0v>\k %,4CHMS\eip %,4CHMS\eipn03kva3kvcjw| Fv""("(v#vd$sU%Z%_%|wX'     %  , 4   CHMS\eip( . 8?i0Q00Zbk0()%8)0)0n0)0))**)00*  A 4 -l3-lE-l_-r         !"#$.EN!Q#S%T%V'W)X+Y-Z/[1\3]5^7_9d;e=f=g?hAiCjEkElGmGnIoIpKqKrMsMtOuOvQwQxSySzU{U}W|W~YY[[]_aacceegikkmmooqsuuwy{}"#'(34567:;<=@CDEFGHIJKLaqrstuxyz{~ = > b hijlkmn!#%')+-/13579;=&?ACEGIKMOQSUWY[]_aceg ikFmGoHqIsJuKwLyM{N}OPQabcdeghiklnopqrstuvwx/tDpD mu}$ 08B/x1  V$0/;:GFHFIFJFNMOMihjhkhlhmhnhpoqo}|   J Y        ) +          # ' + / 3 _        1 3 5 { }         ! # + - / = ? A O Q S         # O S 240 aeA ~X #1)2)3)4)5)6)Onunit.framework.dllCategoryAttributeNUnit.FrameworkDatapointAttributeDatapointsAttributeDescriptionAttributeMessageMatchExpectedExceptionAttributeExplicitAttributeIgnoreAttributeIncludeExcludeAttributePlatformAttributeCultureAttributePropertyAttributeCombinatorialAttributePairwiseAttributeSequentialAttributeMaxTimeAttributeParameterDataAttributeValuesAttributeRandomAttributeSampleTypeRangeAttributeRepeatAttributeRequiredAddinAttributeSetCultureAttributeSetUICultureAttributeSetUpAttributeSetUpFixtureAttributeSuiteAttributeTearDownAttributeITestActionTestActionAttributeTestAttributeITestCaseDataTestCaseAttributeTestCaseSourceAttributeTestFixtureAttributeTestFixtureSetUpAttributeTestFixtureTearDownAttributeTheoryAttributeTimeoutAttributeRequiresSTAAttributeRequiresMTAAttributeRequiresThreadAttributeValueSourceAttributeIResolveConstraintNUnit.Framework.ConstraintsConstraintUnsetObjectAttributeExistsConstraintPrefixConstraintAttributeConstraintBasicConstraintNullConstraintTrueConstraintFalseConstraintNaNConstraintBinaryConstraintAndConstraintFailurePointOrConstraintCollectionConstraintCollectionItemsEqualConstraintEmptyCollectionConstraintUniqueItemsConstraintCollectionContainsConstraintCollectionEquivalentConstraintCollectionSubsetConstraintCollectionOrderedConstraintCollectionTallyComparisonAdapterComparerAdapterDefaultComparisonAdapterComparerAdapter`1ComparisonAdapterForComparison`1ComparisonConstraintActualValueDelegateConstraintBuilderOperatorStackConstraintStackConstraintExpressionBaseConstraintExpressionConstraintFactoryConstraintOperatorPrefixOperatorNotOperatorCollectionOperatorAllOperatorSomeOperatorNoneOperatorExactCountOperatorWithOperatorSelfResolvingOperatorPropOperatorAttributeOperatorThrowsOperatorBinaryOperatorAndOperatorOrOperatorContainsConstraintDelayedConstraintEmptyDirectoryContraintEmptyConstraintEqualConstraintEqualityAdapterEqualityComparerAdapterGenericEqualityAdapter`1EqualityComparerAdapter`1ComparisonAdapter`1FloatingPointNumericsFloatIntUnionDoubleLongUnionGreaterThanConstraintGreaterThanOrEqualConstraintLessThanConstraintLessThanOrEqualConstraintMessageWriterMsgUtilsNumericsNUnitComparerNUnitComparer`1INUnitEqualityComparerNUnitEqualityComparerPathConstraintSamePathConstraintSubPathConstraintSamePathOrUnderConstraintPredicateConstraint`1NotConstraintAllItemsConstraintSomeItemsConstraintNoItemConstraintExactCountConstraintPropertyExistsConstraintPropertyConstraintRangeConstraint`1ResolvableConstraintExpressionReusableConstraintSameAsConstraintBinarySerializableConstraintXmlSerializableConstraintStringConstraintEmptyStringConstraintNullOrEmptyStringConstraintSubstringConstraintStartsWithConstraintEndsWithConstraintRegexConstraintThrowsConstraintThrowsNothingConstraintToleranceModeToleranceTypeConstraintExactTypeConstraintExceptionTypeConstraintInstanceOfTypeConstraintAssignableFromConstraintAssignableToConstraintAssertionExceptionIgnoreExceptionInconclusiveExceptionSuccessExceptionINUnitEqualityComparer`1ActionTargetsTestDelegateAssertAssertionHelperAssumeCollectionAssertContainsDirectoryAssertFileAssertGlobalSettingsHasIExpectExceptionIsIzListListMapperRandomizerSpecialValueStringAssertTestCaseDataTestContextTestAdapterResultAdapterTestDetailsTestStateTestStatusTextTextMessageWriterThrowsmscorlibSystemAttributeEnumObjectTMulticastDelegateValueTypeSystem.IOStringWriterSystem.CollectionsIComparerSystem.Collections.GenericIComparer`1IComparable`1ExceptionRandomcategoryName.ctorget_NameNamedescriptionget_DescriptionDescriptionvalue__ExactRegexStartsWithTypeexpectedExceptionexpectedExceptionNameexpectedMessagematchTypeuserMessagehandlerget_ExpectedExceptionset_ExpectedExceptionget_ExpectedExceptionNameset_ExpectedExceptionNameget_ExpectedMessageset_ExpectedMessageget_UserMessageset_UserMessageget_MatchTypeset_MatchTypeget_Handlerset_HandlerExpectedExceptionExpectedExceptionNameExpectedMessageUserMessageMatchTypeHandlerreasonget_ReasonReasonincludeexcludeget_Includeset_Includeget_Excludeset_Excludeset_ReasonIncludeExcludeIDictionarypropertiesget_PropertiesPropertiesIEnumerableSystem.ReflectionParameterInfoGetDatadatasampleTypecountminmaxdmindmaxRawIntRangeDoubleRangerequiredAddinget_RequiredAddinRequiredAddinBeforeTestAfterTestget_TargetsTargetsset_Descriptionget_Argumentsget_Resultget_HasExpectedResultget_TestNameget_Ignoredget_Explicitget_IgnoreReasonArgumentsResultHasExpectedResultTestNameIgnoredExplicitIgnoreReasonargumentsexpectedResulthasExpectedResultexpectedExceptionTypetestNameisIgnoredisExplicitcategoryset_Resultget_ExpectedResultIListget_Categoriesget_Categoryset_Categoryset_TestNameget_Ignoreset_Ignoreset_Ignoredset_Explicitset_IgnoreReasonExpectedResultCategoriesCategoryIgnoresourceNamesourceTypeget_SourceNameget_SourceTypeSourceNameSourceTypeignoreReasontypeArgsget_TypeArgsset_TypeArgsTypeArgsSystem.ThreadingApartmentStateResolveUNSETactualdisplayNameargcntarg1arg2builderSetBuilderget_DisplayNameset_DisplayNameWriteMessageToMatchesWriteDescriptionToWriteActualValueToToStringGetStringRepresentation_displayableop_BitwiseAndop_BitwiseOrop_LogicalNotget_Andget_Withget_OrAfterNUnit.Framework.Constraints.IResolveConstraint.ResolveDisplayNameAndWithOrexpectedTypebaseConstraintattrFoundexpectedleftrightfailurePointNoneLeftRightIsEmptydoMatchcomparerget_IgnoreCaseUsingComparison`1IEqualityComparerIEqualityComparer`1ItemsEqualTallyIgnoreCasecomparerNamepropertyNamedescendingget_DescendingByDescendingArrayListlistget_CountTryRemoveCountget_DefaultForCompareDefaultcomparisonInvokeIAsyncResultAsyncCallbackBeginInvokeEndInvokeopsconstraintslastPushedget_IsResolvableAppendSetTopOperatorRightContextReduceOperatorStackIsResolvableStack`1stackget_Emptyget_TopPushPopEmptyTopget_Notget_Noget_Allget_Someget_NoneExactlyPropertyget_Lengthget_Messageget_InnerExceptionPredicate`1get_Nullget_Trueget_Falseget_Positiveget_Negativeget_NaNget_Uniqueget_BinarySerializableget_XmlSerializableEqualToSameAsGreaterThanGreaterThanOrEqualToAtLeastLessThanLessThanOrEqualToAtMostTypeOfInstanceOfInstanceOfTypeAssignableFromAssignableToEquivalentToSubsetOfget_OrderedMemberStringContainingContainsSubstringStringStartingEndsWithStringEndingStringMatchingSamePathSubPathSamePathOrUnderInRangeNotNoAllSomeLengthMessageInnerExceptionNullTrueFalsePositiveNegativeNaNUniqueBinarySerializableXmlSerializableOrderedDoesNotContainDoesNotStartWithDoesNotEndWithDoesNotMatchleftContextrightContextleft_precedenceright_precedenceget_LeftContextset_LeftContextget_RightContextset_RightContextget_LeftPrecedenceget_RightPrecedenceReduceLeftContextRightContextLeftPrecedenceRightPrecedenceApplyPrefixexpectedCountnametypeApplyOperatorrealConstraintignoreCaseadapterget_RealConstraintset_RealConstraintRealConstraintdelayInMillisecondspollingIntervalfilessubdirstoleranceclipStringsStringsDiffer_1StringsDiffer_2StreamsDiffer_1StreamsDiffer_2CollectionType_1CollectionType_2ValuesDiffer_1ValuesDiffer_2get_NoClipget_AsCollectionWithinget_Ulpsget_Percentget_Daysget_Hoursget_Minutesget_Secondsget_Millisecondsget_TicksComparerDisplayDifferencesDisplayStringDifferencesStreamDisplayStreamDifferencesICollectionDisplayCollectionDifferencesDisplayTypesAndSizesDisplayFailurePointGetValueFromCollectionDisplayEnumerableDifferencesNoClipAsCollectionUlpsPercentDaysHoursMinutesSecondsMillisecondsTicksAreEqualCanCompareThrowIfNotCompatibleAreAlmostEqualUlpsReinterpretAsIntReinterpretAsLongReinterpretAsFloatReinterpretAsDoubleFloatIntUIntDoubleLongULongget_MaxLineLengthset_MaxLineLengthWriteMessageLineWriteConnectorWritePredicateWriteExpectedValueWriteModifierWriteActualValueWriteValueWriteCollectionElementsMaxLineLengthELLIPSISGetTypeRepresentationEscapeControlCharsGetArrayIndicesAsStringGetArrayIndicesFromCollectionIndexClipStringClipExpectedAndActualFindMismatchPositionIsNumericTypeIsFloatingPointNumericIsFixedPointNumericDecimalcaseInsensitivecompareAsCollectionexternalComparersfailurePointsBUFFER_SIZEset_IgnoreCaseget_CompareAsCollectionset_CompareAsCollectionget_ExternalComparersget_FailurePointsFirstImplementsIEquatableOfSecondGetEquatableGenericArgumentsInvokeFirstIEquatableEqualsSecondGetExternalComparerArrayArraysEqualDictionariesEqualCollectionsEqualStringsEqualEnumerablesEqualDirectoryInfoDirectoriesEqualStreamsEqualCompareAsCollectionExternalComparersFailurePointsPositionExpectedValueActualValueExpectedHasDataActualHasDataDirectorySeparatorCharsexpectedPathactualPathget_RespectCaseIsMatchCanonicalizeIsSamePathIsSubPathIsSamePathOrUnderRespectCasepredicateactualTypepropValuefromtoOperatorAndImplementationOperatorOrImplementationconstraintop_ImplicitSystem.Runtime.Serialization.Formatters.BinaryBinaryFormatterserializerSystem.XmlSystem.Xml.SerializationXmlSerializercaughtExceptionget_ActualExceptionActualExceptionLinearmodeamountModeMustFollowToleranceMultipleToleranceModesNumericToleranceRequiredget_Zeroget_ModeCheckLinearAndNumericget_Valueget_IsEmptyZeroModeValueSystem.Runtime.SerializationSerializationInfoStreamingContextTestSuitecounterget_CounterIncrementAssertCountEqualsReferenceEqualsAssertDoublesAreEqualPassFailInconclusiveThatByValCatchDoesNotThrowIsTrueIsFalseNotNullIsNotNullIsNullIsNaNNullable`1IsNotEmptyIsNullOrEmptyIsNotNullOrEmptyIsAssignableFromIsNotAssignableFromIsInstanceOfIsInstanceOfTypeIsNotInstanceOfIsNotInstanceOfTypeAreNotEqualAreSameAreNotSameGreaterIComparableLessGreaterOrEqualLessOrEqualCounterExpectMapAllItemsAreInstancesOfTypeAllItemsAreNotNullAllItemsAreUniqueAreEquivalentAreNotEquivalentIsNotSubsetOfIsSubsetOfIsOrderedSubstringItemIsWithinIsNotWithinFileInfoDefaultFloatingPointToleranceHandleExceptionoriginalseedGeneratorHashtablerandomizersget_RandomSeedMemberInfoGetRandomizerGetDoublesGetIntsRandomSeedAreEqualIgnoringCaseAreNotEqualIgnoringCaseCATEGORIESReturnsSetNameSetDescriptionSetCategorySetPropertyMakeExplicitcontextKeystateKey_context_test_resultget_CurrentContextget_Testget_TestDirectoryget_WorkDirectoryCurrentContextTestDirectoryWorkDirectoryget_FullNameFullNameget_Stateget_StatusStateStatusMethodInfoget_Fixtureset_Fixtureget_Methodset_Methodset_FullNameget_Typeset_Typeget_IsSuiteset_IsSuitek__BackingFieldk__BackingFieldk__BackingFieldk__BackingFieldk__BackingFieldFixtureMethodIsSuiteNotRunnableSkippedSuccessFailureErrorCancelledPassedFailedDEFAULT_LINE_LENGTHPfx_ExpectedPfx_ActualPrefixLengthFmt_ConnectorFmt_PredicateFmt_ModifierFmt_NullFmt_EmptyStringFmt_EmptyCollectionFmt_StringFmt_CharFmt_DateTimeFmt_ValueTypeFmt_DefaultmaxLineLengthWriteArrayWriteStringWriteCharWriteDoubleWriteFloatWriteDecimalDateTimeWriteDateTimeWriteExpectedLineWriteActualLineWriteCaretLineget_Exceptionget_TargetInvocationExceptionget_ArgumentExceptionget_InvalidOperationExceptionget_NothingTargetInvocationExceptionArgumentExceptionInvalidOperationExceptionNothingexceptionTypeexceptionNamevalueplatformsculturespropertyValuemillisecondsparameterarg3argsParamArrayAttributestepculturetestDetailsargtimeoutapartmentwriterdeloresolvableenumerablecollectionxycobjectmethodcallbackresultoptargetPrecedencepatterndepthindentindexmaxUlpsmessagelevelmismatchclippingconnectormodifiervalstartobjsindicesmaxStringLengthclipStartmaxDisplayLengthistartfirstsecondpathpath1path2itemConstraintinnerinfocontextabdeltaexpressionexprconditioncodeanObjectaDoubleaStringsubsetsupersetsubstringitemdirectoryexmemberseedpropNamefixturefullNameisSuitearraydfdtCLSCompliantAttributeSystem.SecurityAllowPartiallyTrustedCallersAttributeAssemblyDelaySignAttributeAssemblyKeyFileAttributeAssemblyKeyNameAttributeAssemblyCompanyAttributeAssemblyProductAttributeAssemblyCopyrightAttributeAssemblyTrademarkAttributeAssemblyVersionAttributeAssemblyInformationalVersionAttributeAssemblyConfigurationAttributeSystem.Runtime.CompilerServicesCompilationRelaxationsAttributeRuntimeCompatibilityAttributenunit.frameworkAttributeUsageAttributeAttributeTargetsStringTrimGetTypeSystem.Collections.SpecializedListDictionaryAddInt32get_ParameterTypeop_EqualityDBNullInt16RuntimeTypeHandleGetTypeFromHandleByteSByteTimeSpanSystem.GlobalizationCultureInfoget_InvariantCultureConvertIFormatProviderChangeTypeget_ItemInt64SingleCharSplitop_InequalityBooleanToLowerFormat.cctorICustomAttributeProviderGetCustomAttributesConcatTextWriterWriteIEnumeratorGetEnumeratorget_CurrentMoveNextIDisposableDisposeArgumentNullExceptionPropertyInfoGetPropertyGetValueSystem.TextStringBuilderRemoveAtPeekObsoleteAttributeThreadSleepGetFilesGetDirectoriesget_RankGetLengthMathAbsSystem.Runtime.InteropServicesStructLayoutAttributeLayoutKindFieldOffsetAttributeGetElementTypeget_IsArrayget_CharsAppendFormatMaxMinUInt32UInt64UInt16ToDoubleToSingleToDecimalToUInt64ToInt64ToUInt32ToInt32IsInfinityop_GreaterThanop_Subtractionop_LessThanOrEqualop_ExplicitCompareToGetMethodMethodBaseDurationb__0interfaceCS$<>9__CachedAnonymousMethodDelegate2CompilerGeneratedAttributeb__1iEquatableInterfaceConverter`2CS$<>9__CachedAnonymousMethodDelegate3get_IsGenericTypeGetGenericTypeDefinitionIEquatable`1GetGenericArgumentsGetInterfacesFindAllConvertAllMakeGenericTypeget_KeysInsertFileSystemInfoFileAttributesget_Attributesget_CreationTimeget_LastAccessTimeget_CanReadget_CanSeekBinaryReaderget_Positionget_BaseStreamSeekOriginSeekReadset_PositionPathDirectorySeparatorCharToArrayJoinDelegateBindingFlagsMemoryStreamSerializeDeserializeSerializationExceptionNotSupportedExceptionIndexOfSystem.Text.RegularExpressionsRegexOptions<>c__DisplayClass1b__0WriteLineget_StackTraceFromDaysFromHoursFromMinutesFromSecondsFromMillisecondsFromTicksSerializableAttributeFlagsAttributeSystem.ComponentModelEditorBrowsableAttributeEditorBrowsableStateFileStreamOpenReadFileNextset_Itemget_MemberNextDoubleSystem.Runtime.Remoting.MessagingCallContextget_IsValueTypeAttribute_JOINTYPECombinatorialPairwiseSequential9NUnit.Framework.SpecialValue Null_SETCULTURE_SETUICULTUREAPARTMENT_STATE`1`2constraint!<unresolved {0}> <{0}><{0} {1}><{0} {1} {2}> null{0} "{0}" UNSET9Type {0} is not an attribute typeyActual value {0} does not implement ICustomAttributeProvider actual'type with attribute7Attribute {0} was not foundattribute equal to'<attribute {0} {1}> True FalseNaNandorOThe actual value must be an IEnumerable<empty>!all items uniquecontains+collection containingequivalentequivalent tosubsetofsubset ofordered)Null value at index ;Null property value at index %collection ordered+collection ordered bydescending<orderedby  descending >Cannot compare %Cannot compare to QA partial expression may not be resolved Length CountMessageInnerExceptionICannot check a condition in the past'delayInMilliseconds9 after {0} millisecond delay<after {0} {1}>QThe actual value must be a DirectoryInfo%An empty directoryG with {0} files and {1} directoriesThe actual value must be a non-null string, IEnumerable or DirectoryInfoWithin modifier may appear only once in a constraint expression+/- {0}ignoring case Extra:  Missing: % with {0} elementssString lengths are both {0}. Strings differ at index {1}.Expected string length {0} but was {1}. Strings differ at index {2}.uStream lengths are both {0}. Streams differ at offset {1}.OExpected Stream length {0} but was {1}.AExpected and actual are both {0}=Expected is {0}, actual is {1}5Values differ at index {0}kValues differ at expected index {0}, actual index {1}greater thanKCannot compare using a null reference1greater than or equal toless than+less than or equal to[]\\\0\a\b\f\n\r\t\v\x{0:X4}...oUlps may only be specified for floating point argumentsAUnknown tolerance mode specified mode=Both arguments must be numericCompareTomNeither value implements IComparable or IComparable<T> Equals-Stream is not readableexpected-Stream is not seekable<{0} "{1}" {2}>respectcaseignorecase...Path matchingAThe actual value may not be nullPath under-Path under or matchingAThe actual value is not of type value matching<#lambda expressionnotallall items somesome item noneno itemone!exactly one itemexactly itemsproperty )<propertyexists {0}>5Property {0} was not found name%<property {0} {1}>%in range ({0},{1})same as'binary serializable)<binaryserializable>!xml serializable#<xmlserializable>nullorempty;Actual value must be a string)null or empty string#String containing)String starting with%String ending withString matchinggThe actual value must be a TestDelegate but was {0}an exception'no exception thrown<throws>OThe actual value must be a TestDelegate3No Exception to be thrown ({0})mTolerance amount must be specified before setting modemTried to use multiple tolerance modes at the same time?A numeric tolerance is required typeofinstanceofinstance ofassignable fromassignable to_Assert.Equals should not be used for AssertionsqAssert.ReferenceEquals should not be used for Assertions;The directory may not be nulldirectoryA{0} does not have a {1} property_CATEGORIES7NUnit.Framework.TestContextTestDirectoryWorkDirectoryTest.NameTest.FullNameTest.PropertiesResult.State , <  >G17d.0dG9f.0fG29m {0}^ Expected:  But was: {0} {0} , {0}<string.Empty> '{0}'/yyyy-MM-dd HH:mm:ss.fff\GBv3?z\V4!%   (1 1 1  (1(5      5(5  9=   T             (   ((( A (A 11 1 1(1 E 4 4   00   D  (D  1  9 9   0! 0I M 0Q  9(9 9   0! 0I (U 9 ( ! I  ! !I I , 0,! 0,I  Y] Y8< L aL L(La( DL ,p  D , , ,10, 0e         8 <  4     p10p x10x |10| 10 9  9   L P T X   0(%(,(((((((((8(<D <    0! 0I M 0Q(  h   0! 0I M 0Q   ii mm 99 99m(M Q ! IM MQ Q             h  99  h  h  h qqh h h hh  h1111  uuh 55h mmh  99h yy ii (e e    ,, , , ,0} @(@) )()dh d d hh(d(h )  h Y    0 0 0     ) ) ) )1 )1 )1   )))         999 111         qqqqqq                   mmm    0 0 0 0 0 0    m9191 9199 9999 99 99 9999 9 999L yyyyyyyyy iiiiii   D,,,1,D, )8<4p1px1x|1|19 9PTX(8<mm m m-=       1      6NUnit.Framework.TestContextResult.State 5  ((  ((   ( u      q  hp`p`$$RSA1J 5Km 9@ j2v>-ri ^V<㓬=֢ gn&FhIaR_uF+J|1 ВQmR3GKOF c! 7:#1Vdz6! &ET AllowMultipleT Inherited  &T AllowMultipleT Inherited&T AllowMultipleT Inherited&ET AllowMultipleT Inherited&@T AllowMultipleT Inherited1&ET AllowMultipleT Inherited5&T AllowMultipleT Inherited 11 1 19  A9T      &T AllowMultipleT Inherited&@T AllowMultipleT Inherited&T AllowMultipleT Inherited&T AllowMultipleT Inherited&ET AllowMultipleT Inherited&@T AllowMultipleT Inherited A&T AllowMultipleT Inherited1&T AllowMultipleT Inherited   4DD 15 5   = m=A9  hU==A =A I  I=A MM =A$($(I,L LaL L a,8<4px!Use InstanceOf(expectedType)Use InstanceOf()| LPTX( ( @  yy yReplace with 'Using'     uu  u=AQ         e  M M uM1  M MM u M  q       d    d qqqqqqqq q q d    d   d    d   d   q       q  1111 A    11 1111e111 11e1e 1 11 11 11  1=A m =A ==    i i        u1 Ue  =A =A II 1I( 0 i i@ ) )0   hd  )  ))) ,'Use Assert with constraint-based syntaxyyy  UIm=A          A5(#Use Is class for string constraintsUse Is.StringContaining Use Is.Not.StringContainingUse Is.StringStartingUse Is.StringEndingUse Is.StringMatching Use Is.All =A =A   `../../nunit.snk NUnit.org NUnitCopyright (C) 2002-2012 Charlie Poole. Copyright (C) 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov. Copyright (C) 2000-2002 Philip Craig. All Rights Reserved.&!NUnit is a trademark of NUnit.org 2.6.1.12217TWrapNonExceptionThrows!! !_CorDllMainmscoree.dll% @0HX@4VS_VERSION_INFO//?DVarFileInfo$Translation StringFileInfo000004b04 CompanyNameNUnit.org,FileDescription 8 FileVersion2.6.1.12217HInternalNamenunit.framework.dllLegalCopyrightCopyright (C) 2002-2012 Charlie Poole. Copyright (C) 2002-2004 James W. Newkirk, Michael C. Two, Alexei A. Vorontsov. Copyright (C) 2000-2002 Philip Craig. All Rights Reserved.l"LegalTrademarksNUnit is a trademark of NUnit.orgPOriginalFilenamenunit.framework.dll,ProductNameNUnit< ProductVersion2.6.1.12217@ Assembly Version2.6.1.12217  2Serpent-serpent-1.23/dotnet/nuget-pack.cmd000066400000000000000000000002421312652634400205610ustar00rootroot00000000000000@echo Running tests... @call test-dotnet.cmd if %errorlevel% neq 0 exit /b %errorlevel% @echo. @echo Building NuGet package... nuget pack Serpent\Serpent.nuspec Serpent-serpent-1.23/dotnet/nuget-pack.sh000077500000000000000000000001711312652634400204340ustar00rootroot00000000000000#!/bin/sh echo "Building and testing..." . ./test-dotnet.sh echo "Nuget packaging..." nuget pack Serpent/Serpent.nuspec Serpent-serpent-1.23/dotnet/test-dotnet.cmd000066400000000000000000000003431312652634400207770ustar00rootroot00000000000000@echo Building new version... @call build-dotnet-microsoft.cmd @echo. echo "Running tests" L:\tools\nunit2.6\nunit-console-x86 /framework:net-4.0 /nothread /noshadow .\Serpent.Test\bin\Release\Razorvine.Serpent.Test.dll Serpent-serpent-1.23/dotnet/test-dotnet.sh000077500000000000000000000002301312652634400206440ustar00rootroot00000000000000#!/bin/sh echo "Building..." . ./build-dotnet-mono.sh echo "Running tests" nunit-console -noshadow Serpent.Test/bin/Release/Razorvine.Serpent.Test.dll Serpent-serpent-1.23/java/000077500000000000000000000000001312652634400154645ustar00rootroot00000000000000Serpent-serpent-1.23/java/.classpath000066400000000000000000000017441312652634400174550ustar00rootroot00000000000000 Serpent-serpent-1.23/java/.gitignore000066400000000000000000000000131312652634400174460ustar00rootroot00000000000000TEST-*.xml Serpent-serpent-1.23/java/.project000066400000000000000000000010571312652634400171360ustar00rootroot00000000000000 serpent org.eclipse.jdt.core.javabuilder org.eclipse.m2e.core.maven2Builder org.eclipse.m2e.core.maven2Nature org.eclipse.jdt.core.javanature Serpent-serpent-1.23/java/.settings/000077500000000000000000000000001312652634400174025ustar00rootroot00000000000000Serpent-serpent-1.23/java/.settings/org.eclipse.core.resources.prefs000066400000000000000000000001631312652634400256150ustar00rootroot00000000000000eclipse.preferences.version=1 encoding//src/main/java=UTF-8 encoding//src/test/java=UTF-8 encoding/=UTF-8 Serpent-serpent-1.23/java/.settings/org.eclipse.jdt.core.prefs000066400000000000000000000013401312652634400243620ustar00rootroot00000000000000eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve org.eclipse.jdt.core.compiler.compliance=1.8 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.source=1.8 Serpent-serpent-1.23/java/.settings/org.eclipse.m2e.core.prefs000066400000000000000000000001261312652634400242650ustar00rootroot00000000000000activeProfiles= eclipse.preferences.version=1 resolveWorkspaceProjects=true version=1 Serpent-serpent-1.23/java/mvn-release-readme.txt000066400000000000000000000006171312652634400217020ustar00rootroot00000000000000Making a release to Sonatype Nexus/maven central: $ mvn release:clean release:prepare release:perform Requires version number in the pom.xml to be "x.y-SNAPSHOT". Finalise and publish the release via https://oss.sonatype.org/#stagingRepositories See also: http://java.dzone.com/articles/deploy-maven-central http://central.sonatype.org/pages/apache-maven.html#performing-a-release-deployment Serpent-serpent-1.23/java/pom.xml000066400000000000000000000060311312652634400170010ustar00rootroot00000000000000 4.0.0 org.sonatype.oss oss-parent 9 net.razorvine serpent 1.23 jar serpent https://github.com/irmen/Serpent UTF-8 org.apache.maven.plugins maven-compiler-plugin 3.3 -Xlint:deprecation 1.8 1.8 org.apache.maven.plugins maven-gpg-plugin 1.5 org.apache.maven.plugins maven-release-plugin 2.5.2 org.apache.maven.plugins maven-javadoc-plugin -Xdoclint:none junit junit 4.12 test Serpent serializes an object tree into a Python ast.literal_eval() compatible literal expression. It is safe to send serpent data to other machines over the network for instance (because only 'safe' literals are encoded). There is also a deserializer or parse provided that turns such a literal expression back into the appropriate Java object tree. It is an alternative to JSON to provide easy data integration between Java and Python. Serpent is more expressive as JSON (it supports more data types). https://github.com/irmen/Serpent scm:git:https://github.com/irmen/Serpent.git scm:git:https://github.com/irmen/Serpent.git serpent-1.23 Github https://github.com/irmen/Serpent/issues irmen Irmen de Jong irmen@razorvine.net https://github.com/irmen MIT License http://www.opensource.org/licenses/mit-license.php Serpent-serpent-1.23/java/src/000077500000000000000000000000001312652634400162535ustar00rootroot00000000000000Serpent-serpent-1.23/java/src/main/000077500000000000000000000000001312652634400171775ustar00rootroot00000000000000Serpent-serpent-1.23/java/src/main/java/000077500000000000000000000000001312652634400201205ustar00rootroot00000000000000Serpent-serpent-1.23/java/src/main/java/net/000077500000000000000000000000001312652634400207065ustar00rootroot00000000000000Serpent-serpent-1.23/java/src/main/java/net/razorvine/000077500000000000000000000000001312652634400227255ustar00rootroot00000000000000Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/000077500000000000000000000000001312652634400244055ustar00rootroot00000000000000Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ComplexNumber.java000066400000000000000000000041461312652634400300350ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) */ package net.razorvine.serpent; import java.io.Serializable; /** * A complex number. */ public class ComplexNumber implements Serializable { private static final long serialVersionUID = 5396759273405612137L; public double real; public double imaginary; public ComplexNumber(double r, double i) { real=r; imaginary=i; } @Override public String toString() { StringBuilder sb=new StringBuilder(); sb.append(real); if(imaginary>0) sb.append('+'); return sb.append(imaginary).append('i').toString(); } public double Magnitude() { return Math.sqrt(real * real + imaginary * imaginary); } public void add(ComplexNumber other) { real += other.real; imaginary += other.imaginary; } public void subtract(ComplexNumber other) { real -= other.real; imaginary -= other.imaginary; } public void multiply(ComplexNumber other) { double new_real = real * other.real - imaginary * other.imaginary; double new_imaginary = real * other.imaginary + imaginary * other.real; real = new_real; imaginary = new_imaginary; } public void divide(ComplexNumber other) { double new_real = (real * other.real + imaginary * other.imaginary) / (other.real * other.real + other.imaginary * other.imaginary); double new_imaginary = (imaginary * other.real - real * other.imaginary) / (other.real * other.real + other.imaginary * other.imaginary); real = new_real; imaginary = new_imaginary; } @Override public boolean equals(Object obj) { if(!(obj instanceof ComplexNumber)) { return false; } ComplexNumber other = (ComplexNumber) obj; return real==other.real && imaginary==other.imaginary; } @Override public int hashCode() { Double r = Double.valueOf(real); Double i = Double.valueOf(imaginary); return r.hashCode() ^ i.hashCode(); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/DebugVisitor.java000066400000000000000000000051631312652634400276630ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) */ package net.razorvine.serpent; import net.razorvine.serpent.ast.*; /** * Ast nodevisitor that prints out the Ast as a string for debugging purposes */ public class DebugVisitor implements INodeVisitor { private StringBuilder result = new StringBuilder(); private int indentlevel=0; public DebugVisitor() { } /** * Get the debug string representation result. */ @Override public String toString() { return result.toString(); } protected void indent() { for(int i=0; i convert(Object obj); } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/IDictToInstance.java000066400000000000000000000011711312652634400302340ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) */ package net.razorvine.serpent; import java.io.IOException; import java.util.Map; /** * Customization interface for turning dicts back into specific objects. */ public interface IDictToInstance { /** * Convert the given dictionary to a specific object. * Can return null to use the default behavior. */ public Object convert(Map dict) throws IOException; } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/LibraryVersion.java000066400000000000000000000005721312652634400302260ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) */ package net.razorvine.serpent; public final class LibraryVersion { public static final String VERSION = "1.23"; } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ObjectifyVisitor.java000066400000000000000000000057251312652634400305570ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) */ package net.razorvine.serpent; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import net.razorvine.serpent.ast.*; /** * Ast nodevisitor that turns the AST into actual Java objects (array, int, IDictionary, string, etc...) */ public class ObjectifyVisitor implements INodeVisitor { Stack generated = new Stack(); protected IDictToInstance dictConverter = null; public ObjectifyVisitor() { } public ObjectifyVisitor(IDictToInstance dictConverter) { this.dictConverter = dictConverter; } /** * get the resulting object tree. */ public Object getObject() { return generated.pop(); } public void visit(ComplexNumberNode complex) { generated.push(new ComplexNumber(complex.real, complex.imaginary)); } public void visit(DictNode dict) { Map obj = new HashMap(dict.elements.size()); for(INode e: dict.elements) { KeyValueNode kv = (KeyValueNode)e; kv.key.accept(this); Object key = generated.pop(); kv.value.accept(this); Object value = generated.pop(); obj.put(key, value); } if(dictConverter==null || !obj.containsKey("__class__")) { generated.push(obj); } else { Object result; try { result = dictConverter.convert(obj); } catch (IOException e) { throw new RuntimeException("problem converting dict to class", e); } if(result==null) generated.push(obj); else generated.push(result); } } public void visit(ListNode list) { List obj = new ArrayList(list.elements.size()); for(INode node: list.elements) { node.accept(this); obj.add(generated.pop()); } generated.push(obj); } public void visit(NoneNode none) { generated.push(null); } public void visit(IntegerNode value) { generated.push(value.value); } public void visit(LongNode value) { generated.push(value.value); } public void visit(DoubleNode value) { generated.push(value.value); } public void visit(BooleanNode value) { generated.push(value.value); } public void visit(StringNode value) { generated.push(value.value); } public void visit(BigIntNode value) { generated.push(value.value); } public void visit(SetNode setnode) { Set obj = new HashSet(); for(INode node: setnode.elements) { node.accept(this); obj.add(generated.pop()); } generated.push(obj); } public void visit(TupleNode tuple) { Object[] array = new Object[tuple.elements.size()]; int index=0; for(INode node: tuple.elements) { node.accept(this); array[index++] = generated.pop(); } generated.push(array); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ParseException.java000066400000000000000000000013221312652634400301770ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) */ package net.razorvine.serpent; /** * Exception that is used when a serpent parsing error occurs. */ public class ParseException extends RuntimeException { private static final long serialVersionUID = -667969719712045595L; public ParseException() { } public ParseException(String message) { super(message); } public ParseException(Throwable cause) { super(cause); } public ParseException(String message, Throwable cause) { super(message, cause); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/Parser.java000066400000000000000000000377571312652634400265270ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) */ package net.razorvine.serpent; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import net.razorvine.serpent.ast.*; /** * Parse a Python literal into an Ast (abstract syntax tree). */ public class Parser { /** * Parse from a byte array (containing utf-8 encoded string with the Python literal expression in it) */ public Ast parse(byte[] serialized) throws ParseException { try { return parse(new String(serialized, "utf-8")); } catch (UnsupportedEncodingException e) { throw new ParseException(e.toString()); } } /** * Parse from a string with the Python literal expression */ public Ast parse(String expression) { Ast ast=new Ast(); if(expression==null || expression.length()==0) return ast; SeekableStringReader sr = new SeekableStringReader(expression); if(sr.peek()=='#') sr.readUntil('\n'); // skip comment line try { ast.root = parseExpr(sr); sr.skipWhitespace(); if(sr.hasMore()) throw new ParseException("garbage at end of expression"); return ast; } catch (ParseException x) { String faultLocation = extractFaultLocation(sr); throw new ParseException(x.getMessage() + " (at position "+sr.bookmark()+"; '"+faultLocation+"')", x); } } String extractFaultLocation(SeekableStringReader sr) { SeekableStringReader.StringContext ctx = sr.context(-1, 20); return String.format("...%s>>><<<%s...", ctx.left, ctx.right); } INode parseExpr(SeekableStringReader sr) { // expr = [ ] single | compound [ ] . sr.skipWhitespace(); if(!sr.hasMore()) throw new ParseException("unexpected end of line, missing expression or close/open character"); char c = sr.peek(); INode node; if(c=='{' || c=='[' || c=='(') node = parseCompound(sr); else node = parseSingle(sr); sr.skipWhitespace(); return node; } INode parseCompound(SeekableStringReader sr) { // compound = tuple | dict | list | set . sr.skipWhitespace(); switch(sr.peek()) { case '[': return parseList(sr); case '{': { int bm = sr.bookmark(); try { return parseSet(sr); } catch(ParseException x) { sr.flipBack(bm); return parseDict(sr); } } case '(': // tricky case here, it can be a tuple but also a complex number: // if the last character before the closing parenthesis is a 'j', it is a complex number { int bm = sr.bookmark(); String betweenparens = sr.readUntil(")\n").trim(); sr.flipBack(bm); return betweenparens.endsWith("j") ? parseComplex(sr) : parseTuple(sr); } default: throw new ParseException("invalid sequencetype char"); } } TupleNode parseTuple(SeekableStringReader sr) { //tuple = tuple_empty | tuple_one | tuple_more //tuple_empty = '()' . //tuple_one = '(' expr ',' ')' . //tuple_more = '(' expr_list trailing_comma ')' . // trailing_comma = '' | ',' . sr.read(); // ( sr.skipWhitespace(); TupleNode tuple = new TupleNode(); if(sr.peek() == ')') { sr.read(); return tuple; // empty tuple } INode firstelement = parseExpr(sr); if(sr.peek() == ',') { sr.read(); sr.skipWhitespace(); if(sr.read() == ')') { // tuple with just a single element tuple.elements.add(firstelement); return tuple; } sr.rewind(1); // undo the thing that wasn't a ) } tuple.elements = parseExprList(sr); tuple.elements.add(0, firstelement); // handle trailing comma if present sr.skipWhitespace(); if(!sr.hasMore()) throw new ParseException("missing ')'"); if(sr.peek() == ',') sr.read(); if(!sr.hasMore()) throw new ParseException("missing ')'"); char closechar = sr.read(); if(closechar==',') closechar = sr.read(); if(closechar!=')') throw new ParseException("expected ')'"); return tuple; } List parseExprList(SeekableStringReader sr) { //expr_list = expr { ',' expr } . List exprList = new ArrayList(); exprList.add(parseExpr(sr)); while(sr.hasMore() && sr.peek() == ',') { sr.read(); try { exprList.add(parseExpr(sr)); } catch (ParseException x) { sr.rewind(1); break; } } return exprList; } List parseKeyValueList(SeekableStringReader sr) { //keyvalue_list = keyvalue { ',' keyvalue } . List kvs = new ArrayList(); kvs.add(parseKeyValue(sr)); while(sr.hasMore() && sr.peek()==',') { sr.read(); try { kvs.add(parseKeyValue(sr)); } catch (ParseException x) { sr.rewind(1); break; } } return kvs; } KeyValueNode parseKeyValue(SeekableStringReader sr) { //keyvalue = expr ':' expr . INode key = parseExpr(sr); if(sr.hasMore() && sr.peek()==':') { sr.read(); // : INode value = parseExpr(sr); KeyValueNode kv = new KeyValueNode(); kv.key = key; kv.value = value; return kv; } throw new ParseException("expected ':'"); } SetNode parseSet(SeekableStringReader sr) { // set = '{' expr_list trailing_comma '}' . // trailing_comma = '' | ',' . sr.read(); // { sr.skipWhitespace(); SetNode setnode = new SetNode(); List elts = parseExprList(sr); // handle trailing comma if present sr.skipWhitespace(); if(!sr.hasMore()) throw new ParseException("missing '}'"); if(sr.peek() == ',') sr.read(); if(!sr.hasMore()) throw new ParseException("missing '}'"); char closechar = sr.read(); if(closechar!='}') throw new ParseException("expected '}'"); // make sure it has set semantics (remove duplicate elements) Set h = new HashSet(elts); setnode.elements = new ArrayList(h); return setnode; } ListNode parseList(SeekableStringReader sr) { // list = list_empty | list_nonempty . // list_empty = '[]' . // list_nonempty = '[' expr_list trailing_comma ']' . // trailing_comma = '' | ',' . sr.read(); // [ sr.skipWhitespace(); ListNode list = new ListNode(); if(sr.peek() == ']') { sr.read(); return list; // empty list } list.elements = parseExprList(sr); // handle trailing comma if present sr.skipWhitespace(); if(!sr.hasMore()) throw new ParseException("missing ']'"); if(sr.peek() == ',') sr.read(); if(!sr.hasMore()) throw new ParseException("missing ']'"); char closechar = sr.read(); if(closechar!=']') throw new ParseException("expected ']'"); return list; } INode parseDict(SeekableStringReader sr) { //dict = '{' keyvalue_list trailing_comma '}' . //keyvalue_list = keyvalue { ',' keyvalue } . //keyvalue = expr ':' expr . // trailing_comma = '' | ',' . sr.read(); // { sr.skipWhitespace(); DictNode dict = new DictNode(); if(sr.peek() == '}') { sr.read(); return dict; // empty dict } List elts = parseKeyValueList(sr); // handle trailing comma if present sr.skipWhitespace(); if(!sr.hasMore()) throw new ParseException("missing '}'"); if(sr.peek() == ',') sr.read(); if(!sr.hasMore()) throw new ParseException("missing '}'"); char closechar = sr.read(); if(closechar!='}') throw new ParseException("expected '}'"); // make sure it has dict semantics (remove duplicate keys) Map fixedDict = new HashMap(elts.size()); for(INode e: elts) { KeyValueNode kv = (KeyValueNode)e; fixedDict.put(kv.key, kv.value); } for(Map.Entry e: fixedDict.entrySet()) { KeyValueNode kvnode = new KeyValueNode(); kvnode.key = e.getKey(); kvnode.value = e.getValue(); dict.elements.add(kvnode); } // SPECIAL CASE: {'__class__':'float','value':'nan'} ---> Double.NaN if(dict.elements.size()==2) { if(dict.elements.contains(new KeyValueNode(new StringNode("__class__"), new StringNode("float")))) { if(dict.elements.contains(new KeyValueNode(new StringNode("value"), new StringNode("nan")))) { return new DoubleNode(Double.NaN); } } } return dict; } public INode parseSingle(SeekableStringReader sr) { // single = int | float | complex | string | bool | none . sr.skipWhitespace(); switch(sr.peek()) { case 'N': return parseNone(sr); case 'T': case 'F': return parseBool(sr); case '\'': case '"': return parseString(sr); } // int or float or complex. int bookmark = sr.bookmark(); try { return parseComplex(sr); } catch (ParseException x1) { sr.flipBack(bookmark); try { return parseFloat(sr); } catch (ParseException x2) { sr.flipBack(bookmark); return parseInt(sr); } } } final String FloatCharacters = "-+.eE0123456789"; final String IntCharacters = "-0123456789"; INode parseInt(SeekableStringReader sr) { // int = ['-'] digitnonzero {digit} . String numberstr = sr.readWhile(IntCharacters); if(numberstr.length()==0) throw new ParseException("invalid int character"); try { try { return new IntegerNode(Integer.parseInt(numberstr)); } catch (NumberFormatException x1) { // try long try { return new LongNode(Long.parseLong(numberstr)); } catch (NumberFormatException x2) { // try bigint, but it can still overflow because it's not arbitrary precision try { return new BigIntNode(new BigInteger(numberstr)); } catch (NumberFormatException x3) { throw new ParseException("number too large or invalid"); } } } } catch (NumberFormatException x) { throw new ParseException("invalid integer format", x); } } PrimitiveNode parseFloat(SeekableStringReader sr) { String numberstr = sr.readWhile(FloatCharacters); if(numberstr.length()==0) throw new ParseException("invalid float character"); // little bit of a hack: // if the number doesn't contain a decimal point and no 'e'/'E', it is an integer instead. // in that case, we need to reject it as a float. if(numberstr.indexOf('.')<0 && numberstr.indexOf('e')<0 && numberstr.indexOf('E')<0) throw new ParseException("number is not a float (might be an integer though)"); try { return new DoubleNode(Double.parseDouble(numberstr)); } catch (NumberFormatException x) { throw new ParseException("invalid float format", x); } } ComplexNumberNode parseComplex(SeekableStringReader sr) { //complex = complextuple | imaginary . //imaginary = ['+' | '-' ] ( float | int ) 'j' . //complextuple = '(' ( float | int ) imaginary ')' . if(sr.peek()=='(') { // complextuple sr.read(); // ( String numberstr; if(sr.peek()=='-' || sr.peek()=='+') { // starts with a sign, read that first otherwise the readuntil will return immediately numberstr = sr.read(1) + sr.readUntil("+-"); } else { numberstr = sr.readUntil("+-"); } sr.rewind(1); // rewind the +/- // because we're a bit more cautious here with reading chars than in the float parser, // it can be that the parser now stopped directly after the 'e' in a number like "3.14e+20". // ("3.14e20" is fine) So, check if the last char is 'e' and if so, continue reading 0..9. if(numberstr.endsWith("e")||numberstr.endsWith("E")) { // if the next symbol is + or -, accept it, then read the exponent integer if(sr.peek()=='-' || sr.peek()=='+') numberstr+=sr.read(1); numberstr += sr.readWhile("0123456789"); } sr.skipWhitespace(); double real; try { real = Double.parseDouble(numberstr); } catch (NumberFormatException x) { throw new ParseException("invalid float format", x); } double imaginarypart = parseImaginaryPart(sr); if(sr.read()!=')') throw new ParseException("expected ) to end a complex number"); ComplexNumberNode c = new ComplexNumberNode(); c.real = real; c.imaginary = imaginarypart; return c; } else { // imaginary double imag = parseImaginaryPart(sr); ComplexNumberNode c = new ComplexNumberNode(); c.real = 0; c.imaginary = imag; return c; } } double parseImaginaryPart(SeekableStringReader sr) { //imaginary = ['+' | '-' ] ( float | int ) 'j' . if(!sr.hasMore()) throw new ParseException("unexpected end of input string"); char sign_or_digit = sr.peek(); if(sign_or_digit=='+') sr.read(); // skip the '+' // now an int or float follows. double double_value; int bookmark = sr.bookmark(); try { PrimitiveNode float_part = parseFloat(sr); double_value = float_part.value; } catch (ParseException x1) { sr.flipBack(bookmark); INode integer_part = parseInt(sr); if(integer_part instanceof IntegerNode) { double_value = ((IntegerNode)integer_part).value; } else if(integer_part instanceof LongNode) { double_value = ((LongNode)integer_part).value; } else if(integer_part instanceof BigIntNode) { double_value = ((BigIntNode)integer_part).value.doubleValue(); } else { throw new ParseException("not an integer for the imaginary part"); } } // now a 'j' must follow! sr.skipWhitespace(); try { char j_char = sr.read(); if(j_char!='j') throw new ParseException("not an imaginary part"); } catch (IndexOutOfBoundsException x) { throw new ParseException("not an imaginary part"); } return double_value; } PrimitiveNode parseString(SeekableStringReader sr) { char quotechar = sr.read(); // ' or " StringBuilder sb = new StringBuilder(10); while(sr.hasMore()) { char c = sr.read(); if(c=='\\') { // backslash unescape c = sr.read(); switch(c) { case '\\': sb.append('\\'); break; case '\'': sb.append('\''); break; case '"': sb.append('"'); break; case 'b': sb.append('\b'); break; case 'f': sb.append('\f'); break; case 'n': sb.append('\n'); break; case 'r': sb.append('\r'); break; case 't': sb.append('\t'); break; case 'x': // "\x00" sb.append((char)Integer.parseInt(sr.read(2), 16)); break; case 'u': // "\u0000" sb.append((char)Integer.parseInt(sr.read(4), 16)); break; default: sb.append(c); break; } } else if(c==quotechar) { // end of string return new StringNode(sb.toString()); } else { sb.append(c); } } throw new ParseException("unclosed string"); } PrimitiveNode parseBool(SeekableStringReader sr) { // True,False String b = sr.readUntil('e'); if(b.equals("Tru")) return new BooleanNode(true); if(b.equals("Fals")) return new BooleanNode(false); throw new ParseException("expected bool, True or False"); } NoneNode parseNone(SeekableStringReader sr) { // None String n = sr.readUntil('e'); if(n.equals("Non")) return NoneNode.Instance; throw new ParseException("expected None"); } /** * Utility function to convert obj back to actual bytes if it is a serpent-encoded bytes dictionary * (a IDictionary with base-64 encoded 'data' in it and 'encoding'='base64'). * If obj is already a byte array, return obj unmodified. * If it is something else, throw an IllegalArgumentException */ public static byte[] toBytes(Object obj) { if(obj instanceof Map) { @SuppressWarnings("unchecked") Map dict = (Map)obj; String data = dict.get("data"); String encoding = dict.get("encoding"); if(data==null || encoding==null || !encoding.equals("base64")) { throw new IllegalArgumentException("argument is neither bytearray nor serpent base64 encoded bytes dict"); } return Base64.getDecoder().decode(data); } if(obj instanceof byte[]) { return (byte[]) obj; } throw new IllegalArgumentException("argument is neither bytearray nor serpent base64 encoded bytes dict"); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/SeekableStringReader.java000066400000000000000000000113221312652634400312740ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) */ package net.razorvine.serpent; /** * A special string reader that is suitable for the parser to read through * the expression string. You can rewind it, set bookmarks to flip back to, etc. */ public class SeekableStringReader { private String str; private int cursor = 0; private int bookmark = -1; public SeekableStringReader(String str) { if(str==null) throw new IllegalArgumentException("str may not be null"); this.str = str; } /** * Make a nested reader with its own cursor and bookmark. * The cursor starts at the same position as the parent. */ public SeekableStringReader(SeekableStringReader parent) { str = parent.str; cursor = parent.cursor; } /** * Is tehre more to read? */ public boolean hasMore() { return cursor0) throw new ParseException("no more data"); String result = str.substring(cursor, cursor+safecount); cursor += safecount; return result; } /** * Read everything until one the sentinel, which must exist in the string. * Sentinel char is read but not returned in the result. */ public String readUntil(char sentinel) { int i = str.indexOf(sentinel, cursor); if(i>=0) { int from = cursor; cursor = i+1; return str.substring(from, i); } throw new ParseException("terminator not found"); } /** * Read everything until one of the sentinel(s), which must exist in the string. * Sentinel char is read but not returned in the result. */ public String readUntil(String sentinels) { int index=Integer.MAX_VALUE; for(char s: sentinels.toCharArray()) { int i = str.indexOf(s, cursor); if(i>=0) index = Math.min(i, index); } if(index>=0 && index=0) ++cursor; else break; } return str.substring(start, cursor); } /** * Read away any whitespace. * If a comment follows ('# bla bla') read away that as well */ public void skipWhitespace() { while(hasMore()) { char c=read(); if(c=='#') { readUntil('\n'); return; } if(!Character.isWhitespace(c)) { rewind(1); return; } } } /** * Returns the rest of the data until the end. */ public String rest() { if(cursor>=str.length()) throw new ParseException("no more data"); String result=str.substring(cursor); cursor = str.length(); return result; } /** * Rewind a number of characters. */ public void rewind(int count) { cursor = Math.max(0, cursor-count); } /** * Return a bookmark to rewind to later. */ public int bookmark() { return cursor; } /** * Flip back to previously set bookmark. */ public void flipBack(int bookmark) { cursor = bookmark; } /** * Sync the position and bookmark with the current position in another reader. */ public void sync(SeekableStringReader inner) { bookmark = inner.bookmark; cursor = inner.cursor; } /** * Extract a piece of context around the current cursor (if you set cursor to -1) * or around a given position in the string (if you set cursor>=0). */ public class StringContext { public String left; public String right; } public StringContext context(int crsr, int width) { if(crsr<0) crsr=this.cursor; int leftStrt = Math.max(0, crsr-width); int leftLen = crsr-leftStrt; int rightLen = Math.min(width, str.length()-crsr); StringContext result = new StringContext(); result.left = str.substring(leftStrt, leftStrt+leftLen); result.right = str.substring(crsr, crsr+rightLen); return result; } public void close() { this.str = null; } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/Serializer.java000066400000000000000000000460701312652634400273700ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) */ package net.razorvine.serpent; import java.io.IOException; import java.io.Serializable; import java.io.StringWriter; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; import java.util.Base64; import java.util.Map.Entry; /** * Serialize an object tree to a byte stream. * It is not thread-safe: make sure you're not making changes to the object tree that is being serialized. */ public class Serializer { /** * The maximum nesting level of the object graphs that you want to serialize. * This limit has been set to avoid troublesome stack overflow errors. * (If it is reached, an IllegalArgumentException is thrown instead with a clear message) */ public int maximumLevel = 500; // to avoid stack overflow errors /** * Indent the resulting serpent serialization text? */ public boolean indent = false; /** * Use set literals? */ public boolean setliterals = true; /** * Include package name in class name, for classes that are serialized to dicts? */ public boolean packageInClassName = false; private static Map, IClassSerializer> classToDictRegistry = new HashMap, IClassSerializer>(); /** * Create a Serpent serializer with default options. */ public Serializer() { } /** * Create a Serpent serializer with custom options. * @param indent should the output be indented to make it more readable? * @param setliterals should set literals be used (recommended if you use newer Python versions to parse this) * @param packageInClassName should the package name be included with the class name for classes that are serialized to dict? */ public Serializer(boolean indent, boolean setliterals, boolean packageInClassName) { this.indent = indent; this.setliterals = setliterals; this.packageInClassName = packageInClassName; } /** * Register a custom class serializer, if you want to tweak the serialization of classes that Serpent doesn't know about yet. */ public static void registerClass(Class clazz, IClassSerializer converter) { classToDictRegistry.put(clazz, converter); } /** * Serialize an object graph to a serpent serialized form. */ public byte[] serialize(Object obj) { StringWriter sw = new StringWriter(); if(this.setliterals) sw.write("# serpent utf-8 python3.2\n"); // set-literals require python 3.2+ to deserialize (ast.literal_eval limitation) else sw.write("# serpent utf-8 python2.6\n"); serialize(obj, sw, 0); sw.flush(); final String ser = sw.toString(); try { sw.close(); return ser.getBytes("utf-8"); } catch (IOException x) { throw new IllegalArgumentException("error creating output bytes: "+x); } } protected void serialize(Object obj, StringWriter sw, int level) { if(level>maximumLevel) throw new IllegalArgumentException("Object graph nesting too deep. Increase serializer.maximumLevel if you think you need more."); if(obj!=null && obj.getClass().getName().startsWith("org.python.")) obj = convertJythonObject(obj); // null -> None // hashtables/dictionaries -> dict // hashset -> set // array -> tuple // byte arrays --> base64 // any other collection --> list // date//uuid/exception -> custom mapping // random class --> public javabean properties to dictionary // primitive types --> simple mapping Class type = obj==null? null : obj.getClass(); Class componentType = type==null? null : type.getComponentType(); // primitive array? if(componentType!=null) { // byte array? encode as base-64 if(componentType==Byte.TYPE) { serialize_bytes((byte[])obj, sw, level); return; } else { serialize_primitive_array(obj, sw, level); } return; } if(obj==null) { sw.write("None"); } else if(obj instanceof String) { serialize_string((String)obj, sw, level); } else if(type.isPrimitive() || isBoxed(type)) { serialize_primitive(obj, sw, level); } else if(obj instanceof Enum) { serialize_string(obj.toString(), sw, level); } else if(obj instanceof BigDecimal) { serialize_bigdecimal((BigDecimal)obj, sw, level); } else if(obj instanceof Number) { serialize_primitive(obj, sw, level); } else if(obj instanceof Date) { serialize_date((Date)obj, sw, level); } else if(obj instanceof Calendar) { serialize_calendar((Calendar)obj, sw, level); } else if(obj instanceof UUID) { serialize_uuid((UUID)obj, sw, level); } else if(obj instanceof Set) { serialize_set((Set)obj, sw, level); } else if(obj instanceof Map) { serialize_dict((Map)obj, sw, level); } else if(obj instanceof Collection) { serialize_collection((Collection)obj, sw, level); } else if(obj instanceof ComplexNumber) { serialize_complex((ComplexNumber)obj, sw, level); } else if(obj instanceof Exception) { serialize_exception((Exception)obj, sw, level); } else if(obj instanceof Serializable) { serialize_class(obj, sw, level); } else { throw new IllegalArgumentException("cannot serialize object of type "+type); } } /** * When used from Jython directly, it sometimes passes some Jython specific * classes to the serializer (such as org.python.core.PyComplex for a complex number). * Due to the way these are constructed, Serpent is greatly confused, and will often * end up in an endless loop eventually crashing with too deep nesting. * For the know cases, we convert them here to appropriate representations. */ protected Object convertJythonObject(Object obj) { final Class clazz = obj.getClass(); final String classname = clazz.getName(); try { // use reflection because I don't want to have a compiler dependency on Jython. if(classname.equals("org.python.core.PyTuple")) { return clazz.getMethod("toArray").invoke(obj); } else if(classname.equals("org.python.core.PyComplex")) { Object pyImag = clazz.getMethod("getImag").invoke(obj); Object pyReal = clazz.getMethod("getReal").invoke(obj); Double imag = (Double) pyImag.getClass().getMethod("getValue").invoke(pyImag); Double real = (Double) pyReal.getClass().getMethod("getValue").invoke(pyReal); return new ComplexNumber(real, imag); } else if(classname.equals("org.python.core.PyByteArray")) { Object pyStr = clazz.getMethod("__str__").invoke(obj); return pyStr.getClass().getMethod("toBytes").invoke(pyStr); } else if(classname.equals("org.python.core.PyMemoryView")) { Object pyBytes = clazz.getMethod("tobytes").invoke(obj); return pyBytes.getClass().getMethod("toBytes").invoke(pyBytes); } } catch (ReflectiveOperationException e) { throw new IllegalArgumentException("cannot serialize Jython object of type "+clazz, e); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("cannot serialize Jython object of type "+clazz, e); } catch (SecurityException e) { throw new IllegalArgumentException("cannot serialize Jython object of type "+clazz, e); } // instead of an endless nesting loop, report a proper exception throw new IllegalArgumentException("cannot serialize Jython object of type "+obj.getClass()); } protected void serialize_collection(Collection collection, StringWriter sw, int level) { // output a list sw.write("["); serialize_sequence_elements(collection, false, sw, level+1); if(this.indent && collection.size()>0) { for(int i=0; i elts, boolean trailingComma, StringWriter sw, int level) { if(elts.size()==0) return; int count=0; if(this.indent) { sw.write("\n"); String innerindent = ""; for(int i=0; i set, StringWriter sw, int level) { if(!this.setliterals) { // output a tuple instead of a set-literal serialize_tuple(set, sw, level); return; } if(set.size()>0) { sw.write("{"); Collection output = set; if(this.indent) { // try to sort the set Set outputset = set; try { outputset = new TreeSet(set); } catch (ClassCastException x) { // ignore unsortable elements } output = outputset; } serialize_sequence_elements(output, false, sw, level+1); if(this.indent) { for(int i=0; i items = new ArrayList(length); for(int i=0; i items, StringWriter sw, int level) { sw.write("("); serialize_sequence_elements(items, items.size()==1, sw, level+1); if(this.indent && items.size()>0) { for(int i=0; i dict = new HashMap(); dict.put("data", str); dict.put("encoding", "base64"); serialize_dict(dict, sw, level); } protected void serialize_dict(Map dict, StringWriter sw,int level) { if(dict.size()==0) { sw.write("{}"); return; } int counter=0; if(this.indent) { String innerindent = " "; for(int i=0; i outputdict = dict; try { outputdict = new TreeMap(dict); } catch (ClassCastException x) { // ignore unsortable keys } for(Map.Entry e: outputdict.entrySet()) { sw.write(innerindent); serialize(e.getKey(), sw, level+1); sw.write(": "); serialize(e.getValue(), sw, level+1); counter++; if(counter e: dict.entrySet()) { serialize(e.getKey(), sw, level+1); sw.write(":"); serialize(e.getValue(), sw, level+1); counter++; if(counter0) { // we have millis df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"+tzformat); } else { // no millis df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"+tzformat); } df.setTimeZone(cal.getTimeZone()); serialize_string(df.format(cal.getTime()), sw, level); } protected void serialize_date(Date date, StringWriter sw, int level) { DateFormat df; if((date.getTime() % 1000) != 0) { // we have millis df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); } else { // no millis df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); } df.setTimeZone(TimeZone.getTimeZone("UTC")); serialize_string(df.format(date), sw, level); } protected void serialize_complex(ComplexNumber cplx, StringWriter sw, int level) { sw.write("("); serialize_primitive(cplx.real, sw, level); if(cplx.imaginary>=0) sw.write("+"); serialize_primitive(cplx.imaginary, sw, level); sw.write("j)"); } protected void serialize_uuid(UUID obj, StringWriter sw, int level) { serialize_string(obj.toString(), sw, level); } protected void serialize_bigdecimal(BigDecimal decimal, StringWriter sw, int level) { serialize_string(decimal.toEngineeringString(), sw, level); } private static final HashSet> boxedTypes; static { boxedTypes = new HashSet>(); boxedTypes.add(Boolean.class); boxedTypes.add(Character.class); boxedTypes.add(Byte.class); boxedTypes.add(Short.class); boxedTypes.add(Integer.class); boxedTypes.add(Long.class); boxedTypes.add(Float.class); boxedTypes.add(Double.class); }; protected boolean isBoxed(Class type) { return boxedTypes.contains(type); } protected void serialize_class(Object obj, StringWriter sw, int level) { Map map; IClassSerializer converter=getCustomConverter(obj.getClass()); if(null!=converter) { map = converter.convert(obj); } else { map=new HashMap(); try { // note: don't use the java.bean api, because that is not available on Android. for(Method m: obj.getClass().getMethods()) { int modifiers = m.getModifiers(); if((modifiers & Modifier.PUBLIC)!=0 && (modifiers & Modifier.STATIC)==0) { String methodname = m.getName(); int prefixlen = 0; if(methodname.equals("getClass")) continue; if(methodname.startsWith("get")) prefixlen=3; else if(methodname.startsWith("is")) prefixlen=2; else continue; Object value = m.invoke(obj); String name = methodname.substring(prefixlen); if(name.length()==1) { name = name.toLowerCase(); } else { if(!Character.isUpperCase(name.charAt(1))) { name = Character.toLowerCase(name.charAt(0)) + name.substring(1); } } map.put(name, value); } } if(this.packageInClassName) map.put("__class__", obj.getClass().getName()); else map.put("__class__", obj.getClass().getSimpleName()); } catch (IllegalAccessException e) { throw new IllegalArgumentException("couldn't introspect javabean: "+e); } catch (InvocationTargetException e) { throw new IllegalArgumentException("couldn't introspect javabean: "+e); } } serialize_dict(map, sw, level); } protected IClassSerializer getCustomConverter(Class type) { IClassSerializer converter = classToDictRegistry.get(type.getClass()); if(converter!=null) { return converter; // exact match } // check if there's a custom pickler registered for an interface or abstract base class // that this object implements or inherits from. for(Entry, IClassSerializer> x: classToDictRegistry.entrySet()) { if(x.getKey().isAssignableFrom(type)) { return x.getValue(); } } return null; } protected void serialize_primitive(Object obj, StringWriter sw, int level) { if(obj instanceof Boolean || obj.getClass()==Boolean.TYPE) { sw.write(obj.equals(Boolean.TRUE)? "True": "False"); } else if (obj instanceof Float || obj.getClass()==Float.TYPE) { Float f = (Float)obj; serialize_primitive(f.doubleValue(), sw, level); } else if (obj instanceof Double || obj.getClass()==Double.TYPE) { Double d = (Double) obj; if(d.isInfinite()) { // output a literal expression that overflows the float and results in +/-INF if(d>0.0) { sw.write("1e30000"); } else { sw.write("-1e30000"); } } else if(d.isNaN()) { // there's no literal expression for a float NaN... sw.write("{'__class__':'float','value':'nan'}"); } else { sw.write(d.toString()); } } else { sw.write(obj.toString()); } } // the repr translation table for characters 0x00-0xff private final static String[] repr_255; static { repr_255=new String[256]; for(int c=0; c<32; ++c) { repr_255[c] = String.format("\\x%02x",c); } for(char c=0x20; c<0x7f; ++c) { repr_255[c] = String.valueOf(c); } for(int c=0x7f; c<=0xa0; ++c) { repr_255[c] = String.format("\\x%02x", c); } for(char c=0xa1; c<=0xff; ++c) { repr_255[c] = String.valueOf(c); } // odd ones out: repr_255['\t'] = "\\t"; repr_255['\n'] = "\\n"; repr_255['\r'] = "\\r"; repr_255['\\'] = "\\\\"; repr_255[0xad] = "\\xad"; } protected void serialize_string(String str, StringWriter sw, int level) { // create a 'repr' string representation following the same escaping rules as python 3.x repr() does. StringBuilder b=new StringBuilder(str.length()*2); boolean containsSingleQuote=false; boolean containsQuote=false; for(int i=0; i dict; IClassSerializer converter=classToDictRegistry.get(ex.getClass()); if(null!=converter) { dict = converter.convert(ex); } else { dict = new HashMap(); if(this.packageInClassName) dict.put("__class__", ex.getClass().getName()); else dict.put("__class__", ex.getClass().getSimpleName()); dict.put("__exception__", true); dict.put("args", new String[]{ex.getMessage()}); dict.put("attributes", java.util.Collections.EMPTY_MAP); } serialize_dict(dict, sw, level); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/000077500000000000000000000000001312652634400251745ustar00rootroot00000000000000Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/Ast.java000066400000000000000000000024441312652634400265720ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) */ package net.razorvine.serpent.ast; import net.razorvine.serpent.IDictToInstance; import net.razorvine.serpent.ObjectifyVisitor; /** * Abstract syntax tree for the literal expression. This is what the parser returns. */ public class Ast { public INode root; @Override public String toString() { return "# serpent utf-8 .net\n" + root.toString(); } /** * get the actual data as Java objects. */ public Object getData() { ObjectifyVisitor v = new ObjectifyVisitor(); this.accept(v); return v.getObject(); } /** * get the actual data as Java objects. * @param dictConverter object to convert dicts to actual instances for a class, * instead of leaving them as dictionaries. Requires the __class__ key to be present * in the dict node. If it returns null, the normal processing is done. */ public Object getData(IDictToInstance dictConverter) { ObjectifyVisitor v = new ObjectifyVisitor(dictConverter); this.accept(v); return v.getObject(); } public void accept(INodeVisitor visitor) { root.accept(visitor); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/BigIntNode.java000066400000000000000000000004171312652634400300230ustar00rootroot00000000000000package net.razorvine.serpent.ast; import java.math.BigInteger; public class BigIntNode extends PrimitiveNode { public BigIntNode(BigInteger value) { super(value); } @Override public void accept(INodeVisitor visitor) { visitor.visit(this); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/BooleanNode.java000066400000000000000000000003551312652634400302270ustar00rootroot00000000000000package net.razorvine.serpent.ast; public class BooleanNode extends PrimitiveNode { public BooleanNode(boolean value) { super(value); } @Override public void accept(INodeVisitor visitor) { visitor.visit(this); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/ComplexNumberNode.java000066400000000000000000000014471312652634400314330ustar00rootroot00000000000000package net.razorvine.serpent.ast; public class ComplexNumberNode implements INode { public double real; public double imaginary; public ComplexNumberNode() {} public ComplexNumberNode(double real, double imaginary) { this.real=real; this.imaginary=imaginary; } public void accept(INodeVisitor visitor) { visitor.visit(this); } @Override public String toString() { String strReal = ""+real; String strImag = ""+imaginary; if(imaginary>=0) return String.format("(%s+%sj)", strReal, strImag); return String.format("(%s%sj)", strReal, strImag); } @Override public boolean equals(Object obj) { if(!(obj instanceof ComplexNumberNode)) return false; ComplexNumberNode other = (ComplexNumberNode) obj; return real==other.real && imaginary==other.imaginary; } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/DictNode.java000066400000000000000000000004431312652634400275310ustar00rootroot00000000000000package net.razorvine.serpent.ast; public class DictNode extends UnorderedSequenceNode { @Override public void accept(INodeVisitor visitor) { visitor.visit(this); } @Override public char getOpenChar() { return '{'; } @Override public char getCloseChar() { return '}'; } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/DoubleNode.java000066400000000000000000000003501312652634400300550ustar00rootroot00000000000000package net.razorvine.serpent.ast; public class DoubleNode extends PrimitiveNode { public DoubleNode(double value) { super(value); } @Override public void accept(INodeVisitor visitor) { visitor.visit(this); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/INode.java000066400000000000000000000002241312652634400270330ustar00rootroot00000000000000package net.razorvine.serpent.ast; public interface INode { String toString(); boolean equals(Object obj); void accept(INodeVisitor visitor); } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/INodeVisitor.java000066400000000000000000000006701312652634400304200ustar00rootroot00000000000000package net.razorvine.serpent.ast; public interface INodeVisitor { void visit(ComplexNumberNode complex); void visit(DictNode dict); void visit(ListNode list); void visit(NoneNode none); void visit(IntegerNode value); void visit(LongNode value); void visit(DoubleNode value); void visit(BooleanNode value); void visit(StringNode value); void visit(SetNode setnode); void visit(TupleNode tuple); void visit(BigIntNode value); } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/IntegerNode.java000066400000000000000000000003501312652634400302400ustar00rootroot00000000000000package net.razorvine.serpent.ast; public class IntegerNode extends PrimitiveNode { public IntegerNode(int value) { super(value); } @Override public void accept(INodeVisitor visitor) { visitor.visit(this); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/KeyValueNode.java000066400000000000000000000013661312652634400304000ustar00rootroot00000000000000package net.razorvine.serpent.ast; public class KeyValueNode implements INode { public INode key; public INode value; public KeyValueNode() {} public KeyValueNode(INode key, INode value) { this.key = key; this.value = value; } @Override public String toString() { return String.format("%s:%s", key, value); } public void accept(INodeVisitor visitor) { throw new NoSuchMethodError("don't visit a keyvaluenode"); } @Override public boolean equals(Object obj) { if(!(obj instanceof KeyValueNode)) return false; KeyValueNode other = (KeyValueNode) obj; return key.equals(other.key) && value.equals(other.value); } @Override public int hashCode() { return key.hashCode() ^ (1000000007 * value.hashCode()); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/ListNode.java000066400000000000000000000004321312652634400275570ustar00rootroot00000000000000package net.razorvine.serpent.ast; public class ListNode extends SequenceNode { @Override public void accept(INodeVisitor visitor) { visitor.visit(this); } @Override public char getOpenChar() { return '['; } @Override public char getCloseChar() { return ']'; } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/LongNode.java000066400000000000000000000003411312652634400275420ustar00rootroot00000000000000package net.razorvine.serpent.ast; public class LongNode extends PrimitiveNode { public LongNode(long value) { super(value); } @Override public void accept(INodeVisitor visitor) { visitor.visit(this); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/NoneNode.java000066400000000000000000000004271312652634400275470ustar00rootroot00000000000000package net.razorvine.serpent.ast; public class NoneNode implements INode { public static NoneNode Instance = new NoneNode(); private NoneNode() { } public String toString() { return "None"; } public void accept(INodeVisitor visitor) { visitor.visit(this); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/PrimitiveNode.java000066400000000000000000000031721312652634400306200ustar00rootroot00000000000000package net.razorvine.serpent.ast; public abstract class PrimitiveNode implements INode, Comparable { public T value; public PrimitiveNode(T value) { this.value=value; } @Override public int hashCode() { return value!=null? value.hashCode() : 0; } @Override public boolean equals(Object obj) { return (obj instanceof PrimitiveNode) && value.equals(((PrimitiveNode)obj).value); } public int compareTo(T other) { return 0; } public boolean equals(PrimitiveNode other) { return this.value.equals(other.value); } @Override public String toString() { if(value instanceof String) { StringBuilder sb=new StringBuilder(); sb.append("'"); String strValue = (String)value; for(char c: strValue.toCharArray()) { switch(c) { case '\\': sb.append("\\\\"); break; case '\'': sb.append("\\'"); break; case '\b': sb.append("\\b"); break; case '\f': sb.append("\\f"); break; case '\n': sb.append("\\n"); break; case '\r': sb.append("\\r"); break; case '\t': sb.append("\\t"); break; default: sb.append(c); break; } } sb.append("'"); return sb.toString(); } else if(value instanceof Boolean) { return value.equals(Boolean.TRUE)? "True": "False"; } else if(value instanceof Float || value instanceof Double) { String d = value.toString(); if(d.indexOf('.')<=0 && d.indexOf('e')<=0 && d.indexOf('E')<=0) d+=".0"; return d; } else return value.toString(); } public abstract void accept(INodeVisitor visitor); } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/SequenceNode.java000066400000000000000000000020131312652634400304110ustar00rootroot00000000000000package net.razorvine.serpent.ast; import java.util.ArrayList; import java.util.List; public abstract class SequenceNode implements INode { public List elements = new ArrayList(); public abstract char getOpenChar(); public abstract char getCloseChar(); @Override public int hashCode() { int hashCode = 0; for(INode elt: elements) hashCode += 1000000007 * elt.hashCode(); return hashCode; } @Override public boolean equals(Object obj) { if(!(obj instanceof SequenceNode)) return false; SequenceNode other = (SequenceNode)obj; return elements.equals(other.elements); } @Override public String toString() { StringBuilder sb=new StringBuilder(); sb.append(getOpenChar()); if(elements != null) { for(INode elt: elements) { sb.append(elt.toString()); sb.append(','); } } if(elements.size()>0) sb.deleteCharAt(sb.length()-1); // remove last comma sb.append(getCloseChar()); return sb.toString(); } public abstract void accept(INodeVisitor visitor); } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/SetNode.java000066400000000000000000000004421312652634400274000ustar00rootroot00000000000000package net.razorvine.serpent.ast; public class SetNode extends UnorderedSequenceNode { @Override public void accept(INodeVisitor visitor) { visitor.visit(this); } @Override public char getOpenChar() { return '{'; } @Override public char getCloseChar() { return '}'; } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/StringNode.java000066400000000000000000000003501312652634400301110ustar00rootroot00000000000000package net.razorvine.serpent.ast; public class StringNode extends PrimitiveNode { public StringNode(String value) { super(value); } @Override public void accept(INodeVisitor visitor) { visitor.visit(this); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/TupleNode.java000066400000000000000000000012121312652634400277320ustar00rootroot00000000000000package net.razorvine.serpent.ast; public class TupleNode extends SequenceNode { @Override public String toString() { StringBuilder sb=new StringBuilder(); sb.append(getOpenChar()); if(elements != null) { for(INode elt: elements) { sb.append(elt.toString()); sb.append(","); } } if(elements.size()>1) sb.deleteCharAt(sb.length()-1); // remove last comma sb.append(getCloseChar()); return sb.toString(); } @Override public void accept(INodeVisitor visitor) { visitor.visit(this); } @Override public char getOpenChar() { return '('; } @Override public char getCloseChar() { return ')'; } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/ast/UnorderedSequenceNode.java000066400000000000000000000007331312652634400322700ustar00rootroot00000000000000package net.razorvine.serpent.ast; import java.util.HashSet; import java.util.Set; abstract class UnorderedSequenceNode extends SequenceNode { @Override public boolean equals(Object obj) { if(!(obj instanceof UnorderedSequenceNode)) return false; Set set1 = elementsAsSet(); Set set2 = ((UnorderedSequenceNode)obj).elementsAsSet(); return set1.equals(set2); } public Set elementsAsSet() { return new HashSet(elements); } } Serpent-serpent-1.23/java/src/main/java/net/razorvine/serpent/package-info.java000066400000000000000000000004651312652634400276010ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) * @version 1.23 */ package net.razorvine.serpent; Serpent-serpent-1.23/java/src/test/000077500000000000000000000000001312652634400172325ustar00rootroot00000000000000Serpent-serpent-1.23/java/src/test/java/000077500000000000000000000000001312652634400201535ustar00rootroot00000000000000Serpent-serpent-1.23/java/src/test/java/SerpentExample.java000066400000000000000000000075201312652634400237560ustar00rootroot00000000000000 import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.Serializable; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import net.razorvine.serpent.DebugVisitor; import net.razorvine.serpent.LibraryVersion; import net.razorvine.serpent.Parser; import net.razorvine.serpent.Serializer; import net.razorvine.serpent.ast.Ast; public class SerpentExample { enum Foo { Foobar, Jarjar } public static void main(String[] args) { SerpentExample t=new SerpentExample(); try { t.run(); } catch (IOException e) { e.printStackTrace(); } } @SuppressWarnings("unchecked") public void run() throws IOException { // some example use of Serpent System.out.println("Using serpent library version "+LibraryVersion.VERSION); Map data = new HashMap(); data.put("tuple", new int[] { 1,2,3 }); data.put("date", new java.util.Date()); Set set = new HashSet(); set.add("c"); set.add("b"); set.add("a"); data.put("set", set); data.put("class", new SampleClass("Sally", 26)); // serialize data structure to bytes Serializer serpent = new Serializer(true, true, false); byte[] ser = serpent.serialize(data); // print it on the screen, but normally you'd store byte bytes in a file or transfer them across a network connection System.out.println("Serialized:"); System.out.println(new String(ser, "utf-8")); // parse the serialized bytes back into an abstract syntax tree of the datastructure Parser parser = new Parser(); Ast ast = parser.parse(ser); System.out.println("\nParsed AST:"); System.out.println(ast.root.toString()); // print debug representation DebugVisitor dv = new DebugVisitor(); ast.accept(dv); System.out.println("DEBUG string representation:"); System.out.println(dv.toString()); // turn the Ast into regular Java objects Map dict = (Map)ast.getData(); // You can get the data out of the Ast manually as well, by using the supplied visitor: // ObjectifyVisitor visitor = new ObjectifyVisitor(); // ast.accept(visitor); // Map dict = (Map) visitor.getObject(); // print the results System.out.println("PARSED results:"); System.out.print("tuple items: "); Object[] tuple = (Object[]) dict.get("tuple"); for(Object o: tuple) System.out.print(" "+o.toString()+","); System.out.println(""); System.out.println("date: "+dict.get("date")); System.out.print("set items: "); Set set2 = (Set) dict.get("set"); for(Object o: set2) System.out.print(" "+o.toString()+","); System.out.println(""); System.out.println("class attributes:"); Map clazz = (Map) dict.get("class"); // custom classes are serialized as dicts System.out.println(" type: "+clazz.get("__class__")); System.out.println(" name: "+clazz.get("name")); System.out.println(" age: "+clazz.get("age")); System.out.println(""); // parse and print the example file File testdatafile = new File("test/testserpent.utf8.bin"); ser = new byte[(int) testdatafile.length()]; FileInputStream fis=new FileInputStream(testdatafile); DataInputStream dis = new DataInputStream(fis); dis.readFully(ser); dis.close(); fis.close(); ast = parser.parse(ser); dv = new DebugVisitor(); ast.accept(dv); System.out.println("DEBUG string representation of the test file:"); System.out.println(dv.toString()); } public class SampleClass implements Serializable { private static final long serialVersionUID = -782424804184940436L; int a; String n; public SampleClass(String name, int age) { a=age; n=name; } public int getAge() { return a; } public String getName() { return n; } } } Serpent-serpent-1.23/java/src/test/java/net/000077500000000000000000000000001312652634400207415ustar00rootroot00000000000000Serpent-serpent-1.23/java/src/test/java/net/razorvine/000077500000000000000000000000001312652634400227605ustar00rootroot00000000000000Serpent-serpent-1.23/java/src/test/java/net/razorvine/serpent/000077500000000000000000000000001312652634400244405ustar00rootroot00000000000000Serpent-serpent-1.23/java/src/test/java/net/razorvine/serpent/test/000077500000000000000000000000001312652634400254175ustar00rootroot00000000000000Serpent-serpent-1.23/java/src/test/java/net/razorvine/serpent/test/CycleTest.java000066400000000000000000000053141312652634400301640ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) */ package net.razorvine.serpent.test; import java.util.HashMap; import java.util.LinkedList; import net.razorvine.serpent.Parser; import net.razorvine.serpent.Serializer; import org.junit.Test; import static org.junit.Assert.*; public class CycleTest { @Test public void testTupleOk() { Serializer ser = new Serializer(); int[] t = new int[] {1,2,3}; Object[] d = new Object[] {t,t,t}; byte[] data = ser.serialize(d); Parser parser = new Parser(); parser.parse(data); } @Test public void testListOk() { Serializer ser = new Serializer(); LinkedList t = new LinkedList(); t.add(1); t.add(2); t.add(3); LinkedList d = new LinkedList(); d.add(t); d.add(t); d.add(t); byte[] data = ser.serialize(d); Parser parser = new Parser(); parser.parse(data); } @Test public void testDictOk() { Serializer ser = new Serializer(); HashMap t = new HashMap(); t.put("a", 1); HashMap d = new HashMap(); d.put("x", t); d.put("y", t); d.put("z", t); byte[] data = ser.serialize(d); Parser parser = new Parser(); parser.parse(data); } @Test(expected=IllegalArgumentException.class) public void testListCycle() { Serializer ser = new Serializer(); LinkedList d = new LinkedList(); d.add(1); d.add(2); d.add(d); ser.serialize(d); } @Test(expected=IllegalArgumentException.class) public void testDictCycle() { Serializer ser = new Serializer(); HashMap d = new HashMap(); d.put("x", 1); d.put("y", 2); d.put("z", d); ser.serialize(d); } @Test(expected=IllegalArgumentException.class) public void testClassCycle() { Serializer ser = new Serializer(); SerializationHelperClass thing = new SerializationHelperClass(); thing.i = 42; thing.s = "hello"; thing.x = 99; thing.obj = thing; ser.serialize(thing); } @Test public void testMaxLevel() { Serializer ser = new Serializer(); assertEquals(500, ser.maximumLevel); Object[] array = new Object[] { "level1", new Object[] { "level2", new Object[] { "level3", new Object[] { "level 4" } } } }; ser.maximumLevel = 4; ser.serialize(array); // should work ser.maximumLevel = 3; try { ser.serialize(array); fail("should fail"); } catch(IllegalArgumentException x) { assertTrue(x.getMessage().contains("too deep")); } } } Serpent-serpent-1.23/java/src/test/java/net/razorvine/serpent/test/ParserTest.java000066400000000000000000000704501312652634400303640ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) */ package net.razorvine.serpent.test; import static org.junit.Assert.*; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Set; import net.razorvine.serpent.ObjectifyVisitor; import net.razorvine.serpent.ParseException; import net.razorvine.serpent.Parser; import net.razorvine.serpent.SeekableStringReader; import net.razorvine.serpent.Serializer; import net.razorvine.serpent.ast.*; import org.junit.Ignore; import org.junit.Test; public class ParserTest { @Test public void TestBasic() { Parser p = new Parser(); assertNull(p.parse((String)null).root); assertNull(p.parse("").root); assertNotNull(p.parse("# comment\n42\n").root); } @Test public void TestComments() { Parser p = new Parser(); Ast ast = p.parse("[ 1, 2 ]"); // no header whatsoever ObjectifyVisitor visitor = new ObjectifyVisitor(); ast.accept(visitor); Object obj = visitor.getObject(); List expected = new ArrayList(); expected.add(1); expected.add(2); assertEquals(expected, obj); expected = new LinkedList(); expected.add(1); expected.add(2); assertEquals(expected, obj); ast = p.parse("# serpent utf-8 python2.7\n"+ "[ 1, 2,\n"+ " # some comments here\n"+ " 3, 4] # more here\n"+ "# and here.\n"); visitor = new ObjectifyVisitor(); ast.accept(visitor); obj = visitor.getObject(); expected = new LinkedList(); expected.add(1); expected.add(2); expected.add(3); expected.add(4); assertEquals(expected, obj); } @Test public void TestPrimitives() { Parser p = new Parser(); assertEquals(new IntegerNode(42), p.parse("42").root); assertEquals(new IntegerNode(-42), p.parse("-42").root); assertEquals(new DoubleNode(42.331), p.parse("42.331").root); assertEquals(new DoubleNode(-42.331), p.parse("-42.331").root); assertEquals(new DoubleNode(-1.2e19), p.parse("-1.2e+19").root); assertEquals(new DoubleNode(-1.2e-19), p.parse("-1.2e-19").root); assertEquals(new DoubleNode(0.0004), p.parse("4e-4").root); assertEquals(new DoubleNode(40000), p.parse("4e4").root); assertEquals(new BooleanNode(true), p.parse("True").root); assertEquals(new BooleanNode(false), p.parse("False").root); assertEquals(NoneNode.Instance, p.parse("None").root); // long ints assertEquals(new BigIntNode(new BigInteger("123456789123456789123456789")), p.parse("123456789123456789123456789").root); assertFalse(new LongNode(52).equals(p.parse("52").root)); assertEquals(new LongNode(123456789123456789L), p.parse("123456789123456789").root); assertEquals(BigIntNode.class, p.parse("12345678912345678912345678912345678978571892375798273578927389758378467693485903859038593453475897349587348957893457983475983475893475893785732957398475").root.getClass()); } @Test public void TestWeirdFloats() { Parser p = new Parser(); DoubleNode d = (DoubleNode) p.parse("1e30000").root; assertTrue(Double.isInfinite(d.value)); assertTrue(d.value > 0.0); d = (DoubleNode) p.parse("-1e30000").root; assertTrue(Double.isInfinite(d.value)); assertTrue(d.value < 0.0); TupleNode tuple = (TupleNode) p.parse("(1e30000,-1e30000,{'__class__':'float','value':'nan'})").root; assertEquals(3, tuple.elements.size()); d = (DoubleNode) tuple.elements.get(0); assertTrue(Double.isInfinite(d.value)); d = (DoubleNode) tuple.elements.get(1); assertTrue(Double.isInfinite(d.value)); assertTrue(d.value < 0.0); d = (DoubleNode) tuple.elements.get(2); assertTrue(Double.isNaN(d.value)); ComplexNumberNode c = (ComplexNumberNode) p.parse("(1e30000-1e30000j)").root; assertTrue(Double.isInfinite(c.real)); assertTrue(c.real>0.0); assertTrue(Double.isInfinite(c.imaginary)); assertTrue(c.imaginary<0.0); } @Test public void TestFloatPrecision() { Parser p = new Parser(); Serializer serpent = new Serializer(); byte[] ser = serpent.serialize(1.2345678987654321); DoubleNode dv = (DoubleNode) p.parse(ser).root; assertEquals(Double.valueOf(1.2345678987654321), dv.value); ser = serpent.serialize(5555.12345678987656); dv = (DoubleNode) p.parse(ser).root; assertEquals(Double.valueOf(5555.12345678987656), dv.value); ser = serpent.serialize(98765432123456.12345678987656); dv = (DoubleNode) p.parse(ser).root; assertEquals(Double.valueOf(98765432123456.12345678987656), dv.value); ser = serpent.serialize(98765432123456.12345678987656e+44); dv = (DoubleNode) p.parse(ser).root; assertEquals(Double.valueOf(98765432123456.12345678987656e+44), dv.value); ser = serpent.serialize(-98765432123456.12345678987656e-44); dv = (DoubleNode) p.parse(ser).root; assertEquals(Double.valueOf(-98765432123456.12345678987656e-44), dv.value); } @Test public void TestEquality() { INode n1, n2; n1 = new IntegerNode(42); n2 = new IntegerNode(42); assertEquals(n1, n2); n2 = new IntegerNode(43); assertFalse(n1.equals(n2)); n1 = new StringNode("foo"); n2 = new StringNode("foo"); assertEquals(n1, n2); n2 = new StringNode("bar"); assertFalse(n1.equals(n2)); ComplexNumberNode cn1 = new ComplexNumberNode(); cn1.real=1.1; cn1.imaginary=2.2; ComplexNumberNode cn2 = new ComplexNumberNode(); cn2.real=1.1; cn2.imaginary=2.2; assertEquals(cn1, cn2); cn2 = new ComplexNumberNode(); cn2.real=1.1; cn2.imaginary=3.3; assertFalse(cn1.equals(cn2)); KeyValueNode kvn1=new KeyValueNode(); kvn1.key = new IntegerNode(42); kvn1.value = new IntegerNode(42); KeyValueNode kvn2=new KeyValueNode(); kvn2.key=new IntegerNode(42); kvn2.value=new IntegerNode(42); assertEquals(kvn1, kvn2); kvn1=new KeyValueNode(); kvn1.key=new IntegerNode(43); kvn1.value=new IntegerNode(43); assertFalse(kvn1.equals(kvn2)); n1=NoneNode.Instance; n2=NoneNode.Instance; assertEquals(n1, n2); n2=new IntegerNode(42); assertFalse(n1.equals(n2)); DictNode dn1 = new DictNode(); kvn1 = new KeyValueNode(); kvn1.key = new IntegerNode(42); kvn1.value = new IntegerNode(42); dn1.elements.add(kvn1); DictNode dn2=new DictNode(); kvn1 = new KeyValueNode(); kvn1.key =new IntegerNode(42); kvn1.value = new IntegerNode(42); dn2.elements.add(kvn1); assertEquals(dn1, dn2); dn2=new DictNode(); kvn1 = new KeyValueNode(); kvn1.key = new IntegerNode(42); kvn1.value = new IntegerNode(43); dn2.elements.add(kvn1); assertFalse(dn1.equals(dn2)); ListNode ln1=new ListNode(); ln1.elements.add(new IntegerNode(42)); ListNode ln2=new ListNode(); ln2.elements.add(new IntegerNode(42)); assertEquals(ln1,ln2); ln2=new ListNode(); ln2.elements.add(new IntegerNode(43)); assertFalse(ln1.equals(ln2)); SetNode sn1=new SetNode(); sn1.elements.add(new IntegerNode(42)); SetNode sn2=new SetNode(); sn2.elements.add(new IntegerNode(42)); assertEquals(sn1,sn2); sn2=new SetNode(); sn2.elements.add(new IntegerNode(43)); assertFalse(sn1.equals(sn2)); TupleNode tn1=new TupleNode(); tn1.elements.add(new IntegerNode(42)); TupleNode tn2=new TupleNode(); tn2.elements.add(new IntegerNode(42)); assertEquals(tn1,tn2); tn2=new TupleNode(); tn2.elements.add(new IntegerNode(43)); assertFalse(tn1.equals(tn2)); } @Test public void TestPrintSingle() { Parser p = new Parser(); // primitives assertEquals("42", p.parse("42").root.toString()); assertEquals("-42.331", p.parse("-42.331").root.toString()); assertEquals("-42.0", p.parse("-42.0").root.toString()); assertEquals("-2.0E20", p.parse("-2E20").root.toString()); assertEquals("2.0", p.parse("2.0").root.toString()); assertEquals("1.2E19", p.parse("1.2e19").root.toString()); assertEquals("True", p.parse("True").root.toString()); assertEquals("'hello'", p.parse("'hello'").root.toString()); assertEquals("'\\n'", p.parse("'\n'").root.toString()); assertEquals("'\\''", p.parse("'\\''").root.toString()); assertEquals("'\"'", p.parse("'\\\"'").root.toString()); assertEquals("'\"'", p.parse("'\"'").root.toString()); assertEquals("'\\\\'", p.parse("'\\\\'").root.toString()); assertEquals("None", p.parse("None").root.toString()); String ustr = "'\u20ac\u2603'"; assertEquals(ustr, p.parse(ustr).root.toString()); // complex assertEquals("(0.0+2.0j)", p.parse("2j").root.toString()); assertEquals("(-1.1-2.2j)", p.parse("(-1.1-2.2j)").root.toString()); assertEquals("(1.1+2.2j)", p.parse("(1.1+2.2j)").root.toString()); // long int assertEquals("123456789123456789123456789", p.parse("123456789123456789123456789").root.toString()); } @Test public void TestPrintSeq() { Parser p=new Parser(); //tuple assertEquals("()", p.parse("()").root.toString()); assertEquals("(42,)", p.parse("(42,)").root.toString()); assertEquals("(42,43)", p.parse("(42,43)").root.toString()); // list assertEquals("[]", p.parse("[]").root.toString()); assertEquals("[42]", p.parse("[42]").root.toString()); assertEquals("[42,43]", p.parse("[42,43]").root.toString()); // set assertEquals("{42}", p.parse("{42}").root.toString()); assertEquals("{42,43}", p.parse("{42,43,43,43}").root.toString()); // dict assertEquals("{}", p.parse("{}").root.toString()); assertEquals("{'a':42}", p.parse("{'a': 42}").root.toString()); String result = p.parse("{'a': 42, 'b': 43}").root.toString(); assertTrue(result.equals("{'a':42,'b':43}") || result.equals("{'b':43,'a':42}")); result = p.parse("{'a': 42, 'b': 43, 'b': 44, 'b': 45}").root.toString(); assertTrue(result.equals("{'a':42,'b':45}") || result.equals("{'b':45,'a':42}")); } @Test(expected=ParseException.class) public void TestInvalidPrimitive1() { new Parser().parse("1+2"); } @Test(expected=ParseException.class) public void TestInvalidPrimitive2() { new Parser().parse("1-2"); } @Test(expected=ParseException.class) public void TestInvalidPrimitive3() { new Parser().parse("1.1+2.2"); } @Test(expected=ParseException.class) public void TestInvalidPrimitive4() { new Parser().parse("1.1-2.2"); } @Test(expected=ParseException.class) public void TestInvalidPrimitive5() { new Parser().parse("True+2"); } @Test(expected=ParseException.class) public void TestInvalidPrimitive6() { new Parser().parse("False-2"); } @Test(expected=ParseException.class) public void TestInvalidPrimitive7() { new Parser().parse("3j+2"); } @Test(expected=ParseException.class) public void TestInvalidPrimitive8() { new Parser().parse("3j-2"); } @Test(expected=ParseException.class) public void TestInvalidPrimitive9() { new Parser().parse("None+2"); } @Test(expected=ParseException.class) public void TestInvalidPrimitive10() { new Parser().parse("None-2"); } @Test(expected=ParseException.class) public void TestInvalidTuple1() { new Parser().parse("(42,43]"); } @Test(expected=ParseException.class) public void TestInvalidTuple2() { new Parser().parse("()@"); } @Test(expected=ParseException.class) public void TestInvalidTuple3() { new Parser().parse("(42,43)@"); } @Test(expected=ParseException.class) public void TestInvalidList2() { new Parser().parse("[42,43}"); } @Test(expected=ParseException.class) public void TestInvalidList3() { new Parser().parse("[]@"); } @Test(expected=ParseException.class) public void TestInvalidList4() { new Parser().parse("[42,43]@"); } @Test(expected=ParseException.class) public void TestInvalidSet2() { new Parser().parse("{42,43]"); } @Test(expected=ParseException.class) public void TestInvalidSet3() { new Parser().parse("{42,43}@"); } @Test(expected=ParseException.class) public void TestInvalidDict2() { new Parser().parse("{'key': 42]"); } @Test(expected=ParseException.class) public void TestInvalidDict3() { new Parser().parse("{}@"); } @Test(expected=ParseException.class) public void TestInvalidDict4() { new Parser().parse("{'key': 42}@"); } @Test public void TestComplex() { Parser p = new Parser(); ComplexNumberNode cplx = new ComplexNumberNode(); cplx.real = 4.2; cplx.imaginary = 3.2; ComplexNumberNode cplx2 = new ComplexNumberNode(); cplx2.real = 4.2; cplx2.imaginary = 99; assertFalse(cplx.equals(cplx2)); cplx2.imaginary = 3.2; assertEquals(cplx, cplx2); assertEquals(cplx, p.parse("(4.2+3.2j)").root); cplx.real = 0; assertEquals(cplx, p.parse("(0+3.2j)").root); assertEquals(cplx, p.parse("3.2j").root); assertEquals(cplx, p.parse("+3.2j").root); cplx.imaginary = -3.2; assertEquals(cplx, p.parse("-3.2j").root); cplx.real = -9.9; assertEquals(cplx, p.parse("(-9.9-3.2j)").root); cplx.real = 2; cplx.imaginary = 3; assertEquals(cplx, p.parse("(2+3j)").root); cplx.imaginary = -3; assertEquals(cplx, p.parse("(2-3j)").root); cplx.real = 0; assertEquals(cplx, p.parse("-3j").root); cplx.real = -3.2e32; cplx.imaginary = -9.9e44; assertEquals(cplx, p.parse("(-3.2e32-9.9e44j)").root); assertEquals(cplx, p.parse("(-3.2e+32-9.9e+44j)").root); assertEquals(cplx, p.parse("(-3.2e32 -9.9e44j)").root); assertEquals(cplx, p.parse("(-3.2e+32 -9.9e+44j)").root); cplx.imaginary = 9.9e44; assertEquals(cplx, p.parse("(-3.2e32+9.9e44j)").root); assertEquals(cplx, p.parse("(-3.2e+32+9.9e+44j)").root); cplx.real = -3.2e-32; cplx.imaginary = -9.9e-44; assertEquals(cplx, p.parse("(-3.2e-32-9.9e-44j)").root); } @Test public void TestComplexPrecision() { Parser p = new Parser(); ComplexNumberNode cv = (ComplexNumberNode)p.parse("(98765432123456.12345678987656+665544332211.9998877665544j)").root; assertEquals(Double.valueOf(98765432123456.12345678987656), cv.real, 0); assertEquals(Double.valueOf(665544332211.9998877665544), cv.imaginary, 0); cv = (ComplexNumberNode)p.parse("(98765432123456.12345678987656-665544332211.9998877665544j)").root; assertEquals(Double.valueOf(98765432123456.12345678987656), cv.real, 0); assertEquals(Double.valueOf(-665544332211.9998877665544), cv.imaginary, 0); cv = (ComplexNumberNode)p.parse("(98765432123456.12345678987656e+33+665544332211.9998877665544e+44j)").root; assertEquals(Double.valueOf(98765432123456.12345678987656e+33), cv.real, 0); assertEquals(Double.valueOf(665544332211.9998877665544e+44), cv.imaginary, 0); cv = (ComplexNumberNode)p.parse("(-98765432123456.12345678987656e+33-665544332211.9998877665544e+44j)").root; assertEquals(Double.valueOf(-98765432123456.12345678987656e+33), cv.real, 0); assertEquals(Double.valueOf(-665544332211.9998877665544e+44), cv.imaginary, 0); } @Test public void TestPrimitivesStuffAtEnd() { Parser p = new Parser(); assertEquals(new IntegerNode(42), p.parseSingle(new SeekableStringReader("42@"))); assertEquals(new DoubleNode(42.331), p.parseSingle(new SeekableStringReader("42.331@"))); assertEquals(new BooleanNode(true), p.parseSingle(new SeekableStringReader("True@"))); assertEquals(NoneNode.Instance, p.parseSingle(new SeekableStringReader("None@"))); ComplexNumberNode cplx = new ComplexNumberNode(); cplx.real=4; cplx.imaginary=3; assertEquals(cplx, p.parseSingle(new SeekableStringReader("(4+3j)@"))); cplx.real=0; assertEquals(cplx, p.parseSingle(new SeekableStringReader("3j@"))); } @Test public void TestStrings() { Parser p = new Parser(); assertEquals(new StringNode("hello"), p.parse("'hello'").root); assertEquals(new StringNode("hello"), p.parse("\"hello\"").root); assertEquals(new StringNode("\\"), p.parse("'\\\\'").root); assertEquals(new StringNode("\\"), p.parse("\"\\\\\"").root); assertEquals(new StringNode("'"), p.parse("\"'\"").root); assertEquals(new StringNode("\""), p.parse("'\"'").root); assertEquals(new StringNode("tab\tnewline\n."), p.parse("'tab\\tnewline\\n.'").root); } @Test public void TestUnicode() throws UnsupportedEncodingException { Parser p = new Parser(); String str = "'\u20ac\u2603'"; assertEquals(0x20ac, str.charAt(1)); assertEquals(0x2603, str.charAt(2)); byte[] bytes = str.getBytes("utf-8"); String value = "\u20ac\u2603"; assertEquals(new StringNode(value), p.parse(str).root); assertEquals(new StringNode(value), p.parse(bytes).root); } @Test public void testLongUnicodeRoundtrip() { char[] chars64k = new char[65536]; for(int i=0; i<=65535; ++i) chars64k[i]=(char)i; String str64k= new String(chars64k); Serializer ser=new Serializer(); byte[] data = ser.serialize(str64k); assertTrue(data.length > chars64k.length); Parser p=new Parser(); String result = (String)p.parse(data).getData(); assertEquals(str64k, result); } @Test public void TestWhitespace() { Parser p = new Parser(); assertEquals(new IntegerNode(42), p.parse(" 42 ").root); assertEquals(new IntegerNode(42), p.parse(" 42 ").root); assertEquals(new IntegerNode(42), p.parse("\t42\r\n").root); assertEquals(new IntegerNode(42), p.parse(" \t 42 \r \n ").root); assertEquals(new StringNode(" string value "), p.parse(" ' string value ' ").root); try { p.parse(" ( 42 , 43 , "); // missing tuple close ) fail("expected parse error"); } catch (ParseException x) { //ok } try { p.parse(" [ 42 , 43 , "); // missing list close ) fail("expected parse error"); } catch (ParseException x) { //ok } try { p.parse(" { 42 , 43 , "); // missing set close ) fail("expected parse error"); } catch (ParseException x) { //ok } try { p.parse(" { 'a' : 4 2 , "); // missing dict close ) fail("expected parse error"); } catch (ParseException x) { //ok } Ast ast = p.parse(" ( 42 , ( 'x', 'y' ) ) "); TupleNode tuple = (TupleNode) ast.root; assertEquals(new IntegerNode(42), tuple.elements.get(0)); tuple = (TupleNode) tuple.elements.get(1); assertEquals(new StringNode("x"), tuple.elements.get(0)); assertEquals(new StringNode("y"), tuple.elements.get(1)); p.parse(" ( 52 , ) "); p.parse(" [ 52 ] "); p.parse(" { 'a' : 42 } "); p.parse(" { 52 } "); } @Test public void TestTuple() { Parser p = new Parser(); TupleNode tuple = new TupleNode(); TupleNode tuple2 = new TupleNode(); assertEquals(tuple, tuple2); tuple.elements.add(new IntegerNode(42)); tuple2.elements.add(new IntegerNode(99)); assertFalse(tuple.equals(tuple2)); tuple2.elements.clear(); tuple2.elements.add(new IntegerNode(42)); assertEquals(tuple, tuple2); tuple2.elements.add(new IntegerNode(43)); tuple2.elements.add(new IntegerNode(44)); assertFalse(tuple.equals(tuple2)); assertEquals(new TupleNode(), p.parse("()").root); assertEquals(tuple, p.parse("(42,)").root); assertEquals(tuple2, p.parse("( 42,43, 44 )").root); } @Test public void TestList() { Parser p = new Parser(); ListNode list = new ListNode(); ListNode list2 = new ListNode(); assertEquals(list, list2); list.elements.add(new IntegerNode(42)); list2.elements.add(new IntegerNode(99)); assertFalse(list.equals(list2)); list2.elements.clear(); list2.elements.add(new IntegerNode(42)); assertEquals(list, list2); list2.elements.add(new IntegerNode(43)); list2.elements.add(new IntegerNode(44)); assertFalse(list.equals(list2)); assertEquals(new ListNode(), p.parse("[]").root); assertEquals(list, p.parse("[42]").root); assertEquals(list2, p.parse("[ 42,43, 44 ]").root); } @Test public void TestSet() { Parser p = new Parser(); SetNode set1 = new SetNode(); SetNode set2 = new SetNode(); assertEquals(set1, set2); set1.elements.add(new IntegerNode(42)); set2.elements.add(new IntegerNode(99)); assertFalse(set1.equals(set2)); set2.elements.clear(); set2.elements.add(new IntegerNode(42)); assertEquals(set1, set2); set2.elements.add(new IntegerNode(43)); set2.elements.add(new IntegerNode(44)); assertFalse(set1.equals(set2)); assertEquals(set1, p.parse("{42}").root); assertEquals(set2, p.parse("{ 42,43, 44 }").root); set1 = (SetNode) p.parse("{'first','second','third','fourth','fifth','second', 'first', 'third', 'third' }").root; assertTrue(set1.elements.contains(new StringNode("first"))); assertTrue(set1.elements.contains(new StringNode("second"))); assertTrue(set1.elements.contains(new StringNode("third"))); assertTrue(set1.elements.contains(new StringNode("fourth"))); assertTrue(set1.elements.contains(new StringNode("fifth"))); assertEquals(5, set1.elements.size()); } @Test public void TestDict() { Parser p = new Parser(); DictNode dict1 = new DictNode(); DictNode dict2 = new DictNode(); assertEquals(dict1, dict2); KeyValueNode kv1 = new KeyValueNode(); kv1.key = new StringNode("key"); kv1.value = new IntegerNode(42); KeyValueNode kv2 = new KeyValueNode(); kv2.key=new StringNode("key"); kv2.value=new IntegerNode(99); assertFalse(kv1.equals(kv2)); kv2.value = new IntegerNode(42); assertEquals(kv1, kv2); kv1=new KeyValueNode(); kv1.key=new StringNode("key1"); kv1.value=new IntegerNode(42); dict1.elements.add(kv1); kv1=new KeyValueNode(); kv1.key=new StringNode("key1"); kv1.value=new IntegerNode(99); dict2.elements.add(kv1); assertFalse(dict1.equals(dict2)); dict2.elements.clear(); kv1=new KeyValueNode(); kv1.key=new StringNode("key1"); kv1.value=new IntegerNode(42); dict2.elements.add(kv1); assertEquals(dict1, dict2); kv1=new KeyValueNode(); kv1.key=new StringNode("key2"); kv1.value=new IntegerNode(43); dict2.elements.add(kv1); kv1=new KeyValueNode(); kv1.key=new StringNode("key3"); kv1.value=new IntegerNode(44); dict2.elements.add(kv1); assertFalse(dict1.equals(dict2)); assertEquals(new DictNode(), p.parse("{}").root); assertEquals(dict1, p.parse("{'key1': 42}").root); assertEquals(dict2, p.parse("{'key1': 42, 'key2': 43, 'key3':44}").root); dict1 = (DictNode) p.parse("{'a': 1, 'b': 2, 'c': 3, 'c': 4, 'c': 5, 'c': 6}").root; kv1 = new KeyValueNode(); kv1.key=new StringNode("c"); kv1.value=new IntegerNode(6); assertTrue(dict1.elements.contains(kv1)); assertEquals(3, dict1.elements.size()); } @Test public void TestKeyValueEquality() { KeyValueNode kv1=new KeyValueNode(); kv1.key=new StringNode("key1"); kv1.value=new IntegerNode(42); KeyValueNode kv2=new KeyValueNode(); kv2.key=new StringNode("key1"); kv2.value=new IntegerNode(42); assertEquals(kv1, kv2); kv2.value=new IntegerNode(43); assertFalse(kv1.equals(kv2)); } @Test public void TestDictEquality() { DictNode dict1 = new DictNode(); KeyValueNode kv=new KeyValueNode(); kv.key=new StringNode("key1"); kv.value=new IntegerNode(42); dict1.elements.add(kv); DictNode dict2 = new DictNode(); kv=new KeyValueNode(); kv.key=new StringNode("key1"); kv.value=new IntegerNode(42); dict2.elements.add(kv); assertEquals(dict1, dict2); kv=new KeyValueNode(); kv.key=new StringNode("key2"); kv.value=new IntegerNode(43); dict1.elements.add(kv); kv=new KeyValueNode(); kv.key=new StringNode("key3"); kv.value=new IntegerNode(44); dict1.elements.add(kv); dict2 = new DictNode(); kv=new KeyValueNode(); kv.key=new StringNode("key2"); kv.value=new IntegerNode(43); dict2.elements.add(kv); kv=new KeyValueNode(); kv.key=new StringNode("key3"); kv.value=new IntegerNode(44); dict2.elements.add(kv); kv=new KeyValueNode(); kv.key=new StringNode("key1"); kv.value=new IntegerNode(42); dict2.elements.add(kv); assertTrue(dict1.equals(dict2)); assertEquals(dict1, dict2); kv=new KeyValueNode(); kv.key=new StringNode("key4"); kv.value=new IntegerNode(45); dict2.elements.add(kv); assertFalse(dict1.equals(dict2)); } @Test public void TestSetEquality() { SetNode set1 = new SetNode(); set1.elements.add(new IntegerNode(1)); set1.elements.add(new IntegerNode(2)); set1.elements.add(new IntegerNode(3)); SetNode set2 = new SetNode(); set2.elements.add(new IntegerNode(2)); set2.elements.add(new IntegerNode(3)); set2.elements.add(new IntegerNode(1)); assertEquals(set1, set2); set2.elements.add(new IntegerNode(0)); assertFalse(set1.equals(set2)); } @Test public void TestFile() throws IOException { Parser p = new Parser(); File testdatafile = new File("src/test/java/testserpent.utf8.bin"); byte[] ser = new byte[(int) testdatafile.length()]; FileInputStream fis=new FileInputStream(testdatafile); DataInputStream dis = new DataInputStream(fis); dis.readFully(ser); dis.close(); fis.close(); Ast ast = p.parse(ser); String expr = ast.toString(); Ast ast2 = p.parse(expr); String expr2 = ast2.toString(); assertEquals(expr.length(), expr2.length()); StringBuilder sb= new StringBuilder(); Walk(ast.root, sb); String walk1 = sb.toString(); sb= new StringBuilder(); Walk(ast2.root, sb); String walk2 = sb.toString(); assertEquals(walk1.length(), walk2.length()); // TODO assertEquals(ast.root, ast2.root); ast = p.parse(expr2); // TODO assertEquals(ast.root, ast2.root); } @Test public void TestTrailingCommas() throws IOException { Parser p = new Parser(); INode result; result = p.parse("[1,2,3, ]").root; result = p.parse("[1,2,3 , ]").root; result = p.parse("[1,2,3,]").root; assertEquals("[1,2,3]", result.toString()); result = p.parse("(1,2,3, )").root; result = p.parse("(1,2,3 , )").root; result = p.parse("(1,2,3,)").root; assertEquals("(1,2,3)", result.toString()); // for dict and set the asserts are a bit more complex // we cannot simply convert to string because the order of elts is undefined. result = p.parse("{'a':1, 'b':2, 'c':3, }").root; result = p.parse("{'a':1, 'b':2, 'c':3 , }").root; result = p.parse("{'a':1, 'b':2, 'c':3,}").root; DictNode dict = (DictNode) result; assertEquals(3, dict.elements.size()); Set elts = dict.elementsAsSet(); assertTrue(elts.contains(new KeyValueNode(new StringNode("a"), new IntegerNode(1)))); assertTrue(elts.contains(new KeyValueNode(new StringNode("b"), new IntegerNode(2)))); assertTrue(elts.contains(new KeyValueNode(new StringNode("c"), new IntegerNode(3)))); result = p.parse("{1,2,3, }").root; result = p.parse("{1,2,3 , }").root; result = p.parse("{1,2,3,}").root; SetNode set = (SetNode) result; assertEquals(3, set.elements.size()); elts = set.elementsAsSet(); assertTrue(elts.contains(new IntegerNode(1))); assertTrue(elts.contains(new IntegerNode(2))); assertTrue(elts.contains(new IntegerNode(3))); } @Test @Ignore("can't get the ast compare to succeed :(") public void TestAstEquals() throws IOException { Parser p = new Parser(); File testdatafile = new File("test/testserpent.utf8.bin"); byte[] ser = new byte[(int) testdatafile.length()]; FileInputStream fis=new FileInputStream(testdatafile); DataInputStream dis = new DataInputStream(fis); dis.readFully(ser); dis.close(); fis.close(); Ast ast = p.parse(ser); Ast ast2 = p.parse(ser); assertEquals(ast.root, ast2.root); // TODO this fails :( } public void Walk(INode node, StringBuilder sb) { if(node instanceof SequenceNode) { sb.append(String.format("%s (seq)\n", node.getClass())); SequenceNode seq = (SequenceNode)node; for(INode child: seq.elements) { Walk(child, sb); } } else sb.append(String.format(" %s\n", node.toString())); } } Serpent-serpent-1.23/java/src/test/java/net/razorvine/serpent/test/SerializationHelperClass.java000066400000000000000000000010111312652634400332160ustar00rootroot00000000000000package net.razorvine.serpent.test; import java.io.Serializable; public class SerializationHelperClass implements Serializable { private static final long serialVersionUID = 5151254868567404093L; public int x; public String s; public int i; public Object obj; public String getTheString() { return s; } public int getTheInteger() { return i; } public boolean isThingy() { return true; } public int getNUMBER() { return 42; } public String getX() { return "X"; } public Object getObject() { return obj; } }Serpent-serpent-1.23/java/src/test/java/net/razorvine/serpent/test/SerializeTest.java000066400000000000000000000611461312652634400310610ustar00rootroot00000000000000/** * Serpent, a Python literal expression serializer/deserializer * (a.k.a. Python's ast.literal_eval in Java) * Software license: "MIT software license". See http://opensource.org/licenses/MIT * @author Irmen de Jong (irmen@razorvine.net) */ package net.razorvine.serpent.test; import static org.junit.Assert.*; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.math.BigDecimal; import java.math.BigInteger; import java.util.*; import net.razorvine.serpent.ComplexNumber; import net.razorvine.serpent.IClassSerializer; import net.razorvine.serpent.Parser; import net.razorvine.serpent.Serializer; import org.junit.Test; public class SerializeTest { public byte[] strip_header(byte[] data) { int start; for(start=0; start=data.length) { throw new IllegalArgumentException("need header in string"); } start++; byte[] result = new byte[data.length-start]; System.arraycopy(data, start, result, 0, data.length-start); return result; } public byte[] B(String s) { try { return s.getBytes("utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } } public String S(byte[] b) { try { return new String(b, "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); return null; } } @Test public void testHeader() { Serializer ser = new Serializer(); byte[] data = ser.serialize(null); assertEquals(35, data[0]); String strdata = S(data); assertEquals("# serpent utf-8 python3.2", strdata.split("\n")[0]); ser.setliterals=false; data = ser.serialize(null); strdata = S(data); assertEquals("# serpent utf-8 python2.6", strdata.split("\n")[0]); data = B("# header\nfirst-line"); data = strip_header(data); assertEquals("first-line", S(data)); } @Test public void testException() { Serializer.registerClass(IllegalArgumentException.class, null); Exception x = new IllegalArgumentException("errormessage"); Serializer serpent = new Serializer(true, true, false); byte[] ser = strip_header(serpent.serialize(x)); assertEquals("{\n '__class__': 'IllegalArgumentException',\n '__exception__': True,\n 'args': (\n 'errormessage',\n ),\n 'attributes': {}\n}", S(ser)); } @Test public void testExceptionPackage() { Serializer.registerClass(IllegalArgumentException.class, null); Exception x = new IllegalArgumentException("errormessage"); Serializer serpent = new Serializer(true, true, true); byte[] ser = strip_header(serpent.serialize(x)); assertEquals("{\n '__class__': 'java.lang.IllegalArgumentException',\n '__exception__': True,\n 'args': (\n 'errormessage',\n ),\n 'attributes': {}\n}", S(ser)); } @Test public void testStuff() { Serializer ser=new Serializer(); byte[] result = ser.serialize("blerp"); result=strip_header(result); assertEquals("'blerp'", S(result)); result = ser.serialize("\\"); result=strip_header(result); assertEquals("'\\\\'", S(result)); result = ser.serialize(UUID.fromString("f1f8d00e-49a5-4662-ac1d-d5f0426ed293")); result=strip_header(result); assertEquals("'f1f8d00e-49a5-4662-ac1d-d5f0426ed293'", S(result)); result = ser.serialize(new BigDecimal("123456789.987654321987654321987654321987654321")); result=strip_header(result); assertEquals("'123456789.987654321987654321987654321987654321'", S(result)); } @Test public void testNull() { Serializer ser = new Serializer(); byte[] data = ser.serialize(null); data=strip_header(data); assertEquals("None", S(data)); } @Test public void testStrings() { Serializer serpent = new Serializer(); byte[] ser = serpent.serialize("hello"); byte[] data = strip_header(ser); assertEquals("'hello'", S(data)); ser = serpent.serialize("quotes'\""); data = strip_header(ser); assertEquals("'quotes\\'\"'", S(data)); ser = serpent.serialize("quotes2'"); data = strip_header(ser); assertEquals("\"quotes2'\"", S(data)); } @Test public void testUnicodeEscapes() { Serializer serpent=new Serializer(); // regular escaped chars first byte[] ser = serpent.serialize("\b\r\n\f\t \\"); byte[] data = strip_header(ser); // '\\x08\\r\\n\\x0c\\t \\\\' assertArrayEquals(new byte[] {39, 92, 120, 48, 56, 92, 114, 92, 110, 92, 120, 48, 99, 92, 116, 32, 92, 92, 39}, data); // simple cases (chars < 0x80) ser = serpent.serialize("\u0000\u0001\u001f\u007f"); data = strip_header(ser); // '\\x00\\x01\\x1f\\x7f' assertArrayEquals(new byte[] {39, 92, 120, 48, 48, 92, 120, 48, 49, 92, 120, 49, 102, 92, 120, 55, 102, 39 }, data); // chars 0x80 .. 0xff ser = serpent.serialize("\u0080\u0081\u00ff"); data = strip_header(ser); // '\\x80\\x81\xc3\xbf' (has some utf-8 encoded chars in it) assertArrayEquals(new byte[] {39, 92, 120, 56, 48, 92, 120, 56, 49, -61, -65, 39}, data); // chars above 0xff ser = serpent.serialize("\u0100\u20ac\u8899"); data = strip_header(ser); // '\xc4\x80\xe2\x82\xac\xe8\xa2\x99' (has some utf-8 encoded chars in it) assertArrayEquals(new byte[] {39, -60, -128, -30, -126, -84, -24, -94, -103, 39}, data); // some random high chars that are all printable in python and not escaped ser = serpent.serialize("\u0377\u082d\u10c5\u135d\uac00"); data = strip_header(ser); // '\xcd\xb7\xe0\xa0\xad\xe1\x83\x85\xe1\x8d\x9d\xea\xb0\x80' (only a bunch of utf-8 encoded chars) assertArrayEquals(new byte[] {39, -51, -73, -32, -96, -83, -31, -125, -123, -31, -115, -99, -22, -80, -128, 39}, data); // some random high chars that are all non-printable in python and that are escaped ser = serpent.serialize("\u0378\u082e\u10c6\u135c\uabff"); data = strip_header(ser); // '\\u0378\\u082e\\u10c6\\u135c\\uabff' assertArrayEquals(new byte[] {39, 92, 117, 48, 51, 55, 56, 92, 117, 48, 56, 50, 101, 92, 117, 49, 48, 99, 54, 92, 117, 49, 51, 53, 99, 92, 117, 97, 98, 102, 102, 39}, data); } @Test public void testNullByte() { Serializer serpent = new Serializer(); byte[] ser = serpent.serialize("null\u0000byte"); byte[] data = strip_header(ser); assertEquals("'null\\x00byte'", new String(data)); for(byte b: ser) { if(b==0) fail("serialized data may not contain 0-bytes"); } } @Test public void testBool() { Serializer serpent = new Serializer(); byte[] ser = serpent.serialize(true); byte[] data = strip_header(ser); assertEquals("True", S(data)); ser = serpent.serialize(false); data = strip_header(ser); assertEquals("False", S(data)); } @Test public void testBytes() { Serializer serpent = new Serializer(true, true, false); byte[] bytes = new byte[] { 97, 98, 99, 100, 101, 102 }; // abcdef byte[] ser = serpent.serialize(bytes); assertEquals("{\n 'data': 'YWJjZGVm',\n 'encoding': 'base64'\n}", S(strip_header(ser))); Parser p = new Parser(); String parsed = p.parse(ser).root.toString(); assertEquals(39, parsed.length()); Map dict = new HashMap(); dict.put("data", "YWJjZGVm"); dict.put("encoding", "base64"); byte[] bytes2 = Parser.toBytes(dict); assertArrayEquals(bytes, bytes2); dict.put("encoding", "base99"); try { Parser.toBytes(dict); fail("error expected"); } catch (IllegalArgumentException x) { // } dict.clear(); try { Parser.toBytes(dict); fail("error expected"); } catch (IllegalArgumentException x) { // } dict.clear(); dict.put("data", "YWJjZGVm"); try { Parser.toBytes(dict); fail("error expected"); } catch (IllegalArgumentException x) { // } dict.clear(); dict.put("encoding", "base64"); try { Parser.toBytes(dict); fail("error expected"); } catch (IllegalArgumentException x) { // } try { Parser.toBytes(12345); fail("error expected"); } catch (IllegalArgumentException x) { // } try { Parser.toBytes(null); fail("error expected"); } catch (IllegalArgumentException x) { // } } @Test public void testDateTime() { Serializer serpent = new Serializer(); Calendar cal = new GregorianCalendar(2013, 0, 20, 23, 59, 45); cal.set(Calendar.MILLISECOND, 999); cal.setTimeZone(TimeZone.getTimeZone("GMT+0")); byte[] ser = strip_header(serpent.serialize(cal)); assertEquals("'2013-01-20T23:59:45.999Z'", S(ser)); Date date = cal.getTime(); ser = strip_header(serpent.serialize(date)); assertEquals("'2013-01-20T23:59:45.999Z'", S(ser)); ser = strip_header(serpent.serialize(date)); assertEquals("'2013-01-20T23:59:45.999Z'", S(ser)); cal.set(Calendar.MILLISECOND, 0); ser = strip_header(serpent.serialize(cal)); assertEquals("'2013-01-20T23:59:45Z'", S(ser)); date = cal.getTime(); ser = strip_header(serpent.serialize(date)); assertEquals("'2013-01-20T23:59:45Z'", S(ser)); } @Test public void testDateTimeWithTimezone() { Serializer serpent = new Serializer(); Calendar cal = new GregorianCalendar(2013, 0, 20, 23, 59, 45); cal.set(Calendar.MILLISECOND, 999); cal.setTimeZone(TimeZone.getTimeZone("Europe/Amsterdam")); byte[] ser = strip_header(serpent.serialize(cal)); assertEquals("'2013-01-20T23:59:45.999+0100'", S(ser)); // normal time cal = new GregorianCalendar(2013, 4, 10, 13, 59, 45); cal.set(Calendar.MILLISECOND, 999); cal.setTimeZone(TimeZone.getTimeZone("Europe/Amsterdam")); ser = strip_header(serpent.serialize(cal)); assertEquals("'2013-05-10T13:59:45.999+0200'", S(ser)); // daylight saving time Date date=cal.getTime(); ser = strip_header(serpent.serialize(date)); assertEquals("'2013-05-10T11:59:45.999Z'", S(ser)); // the date and time in UTC cal.set(Calendar.MILLISECOND, 0); date=cal.getTime(); ser = strip_header(serpent.serialize(date)); assertEquals("'2013-05-10T11:59:45Z'", S(ser)); // the date and time in UTC } @Test public void testNumbers() { Serializer serpent = new Serializer(); byte[] ser = serpent.serialize((int)12345); byte[] data = strip_header(ser); assertEquals("12345", S(data)); ser = serpent.serialize((long)1234567891234567891L); data = strip_header(ser); assertEquals("1234567891234567891", S(data)); ser = serpent.serialize(99.1234); data = strip_header(ser); assertEquals("99.1234", S(data)); ser = serpent.serialize(new BigInteger("1234999999999912345678901234567890")); data = strip_header(ser); assertEquals("1234999999999912345678901234567890", S(data)); ser = serpent.serialize(new BigDecimal("123456789.987654321987654321987654321987654321")); data=strip_header(ser); assertEquals("'123456789.987654321987654321987654321987654321'", S(data)); ComplexNumber cplx = new ComplexNumber(2.2, 3.3); ser = serpent.serialize(cplx); data = strip_header(ser); assertEquals("(2.2+3.3j)", S(data)); cplx = new ComplexNumber(0, 3); ser = serpent.serialize(cplx); data = strip_header(ser); assertEquals("(0.0+3.0j)", S(data)); cplx = new ComplexNumber(-2, -3); ser = serpent.serialize(cplx); data = strip_header(ser); assertEquals("(-2.0-3.0j)", S(data)); cplx = new ComplexNumber(-2.5, -3.9); ser = serpent.serialize(cplx); data = strip_header(ser); assertEquals("(-2.5-3.9j)", S(data)); } @Test public void testDoubleNanInf() { Serializer serpent = new Serializer(); Object[] doubles = new Object[] {Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN, Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NaN, new ComplexNumber(Double.POSITIVE_INFINITY, 3.3)}; byte[] ser = serpent.serialize(doubles); byte[] data = strip_header(ser); assertEquals("(1e30000,-1e30000,{'__class__':'float','value':'nan'},1e30000,-1e30000,{'__class__':'float','value':'nan'},(1e30000+3.3j))", S(data)); } @Test public void testList() { Serializer serpent = new Serializer(); List list = new LinkedList(); // test empty list byte[] ser = strip_header(serpent.serialize(list)); assertEquals("[]", S(ser)); serpent.indent=true; ser = strip_header(serpent.serialize(list)); assertEquals("[]", S(ser)); serpent.indent=false; // test nonempty list list.add(42); list.add("Sally"); list.add(16.5); ser = strip_header(serpent.serialize(list)); assertEquals("[42,'Sally',16.5]", S(ser)); serpent.indent=true; ser = strip_header(serpent.serialize(list)); assertEquals("[\n 42,\n 'Sally',\n 16.5\n]", S(ser)); } public class UnserializableClass { } @Test(expected = IllegalArgumentException.class) public void testClassFail() { Serializer serpent = new Serializer(true, true, false); Object obj = new UnserializableClass(); serpent.serialize(obj); } @Test public void testClassOk() { Serializer.registerClass(SerializationHelperClass.class, null); Serializer serpent = new Serializer(true, true, false); SerializationHelperClass obj = new SerializationHelperClass(); obj.i=99; obj.s="hi"; obj.x=42; byte[] ser = strip_header(serpent.serialize(obj)); assertEquals("{\n 'NUMBER': 42,\n '__class__': 'SerializationHelperClass',\n 'object': None,\n 'theInteger': 99,\n 'theString': 'hi',\n 'thingy': True,\n 'x': 'X'\n}", S(ser)); } @Test public void testClassPackageOk() { Serializer.registerClass(SerializationHelperClass.class, null); Serializer serpent = new Serializer(true, true, true); SerializationHelperClass obj = new SerializationHelperClass(); obj.i=99; obj.s="hi"; obj.x=42; byte[] ser = strip_header(serpent.serialize(obj)); assertEquals("{\n 'NUMBER': 42,\n '__class__': 'net.razorvine.serpent.test.SerializationHelperClass',\n 'object': None,\n 'theInteger': 99,\n 'theString': 'hi',\n 'thingy': True,\n 'x': 'X'\n}", S(ser)); } class TestclassConverter implements IClassSerializer { @Override public Map convert(Object obj) { SerializationHelperClass o = (SerializationHelperClass) obj; Map result = new HashMap(); result.put("__class@__", o.getClass().getSimpleName()+"@"); result.put("i@", o.i); result.put("s@", o.s); result.put("x@", o.x); return result; } } class ExceptionConverter implements IClassSerializer { @Override public Map convert(Object obj) { IllegalArgumentException e = (IllegalArgumentException) obj; Map result = new HashMap(); result.put("__class@__", e.getClass().getSimpleName()); result.put("msg@", e.getMessage()); return result; } } @Test public void testCustomClassDict() { Serializer.registerClass(SerializationHelperClass.class, new TestclassConverter()); Serializer serpent = new Serializer(true, true, false); SerializationHelperClass obj = new SerializationHelperClass(); obj.i=99; obj.s="hi"; obj.x=42; byte[] ser = strip_header(serpent.serialize(obj)); assertEquals("{\n '__class@__': 'SerializationHelperClass@',\n 'i@': 99,\n 's@': 'hi',\n 'x@': 42\n}", S(ser)); } @Test public void testCustomExceptionDict() { Serializer.registerClass(IllegalArgumentException.class, new ExceptionConverter()); Serializer serpent = new Serializer(true, true, false); Exception x = new IllegalArgumentException("errormessage"); byte[] ser = strip_header(serpent.serialize(x)); assertEquals("{\n '__class@__': 'IllegalArgumentException',\n 'msg@': 'errormessage'\n}", S(ser)); } @Test public void testSet() { Serializer serpent = new Serializer(); Set set = new HashSet(); // test empty set byte[] ser = strip_header(serpent.serialize(set)); assertEquals("()", S(ser)); // empty set is serialized as a tuple. serpent.indent=true; ser = strip_header(serpent.serialize(set)); assertEquals("()", S(ser)); // empty set is serialized as a tuple. serpent.indent=false; // test nonempty set set.add("X"); set.add("Sally"); set.add("Y"); ser = strip_header(serpent.serialize(set)); assertEquals(17, ser.length); assertTrue(S(ser).contains("'Sally'")); assertTrue(S(ser).contains("'X'")); assertTrue(S(ser).contains("'Y'")); serpent.indent=true; ser = strip_header(serpent.serialize(set)); assertEquals("{\n 'Sally',\n 'X',\n 'Y'\n}", S(ser)); // test no set literals serpent.indent=false; serpent.setliterals=false; ser = strip_header(serpent.serialize(set)); assertEquals(17, ser.length); assertTrue(S(ser).contains("'Sally'")); assertTrue(S(ser).contains("'X'")); assertTrue(S(ser).contains("'Y'")); assertTrue(ser[0]=='('); assertTrue(ser[ser.length-1]==')'); } @Test public void testCollection() { Collection intlist = new LinkedList(); intlist.add(42); intlist.add(43); Serializer serpent = new Serializer(); byte[] ser = serpent.serialize(intlist); ser = strip_header(ser); assertEquals("[42,43]", S(ser)); ser=strip_header(serpent.serialize(new int[] {42})); assertEquals("(42,)", S(ser)); ser=strip_header(serpent.serialize(new int[] {42, 43})); assertEquals("(42,43)", S(ser)); serpent.indent=true; ser = strip_header(serpent.serialize(intlist)); assertEquals("[\n 42,\n 43\n]", S(ser)); ser=strip_header(serpent.serialize(new int[] {42})); assertEquals("(\n 42,\n)", S(ser)); ser=strip_header(serpent.serialize(new int[] {42, 43})); assertEquals("(\n 42,\n 43\n)", S(ser)); } @Test public void testDictionary() { Serializer serpent = new Serializer(); Parser p = new Parser(); // test empty dict Hashtable ht = new Hashtable(); byte[] ser = serpent.serialize(ht); assertEquals("{}", S(strip_header(ser))); String parsed = p.parse(ser).root.toString(); assertEquals("{}", parsed); //empty dict with indentation serpent.indent=true; ser = serpent.serialize(ht); assertEquals("{}", S(strip_header(ser))); parsed = p.parse(ser).root.toString(); assertEquals("{}", parsed); // test dict with values serpent.indent=false; ht = new Hashtable(); ht.put(42, "fortytwo"); ht.put("sixteen-and-half", 16.5); ht.put("name", "Sally"); ht.put("status", false); ser = serpent.serialize(ht); assertEquals('}', ser[ser.length-1]); assertTrue(ser[ser.length-2]!=','); parsed = p.parse(ser).root.toString(); assertEquals(69, parsed.length()); // test indentation serpent.indent=true; ser = serpent.serialize(ht); assertEquals('}', ser[ser.length-1]); assertEquals('\n', ser[ser.length-2]); assertTrue(ser[ser.length-3]!=','); String ser_str = S(strip_header(ser)); assertTrue(ser_str.contains("'name': 'Sally'")); assertTrue(ser_str.contains("'status': False")); assertTrue(ser_str.contains("42: 'fortytwo'")); assertTrue(ser_str.contains("'sixteen-and-half': 16.5")); parsed = p.parse(ser).root.toString(); assertEquals(69, parsed.length()); serpent.indent=false; // generic Dictionary test Map mydict = new HashMap(); mydict.put(1, "one"); mydict.put(2, "two"); ser = serpent.serialize(mydict); ser_str = S(strip_header(ser)); assertTrue(ser_str.equals("{2:'two',1:'one'}") || ser_str.equals("{1:'one',2:'two'}")); } @Test public void testIndentation() { Map dict = new HashMap(); List list = new LinkedList(); list.add(1); list.add(2); list.add(new String[] {"a", "b"}); dict.put("first", list); Map subdict = new HashMap(); subdict.put(1, false); dict.put("second", subdict); Set subset = new HashSet(); subset.add(3); subset.add(4); dict.put("third", subset); Serializer serpent = new Serializer(); serpent.indent=true; byte[] ser = strip_header(serpent.serialize(dict)); assertEquals("{\n"+ " 'first': [\n"+ " 1,\n"+ " 2,\n"+ " (\n"+ " 'a',\n"+ " 'b'\n"+ " )\n"+ " ],\n"+ " 'second': {\n"+ " 1: False\n"+ " },\n"+ " 'third': {\n"+ " 3,\n"+ " 4\n"+ " }\n"+ "}", S(ser)); } @Test public void testSorting() { Serializer serpent=new Serializer(); ArrayList data1 = new ArrayList(); data1.add(3); data1.add(2); data1.add(1); byte[] ser = strip_header(serpent.serialize(data1)); assertEquals("[3,2,1]", S(ser)); int[] data2 = new int[] { 3,2,1 }; ser = strip_header(serpent.serialize(data2)); assertEquals("(3,2,1)", S(ser)); Set data3 = new HashSet(); data3.add(42); data3.add("hi"); serpent.indent=true; ser = strip_header(serpent.serialize(data3)); assertTrue(S(ser).equals("{\n 42,\n 'hi'\n}") || S(ser).equals("{\n 'hi',\n 42\n}")); Map data4 = new HashMap(); data4.put(5, "five"); data4.put(3, "three"); data4.put(1, "one"); data4.put(4, "four"); data4.put(2, "two"); serpent.indent=true; ser = strip_header(serpent.serialize(data4)); assertEquals("{\n 1: 'one',\n 2: 'two',\n 3: 'three',\n 4: 'four',\n 5: 'five'\n}", S(ser)); Set data5 = new HashSet(); data5.add("x"); data5.add("y"); data5.add("z"); data5.add("c"); data5.add("b"); data5.add("a"); serpent.indent=true; ser = strip_header(serpent.serialize(data5)); assertEquals("{\n 'a',\n 'b',\n 'c',\n 'x',\n 'y',\n 'z'\n}", S(ser)); } interface IBaseInterface {}; interface ISubInterface extends IBaseInterface {}; class BaseClassWithInterface implements IBaseInterface, Serializable {}; class SubClassWithInterface extends BaseClassWithInterface implements ISubInterface, Serializable {}; class BaseClass implements Serializable {}; class SubClass extends BaseClass implements Serializable {}; abstract class AbstractBaseClass {}; class ConcreteSubClass extends AbstractBaseClass implements Serializable {}; class AnyClassConverter implements IClassSerializer { @Override public Map convert(Object obj) { Map result = new HashMap(); result.put("(SUB)CLASS", obj.getClass().getSimpleName()); return result; } } @Test public void testAbstractBaseClassHierarchyPickler() { ConcreteSubClass c = new ConcreteSubClass(); Serializer serpent=new Serializer(); byte[] data = serpent.serialize(c); assertEquals("{'__class__':'ConcreteSubClass'}", S(strip_header(data))); // the default serializer Serializer.registerClass(AbstractBaseClass.class, new AnyClassConverter()); data = serpent.serialize(c); assertEquals("{'(SUB)CLASS':'ConcreteSubClass'}", S(strip_header(data))); // custom serializer } @Test public void testInterfaceHierarchyPickler() { BaseClassWithInterface b = new BaseClassWithInterface(); SubClassWithInterface sub = new SubClassWithInterface(); Serializer serpent=new Serializer(); byte[] data = serpent.serialize(b); assertEquals("{'__class__':'BaseClassWithInterface'}", S(strip_header(data))); // the default serializer data = serpent.serialize(sub); assertEquals("{'__class__':'SubClassWithInterface'}", S(strip_header(data))); // the default serializer Serializer.registerClass(IBaseInterface.class, new AnyClassConverter()); data = serpent.serialize(b); assertEquals("{'(SUB)CLASS':'BaseClassWithInterface'}", S(strip_header(data))); // custom serializer data = serpent.serialize(sub); assertEquals("{'(SUB)CLASS':'SubClassWithInterface'}", S(strip_header(data))); // custom serializer } } Serpent-serpent-1.23/java/src/test/java/net/razorvine/serpent/test/SlowPerformanceTest.java000066400000000000000000000033561312652634400322370ustar00rootroot00000000000000package net.razorvine.serpent.test; import java.io.IOException; import net.razorvine.serpent.Parser; import net.razorvine.serpent.Serializer; import org.junit.Ignore; import org.junit.Test; public class SlowPerformanceTest { // tests some performance regressions when they occur @Test @Ignore public void TestManyFloats() throws IOException { // System.out.println("enter to start manyfloats"); // System.in.read(); int amount = 500000; double[] array = new double[amount]; for(int i=0; i dict = (Map) thing; assertEquals(11, dict.size()); List list = (List) dict.get("numbers"); assertEquals(4, list.size()); assertEquals(999.1234, list.get(1)); assertEquals(new ComplexNumber(-3, 8), list.get(3)); String euro = (String) dict.get("unicode"); assertEquals("\u20ac", euro); Map exc = (Map) dict.get("exc"); Object[] args = (Object[]) exc.get("args"); assertEquals("fault", args[0]); assertEquals("ZeroDivisionError", exc.get("__class__")); } class ArithmeticExcFromDict implements IDictToInstance { public Object convert(Map dict) { String classname = (String) dict.get("__class__"); if("ZeroDivisionError".equals(classname)) { Object[] args = (Object[]) dict.get("args"); return new ArithmeticException((String)args[0]); } return null; } } @SuppressWarnings("unchecked") @Test public void testObjectifyDictToClass() throws IOException { Parser p = new Parser(); File testdatafile = new File("src/test/java/testserpent.utf8.bin"); byte[] ser = new byte[(int) testdatafile.length()]; FileInputStream fis=new FileInputStream(testdatafile); DataInputStream dis = new DataInputStream(fis); dis.readFully(ser); dis.close(); fis.close(); Ast ast = p.parse(ser); ObjectifyVisitor visitor = new ObjectifyVisitor(new ArithmeticExcFromDict()); ast.accept(visitor); Object thing = visitor.getObject(); Map dict = (Map) thing; assertEquals(11, dict.size()); ArithmeticException exc = (ArithmeticException) dict.get("exc"); assertEquals("fault", exc.getMessage()); } } Serpent-serpent-1.23/java/src/test/java/testserpent.utf8.bin000066400000000000000000000022361312652634400241150ustar00rootroot00000000000000# serpent utf-8 python3.3 { # serpent supports comments in the serialized data 'bytes': { 'data': 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==', 'encoding': 'base64' }, 'dates': [ # test some dates. '2013-01-26T03:32:39.007001', '23:59:45.999888', 500.0 ], 'dict': { 0: '0000', 1: '1111', 2: '2222', 3: '3333', 4: '4444', 5: '5555', 6: '6666', 7: '7777', 8: '8888', 9: '9999' }, 'exc': { '__class__': 'ZeroDivisionError', '__exception__': True, # this is an exception 'args': ( 'fault', ), 'attributes': {} }, 'list': [ 1, 2, 3, 4, 5, 6, 7, 8 ], 'numbers': [ 12345678901234567890123456, 999.1234, '1.99999999999999999991', (-3+8j) ], 'set': { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 'str': 'hello', 'tuple': ( 1, 2, 3, 4, 5, 6, 7, 8 ), 'unicode': '€', # the feared unicode! 'uuid': '57e12d82-511f-456b-ab65-f765144c6a90' }Serpent-serpent-1.23/serpent.py000066400000000000000000000622111312652634400165770ustar00rootroot00000000000000""" ast.literal_eval() compatible object tree serialization. Serpent serializes an object tree into bytes (utf-8 encoded string) that can be decoded and then passed as-is to ast.literal_eval() to rebuild it as the original object tree. As such it is safe to send serpent data to other machines over the network for instance (because only 'safe' literals are encoded). Compatible with Python 2.7+ (including 3.x), IronPython 2.7+, Jython 2.7+. Serpent handles several special Python types to make life easier: - str --> promoted to unicode (see below why this is) - bytes, bytearrays, memoryview, buffer --> string, base-64 (you'll have to manually un-base64 them though) - uuid.UUID, datetime.{datetime, date, time, timespan} --> appropriate string/number - decimal.Decimal --> string (to not lose precision) - array.array typecode 'c'/'u' --> string/unicode - array.array other typecode --> list - Exception --> dict with some fields of the exception (message, args) - collections module types --> mostly equivalent primitive types or dict - enums --> the value of the enum (Python 3.4+) - all other types --> dict with __getstate__ or vars() of the object Notes: All str will be promoted to unicode. This is done because it is the default anyway for Python 3.x, and it solves the problem of the str/unicode difference between different Python versions. Also it means the serialized output doesn't have those problematic 'u' prefixes on strings. The serializer is not thread-safe. Make sure you're not making changes to the object tree that is being serialized, and don't use the same serializer in different threads. Because the serialized format is just valid Python source code, it can contain comments. Set literals are not supported on python <3.2 (ast.literal_eval limitation). If you need Python < 3.2 compatibility, you'll have to use set_literals=False when serializing. Since version 1.6 serpent chooses this wisely for you by default, but you can still override it if needed. Floats +inf and -inf are handled via a trick, Float 'nan' cannot be handled and is represented by the special value: {'__class__':'float','value':'nan'} We chose not to encode it as just the string 'NaN' because that could cause memory issues when used in multiplications. Jython's ast module cannot properly parse some literal reprs of unicode strings. This is a known bug http://bugs.jython.org/issue2008 It seems to work when your server is Python 2.x but safest is perhaps to make sure your data to parse contains only ascii strings when dealing with Jython. Serpent checks for possible problems and will raise an error if it finds one, rather than continuing with string data that might be incorrect. Copyright by Irmen de Jong (irmen@razorvine.net) Software license: "MIT software license". See http://opensource.org/licenses/MIT """ from __future__ import print_function, division import __future__ import ast import base64 import sys import types import os import gc import collections import decimal import datetime import uuid import array import math import numbers import codecs __version__ = "1.23" __all__ = ["dump", "dumps", "load", "loads", "register_class", "unregister_class", "tobytes"] can_use_set_literals = sys.version_info >= (3, 2) # check if we can use set literals def dumps(obj, indent=False, set_literals=can_use_set_literals, module_in_classname=False): """Serialize object tree to bytes""" return Serializer(indent, set_literals, module_in_classname).serialize(obj) def dump(obj, file, indent=False, set_literals=can_use_set_literals, module_in_classname=False): """Serialize object tree to a file""" file.write(dumps(obj, indent=indent, set_literals=set_literals, module_in_classname=module_in_classname)) def loads(serialized_bytes): """Deserialize bytes back to object tree. Uses ast.literal_eval (safe).""" if os.name == "java": if type(serialized_bytes) is memoryview: serialized_bytes = serialized_bytes.tobytes() elif type(serialized_bytes) is buffer: serialized_bytes = serialized_bytes[:] serialized = serialized_bytes.decode("utf-8") elif sys.platform == "cli": if type(serialized_bytes) is memoryview: serialized_bytes = serialized_bytes.tobytes() serialized = codecs.decode(serialized_bytes, "utf-8") else: serialized = codecs.decode(serialized_bytes, "utf-8") if '\x00' in serialized: raise ValueError("The serpent data contains 0-bytes so it cannot be parsed by ast.literal_eval. Has it been corrupted?") if sys.version_info < (3, 0): # python 2.x: parse with unicode_literals (promotes all strings to unicode) # note: this doesn't work on jython... see bug http://bugs.jython.org/issue2008 # so we add a safety net, to avoid working with incorrectly processed unicode strings serialized = compile(serialized, "", mode="eval", flags=ast.PyCF_ONLY_AST | __future__.unicode_literals.compiler_flag) if os.name == "java": for node in ast.walk(serialized): if isinstance(node, ast.Str): if isinstance(node.s, str) and any(c for c in node.s if c > '\x7f'): # In this case there is risk of incorrectly parsed unicode data. Play safe and crash. raise ValueError("cannot properly parse unicode string with ast in Jython, see bug http://bugs.jython.org/issue2008" " - use python 2.x server or convert strings to ascii yourself first") node.s = node.s.decode("unicode-escape") try: if os.name != "java" and sys.platform != "cli": gc.disable() return ast.literal_eval(serialized) finally: gc.enable() def load(file): """Deserialize bytes from a file back to object tree. Uses ast.literal_eval (safe).""" data = file.read() return loads(data) def _ser_OrderedDict(obj, serializer, outputstream, indentlevel): obj = { "__class__": "collections.OrderedDict" if serializer.module_in_classname else "OrderedDict", "items": list(obj.items()) } serializer._serialize(obj, outputstream, indentlevel) def _ser_DictView(obj, serializer, outputstream, indentlevel): serializer.ser_builtins_list(obj, outputstream, indentlevel) _special_classes_registry = collections.OrderedDict() # must be insert-order preserving to make sure of proper precedence rules def _reset_special_classes_registry(): _special_classes_registry.clear() _special_classes_registry[collections.KeysView] = _ser_DictView _special_classes_registry[collections.ValuesView] = _ser_DictView _special_classes_registry[collections.ItemsView] = _ser_DictView if sys.version_info >= (2, 7): _special_classes_registry[collections.OrderedDict] = _ser_OrderedDict if sys.version_info >= (3, 4): import enum def _ser_Enum(obj, serializer, outputstream, indentlevel): serializer._serialize(obj.value, outputstream, indentlevel) _special_classes_registry[enum.Enum] = _ser_Enum _reset_special_classes_registry() def unregister_class(clazz): """Unregister the specialcase serializer for the given class.""" if clazz in _special_classes_registry: del _special_classes_registry[clazz] def register_class(clazz, serializer): """ Register a special serializer function for objects of the given class. The function will be called with (object, serpent_serializer, outputstream, indentlevel) arguments. The function must write the serialized data to outputstream. It doesn't return a value. """ _special_classes_registry[clazz] = serializer class BytesWrapper(object): """ Wrapper for bytes, bytearray etc. to make them appear as base-64 encoded data. You can use the tobytes utility function to decode this back into the actual bytes (or do it manually) """ def __init__(self, data): self.data = data def __getstate__(self): if sys.platform == "cli": b64 = base64.b64encode(str(self.data)) # weird IronPython bug? elif (os.name == "java" or sys.version_info < (2, 7)) and type(self.data) is bytearray: b64 = base64.b64encode(bytes(self.data)) # Jython bug http://bugs.jython.org/issue2011 else: b64 = base64.b64encode(self.data) return { "data": b64 if type(b64) is str else b64.decode("ascii"), "encoding": "base64" } @staticmethod def from_bytes(data): return BytesWrapper(data) @staticmethod def from_bytearray(data): return BytesWrapper(data) @staticmethod def from_memoryview(data): return BytesWrapper(data.tobytes()) @staticmethod def from_buffer(data): return BytesWrapper(data) _repr_types = set([ str, int, bool, type(None) ]) _translate_types = { bytes: BytesWrapper.from_bytes, bytearray: BytesWrapper.from_bytearray, collections.deque: list, } if sys.version_info >= (3, 0): _translate_types.update({ collections.UserDict: dict, collections.UserList: list, collections.UserString: str }) _bytes_types = [bytes, bytearray, memoryview] # do some dynamic changes to the types configuration if needed if bytes is str: del _translate_types[bytes] if hasattr(types, "BufferType"): _translate_types[types.BufferType] = BytesWrapper.from_buffer _bytes_types.append(buffer) try: _translate_types[memoryview] = BytesWrapper.from_memoryview except NameError: pass if sys.platform == "cli": _repr_types.remove(str) # IronPython needs special str treatment, otherwise it treats unicode wrong _bytes_types = tuple(_bytes_types) def tobytes(obj): """ Utility function to convert obj back to actual bytes if it is a serpent-encoded bytes dictionary (a dict with base-64 encoded 'data' in it and 'encoding'='base64'). If obj is already bytes or a byte-like type, return obj unmodified. Will raise TypeError if obj is none of the above. """ if isinstance(obj, _bytes_types): return obj if isinstance(obj, dict) and "data" in obj and obj.get("encoding") == "base64": try: return base64.b64decode(obj["data"]) except TypeError: return base64.b64decode(obj["data"].encode("ascii")) # needed for certain older versions of pypy raise TypeError("argument is neither bytes nor serpent base64 encoded bytes dict") class Serializer(object): """ Serialize an object tree to a byte stream. It is not thread-safe: make sure you're not making changes to the object tree that is being serialized, and don't use the same serializer across different threads. """ dispatch = {} def __init__(self, indent=False, set_literals=can_use_set_literals, module_in_classname=False): """ Initialize the serializer. indent=indent the output over multiple lines (default=false) setLiterals=use set-literals or not (set to False if you need compatibility with Python < 3.2). Serpent chooses a sensible default for you. module_in_classname = include module prefix for class names or only use the class name itself """ self.indent = indent self.set_literals = set_literals self.module_in_classname = module_in_classname self.serialized_obj_ids = set() self.special_classes_registry_copy = None self.maximum_level = min(sys.getrecursionlimit() // 5, 1000) def serialize(self, obj): """Serialize the object tree to bytes.""" self.special_classes_registry_copy = _special_classes_registry.copy() # make it thread safe header = "# serpent utf-8 " if self.set_literals: header += "python3.2\n" # set-literals require python 3.2+ to deserialize (ast.literal_eval limitation) else: header += "python2.6\n" # don't change this even though we don't support 2.6 any longer, otherwise we can't read older serpent strings out = [header] if os.name == "java" and type(obj) is buffer: obj = bytearray(obj) try: if os.name != "java" and sys.platform != "cli": gc.disable() self.serialized_obj_ids = set() self._serialize(obj, out, 0) finally: gc.enable() self.special_classes_registry_copy = None del self.serialized_obj_ids return "".join(out).encode("utf-8") _shortcut_dispatch_types = frozenset([float, complex, tuple, list, dict, set, frozenset]) def _serialize(self, obj, out, level): if level > self.maximum_level: raise ValueError("Object graph nesting too deep. Increase serializer.maximum_level if you think you need more, " " but this may cause a RecursionError instead if Python's recursion limit doesn't allow it.") t = type(obj) if t in _translate_types: obj = _translate_types[t](obj) t = type(obj) if t in _repr_types: out.append(repr(obj)) # just a simple repr() is enough for these objects return if t in self._shortcut_dispatch_types: # we shortcut these builtins directly to the dispatch function to avoid type lookup overhead below return self.dispatch[t](self, obj, out, level) # check special registered types: special_classes = self.special_classes_registry_copy for clazz in special_classes: if isinstance(obj, clazz): special_classes[clazz](obj, self, out, level) return # serialize dispatch try: func = self.dispatch[t] except KeyError: # walk the MRO until we find a base class we recognise for type_ in t.__mro__: if type_ in self.dispatch: func = self.dispatch[type_] break else: # fall back to the default class serializer func = Serializer.ser_default_class func(self, obj, out, level) def ser_builtins_str(self, str_obj, out, level): # special case str, for IronPython where str==unicode and repr() yields undesired result self.ser_builtins_unicode(str_obj, out, level) dispatch[str] = ser_builtins_str def ser_builtins_float(self, float_obj, out, level): if math.isnan(float_obj): # there's no literal expression for a float NaN... out.append("{'__class__':'float','value':'nan'}") elif math.isinf(float_obj): # output a literal expression that overflows the float and results in +/-INF if float_obj > 0: out.append("1e30000") else: out.append("-1e30000") else: out.append(repr(float_obj)) dispatch[float] = ser_builtins_float def ser_builtins_complex(self, complex_obj, out, level): out.append("(") self.ser_builtins_float(complex_obj.real, out, level) if complex_obj.imag >= 0: out.append("+") self.ser_builtins_float(complex_obj.imag, out, level) out.append("j)") dispatch[complex] = ser_builtins_complex if sys.version_info < (3, 0): # this method is used for python 2.x unicode (python 3.x doesn't use this) def ser_builtins_unicode(self, unicode_obj, out, level): z = repr(unicode_obj) if z[0] == 'u': z = z[1:] # get rid of the unicode 'u' prefix out.append(z) dispatch[unicode] = ser_builtins_unicode if sys.version_info < (3, 0): def ser_builtins_long(self, long_obj, out, level): # used with python 2.x out.append(str(long_obj)) dispatch[long] = ser_builtins_long def ser_builtins_tuple(self, tuple_obj, out, level): append = out.append serialize = self._serialize if self.indent and tuple_obj: indent_chars = " " * level indent_chars_inside = indent_chars + " " append("(\n") for elt in tuple_obj: append(indent_chars_inside) serialize(elt, out, level + 1) append(",\n") out[-1] = out[-1].rstrip() # remove the last \n if len(tuple_obj) > 1: del out[-1] # undo the last , append("\n" + indent_chars + ")") else: append("(") for elt in tuple_obj: serialize(elt, out, level + 1) append(",") if len(tuple_obj) > 1: del out[-1] # undo the last , append(")") dispatch[tuple] = ser_builtins_tuple def ser_builtins_list(self, list_obj, out, level): if id(list_obj) in self.serialized_obj_ids: raise ValueError("Circular reference detected (list)") self.serialized_obj_ids.add(id(list_obj)) append = out.append serialize = self._serialize if self.indent and list_obj: indent_chars = " " * level indent_chars_inside = indent_chars + " " append("[\n") for elt in list_obj: append(indent_chars_inside) serialize(elt, out, level + 1) append(",\n") del out[-1] # remove the last ,\n append("\n" + indent_chars + "]") else: append("[") for elt in list_obj: serialize(elt, out, level + 1) append(",") if list_obj: del out[-1] # remove the last , append("]") self.serialized_obj_ids.discard(id(list_obj)) dispatch[list] = ser_builtins_list def _check_hashable_type(self, t): if t not in (bool, bytes, str, tuple) and not issubclass(t, numbers.Number): if sys.version_info >= (3, 4): import enum if issubclass(t, enum.Enum): return elif sys.version_info < (3, 0) and t is unicode: return raise TypeError("one of the keys in a dict or set is not of a primitive hashable type: " + str(t) + ". Use simple types as keys or use a list or tuple as container.") def ser_builtins_dict(self, dict_obj, out, level): if id(dict_obj) in self.serialized_obj_ids: raise ValueError("Circular reference detected (dict)") self.serialized_obj_ids.add(id(dict_obj)) append = out.append serialize = self._serialize if self.indent and dict_obj: indent_chars = " " * level indent_chars_inside = indent_chars + " " append("{\n") dict_items = dict_obj.items() try: sorted_items = sorted(dict_items) except TypeError: # can occur when elements can't be ordered (Python 3.x) sorted_items = dict_items for key, value in sorted_items: append(indent_chars_inside) self._check_hashable_type(type(key)) serialize(key, out, level + 1) append(": ") serialize(value, out, level + 1) append(",\n") del out[-1] # remove last ,\n append("\n" + indent_chars + "}") else: append("{") for key, value in dict_obj.items(): self._check_hashable_type(type(key)) serialize(key, out, level + 1) append(":") serialize(value, out, level + 1) append(",") if dict_obj: del out[-1] # remove the last , append("}") self.serialized_obj_ids.discard(id(dict_obj)) dispatch[dict] = ser_builtins_dict def ser_builtins_set(self, set_obj, out, level): if not self.set_literals: if self.indent: set_obj = sorted(set_obj) self._serialize(tuple(set_obj), out, level) # use a tuple instead of a set literal return append = out.append serialize = self._serialize if self.indent and set_obj: indent_chars = " " * level indent_chars_inside = indent_chars + " " append("{\n") try: sorted_elts = sorted(set_obj) except TypeError: # can occur when elements can't be ordered (Python 3.x) sorted_elts = set_obj for elt in sorted_elts: append(indent_chars_inside) self._check_hashable_type(type(elt)) serialize(elt, out, level + 1) append(",\n") del out[-1] # remove the last ,\n append("\n" + indent_chars + "}") elif set_obj: append("{") for elt in set_obj: self._check_hashable_type(type(elt)) serialize(elt, out, level + 1) append(",") del out[-1] # remove the last , append("}") else: # empty set literal doesn't exist unfortunately, replace with empty tuple self.ser_builtins_tuple((), out, level) dispatch[set] = ser_builtins_set def ser_builtins_frozenset(self, set_obj, out, level): self.ser_builtins_set(set_obj, out, level) dispatch[frozenset] = ser_builtins_set def ser_decimal_Decimal(self, decimal_obj, out, level): # decimal is serialized as a string to avoid losing precision out.append(repr(str(decimal_obj))) dispatch[decimal.Decimal] = ser_decimal_Decimal def ser_datetime_datetime(self, datetime_obj, out, level): out.append(repr(datetime_obj.isoformat())) dispatch[datetime.datetime] = ser_datetime_datetime def ser_datetime_date(self, date_obj, out, level): out.append(repr(date_obj.isoformat())) dispatch[datetime.date] = ser_datetime_date if os.name == "java" or sys.version_info < (2, 7): # jython bug http://bugs.jython.org/issue2010 def ser_datetime_timedelta(self, timedelta_obj, out, level): secs = ((timedelta_obj.days * 86400 + timedelta_obj.seconds) * 10 ** 6 + timedelta_obj.microseconds) / 10 ** 6 out.append(repr(secs)) else: def ser_datetime_timedelta(self, timedelta_obj, out, level): secs = timedelta_obj.total_seconds() out.append(repr(secs)) dispatch[datetime.timedelta] = ser_datetime_timedelta def ser_datetime_time(self, time_obj, out, level): out.append(repr(str(time_obj))) dispatch[datetime.time] = ser_datetime_time def ser_uuid_UUID(self, uuid_obj, out, level): out.append(repr(str(uuid_obj))) dispatch[uuid.UUID] = ser_uuid_UUID def ser_exception_class(self, exc_obj, out, level): value = { "__class__": self.get_class_name(exc_obj), "__exception__": True, "args": exc_obj.args, "attributes": vars(exc_obj) # add any custom attributes } self._serialize(value, out, level) dispatch[BaseException] = ser_exception_class def ser_array_array(self, array_obj, out, level): if array_obj.typecode == 'c': self._serialize(array_obj.tostring(), out, level) elif array_obj.typecode == 'u': self._serialize(array_obj.tounicode(), out, level) else: self._serialize(array_obj.tolist(), out, level) dispatch[array.array] = ser_array_array def ser_default_class(self, obj, out, level): if id(obj) in self.serialized_obj_ids: raise ValueError("Circular reference detected (class)") self.serialized_obj_ids.add(id(obj)) try: try: value = obj.__getstate__() if value is None and isinstance(obj, tuple): # collections.namedtuple specialcase (if it is not handled by the tuple serializer) value = { "__class__": self.get_class_name(obj), "items": list(obj._asdict().items()) } if isinstance(value, dict): self.ser_builtins_dict(value, out, level) return except AttributeError: try: value = dict(vars(obj)) # make sure we can serialize anything that resembles a dict value["__class__"] = self.get_class_name(obj) except TypeError: if hasattr(obj, "__slots__"): # use the __slots__ instead of the vars dict value = {} for slot in obj.__slots__: value[slot] = getattr(obj, slot) value["__class__"] = self.get_class_name(obj) else: raise TypeError("don't know how to serialize class " + str(obj.__class__) + ". Give it vars() or an appropriate __getstate__") self._serialize(value, out, level) finally: self.serialized_obj_ids.discard(id(obj)) def get_class_name(self, obj): if self.module_in_classname: return "%s.%s" % (obj.__class__.__module__, obj.__class__.__name__) else: return obj.__class__.__name__ Serpent-serpent-1.23/setup.cfg000066400000000000000000000000271312652634400163630ustar00rootroot00000000000000[wheel] universal = 1 Serpent-serpent-1.23/setup.py000077500000000000000000000152711312652634400162660ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Serpent: ast.literal_eval() compatible object tree serialization. # Copyright 2013, Irmen de Jong (irmen@razorvine.net) # Software license: "MIT software license". See http://opensource.org/licenses/MIT try: from setuptools import setup using_setuptools = True except ImportError: from distutils.core import setup using_setuptools = False import serpent setup( name='serpent', version=serpent.__version__, py_modules=["serpent"], license='MIT', author='Irmen de Jong', author_email='irmen@razorvine.net', description='Serialization based on ast.literal_eval', long_description=""" Serpent is a simple serialization library based on ast.literal_eval. Because it only serializes literals and recreates the objects using ast.literal_eval(), the serialized data is safe to transport to other machines (over the network for instance) and de-serialize it there. *There is also a Java and a .NET (C#) implementation available. This allows for easy data transfer between the various ecosystems. You can get the full source distribution, a Java .jar file, and a .NET assembly dll.* The java library can be obtained from Maven central (groupid ``net.razorvine`` artifactid ``serpent``), and the .NET assembly can be obtained from Nuget.org (package ``Razorvine.Serpent``). **API** - ``ser_bytes = serpent.dumps(obj, indent=False, set_literals=True, module_in_classname=False):`` # serialize obj tree to bytes - ``obj = serpent.loads(ser_bytes)`` # deserialize bytes back into object tree - You can use ``ast.literal_eval`` yourself to deserialize, but ``serpent.deserialize`` works around a few corner cases. See source for details. Serpent is more sophisticated than a simple repr() + literal_eval(): - it serializes directly to bytes (utf-8 encoded), instead of a string, so it can immediately be saved to a file or sent over a socket - it encodes byte-types as base-64 instead of inefficient escaping notation that repr would use (this does mean you have to base-64 decode these strings manually on the receiving side to get your bytes back. You can use the serpent.tobytes utility function for this.) - it contains a few custom serializers for several additional Python types such as uuid, datetime, array and decimal - it tries to serialize unrecognised types as a dict (you can control this with __getstate__ on your own types) - it can create a pretty-printed (indented) output for readability purposes - it outputs the keys of sets and dicts in alphabetical order (when pretty-printing) - it works around a few quirks of ast.literal_eval() on the various Python implementations Serpent allows comments in the serialized data (because it is just Python source code). Serpent can't serialize object graphs (when an object refers to itself); it will then crash with a ValueError pointing out the problem. Works with Python 2.7+ (including 3.x), IronPython 2.7+, Jython 2.7+. **FAQ** - Why not use XML? Answer: because XML. - Why not use JSON? Answer: because JSON is quite limited in the number of datatypes it supports, and you can't use comments in a JSON file. - Why not use pickle? Answer: because pickle has security problems. - Why not use ``repr()``/``ast.literal_eval()``? See above; serpent is a superset of this and provides more convenience. Serpent provides automatic serialization mappings for types other than the builtin primitive types. ``repr()`` can't serialize these to literals that ``ast.literal_eval()`` understands. - Why not a binary format? Answer: because binary isn't readable by humans. - But I don't care about readability. Answer: doesn't matter, ``ast.literal_eval()`` wants a literal string, so that is what we produce. - But I want better performance. Answer: ok, maybe you shouldn't use serpent in this case. Find an efficient binary protocol (protobuf?) - Why only Python, Java and C#/.NET, but no bindings for insert-favorite-language-here? Answer: I don't speak that language. Maybe you could port serpent yourself? - Where is the source? It's on Github: https://github.com/irmen/Serpent - Can I use it everywhere? Sure, as long as you keep the copyright and disclaimer somewhere. See the LICENSE file. **Demo** .. code:: python # This demo script is written for Python 3.2+ # -*- coding: utf-8 -*- from __future__ import print_function import ast import uuid import datetime import pprint import serpent class DemoClass: def __init__(self): self.i=42 self.b=False data = { "names": ["Harry", "Sally", "Peter"], "big": 2**200, "colorset": { "red", "green" }, "id": uuid.uuid4(), "timestamp": datetime.datetime.now(), "class": DemoClass(), "unicode": "€" } # serialize it ser = serpent.dumps(data, indent=True) open("data.serpent", "wb").write(ser) print("Serialized form:") print(ser.decode("utf-8")) # read it back data = serpent.load(open("data.serpent", "rb")) print("Data:") pprint.pprint(data) # you can also use ast.literal_eval if you like ser_string = open("data.serpent", "r", encoding="utf-8").read() data2 = ast.literal_eval(ser_string) assert data2==data When you run this (with python 3.2+) it prints: .. code:: python Serialized form: # serpent utf-8 python3.2 { 'big': 1606938044258990275541962092341162602522202993782792835301376, 'class': { '__class__': 'DemoClass', 'b': False, 'i': 42 }, 'colorset': { 'green', 'red' }, 'id': 'e461378a-201d-4844-8119-7c1570d9d186', 'names': [ 'Harry', 'Sally', 'Peter' ], 'timestamp': '2013-04-02T00:23:00.924000', 'unicode': '€' } Data: {'big': 1606938044258990275541962092341162602522202993782792835301376, 'class': {'__class__': 'DemoClass', 'b': False, 'i': 42}, 'colorset': {'green', 'red'}, 'id': 'e461378a-201d-4844-8119-7c1570d9d186', 'names': ['Harry', 'Sally', 'Peter'], 'timestamp': '2013-04-02T00:23:00.924000', 'unicode': '€'} """, keywords="serialization", platforms="any", classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Topic :: Software Development" ], ) Serpent-serpent-1.23/syntax.bnf000066400000000000000000000027751312652634400165730ustar00rootroot00000000000000# BNF-ish syntax of a Serpent-serialized data string. expr = single | compound . single = int | float | complex | string | bool | none . compound = tuple | dict | list | set . digit = '0'...'9' . digitnonzero = '1'...'9' . int = ['-'] digitnonzero {digit} . float = pointfloat | exponentfloat . pointfloat = [int] fraction . fraction = '.' digit { digit } . exponentfloat = (int | pointfloat) exponent . exponent = ("e" | "E") ["+" | "-"] digit { digit } . complex = complextuple | imaginary . imaginary = ['+' | '-' ] ( float | int ) 'j' . complextuple = '(' ( float | int ) imaginary ')' . string = singlequoted | doublequoted . singlequoted = '\'' stringvalue_escaped_singlequoted '\'' . doublequoted = '"' stringvalue_escaped_doublequotes '"' . bool = 'True' | 'False' . none = 'None' . expr_list = expr { ',' expr } trailingcomma . tuple = tuple_empty | tuple_one | tuple_more tuple_empty = '()' . tuple_one = '(' expr ',' ')' . tuple_more = '(' expr_list ')' . trailingcomma = '' | ',' list = list_empty | list_nonempty . list_empty = '[]' . list_nonempty = '[' expr_list ']' . set = '{' expr_list '}' . dict = '{' keyvalue_list '}' . keyvalue_list = keyvalue { ',' keyvalue } trailingcomma . keyvalue = expr ':' expr . Serpent-serpent-1.23/tests/000077500000000000000000000000001312652634400157055ustar00rootroot00000000000000Serpent-serpent-1.23/tests/example.py000066400000000000000000000020311312652634400177060ustar00rootroot00000000000000from __future__ import print_function import datetime import serpent class CustomClass(object): def __init__(self, name, age): self.name = name self.age = age def example(): data = { "tuple": (1, 2, 3), "date": datetime.datetime.now(), "set": set(['a', 'b', 'c']), "class": CustomClass("Sally", 26) } # serialize the object ser = serpent.dumps(data, indent=True) # print it to the screen, but usually you'd save the bytes to a file or transfer them over a network connection print("Serialized data:") print(ser.decode("UTF-8")) # deserialize the bytes and print the objects obj = serpent.loads(ser) print("Deserialized data:") print("tuple:", obj["tuple"]) print("date:", obj["date"]) print("set:", obj["set"]) clazz = obj["class"] print("class attributes: type={0} name={1} age={2}".format( clazz["__class__"], clazz["name"], clazz["age"])) if __name__ == "__main__": example() Serpent-serpent-1.23/tests/performance.py000066400000000000000000000140731312652634400205650ustar00rootroot00000000000000""" Prints a comparison between different serializers. Compares results based on size of the output, and time taken to (de)serialize. """ from __future__ import print_function from timeit import default_timer as perf_timer import sys import datetime import decimal import uuid class Person(object): def __init__(self, name, age): self.name = name self.age = age guid = uuid.uuid4() data = { "bytes": b"0123456789abcdefghijklmnopqrstuvwxyz" * 2000, "bytearray": bytearray(b"0123456789abcdefghijklmnopqrstuvwxyz") * 2000, "str": "\"0123456789\"\n'abcdefghijklmnopqrstuvwxyz'\t" * 2000, "unicode": u"abcdefghijklmnopqrstuvwxyz\u20ac\u20ac\u20ac\u20ac\u20ac" * 2000, "int": [123456789] * 1000, "double": [12345.987654321] * 1000, "long": [123456789123456789123456789123456789] * 1000, "tuple": [(x * x, "tuple", (300, 400, (500, 600, (x * x, x * x)))) for x in range(200)], "list": [[x * x, "list", [300, 400, [500, 600, [x * x]]]] for x in range(200)], "set": set(x * x for x in range(1000)), "dict": {str(i * i): {str(1000 + j): chr(j + 65) for j in range(5)} for i in range(100)}, "exception": [ZeroDivisionError("test exeception", x * x) for x in range(1000)], "class": [Person("harry", x * x) for x in range(1000)], "datetime": [datetime.datetime.now() for x in range(1000)], "complex": [complex(x + x, x * x) for x in range(1000)], "decimal": [decimal.Decimal("1122334455667788998877665544332211.9876543212345678987654321123456789") for x in range(1000)], "uuid": [guid for x in range(1000)] } serializers = {} try: import pickle serializers["pickle"] = (pickle.dumps, pickle.loads) except ImportError: pass try: import cPickle serializers["cpickle"] = (cPickle.dumps, cPickle.loads) except ImportError: pass import json serializers["json"] = (lambda d: json.dumps(d).encode("utf-8"), lambda d: json.loads(d.decode("utf-8"))) import serpent serializers["serpent"] = (serpent.dumps, serpent.loads) import marshal serializers["marshal"] = (marshal.dumps, marshal.loads) try: import msgpack serializers["msgpack"] = (lambda d: msgpack.packb(d, use_bin_type=True), lambda d: msgpack.unpackb(d, encoding="utf-8")) except ImportError: pass try: import xmlrpclib as xmlrpc except ImportError: import xmlrpc.client as xmlrpc def xmldumps(data): return xmlrpc.dumps((data,)).encode("utf-8") def xmlloads(data): return xmlrpc.loads(data.decode("utf-8"))[0] serializers["xmlrpc"] = (xmldumps, xmlloads) no_result = 9999999999 def run(): results = {} number = 10 repeat = 3 for ser in serializers: print("serializer:", ser) results[ser] = {"sizes": {}, "ser-times": {}, "deser-times": {}} for key in sorted(data): print(key, end="; ") sys.stdout.flush() try: serialized = serializers[ser][0](data[key]) except (TypeError, ValueError, OverflowError): print("error!") results[ser]["sizes"][key] = no_result results[ser]["ser-times"][key] = no_result results[ser]["deser-times"][key] = no_result else: results[ser]["sizes"][key] = len(serialized) durations_ser = [] durations_deser = [] serializer, deserializer = serializers[ser] serialized_data = serializer(data[key]) for _ in range(repeat): start = perf_timer() for _ in range(number): serializer(data[key]) durations_ser.append(perf_timer() - start) for _ in range(repeat): start = perf_timer() for _ in range(number): deserializer(serialized_data) durations_deser.append(perf_timer() - start) duration_ser = min(durations_ser) duration_deser = min(durations_deser) results[ser]["ser-times"][key] = round(duration_ser * 1e6 / number, 2) results[ser]["deser-times"][key] = round(duration_deser * 1e6 / number, 2) print() return results def tables_size(results): print("\nSIZE RESULTS\n") sizes_per_datatype = {} for ser in results: for datatype in results[ser]["sizes"]: size = results[ser]["sizes"][datatype] if datatype not in sizes_per_datatype: sizes_per_datatype[datatype] = [] sizes_per_datatype[datatype].append((size, ser)) sizes_per_datatype = {datatype: sorted(sizes) for datatype, sizes in sizes_per_datatype.items()} for dt in sorted(sizes_per_datatype): print(dt) for pos, (size, serializer) in enumerate(sizes_per_datatype[dt]): if size == no_result: size = "unsupported" else: size = "%8d" % size print(" %2d: %-8s %s" % (pos + 1, serializer, size)) print() def tables_speed(results, what_times, header): print("\n%s\n" % header) durations_per_datatype = {} for ser in results: for datatype in results[ser]["sizes"]: duration = results[ser][what_times][datatype] if datatype not in durations_per_datatype: durations_per_datatype[datatype] = [] durations_per_datatype[datatype].append((duration, ser)) durations_per_datatype = {datatype: sorted(durations) for datatype, durations in durations_per_datatype.items()} for dt in sorted(durations_per_datatype): print(dt) for pos, (duration, serializer) in enumerate(durations_per_datatype[dt]): if duration == no_result: duration = "unsupported" else: duration = "%8d" % duration print(" %2d: %-8s %s" % (pos + 1, serializer, duration)) print() if __name__ == "__main__": results = run() tables_size(results) tables_speed(results, "ser-times", "SPEED RESULTS (SERIALIZATION)") tables_speed(results, "deser-times", "SPEED RESULTS (DESERIALIZATION)") Serpent-serpent-1.23/tests/test_serpent.py000066400000000000000000001321551312652634400210050ustar00rootroot00000000000000""" Serpent: ast.literal_eval() compatible object tree serialization. Copyright 2013, Irmen de Jong (irmen@razorvine.net) Software license: "MIT software license". See http://opensource.org/licenses/MIT """ from __future__ import print_function, division import __future__ import sys import ast import timeit import datetime import uuid import decimal import array import tempfile import os import hashlib import traceback import threading import time import collections import types if sys.version_info < (2, 7): import unittest2 as unittest else: import unittest import serpent if sys.version_info >= (3, 0): unicode = str unichr = chr def strip_header(ser): if sys.platform == "cli": _, _, data = ser.partition("\n") else: _, _, data = ser.partition(b"\n") return data class TestDeserialize(unittest.TestCase): def test_deserialize(self): data = serpent.loads(b"555") self.assertEqual(555, data) def test_deserialize_unichr(self): unicodestring = u"euro\u20ac" encoded = repr(unicodestring).encode("utf-8") data = serpent.loads(encoded) self.assertEqual(unicodestring, data) @unittest.skipIf(sys.version_info < (3, 0), "Python 2.x ast can't parse complex") def test_weird_complex(self): c1 = complex(float('inf'), 4) ser = serpent.dumps(c1) c2 = serpent.loads(ser) self.assertEqual(c1, c2) c3 = serpent.loads(b"(1e30000+4.0j)") self.assertEqual(c1, c3) def test_trailing_commas(self): v = serpent.loads(b"[1,2,3,]") self.assertEqual([1, 2, 3], v) v = serpent.loads(b"(1,2,3,)") self.assertEqual((1, 2, 3), v) v = serpent.loads(b"{'a':1, 'b':2, 'c':3,}") self.assertEqual({'a': 1, 'b': 2, 'c': 3}, v) @unittest.skipIf(sys.version_info < (3, 2), "needs python 3.3+ to parse set literals") def test_trailing_comma_set(self): v = serpent.loads(b"{1,2,3,}") self.assertEqual(set([1, 2, 3]), v) def test_unicode_escapes(self): v = serpent.loads(b"'\u20ac'") self.assertEqual(u"\u20ac", v) v = serpent.loads(b"'\U00022001'") self.assertEqual(u"\U00022001", v) def test_input_types(self): bytes_input = b"'text'" bytearray_input = bytearray(bytes_input) memview_input = memoryview(bytes_input) self.assertEqual("text", serpent.loads(bytes_input)) self.assertEqual("text", serpent.loads(bytearray_input)) self.assertEqual("text", serpent.loads(memview_input)) if sys.version_info < (3, 0): buffer_input = buffer(bytes_input) self.assertEqual("text", serpent.loads(buffer_input)) class TestBasics(unittest.TestCase): def test_py2_py3_unicode_repr(self): data = u"hello\u20ac" py2repr = b"# serpent utf-8 python2.6\n'hello\\u20ac'" result = serpent.loads(py2repr) self.assertEqual(data, result, "must understand python 2.x repr form of unicode string") py3repr = b"# serpent utf-8 python3.2\n'hello\xe2\x82\xac'" try: result = serpent.loads(py3repr) # jython fails this test. if os.name != "java": self.assertEqual(data, result, "must understand python 3.x repr form of unicode string") except ValueError as x: if os.name == "java": self.assertIn("issue2008", str(x)) else: self.fail("non-jython must parse it correctly") def test_header(self): ser = serpent.dumps(None, set_literals=True) if sys.platform == "cli": header, _, rest = ser.partition("\n") else: self.assertTrue(type(ser) is bytes) header, _, rest = ser.partition(b"\n") hdr = "# serpent utf-8 python3.2".encode("utf-8") self.assertEqual(hdr, header) ser = serpent.dumps(None, set_literals=False) if sys.platform == "cli": header, _, rest = ser.partition("\n") else: self.assertTrue(type(ser) is bytes) header, _, rest = ser.partition(b"\n") hdr = "# serpent utf-8 python2.6".encode("utf-8") # don't change the 2.6 here even though we don't support python 2.6 any longer self.assertEqual(hdr, header) def test_comments(self): ser = b"""# serpent utf-8 python2.7 [ 1, 2, # some comments here 3, 4] # more here # and here.""" data = serpent.loads(ser) self.assertEqual([1, 2, 3, 4], data) ser = b"[ 1, 2 ]" # no header whatsoever data = serpent.loads(ser) self.assertEqual([1, 2], data) def test_sorting(self): obj = [3, 2, 1] ser = serpent.dumps(obj) data = strip_header(ser) self.assertEqual(b"[3,2,1]", data) obj = (3, 2, 1) ser = serpent.dumps(obj) data = strip_header(ser) self.assertEqual(b"(3,2,1)", data) obj = {3: "three", 4: "four", 2: "two", 1: "one"} ser = serpent.dumps(obj) data = strip_header(ser) self.assertEqual(36, len(data)) obj = set([3, 4, 2, 1, 6, 5]) ser = serpent.dumps(obj) data = strip_header(ser) self.assertEqual(13, len(data)) ser = serpent.dumps(obj, indent=True, set_literals=True) data = strip_header(ser) self.assertEqual(b"{\n 1,\n 2,\n 3,\n 4,\n 5,\n 6\n}", data) # sorted obj = set([3, "something"]) ser = serpent.dumps(obj, indent=False, set_literals=True) data = strip_header(ser) self.assertTrue(data == b"{3,'something'}" or data == b"{'something',3}") ser = serpent.dumps(obj, indent=True, set_literals=True) data = strip_header(ser) self.assertTrue(data == b"{\n 3,\n 'something'\n}" or data == b"{\n 'something',\n 3\n}") obj = {3: "three", "something": 99} ser = serpent.dumps(obj, indent=False, set_literals=True) data = strip_header(ser) self.assertTrue(data == b"{'something':99,3:'three'}" or data == b"{3:'three','something':99}") ser = serpent.dumps(obj, indent=True, set_literals=True) data = strip_header(ser) self.assertTrue(data == b"{\n 'something': 99,\n 3: 'three'\n}" or data == b"{\n 3: 'three',\n 'something': 99\n}") obj = {3: "three", 4: "four", 5: "five", 2: "two", 1: "one"} ser = serpent.dumps(obj, indent=True, set_literals=True) data = strip_header(ser) self.assertEqual(b"{\n 1: 'one',\n 2: 'two',\n 3: 'three',\n 4: 'four',\n 5: 'five'\n}", data) # sorted def test_none(self): ser = serpent.dumps(None) data = strip_header(ser) self.assertEqual(b"None", data) def test_string(self): ser = serpent.dumps("hello") data = strip_header(ser) self.assertEqual(b"'hello'", data) ser = serpent.dumps("quotes'\"") data = strip_header(ser) self.assertEqual(b"'quotes\\'\"'", data) ser = serpent.dumps("quotes2'") data = strip_header(ser) self.assertEqual(b"\"quotes2'\"", data) def test_string_with_escapes(self): ser = serpent.dumps("\n") d = strip_header(ser) self.assertEqual(b"'\\n'", d) ser = serpent.dumps("\a") d = strip_header(ser) self.assertEqual(b"'\\x07'", d) # repr() does this hex escape line = "'hello\nlastline\ttab\\@slash\a\b\f\n\r\t\v'" ser = serpent.dumps(line) d = strip_header(ser) self.assertEqual(b"\"'hello\\nlastline\\ttab\\\\@slash\\x07\\x08\\x0c\\n\\r\\t\\x0b'\"", d) # the hex escapes are done by repr() data = serpent.loads(ser) self.assertEqual(line, data) def test_nullbytesstring(self): ser = serpent.dumps(u"\x00null") data = serpent.loads(ser) self.assertEqual("\x00null", data) ser = serpent.dumps(u"\x01") self.assertEqual(b"'\\x01'", strip_header(ser)) data = serpent.loads(ser) self.assertEqual("\x01", data) ser = serpent.dumps(u"\x1f") self.assertEqual(b"'\\x1f'", strip_header(ser)) data = serpent.loads(ser) self.assertEqual("\x1f", data) ser = serpent.dumps(u"\x20") self.assertEqual(b"' '", strip_header(ser)) data = serpent.loads(ser) self.assertEqual(" ", data) def test_nullbytesunicode(self): line = unichr(0) + "null" ser = serpent.dumps(line) data = strip_header(ser) self.assertEqual(b"'\\x00null'", data, "must escape 0-byte") data = serpent.loads(ser) self.assertEqual(line, data) def test_detectNullByte(self): with self.assertRaises(ValueError) as ex: serpent.loads(b"'contains\x00nullbyte'") self.fail("must fail") self.assertTrue("0-bytes" in str(ex.exception)) with self.assertRaises(ValueError) as ex: serpent.loads(bytearray(b"'contains\x00nullbyte'")) self.fail("must fail") self.assertTrue("0-bytes" in str(ex.exception)) with self.assertRaises(ValueError) as ex: serpent.loads(memoryview(b"'contains\x00nullbyte'")) self.fail("must fail") self.assertTrue("0-bytes" in str(ex.exception)) serpent.loads(bytearray(b"'contains no nullbyte'")) serpent.loads(memoryview(b"'contains no nullbyte'")) @unittest.skipIf(os.name == "java", "jython can't parse unicode U's") def test_unicode_U(self): u = "euro" + unichr(0x20ac)+"\U00022001" self.assertTrue(type(u) is unicode) ser = serpent.dumps(u) data = serpent.loads(ser) self.assertEqual(u, data) def test_unicode_escape_allchars(self): # this checks for all 0x0000-0xffff chars that they will be serialized # into a proper repr form and when processed back by ast.literal_parse directly # will get turned back into the chars 0x0000-0xffff again # For Jython we take the range upto 0x4100 because after that it starts # complaining about surrogates or "mark invalid" (an utf-8 parsing bug it seems) highest_char = 0x4100 if os.name == "java" else 0xffff all_chars = u"".join(unichr(c) for c in range(highest_char+1)) ser = serpent.dumps(all_chars) self.assertGreater(len(ser), len(all_chars)) ser = ser.decode("utf-8") if sys.version_info < (3, 0): ser = compile(ser, "", mode="eval", flags=ast.PyCF_ONLY_AST | __future__.unicode_literals.compiler_flag) if os.name == "java": # The ast module in Jython will not have parsed this correctly into unicode literals. # So we have to patch up the ast tree ourselves and decode Str nodes to unicode manually. # (this is the same what Serpent does internally so we replicate it here) # See http://bugs.jython.org/issue2008 for node in ast.walk(ser): if isinstance(node, ast.Str): node.s = node.s.decode("unicode-escape") data = ast.literal_eval(ser) self.assertEqual(highest_char+1, len(data)) for i, c in enumerate(data): if unichr(i) != c: self.fail("char different for "+str(i)) def test_unicode_quotes(self): ser = serpent.dumps(unicode("quotes'\"")) data = strip_header(ser) self.assertEqual(b"'quotes\\'\"'", data) ser = serpent.dumps(unicode("quotes2'")) data = strip_header(ser) self.assertEqual(b"\"quotes2'\"", data) def test_utf8_correctness(self): u = u"\x00\x01\x80\x81\xfe\xffabcdef\u20ac" utf_8_correct = repr(u).encode("utf-8") if utf_8_correct.startswith(b"u"): utf_8_correct=utf_8_correct[1:] ser = serpent.dumps(u) d = strip_header(ser) self.assertEqual(utf_8_correct, d) @unittest.skipIf(sys.version_info >= (3, 0), "py2 escaping tested") def test_unicode_with_escapes_py2(self): ser = serpent.dumps(unicode("\n")) d = strip_header(ser) self.assertEqual(b"'\\n'", d) ser = serpent.dumps(unicode("\a")) d = strip_header(ser) self.assertEqual(b"'\\x07'", d) @unittest.skipIf(sys.version_info >= (3, 0), "py2 escaping tested") def test_unicode_with_escapes_unichrs(self): ser = serpent.dumps("\a"+unichr(0x20ac)) d = strip_header(ser) self.assertEqual(b"'\\x07\\u20ac'", d) line = "'euro" + unichr(0x20ac) + "\nlastline\ttab\\@slash\a\b\f\n\r\t\v'" ser = serpent.dumps(line) d = strip_header(ser) self.assertEqual(b"\"'euro\\u20ac\\nlastline\\ttab\\\\@slash\\x07\\x08\\x0c\\n\\r\\t\\x0b'\"", d) data = serpent.loads(ser) self.assertEqual(line, data) @unittest.skipIf(sys.version_info < (3, 0), "py3 escaping tested") def test_unicode_with_escapes_py3(self): ser = serpent.dumps(unicode("\n")) d = strip_header(ser) self.assertEqual(b"'\\n'", d) ser = serpent.dumps(unicode("\a")) d = strip_header(ser) self.assertEqual(b"'\\x07'", d) ser = serpent.dumps("\a"+unichr(0x20ac)) d = strip_header(ser) self.assertEqual(b"'\\x07\xe2\x82\xac'", d) line = "'euro" + unichr(0x20ac) + "\nlastline\ttab\\@slash\a\b\f\n\r\t\v'" ser = serpent.dumps(line) d = strip_header(ser) self.assertEqual(b"\"'euro\xe2\x82\xac\\nlastline\\ttab\\\\@slash\\x07\\x08\\x0c\\n\\r\\t\\x0b'\"", d) data = serpent.loads(ser) self.assertEqual(line, data) def test_numbers(self): ser = serpent.dumps(12345) data = strip_header(ser) self.assertEqual(b"12345", data) ser = serpent.dumps(123456789123456789123456789) data = strip_header(ser) self.assertEqual(b"123456789123456789123456789", data) ser = serpent.dumps(99.1234) data = strip_header(ser) if sys.platform == 'cli': self.assertEqual(b"99.123400000000004", data) else: self.assertEqual(b"99.1234", data) ser = serpent.dumps(decimal.Decimal("1234.9999999999")) data = strip_header(ser) self.assertEqual(b"'1234.9999999999'", data) ser = serpent.dumps(2 + 3j) data = strip_header(ser) self.assertEqual(b"(2.0+3.0j)", data) ser = serpent.dumps(2 - 3j) data = strip_header(ser) self.assertEqual(b"(2.0-3.0j)", data) def test_bool(self): ser = serpent.dumps(True) data = strip_header(ser) self.assertEqual(b"True", data) def test_dict(self): ser = serpent.dumps({}) data = strip_header(ser) self.assertEqual(b"{}", data) ser = serpent.dumps({}, indent=True) data = strip_header(ser) self.assertEqual(b"{}", data) mydict = { 42: 'fortytwo', 'status': False, 'name': 'Sally', 'sixteen-and-half': 16.5 } ser = serpent.dumps(mydict) data = strip_header(ser) self.assertEqual(69, len(data)) if sys.version_info < (3, 0): self.assertEqual(b"{", data[0]) self.assertEqual(b"}", data[-1]) else: self.assertEqual(ord("{"), data[0]) self.assertEqual(ord("}"), data[-1]) ser = serpent.dumps(mydict, indent=True) data = strip_header(ser) self.assertEqual(86, len(data)) if sys.version_info < (3, 0): self.assertEqual(b"{", data[0]) self.assertEqual(b"}", data[-1]) else: self.assertEqual(ord("{"), data[0]) self.assertEqual(ord("}"), data[-1]) def test_dict_iters(self): data = {"john": 22, "sophie": 34, "bob": 26} ser = serpent.loads(serpent.dumps(data.keys())) self.assertIsInstance(ser, list) self.assertEqual(["bob", "john", "sophie"], sorted(ser)) ser = serpent.loads(serpent.dumps(data.values())) self.assertIsInstance(ser, list) self.assertEqual([22, 26, 34], sorted(ser)) ser = serpent.loads(serpent.dumps(data.items())) self.assertIsInstance(ser, list) self.assertEqual([("bob", 26), ("john", 22), ("sophie", 34)], sorted(ser)) def test_list(self): ser = serpent.dumps([]) data = strip_header(ser) self.assertEqual(b"[]", data) ser = serpent.dumps([], indent=True) data = strip_header(ser) self.assertEqual(b"[]", data) mylist = [42, "Sally", 16.5] ser = serpent.dumps(mylist) data = strip_header(ser) self.assertEqual(b"[42,'Sally',16.5]", data) ser = serpent.dumps(mylist, indent=True) data = strip_header(ser) self.assertEqual(b"""[ 42, 'Sally', 16.5 ]""", data) def test_tuple(self): ser = serpent.dumps(tuple()) data = strip_header(ser) self.assertEqual(b"()", data) ser = serpent.dumps(tuple(), indent=True) data = strip_header(ser) self.assertEqual(b"()", data) ser = serpent.dumps((1,)) data = strip_header(ser) self.assertEqual(b"(1,)", data) ser = serpent.dumps((1,), indent=True) data = strip_header(ser) self.assertEqual(b"(\n 1,\n)", data) mytuple = (42, "Sally", 16.5) ser = serpent.dumps(mytuple) data = strip_header(ser) self.assertEqual(b"(42,'Sally',16.5)", data) ser = serpent.dumps(mytuple, indent=True) data = strip_header(ser) self.assertEqual(b"""( 42, 'Sally', 16.5 )""", data) def test_set(self): ser = serpent.dumps(set()) data = strip_header(ser) self.assertEqual(b"()", data) ser = serpent.dumps(set(), indent=True) data = strip_header(ser) self.assertEqual(b"()", data) # test set-literals myset = set([42, "Sally"]) ser = serpent.dumps(myset, set_literals=True) data = strip_header(ser) self.assertTrue(data == b"{42,'Sally'}" or data == b"{'Sally',42}") ser = serpent.dumps(myset, indent=True, set_literals=True) data = strip_header(ser) self.assertTrue(data == b"{\n 42,\n 'Sally'\n}" or data == b"{\n 'Sally',\n 42\n}") # test no set-literals ser = serpent.dumps(myset, set_literals=False) data = strip_header(ser) self.assertTrue(data == b"(42,'Sally')" or data == b"('Sally',42)") # must output a tuple instead of a set-literal def test_bytes(self): if sys.version_info >= (3, 0): ser = serpent.dumps(bytes(b"abcdef")) data = serpent.loads(ser) self.assertEqual({'encoding': 'base64', 'data': 'YWJjZGVm'}, data) ser = serpent.dumps(bytearray(b"abcdef")) data = serpent.loads(ser) self.assertEqual({'encoding': 'base64', 'data': 'YWJjZGVm'}, data) if sys.version_info >= (2, 7): ser = serpent.dumps(memoryview(b"abcdef")) data = serpent.loads(ser) self.assertEqual({'encoding': 'base64', 'data': 'YWJjZGVm'}, data) if sys.version_info < (3, 0): ser = serpent.dumps(buffer(b"abcdef")) data = serpent.loads(ser) self.assertEqual({'encoding': 'base64', 'data': 'YWJjZGVm'}, data) def test_exception(self): x = ZeroDivisionError("wrong") ser = serpent.dumps(x) data = serpent.loads(ser) self.assertEqual({ '__class__': 'ZeroDivisionError', '__exception__': True, 'args': ('wrong',), 'attributes': {} }, data) x = ZeroDivisionError("wrong", 42) ser = serpent.dumps(x) data = serpent.loads(ser) self.assertEqual({ '__class__': 'ZeroDivisionError', '__exception__': True, 'args': ('wrong', 42), 'attributes': {} }, data) x.custom_attribute = "custom_attr" ser = serpent.dumps(x) data = serpent.loads(ser) self.assertEqual({ '__class__': 'ZeroDivisionError', '__exception__': True, 'args': ('wrong', 42), 'attributes': {'custom_attribute': 'custom_attr'} }, data) def test_exception2(self): x = ZeroDivisionError("wrong") ser = serpent.dumps(x, module_in_classname=True) data = serpent.loads(ser) if sys.version_info < (3, 0): expected_classname = "exceptions.ZeroDivisionError" else: expected_classname = "builtins.ZeroDivisionError" self.assertEqual({ '__class__': expected_classname, '__exception__': True, 'args': ('wrong',), 'attributes': {} }, data) def test_class(self): class Class1(object): def __init__(self): self.attr = 1 class Class2(object): def __getstate__(self): return {"attr": 42} class SlotsClass(object): __slots__ = ["attr"] def __init__(self): self.attr = 1 c = Class1() ser = serpent.dumps(c) data = serpent.loads(ser) self.assertEqual({'__class__': 'Class1', 'attr': 1}, data) c = Class2() ser = serpent.dumps(c) data = serpent.loads(ser) self.assertEqual({'attr': 42}, data) c = SlotsClass() ser = serpent.dumps(c) data = serpent.loads(ser) self.assertEqual({'__class__': 'SlotsClass', 'attr': 1}, data) import pprint p = pprint.PrettyPrinter(stream="dummy", width=99) ser = serpent.dumps(p) data = serpent.loads(ser) self.assertEqual("PrettyPrinter", data["__class__"]) self.assertEqual(99, data["_width"]) def test_class2(self): import pprint pp = pprint.PrettyPrinter(stream="dummy", width=42) ser = serpent.dumps(pp, module_in_classname=True) data = serpent.loads(ser) self.assertEqual('pprint.PrettyPrinter', data["__class__"]) def test_class_hashable_key_check(self): import pprint pp = pprint.PrettyPrinter(stream="dummy", width=42) with self.assertRaises(TypeError) as x: serpent.dumps({1: 1, 2: 1, 3: 1, strip_header: 1}) # can only serialize simple types as dict keys (hashable) self.assertTrue("hashable type" in str(x.exception)) with self.assertRaises(TypeError) as x: serpent.dumps({1: 1, 2: 1, 3: 1, pp: 1}) # can only serialize simple types as dict keys (hashable) self.assertTrue("hashable type" in str(x.exception)) @unittest.skipIf(not serpent.can_use_set_literals, reason="no problem if serpent doesn't serializes set literals") def test_class_hashable_set_element_check(self): import pprint pp = pprint.PrettyPrinter(stream="dummy", width=42) with self.assertRaises(TypeError) as x: serpent.dumps({1, 2, 3, strip_header}) # can only serialize simple typles as set elements (hashable) self.assertTrue("hashable type" in str(x.exception)) with self.assertRaises(TypeError) as x: serpent.dumps({1, 2, 3, pp}) # can only serialize simple typles as set elements (hashable) self.assertTrue("hashable type" in str(x.exception)) @unittest.skipIf(sys.version_info < (3, 4), reason="python 3.4 introduced enums") def test_enum_hashable(self): import enum class Color(enum.Enum): RED = 1 GREEN = 2 BLUE = 3 data = serpent.dumps({"abc", Color.RED, Color.GREEN, Color.BLUE}) orig = serpent.loads(data) self.assertEqual({"abc", 1, 2, 3}, orig) data = serpent.dumps({"abc": 1, Color.RED: 1, Color.GREEN: 1, Color.BLUE: 1}) orig = serpent.loads(data) self.assertEqual({"abc": 1, 1: 1, 2: 1, 3: 1}, orig) def test_array(self): ser = serpent.dumps(array.array('u', unicode("unicode"))) data = strip_header(ser) self.assertEqual(b"'unicode'", data) ser = serpent.dumps(array.array('i', [44, 45, 46])) data = strip_header(ser) self.assertEqual(b"[44,45,46]", data) if sys.version_info < (3, 0): ser = serpent.dumps(array.array('c', "normal")) data = strip_header(ser) self.assertEqual(b"'normal'", data) def test_time(self): ser = serpent.dumps(datetime.datetime(2013, 1, 20, 23, 59, 45, 999888)) data = strip_header(ser) self.assertEqual(b"'2013-01-20T23:59:45.999888'", data) ser = serpent.dumps(datetime.date(2013, 1, 20)) data = strip_header(ser) self.assertEqual(b"'2013-01-20'", data) ser = serpent.dumps(datetime.time(23, 59, 45, 999888)) data = strip_header(ser) self.assertEqual(b"'23:59:45.999888'", data) ser = serpent.dumps(datetime.time(23, 59, 45)) data = strip_header(ser) self.assertEqual(b"'23:59:45'", data) ser = serpent.dumps(datetime.timedelta(1, 4000, 999888, minutes=22)) data = strip_header(ser) if sys.platform == 'cli': self.assertEqual(b"91720.999888000006", data) else: self.assertEqual(b"91720.999888", data) ser = serpent.dumps(datetime.timedelta(seconds=12345)) data = strip_header(ser) self.assertEqual(b"12345.0", data) def test_timezone(self): import pytz # requires pytz library tz_nl = pytz.timezone("Europe/Amsterdam") dt_tz = tz_nl.localize(datetime.datetime(2013, 1, 20, 23, 59, 45, 999888)) ser = serpent.dumps(dt_tz) data = strip_header(ser) self.assertEqual(b"'2013-01-20T23:59:45.999888+01:00'", data) # normal time dt_tz = tz_nl.localize(datetime.datetime(2013, 5, 10, 13, 59, 45, 999888)) ser = serpent.dumps(dt_tz) data = strip_header(ser) self.assertEqual(b"'2013-05-10T13:59:45.999888+02:00'", data) # daylight saving time def test_pickle_api(self): ser = serpent.dumps([1, 2, 3]) serpent.loads(ser) tmpfn = tempfile.mktemp() with open(tmpfn, "wb") as outf: serpent.dump([1, 2, 3], outf, indent=True, set_literals=True) with open(tmpfn, "rb") as inf: data = serpent.load(inf) self.assertEqual([1, 2, 3], data) os.remove(tmpfn) def test_weird_floats(self): values = [float('inf'), float('-inf'), float('nan'), complex(float('inf'), 4)] ser = serpent.dumps(values) ser = strip_header(serpent.dumps(values)) self.assertEqual(b"[1e30000,-1e30000,{'__class__':'float','value':'nan'},(1e30000+4.0j)]", ser) values2 = serpent.loads(ser) self.assertEqual([float('inf'), float('-inf'), {'__class__':'float','value':'nan'}, (float('inf')+4j)], values2) values2 = serpent.loads(b"[1e30000,-1e30000]") self.assertEqual([float('inf'), float('-inf')], values2) def test_float_precision(self): # make sure we don't lose precision when converting floats (including scientific notation) v = serpent.loads(serpent.dumps(1.2345678987654321)) self.assertEqual(1.2345678987654321, v) v = serpent.loads(serpent.dumps(5555.12345678987656)) self.assertEqual(5555.12345678987656, v) v = serpent.loads(serpent.dumps(98765432123456.12345678987656)) self.assertEqual(98765432123456.12345678987656, v) v = serpent.loads(serpent.dumps(98765432123456.12345678987656e+44)) self.assertEqual(98765432123456.12345678987656e+44, v) v = serpent.loads(serpent.dumps((98765432123456.12345678987656e+44+665544332211.9998877665544e+33j))) self.assertEqual((98765432123456.12345678987656e+44+665544332211.9998877665544e+33j), v) v = serpent.loads(serpent.dumps((-98765432123456.12345678987656e+44 -665544332211.9998877665544e+33j))) self.assertEqual((-98765432123456.12345678987656e+44 -665544332211.9998877665544e+33j), v) @unittest.skipIf(sys.version_info < (3, 4), "needs python 3.4 to test enum type") def test_enums(self): import enum class Animal(enum.Enum): BEE = 1 CAT = 2 DOG = 3 v = serpent.loads(serpent.dumps(Animal.CAT)) self.assertEqual(2, v) Animal2 = enum.Enum("Animals2", "BEE CAT DOG HORSE RABBIT") v = serpent.loads(serpent.dumps(Animal2.HORSE)) self.assertEqual(4, v) def test_tobytes(self): obj = b"test" self.assertIs(obj, serpent.tobytes(obj)) obj = memoryview(b"test") self.assertIs(obj, serpent.tobytes(obj)) obj = bytearray(b"test") self.assertIs(obj, serpent.tobytes(obj)) if hasattr(types, "BufferType"): obj = buffer(b"test") self.assertIs(obj, serpent.tobytes(obj)) ser = {'data': 'dGVzdA==', 'encoding': 'base64'} out = serpent.tobytes(ser) self.assertEqual(b"test", out) if sys.platform == 'cli': self.assertIsInstance(out, str) # ironpython base64 decodes into str type.... else: self.assertIsInstance(out, bytes) with self.assertRaises(TypeError): serpent.tobytes({'@@@data': 'dGVzdA==', 'encoding': 'base64'}) with self.assertRaises(TypeError): serpent.tobytes({'data': 'dGVzdA==', '@@@encoding': 'base64'}) with self.assertRaises(TypeError): serpent.tobytes({'data': 'dGVzdA==', 'encoding': 'base99'}) with self.assertRaises(TypeError): serpent.tobytes({}) with self.assertRaises(TypeError): serpent.tobytes(42) @unittest.skip("no performance tests in default test suite") class TestSpeed(unittest.TestCase): def setUp(self): self.data = { "str": "hello", "unicode": unichr(0x20ac), # euro-character "numbers": [123456789012345678901234567890, 999.1234, decimal.Decimal("1.99999999999999999991")], "bytes": bytearray(100), "list": [1, 2, 3, 4, 5, 6, 7, 8, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9], "tuple": (1, 2, 3, 4, 5, 6, 7, 8), "set": set([1, 2, 3, 4, 5, 6, 7, 8, 9]), "dict": dict((i, str(i) * 4) for i in range(10)), "exc": ZeroDivisionError("fault"), "dates": [ datetime.datetime.now(), datetime.date.today(), datetime.time(23, 59, 45, 999888), datetime.timedelta(seconds=500) ], "uuid": uuid.uuid4() } self.floatlist = [12345.6789] * 1000 def test_ser_speed(self): print("serialize without indent:", timeit.timeit(lambda: serpent.dumps(self.data, False), number=1000)) print("serialize long list of floats:", timeit.timeit(lambda: serpent.dumps(self.floatlist, False), number=100)) print("serialize with indent:", timeit.timeit(lambda: serpent.dumps(self.data, True), number=1000)) def test_deser_speed(self): ser = serpent.dumps(self.data, False) print("deserialize without indent:", timeit.timeit(lambda: serpent.loads(ser), number=1000)) ser = serpent.dumps(self.data, True) print("deserialize with indent:", timeit.timeit(lambda: serpent.loads(ser), number=1000)) class TestIndent(unittest.TestCase): def test_indent_primitive(self): data = 12345 ser = serpent.dumps(data, indent=True).decode("utf-8") _, _, ser = ser.partition("\n") self.assertEqual("12345", ser) def test_indent_sorting(self): # non-indented should not be sorted, indented should data = {"ee": 1, "dd": 1, "cc": 1, "bb": 1, "aa": 1, 'ff': 1, 'hh': 1, 'gg': 1} ser = serpent.dumps(data, False) ser = strip_header(ser) self.assertNotEqual(b"{'aa':1,'bb':1,'cc':1,'dd':1,'ee':1,'ff':1,'gg':1,'hh':1}", ser) ser = serpent.dumps(data, True) ser = strip_header(ser) self.assertEqual(b"""{ 'aa': 1, 'bb': 1, 'cc': 1, 'dd': 1, 'ee': 1, 'ff': 1, 'gg': 1, 'hh': 1 }""", ser) data = set("irmen de jong irmen de jong666") ser = serpent.dumps(data, False) ser = strip_header(ser) self.assertNotEqual(b"' ','6','d','e','g','i','j','m','n','o','r'", ser[1:-1]) ser = serpent.dumps(data, True) ser = strip_header(ser) self.assertEqual(b"\n ' ',\n '6',\n 'd',\n 'e',\n 'g',\n 'i',\n 'j',\n 'm',\n 'n',\n 'o',\n 'r'\n", ser[1:-1]) def test_indent_containers(self): data = [1, 2, 3] ser = serpent.dumps(data, indent=True).decode("utf-8") _, _, ser = ser.partition("\n") self.assertEqual("""[ 1, 2, 3 ]""", ser) data = (1, 2, 3) ser = serpent.dumps(data, indent=True).decode("utf-8") _, _, ser = ser.partition("\n") self.assertEqual("""( 1, 2, 3 )""", ser) data = set([1]) ser = serpent.dumps(data, indent=True, set_literals=True).decode("utf-8") _, _, ser = ser.partition("\n") self.assertEqual("""{ 1 }""", ser) data = {"one": 1} ser = serpent.dumps(data, indent=True, set_literals=True).decode("utf-8") _, _, ser = ser.partition("\n") self.assertEqual("""{ 'one': 1 }""", ser) data = {"first": [1, 2, ("a", "b")], "second": {1: False}, "third": set([1, 2])} ser = serpent.dumps(data, indent=True, set_literals=True).decode("utf-8") _, _, ser = ser.partition("\n") self.assertEqual("""{ 'first': [ 1, 2, ( 'a', 'b' ) ], 'second': { 1: False }, 'third': { 1, 2 } }""", ser) class TestFiledump(unittest.TestCase): def testFile(self): if sys.version_info < (3, 2): self.skipTest("testdatafile contains stuff that is not supported by ast.literal_eval on Python < 3.2") with open("testserpent.utf8.bin", "rb") as file: data = file.read() obj = serpent.loads(data) self.assertEqual(-3 + 8j, obj["numbers"][3]) class TestInterceptClass(unittest.TestCase): def testRegular(self): import pprint p = pprint.PrettyPrinter(stream="dummy", width=42) ser = serpent.dumps(p) data = serpent.loads(ser) self.assertEqual(42, data["_width"]) self.assertEqual("PrettyPrinter", data["__class__"]) def testIntercept(self): ex = ZeroDivisionError("wrong") ser = serpent.dumps(ex) data = serpent.loads(ser) # default behavior is to serialize the exception to a dict self.assertEqual({'__exception__': True, 'args': ('wrong',), '__class__': 'ZeroDivisionError', 'attributes': {}}, data) def custom_exception_translate(obj, serializer, stream, indent): serializer._serialize("custom_exception!", stream, indent) try: serpent.register_class(Exception, custom_exception_translate) ser = serpent.dumps(ex) data = serpent.loads(ser) self.assertEqual("custom_exception!", data) finally: serpent.unregister_class(Exception) class Something(object): def __init__(self, name, value): self.name = name self.value = value def __getstate__(self): return ("bogus", "state") class BaseClass(object): pass class SubClass(BaseClass): pass class TestCustomClasses(unittest.TestCase): def testCustomClass(self): def something_serializer(obj, serializer, stream, level): d = { "__class__": "Something", "custom": True, "name": obj.name, "value": obj.value } serializer.ser_builtins_dict(d, stream, level) serpent.register_class(Something, something_serializer) s = Something("hello", 42) d = serpent.dumps(s) x = serpent.loads(d) self.assertEqual({"__class__": "Something", "custom": True, "name": "hello", "value": 42}, x) serpent.unregister_class(Something) d = serpent.dumps(s) x = serpent.loads(d) self.assertEqual(("bogus", "state"), x) def testSubclass(self): def custom_serializer(obj, serializer, stream, level): serializer._serialize("[(sub)class=%s]" % type(obj), stream, level) serpent.register_class(BaseClass, custom_serializer) s = SubClass() d = serpent.dumps(s) x = serpent.loads(d) classname = __name__+".SubClass" self.assertEqual("[(sub)class=]", x) def testUUID(self): uid = uuid.uuid4() string_uid = str(uid) ser = serpent.dumps(uid) x = serpent.loads(ser) self.assertEqual(string_uid, x) def custom_uuid_translate(obj, serp, stream, level): serp._serialize("custom_uuid!", stream, level) serpent.register_class(uuid.UUID, custom_uuid_translate) try: ser = serpent.dumps(uid) x = serpent.loads(ser) self.assertEqual("custom_uuid!", x) finally: serpent.unregister_class(uuid.UUID) def testRegisterOrderPreserving(self): serpent._reset_special_classes_registry() serpent.register_class(BaseClass, lambda: None) serpent.register_class(SubClass, lambda: None) classes = list(serpent._special_classes_registry) self.assertEqual(collections.KeysView, classes.pop(0)) self.assertEqual(collections.ValuesView, classes.pop(0)) self.assertEqual(collections.ItemsView, classes.pop(0)) if sys.version_info >= (2, 7): self.assertEqual(collections.OrderedDict, classes.pop(0)) if sys.version_info >= (3, 4): import enum self.assertEqual(enum.Enum, classes.pop(0)) self.assertEqual(BaseClass, classes.pop(0)) self.assertEqual(SubClass, classes.pop(0)) self.assertEqual(0, len(classes)) class TestPyro4(unittest.TestCase): def testException(self): try: hashlib.new("non-existing-hash-name") ev = None except: et, ev, etb = sys.exc_info() tb_lines = traceback.format_exception(et, ev, etb) ev._pyroTraceback = tb_lines ser = serpent.dumps(ev, module_in_classname=False) data = serpent.loads(ser) self.assertTrue(data["__exception__"]) attrs = data["attributes"] self.assertIsInstance(attrs["_pyroTraceback"], list) tb_txt = "".join(attrs["_pyroTraceback"]) self.assertTrue(tb_txt.startswith("Traceback")) self.assertTrue(data["args"][0].startswith("unsupported hash")) self.assertEqual("ValueError", data["__class__"]) class TestCyclic(unittest.TestCase): def testTupleOk(self): t = (1, 2, 3) d = (t, t, t) data = serpent.dumps(d) serpent.loads(data) def testListOk(self): t = [1, 2, 3] d = [t, t, t] data = serpent.dumps(d) serpent.loads(data) def testDictOk(self): t = {"a": 1} d = {"x": t, "y": t, "z": t} data = serpent.dumps(d) serpent.loads(data) def testListCycle(self): d = [1, 2, 3] d.append(d) with self.assertRaises(ValueError) as e: serpent.dumps(d) self.assertEqual("Circular reference detected (list)", str(e.exception)) def testDictCycle(self): d = {"x": 1, "y": 2} d["d"] = d with self.assertRaises(ValueError) as e: serpent.dumps(d) self.assertEqual("Circular reference detected (dict)", str(e.exception)) def testClassCycle(self): d = Cycle() d.make_cycle(d) with self.assertRaises(ValueError) as e: serpent.dumps(d) self.assertEqual("Circular reference detected (class)", str(e.exception)) # noinspection PyUnreachableCode def testMaxLevel(self): ser = serpent.Serializer() self.assertGreater(ser.maximum_level, 10) # old Pypy appears to have a very low default recursionlimit array=[] arr=array for level in range(min(sys.getrecursionlimit()+10, 2000)): arr.append("level"+str(level)) arr2 = [] arr.append(arr2) arr=arr2 with self.assertRaises(ValueError) as x: ser.serialize(array) self.fail("should crash") self.assertTrue("too deep" in str(x.exception)) # check setting the maxlevel array = ["level1", ["level2", ["level3", ["level4"]]]] ser.maximum_level = 4 ser.serialize(array) # should work ser.maximum_level = 3 with self.assertRaises(ValueError) as x: ser.serialize(array) # should crash self.fail("should crash") self.assertTrue("too deep" in str(x.exception)) class Cycle(object): def __init__(self): self.name = "cycle" self.ref = None def make_cycle(self, ref): self.ref = ref class RegisterThread(threading.Thread): def __init__(self): super(RegisterThread, self).__init__() self.stop_running=False def run(self): i = 0 while not self.stop_running: serpent.register_class(type("clazz %d" % i, (), {}), None) # just register dummy serializer i += 1 class SerializationThread(threading.Thread): def __init__(self): super(SerializationThread, self).__init__() self.stop_running = False self.error = None def run(self): big_list = [Cycle() for _ in range(1000)] while not self.stop_running: try: _ = serpent.dumps(big_list) except RuntimeError as x: self.error = x print(x) break @unittest.skip class TestThreading(unittest.TestCase): def testThreadsafeTypeRegistrations(self): reg = RegisterThread() ser = SerializationThread() reg.daemon = ser.daemon = True reg.start() ser.start() time.sleep(1) reg.stop_running = ser.stop_running = True self.assertIsNone(ser.error) class TestCollections(unittest.TestCase): @unittest.skipIf(sys.version_info < (2, 7), "collections.OrderedDict is python 2.7+") def testOrderedDict(self): o = collections.OrderedDict() o['apple'] = 1 o['banana'] = 2 o['orange'] = 3 d = serpent.dumps(o) o2 = serpent.loads(d) self.assertEqual({"__class__": "OrderedDict", "items": [('apple', 1), ('banana', 2), ('orange', 3)]}, o2) def testNamedTuple(self): Point = collections.namedtuple('Point', ['x', 'y']) p = Point(11, 22) d = serpent.dumps(p) p2 = serpent.loads(d) self.assertEqual((11, 22), p2) # the checks below are valid if named tuples are not serialized by the normal tuple serializer: # if sys.version_info < (2, 7) or sys.platform == "cli": # # named tuple serialization is unfortunately broken on python <2.7 or ironpython; it leaves out the actual values # self.assertEqual({"__class__": "Point"}, p2) # elif os.name == "java": # # named tuple serialization is unfortunately broken on jython; it forgets about the order # self.assertEqual({"__class__": "Point", "x": 11, "y": 22}, p2) # elif sys.version_info >= (3, 3) or ((2, 7) <= sys.version_info < (3, 0)): # # only these versions got it 100% right! # self.assertEqual({"__class__": "Point", "items": [('x', 11), ('y', 22)]}, p2) # else: # # other versions forget about the order.... # self.assertEqual({"__class__": "Point", "x": 11, "y": 22}, p2) @unittest.skipIf(sys.version_info < (2, 7), "collections.Counter is python 2.7+") def testCounter(self): c = collections.Counter("even") d = serpent.dumps(c) c2 = serpent.loads(d) self.assertEqual({'e': 2, 'v': 1, 'n': 1}, c2) def testDeque(self): obj = collections.deque([1, 2, 3]) d = serpent.dumps(obj) obj2 = serpent.loads(d) self.assertEqual([1, 2, 3], obj2) @unittest.skipIf(sys.version_info < (3, 3), "ChainMap is python 3.3+") def testChainMap(self): c = collections.ChainMap({"a": 1}, {"b": 2}, {"c": 3}) d = serpent.dumps(c) c2 = serpent.loads(d) self.assertEqual({'__class__': 'ChainMap', 'maps': [{'a': 1}, {'b': 2}, {'c': 3}]}, c2) def testDefaultDict(self): dd = collections.defaultdict(list) dd['a'] = 1 dd['b'] = 2 d = serpent.dumps(dd) dd2 = serpent.loads(d) self.assertEqual({'a': 1, 'b': 2}, dd2) @unittest.skipIf(sys.version_info < (3, 0), "collections.UserDict is python 3.0+") def testUserDict(self): obj = collections.UserDict() obj['a'] = 1 obj['b'] = 2 d = serpent.dumps(obj) obj2 = serpent.loads(d) self.assertEqual({'a': 1, 'b': 2}, obj2) @unittest.skipIf(sys.version_info < (3, 0), "collections.UserList is python 3.0+") def testUserList(self): obj = collections.UserList([1, 2, 3]) d = serpent.dumps(obj) obj2 = serpent.loads(d) self.assertEqual([1, 2, 3], obj2) @unittest.skipIf(sys.version_info < (3, 0), "collections.UserString is python 3.0+") def testUserString(self): obj = collections.UserString("test") d = serpent.dumps(obj) obj2 = serpent.loads(d) self.assertEqual("test", obj2) if __name__ == '__main__': unittest.main() Serpent-serpent-1.23/tests/test_unicode.py000066400000000000000000000026461312652634400207540ustar00rootroot00000000000000from __future__ import print_function import sys import serpent import platform if sys.version_info>=(3,0): unichr = chr teststrings = [ u"", u"abc", u"\u20ac", u"\x00\x01\x80\x81\xfe\xff\u20ac\u4444\u0240slashu:\\uend.\\u20ac(no euro!)\\U00022001bigone" ] large = u"".join(unichr(i) for i in range(256)) teststrings.append(large) large = u"".join(unichr(i) for i in range(0x20ac+1)) teststrings.append(large) def main(): impl=platform.python_implementation()+"_{0}_{1}".format(sys.version_info[0], sys.version_info[1]) print("IMPL:", impl) with open("data_inputs_utf8.txt", "wb") as out: for source in teststrings: out.write(source.encode("utf-8")+b"\n") results = [] ser = serpent.Serializer() with open("data_"+impl+".serpent", "wb") as out: for i, source in enumerate(teststrings): data = ser.serialize(source) out.write(data) out.write(b"~\n~\n") assert b"\x00" not in data results.append(data) assert len(results)==len(teststrings) for i, source in enumerate(teststrings): print(i) result = serpent.loads(results[i]) if source!=result: print("ERRROR!!! RESULT AFTER serpent.loads IS NOT CORRECT!") print("SOURCE:",repr(source)) print("RESULT:",repr(result)) return print("OK") if __name__ == "__main__": main() Serpent-serpent-1.23/tests/test_unicode_parse.py000066400000000000000000000014551312652634400221430ustar00rootroot00000000000000from __future__ import print_function import os import io import re import serpent from test_unicode import teststrings files = [f for f in os.listdir(".") if f.startswith("data_") and f.endswith(".serpent")] for f in files: print("Checking data file", f) resultstrings=[] with io.open(f, "rb") as inf: data = inf.read() data = re.split(b"~\n~\n", data)[:-1] assert len(data) == len(teststrings) # data = data[:-2] # XXX for num, d in enumerate(data, start=1): try: print("data item ",num,"...") resultstrings.append(serpent.loads(d)) except Exception as x: print("\nSERPENT ERROR", type(x)) if resultstrings==teststrings: print("OK") else: print("!!!FAIL!!!") Serpent-serpent-1.23/tests/testserpent.utf8.bin000066400000000000000000000023161312652634400216460ustar00rootroot00000000000000# serpent utf-8 python3.2 { # serpent supports comments inside the serialized data 'bytes': { 'data': 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==', 'encoding': 'base64' }, 'dates': [ # some dates are here '2013-01-26T03:32:39.007001', '23:59:45.999888', 500.0 ], 'dict': { 0: '0000', 1: '1111', # some more comments 2: '2222', 3: '3333', 4: '4444', 5: '5555', 6: '6666', 7: '7777', 8: '8888', 9: '9999' }, 'exc': { '__class__': 'ZeroDivisionError', '__exception__': True, # this is an exception 'args': ( 'fault', ), 'message': 'fault' }, 'list': [ 1, 2, 3, 4, 5, 6, 7, 8 ], 'numbers': [ 123456789012345678901234567890, 999.1234, '1.99999999999999999991', (-3+8j) ], 'set': { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 'str': 'hello', 'tuple': ( 1, 2, 3, 4, 5, 6, 7, 8 ), 'unicode': '€', # the feared unicode. 'uuid': '57e12d82-511f-456b-ab65-f765144c6a90' }Serpent-serpent-1.23/tox.ini000066400000000000000000000002761312652634400160630ustar00rootroot00000000000000[tox] envlist=py27,py33,py34,py35,py36,pypy,pypy3 [testenv] deps=pytz changedir={toxinidir}/tests commands=python -bb -E test_serpent.py [testenv:py26] deps=unittest2 pytz