Refactor definition of method restype & argtypes 48/3248/5
authorLott, Christopher (cl778h) <cl778h@att.com>
Fri, 10 Apr 2020 20:02:11 +0000 (16:02 -0400)
committerLott, Christopher (cl778h) <cl778h@att.com>
Mon, 13 Apr 2020 14:29:22 +0000 (10:29 -0400)
Use a helper method to set Ctype argument and response types.
Bump version to 1.0.1
Add release note for 1.0.1, generate API docs with autodoc.
Revise release notes to drop code-style formatting of English text.
Add link to RMR man pages at ReadTheDocs.io

Issue-ID: RIC-228
Signed-off-by: Lott, Christopher (cl778h) <cl778h@att.com>
Change-Id: I989566c93f25fa5555b306c569a4bf1d817be2fc

docs/conf.py
docs/release-notes.rst
docs/rmr_api.rst
ricxappframe/rmr/rmr.py
setup.py

index 47e5eb7..d695a9a 100755 (executable)
@@ -5,7 +5,7 @@ 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"]
+extensions = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx.ext.viewcode", "numpydoc"]
 
 # don't alphabetically order
 autodoc_member_order = "bysource"
@@ -21,3 +21,7 @@ nitpick_ignore = [
 
 # RMR c library is not available in ReadTheDocs
 autodoc_mock_imports = ['ricxappframe.rmr.rmrclib']
+
+# Supports links to RMR man pages
+branch = 'latest'
+intersphinx_mapping['ric-plt-lib-rmr'] = ('https://docs.o-ran-sc.org/projects/o-ran-sc-ric-plt-lib-rmr/en/%s' % branch, None)
index bced3fb..e3767bc 100644 (file)
@@ -3,7 +3,7 @@
 .. Copyright (C) 2020 AT&T Intellectual Property
 
 Release Notes
-===============
+=============
 
 All notable changes to this project will be documented in this file.
 
@@ -14,77 +14,107 @@ and this project adheres to `Semantic Versioning <http://semver.org/>`__.
    :depth: 3
    :local:
 
+[1.0.1] - 2020-04-10
+--------------------
+
+* Publish API documentation using Sphinx autodoc, which required
+  changes so Sphinx can run when the RMR .so file is not available,
+  such as during a ReadTheDocs build.
+* Create new subpackage rmr/rmrclib with the C library loaded via
+  ctypes.
+* Extend sphinx configuration to mock the new rmrclib subpackage
+* Add method to get constants from RMR library and detect mock
+  objects to work around a bug in Sphinx 3.0.0.
+* Split test files into test_rmr and test_rmrclib.
+* Add function to define argtype and restype values for library functions
+* Configure intersphinx link for RMR man pages at ReadTheDocs.io
+
+
 [1.0.0] - 4/6/2020
--------------------
-::
+------------------
 
-    * Python rmr has been moved into this repo. The module name has NOT changed in order to make the transition for repos very easy. The only transition needed should be prefixing rmr with ricxappframe in import statements, and to include this rather than rmr in setup.
+* Python rmr has been moved into this repo. The module name has NOT
+  changed in order to make the transition for repos very easy. The
+  only transition needed should be prefixing rmr with ricxappframe in
+  import statements, and to include this rather than rmr in setup.
 
 
 [0.7.0] - 4/2/2020
--------------------
-::
+------------------
 
-    * RMRXapps by default now implement the rmr healthcheck probe; users can also override it with a more complex handler if they wish
-    * Fix a bug in the unit tests where a payload mismatch wouldn't actually fail the test (would now)
+* RMRXapps by default now implement the rmr healthcheck probe;
+  users can also override it with a more complex handler if they
+  wish
+* Fix a bug in the unit tests where a payload mismatch wouldn't
+  actually fail the test (would now)
 
 
 [0.6.0] - 3/23/2020
 -------------------
-::
 
-    * Switch to SI95 for rmr
+* Switch to SI95 for rmr
 
 
 
 [0.5.0] - 3/18/2020
 -------------------
-::
 
-    * All xapps (via the base class) now have a logger attribute that can be invoked to provide mdc logging. It is a passthrough to the RIC mdc logger for python (untouched, no value in an API on top at the current time).
+* All xapps (via the base class) now have a logger attribute that can
+  be invoked to provide mdc logging. It is a passthrough to the RIC
+  mdc logger for python (untouched, no value in an API on top at the
+  current time).
 
 
 [0.4.1] - 3/17/2020
 -------------------
-::
 
-    * Switch tox to use py38
-    * switch to latest builders
+* Switch tox to use py38
+* switch to latest builders
 
 
 [0.4.0] - 3/13/2020
 -------------------
-::
 
-    * minor breaking change; switches the default behavior RE threading for RMRXapps. The default is not to return execution, but the caller (in `run`) can choose to loop in a thread.
-    * Add Dockerized examples
+* Minor breaking change; switches the default behavior RE
+  threading for RMRXapps. The default is not to return execution,
+  but the caller (in `run`) can choose to loop in a thread.
+* Add Dockerized examples
 
 
 [0.3.0] - 3/10/2020
 -------------------
-::
 
-    * Large change to the "feel" of this framework: rather than subclass instantiation, xapps now use initialization and registration functions to register handlers
-    * rmr xapps can now register handlers for specific message types (and they must prodive a default callback); if the user does this then "message to function routing" is now handled by the framework itself
-    * RMRXapp now runs the polling loop in a thread, and returns execution back to the caller. The user is then free to loop, or do nothing, and call stop() when they want.
-    * Raises tox coverage minimum to 70 from 50 (currently at 86)
+* Large change to the "feel" of this framework: rather than subclass
+  instantiation, xapps now use initialization and registration
+  functions to register handlers
+* rmr xapps can now register handlers for specific message types (and
+  they must prodive a default callback); if the user does this then
+  "message to function routing" is now handled by the framework itself
+* RMRXapp now runs the polling loop in a thread, and returns execution
+  back to the caller. The user is then free to loop, or do nothing,
+  and call stop() when they want.
+* Raises tox coverage minimum to 70 from 50 (currently at 86)
 
 [0.2.0] - 3/3/2020
--------------------
-::
-
-    * now allows for RMRXapps to call code before entering the infinite loop
-    * stop is now called before throwing NotImplemented in the case where the client fails to provide a must have callback; this ensures there is no dangling rmr thread
-    * stop now calls rmr_close to correctly free up any port(s)
-    * (breaking) renames `loop` to `entrypoint` since the function does not have to contain a loop (though it most likely does)
-    * Changes wording around the two types of xapps (docs only)
-    * Uses a new version of rmr python that crashes when the rmr mrc fails to init, which prevents an xapp trying to use an unusable rmr
-    * more unit test code coverage
-    * Adds more fields to setup like long_desc and classifiers so the pypi page looks nicer
-    * Removes a bad release file (will be added back in subseq. commit)
+------------------
+
+* now allows for RMRXapps to call code before entering the infinite
+  loop
+* stop is now called before throwing NotImplemented in the case where
+  the client fails to provide a must have callback; this ensures there
+  is no dangling rmr thread
+* stop now calls rmr_close to correctly free up any port(s)
+* (breaking) renames `loop` to `entrypoint` since the function does
+  not have to contain a loop (though it most likely does)
+* Changes wording around the two types of xapps (docs only)
+* Uses a new version of rmr python that crashes when the rmr mrc fails
+  to init, which prevents an xapp trying to use an unusable rmr
+* more unit test code coverage
+* Adds more fields to setup like long_desc and classifiers so the pypi
+  page looks nicer
+* Removes a bad release file (will be added back in subseq. commit)
 
 [0.1.0] - 2/27/2020
 -------------------
-::
 
-    * Initial commit
+* Initial commit
index a33d6a1..a7f0362 100644 (file)
@@ -4,13 +4,17 @@ RMR Python Bindings
 Overview
 --------
 
-The xapp python framework repository includes a python submodule
-called `rmr`.  This package (`ricxappframe.rmr`) is a CTYPES wrapper
+The xapp python framework package includes a python subpackage called
+`rmr`.  This subpackage (`ricxappframe.rmr`) is a CTYPES wrapper
 around the RMR shared library.  Most Xapp users will never use this
-package natively; however python apps that need access to the low
-level RMR API can use this package.  Usage of this python package
-requires that you have the RMR shared-object library installed.
+subpackage natively; however python apps that need access to the
+low-level RMR API can use it.
 
+Usage of this python package requires that the RMR shared-object
+library is installed in a system library that is included in the
+directories found by default, usually something like /usr/local/lib.
+
+The RMR library man pages are available here: :doc:`RMR Man Pages <ric-plt-lib-rmr:index>`
 
 RMR API
 -------
index e29a5de..59885ed 100644 (file)
@@ -34,11 +34,38 @@ def _get_rmr_constant(key: str, default=None):
     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'>
+    Workaround for https://github.com/sphinx-doc/sphinx/issues/7422
     """
     val = get_constants().get(key, default)
     return val if isinstance(val, (type(None), bool, bytes, float, int, str)) else None
 
 
+# argtypes and restype are important:
+# https://stackoverflow.com/questions/24377845/ctype-why-specify-argtypes
+def _wrap_rmr_function(funcname, restype, argtypes):
+    """
+    Simplify wrapping ctypes functions.
+
+    Parameters
+    ----------
+    funcname: str
+        Name of library method
+    restype: class
+        Name of ctypes class; e.g., c_char_p
+    argtypes: list
+        List of ctypes classes; e.g., [ c_char_p, int ]
+
+    Returns
+    -------
+    _FuncPointer:
+        Pointer to C library function
+"""
+    func = rmr_c_lib.__getattr__(funcname)
+    func.restype = restype
+    func.argtypes = argtypes
+    return func
+
+
 ##############
 # PUBLIC API
 ##############
@@ -100,10 +127,7 @@ class rmr_mbuf_t(Structure):
     ]
 
 
-# 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
+_rmr_init = _wrap_rmr_function('rmr_init', c_void_p, [c_char_p, c_int, c_int])
 
 
 def rmr_init(uproto_port: c_char_p, max_msg_size: int, flags: int) -> c_void_p:
@@ -135,9 +159,7 @@ def rmr_init(uproto_port: c_char_p, max_msg_size: int, flags: int) -> c_void_p:
     return mrc
 
 
-_rmr_ready = rmr_c_lib.rmr_ready
-_rmr_ready.argtypes = [c_void_p]
-_rmr_ready.restype = c_int
+_rmr_ready = _wrap_rmr_function('rmr_ready', c_int, [c_void_p])
 
 
 def rmr_ready(vctx: c_void_p) -> int:
@@ -159,8 +181,7 @@ def rmr_ready(vctx: c_void_p) -> int:
     return _rmr_ready(vctx)
 
 
-_rmr_close = rmr_c_lib.rmr_close
-_rmr_close.argtypes = [c_void_p]
+_rmr_close = _wrap_rmr_function('rmr_close', None, [c_void_p])
 
 
 def rmr_close(vctx: c_void_p):
@@ -182,9 +203,7 @@ def rmr_close(vctx: c_void_p):
     _rmr_close(vctx)
 
 
-_rmr_set_stimeout = rmr_c_lib.rmr_set_stimeout
-_rmr_set_stimeout.argtypes = [c_void_p, c_int]
-_rmr_set_stimeout.restype = c_int
+_rmr_set_stimeout = _wrap_rmr_function('rmr_set_stimeout', c_int, [c_void_p, c_int])
 
 
 def rmr_set_stimeout(vctx: c_void_p, rloops: int) -> int:
@@ -208,9 +227,7 @@ def rmr_set_stimeout(vctx: c_void_p, rloops: int) -> int:
     return _rmr_set_stimeout(vctx, rloops)
 
 
-_rmr_alloc_msg = rmr_c_lib.rmr_alloc_msg
-_rmr_alloc_msg.argtypes = [c_void_p, c_int]
-_rmr_alloc_msg.restype = POINTER(rmr_mbuf_t)
+_rmr_alloc_msg = _wrap_rmr_function('rmr_alloc_msg', POINTER(rmr_mbuf_t), [c_void_p, c_int])
 
 
 def rmr_alloc_msg(vctx: c_void_p, size: int,
@@ -281,9 +298,7 @@ def rmr_alloc_msg(vctx: c_void_p, size: int,
         raise BadBufferAllocation
 
 
-_rmr_realloc_payload = rmr_c_lib.rmr_realloc_payload
-_rmr_realloc_payload.argtypes = [POINTER(rmr_mbuf_t), c_int, c_int, c_int]  # new_len, copy, clone
-_rmr_realloc_payload.restype = POINTER(rmr_mbuf_t)
+_rmr_realloc_payload = _wrap_rmr_function('rmr_realloc_payload', POINTER(rmr_mbuf_t), [POINTER(rmr_mbuf_t), c_int, c_int, c_int])  # new_len, copy, clone
 
 
 def rmr_realloc_payload(ptr_mbuf: c_void_p, new_len: int, copy=False, clone=False):
@@ -312,9 +327,7 @@ def rmr_realloc_payload(ptr_mbuf: c_void_p, new_len: int, copy=False, clone=Fals
     return _rmr_realloc_payload(ptr_mbuf, new_len, copy, clone)
 
 
-_rmr_free_msg = rmr_c_lib.rmr_free_msg
-_rmr_free_msg.argtypes = [POINTER(rmr_mbuf_t)]
-_rmr_free_msg.restype = None
+_rmr_free_msg = _wrap_rmr_function('rmr_free_msg', None, [POINTER(rmr_mbuf_t)])
 
 
 def rmr_free_msg(ptr_mbuf: c_void_p):
@@ -337,9 +350,7 @@ def rmr_free_msg(ptr_mbuf: c_void_p):
         _rmr_free_msg(ptr_mbuf)
 
 
-_rmr_payload_size = rmr_c_lib.rmr_payload_size
-_rmr_payload_size.argtypes = [POINTER(rmr_mbuf_t)]
-_rmr_payload_size.restype = c_int
+_rmr_payload_size = _wrap_rmr_function('rmr_payload_size', c_int, [POINTER(rmr_mbuf_t)])
 
 
 def rmr_payload_size(ptr_mbuf: c_void_p) -> int:
@@ -366,9 +377,7 @@ def rmr_payload_size(ptr_mbuf: c_void_p) -> int:
 The following functions all seem to have the same interface
 """
 
-_rmr_send_msg = rmr_c_lib.rmr_send_msg
-_rmr_send_msg.argtypes = [c_void_p, POINTER(rmr_mbuf_t)]
-_rmr_send_msg.restype = POINTER(rmr_mbuf_t)
+_rmr_send_msg = _wrap_rmr_function('rmr_send_msg', POINTER(rmr_mbuf_t), [c_void_p, POINTER(rmr_mbuf_t)])
 
 
 def rmr_send_msg(vctx: c_void_p, ptr_mbuf: POINTER(rmr_mbuf_t)) -> POINTER(rmr_mbuf_t):
@@ -394,9 +403,7 @@ def rmr_send_msg(vctx: c_void_p, ptr_mbuf: POINTER(rmr_mbuf_t)) -> POINTER(rmr_m
 
 
 # TODO: the old message (Send param) is actually optional, but I don't know how to specify that in Ctypes.
-_rmr_rcv_msg = rmr_c_lib.rmr_rcv_msg
-_rmr_rcv_msg.argtypes = [c_void_p, POINTER(rmr_mbuf_t)]
-_rmr_rcv_msg.restype = POINTER(rmr_mbuf_t)
+_rmr_rcv_msg = _wrap_rmr_function('rmr_rcv_msg', POINTER(rmr_mbuf_t), [c_void_p, POINTER(rmr_mbuf_t)])
 
 
 def rmr_rcv_msg(vctx: c_void_p, ptr_mbuf: POINTER(rmr_mbuf_t)) -> POINTER(rmr_mbuf_t):
@@ -421,9 +428,7 @@ def rmr_rcv_msg(vctx: c_void_p, ptr_mbuf: POINTER(rmr_mbuf_t)) -> POINTER(rmr_mb
     return _rmr_rcv_msg(vctx, ptr_mbuf)
 
 
-_rmr_torcv_msg = rmr_c_lib.rmr_torcv_msg
-_rmr_torcv_msg.argtypes = [c_void_p, POINTER(rmr_mbuf_t), c_int]
-_rmr_torcv_msg.restype = POINTER(rmr_mbuf_t)
+_rmr_torcv_msg = _wrap_rmr_function('rmr_torcv_msg', POINTER(rmr_mbuf_t), [c_void_p, POINTER(rmr_mbuf_t), c_int])
 
 
 def rmr_torcv_msg(vctx: c_void_p, ptr_mbuf: POINTER(rmr_mbuf_t), ms_to: int) -> POINTER(rmr_mbuf_t):
@@ -450,9 +455,7 @@ def rmr_torcv_msg(vctx: c_void_p, ptr_mbuf: POINTER(rmr_mbuf_t), ms_to: int) ->
     return _rmr_torcv_msg(vctx, ptr_mbuf, ms_to)
 
 
-_rmr_rts_msg = rmr_c_lib.rmr_rts_msg
-_rmr_rts_msg.argtypes = [c_void_p, POINTER(rmr_mbuf_t)]
-_rmr_rts_msg.restype = POINTER(rmr_mbuf_t)
+_rmr_rts_msg = _wrap_rmr_function('rmr_rts_msg', POINTER(rmr_mbuf_t), [c_void_p, POINTER(rmr_mbuf_t)])
 
 
 def rmr_rts_msg(vctx: c_void_p, ptr_mbuf: POINTER(rmr_mbuf_t), payload=None, mtype=None) -> POINTER(rmr_mbuf_t):
@@ -492,9 +495,7 @@ def rmr_rts_msg(vctx: c_void_p, ptr_mbuf: POINTER(rmr_mbuf_t), payload=None, mty
     return _rmr_rts_msg(vctx, ptr_mbuf)
 
 
-_rmr_call = rmr_c_lib.rmr_call
-_rmr_call.argtypes = [c_void_p, POINTER(rmr_mbuf_t)]
-_rmr_call.restype = POINTER(rmr_mbuf_t)
+_rmr_call = _wrap_rmr_function('rmr_call', POINTER(rmr_mbuf_t), [c_void_p, POINTER(rmr_mbuf_t)])
 
 
 def rmr_call(vctx: c_void_p, ptr_mbuf: POINTER(rmr_mbuf_t)) -> POINTER(rmr_mbuf_t):
@@ -517,9 +518,7 @@ def rmr_call(vctx: c_void_p, ptr_mbuf: POINTER(rmr_mbuf_t)) -> POINTER(rmr_mbuf_
     return _rmr_call(vctx, ptr_mbuf)
 
 
-_rmr_bytes2meid = rmr_c_lib.rmr_bytes2meid
-_rmr_bytes2meid.argtypes = [POINTER(rmr_mbuf_t), c_char_p, c_int]
-_rmr_bytes2meid.restype = c_int
+_rmr_bytes2meid = _wrap_rmr_function('rmr_bytes2meid', c_int, [POINTER(rmr_mbuf_t), c_char_p, c_int])
 
 
 def rmr_set_meid(ptr_mbuf: POINTER(rmr_mbuf_t), byte_str: bytes) -> int:
@@ -560,9 +559,7 @@ def rmr_set_meid(ptr_mbuf: POINTER(rmr_mbuf_t), byte_str: bytes) -> int:
 # extern unsigned char*  rmr_get_meid(rmr_mbuf_t* mbuf, unsigned char* dest);
 # we don't provide direct access to this function (unless it is asked for) because it is not really useful to provide your own buffer.
 # Rather, rmr_get_meid does this for you, and just returns the string.
-_rmr_get_meid = rmr_c_lib.rmr_get_meid
-_rmr_get_meid.argtypes = [POINTER(rmr_mbuf_t), c_char_p]
-_rmr_get_meid.restype = c_char_p
+_rmr_get_meid = _wrap_rmr_function('rmr_get_meid', c_char_p, [POINTER(rmr_mbuf_t), c_char_p])
 
 
 def rmr_get_meid(ptr_mbuf: POINTER(rmr_mbuf_t)) -> bytes:
@@ -588,9 +585,7 @@ def rmr_get_meid(ptr_mbuf: POINTER(rmr_mbuf_t)) -> bytes:
     return buf.value
 
 
-_rmr_get_src = rmr_c_lib.rmr_get_src
-_rmr_get_src.argtypes = [POINTER(rmr_mbuf_t), c_char_p]
-_rmr_get_src.restype = c_char_p
+_rmr_get_src = _wrap_rmr_function('rmr_get_src', c_char_p, [POINTER(rmr_mbuf_t), c_char_p])
 
 
 def rmr_get_src(ptr_mbuf: POINTER(rmr_mbuf_t), dest: c_char_p) -> c_char_p:
index c7b2712..68bde4b 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -32,10 +32,10 @@ def _long_descr():
 
 setup(
     name="ricxappframe",
-    version="1.0.0",
+    version="1.0.1",
     packages=find_packages(exclude=["tests.*", "tests"]),
     author="Tommy Carpenter, E. Scott Daniels",
-    description="Xapp and rmr framework for python",
+    description="Xapp and RMR framework for python",
     url="https://gerrit.o-ran-sc.org/r/admin/repos/ric-plt/xapp-frame-py",
     install_requires=["msgpack", "mdclogpy", "ricsdl>=2.0.3,<3.0.0"],
     classifiers=[