Skip to content
This repository has been archived by the owner on May 24, 2022. It is now read-only.

Commit

Permalink
Merge branch 'release/0.0.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
adam-hanna committed Aug 18, 2018
2 parents e1dd6e5 + beff182 commit 5632f87
Show file tree
Hide file tree
Showing 8 changed files with 986 additions and 0 deletions.
41 changes: 41 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# macOS
.DS_Store

# vim
.swp
*~
*.swp
*.swo

# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.h
*.dylib
bin/
build/

# Test binary, build with `go test -c`
*.test

# Output of the go coverage tool, specifically when used with LiteIDE
*.out

# misc
notes.txt

.data/

.testfiles/

*.pem

*.tar

node/test_data/go_example_image.tar

coverage.out

__pycache__
661 changes: 661 additions & 0 deletions GNU-AGPL-3.0.txt

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
c3-sdk-python is an sdk for developing c3 apps in python.
Copyright (C) 2018 C3 Labs

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
all: deps

.PHONY: deps
deps: clean
@go get github.com/c3systems/c3-go && \
(cd "${GOPATH}/src/github.com/c3systems/c3-go/lib/c" && \
make) && \
cp "${GOPATH}/src/github.com/c3systems/c3-go/lib/c/common/hashing/hashing.so" ./lib/hashing && \
cp "${GOPATH}/src/github.com/c3systems/c3-go/lib/c/common/hexutil/hexutil.so" ./lib/hexutil && \
cp "${GOPATH}/src/github.com/c3systems/c3-go/lib/c/common/stringutil/stringutil.so" ./lib/stringutil && \
cp "${GOPATH}/src/github.com/c3systems/c3-go/lib/c/config/config.so" ./lib/config

clean:
@-find . -type f -name *.so -delete
Empty file added README.md
Empty file.
3 changes: 3 additions & 0 deletions lib/state.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"foo": "bar"
}
175 changes: 175 additions & 0 deletions sdk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
from ctypes import *
from queue import Queue
from threading import Thread
import socket
import json

# note: these files must first be built. see the make file
hashing = CDLL('./lib/hashing/hashing.so')
hexutil = CDLL('./lib/hexutil/hexutil.so')
config = CDLL('./lib/config/config.so')
stringutil = CDLL('./lib/stringutil/stringutil.so')

class BytesResponse(Structure):
_fields_ = [
("r0", c_void_p),
("r1", c_int),
]

stringutil.CompactJSON.restype = BytesResponse
hexutil.DecodeString.restype = BytesResponse

ErrMethodAlreadyRegistered = Exception("method already registered")
ErrMethodNotExists = Exception("method does not exist")
ErrIncorrectNumberOfArgs = Exception("method requires two arguments")

class C3():
def __init__(self, statefile):
self.methods = {}
self.state = {}
self.q = Queue(maxsize=0)
self.statefile = statefile

def registerMethod(self, methodName, ifn):
b = bytearray()
b.extend(map(ord, methodName))
arr = (c_byte * len(b))(*b)

methodNameHash = c_char_p(hashing.HashToHexString(arr, len(arr))).value.decode('utf-8')
if methodNameHash in self.methods:
raise ErrMethodAlreadyExists

def newMethod(*args):
if len(args) != 2:
raise ErrIncorrectNumberOfArgs

key = args[0]
res = hexutil.DecodeString(c_char_p(key.encode('utf-8')))
ArrayType = c_ubyte*(c_int(res.r1).value)
pa = cast(c_void_p(res.r0), POINTER(ArrayType))
key = "".join(map(chr, pa.contents[:]))

val = args[1]
res = hexutil.DecodeString(c_char_p(val.encode('utf-8')))
ArrayType = c_ubyte*(c_int(res.r1).value)
pa = cast(c_void_p(res.r0), POINTER(ArrayType))
val = "".join(map(chr, pa.contents[:]))

try:
res = ifn(key, val)
print("[c3] result", res)
except Exception as inst:
print("[c3] invokation failed", inst)

self.methods[methodNameHash] = newMethod

def setInitialState(self):
currState = ""

file = open(self.statefile, "r")
currState = file.read()

if len(currState) == 0:
print("no current state")
return

b = bytearray()
b.extend(map(ord, currState))
arr = (c_byte * len(b))(*b)

res = stringutil.CompactJSON(arr, len(arr))
ArrayType = c_ubyte*(c_int(res.r1).value)
pa = cast(c_void_p(res.r0), POINTER(ArrayType))

