devsup Package

NO_ALARM, MINOR_ALARM, MAJOR_ALARM, INVALID_ALARM

Constants for alarm severity. Use with Record.setSevr

NO_ALARM, READ_ALARM, WRITE_ALARM, ...

Constants for alarm status. Use with Record.setSevr

db Module

devsup.db.getRecord(name)

Retrieve a Record instance by the full record name.

The result is cached so the future calls will return the same instance. This is the preferred way to get Record instances.

>>> R = getRecord("my:record:name")
Record("my:record:name")

Record Class

class devsup.db.Record

Allows access to a single record instance. Record instances can be created for any record in the process database. However, some features will only function when the record has Python device support.

Record objects allow access to fields through the field() method and direct access to field values through attributes.

Attribute access is currently limited to scalar fields.

>>> R = getRecord("my:record:name")
>>> F = R.field('VAL')
>>> F.getval()
42
>>> R.VAL
42
>>> R.VAL = 43
name() → str

Record name.

R = getRecord("my:record:name")
assert R.name()=="my:record:name"
rtype() → str

Return record type name string

field(name)

Lookup field in this record

Return type:Field
Throws:KeyError for non-existent fields.

The returned object is cached so future calls will return the same instance.

>>> getRecord("rec").field('HOPR')
Field("rec.HOPR")
setSevr(sevr=INVALID_ALARM, stat=COMM_ALARM)

Set alarm new alarm severity/status. Record must be locked!

setTime(ts)

Set record timestamp.

Parameters:ts – datetime, float, or (sec, nsec).

Has not effect if the TSE field is not set to -2. All inputs must be referenced to the posix epoch.

If a datetime is provided, it must use the local system timezone.

scan(sync=False, reason=None, force=0)

Scan this record.

Parameters:
  • sync – scan in current thread (True), or queue to a worker (False).
  • reason – Reason object passed to process (sync=True only)
  • 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 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.

asyncStart()

Start asynchronous processing

This method may be called from a device support process method to indicate that processing will continue later.

Important

This method is only safe to call within a process method.

asyncFinish(reason=None)

Indicate that asynchronous processing can complete

Similar to scan(). Used to conclude asynchronous process started with asyncStart().

Processing is completed on the current thread.

Important

This method should never be called within a 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()
info(key[, default]) → str

Lookup info by name :rtype: str :throws: KeyError

infos() → {'name':'value'}

Return a dictionary of all infos for this record.

Field Class

class devsup.db.Field
name() -> (recname, fldname)
record

Fetch the Record associated with this field

getval() → object
putval(object)
getarray() → numpy.ndarray

Return a numpy ndarray refering to this field for in-place operations.

getarraylen() → int

Return current number of valid elements for array fields.

putarraylen(int)

Set number of valid elements for array fields.

