Skip to content

A Python library to overload the basic Data-Model to convert it to reactive data structures making it possible to fuse reactive programming with ordinary imperative programming.

License

deepanshumehndiratta/reactive-py

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Reactive-Py

A Python library to overload the basic Data-Model to convert it to reactive data structures making it possible to fuse reactive programming with ordinary imperative programming for:

  • On-Change Callbacks
  • On-Compare Callbacks
  • Pre and Post filters for function calls

Data-structures supported (user-defined):

  • str
  • int
  • float
  • bool
  • list
  • tuples
  • dict
  • functions/methods
  • classes

Importing the module

from reactive import _

On-Change Callbacks

Basic Callbacks (Asynchronous)

def printer(trace, message='Hello', *args, **kwargs):
  print 'Message: ' + str(message)
  print 'Trace: ' + str(trace) 
  print 'Args: ' + str(args)
  print 'Kwargs: ' + str(kwargs)
 
obj = _(1)

obj.onchange(printer, args=(), kwargs={'message': 'Test'})

obj += 1

Output:

Message: Test
Trace: {'after_value': 2, 'before_value': 1, 'after_type': <type 'int'>, 'event': '__iadd__', 'before_type': <type 'int'>}
Args: ()
Kwargs: {}
[Finished in 0.1s]

(By default a callback is Asynchronous

Synchronous Callbacks

import time

def printer(trace, message='Hello', *args, **kwargs):
  time.sleep(2)
  print 'Message: ' + str(message)
 
obj = _("Hello")

obj.onchange(printer, args=(), kwargs={'message': 'Test'}, async=False)

obj += " World"

print "Here!"

Output:

Message: Test
Here!
[Finished in 2.1s]

Chaining Callbacks

def printer(trace, message='Hello', *args, **kwargs):
  print 'Message: ' + str(message)

def hello_world(trace, name='Deepanshu', *args, **kwargs):
  print 'Hello ' + name + '!'
 
obj = _("Hello")

obj.onchange(printer, args=(), kwargs={'message': "Test"})\
  .onchange(hello_world, args=(), kwargs={})

obj += " World"

Output:

Message: Test
Hello Deepanshu!
[Finished in 0.1s]

On-compare Callbacks

def printer(trace, *args, **kwargs):
  print 'Trace: ' + str(trace)
 
obj = _("Hello")

obj.oncompare(printer, args=(), kwargs={})

obj == " World"

Output:

Trace: {'after_type': <type 'str'>, 'before_type': <type 'str'>, 'after_value': ' World', 'comparison_output': False, 'before_value': 'Hello', 'event': '__eq__'}
[Finished in 0.1s]

Function callbacks

Before Function-Call Callback

def foo(bar=None, baz=None):
  print 'bar: ' + str(bar)
  print 'baz: ' + str(baz)

def printer(trace, *args, **kwargs):
  print 'Trace: ' + str(trace)
 
foo_obj = _(foo)

foo_obj.before_call(printer, args=(), kwargs={})

foo_obj(bar=1)

Output:

bar: 1
baz: None
Trace: {'kwargs': {'bar': 1}, 'args': (), 'event': '__call__', 'before_call': True}
[Finished in 0.1s]

After Function-Call Callback

def foo(bar=None, baz=None):
  print 'bar: ' + str(bar)
  print 'baz: ' + str(baz)
  return "Return Value"

def printer(trace, *args, **kwargs):
  print 'Trace: ' + str(trace)
 
foo_obj = _(foo)

foo_obj.after_call(printer, args=(), kwargs={})

foo_obj(bar=1)

Output:

bar: 1
baz: None
Trace: {'kwargs': {'bar': 1}, 'args': (), 'output': 'Return Value', 'event': '__call__', 'after_call': True}
[Finished in 0.1s]

Assigning values to reactive objects

In Python, assignment is a language intrinsic which doesn't have a modification hook. Which means, it can't be overloaded like the other Arithemetic or Logical operators.

A hack around this would be to inspect the stack at every tick and call the respective hooks accordingly. This however, isn't an ideal way to do it and acts as a major bottleneck in program execution.

However, to truly make the python object "reactive", assignment is done in the following way:

def foo(trace, *args, **kwargs):
  print 'Trace: ' + str(trace)

# Create a new reactive <'type' int> object
obj = _(1)

obj.onchange(foo, args=(), kwargs={})

# Assign value 2 to the object while keeping all its hooks (onchange, oncompare, before_call, after_call, etc) if any, intact and trigger a onchange event simultaneously
 
obj._(2)

Output:

Trace: {'after_value': 2, 'before_value': 1, 'after_type': <type 'int'>, 'event': '__assignment__', 'before_type': <type 'int'>}
[Finished in 0.1s]

Working with lists

def foo(trace, *args, **kwargs):
  print 'Trace: ' + str(trace)

# Create a new reactive list
obj = _([1, 2, 3, 4, 5])

obj.onchange(foo, args=(), kwargs={})
 
obj[2] = 4

Output:

Trace: {'index': 2, 'after_type': <type 'int'>, 'before_type': <type 'int'>, 'after_value': 4, 'before_value': 3, 'event': '__setitem__'}
[Finished in 0.1s]

Working with tuples

def foo(trace, *args, **kwargs):
  print 'Trace: ' + str(trace)

# Create a new reactive tuple
obj = _(('a', 'b'))

obj.onchange(foo, args=(), kwargs={})
 
obj += ('c', 'd')

Output:

Trace: {'after_value': ('a', 'b', 'c', 'd'), 'before_value': ('a', 'b'), 'after_type': <type 'tuple'>, 'event': '__iadd__', 'before_type': <type 'tuple'>}
[Finished in 0.1s]

Working with dicts

def foo(trace, *args, **kwargs):
  print 'Trace: ' + str(trace)

# Create a new reactive dict
obj = _({'a': 'b', 'c': 'd'})

obj.onchange(foo, args=(), kwargs={})
 
obj['e'] = 'f'

Output:

Trace: {'index': 'e', 'after_type': <type 'str'>, 'before_type': <type 'NoneType'>, 'after_value': 'f', 'before_value': None, 'event': '__setitem__'}
[Finished in 0.1s]

Working with classes

def foo(trace, *args, **kwargs):
  print 'Trace: ' + str(trace)

class A:
  pass

# Create a new reactive <'type' int> object
obj = _(A())

obj.onchange(foo, args=(), kwargs={})
 
obj.a = 'B'
obj.a = 'C'

Output:

Trace: {'attr': 'a', 'after_type': <type 'str'>, 'before_type': <type 'NoneType'>, 'after_value': 'B', 'before_value': None, 'event': '__setattr__'}
Trace: {'attr': 'a', 'after_type': <type 'str'>, 'before_type': <type 'str'>, 'after_value': 'C', 'before_value': 'B', 'event': '__setattr__'}
[Finished in 0.1s]

Supported Operators

License

The MIT License (MIT)

Copyright (c) 2014 Deepanshu Mehndiratta

About

A Python library to overload the basic Data-Model to convert it to reactive data structures making it possible to fuse reactive programming with ordinary imperative programming.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages