Ladon Attachments

From Ladon Webservice

Jump to: navigation, search

Attachments

Ladon for Python supports attachments from version 0.6.1 using multipart messages. The ladonize decorator lets you declare both inbound and outbound binary octet-streams using a new very simple type called attachment.

There is no limitation to how many attachments you can send and recieve per method invocation, this means that you can declare methods that send or recieve lists of attachments. You can also structure your attachments to be contained in LadonType structures - in short: you can use the new attachment type exactly the same way as you use primitives.

See the wikipedia entry on attachments for JSON-WSP to get an idea of how the it works in the JSON-WSP protocol.

Unfortunately the SOAP interface will not support attachments in version 0.6.1 - Development focus has been on the framework and making a single interface (JSON-WSP) work with attachments and the SOAP/WSDL specifications in general seem to have very bad attachment support. In fact trying to retrieve WSDL for a Ladon service having attachments will fail. I hope to fix this problem as fast as possible.

The Python JSON-WSP client that comes with the Ladon framework has build-in support for attachments and the JSON-WSP client for javascript will have upstream attachment support.

Streams

If you are wondering if large outbound or inbound attachments will allocate lots of memory on either the server or client the answer is no. Attachments are handled as streams - so as multipart messages arrive at the server or client they are read in chunks and stored on the filesystem.

When a message reaches end-of-stream all attachments have been placed on the filesystem with memory consumption never exeeding 30Kb per message. The attachments are then passed on to the method (server side) or user (client side) using open file-handles. From here it is up to the user/service developer how much memory to consume when reading from the file-handles - I recommend reading in chunks for safty sake.

Sample Service with attachments

# -*- coding: utf-8 -*-
from ladon.ladonizer import ladonize
from ladon.types.ladontype import LadonType
from ladon.types.attachment import attachment
from ladon.compat import PORTABLE_STRING
 
class File(LadonType):
  data = attachment
  name = PORTABLE_STRING
 
class TransferService(object):
 
  @ladonize(File,rtype=int)
  def upload(self,incomming):
    f = open(incomming.name,'wb')
    # Attachments are passed to service methods as attachment objects
    # the attachment object is a file-like object, so data can be read directly
    # from it.
    f.write(incomming.data.read())
    f.close()
    return 1
 
  @ladonize(PORTABLE_STRING, rtype=File)
  def download(self,name):
    response = File()
    response.name = name
    # To send an attachment back to the client create an instance of attachment
    # with an open binary file handle to the file that holds the attachment data.
    response.data = attachment(open(name,'rb'))
    return response

Send and recieve attachments using Ladon's JSON-WSP client

It is pretty straight forward to send and recieve data as attachments with the JSON-WSP client. Study this example and it's comments to get an idea how it works.

from ladon.clients.jsonwsp import JSONWSPClient
import pprint
 
# Create a client object pointing to your JSON-WSP service description
cli = JSONWSPClient('http://localhost:8888/TransferService/jsonwsp/description')
 
# Use the upload method exposed from TransferService
# There are 2 ways to bind data to an attachment argument/attribute
 
# 1. Use the @-notation. If a @ is found in front of the path to an existing file it will be 
# bound to the request as an attachment, example:
jsonwsp_response=cli.upload(incomming={'data': '@mylocalfile.py' , 'name': 'remotefile.py'})
# Print the response
pprint.pprint(jsonwsp_response.response_dict)
 
# 2. Use file-like objects. If a file-like object is found it will also be bound to the request as an
# attachment, example:
jsonwsp_response=cli.upload(incomming={'data': open('mylocalfile.py','rb') , 'name': 'remotefile.py'})
# binary file-handles are expected as attachments are binary octet-streams (default in python 2)
# Print the response
pprint.pprint(jsonwsp_response.response_dict)
 
# Download a file
jsonwsp_response = cli.download(name='remotefile.py')
# Print the response
pprint.pprint(jsonwsp_response.response_dict)
# Attachments are handled by the JSONWSPClient and placed in temporary files
# The result dict's attachments will reference to these files with open file handles
# When the response_object is garbage collected all handles are closed automatically
# and temporary files deleted.
 
# Print the content of the downloaded file to stdout
print(jsonwsp_response.response_dict['result']['data'].read())