fieldinfo() → (dbf, elem_size, elem_count
getTime()

Get timestamp of link target.

Only works for DBF_INLINK fields. Returns the time in seconds since the POSIX epoch.

Return type:float
getAlarm() → (severity, status).
class devsup.db.IOScanListBlock

A list of records which will be processed together.

This convenience class to handle the accounting to maintain a list of records.

add(rec)

Add a record to the scan list.

This method is designed to be consistent with allowScan by returning its remove() method. If fact this function can be completely delegated.

class MyDriver(util.StoppableThread):
  def __init__(self):
    super(MyDriver,self).__init__()
    self.lock = threading.Lock()
    self.scan1 = IOScanListBlock()
  def run(self):
    while self.shouldRun():
      time.sleep(1)
      with self.lock:
        self.scan1.interrupt()

class MySup(object):
  def __init__(self, driver):
    self.driver = driver
  def allowScan(rec):
    with self.driver.lock:
      return self.driver.scan1.add(rec)
interrupt(reason=None, mask=None)

Scan the records in this list.

Parameters:
  • reason – Passed to Record.scan().
  • mask – A list or set or records which should not be scanned.
Returns:

True

This method will call Record.scan() of each of the records currently in the list. This is done synchronously in the current thread. It should never be call when any record locks are held.

remove(rec)

Remove a record from the scan list.

class devsup.db.IOScanListThread

A list of records w/ a worker thread to run them.

All methods are thread-safe.

add(rec)

Add a record to the scan list.

This method is thread-safe and may be used without additional locking.

class MyDriver(util.StoppableThread):
  def __init__(self):
    super(MyDriver,self).__init__()
    self.scan1 = IOScanListThread()
  def run(self):
    while self.shouldRun():
      time.sleep(1)
      self.scan1.interrupt()
      
class MySup(object):
  def __init__(self, driver):
    self.driver = driver
    self.allowScan = self.driver.scan1.add
interrupt(reason=None, mask=None, whendone=<function _default_whendone>)

Queue a request to process the scan list.

Parameters:
  • reason – Passed to Record.scan().
  • mask – A list or set or records which should not be scanned.
  • whendone – A callable which will be invoked after all records are processed.
Returns:

True is the request was queued, and False if the queue was full.

Throws:

RuntimeError is the request can’t be queued.

Calling this method will cause a request to be sent to a worker thread. This method can be called several times to queue several requests.

If provided, the whendone callable is invoked with three arguments. These will be None except in the case an interrupt is raised in the worker in which case they are: exception type, value, and traceback.

Note

This method may be safely called while record locks are held.

hooks Module

devsup.hooks.addHook("stats", funcion)

Add callable to IOC start sequence.

Callables are run in the order in which they were added (except for ‘AtIocExit’).

>>> def show():
...     print('State Occurred')
>>> addHook("AfterIocRunning", show)

An additional special hook ‘AtIocExit’ may be used for cleanup actions during IOC shutdown.

devsup.hooks.initHook(state)

Decorator for initHook functions

@initHook(“AfterIocRunning”) def myfn():

pass
devsup.hooks.debugHooks()

Install debugging print to hooks

util Module

class devsup.util.StoppableThread(max=0)

Bases: threading.Thread

A thread which can be requested to stop.

The thread run() method should periodically call the shouldRun() method and return if this yields False.

>>> class TestThread(StoppableThread):
...     def __init__(self):
...         super(TestThread,self).__init__()
...         self.E=threading.Event()
...     def run(self):
...         import time
...         self.cur = threading.current_thread()
...         self.E.set()
...         while self.shouldRun():
...             self.sleep(10.0)
>>> T = TestThread()
>>> T.start()
>>> T.E.wait(1.0)
True
>>> T.cur is T
True
>>> T.join()
>>> T.is_alive()
False
join()

Wait until the thread terminates.

This blocks the calling thread until the thread whose join() method is called terminates – either normally or through an unhandled exception or until the optional timeout occurs.

When the timeout argument is present and not None, it should be a floating point number specifying a timeout for the operation in seconds (or fractions thereof). As join() always returns None, you must call is_alive() after join() to decide whether a timeout happened – if the thread is still alive, the join() call timed out.

When the timeout argument is not present or None, the operation will block until the thread terminates.

A thread can be join()ed many times.

join() raises a RuntimeError if an attempt is made to join the current thread as that would cause a deadlock. It is also an error to join() a thread before it has been started and attempts to do so raises the same exception.

shouldRun()

Returns False is a request to stop is made.

sleep(delay)

Sleep for some time, or until a request to stop is made. Return value is the same as shouldRun()

start()

Start the thread’s activity.

It must be called at most once per thread object. It arranges for the object’s run() method to be invoked in a separate thread of control.

This method will raise a RuntimeError if called more than once on the same thread object.

class devsup.util.Worker(max=0)

Bases: threading.Thread

A threaded work queue.

>>> w = Worker()
>>> w.start()
>>> w.join()
>>> import threading
>>> E = threading.Event()
>>> w = Worker()
>>> w.start()
>>> w.add(E.set)
True
>>> E.wait(1.0)
True
>>> w.join()
StopWorker = <object object>
add(func, args=(), kws={}, block=True)

Attempt to send a job to the worker.

Returns:True if the job was queued. False if the queue is full, or has been joined.

When block=True then this method will only return False if the Worker has been joined.

join(flush=True)

Stop accepting new jobs and join the worker thread

Blocks until currently queued work is complete.

run()

Method representing the thread’s activity.

You may override this method in a subclass. The standard run() method invokes the callable object passed to the object’s constructor as the target argument, if any, with sequential and keyword arguments taken from the args and kwargs arguments, respectively.

devsup.util.importmod(modname)

Import the named python module(s) add return the leaf.

>>> M=importmod('xml.sax')
>>> M.__name__
'xml.sax'
>>>

disect Module

Python reference counter statistics.

class devsup.disect.StatsDelta

GC statistics tracking.

Monitors the number of instances of each type/class (cf. gcstats()) and prints a report of any changes (ie. new types/count changes). Intended to assist in detecting reference leaks.

collect(file=<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>)

Collect stats and print results to file

Parameters:file – A writable file-like object
reset()

Reset internal statistics counters

devsup.disect.gcstats()

Count the number of instances of each type/class

Returns:A dict() mapping type (as a string) to an integer number of references
devsup.disect.periodic(period=60.0, file=<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>)

Start a daemon thread which will periodically print GC stats

Parameters:
  • period – Update period in seconds
  • file – A writable file-like object