Skip to content

Commit

Permalink
better debug info
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreaCensi committed Mar 11, 2019
1 parent 6dcf7f0 commit 7b73f88
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 37 deletions.
4 changes: 2 additions & 2 deletions minimal-nodes-stubs/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ services:
- "fifos:/fifos"
- "./test_data:/test_data"
# Uncomment the following to use the current python code
- ../src:/src/aido-protocols/src
# - ../src:/src/aido-protocols/src
# If the one below is uncommented Andrea forgot it. Do comment.
- /data/work/zupermind/env/zuper_json:/src/zuper-utils
# - /data/work/zupermind/env/zuper_json:/src/zuper-utils
cpu_percent: 50
mem_limit: 100MB

Expand Down
1 change: 1 addition & 0 deletions minimal-nodes-stubs/dummy_image_filter/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@

numpy==1.16.2


Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class DummyImageSourceState:

@dataclass
class DummyImageSource:
""" A simple example of an image source """
config: DummyImageSourceConfig = field(default_factory=DummyImageSourceConfig)
state: DummyImageSourceState = field(default_factory=DummyImageSourceState)

Expand Down
1 change: 1 addition & 0 deletions minimal-nodes-stubs/dummy_image_source/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
numpy==1.16.2
opencv-python==4.0.0.21


1 change: 1 addition & 0 deletions minimal-nodes-stubs/random_agent/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@

numpy==1.16.2


194 changes: 164 additions & 30 deletions src/aido_node_wrapper/identify.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,177 @@
import argparse
import dataclasses
import json
import os
import time
import subprocess
import sys
from dataclasses import dataclass
from io import BytesIO, BufferedReader

from docker.models.containers import Container
from aido_node_wrapper import ProtocolDescription, InteractionProtocol, logger
from aido_node_wrapper.meta_protocol import BuildDescription, NodeDescription, ConfigDescription
from contracts import indent
from zuper_json import read_cbor_or_json_objects
from zuper_json.ipce import ipce_to_object


def identify_main():
usage = None
parser = argparse.ArgumentParser(usage=usage)

parser.add_argument('--image', default=None, required=True)
parser.add_argument('--image', default=None)

parser.add_argument('--command', default=None)

parsed = parser.parse_args()

image = parsed.image
identify_image(image)
from socket import SocketIO

def identify_image(image):
import docker
client = docker.from_env()

d = {'compat': ['aido2'], 'topic': 'wrapper.describe_protocol'}
j = (json.dumps(d) + '\n').encode('utf-8')
container: Container = client.containers.create(image, detach=True, stdin_open=True)
print(container)
# time.sleep(4)
# attach to the container stdin socket
container.start()
# s = container.exec_run()
s: SocketIO = container.attach_socket(params={'stdin': 1, 'stream': 1, 'stderr': 0, 'stdout': 0})
s_out: SocketIO = container.attach_socket(params={ 'stream': 1, 'stdout': 1, 'stderr': 0, 'stdin': 0})
print(s.__dict__)
print(s_out.__dict__)
# send text
# s.write(j)
os.write(s._sock.fileno(), j)
print(os.read(s_out._sock.fileno(), 100))

# close, stop and disconnect
s.close()
if image is not None:
ni: NodeInfo = identify_image2(image)
elif parsed.command is not None:
command = parsed.command.split()
ni: NodeInfo = identify_command(command)
else:
msg = 'Please specify either --image or --command'
logger.error(msg)
sys.exit(1)

print('\n\n')
print(indent(describe_nd(ni.nd), '', 'desc: '))
print('\n\n')
print(indent(describe_bd(ni.bd), '', 'build: '))
print('\n\n')
print(indent(describe_cd(ni.cd), '', 'config: '))
print('\n\n')
print(indent(describe(ni.pd.data), '', 'data: '))
print('\n\n')
print(indent(describe(ni.pd.meta), '', 'meta: '))


def describe_nd(nd: NodeDescription):
return str(nd.description)


