diff --git a/pyogp/lib/base/OGPLogin.py b/pyogp/lib/base/OGPLogin.py index 7d083fe..9713eef 100644 --- a/pyogp/lib/base/OGPLogin.py +++ b/pyogp/lib/base/OGPLogin.py @@ -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 diff --git a/pyogp/lib/base/agent.py b/pyogp/lib/base/agent.py index 5d69f9a..7c8c797 100644 --- a/pyogp/lib/base/agent.py +++ b/pyogp/lib/base/agent.py @@ -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) - - - \ No newline at end of file diff --git a/pyogp/lib/base/agentdomain.py b/pyogp/lib/base/agentdomain.py index 78c9e7f..de03db2 100644 --- a/pyogp/lib/base/agentdomain.py +++ b/pyogp/lib/base/agentdomain.py @@ -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 diff --git a/pyogp/lib/base/api.py b/pyogp/lib/base/api.py new file mode 100644 index 0000000..b1778ed --- /dev/null +++ b/pyogp/lib/base/api.py @@ -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 + + + And this again a seed capability: + >>> agent.agentdomain.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 + + + 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) + + + """ + + \ No newline at end of file diff --git a/pyogp/lib/base/avatar.py b/pyogp/lib/base/avatar.py new file mode 100644 index 0000000..1f8ebec --- /dev/null +++ b/pyogp/lib/base/avatar.py @@ -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 \ No newline at end of file diff --git a/pyogp/lib/base/caps.py b/pyogp/lib/base/caps.py index f463868..a848dc9 100644 --- a/pyogp/lib/base/caps.py +++ b/pyogp/lib/base/caps.py @@ -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()) diff --git a/pyogp/lib/base/configure.zcml b/pyogp/lib/base/configure.zcml new file mode 100644 index 0000000..65a51ae --- /dev/null +++ b/pyogp/lib/base/configure.zcml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/pyogp/lib/base/credentials.py b/pyogp/lib/base/credentials.py index c1645bd..c226334 100644 --- a/pyogp/lib/base/credentials.py +++ b/pyogp/lib/base/credentials.py @@ -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) diff --git a/pyogp/lib/base/data_packer.py b/pyogp/lib/base/data_packer.py index 602f7e7..c15aa5f 100644 --- a/pyogp/lib/base/data_packer.py +++ b/pyogp/lib/base/data_packer.py @@ -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 diff --git a/pyogp/lib/base/example.py b/pyogp/lib/base/example.py index 8795523..af234c4 100644 --- a/pyogp/lib/base/example.py +++ b/pyogp/lib/base/example.py @@ -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" diff --git a/pyogp/lib/base/interfaces.py b/pyogp/lib/base/interfaces.py index cac5af0..2667642 100644 --- a/pyogp/lib/base/interfaces.py +++ b/pyogp/lib/base/interfaces.py @@ -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): diff --git a/pyogp/lib/base/message_template.py b/pyogp/lib/base/message_template.py index 47e58ae..b7968ec 100644 --- a/pyogp/lib/base/message_template.py +++ b/pyogp/lib/base/message_template.py @@ -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 diff --git a/pyogp/lib/base/message_template_builder.py b/pyogp/lib/base/message_template_builder.py index 985159b..b57a774 100644 --- a/pyogp/lib/base/message_template_builder.py +++ b/pyogp/lib/base/message_template_builder.py @@ -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. """ diff --git a/pyogp/lib/base/message_template_dict.py b/pyogp/lib/base/message_template_dict.py index bf0c6c2..6987978 100644 --- a/pyogp/lib/base/message_template_dict.py +++ b/pyogp/lib/base/message_template_dict.py @@ -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') diff --git a/pyogp/lib/base/message_template_parser.py b/pyogp/lib/base/message_template_parser.py index 6e64925..aa2b866 100644 --- a/pyogp/lib/base/message_template_parser.py +++ b/pyogp/lib/base/message_template_parser.py @@ -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') diff --git a/pyogp/lib/base/message_types.py b/pyogp/lib/base/message_types.py index 051fb9c..f58f945 100644 --- a/pyogp/lib/base/message_types.py +++ b/pyogp/lib/base/message_types.py @@ -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, \ diff --git a/pyogp/lib/base/regiondomain.py b/pyogp/lib/base/regiondomain.py index e1a3b97..06b668c 100644 --- a/pyogp/lib/base/regiondomain.py +++ b/pyogp/lib/base/regiondomain.py @@ -10,5 +10,8 @@ class Region(object): def __init__(self, uri): """initialize the region with the region uri""" self.uri = uri + self.details = {} + + \ No newline at end of file diff --git a/pyogp/lib/base/registration.py b/pyogp/lib/base/registration.py new file mode 100644 index 0000000..b4d4c2c --- /dev/null +++ b/pyogp/lib/base/registration.py @@ -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) \ No newline at end of file diff --git a/pyogp/lib/base/tests/login.txt b/pyogp/lib/base/tests/login.txt index 351f99c..d91c956 100644 --- a/pyogp/lib/base/tests/login.txt +++ b/pyogp/lib/base/tests/login.txt @@ -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. diff --git a/pyogp/lib/base/tests/testDocTests.py b/pyogp/lib/base/tests/testDocTests.py index aa9c8f9..9c60790 100644 --- a/pyogp/lib/base/tests/testDocTests.py +++ b/pyogp/lib/base/tests/testDocTests.py @@ -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 diff --git a/setup.py b/setup.py index 9b73f10..57ca1e6 100644 --- a/setup.py +++ b/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': [