merged branch mrtopf-grokified into trunk. For making it work I resolved some obvious conflicts and changed the message parser classes to new style classes.
This commit is contained in:
@@ -2,7 +2,7 @@ from pyogp.lib.base.credentials import PlainPasswordCredential
|
||||
from pyogp.lib.base.agentdomain import AgentDomain
|
||||
from pyogp.lib.base.regiondomain import Region
|
||||
|
||||
from pyogp.lib.base.interfaces import IPlaceAvatarAdapter
|
||||
from pyogp.lib.base.interfaces import IPlaceAvatar
|
||||
|
||||
import getpass, sys
|
||||
from optparse import OptionParser
|
||||
@@ -43,7 +43,7 @@ class OGPLogin(object):
|
||||
def placeAvatarCap(self):
|
||||
""" actually gets the place_avatar cap and posts to it """
|
||||
region = Region(self.regionuri)
|
||||
place = IPlaceAvatarAdapter(self.agent)
|
||||
place = IPlaceAvatar(self.agent)
|
||||
self.avatar = place(region)
|
||||
return self.avatar
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from zope.interface import implements
|
||||
from zope.component import adapts
|
||||
|
||||
from interfaces import IAgent, IPlaceAvatarAdapter
|
||||
from interfaces import IAgent
|
||||
|
||||
import grokcore.component as grok
|
||||
|
||||
class Agent(object):
|
||||
"""an OGP agent"""
|
||||
@@ -14,33 +16,5 @@ class Agent(object):
|
||||
|
||||
|
||||
|
||||
class PlaceAvatarAdapter(object):
|
||||
"""handles placing an avatar for an agent object"""
|
||||
implements(IPlaceAvatarAdapter)
|
||||
adapts(IAgent)
|
||||
|
||||
def __init__(self, agent):
|
||||
"""initialize this adapter"""
|
||||
self.agent = agent
|
||||
|
||||
# let's retrieve the cap we need
|
||||
self.seed_cap = self.agent.agentdomain.seed_cap # ISeedCapability
|
||||
self.place_avatar_cap = self.seed_cap.get(['place_avatar'])['place_avatar']
|
||||
|
||||
def __call__(self, region):
|
||||
"""initiate the placing process"""
|
||||
region_uri = region.uri
|
||||
payload = {'region_url' : region_uri }
|
||||
result = self.place_avatar_cap(payload)
|
||||
|
||||
return result
|
||||
|
||||
# now we register this adapter so it can be used later:
|
||||
from zope.component import provideAdapter
|
||||
|
||||
# register adapters for the HTML node
|
||||
provideAdapter(PlaceAvatarAdapter)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
from agent import Agent
|
||||
from interfaces import ISerialization
|
||||
from caps import SeedCapability
|
||||
|
||||
import urllib2
|
||||
|
||||
from zope.interface import implements
|
||||
import grokcore.component as grok
|
||||
|
||||
from indra.base import llsd
|
||||
|
||||
from interfaces import ICredentialSerializer, IPlaceAvatar, IAgentDomain
|
||||
|
||||
from agent import Agent
|
||||
from avatar import Avatar
|
||||
from caps import SeedCapability
|
||||
|
||||
# URL Opener for the agent domain login
|
||||
#
|
||||
@@ -12,6 +22,7 @@ class RedirectHandler(urllib2.HTTPRedirectHandler):
|
||||
def http_error_302(self, req, fp, code, msg, headers):
|
||||
#ignore the redirect, grabbing the seed cap url from the headers
|
||||
# TODO: add logging and error handling
|
||||
print "huhu"
|
||||
return headers['location']
|
||||
|
||||
|
||||
@@ -22,6 +33,8 @@ AgentDomainLoginOpener = urllib2.build_opener(RedirectHandler())
|
||||
class AgentDomain(object):
|
||||
"""an agent domain endpoint"""
|
||||
|
||||
implements(IAgentDomain)
|
||||
|
||||
def __init__(self,uri):
|
||||
"""initialize the agent domain endpoint"""
|
||||
self.uri = uri
|
||||
@@ -38,7 +51,11 @@ class AgentDomain(object):
|
||||
# TODO: add logging and error handling
|
||||
#
|
||||
request = urllib2.Request(self.uri,payload,headers)
|
||||
res = AgentDomainLoginOpener.open(request)
|
||||
try:
|
||||
res = AgentDomainLoginOpener.open(request)
|
||||
except urllib2.HTTPError,e:
|
||||
print e.read()
|
||||
raise
|
||||
if type(res)!=type(""):
|
||||
seed_cap_url_data = res.read() # it might be an addinfourl object
|
||||
seed_cap_url = llsd.parse(seed_cap_url_data)['agent_seed_capability']
|
||||
@@ -49,3 +66,26 @@ class AgentDomain(object):
|
||||
return Agent(self)
|
||||
|
||||
|
||||
class PlaceAvatar(grok.Adapter):
|
||||
"""handles placing an avatar for an agent object"""
|
||||
grok.implements(IPlaceAvatar)
|
||||
grok.context(IAgentDomain)
|
||||
|
||||
def __init__(self, context):
|
||||
"""initialize this adapter"""
|
||||
self.context = context
|
||||
|
||||
# let's retrieve the cap we need
|
||||
self.seed_cap = self.context.seed_cap # ISeedCapability
|
||||
self.place_avatar_cap = self.seed_cap.get(['place_avatar'])['place_avatar']
|
||||
|
||||
def __call__(self, region):
|
||||
"""initiate the placing process"""
|
||||
region_uri = region.uri
|
||||
payload = {'region_url' : region_uri }
|
||||
result = self.place_avatar_cap(payload)
|
||||
|
||||
region.details = result
|
||||
avatar = Avatar(region)
|
||||
|
||||
return avatar
|
||||
|
||||
76
pyogp/lib/base/api.py
Normal file
76
pyogp/lib/base/api.py
Normal file
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
High level API
|
||||
"""
|
||||
|
||||
from pyogp.lib.base.credentials import PlainPasswordCredential
|
||||
from pyogp.lib.base.agentdomain import AgentDomain
|
||||
from pyogp.lib.base.regiondomain import Region
|
||||
|
||||
from pyogp.lib.base.interfaces import IPlaceAvatar
|
||||
|
||||
### login methods
|
||||
def login_with_plainpassword(agentdomain_url, firstname, lastname, password):
|
||||
"""logs an agent into the agent domain and returns an agent handle
|
||||
|
||||
takes firstname, lastname and plain password and returns an agent object
|
||||
|
||||
Using it is simple:
|
||||
>>> agent = login_with_plainpassword("http://localhost:12345","Firstname","Lastname","secret")
|
||||
|
||||
Now this agent should contain an agentdomain object
|
||||
>>> agent.agentdomain
|
||||
<pyogp.lib.base.agentdomain.AgentDomain object at ...>
|
||||
|
||||
And this again a seed capability:
|
||||
>>> agent.agentdomain.seed_cap
|
||||
<SeedCapability for http://127.0.0.1:12345/seed_cap>
|
||||
|
||||
"""
|
||||
|
||||
credentials = PlainPasswordCredential(firstname, lastname, password)
|
||||
agentdomain = AgentDomain(agentdomain_url)
|
||||
agent = agentdomain.login(credentials)
|
||||
return agent
|
||||
|
||||
|
||||
### place avatar
|
||||
def place_avatar(agent, region_url):
|
||||
"""place an avatar on a region
|
||||
|
||||
Placing an avatar is simple. We just need an agent object and a region url.
|
||||
|
||||
We get an agent object via the login:
|
||||
>>> agent = login_with_plainpassword("http://localhost:12345","Firstname","Lastname","secret")
|
||||
|
||||
And now we can call it:
|
||||
>>> avatar = place_avatar(agent, "http://localhost:12345/region")
|
||||
|
||||
The avatar should now contain the region:
|
||||
>>> avatar.region
|
||||
<pyogp.lib.base.regiondomain.Region object at ...>
|
||||
|
||||
and this in turn the region details:
|
||||
>>> avatar.region.details
|
||||
{'sim_port': 12345, 'sim_ip': '127.0.0.1'}
|
||||
|
||||
"""
|
||||
place = IPlaceAvatar(agent.agentdomain)
|
||||
region = Region(region_url)
|
||||
avatar = place(region)
|
||||
|
||||
return avatar
|
||||
|
||||
def run_loop(avatar):
|
||||
"""run the UDP loop for the avatar
|
||||
|
||||
First we create one as seen above:
|
||||
>>> agent = login_with_plainpassword("http://localhost:12345","Firstname","Lastname","secret")
|
||||
>>> avatar = place_avatar(agent, "http://localhost:12345/region")
|
||||
|
||||
And now we can run the loop:
|
||||
>>> run_loop(avatar)
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
10
pyogp/lib/base/avatar.py
Normal file
10
pyogp/lib/base/avatar.py
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
class Avatar(object):
|
||||
"""an avatar - the agent representation in 3D on a region"""
|
||||
|
||||
def __init__(self, region):
|
||||
"""initialize the avatar with the actual region we are on
|
||||
|
||||
we need to instantiate the avatar after place_avatar
|
||||
"""
|
||||
self.region = region
|
||||
@@ -47,6 +47,8 @@ class Capability(object):
|
||||
deserializer = queryUtility(IDeserialization,name=content_type)
|
||||
if deserializer is None:
|
||||
# TODO: do better error handling here
|
||||
print "RESULT", result.read()
|
||||
print result.headers
|
||||
raise "deserialization for %s not supported" %(content_type)
|
||||
return deserializer.deserialize_string(result.read())
|
||||
|
||||
|
||||
13
pyogp/lib/base/configure.zcml
Normal file
13
pyogp/lib/base/configure.zcml
Normal file
@@ -0,0 +1,13 @@
|
||||
<configure
|
||||
xmlns="http://namespaces.zope.org/zope"
|
||||
xmlns:grok="http://namespaces.zope.org/grok">
|
||||
|
||||
<!-- do the meta configuration to make the ZCML directives available -->
|
||||
<include package="zope.component" file="meta.zcml" />
|
||||
<include package="grokcore.component" file="meta.zcml" />
|
||||
|
||||
|
||||
<!-- finally load my components which are based on grokcore.component -->
|
||||
<grok:grok package="pyogp.lib.base" />
|
||||
|
||||
</configure>
|
||||
@@ -3,6 +3,7 @@ from zope.component import adapts
|
||||
|
||||
from indra.base import llsd
|
||||
|
||||
import grokcore.component as grok
|
||||
|
||||
from interfaces import IPlainPasswordCredential, ISerialization
|
||||
|
||||
@@ -20,7 +21,7 @@ class PlainPasswordCredential(object):
|
||||
|
||||
# an adapter to serialize this to LLSD
|
||||
|
||||
class PlainPasswordLLSDSerializer(object):
|
||||
class PlainPasswordLLSDSerializer(grok.Adapter):
|
||||
"""converts a plain password credential to LLSD
|
||||
|
||||
Here is how you can use it:
|
||||
@@ -32,8 +33,8 @@ class PlainPasswordLLSDSerializer(object):
|
||||
'application/llsd+xml'
|
||||
"""
|
||||
|
||||
implements(ISerialization)
|
||||
adapts(IPlainPasswordCredential)
|
||||
grok.implements(ISerialization)
|
||||
grok.context(IPlainPasswordCredential)
|
||||
|
||||
def __init__(self, context):
|
||||
"""initialize this adapter by storing the context (the credential)"""
|
||||
@@ -56,8 +57,3 @@ class PlainPasswordLLSDSerializer(object):
|
||||
"""return HTTP headers needed here"""
|
||||
return "application/llsd+xml"
|
||||
|
||||
# now we register this adapter so it can be used later:
|
||||
from zope.component import provideAdapter
|
||||
|
||||
# register adapters for the HTML node
|
||||
provideAdapter(PlainPasswordLLSDSerializer)
|
||||
|
||||
@@ -2,7 +2,7 @@ import struct
|
||||
|
||||
from pyogp.lib.base.message_types import MsgType
|
||||
|
||||
class DataPacker():
|
||||
class DataPacker(object):
|
||||
def __init__(self):
|
||||
self.packer = {}
|
||||
self.packer[MsgType.MVT_VARIABLE] = self.__pack_string
|
||||
|
||||
@@ -2,7 +2,7 @@ from pyogp.lib.base.credentials import PlainPasswordCredential
|
||||
from pyogp.lib.base.agentdomain import AgentDomain
|
||||
from pyogp.lib.base.regiondomain import Region
|
||||
|
||||
from pyogp.lib.base.interfaces import IPlaceAvatarAdapter
|
||||
from pyogp.lib.base.interfaces import IPlaceAvatar
|
||||
|
||||
import getpass, sys
|
||||
from optparse import OptionParser
|
||||
@@ -31,7 +31,7 @@ class ExampleLogin(object):
|
||||
|
||||
print "logged in, we now have an agent: ", agent
|
||||
|
||||
place = IPlaceAvatarAdapter(agent)
|
||||
place = IPlaceAvatar(agentdomain)
|
||||
region = Region(options.regionuri)
|
||||
|
||||
print "now we try to place the avatar on a region"
|
||||
|
||||
@@ -40,6 +40,10 @@ class IAgent(Interface):
|
||||
|
||||
agentdomain = Attribute("""the agent domain endpoint""")
|
||||
|
||||
|
||||
class IAgentDomain(Interface):
|
||||
"""an agent domain"""
|
||||
seed_cap = Attribute("""the seed capability""")
|
||||
|
||||
class IRegion(Interface):
|
||||
"""a region endpoint"""
|
||||
@@ -53,7 +57,7 @@ class IAvatar(Interface):
|
||||
def establish_presence():
|
||||
"""for now it will do a loop to establish a presence on a region"""
|
||||
|
||||
class IPlaceAvatarAdapter(Interface):
|
||||
class IPlaceAvatar(Interface):
|
||||
"""adapts an agents to a method which can place an avatar on a region"""
|
||||
|
||||
def __call__(region):
|
||||
|
||||
@@ -38,7 +38,7 @@ myreversedictionary = makereversepacketdict()
|
||||
do with the packet payload, yet. """
|
||||
|
||||
#this probably needs to implement an interface so it can be serialized
|
||||
class MsgData():
|
||||
class MsgData(object):
|
||||
""" Used as a Message that is being created that will be
|
||||
serialized and sent. """
|
||||
def __init__(self, name):
|
||||
@@ -59,7 +59,7 @@ class MsgData():
|
||||
get_block(block_name).add_data(var_name, data, data_size)
|
||||
|
||||
#this probably needs to implement an interface so it can be serialized
|
||||
class MsgBlockData():
|
||||
class MsgBlockData(object):
|
||||
""" Used as a Message block that is being created that will be
|
||||
serialized and sent. """
|
||||
def __init__(self, name):
|
||||
@@ -80,7 +80,7 @@ class MsgBlockData():
|
||||
def add_data(self, var_name, data, data_size):
|
||||
self.get_variable(var_name).add_data(data, data_size)
|
||||
|
||||
class MsgVariableData():
|
||||
class MsgVariableData(object):
|
||||
""" Used as a Message Block variable that is being created that will be
|
||||
serialized and sent """
|
||||
def __init__(self, name, tp):
|
||||
@@ -97,13 +97,13 @@ class MsgVariableData():
|
||||
self.data = data
|
||||
self.size = data_size
|
||||
|
||||
class MessageTemplateVariable():
|
||||
class MessageTemplateVariable(object):
|
||||
def __init__(self, name, tp, size):
|
||||
self.name = name
|
||||
self.type = tp
|
||||
self.size = size
|
||||
|
||||
class MessageTemplateBlock():
|
||||
class MessageTemplateBlock(object):
|
||||
def __init__(self, name):
|
||||
self.variable_map = {}
|
||||
self.name = name
|
||||
@@ -119,7 +119,7 @@ class MessageTemplateBlock():
|
||||
def get_variable(self, name):
|
||||
return self.variable_map[name]
|
||||
|
||||
class MessageTemplate():
|
||||
class MessageTemplate(object):
|
||||
def __init__(self, name):
|
||||
self.block_map = {}
|
||||
#this is the function or object that will handle this type of message
|
||||
|
||||
@@ -8,7 +8,7 @@ from pyogp.lib.base.message_template import MsgData, MsgBlockData, \
|
||||
from pyogp.lib.base.message_types import MsgType, MsgBlockType, MsgFrequency, sizeof
|
||||
from pyogp.lib.base.data_packer import DataPacker
|
||||
|
||||
class MessageTemplateBuilder():
|
||||
class MessageTemplateBuilder(object):
|
||||
""" This class builds messages at its high level, that is, keeping
|
||||
that data in data structure form. A serializer should be used on
|
||||
the message produced by this so that it can be sent over a network. """
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from pyogp.lib.base.data import msg_tmpl
|
||||
from pyogp.lib.base.message_types import MsgFrequency
|
||||
|
||||
class TemplateDictionary():
|
||||
class TemplateDictionary(object):
|
||||
def __init__(self, template_list):
|
||||
if template_list == None:
|
||||
raise Exception('Template list null')
|
||||
|
||||
@@ -10,7 +10,7 @@ from pyogp.lib.base.data import msg_tmpl
|
||||
from pyogp.lib.base.message_types import MsgFrequency, MsgTrust, \
|
||||
MsgEncoding, MsgDeprecation, MsgBlockType, MsgType
|
||||
|
||||
class MessageTemplateParser():
|
||||
class MessageTemplateParser(object):
|
||||
def __init__(self, template_file):
|
||||
if template_file == None:
|
||||
raise Exception('Template file cannot be None')
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
class MsgBlockType():
|
||||
class MsgBlockType(object):
|
||||
MBT_SINGLE, \
|
||||
MBT_MULTIPLE, \
|
||||
MBT_VARIABLE = range(3)
|
||||
@@ -10,7 +10,7 @@ class MsgBlockType():
|
||||
#= '\x20'
|
||||
#= '\x10'
|
||||
#= '\x00'
|
||||
class PackFlags():
|
||||
class PackFlags(object):
|
||||
LL_ZERO_CODE_FLAG, \
|
||||
LL_RELIABLE_FLAG, \
|
||||
LL_RESENT_FLAG, \
|
||||
@@ -22,27 +22,27 @@ class PackFlags():
|
||||
#= '\xFF\xFF'
|
||||
#= '\xFF'
|
||||
#= ''
|
||||
class MsgFrequency():
|
||||
class MsgFrequency(object):
|
||||
FIXED_FREQUENCY_MESSAGE, \
|
||||
LOW_FREQUENCY_MESSAGE, \
|
||||
MEDIUM_FREQUENCY_MESSAGE, \
|
||||
HIGH_FREQUENCY_MESSAGE = range(4)
|
||||
|
||||
class MsgTrust():
|
||||
class MsgTrust(object):
|
||||
LL_TRUSTED, \
|
||||
LL_NOTRUST = range(2)
|
||||
|
||||
class MsgEncoding():
|
||||
class MsgEncoding(object):
|
||||
LL_UNENCODED, \
|
||||
LL_ZEROCODED = range(2)
|
||||
|
||||
class MsgDeprecation():
|
||||
class MsgDeprecation(object):
|
||||
LL_DEPRECATED, \
|
||||
LL_UDPDEPRECATED, \
|
||||
LL_NOTDEPRECATED = range(3)
|
||||
|
||||
#message variable types
|
||||
class MsgType():
|
||||
class MsgType(object):
|
||||
#these are variables that aren't used because they can't be added to the
|
||||
#builder
|
||||
#MVT_NULL, \
|
||||
|
||||
@@ -10,5 +10,8 @@ class Region(object):
|
||||
def __init__(self, uri):
|
||||
"""initialize the region with the region uri"""
|
||||
self.uri = uri
|
||||
self.details = {}
|
||||
|
||||
|
||||
|
||||
|
||||
6
pyogp/lib/base/registration.py
Normal file
6
pyogp/lib/base/registration.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from zope.configuration.xmlconfig import xmlconfig
|
||||
|
||||
def init():
|
||||
from pkg_resources import resource_stream
|
||||
fp = resource_stream(__name__, 'configure.zcml')
|
||||
xmlconfig(fp)
|
||||
@@ -4,7 +4,6 @@ Login
|
||||
>>> from pyogp.lib.base.credentials import PlainPasswordCredential
|
||||
>>> from pyogp.lib.base.agentdomain import AgentDomain
|
||||
>>> from pyogp.lib.base.regiondomain import Region
|
||||
>>> from pyogp.lib.base.regiondomain import Region
|
||||
|
||||
|
||||
|
||||
@@ -30,17 +29,17 @@ Now we want to place out avatar somewhere on a region. To do so we first need a
|
||||
>>> region = Region('http://localhost:12345/region')
|
||||
|
||||
Now we adapt the agent to the place avatar functionality like this:
|
||||
>>> from pyogp.lib.base.interfaces import IPlaceAvatarAdapter
|
||||
>>> place = IPlaceAvatarAdapter(agent)
|
||||
>>> from pyogp.lib.base.interfaces import IPlaceAvatar
|
||||
>>> place = IPlaceAvatar(agentdomain)
|
||||
|
||||
'place' now is an adapter which knows how to call the place_avatar capability. We can ask it to do it:
|
||||
>>> avatar = place(region)
|
||||
|
||||
The result is dummy right now and should contain a long dictionary with region info:
|
||||
>>> avatar['sim_ip']
|
||||
>>> avatar.region.details['sim_ip']
|
||||
'127.0.0.1'
|
||||
|
||||
>>> avatar['sim_port']
|
||||
>>> avatar.region.details['sim_port']
|
||||
12345
|
||||
|
||||
This needs to be worked on to be a region and not an avatar.
|
||||
|
||||
@@ -6,21 +6,27 @@ optionflags = doctest.REPORT_ONLY_FIRST_FAILURE | doctest.ELLIPSIS
|
||||
# setup functions
|
||||
|
||||
def setUp(self):
|
||||
print "ok"
|
||||
from pyogp.lib.base.registration import init
|
||||
init()
|
||||
|
||||
def tearDown(self):
|
||||
print "down"
|
||||
pass
|
||||
|
||||
def test_suite():
|
||||
suite = unittest.TestSuite()
|
||||
suite.addTest(
|
||||
doctest.DocFileSuite("login.txt","caps.txt",
|
||||
doctest.DocFileSuite(
|
||||
"login.txt",
|
||||
"caps.txt",
|
||||
package="pyogp.lib.base.tests",
|
||||
setUp = setUp,
|
||||
tearDown = tearDown,
|
||||
tearDown = tearDown,
|
||||
optionflags=optionflags,
|
||||
)
|
||||
)
|
||||
suite.addTest(doctest.DocTestSuite('pyogp.lib.base.caps', optionflags=optionflags))
|
||||
suite.addTest(doctest.DocTestSuite('pyogp.lib.base.credentials', optionflags=optionflags))
|
||||
suite.addTest(doctest.DocTestSuite('pyogp.lib.base.api', optionflags=optionflags))
|
||||
|
||||
|
||||
return suite
|
||||
|
||||
6
setup.py
6
setup.py
@@ -24,11 +24,13 @@ setup(name='pyogp.lib.base',
|
||||
zip_safe=False,
|
||||
install_requires=[
|
||||
'setuptools',
|
||||
'zope.interface',
|
||||
'zope.component',
|
||||
# -*- Extra requirements: -*-
|
||||
'zope.interface',
|
||||
'zope.component [zcml]',
|
||||
'webob',
|
||||
'wsgiref',
|
||||
'grokcore.component',
|
||||
|
||||
],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
|
||||
Reference in New Issue
Block a user