From dcf81c37442746f1285c453d8b75afce002d9eff Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 4 Nov 2018 16:25:24 -0800 Subject: [PATCH] update doc --- devsupApp/src/Makefile | 1 - devsupApp/src/dbfield.c | 17 +-- devsupApp/src/dbrec.c | 19 ++- devsupApp/src/devsup/_nullapi.py | 205 ------------------------------- devsupApp/src/devsup/db.py | 70 +++++++++++ devsupApp/src/devsup/hooks.py | 2 +- documentation/environment.rst | 2 +- 7 files changed, 96 insertions(+), 220 deletions(-) delete mode 100644 devsupApp/src/devsup/_nullapi.py diff --git a/devsupApp/src/Makefile b/devsupApp/src/Makefile index 6e324b1..2d01d17 100644 --- a/devsupApp/src/Makefile +++ b/devsupApp/src/Makefile @@ -31,7 +31,6 @@ _dbapi_SRCS += pyDevSupCommon_registerRecordDeviceDriver.cpp _dbapi_LIBS += $(EPICS_BASE_IOC_LIBS) PY += devsup/__init__.py -PY += devsup/_nullapi.py PY += devsup/db.py PY += devsup/dset.py PY += devsup/hooks.py diff --git a/devsupApp/src/dbfield.c b/devsupApp/src/dbfield.c index ed48687..42f7fd9 100644 --- a/devsupApp/src/dbfield.c +++ b/devsupApp/src/dbfield.c @@ -445,25 +445,28 @@ static PyObject *pyField_len(pyField *self) static PyMethodDef pyField_methods[] = { {"name", (PyCFunction)pyField_name, METH_NOARGS, - "Return Names (\"record\",\"field\")"}, + "name() -> (recname, fldname)\n"}, {"fieldinfo", (PyCFunction)pyField_fldinfo, METH_NOARGS, - "Field type info\nReturn (type, size, #elements"}, + "fieldinfo() -> (dbf, elem_size, elem_count"}, {"getval", (PyCFunction)pyField_getval, METH_NOARGS, - "Returns scalar version of field value"}, + "getval() -> object\n"}, {"putval", (PyCFunction)pyField_putval, METH_VARARGS, - "Sets field value from a scalar"}, + "putval(object)\n"}, {"getarray", (PyCFunction)pyField_getarray, METH_NOARGS, + "getarray() -> numpy.ndarray\n" "Return a numpy ndarray refering to this field for in-place operations."}, {"getarraylen", (PyCFunction)pyField_getlen, METH_NOARGS, + "getarraylen() -> int\n" "Return current number of valid elements for array fields."}, {"putarraylen", (PyCFunction)pyField_setlen, METH_VARARGS, + "putarraylen(int)\n" "Set number of valid elements for array fields."}, {"getTime", (PyCFunction)pyField_getTime, METH_NOARGS, - "Return link target timestamp as a tuple (sec, nsec)."}, + "getTime() -> (sec, nsec)."}, {"getAlarm", (PyCFunction)pyField_getAlarm, METH_NOARGS, - "Return link target alarm condtions as a tuple (severity, status)."}, + "getAlarm() -> (severity, status)."}, {"__len__", (PyCFunction)pyField_len, METH_NOARGS, - "Maximum number of elements storable in this field"}, + "Maximum number of elements storable in this field."}, {NULL, NULL, 0, NULL} }; diff --git a/devsupApp/src/dbrec.c b/devsupApp/src/dbrec.c index aa8a932..e85b5e4 100644 --- a/devsupApp/src/dbrec.c +++ b/devsupApp/src/dbrec.c @@ -293,23 +293,32 @@ static PyObject *pyRecord_exit(pyRecord *self, PyObject *args) static PyMethodDef pyRecord_methods[] = { {"name", (PyCFunction)pyRecord_name, METH_NOARGS, - "Return record name string"}, + "name() -> str\n\n" + "Record name. ::\n" + "\n" + " R = getRecord(\"my:record:name\")\n" + " assert R.name()==\"my:record:name\"\n"}, {"rtype", (PyCFunction)pyRecord_rtype, METH_NOARGS, + "rtype() -> str\n" "Return record type name string"}, {"isPyRecord", (PyCFunction)pyRecord_ispyrec, METH_NOARGS, + "isPyRecord() -> bool\n" "Is this record using Python Device."}, {"info", (PyCFunction)pyRecord_info, METH_VARARGS, - "Lookup info name\ninfo(name, def=None)"}, + "info(key [,default]) -> str\n" + "Lookup info by name\n" + ":rtype: str\n" + ":throws: KeyError\n"}, {"infos", (PyCFunction)pyRecord_infos, METH_NOARGS, + "infos() -> {'name':'value'}\n" "Return a dictionary of all infos for this record."}, {"setSevr", (PyCFunction)pyRecord_setSevr, METH_VARARGS|METH_KEYWORDS, + "setSevr(sevr=INVALID_ALARM, stat=COMM_ALARM)\n" "Set alarm new alarm severity/status. Record must be locked!"}, {"setTime", (PyCFunction)pyRecord_setTime, METH_VARARGS, "Set record timestamp if TSE==-2. Record must be locked!"}, {"scan", (PyCFunction)pyRecord_scan, METH_VARARGS|METH_KEYWORDS, - "scan(sync=False)\nScan this record. If sync is False then" - "a scan request is queued. If sync is True then the record" - "is scannined immidately on the current thread."}, + "scan(sync=False, reason=None, force=0)\n"}, {"asyncStart", (PyCFunction)pyRecord_asyncStart, METH_NOARGS, "Begin an asynchronous action. Record must be locked!"}, {"asyncFinish", (PyCFunction)pyRecord_asyncFinish, METH_VARARGS|METH_KEYWORDS, diff --git a/devsupApp/src/devsup/_nullapi.py b/devsupApp/src/devsup/_nullapi.py deleted file mode 100644 index 9d5fda6..0000000 --- a/devsupApp/src/devsup/_nullapi.py +++ /dev/null @@ -1,205 +0,0 @@ - -class _Record(object): - """Handle for record operations - - r = _Record("rec:name") - """ - - def __init__(self, rec): - pass - def name(self): - """Record name string. - - >>> R = getRecord("my:record:name") - >>> R.name() - "my:record:name" - """ - def rtype(self): - """Record type name string. - - >>> R = getRecord("my:record:name") - >>> R.type() - "longin" - """ - def isPyRecord(self): - """Is this record using Python device support. - - :rtype: bool - """ - def info(self, key): - """info(key [,default]) - - :rtype: str - :throws: KeyError - - Lookup record info tag. If no default - is provided then an exception is raised - if the info key does not exist. - """ - def infos(self): - """Return a dictionary of all info tags - for this record - """ - - def setSevr(self, sevr=3, stat=15): - """setSevr(sevr=INVALID_ALARM, stat=COMM_ALARM) - - Signal a new alarm condition. The effect of this - call depends on the current alarm condition. - - See :c:func:`recGblSetSevr` in EPICS Base. - """ - - def scan(self, sync=False, reason=None, force=0): - """Scan this record. - - :param sync: scan in current thread (``True``), or queue to a worker (``False``). - :param reason: Reason object passed to :meth:`process ` (sync=True only) - :param force: Record processing condtion (0=Passive, 1=Force, 2=I/O Intr) - :throws: ``RuntimeError`` when ``sync=True``, but ``force`` prevents scanning. - - If ``sync`` is False then a scan request is queued to run in another thread.. - If ``sync`` is True then the record is scanned immediately on the current thread. - - For ``reason`` argument must be used in conjunction with ``sync=True`` - on records with Python device support. This provides a means - of providing extra contextual information to the record's - :meth:`process ` method. - - ``force`` is used to decide if the record will actually be processed, - ``force=0`` will only process records with SCAN=Passive. - ``force=1`` will process any record if at all possible. - ``force=2`` will only process records with Python device support and - SCAN=I/O Intr. - - .. important:: - It is **never** safe to use ``sync=True`` while holding record locks, - including from within a *process* method. - """ - - def asyncStart(self): - """Start asynchronous processing - - This method may be called from a device support - :meth:`process ` method - to indicate that processing will continue - later. - - .. important:: - This method is **only** safe to call within a *process* method. - """ - def asyncFinish(self, reason=None): - """Indicate that asynchronous processing can complete - - Similar to :meth:`scan`. Used to conclude asynchronous - process started with :meth:`asyncStart`. - - Processing is completed on the current thread. - - .. important:: - This method should **never** be called within - a :meth:`process ` method, - or any other context where a Record lock is held. - Doing so will result in a deadlock. - - Typically a *reason* will be passed to *process* as a way - of indicating that this is the completion of an async action. :: - - AsyncDone = object() - class MySup(object): - def process(record, reason): - if reason is AsyncDone: - record.VAL = ... # store result - else: - threading.Timer(1.0, record.asyncFinish, kwargs={'reason':AsyncDone}) - record.asyncStart() - """ - -class _Field(object): - """Handle for field operations - - f = Field("rec:name.HOPR") - - Field objects implement the buffer protocol. - """ - def __init__(self, fld): - pass - def name(self): - """Fetch the record and field names. - - >>> FLD = getRecord("rec").field("FLD") - >>> FLD.name() - ("rec", "FLD") - """ - def fieldinfo(self): - """(type, size, #elements) = fieldinfo() - - Type is DBF type code - size is number of bytes to start a single element - #elements is the maximum number of elements the field can hold - """ - - def getval(self): - """Fetch the current field value as a scalar or numpy.ndarray. - - :rtype: int, float, str, or ndarray - - Returned type depends of field DBF type. - An ``int`` is returned for CHAR, SHORT, LONG, and ENUM. - A ``float`` is returned for FLOAT and DOUBLE. - A ``str`` is returned for STRING. - A ``numpy.ndarray`` is returned for array fields. - This array is read-only and has the size of the present valid values. - - .. important:: - It is only safe to read this ndarray while the record - lock is held (ie within :meth:`process `). - """ - - def putval(self, val): - """Update the field value - - Must be an Int, Float, str, or numpy.ndarray. - Strings will be truncated to 39 characters. - Arrays must have a size less than or equal to the max element count. - Arrays are converted as necessary to the field's native type. - """ - - def getarray(self): - """Return a numpy ndarray refering to this field for in-place operations. - - The dtype of the ndarray will correspond to the field's DBF type. - Its size will be the **maximum** number of elements. - - .. important:: - It is only safe to read or write to this ndarray while the record - lock is held (ie within :meth:`process `). - """ - - def getarraylen(self): - """Return the number of active elements for the field. - - >>> F = Field(...) - >>> assert len(F)>=F.getarraylen() - """ - - def putarraylen(self, len): - """Set the number of active elements in field's array. - - Requires that the underlying field be an array. - Must be greater than one and less than or equal to the maximum length of the field. - """ - - def getAlarm(self): - """Returns a tuple (severity, status) with the condition of the linked field. - - Only works for fields of type DBF_INLINK. - """ - - def __len__(self): - """Returns the maximum number of elements which may be stored in the field. - - This is always 1 for scalar fields. - """ - -_hooks = {} diff --git a/devsupApp/src/devsup/db.py b/devsupApp/src/devsup/db.py index 8b29498..996df5b 100644 --- a/devsupApp/src/devsup/db.py +++ b/devsupApp/src/devsup/db.py @@ -260,6 +260,76 @@ class Record(_dbapi._Record): super(Record, self).setTime(sec, nsec) + def scan(self, *args, **kws): + """scan(sync=False, reason=None, force=0) + Scan this record. + + :param sync: scan in current thread (``True``), or queue to a worker (``False``). + :param reason: Reason object passed to :meth:`process ` (sync=True only) + :param force: Record processing condtion (0=Passive, 1=Force, 2=I/O Intr) + :throws: ``RuntimeError`` when ``sync=True``, but ``force`` prevents scanning. + + If ``sync`` is False then a scan request is queued to run in another thread.. + If ``sync`` is True then the record is scanned immediately on the current thread. + + For ``reason`` argument must be used in conjunction with ``sync=True`` + on records with Python device support. This provides a means + of providing extra contextual information to the record's + :meth:`process ` method. + + ``force`` is used to decide if the record will actually be processed, + ``force=0`` will only process records with SCAN=Passive. + ``force=1`` will process any record if at all possible. + ``force=2`` will only process records with Python device support and + SCAN=I/O Intr. + + .. important:: + It is **never** safe to use ``sync=True`` while holding record locks, + including from within a *process* method. + """ + return _dbapi._Record.scan(self, *args, **kws) + + def asyncStart(self): + """Start asynchronous processing + + This method may be called from a device support + :meth:`process ` method + to indicate that processing will continue + later. + + .. important:: + This method is **only** safe to call within a *process* method. + """ + return _dbapi._Record.asyncStart(self) + + def asyncFinish(self, reason=None): + """Indicate that asynchronous processing can complete + + Similar to :meth:`scan`. Used to conclude asynchronous + process started with :meth:`asyncStart`. + + Processing is completed on the current thread. + + .. important:: + This method should **never** be called within + a :meth:`process ` method, + or any other context where a Record lock is held. + Doing so will result in a deadlock. + + Typically a *reason* will be passed to *process* as a way + of indicating that this is the completion of an async action. :: + + AsyncDone = object() + class MySup(object): + def process(record, reason): + if reason is AsyncDone: + record.VAL = ... # store result + else: + threading.Timer(1.0, record.asyncFinish, kwargs={'reason':AsyncDone}) + record.asyncStart() + """ + return _dbapi._Record.asyncStart(self, reason=reason) + def __getattr__(self, name): try: F = self.field(name) diff --git a/devsupApp/src/devsup/hooks.py b/devsupApp/src/devsup/hooks.py index 9ac261a..c9a4a6e 100644 --- a/devsupApp/src/devsup/hooks.py +++ b/devsupApp/src/devsup/hooks.py @@ -42,7 +42,7 @@ def initHook(state): @initHook("AfterIocRunning") def myfn(): - # do stuff + pass """ def _add(fn): addHook(state, fn) diff --git a/documentation/environment.rst b/documentation/environment.rst index 6384c06..a33677b 100644 --- a/documentation/environment.rst +++ b/documentation/environment.rst @@ -108,7 +108,7 @@ In somefile.c :: PyMODINIT_FUNC init_myextname(void) Installing for several Python versions ------------------------------------- +-------------------------------------- The recipe for building and installing the pyDevSup module for several python version side by side is ::