Do I have to use "def application" with the wsgi_module? - python

So I installed python on my server and I'm using the wsgi_module with apache.
Do all my python programs have to use this format:
def application(environ, start_response):
headers = []
headers.append(('Content-Type', 'text/plain'))
write = start_response('200 OK', headers)
input = environ['wsgi.input']
output = cStringIO.StringIO()
print >> output, "test"
output.write(input.read(int(environ.get('CONTENT_LENGTH', '0'))))
return [output.getvalue()]
Is there anyway to set it up so I can write python scripts and just have:
print "test"

Yes, your script certainly need to have callable (not necessarily a function, it can also be a class with __call__ method defined) that accepts environ and *start_response* arguments as it is a part of wsgi standard and that is the way how it works.
The way you'd like to write scripts in PHP-style like "print something" doesn't work. There's a currently dead initiative called initially PSP (Python Server Pages) that mixed up code and templates, but it couldn't gain popularity as it's certainly not a Python-way of writing applications.

Related

Python: WSGI crashing with no error

I'm currently developing a web service in WSGI, but the script crashes on the line where I am executing my query. I'm using the exact same code as other working web services, and even simplified my query for testing purposes, but to no avail.
The real problem is that while I can manually print stuff to the error_log specified in my VirtualHost, there is no log for the error that occurs when the script crashes.
All I know now is that the print before the line is written to the log, but the print after isn't. How can I print the error to the log and get to the root of my problem?
The code (simplified a bit):
webservice.wsgi:
def application(environ, start_response):
ENV = environ.get('APPLICATION_ENV', 'DEV')
connector = ConnectorObj(confParams['dbname'], confParams['host'], confParams['port'], confParams['user'], confParams['password'])
method = environ.get('REQUEST_METHOD', '')
if (method == 'POST'):
content_body = json.loads(request_body)
han = HandlerObj(connector)
res = han.getBld()
start_response('200 OK', [('content-type', 'application/json; charset=utf-8'), ('Access-Control-Allow-Origin', '*')])
return(res)
getBld:
def getBld(self):
print "execute query"
self.cur.execute("""
SELECT * FROM adr.bld
""")
print "after executing query"
After doing the post call, I can see that "execute query" is being printed to the error_log, but then it just crashes and doesn't get to "after executing query".
Again, I'm not asking what is wrong with my actual code (I would have to provide much more in order to make sense of it), but simply how I can get an error trace somehow so I can start to debug it myself..
It depends on the database you use. If it is some open source I would recommend taking the source codes and compiling it with enabled debug mode which will result DB to create log file for itself which can point to the error.

python django soaplib response with classmodel issue

I run a soap server in django.
Is it possible to create a soap method that returns a soaplib classmodel instance without <{method name}Response><{method name}Result> tags?
For example, here is a part of my soap server code:
# -*- coding: cp1254 -*-
from soaplib.core.service import rpc, DefinitionBase, soap
from soaplib.core.model.primitive import String, Integer, Boolean
from soaplib.core.model.clazz import Array, ClassModel
from soaplib.core import Application
from soaplib.core.server.wsgi import Application as WSGIApplication
from soaplib.core.model.binary import Attachment
class documentResponse(ClassModel):
__namespace__ = ""
msg = String
hash = String
class MyService(DefinitionBase):
__service_interface__ = "MyService"
__port_types__ = ["MyServicePortType"]
#soap(String, Attachment, String ,_returns=documentResponse,_faults=(MyServiceFaultMessage,) , _port_type="MyServicePortType" )
def sendDocument(self, fileName, binaryData, hash ):
binaryData.file_name = fileName
binaryData.save_to_file()
resp = documentResponse()
resp.msg = "Saved"
resp.hash = hash
return resp
and it responses like that:
<senv:Body>
<tns:sendDocumentResponse>
<tns:sendDocumentResult>
<hash>14a95636ddcf022fa2593c69af1a02f6</hash>
<msg>Saved</msg>
</tns:sendDocumentResult>
</tns:sendDocumentResponse>
</senv:Body>
But i need a response like this:
<senv:Body>
<ns3:documentResponse>
<hash>A694EFB083E81568A66B96FC90EEBACE</hash>
<msg>Saved</msg>
</ns3:documentResponse>
</senv:Body>
What kind of configurations should i make in order to get that second response i mentioned above ?
Thanks in advance.
I haven't used Python's SoapLib yet, but had the same problem while using .NET soap libs. Just for reference, in .NET this is done using the following decorator:
[SoapDocumentMethod(ParameterStyle=SoapParameterStyle.Bare)]
I've looked in the soaplib source, but it seems it doesn't have a similar decorator. The closest thing I've found is the _style property. As seen from the code https://github.com/soaplib/soaplib/blob/master/src/soaplib/core/service.py#L124 - when using
#soap(..., _style='document')
it doesn't append the %sResult tag, but I haven't tested this. Just try it and see if this works in the way you want it.
If it doesn't work, but you still want to get this kind of response, look at Spyne:
http://spyne.io/docs/2.10/reference/decorator.html
It is a fork from soaplib(I think) and has the _soap_body_style='bare' decorator, which I believe is what you want.