def describe_bd(nd: BuildDescription):
return str(nd)


def describe_cd(nd: ConfigDescription):
s = []
for f in dataclasses.fields(nd.config):

# for k, v in nd.config.__annotations__.items():
s.append('%20s: %s = %s' % (f.name, f.type, f.default))
if not s:
return 'No configuration switches available.'

if hasattr(nd.config, '__doc__'):
s.insert(0, nd.config.__doc__)
return "\n".join(s)


def describe(ip: InteractionProtocol):
s = "InteractionProtocol"

s += '\n\n' + '* Description:'
s += '\n\n' + indent(ip.description.strip(), ' ')

s += '\n\n' + '* Inputs:'
for name, type_ in ip.inputs.items():
s += '\n %25s: %s' % (name, type_)

s += '\n\n' + '* Outputs:'
for name, type_ in ip.outputs.items():
s += '\n %25s: %s' % (name, type_)

s += '\n\n' + '* Language:'
s += '\n\n' + ip.language

return s


@dataclass
class NodeInfo:
pd: ProtocolDescription
nd: NodeDescription
bd: BuildDescription
cd: ConfigDescription


def identify_command(command) -> NodeInfo:
# "describe_config": type(None),
#
# "set_config": SetConfig,
#
# "describe_protocol": type(None),
#
# "describe_node": type(None),
#
# "describe_build": type(None),
d = [{'topic': 'wrapper.describe_protocol'},
{'topic': 'wrapper.describe_config'},
{'topic': 'wrapper.describe_node'},
{'topic': 'wrapper.describe_build'}
]
to_send = b''
for p in d:
p['compat'] = ['aido2']
to_send += (json.dumps(p) + '\n').encode('utf-8')
cp = subprocess.run(command, input=to_send, capture_output=True)
# f = open('/dev/stderr', 'wb')
s = cp.stderr.decode('utf-8')
# f.write(cp.stderr)
sys.stderr.write(indent(s.strip(), '|', ' stderr: |') +'\n\n')
# noinspection PyTypeChecker
f = BufferedReader(BytesIO(cp.stdout))
stream = read_cbor_or_json_objects(f)

res = stream.__next__()
pd: ProtocolDescription = ipce_to_object(res['data'], {}, {}, expect_type=ProtocolDescription)
res = stream.__next__()
cd: ConfigDescription = ipce_to_object(res['data'], {}, {}, expect_type=ConfigDescription)
res = stream.__next__()
nd: NodeDescription = ipce_to_object(res['data'], {}, {}, expect_type=NodeDescription)
res = stream.__next__()
bd: BuildDescription = ipce_to_object(res['data'], {}, {}, expect_type=BuildDescription)

return NodeInfo(pd, nd, bd, cd)


def identify_image2(image) -> NodeInfo:
cmd = ['docker', 'run', '--rm', '-i', image]
return identify_command(cmd)

# def identify_image(image):
# import docker
# client = docker.from_env()
#
#
# container: Container = client.containers.create(image, detach=True, stdin_open=True)
# print(container)
# # time.sleep(4)
# # attach to the container stdin socket
# container.start()
# # s = container.exec_run()
# s: SocketIO = container.attach_socket(params={'stdin': 1, 'stream': 1, 'stderr': 0, 'stdout': 0})
# s_out: SocketIO = container.attach_socket(params={ 'stream': 1, 'stdout': 1, 'stderr': 0, 'stdin': 0})
# s_stderr: SocketIO = container.attach_socket(params={'stream': 1, 'stdout': 0, 'stderr': 1, 'stdin': 0})
# print(s.__dict__)
# print(s_out.__dict__)
# # send text
# # s.write(j)
# os.write(s._sock.fileno(), j)
# os.close(s._sock.fileno())
# s._sock.close()
# # s.close()
#
# f = os.fdopen(s_out._sock.fileno(), 'rb')
# # there is some garbage: b'\x01\x00\x00\x00\x00\x00\x1e|{
# f.read(8)
#
# for x in read_cbor_or_json_objects(f):
# print(x)
# print(f.read(10))
# # print(os.read(s_out._sock.fileno(), 100))
#
# print(os.read(s_stderr._sock.fileno(), 100))
# # close, stop and disconnect
# s.close()
2 changes: 1 addition & 1 deletion src/aido_node_wrapper/meta_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class ConfigDescription:

