2009-08-20 22:57:24 +00:00
"""
Contributors can be viewed at :
http : / / svn . secondlife . com / svn / linden / projects / 2008 / pyogp / lib / base / trunk / CONTRIBUTORS . txt
$ LicenseInfo : firstyear = 2008 & license = apachev2 $
Copyright 2009 , Linden Research , Inc .
Licensed under the Apache License , Version 2.0 .
You may obtain a copy of the License at :
http : / / www . apache . org / licenses / LICENSE - 2.0
or in
http : / / svn . secondlife . com / svn / linden / projects / 2008 / pyogp / lib / base / LICENSE . txt
$ / LicenseInfo $
"""
2008-08-28 22:16:33 +00:00
# std lib
2008-06-27 15:10:24 +00:00
import urllib2
2008-11-26 06:00:42 +00:00
from types import *
2009-09-04 05:00:00 +00:00
from logging import getLogger
2009-08-10 18:46:31 +00:00
import re
2008-08-28 22:16:33 +00:00
# related
2009-03-03 01:40:52 +00:00
# pyogp
from pyogp . lib . base . network . stdlib_client import StdLibClient , HTTPError
from pyogp . lib . base . exc import ResourceNotFound , ResourceError , DeserializerNotFound
from pyogp . lib . base . settings import Settings
2009-09-04 05:30:18 +00:00
from pyogp . lib . base . helpers import LLSDDeserializer , ListLLSDSerializer , DictLLSDSerializer
2008-07-19 14:21:18 +00:00
2008-11-26 06:00:42 +00:00
# initialize logging
2008-08-28 22:16:33 +00:00
logger = getLogger ( ' pyogp.lib.base.caps ' )
2008-06-27 15:10:24 +00:00
class Capability ( object ) :
2009-03-03 01:40:52 +00:00
""" models a capability
A capability is a web resource which enables functionality for a client
The seed capability is a special type , through which other capabilities
2009-04-27 22:48:01 +00:00
are procured
2009-03-03 01:40:52 +00:00
A capability in pyogp provides a GET and a POST method
Sample implementations : region . py
Tests : tests / caps . txt , tests / test_caps . py
"""
def __init__ ( self , name , public_url , restclient = None , settings = None ) :
""" initialize the capability """
# allow the settings to be passed in
# otherwise, grab the defaults
if settings != None :
self . settings = settings
else :
from pyogp . lib . base . settings import Settings
self . settings = Settings ( )
2008-11-26 06:00:42 +00:00
if restclient == None :
self . restclient = StdLibClient ( )
else :
self . restclient = restclient
2008-06-27 15:10:24 +00:00
self . name = name
2008-07-19 14:21:18 +00:00
self . public_url = public_url
2009-09-01 16:34:23 +00:00
#logger.debug('instantiated cap %s' %self)
2008-11-26 06:00:42 +00:00
2008-08-05 21:19:26 +00:00
def GET ( self , custom_headers = { } ) :
""" call this capability, return the parsed result """
2008-11-26 06:00:42 +00:00
2009-09-01 16:34:23 +00:00
if self . settings . ENABLE_CAPS_LOGGING : logger . debug ( ' %s : GETing %s ' % ( self . name , self . public_url ) )
2008-08-05 21:19:26 +00:00
try :
2008-11-26 06:00:42 +00:00
response = self . restclient . GET ( self . public_url )
2008-08-05 21:19:26 +00:00
except HTTPError , e :
2008-11-11 00:24:53 +00:00
if e . code == 404 :
2009-03-03 01:40:52 +00:00
raise ResourceNotFound ( self . public_url )
2008-11-11 00:24:53 +00:00
else :
2009-03-03 01:40:52 +00:00
raise ResourceError ( self . public_url , e . code , e . msg , e . fp . read ( ) , method = " GET " )
2008-11-26 06:00:42 +00:00
# now deserialize the data again
2008-08-05 21:19:26 +00:00
content_type_charset = response . headers [ ' Content-Type ' ]
content_type = content_type_charset . split ( " ; " ) [ 0 ] # remove the charset part
2008-08-28 22:16:33 +00:00
2008-11-26 06:00:42 +00:00
# ToDo: write a generic serializer/deserializer
if ( content_type == ' application/llsd+xml ' ) :
2009-01-29 06:58:03 +00:00
deserializer = LLSDDeserializer ( )
2008-11-26 06:00:42 +00:00
else :
2009-03-03 01:40:52 +00:00
raise DeserializerNotFound ( content_type )
2009-09-01 16:34:23 +00:00
2009-03-03 01:40:52 +00:00
data = deserializer . deserialize ( response . body )
2009-09-01 16:34:23 +00:00
if self . settings . LOG_VERBOSE and self . settings . ENABLE_CAPS_LLSD_LOGGING : logger . debug ( ' Received the following llsd from %s : %s ' % ( self . public_url , response . body . strip ( ) ) )
if self . settings . ENABLE_CAPS_LOGGING : logger . debug ( ' Get of cap %s response is: %s ' % ( self . public_url , data ) )
2009-03-03 01:40:52 +00:00
2008-09-16 00:03:48 +00:00
return data
2008-08-05 21:19:26 +00:00
2008-07-24 19:25:02 +00:00
def POST ( self , payload , custom_headers = { } ) :
2008-06-27 15:10:24 +00:00
""" call this capability, return the parsed result """
2008-11-26 06:00:42 +00:00
2009-09-01 16:34:23 +00:00
if self . settings . ENABLE_CAPS_LOGGING : logger . debug ( ' Sending to cap %s the following payload: %s ' % ( self . public_url , payload ) )
2008-11-26 06:00:42 +00:00
2008-07-19 14:21:18 +00:00
# serialize the data
2008-11-26 06:00:42 +00:00
if ( type ( payload ) is ListType ) :
serializer = ListLLSDSerializer ( payload )
elif ( type ( payload ) is DictType ) :
serializer = DictLLSDSerializer ( payload )
else :
raise DeserializerNotFound ( type ( payload ) )
2008-07-19 14:21:18 +00:00
content_type = serializer . content_type
serialized_payload = serializer . serialize ( )
2008-11-26 06:00:42 +00:00
2009-09-01 16:34:23 +00:00
if self . settings . LOG_VERBOSE and self . settings . ENABLE_CAPS_LLSD_LOGGING : logger . debug ( ' Posting the following payload to %s : %s ' % ( self . public_url , serialized_payload ) )
2009-03-03 01:40:52 +00:00
2008-07-19 14:21:18 +00:00
headers = { " Content-type " : content_type }
headers . update ( custom_headers ) # give the user the ability to add headers
2008-10-01 07:37:34 +00:00
2008-06-27 15:10:24 +00:00
try :
2008-11-26 06:00:42 +00:00
response = self . restclient . POST ( self . public_url , serialized_payload , headers = headers )
2008-07-24 19:25:02 +00:00
except HTTPError , e :
2008-10-01 07:37:34 +00:00
if e . code == 404 :
2009-03-03 01:40:52 +00:00
raise ResourceNotFound ( self . public_url )
2008-10-01 07:37:34 +00:00
else :
2009-03-03 01:40:52 +00:00
raise ResourceError ( self . public_url , e . code , e . msg , e . fp . read ( ) , method = " POST " )
2009-08-10 18:46:31 +00:00
return self . _response_handler ( response )
2009-09-01 16:34:23 +00:00
2009-10-17 00:24:02 +00:00
def POST_CUSTOM ( self , headers , payload ) :
"""
call this capability with custom header and payload , useful for posting
non - LLSD data such as LSLs or notecards
"""
2009-08-10 18:46:31 +00:00
try :
response = self . restclient . POST ( self . public_url ,
payload , headers = headers )
except HTTPError , e :
if e . code == 404 :
raise ResourceNotFound ( self . public_url )
else :
raise ResourceError ( self . public_url , e . code , e . msg ,
e . fp . read ( ) , method = " POST " )
return self . _response_handler ( response )
2009-09-01 16:34:23 +00:00
2009-08-10 18:46:31 +00:00
def _response_handler ( self , response ) :
2008-07-19 14:21:18 +00:00
# now deserialize the data again, we ask for a utility with the content type
# as the name
2008-07-24 19:25:02 +00:00
content_type_charset = response . headers [ ' Content-Type ' ]
2008-07-19 14:21:18 +00:00
content_type = content_type_charset . split ( " ; " ) [ 0 ] # remove the charset part
2008-09-16 00:03:48 +00:00
2009-10-17 00:24:02 +00:00
pattern = re . compile ( ' < \ ?xml \ sversion= " 1.0 " \ s \ ?><llsd>.*?</llsd>.* ' )
2008-11-26 06:00:42 +00:00
# ToDo: write a generic serializer/deserializer
2009-08-10 18:46:31 +00:00
if ( content_type == ' application/llsd+xml ' ) or \
( content_type == ' application/xml ' ) or \
( content_type == ' text/html ' and \
pattern . match ( response . body ) != None ) :
2009-01-29 06:58:03 +00:00
deserializer = LLSDDeserializer ( )
2008-11-26 06:00:42 +00:00
else :
2009-08-10 18:46:31 +00:00
print response
2009-03-03 01:40:52 +00:00
raise DeserializerNotFound ( content_type )
2009-09-01 16:34:23 +00:00
2009-03-03 01:40:52 +00:00
data = deserializer . deserialize ( response . body )
2009-09-01 16:34:23 +00:00
if self . settings . ENABLE_CAPS_LLSD_LOGGING : logger . debug ( ' Received the following llsd from %s : %s ' % ( self . public_url , response . body . strip ( ) ) )
if self . settings . ENABLE_CAPS_LOGGING : logger . debug ( ' Post to cap %s response is: %s ' % ( self . public_url , data ) )
2008-11-26 06:00:42 +00:00
2008-09-16 00:03:48 +00:00
return data
2008-11-26 06:00:42 +00:00
2009-08-10 18:46:31 +00:00
2008-07-14 18:42:24 +00:00
def __repr__ ( self ) :
2008-08-28 22:16:33 +00:00
return " <Capability ' %s ' for %s > " % ( self . name , self . public_url )
2009-03-03 01:40:52 +00:00
2008-06-27 15:10:24 +00:00
class SeedCapability ( Capability ) :
2008-11-26 06:00:42 +00:00
""" a seed capability which is able to retrieve other capabilities """
2008-06-27 15:10:24 +00:00
def get ( self , names = [ ] ) :
2008-09-24 04:49:43 +00:00
""" if this is a seed cap we can retrieve other caps here
Note : changing post key from ' caps ' to ' capabilities ' for OGP spec updates in Draft 3
see http : / / wiki . secondlife . com / wiki / OGP_Base_Draft_3 #Seed_Capability_.28Resource_Class.29
"""
payload = { ' capabilities ' : names }
parsed_result = self . POST ( payload ) [ ' capabilities ' ]
2009-03-03 01:40:52 +00:00
2008-06-27 15:10:24 +00:00
caps = { }
for name in names :
# TODO: some caps might be seed caps, how do we know?
2008-11-26 06:00:42 +00:00
caps [ name ] = Capability ( name , parsed_result [ name ] , self . restclient )
2008-06-27 15:10:24 +00:00
return caps
2008-11-26 06:00:42 +00:00
2008-07-14 18:42:24 +00:00
def __repr__ ( self ) :
2008-07-19 14:21:18 +00:00
return " <SeedCapability for %s > " % self . public_url
2009-04-27 22:48:01 +00:00