Owyl File format

87 views
Skip to first unread message

glennpierce

unread,
Jun 2, 2011, 3:59:32 PM6/2/11
to Owyl-discuss
Hi I am currently using owyl to implement an intelligence response
system.
I need to really implement the a file format for the behaviour tree
and would
like to do this in a generic way. I'm probably find with xml but the
format is not that important to me.

I wondered if you had any idea how to go about it.
Mainly how we get instances of our behaviour functions / trees from
reading the file format.

Any advice would be appreciated .

Thanks

Glenn

David Eyk

unread,
Jun 2, 2011, 7:41:07 PM6/2/11
to Owyl-discuss
Glenn,

Owyl definitely isn't implemented in an easily-serializable fashion!
To be honest, the best "serialization" format is python itself. You
could probably do something fancy with `exec` and a context that
contains all the functions and decorators, but dynamic imports and a
module structure convention could work just as well, with less magic.

That being said, it's not *impossible* to imagine translating a
serialization format into an Owyl tree. You could use an event-driven
parser (e.g. SAX, if XML is your thing) to build a string containing
the tree definition, then `exec` it. That might be the easiest
approach.

Really, XML does seem like the best format for serializing a behavior
tree. It's got a great tool ecosystem, and the tag/attribute/text tree
structure maps well to the domain, without undue ambiguity (as opposed
to, say, YAML or JSON). I'm actually currently working on a behavior
tree system (not Owyl) that uses XML as a primary serialization, and
I'm very happy with the ease of translation.

I'm glad to hear you're using Owyl. The project is in a state of
disarray at the moment--I really should polish up and push out another
release. The master branch on GitHub should be stable--all the tests
pass, at least. :) The major new feature post 0.3 is mutable trees
using behavior queues, but if you don't need that, then I suppose the
0.3 release should be fine.

Let me know if you run into any trouble, or see anything that needs
improvement!

David

Glenn Pierce

unread,
Jun 3, 2011, 3:46:16 PM6/3/11
to owyl-d...@googlegroups.com
I did think of making my leaf items callable objects.
That way I could inherit a standard interface for serialisation etc.
This would help creating the objects from a file format.

However, I never could work out how to do that because I cant
apply your decorators like @taskmethod to the

def __call__(self, *args, **kargs)


In the end I just provide an Action method to all my objects.
When constructing the behaviour trees I do something like

owyl.sequence( SpokenResponse().Action(), ..)

which isn't as nice as just passing callables.

If you can think of a way of passing and callables instead of just
functions and methods
that maybe a nice feature.

Thanks

> --
> You received this message because you are subscribed to the Google Groups "Owyl-discuss" group.
> To post to this group, send email to owyl-d...@googlegroups.com.
> To unsubscribe from this group, send email to owyl-discuss...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/owyl-discuss?hl=en.
>
>

David Eyk

unread,
Jun 3, 2011, 7:21:18 PM6/3/11
to Owyl-discuss
Regarding __call__, you're correct that it wouldn't be compatible with
owyl.taskmethod(), but you should be able to use this in your tree
definition:

class MyCallableClass(object):
def __call__(...):
...

owyl.task(MyCallableClass())()

Or, perhaps more clearly...

instance = MyCallableClass()

tree = owyl.sequence(
owyl.task(instance)(),
...
)

I hope that makes sense. You don't have a callable object until you
instantiate the class. You then need to wrap the instance in task(),
and call the resulting function to get a generator. Whew. This is the
same thing you're doing with functions and methods, just you can use
declarative decorators with them, because functions and methods are
already callable.

You could probably do something cutesy and declarative with a
metaclass or class decorator, come to think of it, but I'll leave that
as an exercise to the reader.

However, this doesn't get you any closer to easy serialization. In
fact, serializing a tree that you build in Python would probably be
the hardest way to go, because you really don't have good
introspection into nested active generators, which is what an owyl
tree is built out of. It might be possible, but you might end up
having to hack Python's C interface to do it. Just speculating here--
I'm way beyond my knowledge on this.

Even then, you still have the problem of deserialization, which is
what I was talking about previously. This is a much more tractable
problem. So long as your XML schema (or whatever) is rich enough to
describe tree nodes, arguments, and children, you can construct a tree
out of it without much sweat.

Again, I hope that's helpful.

David

On Jun 3, 12:46 pm, Glenn Pierce <glennpie...@gmail.com> wrote:
> I did think of making my leaf items callable objects.
> That way I could inherit a standard interface for serialisation etc.
> This would help creating the objects from a file format.
>
> However, I never could work out how to do that because I cant
> apply your decorators like @taskmethod to the
>
> def __call__(self, *args, **kargs)
>
> In the end I just provide an Action method to all my objects.
> When constructing the behaviour trees I do something like
>
> owyl.sequence( SpokenResponse().Action(), ..)
>
> which isn't as nice as just passing callables.
>
> If you can think of a way of passing and callables instead of just
> functions and methods
> that maybe a nice feature.
>
> Thanks
>

Glenn Pierce

