Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion twotp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
Twisted as an erlang node.
"""

from twotp.term import Tuple, Atom, String
from twotp.term import Tuple, Atom, String, Map
from twotp.server import NodeServerFactory
from twotp.client import NodeClientFactory
from twotp.epmd import PersistentPortMapperFactory, OneShotPortMapperFactory
Expand Down
13 changes: 13 additions & 0 deletions twotp/packer.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,19 @@ def pack_list(self, term):
return packetData + self.packChar(self.MAGIC_NIL)


def pack_map(self, term):
"""
Pack a map.
"""
length = len(term)
packetData = (self.packChar(self.MAGIC_MAP) + self.packInt(length))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove the parenthesis here.


for (key, value) in term.iteritems():
packetData += self.packOneTerm(key) + self.packOneTerm(value)

return packetData


def packOneTerm(self, term):
"""
Try to pack a term into binary data.
Expand Down
12 changes: 11 additions & 1 deletion twotp/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from twotp.term import Integer, List, Tuple, Float, Atom, Reference
from twotp.term import Port, Pid, Binary, Fun, NewFun, Export, BitBinary
from twotp.term import ConstantHolder, Dict, Set
from twotp.term import ConstantHolder, Dict, Set, Map



Expand Down Expand Up @@ -117,6 +117,16 @@ def parse_list(self, data):
return List(elements), data[1:]


def parse_map(self, data):
"""
Parse a map.
"""
arity = self.parseInt(data[:4])
elements, data = self._parse_seq(arity*2, data[4:]) # K1,V1,K2,V2, ...

return Map(dict([(elements[i], elements[i+1]) for i in range(0, len(elements), 2)])), data

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can remove the brackets, maybe use xrange too.



def _parse_seq(self, arity, data):
"""
Helper methods to deal with sequence of encoded terms.
Expand Down
30 changes: 30 additions & 0 deletions twotp/term.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,35 @@ def Set(val):
return set(val)


class Map(Term):
def __init__(self, val):
self._val = val


def __len__(self):

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be worth maybe inheriting collections.Mapping and to provide the necessary methods.

return len(self._val)


def __eq__(self, other):
"""
Check for equality of atom text.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docstring doesn't match.

"""
if not isinstance(other, Map):
return False
return self._val == other._val


def __repr__(self):
"""
Simple representation with the text.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

"""
s = "<%s at %s, values %s>" % (self.__class__.__name__,
hex(id(self)), self._val)
return s

def iteritems(self):
return self._val.iteritems()


class Pid(Term):
"""
Expand Down Expand Up @@ -548,6 +577,7 @@ class ConstantHolder(object):
MAGIC_BIT_BINARY = 77
MAGIC_COMPRESSED = 80
MAGIC_NEW_FLOAT = 70
MAGIC_MAP = 116



Expand Down
26 changes: 25 additions & 1 deletion twotp/test/test_packer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Packer tests.
"""

from twotp.term import Atom, Tuple, Pid, Reference, List, NewFloat, Binary
from twotp.term import Atom, Tuple, Pid, Reference, List, NewFloat, Binary, Map
from twotp.packer import Packer, UnhandledClass
from twotp.test.util import TestCase

Expand Down Expand Up @@ -322,3 +322,27 @@ def test_new_float(self):
"""
self.assertEqual(
self.packer.packOneTerm(NewFloat(1.234)), "F?\xf3\xbev\xc8\xb49X")


def test_packEmptyMap(self):
"""
Test packing empty map
"""
m = Map({})
self.assertEqual(
self.packer.packOneTerm(m),
"\x74\x00\x00\x00\x00" # map magic + len
)


def test_packMap(self):
"""
Test primary support for maps.
"""
m = Map({Atom("foo") : 42})
self.assertEqual(
self.packer.packOneTerm(m),
"\x74\x00\x00\x00\x01"+ # map magic + len
"\x64\x00\x03foo" + # 'foo' Atom (magic+len+content)
"\x61\x2a" # 42 int (magic+value)
)
27 changes: 26 additions & 1 deletion twotp/test/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Parser tests.
"""

from twotp.term import Atom, Tuple, Pid, Reference, Integer, List
from twotp.term import Atom, Tuple, Pid, Reference, Integer, List, Map
from twotp.term import Float, Port, Binary, Fun, NewFun, Export, BitBinary
from twotp.parser import Parser, RemainingDataError, UnhandledCode
from twotp.test.util import TestCase
Expand Down Expand Up @@ -338,3 +338,28 @@ def test_parseNewFloat(self):
"""
self.assertEqual(
self.parser.binaryToTerm('F?\xf3\xae\x14z\xe1G\xae'), (1.23, ""))


def test_parseEmptyMap(self):
"""
Try to parse an empty map.
"""
self.assertEqual(
self.parser.binaryToTerm("\x74\x00\x00\x00\x00"),
(Map({}), ""))


def test_parseMap(self):
"""
Try to parse a map.
"""
self.assertEqual(
self.parser.binaryToTerm(
"\x74\x00\x00\x00\x02"+ # map magic + len
"\x64\x00\x03foo" + # 'foo' Atom (magic+len+content)
"\x6b\x00\x03bar" + # 'bar' String (magic+len+content)
"\x61\x2a" + # 42 int (magic+value)
"\x6d\x00\x00\x00\x04fish" # 'fish' binary ()
),
(Map({Atom('foo'): List([98,97,114]), 42: Binary('fish')}), ""))