self.state = json.loads("".join(map(chr, pa.contents[:])))
print("initial state loaded")

def process(self, payloadBytes):
payload = json.loads(payloadBytes)

if len(payload) <= 1:
return

# ifc format is [a, b, c]
if isinstance(payload[0], str):
self.invoke(payload[0], payload[1:])

# ifc format is [[a, b, c], [a, b, c]]
for ifc in payload:
self.invoke(ifc[0], *ifc[1:])

def invoke(self, methodName, *params):
b = bytearray()
b.extend(map(ord, methodName))
arr = (c_byte * len(b))(*b)

methodNameHash = c_char_p(hashing.HashToHexString(arr, len(arr))).value.decode('utf-8')
if methodNameHash not in self.methods:
raise ErrMethodNotExists

fn = self.methods[methodNameHash]
try:
fn(*params)
except Exception as inst:
print("[c3] err invoking method", inst)
return

def listen(self):
while True:
self.process(self.q.get())
q.task_done()

def serve():
host = c_char_p(config.ServerHost()).value
port = c_int(config.ServerPort()).value

server = Server(host, port, self.q)

worker = Thread(target=server.run)
# worker.setDaemon(True)
worker.start()

def NewC3(stateFilePath = c_char_p(config.TempContainerStateFilePath()).value.decode('utf-8')):
c3 = C3(stateFilePath)

c3.setInitialState()

worker = Thread(target=c3.listen)
# worker.setDaemon(True)
worker.start()

return c3

class Server():
def __init__(self, host, port, q):
self.host = host
self.port = port
self.q = q

def run():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((self.host, self.port))
server.listen(1) # max backlog of connections

print("Listening on {0}:{1}".format(bind_ip, bind_port))


def handle_conn(conn):
data = []
while 1:
tmp = conn.recv(1024)
if not tmp: break
data.append(tmp)

self.q.put(''.join(data))
conn.close()

while True:
client_sock, address = server.accept()
print('Accepted connection from {0}:{1}'.format(address[0], address[1]))
client_handler = threading.Thread(
target=handle_conn,
args=(client_sock,) # note: comment required!
)

client_handler.start()
77 changes: 77 additions & 0 deletions sdk_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from ctypes import *
import unittest
import sdk
import json

hexutil = CDLL('./lib/hexutil/hexutil.so')

c3 = sdk.NewC3(stateFilePath = './lib/state.json')

class TestSDK(unittest.TestCase):
def test_registerAndInvokeMethod(self):
key = ""
val = ""
expectKey = "expectKey"
expectVal = "expectVal"
methodName = "foo"

inputKey = c_char_p(hexutil.EncodeString(c_char_p(expectKey.encode('utf-8')))).value.decode('utf-8')
inputVal = c_char_p(hexutil.EncodeString(c_char_p(expectVal.encode('utf-8')))).value.decode('utf-8')

def setStuff(k, v):
nonlocal key
key = k

nonlocal val
val = v

c3.registerMethod(methodName, setStuff)
c3.invoke(methodName, inputKey, inputVal)

self.assertEqual(expectKey, key)
self.assertEqual(expectVal, val)

def test_store(self):
key = "foo"
val = "bar"

c3.state[key] = val

self.assertEqual(c3.state[key], val)
del c3.state[key]

def test_state(self):
methodName = "setState"

key1 = "foo"
val1 = "bar"

key2 = "foofoo"
val2 = "barbar"

def setState(k, v):
c3.state[k] = v

c3.registerMethod(methodName, setState)

p1 = [
methodName,
c_char_p(hexutil.EncodeString(c_char_p(key1.encode('utf-8')))).value.decode('utf-8'),
c_char_p(hexutil.EncodeString(c_char_p(val1.encode('utf-8')))).value.decode('utf-8'),
]
p2 = [
methodName,
c_char_p(hexutil.EncodeString(c_char_p(key2.encode('utf-8')))).value.decode('utf-8'),
c_char_p(hexutil.EncodeString(c_char_p(val2.encode('utf-8')))).value.decode('utf-8'),
]

params = [p1, p2]
paramsJSON = json.dumps(params)

c3.process(paramsJSON)

self.assertEqual(c3.state[key1], val1)
self.assertEqual(c3.state[key2], val2)

if __name__ == '__main__':
unittest.main()

0 comments on commit 5632f87

Please sign in to comment.