Skip to content

added preliminary Python 3 support #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.pyc
__pycache__
39 changes: 27 additions & 12 deletions collectd.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import re
import sys
import time
import socket
import struct
import logging
import traceback
from functools import wraps
from Queue import Queue, Empty
from collections import defaultdict
from threading import RLock, Thread, Semaphore

try:
from Queue import Queue, Empty # Python 2
except ImportError:
from queue import Queue, Empty # Python 3


__all__ = ["Connection", "start_threads"]

Expand All @@ -17,6 +22,8 @@

logger = logging.getLogger("collectd")

StringTypes = (type(b""), type(u""))

SEND_INTERVAL = 10 # seconds
MAX_PACKET_SIZE = 1024 # bytes

Expand Down Expand Up @@ -46,20 +53,24 @@


def pack_numeric(type_code, number):
return struct.pack("!HHq", type_code, 12, number)
return struct.pack("!HHq", type_code, 12, int(number))

def pack_string(type_code, string):
return struct.pack("!HH", type_code, 5 + len(string)) + string + "\0"
if isinstance(string, type(u"")):
string = string.encode("UTF-8")
return struct.pack("!HH", type_code, 5 + len(string)) + string + b"\0"

def pack_value(name, value):
return "".join([
if isinstance(value, type(u"")):
value = value.encode("UTF-8")
return b"".join([
pack(TYPE_TYPE_INSTANCE, name),
struct.pack("!HHH", TYPE_VALUES, 15, 1),
struct.pack("<Bd", VALUE_GAUGE, value)
])

def pack(id, value):
if isinstance(id, basestring):
if isinstance(id, StringTypes):
return pack_value(id, value)
elif id in LONG_INT_CODES:
return pack_numeric(id, value)
Expand All @@ -69,7 +80,7 @@ def pack(id, value):
raise AssertionError("invalid type code " + str(id))

def message_start(when=None, host=socket.gethostname(), plugin_inst="", plugin_name="any"):
return "".join([
return b"".join([
pack(TYPE_HOST, host),
pack(TYPE_TIME, when or time.time()),
pack(TYPE_PLUGIN, plugin_name),
Expand All @@ -87,16 +98,18 @@ def messages(counts, when=None, host=socket.gethostname(), plugin_inst="", plugi
curr, curr_len = [start], len(start)
for part in parts:
if curr_len + len(part) > MAX_PACKET_SIZE:
packets.append("".join(curr))
packets.append(b"".join(curr))
curr, curr_len = [start], len(start)
curr.append(part)
curr_len += len(part)
packets.append("".join(curr))
packets.append(b"".join(curr))
return packets



def sanitize(s):
if sys.version_info.major == 3 and isinstance(s, bytes):
s = s.decode("UTF-8")
return re.sub(r"[^a-zA-Z0-9]+", "_", s).strip("_")

def swallow_errors(func):
Expand Down Expand Up @@ -127,18 +140,20 @@ def __init__(self, category):
@swallow_errors
@synchronized
def record(self, *args, **kwargs):
for specific in list(args) + [""]:
assert isinstance(specific, basestring)
for specific in list(args) + [b""]:
assert isinstance(specific, StringTypes), str(type(specific))
if isinstance(specific, type(u"")):
specific = specific.encode("UTF-8")
for stat, value in kwargs.items():
assert isinstance(value, (int, float))
self.counts[str(specific)][str(stat)] += value
self.counts[specific][stat] += value

@swallow_errors
@synchronized
def set_exact(self, **kwargs):
for stat, value in kwargs.items():
assert isinstance(value, (int, float))
self.counts[""][str(stat)] = value
self.counts[b""][str(stat)] = value

@synchronized
def snapshot(self):
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name = "collectd",
version = "1.0.2",
version = "1.0.3",
py_modules = ["collectd"],

author = "Eli Courtwright",
Expand Down
19 changes: 10 additions & 9 deletions unit_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,15 @@ def assertValidPacket(self, expected_type_count, s):
self.assertEqual(size, 12)
struct.unpack("!q", s[4:12])
elif type_code in collectd.STRING_CODES:
self.assertEqual(s[size-1], "\0")
self.assertIn(s[size-1], [0, "\0"])
struct.unpack(str(size-4) + "s", s[4:size])
else:
self.assertEqual(type_code, collectd.TYPE_VALUES)
values = s[6:size]
count = 0
while values:
value_code = struct.unpack("B", values[0])[0]
val = values[0]
value_code = struct.unpack("B", val)[0] if isinstance(val, collectd.StringTypes) else val
self.assertTrue(value_code in collectd.VALUE_CODES)
struct.unpack(collectd.VALUE_CODES[value_code], values[1:9])
values = values[9:]
Expand Down Expand Up @@ -277,22 +278,22 @@ def test_single(self):
self.send_and_recv(foo = 5)

def test_multiple(self):
stats = {"foo": 345352, "bar": -5023123}
stats = {u"foo": 345352, u"bar": -5023123}
packet = self.send_and_recv(**stats)
for name, value in stats.items():
self.assertTrue(name + "\0" in packet)
self.assertTrue(name.encode("UTF-8") + b"\0" in packet)
self.assertTrue(struct.pack("<d", value) in packet)
self.assertTrue(collectd.pack("test-"+name, value) in packet)

def test_plugin_name(self):
conn = collectd.Connection(collectd_port = self.TEST_PORT,
plugin_name = "dckx")
self.assertTrue("dckx" in self.send_and_recv(conn, foo=5))
self.assertTrue(b"dckx" in self.send_and_recv(conn, foo=5))

def test_plugin_inst(self):
conn = collectd.Connection(collectd_port = self.TEST_PORT,
plugin_inst = "xkcd")
self.assertTrue("xkcd" in self.send_and_recv(conn, foo=5))
self.assertTrue(b"xkcd" in self.send_and_recv(conn, foo=5))

def test_unicode(self):
self.send_and_recv(self.conn, u"foo.bar", hits = 1)
Expand All @@ -305,7 +306,7 @@ def test_too_large(self):
collectd.send_stats(raise_on_empty = True)
for name,val in stats:
packet = self.server.recv(collectd.MAX_PACKET_SIZE)
self.assertTrue(name + "\0" in packet)
self.assertTrue(name.encode("UTF-8") + b"\0" in packet)
self.assertTrue(struct.pack("<d", val) in packet)
self.assertValidPacket(8, packet)

Expand All @@ -319,9 +320,9 @@ def test_too_many(self):
for packet in packets:
self.assertValidPacket(8, packet)

data = "".join(packets)
data = b"".join(packets)
for name,val in stats:
self.assertTrue(name + "\0" in data)
self.assertTrue(name.encode("UTF-8") + b"\0" in data)
self.assertTrue(struct.pack("<d", val) in data)


Expand Down