[CalendarServer-changes] [14351] twext/trunk/twext/python
source_changes at macosforge.org
source_changes at macosforge.org
Thu Jan 29 12:21:25 PST 2015
Revision: 14351
http://trac.calendarserver.org//changeset/14351
Author: sagen at apple.com
Date: 2015-01-29 12:21:25 -0800 (Thu, 29 Jan 2015)
Log Message:
-----------
OMG launchd API is down to a single function
Modified Paths:
--------------
twext/trunk/twext/python/launchd.py
twext/trunk/twext/python/test/test_launchd.py
Modified: twext/trunk/twext/python/launchd.py
===================================================================
--- twext/trunk/twext/python/launchd.py 2015-01-28 04:11:47 UTC (rev 14350)
+++ twext/trunk/twext/python/launchd.py 2015-01-29 20:21:25 UTC (rev 14351)
@@ -16,20 +16,7 @@
##
"""
-Bindings for launchd check-in API.
-
- at see: U{SampleD.c
- <http://developer.apple.com/library/mac/#samplecode/SampleD/>}
-
- at var ffi: a L{cffi.FFI} instance wrapping the functions exposed by C{launch.h}.
-
- at var lib: a L{cffi} "U{dynamic library object
- <http://cffi.readthedocs.org/en/release-0.6/#the-verification-step>}"
- wrapping the functions exposed by C{launch.h}.
-
- at var constants: Select C{LAUNCH_*} constants from C{launch.h}, exposed as plain
- Python values. Note that this is not a complete wrapping, but as the
- header file suggests, these APIs are only for use during check-in.
+Binding for launchd socket hand-off API.
"""
from __future__ import print_function
@@ -40,61 +27,7 @@
ffi.cdef(
"""
- static char const* const LAUNCH_KEY_CHECKIN;
- static char const* const LAUNCH_JOBKEY_LABEL;
- static char const* const LAUNCH_JOBKEY_SOCKETS;
-
- typedef enum {
- LAUNCH_DATA_DICTIONARY = 1,
- LAUNCH_DATA_ARRAY,
- LAUNCH_DATA_FD,
- LAUNCH_DATA_INTEGER,
- LAUNCH_DATA_REAL,
- LAUNCH_DATA_BOOL,
- LAUNCH_DATA_STRING,
- LAUNCH_DATA_OPAQUE,
- LAUNCH_DATA_ERRNO,
- LAUNCH_DATA_MACHPORT,
- } launch_data_type_t;
-
- typedef struct _launch_data *launch_data_t;
-
- bool launch_data_dict_insert(
- launch_data_t, const launch_data_t, const char *
- );
-
- launch_data_t launch_data_alloc(launch_data_type_t);
- launch_data_t launch_data_new_string(const char *);
- launch_data_t launch_data_new_integer(long long);
- launch_data_t launch_data_new_fd(int);
- launch_data_t launch_data_new_bool(bool);
- launch_data_t launch_data_new_real(double);
- launch_data_t launch_msg(const launch_data_t);
-
- launch_data_type_t launch_data_get_type(const launch_data_t);
-
- launch_data_t launch_data_dict_lookup(const launch_data_t, const char *);
- size_t launch_data_dict_get_count(const launch_data_t);
- long long launch_data_get_integer(const launch_data_t);
- void launch_data_dict_iterate(
- const launch_data_t, void (*)(
- const launch_data_t, const char *, void *
- ),
- void *
- );
-
- int launch_data_get_fd(const launch_data_t);
- bool launch_data_get_bool(const launch_data_t);
- const char * launch_data_get_string(const launch_data_t);
- double launch_data_get_real(const launch_data_t);
-
- size_t launch_data_array_get_count(const launch_data_t);
- launch_data_t launch_data_array_get_index(const launch_data_t, size_t);
- bool launch_data_array_set_index(
- launch_data_t, const launch_data_t, size_t
- );
-
- void launch_data_free(launch_data_t);
+ int launch_activate_socket(const char *name, int **fds, size_t *cnt);
"""
)
@@ -109,201 +42,18 @@
raise ImportError(ve)
+def launchActivateSocket(name):
+ fdList = []
-class _LaunchArray(object):
- def __init__(self, launchdata):
- self.launchdata = launchdata
+ fds = ffi.new('int **')
+ count = ffi.new('size_t *')
+ result = lib.launch_activate_socket(name, fds, count)
+ if result == 0:
+ for i in xrange(count[0]):
+ fdList.append(fds[0][i])
+ return fdList
- def __len__(self):
- return lib.launch_data_array_get_count(self.launchdata)
-
-
- def __getitem__(self, index):
- if index >= len(self):
- raise IndexError(index)
- return _launchify(
- lib.launch_data_array_get_index(self.launchdata, index)
- )
-
-
-
-class _LaunchDictionary(object):
- def __init__(self, launchdata):
- self.launchdata = launchdata
-
-
- def keys(self):
- """
- Return keys in the dictionary.
- """
- keys = []
-
- @ffi.callback("void (*)(const launch_data_t, const char *, void *)")
- def icb(v, k, n):
- keys.append(ffi.string(k))
-
- lib.launch_data_dict_iterate(self.launchdata, icb, ffi.NULL)
- return keys
-
-
- def values(self):
- """
- Return values in the dictionary.
- """
- values = []
-
- @ffi.callback("void (*)(const launch_data_t, const char *, void *)")
- def icb(v, k, n):
- values.append(_launchify(v))
-
- lib.launch_data_dict_iterate(self.launchdata, icb, ffi.NULL)
- return values
-
-
- def items(self):
- """
- Return items in the dictionary.
- """
- values = []
-
- @ffi.callback("void (*)(const launch_data_t, const char *, void *)")
- def icb(v, k, n):
- values.append((ffi.string(k), _launchify(v)))
-
- lib.launch_data_dict_iterate(self.launchdata, icb, ffi.NULL)
- return values
-
-
- def __getitem__(self, key):
- launchvalue = lib.launch_data_dict_lookup(self.launchdata, key)
- try:
- return _launchify(launchvalue)
- except LaunchErrno:
- raise KeyError(key)
-
-
- def __len__(self):
- return lib.launch_data_dict_get_count(self.launchdata)
-
-
-
-def plainPython(x):
- """
- Convert a launchd python-like data structure into regular Python
- dictionaries and lists.
- """
- if isinstance(x, _LaunchDictionary):
- result = {}
- for k, v in x.items():
- result[k] = plainPython(v)
- return result
- elif isinstance(x, _LaunchArray):
- return map(plainPython, x)
- else:
- return x
-
-
-
-class LaunchErrno(Exception):
- """
- Error from launchd.
- """
-
-
-
-def _launchify(launchvalue):
- """
- Convert a ctypes value wrapping a C{_launch_data} structure into the
- relevant Python object (integer, bytes, L{_LaunchDictionary},
- L{_LaunchArray}).
- """
- if launchvalue == ffi.NULL:
- return None
- dtype = lib.launch_data_get_type(launchvalue)
-
- if dtype == lib.LAUNCH_DATA_DICTIONARY:
- return _LaunchDictionary(launchvalue)
- elif dtype == lib.LAUNCH_DATA_ARRAY:
- return _LaunchArray(launchvalue)
- elif dtype == lib.LAUNCH_DATA_FD:
- return lib.launch_data_get_fd(launchvalue)
- elif dtype == lib.LAUNCH_DATA_INTEGER:
- return lib.launch_data_get_integer(launchvalue)
- elif dtype == lib.LAUNCH_DATA_REAL:
- return lib.launch_data_get_real(launchvalue)
- elif dtype == lib.LAUNCH_DATA_BOOL:
- return lib.launch_data_get_bool(launchvalue)
- elif dtype == lib.LAUNCH_DATA_STRING:
- cvalue = lib.launch_data_get_string(launchvalue)
- if cvalue == ffi.NULL:
- return None
- return ffi.string(cvalue)
- elif dtype == lib.LAUNCH_DATA_OPAQUE:
- return launchvalue
- elif dtype == lib.LAUNCH_DATA_ERRNO:
- raise LaunchErrno(launchvalue)
- elif dtype == lib.LAUNCH_DATA_MACHPORT:
- return lib.launch_data_get_machport(launchvalue)
- else:
- raise TypeError("Unknown Launch Data Type", dtype)
-
-
-
-def checkin():
- """
- Perform a launchd checkin, returning a Pythonic wrapped data structure
- representing the retrieved check-in plist.
-
- @return: a C{dict}-like object.
- """
- lkey = lib.launch_data_new_string(lib.LAUNCH_KEY_CHECKIN)
- msgr = lib.launch_msg(lkey)
- return _launchify(msgr)
-
-
-
-def _managed(obj):
- """
- Automatically free an object that was allocated with a launch_data_*
- function, or raise L{MemoryError} if it's C{NULL}.
- """
- if obj == ffi.NULL:
- raise MemoryError()
- else:
- return ffi.gc(obj, lib.launch_data_free)
-
-
-
-class _Strings(object):
- """
- Expose constants as Python-readable values rather than wrapped ctypes
- pointers.
- """
- def __getattribute__(self, name):
- value = getattr(lib, name)
- if isinstance(value, int):
- return value
- if ffi.typeof(value) != ffi.typeof("char *"):
- raise AttributeError("no such constant", name)
- return ffi.string(value)
-
-constants = _Strings()
-
-
-
-def getLaunchDSocketFDs():
- """
- Perform checkin via L{checkin} and return just a dictionary mapping the
- sockets to file descriptors.
- """
- return plainPython(checkin()[constants.LAUNCH_JOBKEY_SOCKETS])
-
-
-
__all__ = [
- 'checkin',
- 'lib',
- 'ffi',
- 'plainPython',
+ 'launchActivateSocket',
]
Modified: twext/trunk/twext/python/test/test_launchd.py
===================================================================
--- twext/trunk/twext/python/test/test_launchd.py 2015-01-28 04:11:47 UTC (rev 14350)
+++ twext/trunk/twext/python/test/test_launchd.py 2015-01-29 20:21:25 UTC (rev 14351)
@@ -42,10 +42,7 @@
try:
- from twext.python.launchd import (
- lib, ffi, _LaunchDictionary, _LaunchArray, _managed, constants,
- plainPython, checkin, _launchify, getLaunchDSocketFDs
- )
+ from twext.python.launchd import launchActivateSocket
except ImportError:
skip = "LaunchD not available."
else:
@@ -55,235 +52,6 @@
from twisted.python.filepath import FilePath
-class LaunchDataStructures(TestCase):
- """
- Tests for L{_launchify} converting data structures from launchd's internals
- to Python objects.
- """
-
- def test_fd(self):
- """
- Test converting a launchd FD to an integer.
- """
- fd = _managed(lib.launch_data_new_fd(2))
- self.assertEquals(_launchify(fd), 2)
-
- test_fd.todo = "Figure out why this test stopped working"
-
-
- def test_bool(self):
- """
- Test converting a launchd bool to a Python bool.
- """
- t = _managed(lib.launch_data_new_bool(True))
- f = _managed(lib.launch_data_new_bool(False))
- self.assertEqual(_launchify(t), True)
- self.assertEqual(_launchify(f), False)
-
-
- def test_real(self):
- """
- Test converting a launchd real to a Python float.
- """
- notQuitePi = _managed(lib.launch_data_new_real(3.14158))
- self.assertEqual(_launchify(notQuitePi), 3.14158)
-
-
-
-class DictionaryTests(TestCase):
- """
- Tests for L{_LaunchDictionary}
- """
-
- def setUp(self):
- """
- Assemble a test dictionary.
- """
- self.testDict = _managed(
- lib.launch_data_alloc(lib.LAUNCH_DATA_DICTIONARY)
- )
- key1 = ffi.new("char[]", "alpha")
- val1 = lib.launch_data_new_string("alpha-value")
- key2 = ffi.new("char[]", "beta")
- val2 = lib.launch_data_new_string("beta-value")
- key3 = ffi.new("char[]", "gamma")
- val3 = lib.launch_data_new_integer(3)
- lib.launch_data_dict_insert(self.testDict, val1, key1)
- lib.launch_data_dict_insert(self.testDict, val2, key2)
- lib.launch_data_dict_insert(self.testDict, val3, key3)
- self.assertEquals(lib.launch_data_dict_get_count(self.testDict), 3)
-
-
- def test_len(self):
- """
- C{len(_LaunchDictionary())} returns the number of keys in the
- dictionary.
- """
- self.assertEquals(len(_LaunchDictionary(self.testDict)), 3)
-
-
- def test_keys(self):
- """
- L{_LaunchDictionary.keys} returns keys present in a C{launch_data_dict}.
- """
- dictionary = _LaunchDictionary(self.testDict)
- self.assertEquals(set(dictionary.keys()),
- set([b"alpha", b"beta", b"gamma"]))
-
-
- def test_values(self):
- """
- L{_LaunchDictionary.values} returns keys present in a
- C{launch_data_dict}.
- """
- dictionary = _LaunchDictionary(self.testDict)
- self.assertEquals(set(dictionary.values()),
- set([b"alpha-value", b"beta-value", 3]))
-
-
- def test_items(self):
- """
- L{_LaunchDictionary.items} returns all (key, value) tuples present in a
- C{launch_data_dict}.
- """
- dictionary = _LaunchDictionary(self.testDict)
- self.assertEquals(set(dictionary.items()),
- set([(b"alpha", b"alpha-value"),
- (b"beta", b"beta-value"), (b"gamma", 3)]))
-
-
- def test_plainPython(self):
- """
- L{plainPython} will convert a L{_LaunchDictionary} into a Python
- dictionary.
- """
- self.assertEquals(
- {b"alpha": b"alpha-value", b"beta": b"beta-value", b"gamma": 3},
- plainPython(_LaunchDictionary(self.testDict))
- )
-
-
- def test_plainPythonNested(self):
- """
- L{plainPython} will convert a L{_LaunchDictionary} containing another
- L{_LaunchDictionary} into a nested Python dictionary.
- """
- otherDict = lib.launch_data_alloc(lib.LAUNCH_DATA_DICTIONARY)
- lib.launch_data_dict_insert(otherDict,
- lib.launch_data_new_string("bar"), "foo")
- lib.launch_data_dict_insert(self.testDict, otherDict, "delta")
- self.assertEquals(
- {b"alpha": b"alpha-value", b"beta": b"beta-value",
- b"gamma": 3, b"delta": {b"foo": b"bar"}},
- plainPython(_LaunchDictionary(self.testDict))
- )
-
-
-
-class ArrayTests(TestCase):
- """
- Tests for L{_LaunchArray}
- """
-
- def setUp(self):
- """
- Assemble a test array.
- """
- self.testArray = ffi.gc(
- lib.launch_data_alloc(lib.LAUNCH_DATA_ARRAY),
- lib.launch_data_free
- )
-
- lib.launch_data_array_set_index(
- self.testArray, lib.launch_data_new_string("test-string-1"), 0
- )
- lib.launch_data_array_set_index(
- self.testArray, lib.launch_data_new_string("another string."), 1
- )
- lib.launch_data_array_set_index(
- self.testArray, lib.launch_data_new_integer(4321), 2
- )
-
-
- def test_length(self):
- """
- C{len(_LaunchArray(...))} returns the number of elements in the array.
- """
- self.assertEquals(len(_LaunchArray(self.testArray)), 3)
-
-
- def test_indexing(self):
- """
- C{_LaunchArray(...)[n]} returns the n'th element in the array.
- """
- array = _LaunchArray(self.testArray)
- self.assertEquals(array[0], b"test-string-1")
- self.assertEquals(array[1], b"another string.")
- self.assertEquals(array[2], 4321)
-
-
- def test_indexTooBig(self):
- """
- C{_LaunchArray(...)[n]}, where C{n} is greater than the length of the
- array, raises an L{IndexError}.
- """
- array = _LaunchArray(self.testArray)
- self.assertRaises(IndexError, lambda: array[3])
-
-
- def test_iterating(self):
- """
- Iterating over a C{_LaunchArray} returns each item in sequence.
- """
- array = _LaunchArray(self.testArray)
- i = iter(array)
- self.assertEquals(i.next(), b"test-string-1")
- self.assertEquals(i.next(), b"another string.")
- self.assertEquals(i.next(), 4321)
- self.assertRaises(StopIteration, i.next)
-
-
- def test_plainPython(self):
- """
- L{plainPython} converts a L{_LaunchArray} into a Python list.
- """
- array = _LaunchArray(self.testArray)
- self.assertEquals(plainPython(array),
- [b"test-string-1", b"another string.", 4321])
-
-
- def test_plainPythonNested(self):
- """
- L{plainPython} converts a L{_LaunchArray} containing another
- L{_LaunchArray} into a Python list.
- """
- sub = lib.launch_data_alloc(lib.LAUNCH_DATA_ARRAY)
- lib.launch_data_array_set_index(sub, lib.launch_data_new_integer(7), 0)
- lib.launch_data_array_set_index(self.testArray, sub, 3)
- array = _LaunchArray(self.testArray)
- self.assertEqual(plainPython(array), [b"test-string-1",
- b"another string.", 4321, [7]])
-
-
-
-class SimpleStringConstants(TestCase):
- """
- Tests for bytestring-constants wrapping.
- """
-
- def test_constant(self):
- """
- C{launchd.constants.LAUNCH_*} will return a bytes object corresponding
- to a constant.
- """
- self.assertEqual(constants.LAUNCH_JOBKEY_SOCKETS,
- b"Sockets")
- self.assertRaises(AttributeError, getattr, constants,
- "launch_data_alloc")
- self.assertEquals(constants.LAUNCH_DATA_ARRAY, 2)
-
-
-
class CheckInTests(TestCase):
"""
Integration tests making sure that actual checkin with launchd results in
@@ -348,36 +116,12 @@
@staticmethod
- def job_checkin():
- """
- Check in in the subprocess.
- """
- sys.stdout.write(json.dumps(plainPython(checkin())))
-
-
- def test_checkin(self):
- """
- L{checkin} performs launchd checkin and returns a launchd data
- structure.
- """
- d = json.loads(self.stdout.getContent())
- self.assertEqual(d[constants.LAUNCH_JOBKEY_LABEL], self.launchLabel)
- self.assertIsInstance(d, dict)
- sockets = d[constants.LAUNCH_JOBKEY_SOCKETS]
- self.assertEquals(len(sockets), 1)
- self.assertEqual(['Awesome'], sockets.keys())
- awesomeSocket = sockets['Awesome']
- self.assertEqual(len(awesomeSocket), 1)
- self.assertIsInstance(awesomeSocket[0], int)
-
-
- @staticmethod
def job_getFDs():
"""
Check-in via the high-level C{getLaunchDSocketFDs} API, that just gives
us listening FDs.
"""
- sys.stdout.write(json.dumps(getLaunchDSocketFDs()))
+ sys.stdout.write(json.dumps(launchActivateSocket("Awesome")))
def test_getFDs(self):
@@ -388,10 +132,7 @@
"""
sockets = json.loads(self.stdout.getContent())
self.assertEquals(len(sockets), 1)
- self.assertEqual(['Awesome'], sockets.keys())
- awesomeSocket = sockets['Awesome']
- self.assertEqual(len(awesomeSocket), 1)
- self.assertIsInstance(awesomeSocket[0], int)
+ self.assertIsInstance(sockets[0], int)
def tearDown(self):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20150129/6fabec55/attachment-0001.html>
More information about the calendarserver-changes
mailing list