Welcome, guest | Sign In | My Account | Store | Cart

A LoopStatus object tests true according to geometric progression or time intervals. This enables rapid time estimates for long running codes, typically in a loop, without producing copious highly repetitive output.

Python, 58 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
'''
    import logging
    from LoopStatus import LoopStatus

    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s %(levelname)-8s L%(lineno)d %(message)s',
        datefmt='%H:%M:%S',
        )

    status = LoopStatus()
    ...
    if status():
        logging.info('%dnth occurence',status.Value)
'''

import signal

class LoopStatus(object):

    def __init__(self,base=2,seconds=5*60,target=1):
        self.i = 0
        self.base = base
        self.target = target
        self.nonzero = True
        self.timesup = False
        self.interval = seconds
        if 0 < seconds:
            self.reset_timer()
            signal.signal(signal.SIGALRM,self.trap_alarm)

    def trap_alarm(self,*args,**kwargs):
        self.timesup = True

    def reset_timer(self,interval=None):
        if interval is None:
            interval = self.interval
        if 0 < interval:
            self.timesup = False
            signal.alarm(interval)

    def __call__(self):
        self.i += 1
        self.nonzero = self.timesup or (self.target <= self.i)
        if self:
            self.target *= self.base
            self.reset_timer()
        return self.nonzero

    @property
    def Value(self):
        return self.i

    def __nonzero__(self):
        return self.nonzero

    def __str__(self):
        return str(self.Value)

I'd include LoopStatusTest.py, except that I'd also have to post my testing framework. Tests are important. Here is an annotated sample use case terminated by keyboard interrupt:

>>> import LoopStatus
>>> status = LoopStatus.LoopStatus(seconds=4)
>>> while True:
...     if status():
...         print s.Value
...
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
3592136       # the 4 second timer caused this event
5106735
6616367
8126450
^C
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "ls.py", line 44, in __call__
    if self.timesup or (self.target <= self.i):
KeyboardInterrupt
>>>

CONTROL

To closer pack the geometric sequence---set base to a float between 1 and 2.

For decade counting---set base to 10. I don't understand the fuss regardinging the usual number of fingers, hence the binary default.

For more frequent initial true tests---set target between 0 and 1

0 < target < 1

To test true less often initially---increase target.

To disable truth by time---choose 0 for seconds. The default is 5*60 seconds == 5 minutes.

To disable truth by count---use a large value of target.

METHODS

Call the LoopStatus object to increment its count, set the object's truth, and manage internals. It returns its truth value.

LoopStatus defines __nonzero__ which does not alter the object's truth.

LSObject.Value returns the LSObject() call count.

sample:

from WriteNumber import WriteNumber
unfinished = True
status = LoopStatus.LoopStatus()

while unfinished:

    if status():                    # Call updates truth state
        info('measure %s.',WriteNumber(status.Value)) # progress

    unfinished = symphony(
        verbose = not not status)   # safely use boolean state

1 comment

David Lambert (author) 13 years, 1 month ago  # | flag

correct! The first example in the discussion should read

status.Value

where it says "s.Value".

base = 0 causes aways True.

the reset_timer method encourages changing the time interval.

I didn't find the "preview" option. These are the corrections I see. I'm sure you will find others. Happy programming!