unread,
Jun 4, 2011, 4:16:18 AM6/4/11
to owyl-d...@googlegroups.com
On 4 June 2011 00:21, David Eyk <davi...@gmail.com> wrote:
> Regarding __call__, you're correct that it wouldn't be compatible with
> owyl.taskmethod(), but you should be able to use this in your tree
> definition:
>
> class MyCallableClass(object):
> def __call__(...):
> ...
>
> owyl.task(MyCallableClass())()
>
> Or, perhaps more clearly...
>
> instance = MyCallableClass()
>
> tree = owyl.sequence(
> owyl.task(instance)(),
> ...
> )
>
> I hope that makes sense. You don't have a callable object until you
> instantiate the class. You then need to wrap the instance in task(),
> and call the resulting function to get a generator. Whew. This is the
> same thing you're doing with functions and methods, just you can use
> declarative decorators with them, because functions and methods are
> already callable.
>

Ah Thanks
I should have noticed wrap. Sorry


class SpokenResponse(object):

def __init_(self):
super(SpokenResponse, self).__init__()
self.__name__ = "SpokenResponse"

def __call__(self, *args, **kwargs):
yield self.Action(*args, **kwargs)

@owyl.taskmethod
def Action(self, *args, **kwargs):
print "Spoken Response"
yield True

instance = SpokenResponse()

tree = owyl.sequence(
owyl.task(instance)(),
owyl.task(instance)(),
owyl.task(instance)(),
owyl.fail()
)

v = owyl.visit(tree)
[x for x in v]

I get
line 59, in task
initTask.__name__ = func.__name__
AttributeError: 'SpokenResponse' object has no attribute '__name__'

I guessing my callables have to have __name__ and __doc__ defined.
But that didn't seem to help above.


> You could probably do something cutesy and declarative with a
> metaclass or class decorator, come to think of it, but I'll leave that
> as an exercise to the reader.
>
> However, this doesn't get you any closer to easy serialization. In
> fact, serializing a tree that you build in Python would probably be
> the hardest way to go, because you really don't have good
> introspection into nested active generators, which is what an owyl
> tree is built out of. It might be possible, but you might end up
> having to hack Python's C interface to do it. Just speculating here--
> I'm way beyond my knowledge on this.
>
> Even then, you still have the problem of deserialization, which is
> what I was talking about previously. This is a much more tractable
> problem. So long as your XML schema (or whatever) is rich enough to
> describe tree nodes, arguments, and children, you can construct a tree
> out of it without much sweat.
>
> Again, I hope that's helpful.
>


Thanks David. That does all make sense, fortunately I only need deserialisation.


And thanks for the code.

David Eyk

unread,
Jun 4, 2011, 12:09:09 PM6/4/11
to Owyl-discuss
Hmm, __name__ is one of those funny special attributes. Looks like
your assignment in __init__ is getting eaten somewhere under the hood.
This works, though:

class SpokenResponse(object):
...

@property
def __name__(self):
return self.__class__.__name__

Doesn't seem like it should work, but it does. Now you don't need to
set __name__ in your __init__.

However, I might also add try/except blocks to the various @task
decorators, to account for this. For now, use the property.

David


On Jun 4, 1:16 am, Glenn Pierce <glennpie...@gmail.com> wrote:

Glenn Pierce

unread,
Jun 4, 2011, 1:25:27 PM6/4/11
to owyl-d...@googlegroups.com
Thanks

Glenn Pierce

unread,
Jun 6, 2011, 4:36:49 AM6/6/11
to owyl-d...@googlegroups.com
Sorry to bother you again David

From the test following I get the error.
child = current.next()
AttributeError: 'function' object has no attribute 'next'

I tried experimenting with wrap versus task etc with no luck.

Have you any ideas ?
Thanks.

class SpokenResponse(object):

def __init_(self):
super(SpokenResponse, self).__init__()

@property
def __name__(self):
return self.__class__.__name__

def __call__(self, *args, **kwargs):
return self.Action(*args, **kwargs)

@owyl.taskmethod
def Action(self, *args, **kwargs):
print "Spoken Response"
yield True

instance = SpokenResponse()

tree = owyl.sequence(
owyl.task(instance)(),
owyl.task(instance)(),

owyl.fail()
)

v = owyl.visit(tree)
[x for x in v]

David Eyk

unread,
Jun 6, 2011, 9:41:07 AM6/6/11
to owyl-d...@googlegroups.com
I think the problem is that you're wrapping your Action method w/ @taskmethod. The @task- decorators wrap their functions/methods in yet another function that returns the original function. This is necessary for the tree to get set up correctly, but it plays havoc with any expectations you might have that your wrapped functions are going to operate like you expect. :)

Again, remove the decorator from Action, and it should work, since you're already wrapping the instance itself to create your task. At this point, you could also merge the body of Action into your __call__, to save yourself an extra function call.

David

Glenn Pierce

unread,
Jun 6, 2011, 4:03:59 PM6/6/11
to owyl-d...@googlegroups.com
That works great, thanks.
I forgot about the decorator.
Reply all
Reply to author
Forward
0 new messages