diff options
author | qsr <qsr@chromium.org> | 2014-08-25 10:00:45 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-08-25 17:01:40 +0000 |
commit | 28e16123231497d889ef08ebd917e80d5008ffbf (patch) | |
tree | 8e7a961631240f7792423918d84730926d77f4d2 | |
parent | 0aff6f3f6d49226f59a6b5adce44b1cd0b30ffbd (diff) | |
download | chromium_src-28e16123231497d889ef08ebd917e80d5008ffbf.zip chromium_src-28e16123231497d889ef08ebd917e80d5008ffbf.tar.gz chromium_src-28e16123231497d889ef08ebd917e80d5008ffbf.tar.bz2 |
Core mojo API for python.
Review URL: https://codereview.chromium.org/377293002
Cr-Commit-Position: refs/heads/master@{#291701}
-rw-r--r-- | mojo/BUILD.gn | 6 | ||||
-rw-r--r-- | mojo/mojo.gyp | 79 | ||||
-rw-r--r-- | mojo/public/BUILD.gn | 6 | ||||
-rw-r--r-- | mojo/public/python/BUILD.gn | 33 | ||||
-rw-r--r-- | mojo/public/python/mojo/__init__.py | 3 | ||||
-rw-r--r-- | mojo/public/python/mojo/c_core.pxd | 201 | ||||
-rw-r--r-- | mojo/public/python/mojo/system.pyx | 696 | ||||
-rw-r--r-- | mojo/python/BUILD.gn | 29 | ||||
-rw-r--r-- | mojo/python/system/mojo/embedder.pyx | 41 | ||||
-rw-r--r-- | mojo/python/tests/BUILD.gn | 24 | ||||
-rw-r--r-- | mojo/python/tests/test_core.py | 314 | ||||
-rw-r--r-- | third_party/cython/cp_python_binary_modules.py | 42 | ||||
-rw-r--r-- | third_party/cython/cython_compiler.gypi | 71 | ||||
-rw-r--r-- | third_party/cython/python_export.h | 13 | ||||
-rw-r--r-- | third_party/cython/python_flags.py | 53 | ||||
-rw-r--r-- | third_party/cython/python_module.gypi | 51 | ||||
-rw-r--r-- | third_party/cython/rules.gni | 84 |
17 files changed, 1746 insertions, 0 deletions
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn index 4a46ecd..39b3859 100644 --- a/mojo/BUILD.gn +++ b/mojo/BUILD.gn @@ -13,6 +13,12 @@ group("mojo") { "//mojo/services", "//mojo/shell:mojo_shell", ] + + if (is_linux) { + deps += [ + "//mojo/python", + ] + } } group("tests") { diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp index f38a793..acd8409 100644 --- a/mojo/mojo.gyp +++ b/mojo/mojo.gyp @@ -87,6 +87,13 @@ 'mojo_dbus_echo_service', ], }], + ['component != "shared_library" and OS == "linux"', { + 'dependencies': [ + 'mojo_python_embedder', + 'mojo_python_system', + 'mojo_python', + ], + }], ] }, { @@ -531,5 +538,77 @@ }, ], }], + ['component!="shared_library" and OS=="linux"', { + 'targets': [ + { + 'target_name': 'mojo_python_system', + 'variables': { + 'python_base_module': 'mojo', + 'python_cython_module': 'system', + }, + 'sources': [ + 'public/python/mojo/c_core.pxd', + 'public/python/mojo/system.pyx', + ], + 'dependencies': [ + 'mojo_base.gyp:mojo_system', + ], + 'includes': [ '../third_party/cython/cython_compiler.gypi' ], + }, + { + 'target_name': 'mojo_python_embedder', + 'type': 'loadable_module', + 'variables': { + 'python_base_module': 'mojo', + 'python_cython_module': 'embedder', + }, + 'sources': [ + 'python/system/mojo/embedder.pyx', + ], + 'dependencies': [ + 'mojo_base.gyp:mojo_system_impl', + ], + 'includes': [ '../third_party/cython/cython_compiler.gypi' ], + }, + { + 'target_name': 'mojo_python', + 'type': 'none', + 'variables': { + 'python_base_module': 'mojo', + }, + 'sources': [ + 'public/python/mojo/__init__.py', + ], + 'dependencies': [ + 'mojo_python_embedder', + 'mojo_python_system', + ], + 'includes': [ '../third_party/cython/python_module.gypi' ], + }, + { + 'target_name': 'mojo_python_unittests', + 'type': 'none', + 'actions': [ + { + 'action_name': 'run_mojo_python_unittests', + 'inputs': [ + 'python/tests/test_core.py', + '<(SHARED_INTERMEDIATE_DIR)/mojo_python_py_module.stamp', + '<(PRODUCT_DIR)/python/mojo/__init__.py', + ], + 'outputs': [ + 'none', + ], + 'action': [ + 'python', '<@(_inputs)', '<(PRODUCT_DIR)/python', + ], + }, + ], + 'dependency': [ + 'mojo_python', + ], + }, + ], + }], ], } diff --git a/mojo/public/BUILD.gn b/mojo/public/BUILD.gn index 631b6e9..03aefb9 100644 --- a/mojo/public/BUILD.gn +++ b/mojo/public/BUILD.gn @@ -14,6 +14,12 @@ group("public") { "//mojo/public/js/bindings", ] + if (is_linux) { + deps += [ + "//mojo/public/python", + ] + } + if (is_android) { deps += [ "//mojo/public/java:system", diff --git a/mojo/public/python/BUILD.gn b/mojo/public/python/BUILD.gn new file mode 100644 index 0000000..c1c6fbc --- /dev/null +++ b/mojo/public/python/BUILD.gn @@ -0,0 +1,33 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/cython/rules.gni") + +group("python") { + deps = [ + ":base", + ":system", + ] +} + +python_binary_module("system") { + python_base_module = "mojo" + sources = [ + "mojo/c_core.pxd", + "mojo/system.pyx", + ] + deps = [ + "//mojo/public/c/system", + ":base", + ] +} + +copy("base") { + sources = [ + "mojo/__init__.py", + ] + outputs = [ + "$root_out_dir/python/mojo/__init__.py", + ] +} diff --git a/mojo/public/python/mojo/__init__.py b/mojo/public/python/mojo/__init__.py new file mode 100644 index 0000000..4d6aabb --- /dev/null +++ b/mojo/public/python/mojo/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. diff --git a/mojo/public/python/mojo/c_core.pxd b/mojo/public/python/mojo/c_core.pxd new file mode 100644 index 0000000..80b8487d --- /dev/null +++ b/mojo/public/python/mojo/c_core.pxd @@ -0,0 +1,201 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# distutils: language = c++ + +from cpython.buffer cimport PyBUF_CONTIG +from cpython.buffer cimport PyBUF_CONTIG_RO +from cpython.buffer cimport Py_buffer +from cpython.buffer cimport PyBuffer_FillInfo +from cpython.buffer cimport PyBuffer_Release +from cpython.buffer cimport PyObject_GetBuffer +from cpython.mem cimport PyMem_Malloc, PyMem_Free +from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t, uintptr_t + +cdef extern from "third_party/cython/python_export.h": + pass + +cdef extern from "mojo/public/platform/native/system_thunks.h" nogil: + cdef struct MojoSystemThunks: + pass + +cdef extern size_t MojoSetSystemThunks(const MojoSystemThunks* system_thunks) + +cdef extern from "mojo/public/c/system/core.h" nogil: + # types.h + ctypedef int64_t MojoTimeTicks + + ctypedef uint32_t MojoHandle + const MojoHandle MOJO_HANDLE_INVALID + + ctypedef int32_t MojoResult + const MojoResult MOJO_RESULT_OK + const MojoResult MOJO_RESULT_CANCELLED + const MojoResult MOJO_RESULT_UNKNOWN + const MojoResult MOJO_RESULT_INVALID_ARGUMENT + const MojoResult MOJO_RESULT_DEADLINE_EXCEEDED + const MojoResult MOJO_RESULT_NOT_FOUND + const MojoResult MOJO_RESULT_ALREADY_EXISTS + const MojoResult MOJO_RESULT_PERMISSION_DENIED + const MojoResult MOJO_RESULT_RESOURCE_EXHAUSTED + const MojoResult MOJO_RESULT_FAILED_PRECONDITION + const MojoResult MOJO_RESULT_ABORTED + const MojoResult MOJO_RESULT_OUT_OF_RANGE + const MojoResult MOJO_RESULT_UNIMPLEMENTED + const MojoResult MOJO_RESULT_INTERNAL + const MojoResult MOJO_RESULT_UNAVAILABLE + const MojoResult MOJO_RESULT_DATA_LOSS + const MojoResult MOJO_RESULT_BUSY + const MojoResult MOJO_RESULT_SHOULD_WAIT + + ctypedef uint64_t MojoDeadline + const MojoDeadline MOJO_DEADLINE_INDEFINITE + + ctypedef uint32_t MojoHandleSignals + const MojoHandleSignals MOJO_HANDLE_SIGNAL_NONE + const MojoHandleSignals MOJO_HANDLE_SIGNAL_READABLE + const MojoHandleSignals MOJO_HANDLE_SIGNAL_WRITABLE + + # functions.h + MojoTimeTicks MojoGetTimeTicksNow() + MojoResult MojoClose(MojoHandle handle) + MojoResult MojoWait(MojoHandle handle, + MojoHandleSignals signals, + MojoDeadline deadline) + MojoResult MojoWaitMany(const MojoHandle* handles, + const MojoHandleSignals* signals, + uint32_t num_handles, + MojoDeadline deadline) + + # message_pipe.h + ctypedef uint32_t MojoCreateMessagePipeOptionsFlags + const MojoCreateMessagePipeOptionsFlags MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE + + ctypedef uint32_t MojoWriteMessageFlags + const MojoWriteMessageFlags MOJO_WRITE_MESSAGE_FLAG_NONE + + ctypedef uint32_t MojoReadMessageFlags + const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_NONE + const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_MAY_DISCARD + + cdef struct MojoCreateMessagePipeOptions: + uint32_t struct_size + MojoCreateMessagePipeOptionsFlags flags + + MojoResult MojoCreateMessagePipe( + const MojoCreateMessagePipeOptions* options, + MojoHandle* message_pipe_handle0, + MojoHandle* message_pipe_handle1) + + MojoResult MojoWriteMessage( + MojoHandle message_pipe_handle, + const void* bytes, + uint32_t num_bytes, + const MojoHandle* handles, + uint32_t num_handles, + MojoWriteMessageFlags flags) + + MojoResult MojoReadMessage( + MojoHandle message_pipe_handle, + void* bytes, + uint32_t* num_bytes, + MojoHandle* handles, + uint32_t* num_handles, + MojoReadMessageFlags flags) + + # data_pipe.h + ctypedef uint32_t MojoCreateDataPipeOptionsFlags + const MojoCreateDataPipeOptionsFlags MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE + const MojoCreateDataPipeOptionsFlags MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD + + cdef struct MojoCreateDataPipeOptions: + uint32_t struct_size + MojoCreateDataPipeOptionsFlags flags + uint32_t element_num_bytes + uint32_t capacity_num_bytes + + ctypedef uint32_t MojoWriteDataFlags + + const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_NONE + const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_ALL_OR_NONE + + ctypedef uint32_t MojoReadDataFlags + + const MojoReadDataFlags MOJO_READ_DATA_FLAG_NONE + const MojoReadDataFlags MOJO_READ_DATA_FLAG_ALL_OR_NONE + const MojoReadDataFlags MOJO_READ_DATA_FLAG_DISCARD + const MojoReadDataFlags MOJO_READ_DATA_FLAG_QUERY + + MojoResult MojoCreateDataPipe( + const MojoCreateDataPipeOptions* options, + MojoHandle* data_pipe_producer_handle, + MojoHandle* data_pipe_consumer_handle) + + MojoResult MojoWriteData( + MojoHandle data_pipe_producer_handle, + const void* elements, + uint32_t* num_bytes, + MojoWriteDataFlags flags) + + MojoResult MojoBeginWriteData( + MojoHandle data_pipe_producer_handle, + void** buffer, + uint32_t* buffer_num_bytes, + MojoWriteDataFlags flags) + + MojoResult MojoEndWriteData( + MojoHandle data_pipe_producer_handle, + uint32_t num_bytes_written) + + MojoResult MojoReadData( + MojoHandle data_pipe_consumer_handle, + void* elements, + uint32_t* num_bytes, + MojoReadDataFlags flags) + + MojoResult MojoBeginReadData( + MojoHandle data_pipe_consumer_handle, + const void** buffer, + uint32_t* buffer_num_bytes, + MojoReadDataFlags flags) + + MojoResult MojoEndReadData( + MojoHandle data_pipe_consumer_handle, + uint32_t num_bytes_read) + + # buffer.h + ctypedef uint32_t MojoCreateSharedBufferOptionsFlags + const MojoCreateSharedBufferOptionsFlags MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE + + cdef struct MojoCreateSharedBufferOptions: + uint32_t struct_size + MojoCreateSharedBufferOptionsFlags flags + + ctypedef uint32_t MojoDuplicateBufferHandleOptionsFlags + const MojoDuplicateBufferHandleOptionsFlags MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE + + cdef struct MojoDuplicateBufferHandleOptions: + uint32_t struct_size + MojoDuplicateBufferHandleOptionsFlags flags + + ctypedef uint32_t MojoMapBufferFlags + const MojoMapBufferFlags MOJO_MAP_BUFFER_FLAG_NONE + + MojoResult MojoCreateSharedBuffer( + const MojoCreateSharedBufferOptions* options, + uint64_t num_bytes, + MojoHandle* shared_buffer_handle) + + MojoResult MojoDuplicateBufferHandle( + MojoHandle buffer_handle, + const MojoDuplicateBufferHandleOptions* options, + MojoHandle* new_buffer_handle) + + MojoResult MojoMapBuffer(MojoHandle buffer_handle, + uint64_t offset, + uint64_t num_bytes, + void** buffer, + MojoMapBufferFlags flags) + + MojoResult MojoUnmapBuffer(void* buffer) diff --git a/mojo/public/python/mojo/system.pyx b/mojo/public/python/mojo/system.pyx new file mode 100644 index 0000000..80b8dd3 --- /dev/null +++ b/mojo/public/python/mojo/system.pyx @@ -0,0 +1,696 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# distutils language = c++ + +cimport c_core + +from cpython.buffer cimport PyBUF_CONTIG +from cpython.buffer cimport PyBUF_CONTIG_RO +from cpython.buffer cimport Py_buffer +from cpython.buffer cimport PyBuffer_FillInfo +from cpython.buffer cimport PyBuffer_Release +from cpython.buffer cimport PyObject_GetBuffer +from cpython.mem cimport PyMem_Malloc, PyMem_Free +from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t, uintptr_t + +def set_system_thunks(system_thunks_as_object): + """Bind the basic Mojo Core functions. + + This should only be used by the embedder. + """ + cdef const c_core.MojoSystemThunks* system_thunks = ( + <const c_core.MojoSystemThunks*><uintptr_t>system_thunks_as_object) + c_core.MojoSetSystemThunks(system_thunks) + +HANDLE_INVALID = c_core.MOJO_HANDLE_INVALID +RESULT_OK = c_core.MOJO_RESULT_OK +RESULT_CANCELLED = c_core.MOJO_RESULT_CANCELLED +RESULT_UNKNOWN = c_core.MOJO_RESULT_UNKNOWN +RESULT_INVALID_ARGUMENT = c_core.MOJO_RESULT_INVALID_ARGUMENT +RESULT_DEADLINE_EXCEEDED = c_core.MOJO_RESULT_DEADLINE_EXCEEDED +RESULT_NOT_FOUND = c_core.MOJO_RESULT_NOT_FOUND +RESULT_ALREADY_EXISTS = c_core.MOJO_RESULT_ALREADY_EXISTS +RESULT_PERMISSION_DENIED = c_core.MOJO_RESULT_PERMISSION_DENIED +RESULT_RESOURCE_EXHAUSTED = c_core.MOJO_RESULT_RESOURCE_EXHAUSTED +RESULT_FAILED_PRECONDITION = c_core.MOJO_RESULT_FAILED_PRECONDITION +RESULT_ABORTED = c_core.MOJO_RESULT_ABORTED +RESULT_OUT_OF_RANGE = c_core.MOJO_RESULT_OUT_OF_RANGE +RESULT_UNIMPLEMENTED = c_core.MOJO_RESULT_UNIMPLEMENTED +RESULT_INTERNAL = c_core.MOJO_RESULT_INTERNAL +RESULT_UNAVAILABLE = c_core.MOJO_RESULT_UNAVAILABLE +RESULT_DATA_LOSS = c_core.MOJO_RESULT_DATA_LOSS +RESULT_BUSY = c_core.MOJO_RESULT_BUSY +RESULT_SHOULD_WAIT = c_core.MOJO_RESULT_SHOULD_WAIT +DEADLINE_INDEFINITE = c_core.MOJO_DEADLINE_INDEFINITE +HANDLE_SIGNAL_NONE = c_core.MOJO_HANDLE_SIGNAL_NONE +HANDLE_SIGNAL_READABLE = c_core.MOJO_HANDLE_SIGNAL_READABLE +HANDLE_SIGNAL_WRITABLE = c_core.MOJO_HANDLE_SIGNAL_WRITABLE +WRITE_MESSAGE_FLAG_NONE = c_core.MOJO_WRITE_MESSAGE_FLAG_NONE +READ_MESSAGE_FLAG_NONE = c_core.MOJO_READ_MESSAGE_FLAG_NONE +READ_MESSAGE_FLAG_MAY_DISCARD = c_core.MOJO_READ_MESSAGE_FLAG_MAY_DISCARD +WRITE_DATA_FLAG_NONE = c_core.MOJO_WRITE_DATA_FLAG_NONE +WRITE_DATA_FLAG_ALL_OR_NONE = c_core.MOJO_WRITE_DATA_FLAG_ALL_OR_NONE +READ_DATA_FLAG_NONE = c_core.MOJO_READ_DATA_FLAG_NONE +READ_DATA_FLAG_ALL_OR_NONE = c_core.MOJO_READ_DATA_FLAG_ALL_OR_NONE +READ_DATA_FLAG_DISCARD = c_core.MOJO_READ_DATA_FLAG_DISCARD +READ_DATA_FLAG_QUERY = c_core.MOJO_READ_DATA_FLAG_QUERY +MAP_BUFFER_FLAG_NONE = c_core.MOJO_MAP_BUFFER_FLAG_NONE + +def get_time_ticks_now(): + """Monotonically increasing tick count representing "right now." + + See mojo/public/c/system/functions.h + """ + return c_core.MojoGetTimeTicksNow() + +cdef class _ScopedMemory: + """Allocate memory at creation, and deallocate it at destruction.""" + cdef void* memory + def __init__(self, size): + self.memory = PyMem_Malloc(size) + + def __dealloc__(self): + PyMem_Free(self.memory) + +cdef class _ScopedBuffer: + """Retrieve pointer to a buffer a creation, and release it at destruction. + """ + cdef Py_buffer _buf + cdef void* buf + cdef Py_ssize_t len + + def __init__(self, obj, flags=PyBUF_CONTIG_RO): + if obj: + if PyObject_GetBuffer(obj, &self._buf, flags) < 0: + raise TypeError('Unable to read buffer.') + self.buf = self._buf.buf + self.len = self._buf.len + else: + self.buf = NULL + self.len = 0 + + def __dealloc__(self): + if self.buf: + PyBuffer_Release(&self._buf) + +def _slice_buffer(buffer, size): + """Slice the given buffer, reducing it to the given size. + + Return None if None is passed in. + """ + if not buffer: + return buffer + return buffer[:size] + +cdef class _NativeMemoryView(object): + """Create a python buffer wrapping the given memory. + + Will also retain the given handle until this object is deallocated. + """ + cdef void* _memory + cdef uint32_t _size + cdef char _read_only + cdef char _wrapped + cdef object _handle + + def __init__(self, handle): + self._handle = handle + + def __cinit__(self): + self._memory = NULL + self._size = 0 + self._read_only = True + self._wrapped = False + + cdef wrap(self, + const void* memory, + uint32_t size, + read_only=True): + """Makes this buffer wraps the given memory. + + Must be called before using this buffer, and must only be called once. + """ + assert not self._wrapped + self._wrapped = True + self._memory = <void*>memory + self._size = size + self._read_only = read_only + + # buffer interface (PEP 3118) + def __getbuffer__(self, Py_buffer *view, int flags): + assert self._wrapped + if view == NULL: + return + PyBuffer_FillInfo(view, + self, + self._memory, + self._size, + self._read_only, + flags) + + def __releasebuffer__(self, Py_buffer *view): + assert self._wrapped + pass + + # legacy buffer interface + def __getsegcount__(self, Py_ssize_t *sizes): + assert self._wrapped + if sizes != NULL: + sizes[0] = self._size + return 1 + + def __getreadbuffer__(self, Py_ssize_t index, void **data): + assert self._wrapped + if index != 0: + raise SystemError('Index out of bounds: %d' % index) + data[0] = self._memory + return self._size + + def __getwritebuffer__(self, Py_ssize_t index, void **data): + assert self._wrapped + if index != 0: + raise SystemError('Index out of bounds: %d' % index) + if self._read_only: + raise TypeError('Buffer is read-only.') + data[0] = self._memory + return self._size + +class MojoException(Exception): + """Exception wrapping a mojo result error code.""" + + def __init__(self, mojo_result): + self.mojo_result = mojo_result + +def wait_many(handles_and_signals, deadline): + """Waits on a list of handles. + + Args: + handles_and_signals: list of tuples of handle and signal. + + See mojo/public/c/system/functions.h + """ + cdef uint32_t length = len(handles_and_signals) + cdef _ScopedMemory handles_alloc = _ScopedMemory( + sizeof(c_core.MojoHandle) * length) + cdef _ScopedMemory signals_alloc = _ScopedMemory( + sizeof(c_core.MojoHandleSignals) * length) + cdef c_core.MojoHandle* handles = <c_core.MojoHandle*>handles_alloc.memory + cdef c_core.MojoHandleSignals* signals = ( + <c_core.MojoHandleSignals*>signals_alloc.memory) + cdef int index = 0 + for (h, s) in handles_and_signals: + handles[index] = (<Handle?>h)._mojo_handle + signals[index] = s + index += 1 + cdef c_core.MojoResult result = c_core.MOJO_RESULT_OK + cdef c_core.MojoDeadline cdeadline = deadline + with nogil: + result = c_core.MojoWaitMany(handles, signals, length, cdeadline) + return result + +cdef class DataPipeTwoPhaseBuffer(object): + """Return value for two phases read and write. + + The buffer field contains the python buffer where data can be read or written. + When done with the buffer, the |end| method must be called with the number of + bytes read or written. + """ + + cdef object _buffer + cdef Handle _handle + cdef char _read + + def __init__(self, handle, buffer, read=True): + self._buffer = buffer + self._handle = handle + self._read = read + + def end(self, num_bytes): + self._buffer = None + cdef c_core.MojoResult result + if self._read: + result = c_core.MojoEndReadData(self._handle._mojo_handle, num_bytes) + else: + result = c_core.MojoEndWriteData(self._handle._mojo_handle, num_bytes) + self._handle = None + return result + + @property + def buffer(self): + return self._buffer + + def __dealloc__(self): + assert not self._buffer + +cdef class MappedBuffer(object): + """Return value for the |map| operation on shared buffer handles. + + The buffer field contains the python buffer where data can be read or written. + When done with the buffer, the |unmap| method must be called. + """ + + cdef object _buffer + cdef object _handle + cdef object _cleanup + + def __init__(self, handle, buffer, cleanup): + self._buffer = buffer + self._handle = handle + self._cleanup = cleanup + + def unmap(self): + self._buffer = None + cdef c_core.MojoResult result = self._cleanup() + self._cleanup = None + self._handle = None + return result + + @property + def buffer(self): + return self._buffer + + def __dealloc__(self): + if self._buffer: + self.unmap() + +cdef class Handle(object): + """A mojo object.""" + + cdef c_core.MojoHandle _mojo_handle + + def __init__(self, mojo_handle=c_core.MOJO_HANDLE_INVALID): + self._mojo_handle = mojo_handle + + def _invalidate(self): + """Invalidate the current handle. + + The close operation is not called. It is the responsability of the caller to + ensure that the handle is not leaked. + """ + self._mojo_handle = c_core.MOJO_HANDLE_INVALID + + def is_valid(self): + """Returns whether this handle is valid.""" + return self._mojo_handle != c_core.MOJO_HANDLE_INVALID + + def close(self): + """Closes this handle. + + See mojo/public/c/system/functions.h + """ + cdef c_core.MojoResult result = c_core.MOJO_RESULT_OK + if self.is_valid(): + result = c_core.MojoClose(self._mojo_handle) + self._invalidate() + return result + + def __dealloc__(self): + self.close() + + def wait(self, signals, deadline): + """Waits on the given handle. + + See mojo/public/c/system/functions.h + """ + cdef c_core.MojoHandle handle = self._mojo_handle + cdef c_core.MojoHandleSignals csignals = signals + cdef c_core.MojoDeadline cdeadline = deadline + cdef c_core.MojoResult result + with nogil: + result = c_core.MojoWait(handle, csignals, cdeadline) + return result + + def write_message(self, + buffer=None, + handles=None, + flags=WRITE_MESSAGE_FLAG_NONE): + """Writes a message to the message pipe. + + This method can only be used on a handle obtained from |MessagePipe()|. + + See mojo/public/c/system/message_pipe.h + """ + cdef _ScopedBuffer buffer_as_buffer = _ScopedBuffer(buffer) + cdef uint32_t input_buffer_length = buffer_as_buffer.len + cdef c_core.MojoHandle* input_handles = NULL + cdef uint32_t input_handles_length = 0 + cdef _ScopedMemory handles_alloc = None + if handles: + input_handles_length = len(handles) + handles_alloc = _ScopedMemory(sizeof(c_core.MojoHandle) * + input_handles_length) + input_handles = <c_core.MojoHandle*>handles_alloc.memory + for i in xrange(input_handles_length): + input_handles[i] = (<Handle?>handles[i])._mojo_handle + cdef c_core.MojoResult res = c_core.MojoWriteMessage(self._mojo_handle, + buffer_as_buffer.buf, + input_buffer_length, + input_handles, + input_handles_length, + flags) + if res == c_core.MOJO_RESULT_OK and handles: + # Handles have been transferred. Let's invalidate those. + for handle in handles: + handle._invalidate() + return res + + def read_message(self, + buffer=None, + max_number_of_handles=0, + flags=READ_MESSAGE_FLAG_NONE): + """Reads a message from the message pipe. + + This method can only be used on a handle obtained from |MessagePipe()|. + + This method returns a triplet of value (code, data, sizes): + - if code is RESULT_OK, sizes will be None, and data will be a pair of + (buffer, handles) where buffer is a view of the input buffer with the read + data, and handles is a list of received handles. + - if code is RESULT_RESOURCE_EXHAUSTED, data will be None and sizes will be + a pair of (buffer_size, handles_size) where buffer_size is the size of the + next message data and handles_size is the number of handles in the next + message. + - if code is any other value, data and sizes will be None. + + See mojo/public/c/system/message_pipe.h + """ + cdef _ScopedBuffer buffer_as_buffer = _ScopedBuffer(buffer, PyBUF_CONTIG) + cdef uint32_t input_buffer_length = buffer_as_buffer.len + cdef c_core.MojoHandle* input_handles = NULL + cdef uint32_t input_handles_length = 0 + cdef _ScopedMemory handles_alloc = None + if max_number_of_handles > 0: + input_handles_length = max_number_of_handles + handles_alloc = _ScopedMemory(sizeof(c_core.MojoHandle) * + input_handles_length) + input_handles = <c_core.MojoHandle*>handles_alloc.memory + cdef res = c_core.MojoReadMessage(self._mojo_handle, + buffer_as_buffer.buf, + &input_buffer_length, + input_handles, + &input_handles_length, + flags) + if res == c_core.MOJO_RESULT_RESOURCE_EXHAUSTED: + return (res, None, (input_buffer_length, input_handles_length)) + if res == c_core.MOJO_RESULT_OK: + returned_handles = [Handle(input_handles[i]) + for i in xrange(input_handles_length)] + return (res, + (_slice_buffer(buffer, input_buffer_length), returned_handles), + None) + return (res, None, None) + + def write_data(self, buffer=None, flags=WRITE_DATA_FLAG_NONE): + """ + Writes the given data to the data pipe producer. + + This method can only be used on a producer handle obtained from + |DataPipe()|. + + This method returns a tuple (code, num_bytes). + - If code is RESULT_OK, num_bytes is the number of written bytes. + - Otherwise, num_bytes is None. + + See mojo/public/c/system/data_pipe.h + """ + cdef _ScopedBuffer buffer_as_buffer = _ScopedBuffer(buffer) + cdef uint32_t input_buffer_length = buffer_as_buffer.len + cdef c_core.MojoResult res = c_core.MojoWriteData(self._mojo_handle, + buffer_as_buffer.buf, + &input_buffer_length, + flags) + if res == c_core.MOJO_RESULT_OK: + return (res, input_buffer_length) + return (res, None) + + def begin_write_data(self, + min_size=None, + flags=WRITE_DATA_FLAG_NONE): + """ + Begins a two-phase write to the data pipe producer. + + This method can only be used on a producer handle obtained from + |DataPipe()|. + + This method returns a tuple (code, two_phase_buffer). + - If code is RESULT_OK, two_phase_buffer is a writable + DataPipeTwoPhaseBuffer + - Otherwise, two_phase_buffer is None. + + See mojo/public/c/system/data_pipe.h + """ + cdef void* out_buffer + cdef uint32_t out_size = 0 + if min_size: + flags |= c_core.MOJO_WRITE_DATA_FLAG_ALL_OR_NONE + out_size = min_size + cdef c_core.MojoResult res = c_core.MojoBeginWriteData(self._mojo_handle, + &out_buffer, + &out_size, + flags) + if res != c_core.MOJO_RESULT_OK: + return (res, None) + cdef _NativeMemoryView view_buffer = _NativeMemoryView(self) + view_buffer.wrap(out_buffer, out_size, read_only=False) + return (res, DataPipeTwoPhaseBuffer(self, memoryview(view_buffer), False)) + + def read_data(self, buffer=None, flags=READ_DATA_FLAG_NONE): + """Reads data from the data pipe consumer. + + This method can only be used on a consumer handle obtained from + |DataPipe()|. + + This method returns a tuple (code, buffer) + - if code is RESULT_OK, buffer will be a view of the input buffer with the + read data. + - otherwise, buffer will be None. + + See mojo/public/c/system/data_pipe.h + """ + cdef _ScopedBuffer buffer_as_buffer = _ScopedBuffer(buffer) + cdef uint32_t input_buffer_length = buffer_as_buffer.len + cdef c_core.MojoResult res = c_core.MojoReadData(self._mojo_handle, + buffer_as_buffer.buf, + &input_buffer_length, + flags) + if res == c_core.MOJO_RESULT_OK: + return (res, _slice_buffer(buffer, input_buffer_length)) + return (res, None) + + def query_data(self, flags=READ_DATA_FLAG_NONE): + """Queries the amount of data available on the data pipe consumer. + + This method can only be used on a consumer handle obtained from + |DataPipe()|. + + This method returns a tuple (code, num_bytes) + - if code is RESULT_OK, num_bytes will be the number of bytes available on + the data pipe consumer. + - otherwise, num_bytes will be None. + + See mojo/public/c/system/data_pipe.h + """ + cdef uint32_t num_bytes = 0 + cdef c_core.MojoResult res = c_core.MojoReadData( + self._mojo_handle, + NULL, + &num_bytes, + flags|c_core.MOJO_READ_DATA_FLAG_QUERY) + return (res, num_bytes) + + def begin_read_data(self, min_size=None, flags=READ_DATA_FLAG_NONE): + """ + Begins a two-phase read to the data pipe consumer. + + This method can only be used on a consumer handle obtained from + |DataPipe()|. + + This method returns a tuple (code, two_phase_buffer). + - If code is RESULT_OK, two_phase_buffer is a readable + DataPipeTwoPhaseBuffer + - Otherwise, two_phase_buffer is None. + + See mojo/public/c/system/data_pipe.h + """ + cdef const void* out_buffer + cdef uint32_t out_size = 0 + if min_size: + flags |= c_core.MOJO_READ_DATA_FLAG_ALL_OR_NONE + out_size = min_size + cdef c_core.MojoResult res = c_core.MojoBeginReadData(self._mojo_handle, + &out_buffer, + &out_size, + flags) + if res != c_core.MOJO_RESULT_OK: + return (res, None) + cdef _NativeMemoryView view_buffer = _NativeMemoryView(self) + view_buffer.wrap(out_buffer, out_size, read_only=True) + return (res, DataPipeTwoPhaseBuffer(self, memoryview(view_buffer), True)) + + def duplicate(self, options=None): + """Duplicate the shared buffer handle. + + This method can only be used on a handle obtained from + |create_shared_buffer()| or |duplicate()|. + + See mojo/public/c/system/buffer.h + """ + cdef c_core.MojoDuplicateBufferHandleOptions coptions + cdef c_core.MojoDuplicateBufferHandleOptions* coptions_ptr = NULL + cdef c_core.MojoHandle cnew_handle = c_core.MOJO_HANDLE_INVALID + if options: + coptions.struct_size = sizeof(c_core.MojoDuplicateBufferHandleOptions) + coptions.flags = options.flags + coptions_ptr = &coptions + cdef c_core.MojoResult result = c_core.MojoDuplicateBufferHandle( + self._mojo_handle, coptions_ptr, &cnew_handle) + new_handle = Handle(cnew_handle) + if result != c_core.MOJO_RESULT_OK: + raise MojoException(result) + return new_handle + + def map(self, offset, num_bytes, flags=MAP_BUFFER_FLAG_NONE): + """Maps the part (at offset |offset| of length |num_bytes|) of the buffer. + + This method can only be used on a handle obtained from + |create_shared_buffer()| or |duplicate()|. + + This method returns a tuple (code, mapped_buffer). + - If code is RESULT_OK, mapped_buffer is a readable/writable + MappedBuffer + - Otherwise, mapped_buffer is None. + + See mojo/public/c/system/buffer.h + """ + cdef void* buffer + res = c_core.MojoMapBuffer(self._mojo_handle, + offset, + num_bytes, + &buffer, + flags) + if res != c_core.MOJO_RESULT_OK: + return (res, None) + cdef _NativeMemoryView view_buffer = _NativeMemoryView(self) + view_buffer.wrap(buffer, num_bytes, read_only=False) + return (res, MappedBuffer(self, + memoryview(view_buffer), + lambda: c_core.MojoUnmapBuffer(buffer))) + +class CreateMessagePipeOptions(object): + """Options for creating a message pipe. + + See mojo/public/c/system/message_pipe.h + """ + FLAG_NONE = c_core.MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE + + def __init__(self): + self.flags = CreateMessagePipeOptions.FLAG_NONE + +class MessagePipe(object): + """Creates a message pipe. + + The two ends of the message pipe are accessible with the members handle0 and + handle1. + + See mojo/public/c/system/message_pipe.h + """ + def __init__(self, options=None): + cdef c_core.MojoCreateMessagePipeOptions coptions + cdef c_core.MojoCreateMessagePipeOptions* coptions_ptr = NULL + cdef c_core.MojoHandle chandle0 = c_core.MOJO_HANDLE_INVALID + cdef c_core.MojoHandle chandle1 = c_core.MOJO_HANDLE_INVALID + if options: + coptions.struct_size = sizeof(c_core.MojoCreateMessagePipeOptions) + coptions.flags = options.flags + coptions_ptr = &coptions + cdef c_core.MojoResult result = c_core.MojoCreateMessagePipe(coptions_ptr, + &chandle0, + &chandle1) + self.handle0 = Handle(chandle0) + self.handle1 = Handle(chandle1) + if result != c_core.MOJO_RESULT_OK: + raise c_core.MojoException(result) + + +class CreateDataPipeOptions(object): + """Options for creating a data pipe. + + See mojo/public/c/system/data_pipe.h + """ + FLAG_NONE = c_core.MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE + FLAG_MAY_DISCARD = c_core.MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD + + def __init__(self): + self.flags = CreateDataPipeOptions.FLAG_NONE + self.element_num_bytes = 1 + self.capacity_num_bytes = 0 + +class DataPipe(object): + """Creates a data pipe. + + The producer end of the data pipe is accessible with the member + producer_handle and the consumer end of the data pipe is accessible with the + member cconsumer_handle. + + See mojo/public/c/system/data_pipe.h + """ + def __init__(self, options=None): + cdef c_core.MojoCreateDataPipeOptions coptions + cdef c_core.MojoCreateDataPipeOptions* coptions_ptr = NULL + cdef c_core.MojoHandle cproducer_handle = c_core.MOJO_HANDLE_INVALID + cdef c_core.MojoHandle cconsumer_handle = c_core.MOJO_HANDLE_INVALID + if options: + coptions.struct_size = sizeof(c_core.MojoCreateDataPipeOptions) + coptions.flags = options.flags + coptions.element_num_bytes = options.element_num_bytes + coptions.capacity_num_bytes = options.capacity_num_bytes + coptions_ptr = &coptions + cdef c_core.MojoResult result = c_core.MojoCreateDataPipe(coptions_ptr, + &cproducer_handle, + &cconsumer_handle) + self.producer_handle = Handle(cproducer_handle) + self.consumer_handle = Handle(cconsumer_handle) + if result != c_core.MOJO_RESULT_OK: + raise MojoException(result) + +class CreateSharedBufferOptions(object): + """Options for creating a shared buffer. + + See mojo/public/c/system/buffer.h + """ + FLAG_NONE = c_core.MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE + + def __init__(self): + self.flags = CreateSharedBufferOptions.FLAG_NONE + +def create_shared_buffer(num_bytes, options=None): + """Creates a buffer of size |num_bytes| bytes that can be shared. + + See mojo/public/c/system/buffer.h + """ + cdef c_core.MojoCreateSharedBufferOptions coptions + cdef c_core.MojoCreateSharedBufferOptions* coptions_ptr = NULL + cdef c_core.MojoHandle chandle = c_core.MOJO_HANDLE_INVALID + if options: + coptions.struct_size = sizeof(c_core.MojoCreateSharedBufferOptions) + coptions.flags = options.flags + coptions_ptr = &coptions + cdef c_core.MojoResult result = c_core.MojoCreateSharedBuffer(coptions_ptr, + num_bytes, + &chandle) + handle = Handle(chandle) + if result != c_core.MOJO_RESULT_OK: + raise MojoException(result) + return handle + +class DuplicateSharedBufferOptions(object): + """Options for duplicating a shared buffer. + + See mojo/public/c/system/buffer.h + """ + FLAG_NONE = c_core.MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE + + def __init__(self): + self.flags = DuplicateSharedBufferOptions.FLAG_NONE diff --git a/mojo/python/BUILD.gn b/mojo/python/BUILD.gn new file mode 100644 index 0000000..18b8737 --- /dev/null +++ b/mojo/python/BUILD.gn @@ -0,0 +1,29 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/cython/rules.gni") + +group("python") { + deps = [ + ":embedder", + ] +} + +group("tests") { + deps = [ + "//mojo/python/tests", + ] +} +python_binary_module("embedder") { + python_base_module = "mojo" + sources = [ + "system/mojo/embedder.pyx", + ] + deps = [ + "//mojo/system", + ] + datadeps = [ + "//mojo/public/python:system", + ] +} diff --git a/mojo/python/system/mojo/embedder.pyx b/mojo/python/system/mojo/embedder.pyx new file mode 100644 index 0000000..c7021f0 --- /dev/null +++ b/mojo/python/system/mojo/embedder.pyx @@ -0,0 +1,41 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# distutils: language = c++ + +from libc.stdint cimport uintptr_t + +from mojo import system + +cdef extern from "third_party/cython/python_export.h": + pass + +cdef extern from "base/memory/scoped_ptr.h": + cdef cppclass scoped_ptr[T]: + scoped_ptr(T*) + +cdef extern from "mojo/embedder/platform_support.h" \ + namespace "mojo::embedder" nogil: + cdef cppclass PlatformSupport: + pass + +cdef extern from "mojo/embedder/simple_platform_support.h" \ + namespace "mojo::embedder" nogil: + cdef cppclass SimplePlatformSupport(PlatformSupport): + SimplePlatformSupport() + +cdef extern from "mojo/embedder/embedder.h" nogil: + cdef void InitCEmbedder "mojo::embedder::Init"( + scoped_ptr[PlatformSupport] platform_support) + +cdef extern from "mojo/public/platform/native/system_thunks.h" nogil: + cdef struct MojoSystemThunks: + pass + cdef MojoSystemThunks MojoMakeSystemThunks() + +def init(): + InitCEmbedder(scoped_ptr[PlatformSupport]( + new SimplePlatformSupport())) + cdef MojoSystemThunks thunks = MojoMakeSystemThunks() + system.set_system_thunks(<uintptr_t>(&thunks)) diff --git a/mojo/python/tests/BUILD.gn b/mojo/python/tests/BUILD.gn new file mode 100644 index 0000000..0d2949d --- /dev/null +++ b/mojo/python/tests/BUILD.gn @@ -0,0 +1,24 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/cython/rules.gni") + +group("tests") { + deps = [ + ":test_core", + ] +} + +action("test_core") { + script = "test_core.py" + outputs = [ + "$root_out_dir/no_file", + ] + inputs = [] + deps = [ + "//mojo/public/python:system", + "//mojo/python:embedder", + ] + args = [ rebase_path("$root_out_dir/python", root_build_dir) ] +} diff --git a/mojo/python/tests/test_core.py b/mojo/python/tests/test_core.py new file mode 100644 index 0000000..be0253b --- /dev/null +++ b/mojo/python/tests/test_core.py @@ -0,0 +1,314 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import random +import sys +import time +import unittest + +# Setup sys path +for path in sys.argv[1:]: + sys.path.append(path) + +# pylint: disable=F0401 +from mojo.embedder import init as init_embedder +from mojo import system + +DATA_SIZE = 1024 + + +def get_random_buffer(size): + random.seed(size) + return bytearray(''.join(chr(random.randint(0, 255)) for i in xrange(size))) + + +class BaseMojoTest(unittest.TestCase): + + def setUp(self): + init_embedder() + + +class CoreTest(BaseMojoTest): + + def test_results(self): + self.assertEquals(system.RESULT_OK, 0) + self.assertLess(system.RESULT_CANCELLED, 0) + self.assertLess(system.RESULT_UNKNOWN, 0) + self.assertLess(system.RESULT_INVALID_ARGUMENT, 0) + self.assertLess(system.RESULT_DEADLINE_EXCEEDED, 0) + self.assertLess(system.RESULT_NOT_FOUND, 0) + self.assertLess(system.RESULT_ALREADY_EXISTS, 0) + self.assertLess(system.RESULT_PERMISSION_DENIED, 0) + self.assertLess(system.RESULT_RESOURCE_EXHAUSTED, 0) + self.assertLess(system.RESULT_FAILED_PRECONDITION, 0) + self.assertLess(system.RESULT_ABORTED, 0) + self.assertLess(system.RESULT_OUT_OF_RANGE, 0) + self.assertLess(system.RESULT_UNIMPLEMENTED, 0) + self.assertLess(system.RESULT_INTERNAL, 0) + self.assertLess(system.RESULT_UNAVAILABLE, 0) + self.assertLess(system.RESULT_DATA_LOSS, 0) + self.assertLess(system.RESULT_BUSY, 0) + self.assertLess(system.RESULT_SHOULD_WAIT, 0) + + def test_constants(self): + self.assertGreaterEqual(system.DEADLINE_INDEFINITE, 0) + self.assertGreaterEqual(system.HANDLE_SIGNAL_NONE, 0) + self.assertGreaterEqual(system.HANDLE_SIGNAL_READABLE, 0) + self.assertGreaterEqual(system.HANDLE_SIGNAL_WRITABLE, 0) + self.assertGreaterEqual(system.WRITE_MESSAGE_FLAG_NONE, 0) + self.assertGreaterEqual(system.READ_MESSAGE_FLAG_NONE, 0) + self.assertGreaterEqual(system.READ_MESSAGE_FLAG_MAY_DISCARD, 0) + self.assertGreaterEqual(system.WRITE_DATA_FLAG_NONE, 0) + self.assertGreaterEqual(system.WRITE_DATA_FLAG_ALL_OR_NONE, 0) + self.assertGreaterEqual(system.READ_DATA_FLAG_NONE, 0) + self.assertGreaterEqual(system.READ_DATA_FLAG_ALL_OR_NONE, 0) + self.assertGreaterEqual(system.READ_DATA_FLAG_DISCARD, 0) + self.assertGreaterEqual(system.READ_DATA_FLAG_QUERY, 0) + self.assertGreaterEqual(system.MAP_BUFFER_FLAG_NONE, 0) + + def test_get_time_ticks_now(self): + pt1 = time.time() + v1 = system.get_time_ticks_now() + time.sleep(1e-3) + v2 = system.get_time_ticks_now() + pt2 = time.time() + self.assertGreater(v1, 0) + self.assertGreater(v2, v1 + 1000) + self.assertGreater(1e6 * (pt2 - pt1), v2 - v1) + + def _test_handles_creation(self, *args): + for handle in args: + self.assertTrue(handle.is_valid()) + handle.close() + self.assertFalse(handle.is_valid()) + + def _test_message_handle_creation(self, handles): + self._test_handles_creation(handles.handle0, handles.handle1) + + def test_create_message_pipe(self): + self._test_message_handle_creation(system.MessagePipe()) + + def test_create_message_pipe_with_none_options(self): + self._test_message_handle_creation(system.MessagePipe(None)) + + def test_create_message_pipe_with_options(self): + self._test_message_handle_creation( + system.MessagePipe(system.CreateMessagePipeOptions())) + + def test_wait_over_message_pipe(self): + handles = system.MessagePipe() + handle = handles.handle0 + + self.assertEquals(system.RESULT_OK, handle.wait( + system.HANDLE_SIGNAL_WRITABLE, system.DEADLINE_INDEFINITE)) + self.assertEquals(system.RESULT_DEADLINE_EXCEEDED, + handle.wait(system.HANDLE_SIGNAL_READABLE, 0)) + + handles.handle1.write_message() + + self.assertEquals( + system.RESULT_OK, + handle.wait( + system.HANDLE_SIGNAL_READABLE, + system.DEADLINE_INDEFINITE)) + + def test_wait_over_many_message_pipe(self): + handles = system.MessagePipe() + handle0 = handles.handle0 + handle1 = handles.handle1 + + self.assertEquals( + 0, + system.wait_many( + [(handle0, system.HANDLE_SIGNAL_WRITABLE), + (handle1, system.HANDLE_SIGNAL_WRITABLE)], + system.DEADLINE_INDEFINITE)) + self.assertEquals( + system.RESULT_DEADLINE_EXCEEDED, + system.wait_many( + [(handle0, system.HANDLE_SIGNAL_READABLE), + (handle1, system.HANDLE_SIGNAL_READABLE)], 0)) + + handle0.write_message() + + self.assertEquals( + 1, + system.wait_many( + [(handle0, system.HANDLE_SIGNAL_READABLE), + (handle1, system.HANDLE_SIGNAL_READABLE)], + system.DEADLINE_INDEFINITE)) + + def test_send_bytes_over_message_pipe(self): + handles = system.MessagePipe() + data = get_random_buffer(DATA_SIZE) + handles.handle0.write_message(data) + (res, buffers, next_message) = handles.handle1.read_message() + self.assertEquals(system.RESULT_RESOURCE_EXHAUSTED, res) + self.assertEquals(None, buffers) + self.assertEquals((DATA_SIZE, 0), next_message) + result = bytearray(DATA_SIZE) + (res, buffers, next_message) = handles.handle1.read_message(result) + self.assertEquals(system.RESULT_OK, res) + self.assertEquals(None, next_message) + self.assertEquals((data, []), buffers) + + def test_send_empty_data_over_message_pipe(self): + handles = system.MessagePipe() + handles.handle0.write_message(None) + (res, buffers, next_message) = handles.handle1.read_message() + + self.assertEquals(system.RESULT_OK, res) + self.assertEquals(None, next_message) + self.assertEquals((None, []), buffers) + + def test_send_handle_over_message_pipe(self): + handles = system.MessagePipe() + handles_to_send = system.MessagePipe() + handles.handle0.write_message(handles=[handles_to_send.handle0, + handles_to_send.handle1]) + (res, buffers, next_message) = handles.handle1.read_message( + max_number_of_handles=2) + + self.assertFalse(handles_to_send.handle0.is_valid()) + self.assertFalse(handles_to_send.handle1.is_valid()) + self.assertEquals(system.RESULT_OK, res) + self.assertEquals(None, next_message) + self.assertEquals(None, buffers[0]) + self.assertEquals(2, len(buffers[1])) + + handles = buffers[1] + for handle in handles: + self.assertTrue(handle.is_valid()) + (res, buffers, next_message) = handle.read_message() + self.assertEquals(system.RESULT_SHOULD_WAIT, res) + + for handle in handles: + handle.write_message() + + for handle in handles: + (res, buffers, next_message) = handle.read_message() + self.assertEquals(system.RESULT_OK, res) + + def _test_data_handle_creation(self, handles): + self._test_handles_creation( + handles.producer_handle, handles.consumer_handle) + + def test_create_data_pipe(self): + self._test_data_handle_creation(system.DataPipe()) + + def test_create_data_pipe_with_none_options(self): + self._test_data_handle_creation(system.DataPipe(None)) + + def test_create_data_pipe_with_default_options(self): + self._test_data_handle_creation( + system.DataPipe(system.CreateDataPipeOptions())) + + def test_create_data_pipe_with_discard_flag(self): + options = system.CreateDataPipeOptions() + options.flags = system.CreateDataPipeOptions.FLAG_MAY_DISCARD + self._test_data_handle_creation(system.DataPipe(options)) + + def test_create_data_pipe_with_element_size(self): + options = system.CreateDataPipeOptions() + options.element_num_bytes = 5 + self._test_data_handle_creation(system.DataPipe(options)) + + def test_create_data_pipe_with_capacity(self): + options = system.CreateDataPipeOptions() + options.element_capacity_num_bytes = DATA_SIZE + self._test_data_handle_creation(system.DataPipe(options)) + + def test_create_data_pipe_with_incorrect_parameters(self): + options = system.CreateDataPipeOptions() + options.element_num_bytes = 5 + options.capacity_num_bytes = DATA_SIZE + with self.assertRaises(system.MojoException) as cm: + self._test_data_handle_creation(system.DataPipe(options)) + self.assertEquals(system.RESULT_INVALID_ARGUMENT, cm.exception.mojo_result) + + def test_send_empty_data_over_data_pipe(self): + pipes = system.DataPipe() + self.assertEquals((system.RESULT_OK, 0), pipes.producer_handle.write_data()) + self.assertEquals( + (system.RESULT_OK, None), pipes.consumer_handle.read_data()) + + def test_send_data_over_data_pipe(self): + pipes = system.DataPipe() + data = get_random_buffer(DATA_SIZE) + self.assertEquals((system.RESULT_OK, DATA_SIZE), + pipes.producer_handle.write_data(data)) + self.assertEquals((system.RESULT_OK, data), + pipes.consumer_handle.read_data(bytearray(DATA_SIZE))) + + def test_two_phase_write_on_data_pipe(self): + pipes = system.DataPipe() + (res, buf) = pipes.producer_handle.begin_write_data(DATA_SIZE) + self.assertEquals(system.RESULT_OK, res) + self.assertGreaterEqual(len(buf.buffer), DATA_SIZE) + data = get_random_buffer(DATA_SIZE) + buf.buffer[0:DATA_SIZE] = data + self.assertEquals(system.RESULT_OK, buf.end(DATA_SIZE)) + self.assertEquals((system.RESULT_OK, data), + pipes.consumer_handle.read_data(bytearray(DATA_SIZE))) + + def test_two_phase_read_on_data_pipe(self): + pipes = system.DataPipe() + data = get_random_buffer(DATA_SIZE) + self.assertEquals((system.RESULT_OK, DATA_SIZE), + pipes.producer_handle.write_data(data)) + (res, buf) = pipes.consumer_handle.begin_read_data() + self.assertEquals(system.RESULT_OK, res) + self.assertEquals(DATA_SIZE, len(buf.buffer)) + self.assertEquals(data, buf.buffer) + self.assertEquals(system.RESULT_OK, buf.end(DATA_SIZE)) + + def test_create_shared_buffer(self): + self._test_handles_creation(system.create_shared_buffer(DATA_SIZE)) + + def test_create_shared_buffer_with_none_options(self): + self._test_handles_creation(system.create_shared_buffer(DATA_SIZE, None)) + + def test_create_shared_buffer_with_default_options(self): + self._test_handles_creation( + system.create_shared_buffer( + DATA_SIZE, + system.CreateSharedBufferOptions())) + + def test_duplicate_shared_buffer(self): + handle = system.create_shared_buffer(DATA_SIZE) + self._test_handles_creation(handle.duplicate()) + + def test_duplicate_shared_buffer_with_none_options(self): + handle = system.create_shared_buffer(DATA_SIZE) + self._test_handles_creation(handle.duplicate(None)) + + def test_duplicate_shared_buffer_with_default_options(self): + handle = system.create_shared_buffer(DATA_SIZE) + self._test_handles_creation( + handle.duplicate(system.DuplicateSharedBufferOptions())) + + def test_send_bytes_over_shared_buffer(self): + handle = system.create_shared_buffer(DATA_SIZE) + duplicated = handle.duplicate() + data = get_random_buffer(DATA_SIZE) + (res1, buf1) = handle.map(0, DATA_SIZE) + (res2, buf2) = duplicated.map(0, DATA_SIZE) + self.assertEquals(system.RESULT_OK, res1) + self.assertEquals(system.RESULT_OK, res2) + self.assertEquals(DATA_SIZE, len(buf1.buffer)) + self.assertEquals(DATA_SIZE, len(buf2.buffer)) + self.assertEquals(buf1.buffer, buf2.buffer) + + buf1.buffer[:] = data + self.assertEquals(data, buf1.buffer) + self.assertEquals(data, buf2.buffer) + self.assertEquals(buf1.buffer, buf2.buffer) + + +if __name__ == '__main__': + suite = unittest.TestLoader().loadTestsFromTestCase(CoreTest) + test_results = unittest.TextTestRunner(verbosity=0).run(suite) + if not test_results.wasSuccessful(): + sys.exit(1) + sys.exit(0) diff --git a/third_party/cython/cp_python_binary_modules.py b/third_party/cython/cp_python_binary_modules.py new file mode 100644 index 0000000..dd38e64 --- /dev/null +++ b/third_party/cython/cp_python_binary_modules.py @@ -0,0 +1,42 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import os +import shutil +import sys + +def touch(fname): + if os.path.exists(fname): + os.utime(fname, None) + else: + open(fname, 'a').close() + +def main(): + """Command line utility to copy python binary modules to the correct package + hierarchy. + """ + parser = argparse.ArgumentParser( + description='Copy python binary modules to the correct package ' + 'hierarchy.') + parser.add_argument('timestamp', help='The timetsamp file.') + parser.add_argument('lib_dir', help='The directory containing the modules') + parser.add_argument('destination_dir', + help='The destination directory of the module') + parser.add_argument('mappings', nargs='+', + help='The mapping from module to library.') + opts = parser.parse_args() + + if not os.path.exists(opts.destination_dir): + os.makedirs(opts.destination_dir) + + for mapping in opts.mappings: + [module, library] = mapping.split('=') + shutil.copy(os.path.join(opts.lib_dir, library), + os.path.join(opts.destination_dir, module)) + + touch(opts.timestamp) + +if __name__ == '__main__': + main() diff --git a/third_party/cython/cython_compiler.gypi b/third_party/cython/cython_compiler.gypi new file mode 100644 index 0000000..39d8318 --- /dev/null +++ b/third_party/cython/cython_compiler.gypi @@ -0,0 +1,71 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'python_flags': '<(DEPTH)/third_party/cython/python_flags.py', + }, + 'conditions': [ + ['OS=="mac"', { + 'variables': { + 'module_prefix': '', + 'module_suffix': '.so', + }, + }, { + 'variables': { + 'module_prefix': '<(SHARED_LIB_PREFIX)', + 'module_suffix': '<(SHARED_LIB_SUFFIX)', + }, + }], + ], + 'type': 'loadable_module', + 'rules': [ + { + 'rule_name': '<(_target_name)_cython_compiler', + 'extension': 'pyx', + 'variables': { + 'cython_compiler': '<(DEPTH)/third_party/cython/src/cython.py', + }, + 'inputs': [ + '<(cython_compiler)', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/cython/<(python_base_module)/<(RULE_INPUT_ROOT).cc', + ], + 'action': [ + 'python', '<(cython_compiler)', + '--cplus', + '-I<(DEPTH)', + '-o', '<@(_outputs)', + '<(RULE_INPUT_PATH)', + ], + 'message': 'Generating C++ source from <(RULE_INPUT_PATH)', + 'process_outputs_as_sources': 1, + } + ], + 'include_dirs': [ + '<!@(python <(python_flags) --includes)', + '<(DEPTH)', + ], + 'libraries': [ + '<!@(python <(python_flags) --libraries)', + ], + 'cflags': [ + '-Wno-unused-function', + ], + 'xcode_settings': { + 'WARNING_CFLAGS': [ '-Wno-unused-function' ], + }, + 'library_dirs': [ + '<!@(python <(python_flags) --library_dirs)', + ], + 'direct_dependent_settings': { + 'variables': { + 'python_binary_modules': [ + '<(python_cython_module)<(module_suffix)=<(module_prefix)<(_target_name)<(module_suffix)', + ], + }, + }, + 'hard_dependency': 1, +} diff --git a/third_party/cython/python_export.h b/third_party/cython/python_export.h new file mode 100644 index 0000000..e943399 --- /dev/null +++ b/third_party/cython/python_export.h @@ -0,0 +1,13 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if defined(PyMODINIT_FUNC) +#undef PyMODINIT_FUNC +#endif + +#if defined(WIN32) +#define PyMODINIT_FUNC extern "C" __declspec(dllexport) void +#else +#define PyMODINIT_FUNC extern "C" __attribute__((visibility("default"))) void +#endif diff --git a/third_party/cython/python_flags.py b/third_party/cython/python_flags.py new file mode 100644 index 0000000..03c4ad7 --- /dev/null +++ b/third_party/cython/python_flags.py @@ -0,0 +1,53 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import argparse +import os +import sys + +from distutils import sysconfig +from distutils.command import build_ext +from distutils.dist import Distribution +from distutils.extension import Extension + +def main(): + """Command line utility to retrieve compilation options for python modules' + """ + parser = argparse.ArgumentParser( + description='Retrieves compilation options for python modules.') + parser.add_argument('--gn', + help='Returns all values in a format suitable for gn', + action='store_true') + parser.add_argument('--libraries', help='Returns libraries', + action='store_true') + parser.add_argument('--includes', help='Returns includes', + action='store_true') + parser.add_argument('--library_dirs', help='Returns library_dirs', + action='store_true') + opts = parser.parse_args() + + ext = Extension('Dummy', []) + b = build_ext.build_ext(Distribution()) + b.initialize_options() + b.finalize_options() + result = [] + if opts.libraries: + libraries = b.get_libraries(ext) + if sys.platform == 'darwin': + libraries += [ '-lpython%s' % sys.version[:3] ] + result = result + libraries + if opts.includes: + result = result + b.include_dirs + if opts.library_dirs: + if sys.platform == 'darwin': + result.append('%s/lib' % sysconfig.get_config_vars('prefix')[0]) + + if opts.gn: + for x in result: + print x + else: + print ''.join(['"%s"' % x for x in result]) + +if __name__ == '__main__': + main() diff --git a/third_party/cython/python_module.gypi b/third_party/cython/python_module.gypi new file mode 100644 index 0000000..a61ca2d --- /dev/null +++ b/third_party/cython/python_module.gypi @@ -0,0 +1,51 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'python_binary_modules%': [], + 'python_module_destination': '<(PRODUCT_DIR)/python/<(python_base_module)', + 'cp': '<(DEPTH)/third_party/cython/cp_python_binary_modules.py', + 'timestamp': '<(SHARED_INTERMEDIATE_DIR)/<(_target_name)_py_module.stamp', + }, + 'rules': [ + { + 'rule_name': '<(_target_name)_cp_python', + 'extension': 'py', + 'inputs': [ + '<(DEPTH)/build/cp.py', + ], + 'outputs': [ + '<(python_module_destination)/<(RULE_INPUT_NAME)', + ], + 'action': [ + 'python', + '<@(_inputs)', + '<(RULE_INPUT_PATH)', + '<@(_outputs)', + ], + 'message': 'Moving <(RULE_INPUT_PATH) to its destination', + } + ], + 'actions': [ + { + 'action_name': '<(_target_name)_move_to_python_modules', + 'inputs': [ + '<(cp)', + ], + 'outputs': [ + '<(timestamp)', + ], + 'action': [ + 'python', + '<(cp)', + '<(timestamp)', + '<(PRODUCT_DIR)', + '<(python_module_destination)', + '>@(python_binary_modules)', + ], + }, + ], + 'hard_dependency': 1, +} diff --git a/third_party/cython/rules.gni b/third_party/cython/rules.gni new file mode 100644 index 0000000..22a56c3 --- /dev/null +++ b/third_party/cython/rules.gni @@ -0,0 +1,84 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +template("python_binary_module") { + # Only available on linux for now. + assert(is_linux) + assert(defined(invoker.sources)) + assert(defined(invoker.python_base_module)) + + cython_root = "//third_party/cython" + cython_script = "$cython_root/src/cython.py" + cython_output = "${target_out_dir}/${target_name}.cc" + + generator_target_name = target_name + "_cython_compiler" + shared_library_name = target_name + "_shared_library" + config_name = target_name + "_python_config" + + if (is_linux) { + shared_library_prefix = "lib" + shared_library_suffix = ".so" + python_module_suffix = ".so" + } + + target_visibility = [ + ":$generator_target_name", + ":$shared_library_name", + ":$target_name", + ] + + action(generator_target_name) { + visibility = target_visibility + script = cython_script + sources = invoker.sources + outputs = [ cython_output ] + args = [ + "--cplus", + "-I", rebase_path("//", root_build_dir), + "-o", rebase_path(cython_output, root_build_dir), + ] + rebase_path(sources, root_build_dir) + } + + config(config_name) { + visibility = target_visibility + python_flags = "//third_party/cython/python_flags.py" + include_dirs = exec_script(python_flags, + [ "--gn", "--includes" ], + "list lines") + libs = exec_script(python_flags, + [ "--gn", "--libraries" ], + "list lines") + lib_dirs = exec_script(python_flags, + [ "--gn", "--library_dirs" ], + "list lines") + } + + shared_library(shared_library_name) { + visibility = target_visibility + deps = [ + ":$generator_target_name", + ] + if (defined(invoker.deps)) { + deps += invoker.deps + } + if (defined(invoker.datadeps)) { + datadeps = invoker.datadeps + } + sources = [ cython_output ] + configs += [ ":$config_name" ] + } + + copy(target_name) { + python_base_module = invoker.python_base_module + sources = [ + "$root_out_dir/${shared_library_prefix}${shared_library_name}${shared_library_suffix}" + ] + outputs = [ + "$root_out_dir/python/$python_base_module/${target_name}${python_module_suffix}" + ] + deps = [ + ":$shared_library_name" + ] + } +} |