@dataclass
class NodeDescription:
pass
description: str


@dataclass
Expand Down
37 changes: 33 additions & 4 deletions src/aido_node_wrapper/wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
import sys
import time
from abc import ABCMeta, abstractmethod
from dataclasses import dataclass
from typing import List, Optional, Iterator, Dict, Tuple, Any

import cbor2
from networkx.drawing.nx_pydot import write_dot

from aido_node_wrapper.meta_protocol import basic_protocol, SetConfig, ProtocolDescription
from aido_node_wrapper.meta_protocol import basic_protocol, SetConfig, ProtocolDescription, ConfigDescription, \
BuildDescription, NodeDescription
from aido_nodes import InteractionProtocol, InputReceived, OutputProduced, Unexpected, LanguageChecker, logger
from aido_nodes.structures import TimingInfo, local_time, TimeSpec, timestamp_from_seconds, DecodingError, \
ExternalProtocolViolation, NotConforming
Expand Down Expand Up @@ -66,7 +68,7 @@ def set_last_timing(self, timing: TimingInfo):
def get_hostname(self):
return self.hostname

def write(self, topic, data, timing=None):
def write(self, topic, data, timing=None, with_schema=False):
if topic not in self.protocol.outputs:
msg = f'Output channel "{topic}" not found in protocol; know {sorted(self.protocol.outputs)}.'
raise Exception(msg)
Expand Down Expand Up @@ -105,7 +107,7 @@ def write(self, topic, data, timing=None):
m = {}
m[FIELD_COMPAT] = [CUR_PROTOCOL]
m[FIELD_TOPIC] = self.tout.get(topic, topic)
m[FIELD_DATA] = object_to_ipce(data, {}, with_schema=False)
m[FIELD_DATA] = object_to_ipce(data, {}, with_schema=with_schema)
timing.received = None
m[FIELD_TIMING] = object_to_ipce(timing, {}, with_schema=False)
self._write_raw(m)
Expand Down Expand Up @@ -286,12 +288,14 @@ def loop(node_name, fi, fo, mi, mo, node, protocol, tin, tout, binary_out: bool)
binary_out=binary_out)
initialized = False

ATT_CONFIG = 'config'

class Wrapper:
def on_received_set_config(self, context, data: SetConfig):
key = data.key
value = data.value

if hasattr(node, 'config'):
if hasattr(node, ATT_CONFIG):
config = node.config
if hasattr(config, key):
setattr(node.config, key, value)
Expand All @@ -310,6 +314,31 @@ def on_received_describe_protocol(self, context):
desc = ProtocolDescription(data=protocol, meta=basic_protocol)
context.write('protocol_description', desc)

def on_received_describe_config(self, context):
K = type(node)
if hasattr(K, '__annotations__') and ATT_CONFIG in K.__annotations__:
config_type = K.__annotations__[ATT_CONFIG]
config_current = getattr(node, ATT_CONFIG)
else:
@dataclass
class NoConfig:
pass

config_type = NoConfig
config_current = NoConfig()
desc = ConfigDescription(config=config_type, current=config_current)
context.write('config_description', desc, with_schema=True)

def on_received_describe_node(self, context):
desc = NodeDescription(node.__doc__)

context.write('node_description', desc, with_schema=True)

def on_received_describe_build(self, context):
desc = BuildDescription()

context.write('build_description', desc, with_schema=True)

wrapper = Wrapper()

for stream, parsed in inputs([fi, mi]):
Expand Down

0 comments on commit 7b73f88

Please sign in to comment.