How to override the server version string with wsgiref.simple_server?

Using Python 2.7.2 on OSX (darwin), I would like to hide or customize the "Server" response header sent by the wsgiref.simple_server.make_server().
I tried many things without any success and was pretty sure this sample code should work:
from wsgiref import simple_server
class MyWSGIRequestHandler(simple_server.WSGIRequestHandler):
server_version = "X/1"
sys_version = "Y/2"
httpd = simple_server.make_server('', 8082, simple_server.demo_app, handler_class=MyWSGIRequestHandler)
print "version_string: %s %s" % (httpd.RequestHandlerClass.server_version, httpd.RequestHandlerClass.sys_version)
# it prints "X/1 Y/2"
httpd.serve_forever()
But it's always the same and there's no way to get rid of the "Server: WSGIServer/0.1 Python/2.7.2" sent by the server. I've also tried to override the version_string method in my class, for example with something like that:
class MyWSGIRequestHandler(simple_server.WSGIRequestHandler):
def version_string(self):
return "42"
It changes nothing, I really don't understand what's happening here.
Can someone help me please?
I've finally found the solution, no need to override WSGIRequestHandler.
from wsgiref.simple_server import ServerHandler
ServerHandler.server_software = "Fake Server Name Here"
And then you can call make_server().

Accept parameters only from POST request in python

Is there a way to accept parameters only from POST request?
If I use cgi.FieldStorage() from cgi module, it accepts parameters from both GET and POST request.
By default, most things in the cgi module merge os.environ['QUERY_STRING'] and sys.stdin (in the format suggested by os.environ['CONTENT_TYPE']). So the simple solution would be to modify os.environ, or rather, provide an alternative, with no query string.
# make a COPY of the environment
environ = dict(os.environ)
# remove the query string from it
del environ['QUERY_STRING']
# parse the environment
form = cgi.FieldStorage(environ=environ)
# form contains no arguments from the query string!
Ignacio Vazquez-Abrams suggests avoiding the cgi module altogether; modern python web apps should usually adhere to the WSGI interface. That might instead look like:
import webob
def application(environ, start_response):
req = webob.Request(environ)
if req.method == 'POST':
# do something with req.POST
# still a CGI application:
if __name__ == '__main__':
import wsgiref.handlers
wsgiref.handlers.CGIHandler().run(application)
From the documentation, I think you can do the following:
form = cgi.FieldStorage()
if isinstance(form["key"], cgi.FieldStorage):
pass #handle field
This code is untested.

How can I set a custom response header for pylons static (public) files?

How do I add a custom header to files pylons is serving from public?
a) Let your webserver serve files from /public instead of paster and configure it to pass some special headers.
b) Add a special route and serve the files yourself ala
class FilesController(BaseController):
def download(self, path)
fapp = FileApp( path, headers=self.get_headers(path) )
return fapp(request.environ, self.start_response)
c) maybe there is a way to overwrite headers and i just dont know how.
With a recent version of route, you can use the 'Magic path_info' feature, and follow the documentation from here to write your controller so it calls paster.DirectoryApp.
In my project, I wanted to serve any file in the public directory, including subdirs, and ended with this as controller, to be able to override content_type :
import logging
from paste.fileapp import FileApp
from paste.urlparser import StaticURLParser
from pylons import config
from os.path import basename
class ForceDownloadController(StaticURLParser):
def __init__(self, directory=None, root_directory=None, cache_max_age=None):
if not directory:
directory = config['pylons.paths']['static_files']
StaticURLParser.__init__(self, directory, root_directory, cache_max_age)
def make_app(self, filename):
headers = [('Content-Disposition', 'filename=%s' % (basename(filename)))]
return FileApp(filename, headers, content_type='application/octetstream')
In a standard Pylons setup, the public files are served from a StaticUrlParser. This is typically setup in your config/middleware.py:make_app() function
You need to subclass the StaticUrlParser like Antonin ENFRUN describes, though calling it a Controller is confusing because it's doing a different purpose. Add something like the following to the top of the config/middleware.py:
from paste.fileapp import FileApp
from paste.urlparser import StaticURLParser
class HeaderUrlParser(StaticURLParser):
def make_app(self, filename):
headers = # your headers here
return FileApp(filename, headers, content_type='application/octetstream')
then replace StaticUrlParser in config/middleware.py:make_app() with HeaderUrlParser
static_app = StaticURLParser(config['pylons.paths']['static_files'])
becomes
static_app = HeaderURLParser(config['pylons.paths']['static_files'])
A simpler way to use FileApp for streaming, based on the pylons book. The code below assumes your route provides some_file_identifier, but the other two variables are "magic" (see explanation after code).
class MyFileController(BaseController):
def serve(self, environ, start_response, some_file_identifier):
path = self._convert_id_to_path(some_file_identifier)
app = FileApp(path)
return app(environ, start_response)
Pylons automatically gives you the wsgi environ and start_response variables if you have variables of those names in your method signature. You should not need to set or munge headers otherwise, but if you do you can use the abilities built in to FileApp to achieve this.

Categories