Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,29 @@ Install the package using:
=============
## Basic application
```Python
from spacetime import Application
from spacetime import Node

def my_app(dataframe):
print ("Hello Universe!")

if __name__ == "__main__":
app = Application(my_app)
app = Node(my_app)
app.start()
```

`Application` is the basic worker in the spacetime system. An application needs a function to execute. In this case, we provide it the `my_app` function. Application objects are very similar to the objects created by multiprocessing.Process. An application can be launched by using the `app.start()` function call. When it is executed, the main process blocks on the call until the application finishes. To start the application asynchronously, we can start the application using the call `app.start_async()` instead. The main process launches the application and continues. Since the application is run as a daemon, the application will be terminated when the main process ends. To wait for the application to end, we can use the `app.join()` call.
`Node` is the basic worker in the spacetime system. An application needs a function to execute. In this case, we provide it the `my_app` function. Node objects are very similar to the objects created by multiprocessing.Process. An application can be launched by using the `app.start()` function call. When it is executed, the main process blocks on the call until the application finishes. To start the application asynchronously, we can start the application using the call `app.start_async()` instead. The main process launches the application and continues. Since the application is run as a daemon, the application will be terminated when the main process ends. To wait for the application to end, we can use the `app.join()` call.

Every application function, takes dataframe as the first parameter. This is of the type `rtypes.dataframe.Dataframe` and will be discussed in more details down below.

## Parameters in the function
```Python
from spacetime import Application
from spacetime import Node

def my_app(dataframe, secret_msg):
print ("Hello Universe! My secret is ", secret_msg)

if __name__ == "__main__":
app = Application(my_app)
app = Node(my_app)
app.start_async("not for you to know.")
app.join()
```
Expand All @@ -44,7 +44,7 @@ Parameters can be added after the default first parameter, and can be given to t
The dataframe is a container for objects of special types (called relational types or rtypes). These objects can be synchronized between several applications and can be used as a means of inter application communication.

```Python
from spacetime import Application
from spacetime import Node
from rtypes import pcc_set, primarykey, dimension

@pcc_set
Expand All @@ -65,7 +65,7 @@ def create_cars(dataframe):
dataframe.add_many(Car, [Car(i) for i in range(10)])

if __name__ == "__main__":
app = Application(create_cars, Types=[Car])
app = Node(create_cars, Types=[Car])
app.start()
```
There are a lot of things to unpack in the example. We define a class `Car` and declare it to be a `pcc_set`. When a type is declared as a `pcc_set` the dataframe can track objects of that type. All objects of that type registered with the dataframe are kept together in a collection. Rtypes define both the collections that are possible, and the type of the objects within the collection. There are more complex types that can be defined within rtypes, but they will be discussed later. All of those types use `pcc_set` as the base type, and build complex collections on top of the `pcc_set` collection of objects. The type `Car` is also defined with 1 primarykey: `vin` and 3 dimensions: `color`, `position`, and `velocity`. Both `primarykey` and `dimension` are properties that are tracked by the dataframe. All other attributes and properties are local, and will not be tracked (EG: `number_of_tires`).
Expand All @@ -74,12 +74,12 @@ Only one property can be defined as the `primarykey` and defines the unique iden

Both `primarykey` and `dimension` take in a type that signifies the type of the property and this is enforced. Additionally, `primarykey` has to be either an integer, float, or a string. (Boolean values are allowed, but they are obviously not going to be extremely useful.) Lists, and custom objects are not allowed (yet).

The line `app = Application(create_cars, Types=[Car])` defines the an application whose dataframe tracks objects of type `Car`. Multiple types can be tracked.
The line `app = Node(create_cars, Types=[Car])` defines the an application whose dataframe tracks objects of type `Car`. Multiple types can be tracked.

Objects of type `Car` can be added to the dataframe using the `add_many` method call. `dataframe.add_many(Car, [Car(i) for i in range(10)])` adds 10 `Car` objects to the dataframe in `create_cars`.

```Python
from spacetime import Application
from spacetime import Node
from rtypes import pcc_set, primarykey, dimension

@pcc_set
Expand All @@ -100,16 +100,16 @@ def create_cars(dataframe):
dataframe.add_many(Car, [Car(i) for i in range(10)])

def traffic_sim(dataframe):
car_creator = Application(create_cars, Producer=[Car], dataframe=dataframe)
car_creator = Node(create_cars, Producer=[Car], dataframe=dataframe)
car_creator.start()
for _ in range(100):
for car in dataframe.read_all(Car):
car.move()

if __name__ == "__main__":
app = Application(traffic_sim, Types=[Car])
app = Node(traffic_sim, Types=[Car])
app.start()
```
In this example, the Main process launches one instance of the `traffic_sim` Application. This application in turn creates a single instance of the `create_cars` application and launches it. By defining the instance using `car_creator = Application(create_cars, Producer=[Car], dataframe=dataframe)`, the application is creating a sub application that can only producer new objects of type `Car`, and must synchronize the objects it creates with `traffic_sim`'s dataframe (using the keyword argument `dataframe=dataframe`). `traffic_sim` waits for `car_creator` to complete, which also ensures that all cars created are synchronized into the dataframe in `traffic_sim`. The `traffic_sim` then proceeds to move each car it has for 100 iterations before completing it's task.
In this example, the Main process launches one instance of the `traffic_sim` Node. This application in turn creates a single instance of the `create_cars` application and launches it. By defining the instance using `car_creator = Node(create_cars, Producer=[Car], dataframe=dataframe)`, the application is creating a sub application that can only producer new objects of type `Car`, and must synchronize the objects it creates with `traffic_sim`'s dataframe (using the keyword argument `dataframe=dataframe`). `traffic_sim` waits for `car_creator` to complete, which also ensures that all cars created are synchronized into the dataframe in `traffic_sim`. The `traffic_sim` then proceeds to move each car it has for 100 iterations before completing it's task.

