Move wrapped C library to subpackage
Create new subpackage rmr/rmrclib with the C library loaded via ctypes.
Add method to get constants from RMR library and detect mock objects.
Split test files into test_rmr and test_rmrclib.
Extend sphinx configuration to mock the rmrclib subpackage, so the
rmr package receives a mock instead of a real object.
This change allows generation of API documentation by Sphinx
when the .so file is not available and import fails, for example
at ReadTheDocs.
Signed-off-by: Lott, Christopher (cl778h) <cl778h@att.com>
Change-Id: Idb9bb1d7a534c4142ea0353b5a48d3132e55f6e6
diff --git a/docs/conf.py b/docs/conf.py
old mode 100644
new mode 100755
index b92d4d9..47e5eb7
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -2,16 +2,22 @@
import sys
from docs_conf.conf import *
+# autodoc needs this to find the code
sys.path.insert(0, os.path.abspath("../"))
extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode", "numpydoc"]
-# dont alphabetically order
+# don't alphabetically order
autodoc_member_order = "bysource"
linkcheck_ignore = ["http://localhost.*", "http://127.0.0.1.*", "https://gerrit.o-ran-sc.org.*"]
+
+# silence complaints from autodoc gen
nitpick_ignore = [
('py:class', 'ctypes.c_char_p'),
('py:class', 'ctypes.c_void_p'),
('py:class', 'ricxappframe.rmr.rmr.LP_rmr_mbuf_t'),
]
+
+# RMR c library is not available in ReadTheDocs
+autodoc_mock_imports = ['ricxappframe.rmr.rmrclib']
diff --git a/docs/rmr_api.rst b/docs/rmr_api.rst
index 4db5b7e..a33d6a1 100644
--- a/docs/rmr_api.rst
+++ b/docs/rmr_api.rst
@@ -15,1087 +15,12 @@
RMR API
-------
-..
- Sphinx can generate API documentation by running Python to pull doc strings
- from the binding code using these Sphinx directives that are commented out:
- .. automodule:: ricxappframe.rmr.rmr
- :members:
- But that approach requires the RMR library to be installed, which is difficult
- to achieve at ReadTheDocs.io. Instead, the RST below was generated and captured
- according to the method shown at
- https://stackoverflow.com/questions/2668187/make-sphinx-generate-rst-class-documentation-from-pydoc
+.. automodule:: ricxappframe.rmr.rmr
+ :members:
+RMR Helper API
+--------------
-.. py:module:: ricxappframe.rmr.rmr
-
-
-..
- !! processed by numpydoc !!
-
-.. py:data:: RMR_MAX_RCV_BYTES
- :module: ricxappframe.rmr.rmr
- :value: 65536
-
-
- Maximum size message to receive
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:data:: RMRFL_MTCALL
- :module: ricxappframe.rmr.rmr
- :value: 2
-
-
- Multi-threaded initialization flag
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:data:: RMRFL_NONE
- :module: ricxappframe.rmr.rmr
- :value: 0
-
-
- Empty flag
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:data:: RMR_OK
- :module: ricxappframe.rmr.rmr
- :value: 0
-
-
- State constant for OK
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:data:: RMR_ERR_TIMEOUT
- :module: ricxappframe.rmr.rmr
- :value: 12
-
-
- State constant for timeout
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:data:: RMR_ERR_RETRY
- :module: ricxappframe.rmr.rmr
- :value: 10
-
-
- State constant for retry
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:class:: rmr_mbuf_t
- :module: ricxappframe.rmr.rmr
-
-
- Mirrors public members of type rmr_mbuf_t from RMR header file src/common/include/rmr.h
-
- | typedef struct {
- | int state; // state of processing
- | int mtype; // message type
- | int len; // length of data in the payload (send or received)
- | unsigned char* payload; // transported data
- | unsigned char* xaction; // pointer to fixed length transaction id bytes
- | int sub_id; // subscription id
- | int tp_state; // transport state (a.k.a errno)
- |
- | these things are off limits to the user application
- |
- | void* tp_buf; // underlying transport allocated pointer (e.g. nng message)
- | void* header; // internal message header (whole buffer: header+payload)
- | unsigned char* id; // if we need an ID in the message separate from the xaction id
- | int flags; // various MFL (private) flags as needed
- | int alloc_len; // the length of the allocated space (hdr+payload)
- | } rmr_mbuf_t;
-
- RE PAYLOADs type below, see the documentation for c_char_p:
- class ctypes.c_char_p
- Represents the C char * datatype when it points to a zero-terminated string.
- For a general character pointer that may also point to binary data, POINTER(c_char)
- must be used. The constructor accepts an integer address, or a bytes object.
-
-
-
-
-
-
-
-
-
-
-
-
-
- :Attributes:
-
- **len**
- Structure/Union member
-
- **mtype**
- Structure/Union member
-
- **payload**
- Structure/Union member
-
- **state**
- Structure/Union member
-
- **sub_id**
- Structure/Union member
-
- **tp_state**
- Structure/Union member
-
- **xaction**
- Structure/Union member
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_init(uproto_port: ctypes.c_char_p, max_msg_size: int, flags: int) -> ctypes.c_void_p
- :module: ricxappframe.rmr.rmr
-
-
- Prepares the environment for sending and receiving messages.
- Refer to RMR C documentation for method::
-
- extern void* rmr_init(char* uproto_port, int max_msg_size, int flags)
-
- This function raises an exception if the returned context is None.
-
- :Parameters:
-
- **uproto_port: c_char_p**
- Pointer to a buffer with the port number as a string; e.g., "4550"
-
- **max_msg_size: integer**
- Maximum message size to receive
-
- **flags: integer**
- RMR option flags
-
- :Returns:
-
- c_void_p:
- Pointer to RMR context
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_ready(vctx: ctypes.c_void_p) -> int
- :module: ricxappframe.rmr.rmr
-
-
- Checks if a routing table has been received and installed.
- Refer to RMR C documentation for method::
-
- extern int rmr_ready(void* vctx)
-
- :Parameters:
-
- **vctx: ctypes c_void_p**
- Pointer to RMR context
-
- :Returns:
-
- 1 for yes, 0 for no
- ..
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_close(vctx: ctypes.c_void_p)
- :module: ricxappframe.rmr.rmr
-
-
- Closes the listen socket.
- Refer to RMR C documentation for method::
-
- extern void rmr_close(void* vctx)
-
- :Parameters:
-
- **vctx: ctypes c_void_p**
- Pointer to RMR context
-
- :Returns:
-
- None
- ..
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_set_stimeout(vctx: ctypes.c_void_p, rloops: int) -> int
- :module: ricxappframe.rmr.rmr
-
-
- Sets the configuration for how RMR will retry message send operations.
- Refer to RMR C documentation for method::
-
- extern int rmr_set_stimeout(void* vctx, int rloops)
-
- :Parameters:
-
- **vctx: ctypes c_void_p**
- Pointer to RMR context
-
- **rloops: int**
- Number of retry loops
-
- :Returns:
-
- 0 on success, -1 on failure
- ..
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_alloc_msg(vctx: ctypes.c_void_p, size: int, payload=None, gen_transaction_id: bool = False, mtype=None, meid=None, sub_id=None, fixed_transaction_id=None)
- :module: ricxappframe.rmr.rmr
-
-
- Allocates and returns a buffer to write and send through the RMR library.
- Refer to RMR C documentation for method::
-
- extern rmr_mbuf_t* rmr_alloc_msg(void* vctx, int size)
-
- Optionally populates the message from the remaining arguments.
-
- TODO: on next API break, clean up transaction_id ugliness. Kept for now to preserve API.
-
- :Parameters:
-
- **vctx: ctypes c_void_p**
- Pointer to RMR context
-
- **size: int**
- How much space to allocate
-
- **payload: bytes**
- if not None, attempts to set the payload
-
- **gen_transaction_id: bool**
- if True, generates and sets a transaction ID.
- Note, option fixed_transaction_id overrides this option
-
- **mtype: bytes**
- if not None, sets the sbuf's message type
-
- **meid: bytes**
- if not None, sets the sbuf's meid
-
- **sub_id: bytes**
- if not None, sets the sbuf's subscription id
-
- **fixed_transaction_id: bytes**
- if not None, used as the transaction ID.
- Note, this overrides the option gen_transaction_id
-
- :Returns:
-
- c_void_p:
- Pointer to rmr_mbuf structure
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_realloc_payload(ptr_mbuf: ctypes.c_void_p, new_len: int, copy: bool = False, clone: bool = False)
- :module: ricxappframe.rmr.rmr
-
-
- Allocates and returns a message buffer large enough for the new length.
- Refer to RMR C documentation for method::
-
- extern rmr_mbuf_t* rmr_realloc_payload(rmr_mbuf_t*, int, int, int)
-
- :Parameters:
-
- **ptr_mbuf: c_void_p**
- Pointer to rmr_mbuf structure
-
- **new_len: int**
- Length
-
- **copy: bool**
- Whether to copy the original paylod
-
- **clone: bool**
- Whether to clone the original buffer
-
- :Returns:
-
- c_void_p:
- Pointer to rmr_mbuf structure
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_free_msg(ptr_mbuf: ctypes.c_void_p)
- :module: ricxappframe.rmr.rmr
-
-
- Releases the message buffer.
- Refer to RMR C documentation for method::
-
- extern void rmr_free_msg(rmr_mbuf_t* mbuf )
-
- :Parameters:
-
- **ptr_mbuf: c_void_p**
- Pointer to rmr_mbuf structure
-
- :Returns:
-
- None
- ..
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_payload_size(ptr_mbuf: ctypes.c_void_p) -> int
- :module: ricxappframe.rmr.rmr
-
-
- Gets the number of bytes available in the payload.
- Refer to RMR C documentation for method::
-
- extern int rmr_payload_size(rmr_mbuf_t* msg)
-
- :Parameters:
-
- **ptr_mbuf: c_void_p**
- Pointer to rmr_mbuf structure
-
- :Returns:
-
- int:
- Number of bytes available
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_send_msg(vctx: ctypes.c_void_p, ptr_mbuf: ricxappframe.rmr.rmr.LP_rmr_mbuf_t) -> ricxappframe.rmr.rmr.LP_rmr_mbuf_t
- :module: ricxappframe.rmr.rmr
-
-
- Sends the message according to the routing table and returns an empty buffer.
- Refer to RMR C documentation for method::
-
- extern rmr_mbuf_t* rmr_send_msg(void* vctx, rmr_mbuf_t* msg)
-
- :Parameters:
-
- **vctx: ctypes c_void_p**
- Pointer to RMR context
-
- **ptr_mbuf: c_void_p**
- Pointer to rmr_mbuf structure
-
- :Returns:
-
- c_void_p:
- Pointer to rmr_mbuf structure
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_rcv_msg(vctx: ctypes.c_void_p, ptr_mbuf: ricxappframe.rmr.rmr.LP_rmr_mbuf_t) -> ricxappframe.rmr.rmr.LP_rmr_mbuf_t
- :module: ricxappframe.rmr.rmr
-
-
- Waits for a message to arrive, and returns it.
- Refer to RMR C documentation for method::
-
- extern rmr_mbuf_t* rmr_rcv_msg(void* vctx, rmr_mbuf_t* old_msg)
-
- :Parameters:
-
- **vctx: ctypes c_void_p**
- Pointer to RMR context
-
- **ptr_mbuf: c_void_p**
- Pointer to rmr_mbuf structure
-
- :Returns:
-
- c_void_p:
- Pointer to rmr_mbuf structure
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_torcv_msg(vctx: ctypes.c_void_p, ptr_mbuf: ricxappframe.rmr.rmr.LP_rmr_mbuf_t, ms_to: int) -> ricxappframe.rmr.rmr.LP_rmr_mbuf_t
- :module: ricxappframe.rmr.rmr
-
-
- Waits up to the timeout value for a message to arrive, and returns it.
- Refer to RMR C documentation for method::
-
- extern rmr_mbuf_t* rmr_torcv_msg(void* vctx, rmr_mbuf_t* old_msg, int ms_to)
-
- :Parameters:
-
- **vctx: ctypes c_void_p**
- Pointer to RMR context
-
- **ptr_mbuf: c_void_p**
- Pointer to rmr_mbuf structure
-
- **ms_to: int**
- Time out value in milliseconds
-
- :Returns:
-
- c_void_p:
- Pointer to rmr_mbuf structure
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_rts_msg(vctx: ctypes.c_void_p, ptr_mbuf: ricxappframe.rmr.rmr.LP_rmr_mbuf_t, payload=None, mtype=None) -> ricxappframe.rmr.rmr.LP_rmr_mbuf_t
- :module: ricxappframe.rmr.rmr
-
-
- Sends a message to the originating endpoint and returns an empty buffer.
- Refer to RMR C documentation for method::
-
- extern rmr_mbuf_t* rmr_rts_msg(void* vctx, rmr_mbuf_t* msg)
-
- additional features beyond c-rmr:
- if payload is not None, attempts to set the payload
- if mtype is not None, sets the sbuf's message type
-
- :Parameters:
-
- **vctx: ctypes c_void_p**
- Pointer to an RMR context
-
- **ptr_mbuf: ctypes c_void_p**
- Pointer to an RMR message buffer
-
- **payload: bytes**
- Payload
-
- **mtype: bytes**
- Message type
-
- :Returns:
-
- c_void_p:
- Pointer to rmr_mbuf structure
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_call(vctx: ctypes.c_void_p, ptr_mbuf: ricxappframe.rmr.rmr.LP_rmr_mbuf_t) -> ricxappframe.rmr.rmr.LP_rmr_mbuf_t
- :module: ricxappframe.rmr.rmr
-
-
- Sends a message, waits for a response and returns it.
- Refer to RMR C documentation for method::
-
- extern rmr_mbuf_t* rmr_call(void* vctx, rmr_mbuf_t* msg)
-
- :Parameters:
-
- **ptr_mbuf: ctypes c_void_p**
- Pointer to an RMR message buffer
-
- :Returns:
-
- c_void_p:
- Pointer to rmr_mbuf structure
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_set_meid(ptr_mbuf: ricxappframe.rmr.rmr.LP_rmr_mbuf_t, byte_str: bytes) -> int
- :module: ricxappframe.rmr.rmr
-
-
- Sets the managed entity field in the message and returns the number of bytes copied.
- Refer to RMR C documentation for method::
-
- extern int rmr_bytes2meid(rmr_mbuf_t* mbuf, unsigned char const* src, int len);
-
- Caution: the meid length supported in an RMR message is 32 bytes, but C applications
- expect this to be a nil terminated string and thus only 31 bytes are actually available.
-
- Raises: exceptions.MeidSizeOutOfRang
-
- :Parameters:
-
- **ptr_mbuf: ctypes c_void_p**
- Pointer to an RMR message buffer
-
- **byte_tr: bytes**
- Managed entity ID value
-
- :Returns:
-
- int:
- number of bytes copied
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_get_meid(ptr_mbuf: ricxappframe.rmr.rmr.LP_rmr_mbuf_t) -> bytes
- :module: ricxappframe.rmr.rmr
-
-
- Gets the managed entity ID (meid) from the message header.
- This is a python-friendly version of RMR C method::
-
- extern unsigned char* rmr_get_meid(rmr_mbuf_t* mbuf, unsigned char* dest);
-
- :Parameters:
-
- **ptr_mbuf: ctypes c_void_p**
- Pointer to an RMR message buffer
-
- :Returns:
-
- bytes:
- Managed entity ID
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: rmr_get_src(ptr_mbuf: ricxappframe.rmr.rmr.LP_rmr_mbuf_t, dest: ctypes.c_char_p) -> ctypes.c_char_p
- :module: ricxappframe.rmr.rmr
-
-
- Copies the message-source information to the buffer.
- Refer to RMR C documentation for method::
-
- extern unsigned char* rmr_get_src(rmr_mbuf_t* mbuf, unsigned char* dest);
-
- :Parameters:
-
- **ptr_mbuf: ctypes POINTER(rmr_mbuf_t)**
- Pointer to an RMR message buffer
-
- **dest: ctypes c_char_p**
- Pointer to a buffer to receive the message source
-
- :Returns:
-
- string:
- message-source information
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: get_payload(ptr_mbuf: ctypes.c_void_p) -> bytes
- :module: ricxappframe.rmr.rmr
-
-
- Gets the binary payload from the rmr_buf_t*.
-
-
- :Parameters:
-
- **ptr_mbuf: ctypes c_void_p**
- Pointer to an rmr message buffer
-
- :Returns:
-
- bytes:
- the message payload
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: get_xaction(ptr_mbuf: ctypes.c_void_p) -> bytes
- :module: ricxappframe.rmr.rmr
-
-
- Gets the transaction ID from the rmr_buf_t*.
-
-
- :Parameters:
-
- **ptr_mbuf: ctypes c_void_p**
- Pointer to an rmr message buffer
-
- :Returns:
-
- bytes:
- the transaction id
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: message_summary(ptr_mbuf: ctypes.c_void_p) -> dict
- :module: ricxappframe.rmr.rmr
-
-
- Returns a dict with the fields of an RMR message.
-
-
- :Parameters:
-
- **ptr_mbuf: ctypes c_void_p**
- Pointer to an rmr message buffer
-
- :Returns:
-
- dict:
- dict message summary
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: set_payload_and_length(byte_str: bytes, ptr_mbuf: ctypes.c_void_p)
- :module: ricxappframe.rmr.rmr
-
-
- Sets an rmr payload and content length.
- In place method, no return.
-
-
- :Parameters:
-
- **byte_str: bytes**
- the bytes to set the payload to
-
- **ptr_mbuf: ctypes c_void_p**
- Pointer to an rmr message buffer
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: generate_and_set_transaction_id(ptr_mbuf: ctypes.c_void_p)
- :module: ricxappframe.rmr.rmr
-
-
- Generates a UUID and sets the RMR transaction id to it
-
-
- :Parameters:
-
- **ptr_mbuf: ctypes c_void_p**
- Pointer to an rmr message buffer
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: set_transaction_id(ptr_mbuf: ctypes.c_void_p, tid_bytes: bytes)
- :module: ricxappframe.rmr.rmr
-
-
- Sets an RMR transaction id
- TODO: on next API break, merge these two functions. Not done now to preserve API.
-
-
- :Parameters:
-
- **ptr_mbuf: ctypes c_void_p**
- Pointer to an rmr message buffer
-
- **tid_bytes: bytes**
- bytes of the desired transaction id
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
-.. py:function:: get_src(ptr_mbuf: ctypes.c_void_p) -> str
- :module: ricxappframe.rmr.rmr
-
-
- Gets the message source (likely host:port)
-
-
- :Parameters:
-
- **ptr_mbuf: ctypes c_void_p**
- Pointer to an rmr message buffer
-
- :Returns:
-
- string:
- message source
-
-
-
-
-
-
-
-
-
-
-
-
-
- ..
- !! processed by numpydoc !!
-
+.. automodule:: ricxappframe.rmr.helpers
+ :members:
diff --git a/ricxappframe/rmr/rmr.py b/ricxappframe/rmr/rmr.py
index 80617a7..e29a5de 100644
--- a/ricxappframe/rmr/rmr.py
+++ b/ricxappframe/rmr/rmr.py
@@ -15,107 +15,48 @@
# limitations under the License.
# ==================================================================================
import uuid
-import json
-from ctypes import CDLL, POINTER, RTLD_GLOBAL, Structure
-from ctypes import c_char, c_char_p, c_int, c_void_p, cast, create_string_buffer, memmove
+from ctypes import POINTER, Structure
+from ctypes import c_int, c_char, c_char_p, c_void_p, memmove, cast, create_string_buffer
from ricxappframe.rmr.exceptions import BadBufferAllocation, MeidSizeOutOfRange, InitFailed
+from ricxappframe.rmr.rmrclib.rmrclib import rmr_c_lib, get_constants, state_to_status
-# https://docs.python.org/3.7/library/ctypes.html
-# https://stackoverflow.com/questions/2327344/ctypes-loading-a-c-shared-library-that-has-dependencies/30845750#30845750
-# make sure you do a set -x LD_LIBRARY_PATH /usr/local/lib/;
-rmr_c_lib = CDLL("librmr_si.so", mode=RTLD_GLOBAL)
+##############
+# PRIVATE API
+##############
-# Internal Helpers (not a part of public api)
-
-
-_rmr_const = rmr_c_lib.rmr_get_consts
-_rmr_const.argtypes = []
-_rmr_const.restype = c_char_p
-
-
-def _get_constants(cache={}) -> dict:
+def _get_rmr_constant(key: str, default=None):
"""
- Gets constants published by RMR and caches for subsequent calls.
- TODO: are there constants that end user applications need?
+ Gets the constant with the named key from the RMR C library.
+ Returns None if the value is not a simple type. This happens
+ during sphinx autodoc document generation, which mocks the
+ rmrclib package to work without the RMR shared object file,
+ and the response is something like this:
+ <class 'ricxappframe.rmr.rmrclib.rmrclib.get_constants.get'>
"""
- if cache:
- return cache
-
- js = _rmr_const() # read json string
- cache = json.loads(str(js.decode())) # create constants value object as a hash
- return cache
-
-
-def _get_mapping_dict(cache={}) -> dict:
- """
- Builds a state mapping dict from constants and caches for subsequent calls.
- Relevant constants at this writing include:
-
- RMR_OK 0 state is good
- RMR_ERR_BADARG 1 argument passd to function was unusable
- RMR_ERR_NOENDPT 2 send/call could not find an endpoint based on msg type
- RMR_ERR_EMPTY 3 msg received had no payload; attempt to send an empty message
- RMR_ERR_NOHDR 4 message didn't contain a valid header
- RMR_ERR_SENDFAILED 5 send failed; errno has nano reason
- RMR_ERR_CALLFAILED 6 unable to send call() message
- RMR_ERR_NOWHOPEN 7 no wormholes are open
- RMR_ERR_WHID 8 wormhole id was invalid
- RMR_ERR_OVERFLOW 9 operation would have busted through a buffer/field size
- RMR_ERR_RETRY 10 request (send/call/rts) failed, but caller should retry (EAGAIN for wrappers)
- RMR_ERR_RCVFAILED 11 receive failed (hard error)
- RMR_ERR_TIMEOUT 12 message processing call timed out
- RMR_ERR_UNSET 13 the message hasn't been populated with a transport buffer
- RMR_ERR_TRUNC 14 received message likely truncated
- RMR_ERR_INITFAILED 15 initialization of something (probably message) failed
-
- """
- if cache:
- return cache
-
- rmr_consts = _get_constants()
- for key in rmr_consts: # build the state mapping dict
- if key[:7] in ["RMR_ERR", "RMR_OK"]:
- en = int(rmr_consts[key])
- cache[en] = key
-
- return cache
-
-
-def _state_to_status(stateno: int) -> str:
- """
- Converts a msg state integer to a status string.
- Returns "UNKNOWN STATE" if the int value is not known.
-
- """
- sdict = _get_mapping_dict()
- return sdict.get(stateno, "UNKNOWN STATE")
-
-
-_RCONST = _get_constants()
+ val = get_constants().get(key, default)
+ return val if isinstance(val, (type(None), bool, bytes, float, int, str)) else None
##############
# PUBLIC API
##############
-
-# These constants are directly usable by importers of this library
+# Publish constants from RMR C-language header files for use by importers of this library.
# TODO: Are there others that will be useful?
-
-#: Maximum size message to receive
-RMR_MAX_RCV_BYTES = _RCONST["RMR_MAX_RCV_BYTES"]
+#: Typical size message to receive; size is not limited
+RMR_MAX_RCV_BYTES = _get_rmr_constant('RMR_MAX_RCV_BYTES')
#: Multi-threaded initialization flag
-RMRFL_MTCALL = _RCONST.get("RMRFL_MTCALL", 0x02) # initialization flags
+RMRFL_MTCALL = _get_rmr_constant('RMRFL_MTCALL', 0x02) # initialization flags
#: Empty flag
-RMRFL_NONE = _RCONST.get("RMRFL_NONE", 0x0)
+RMRFL_NONE = _get_rmr_constant('RMRFL_NONE', 0x0)
#: State constant for OK
-RMR_OK = _RCONST["RMR_OK"]
+RMR_OK = _get_rmr_constant('RMR_OK', 0x00)
#: State constant for timeout
-RMR_ERR_TIMEOUT = _RCONST["RMR_ERR_TIMEOUT"]
+RMR_ERR_TIMEOUT = _get_rmr_constant('RMR_ERR_TIMEOUT')
#: State constant for retry
-RMR_ERR_RETRY = _RCONST["RMR_ERR_RETRY"]
+RMR_ERR_RETRY = _get_rmr_constant('RMR_ERR_RETRY')
class rmr_mbuf_t(Structure):
@@ -160,8 +101,6 @@
# argtypes and restype are important: https://stackoverflow.com/questions/24377845/ctype-why-specify-argtypes
-
-
_rmr_init = rmr_c_lib.rmr_init
_rmr_init.argtypes = [c_char_p, c_int, c_int]
_rmr_init.restype = c_void_p
@@ -607,7 +546,7 @@
int:
number of bytes copied
"""
- max = _get_constants().get("RMR_MAX_MEID", 32)
+ max = _get_rmr_constant("RMR_MAX_MEID", 32)
if len(byte_str) >= max:
raise MeidSizeOutOfRange
@@ -643,7 +582,7 @@
bytes:
Managed entity ID
"""
- sz = _get_constants().get("RMR_MAX_MEID", 32) # size for buffer to fill
+ sz = _get_rmr_constant("RMR_MAX_MEID", 32) # size for buffer to fill
buf = create_string_buffer(sz)
_rmr_get_meid(ptr_mbuf, buf)
return buf.value
@@ -715,7 +654,7 @@
the transaction id
"""
val = cast(ptr_mbuf.contents.xaction, c_char_p).value
- sz = _get_constants().get("RMR_MAX_XID", 0)
+ sz = _get_rmr_constant("RMR_MAX_XID", 0)
return val[:sz]
@@ -740,7 +679,7 @@
"subscription id": ptr_mbuf.contents.sub_id,
"transaction id": get_xaction(ptr_mbuf),
"message state": ptr_mbuf.contents.state,
- "message status": _state_to_status(ptr_mbuf.contents.state),
+ "message status": state_to_status(ptr_mbuf.contents.state),
"payload max size": rmr_payload_size(ptr_mbuf),
"meid": rmr_get_meid(ptr_mbuf),
"message source": get_src(ptr_mbuf),
@@ -794,7 +733,7 @@
tid_bytes: bytes
bytes of the desired transaction id
"""
- sz = _get_constants().get("RMR_MAX_XID", 0)
+ sz = _get_rmr_constant("RMR_MAX_XID", 0)
memmove(ptr_mbuf.contents.xaction, tid_bytes, sz)
@@ -812,7 +751,7 @@
string:
message source
"""
- sz = _get_constants().get("RMR_MAX_SRC", 64) # size to fill
+ sz = _get_rmr_constant("RMR_MAX_SRC", 64) # size to fill
buf = create_string_buffer(sz)
rmr_get_src(ptr_mbuf, buf)
return buf.value.decode()
diff --git a/ricxappframe/rmr/rmrclib/__init__.py b/ricxappframe/rmr/rmrclib/__init__.py
new file mode 100644
index 0000000..a1dc889
--- /dev/null
+++ b/ricxappframe/rmr/rmrclib/__init__.py
@@ -0,0 +1,16 @@
+# ==================================================================================
+# Copyright (c) 2019-2020 Nokia
+# Copyright (c) 2018-2020 AT&T Intellectual Property.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==================================================================================
diff --git a/ricxappframe/rmr/rmrclib/rmrclib.py b/ricxappframe/rmr/rmrclib/rmrclib.py
new file mode 100644
index 0000000..ad770ab
--- /dev/null
+++ b/ricxappframe/rmr/rmrclib/rmrclib.py
@@ -0,0 +1,88 @@
+# ==================================================================================
+# Copyright (c) 2019-2020 Nokia
+# Copyright (c) 2018-2020 AT&T Intellectual Property.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==================================================================================
+import ctypes
+import json
+
+# Subpackage that creates and publishes a reference to the C shared library.
+# Intended to be private; RMR-python and xapp-frame-py users should not need this.
+# Sphinx and autodoc mock this module to run without the .so file present.
+
+# https://docs.python.org/3.7/library/ctypes.html
+# https://stackoverflow.com/questions/2327344/ctypes-loading-a-c-shared-library-that-has-dependencies/30845750#30845750
+# make sure you do a set -x LD_LIBRARY_PATH /usr/local/lib/;
+rmr_c_lib = ctypes.CDLL("librmr_si.so", mode=ctypes.RTLD_GLOBAL)
+_rmr_get_consts = rmr_c_lib.rmr_get_consts
+_rmr_get_consts.argtypes = []
+_rmr_get_consts.restype = ctypes.c_char_p
+
+
+def get_constants(cache={}):
+ """
+ Gets constants published by RMR and caches for subsequent calls.
+ TODO: are there constants that end user applications need?
+ """
+ if cache:
+ return cache
+
+ js = _rmr_get_consts() # read json string
+ cache = json.loads(str(js.decode())) # create constants value object as a hash
+ return cache
+
+
+def get_mapping_dict(cache={}):
+ """
+ Builds a state mapping dict from constants and caches for subsequent calls.
+ Relevant constants at this writing include:
+
+ RMR_OK 0 state is good
+ RMR_ERR_BADARG 1 argument passd to function was unusable
+ RMR_ERR_NOENDPT 2 send/call could not find an endpoint based on msg type
+ RMR_ERR_EMPTY 3 msg received had no payload; attempt to send an empty message
+ RMR_ERR_NOHDR 4 message didn't contain a valid header
+ RMR_ERR_SENDFAILED 5 send failed; errno has nano reason
+ RMR_ERR_CALLFAILED 6 unable to send call() message
+ RMR_ERR_NOWHOPEN 7 no wormholes are open
+ RMR_ERR_WHID 8 wormhole id was invalid
+ RMR_ERR_OVERFLOW 9 operation would have busted through a buffer/field size
+ RMR_ERR_RETRY 10 request (send/call/rts) failed, but caller should retry (EAGAIN for wrappers)
+ RMR_ERR_RCVFAILED 11 receive failed (hard error)
+ RMR_ERR_TIMEOUT 12 message processing call timed out
+ RMR_ERR_UNSET 13 the message hasn't been populated with a transport buffer
+ RMR_ERR_TRUNC 14 received message likely truncated
+ RMR_ERR_INITFAILED 15 initialization of something (probably message) failed
+
+ """
+ if cache:
+ return cache
+
+ rmr_consts = get_constants()
+ for key in rmr_consts: # build the state mapping dict
+ if key[:7] in ["RMR_ERR", "RMR_OK"]:
+ en = int(rmr_consts[key])
+ cache[en] = key
+
+ return cache
+
+
+def state_to_status(stateno):
+ """
+ Converts a msg state integer to a status string.
+
+ Returns "UNKNOWN STATE" if the int value is not known.
+ """
+ sdict = get_mapping_dict()
+ return sdict.get(stateno, "UNKNOWN STATE")
diff --git a/tests/test_rmr.py b/tests/test_rmr.py
index a503968..afd9980 100644
--- a/tests/test_rmr.py
+++ b/tests/test_rmr.py
@@ -68,38 +68,6 @@
assert summary["errno"] == 0
-def test_get_constants(expected_constants):
- """
- test getting constants. We don't care what values are returned as those
- should be meaningful only to RMR. We do care that all of the constants
- which are defined in expected_contents are returned. Further, we don't
- consider it to be an error if the returned list has more constants than
- what are in our list.
-
- To avoid frustration, this should list all missing keys, not fail on the
- first missing key.
- """
- errors = 0
- econst = expected_constants
- rconst = rmr._get_constants()
- for key in econst: # test all expected constants
- if key not in rconst: # expected value not listed by rmr
- errors += 1
- print("did not find required constant in list from RMR: %s" % key)
-
- assert errors == 0
-
-
-def test_get_mapping_dict(expected_states):
- """
- test getting mapping string
- """
- assert rmr._get_mapping_dict() == expected_states
- assert rmr._state_to_status(0) == "RMR_OK"
- assert rmr._state_to_status(12) == "RMR_ERR_TIMEOUT"
- assert rmr._state_to_status(666) == "UNKNOWN STATE"
-
-
def test_meid():
"""
test meid stringification
diff --git a/tests/test_rmrclib.py b/tests/test_rmrclib.py
new file mode 100644
index 0000000..eba4436
--- /dev/null
+++ b/tests/test_rmrclib.py
@@ -0,0 +1,49 @@
+# =================================================================================2
+# Copyright (c) 2019-2020 Nokia
+# Copyright (c) 2018-2020 AT&T Intellectual Property.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ==================================================================================
+from ricxappframe.rmr.rmrclib import rmrclib
+
+
+def test_get_constants(expected_constants):
+ """
+ test getting constants. We don't care what values are returned as those
+ should be meaningful only to RMR. We do care that all of the constants
+ which are defined in expected_contents are returned. Further, we don't
+ consider it to be an error if the returned list has more constants than
+ what are in our list.
+
+ To avoid frustration, this should list all missing keys, not fail on the
+ first missing key.
+ """
+ errors = 0
+ econst = expected_constants
+ rconst = rmrclib.get_constants()
+ for key in econst: # test all expected constants
+ if key not in rconst: # expected value not listed by rmr
+ errors += 1
+ print("did not find required constant in list from RMR: %s" % key)
+
+ assert errors == 0
+
+
+def test_get_mapping_dict(expected_states):
+ """
+ test getting mapping string
+ """
+ assert rmrclib.get_mapping_dict() == expected_states
+ assert rmrclib.state_to_status(0) == "RMR_OK"
+ assert rmrclib.state_to_status(12) == "RMR_ERR_TIMEOUT"
+ assert rmrclib.state_to_status(666) == "UNKNOWN STATE"