Skip to main content

Package for time-controlled consecutive execution of a function using threading.

Project description

loopimer

Package for time-controlled consecutive execution of a function using threading. Allows control over the time between each function execution and the introduction of time delays/pauses. Furthermore, the package utilizes Queues and slicing to provide the user with the ability to control the execution of the looping function using queued slices. The package was originally developed for working with rate limited APIs and only uses python standard libraries.

Requirements

Python Standard Libraries

  • threading
  • time
  • datetime
  • queue
  • math
  • sys
  • os

Installation

pip install loopimer
pip install git+https://github.com/rouzbeh-afrasiabi/loopimer.git

Decorators:

  • loopimer

Examples

Importing

from loopimer import *

Usage:

In the simplest usage case, you pass a value in seconds to the decorator through the 'every' variable. This will modify the function that is declared after the decorator to execute every x seconds as an indefinite loop when called.

#cauion: infinite loop
@loopimer(every=5)
def test(loop,): 
    print('loopimer')
test()    

Setting 'every' to zero will remove any delays between function executions and will convert the loop into a basic loop.

#caution: infinite loop
@loopimer(every=0)
def test(loop,): 
    print('loopimer')
test()    

The first variable passed to the target function allows you to control the loop running in the background (called 'loop' in the examples on this page). You can choose any name for this variable, but it is important that you pass a variable name as a placeholder to the target function when declaring your desired function. The first variable name passed to your target function during declaration will always be reserved for the loop that will be running in the background. Upon calling the target function, this placeholder will be linked to the related thread controlling the loop.

The loop starts authomatically when the function is called and will run indefinity. You can control the loop and how the function is executed using the placeholder variable. This is currently only possible when declaring the target function.

Stopping the loop

@loopimer(every=5)
def test(loop,): 
    print('loopimer')
    loop.kill()
test()    

Please note that when calling the target function we don't pass the placeholder variable name to the function.

Stopping the loop using loop counter

It is important to note that the counter starts at 1 instead of 0.

@loopimer(every=1)
def test(loop,):
    print(loop.counter)
    if(loop.counter==10):
        print('loopimer')
        loop.kill()
test()   

Stopping the loop using loop elapsed time

@loopimer(every=1)
def test(loop,):
    print(loop.elapsed,loop.total_seconds)
    if(loop.total_seconds>=6):
        print('loopimer')
        loop.kill()
test()  

Adding custom variables to the loop

The loop placeholder variable is an open class and you can add new attributes to it. However, it is important to use attribute names that don't cause a naming conflict with names already being used by the class.

Attributes can be created inside the function or passed to the decorator instead.

from random import randrange

@loopimer(every=1,i=0)
def test(loop,):
    print(loop.i)
    if(loop.i==10):
        print('loopimer')
        loop.kill()
    loop.i+=1
test()
@loopimer(every=1)
def test(loop,):
    loop.k=randrange(100)
    print(loop.k)
    if(loop.k>80):
        loop.kill()
test()     

Changing the value of 'every' in the loop function

The 'every' variable passed through the decorator is available to the loop. You can change the value of this variable using 'loop.every'.

@loopimer(every=1)
def do(loop):
    print(loop.counter)
    if(loop.counter>10):
        loop.every=5
    if(loop.counter>20):
        loop.kill()
do()    

Using queue

A sliceable variable can be passed to the loopimer decorator through the 'target' variable for processing. This sliceable variable is then split into slices of 'n_splits' size. The slices are placed in a queue and can be accessed through the 'sequence' attribute of the loop (loop.squence). This attribute is an instance of Queue. The loop automatically stops when no items are left in the queue.

When a target is not provided the queue is automatically filled with one item containing a zero. This means that the loop will stop after one cycle if the queue is accessed.

@loopimer(every=1)
def test(loop,):
    print(loop.counter,loop.sequence.get())
test()   

Using queue with target and n_splits

target=[i for i in range(0,100,1)]
n_splits=10 

@loopimer(target=target,n_splits=n_splits,every=1)
def test(loop,):
    print(loop.sequence.get())
test()      

Using queue without providing target and n_splits to decorator

import queue

new_queue=queue.Queue()
new_target=[1,2,3,4,5,6,7,8,9]
for item in new_target:
    new_queue.put(item)
    
@loopimer(every=1)
def test(loop,new_queue):
    if(loop.counter==1):
        loop.sequence=new_queue
    else:
        print(loop.counter,loop.sequence.get())
test(new_queue=new_queue) 
import queue

new_queue=queue.Queue()
new_target=[1,2,3,4,5,6,7,8,9]
for item in new_target:
    new_queue.put(item)
    
@loopimer(every=1,new_queue=new_queue)
def test(loop,):
    if(loop.counter==1):
        loop.sequence=new_queue
    else:
        print(loop.counter,loop.sequence.get())
test()
import queue

@loopimer(every=1)
def test(loop):
    if(loop.counter==1):
        items=[1,2,3,4,5,6]
        loop.sequence=queue.Queue()
        for item in items:
            loop.sequence.put(item)
    else:
        print(loop.counter,loop.sequence.get())
test()   

Adding your own queue variable to the loop

import queue

@loopimer(every=1)
def test(loop):
    if(loop.counter==1):
        items=[1,2,3,4,5,6]
        loop.my_queue=queue.Queue()
        for item in items:
            loop.my_queue.put(item)
        
    else:
        print(loop.counter,loop.my_queue.get())
    if(loop.my_queue.qsize()==0):
        loop.kill()
test() 

Using time delays

By Changing the value of the loop's pause attribute you can introduce time delays. This is especially useful when you reach a rate limit when pulling/pushing data from/to an API.

target=[i for i in range(0,100,1)]
n_splits=10

@loopimer(target=target,n_splits=n_splits,every=1)
def test(loop,):
    print(loop.sequence.qsize(),loop.sequence.get())
    if(loop.sequence.qsize()==8):
        loop.pause=20
test()         

Using queue and json

import numpy as np
import pandas as pd
import datetime as dt
import simplejson as json

df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD'))
df['date']=dt.datetime.now()

df_json=json.loads(df.to_json(orient='records'))

@loopimer(target=df_json,n_splits=10,every=1)
def test(loop,):
    print(json.dumps(loop.sequence.get()),'\n')
test()  

Without using the loopimer decorator

def test(loop,):
    print(loop.counter)
    if(loop.counter==10):
        print('loopimer')
        loop.kill() 

x=[i for i in range(0,10000,1)]
loop=loopi(target=x,)
loop.apply_to(test,)
loop.startTimedLoop(every=1)
def test(loop,t):
    print(loop.counter+t)
    if(loop.counter==10):
        print('loopimer')
        loop.kill() 

x=[i for i in range(0,10000,1)]
loop=loopi(target=x,)
loop.apply_to(test,t=2)
loop.startTimedLoop(every=1)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

loopimer-1.0.2.tar.gz (5.9 kB view details)

Uploaded Source

File details

Details for the file loopimer-1.0.2.tar.gz.

File metadata

  • Download URL: loopimer-1.0.2.tar.gz
  • Upload date:
  • Size: 5.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.3

File hashes

Hashes for loopimer-1.0.2.tar.gz
Algorithm Hash digest
SHA256 2e6d0c9ae46a25eca75d78c6539d14482e7f975755a0fd9d3094fd069626a231
MD5 40ddf3e4b0573027aa99cc1ecb64b843
BLAKE2b-256 1340dacc6c1da2583cc8f486786a8461365a869c416a9b62e71c53bc61dbf3e2

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page