TO BE CONTINUED
6 changes: 3 additions & 3 deletions python/benchmarks/base_set/baseset_create_objs.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,14 @@ def consumer(dataframe):
VERSIONBY[dataframe.version_by]))

def create_objs(dataframe, version_by):
prod_app = Application(producer, types=[BaseSet], dataframe=dataframe, version_by=version_by)
con_app = Application(consumer, types=[BaseSet], dataframe=dataframe, version_by=version_by)
prod_app = Node(producer, types=[BaseSet], dataframe=dataframe, version_by=version_by)
con_app = Node(consumer, types=[BaseSet], dataframe=dataframe, version_by=version_by)
con_app.start_async()
prod_app.start()
con_app.join()

def main():
app = Application(create_objs, types=[BaseSet], dataframe=dataframe, version_by=version_by))
app = Node(create_objs, types=[BaseSet], dataframe=dataframe, version_by=version_by))
app.start()

# main().start()
4 changes: 2 additions & 2 deletions python/mp_ll_client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys
from sample_apps.gym_mp_lunarlander.envs.multiplayer_lunar_lander import Lander
from spacetime import Application
from spacetime import Node

def lander(dataframe):
my_lander = Lander()
Expand All @@ -16,5 +16,5 @@ def lander(dataframe):

if __name__ == "__main__":
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000
Application(lander, dataframe=("0.0.0.0", port), Producer=[Lander]).start()
Node(lander, dataframe=("0.0.0.0", port), Producer=[Lander]).start()

4 changes: 2 additions & 2 deletions python/mp_ll_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import time
import sys
from sample_apps.gym_mp_lunarlander.envs.multiplayer_lunar_lander import Lander
from spacetime import Application
from spacetime import Node

WAIT_FOR_START = 10.0

Expand Down Expand Up @@ -33,7 +33,7 @@ def lander_server(dataframe):
time.sleep(5)

def main(port):
server = Application(lander_server, server_port=port, Types=[Lander])
server = Node(lander_server, server_port=port, Types=[Lander])
server.start()

if __name__ == "__main__":
Expand Down
8 changes: 5 additions & 3 deletions python/rtypes/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ def set(self, oid, dimname, dim_obj, value):
converted = convert(dim_obj.dim_type, value)
if oid in self.store_as_temp:
self.store_as_temp[oid][dimname] = converted

# Write to local state map.
self.object_table[oid][dimname] = convert(dim_obj.dim_type, value)
else:
# Write to local state map.
self.object_table[oid][dimname] = convert(dim_obj.dim_type, value)
return oid

def set_primarykey(self, oid, dimname, dim_obj, value):
Expand All @@ -55,6 +55,8 @@ def set_primarykey(self, oid, dimname, dim_obj, value):


def get(self, oid, dimname, dim_obj):
if oid in self.store_as_temp and dimname in self.store_as_temp[oid]:
return unconvert(self.store_as_temp[oid][dimname], dim_obj.dim_type)
if oid not in self.object_table and dimname not in self.object_table[oid]:
# Value has not been assigned.
raise AttributeError("{0} has not been assigned a value.".format(dimname))
Expand Down
2 changes: 1 addition & 1 deletion python/rtypes/utils/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
try:
import numpy as np
HASNUMPY = True
except AttributeError:
except ModuleNotFoundError:
HASNUMPY = False


Expand Down
Empty file modified python/sample_apps/__init__.py
100755 → 100644
Empty file.
4 changes: 2 additions & 2 deletions python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
# https://packaging.python.org/en/latest/single_source_version.html
version='2.0.0',

description='Spacetime Application Framework',
description='Spacetime Node Framework',
long_description=long_description,

# The project's main homepage.
Expand All @@ -51,7 +51,7 @@
'Intended Audience :: Science/Research',

'Topic :: Software Development :: Libraries',
'Topic :: Software Development :: Libraries :: Application Frameworks',
'Topic :: Software Development :: Libraries :: Node Frameworks',

# Pick your license as you wish (should match "license" above)
'License :: OSI Approved :: MIT License',
Expand Down
7 changes: 5 additions & 2 deletions python/spacetime/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
from spacetime.application import app, Application
from spacetime.dataframe import Dataframe
from spacetime.application import app, Node
from spacetime.debugger_server import server_func
from spacetime.dataframe import Dataframe
from spacetime.debug_dataframe import DebugDataframe
from spacetime.debugger_types import CheckoutObj, CommitObj, PushObj, PullObj, AcceptPullObj, AcceptPushObj,ConfirmPullObj, Register
Loading