Request from wsgiref server works in Python 2 but not in Python 3

528 Views Asked by At

I have the following request file that is called when running a wsgiref server:

import cgi, os
import cgitb; cgitb.enable() 
import pdb, time


STATUS_TEXT = \
  {
  200: 'OK',
  400: 'Bad Request',
  401: 'Unauthorized',
  403: 'Forbidden',
  404: 'Not Found',
  409: 'Conflict'
  }

class AmyTest( object ):

  def __init__( self, env, start_response ):
    """
rootDir_  is used to have the path just in case you need to refer to
    files in the directory where your script lives
environ_  is how most things are passed
startReponse_  is WSGI interface for communicating results
"""
    self.rootDir_ = os.environ.get( 'ROOT_DIR', ',' )
    self.environ_ = env
    self.startResponse_ = start_response

  def doTest( self ):

    method = self.environ_.get( 'REQUEST_METHOD', 'get' ).lower()
    if method == 'post':
      params = cgi.FieldStorage(
      fp = self.environ_[ 'wsgi.input' ],
          environ = self.environ_, keep_blank_values = True
      )
    else:
      params = cgi.FieldStorage(
          environ = self.environ_, keep_blank_values = True
      )

    vehicle_type = params.getfirst( 'vehicletype' )
    power_train_type = params.getfirst( 'powertraintype' )
    engine_displacement = params.getfirst( 'enginedisplacement' )
    engine_power = params.getfirst( 'enginepower' )
    curb_weight = params.getfirst( 'curbweight' )
    gv_weight = params.getfirst( 'gvweight' )
    frontal_area = params.getfirst( 'frontalarea' )
    coefficient_adrag = params.getfirst( 'coad' )
    rr_coefficient = params.getfirst( 'rrco' )
    sat_options = params.getfirst( 'satoptions' )

# This is the response HTML we are generating.
    fmt = """
<h2>Results</h2>
<table>
  <tr><td>Vehicle Type:</td><td>%s</td></tr>
  <tr><td>Power Train Type:</td><td>%s</td></tr>
  <tr><td>Engine Displacement (L):</td><td>%s</td></tr>
  <tr><td>Engine Power (hp):</td><td>%s</td></tr>
  <tr><td>Curb Weight (lbs):</td><td>%s</td></tr>
  <tr><td>Gross Vehicle Weight Rating (lbs):</td><td>%s</td></tr>
  <tr><td>Frontal Area (m^2):</td><td>%s</td></tr>
  <tr><td>Coefficient of Aerodynamic Drag:</td><td>%s</td></tr>
  <tr><td>Rolling Resistance Coefficient:</td><td>%s</td></tr>
  <tr><td>Selected Advanced Technology Options:</td><td>%s</td></tr>
</table>
"""
    content = fmt % ( vehicle_type, power_train_type, engine_displacement, engine_power, curb_weight, gv_weight, frontal_area, coefficient_adrag, rr_coefficient, sat_options)

    headers = \
      [
#        ( 'Content-disposition',
#          'inline; filename="%s.kmz"' % excat_in[ 'name' ] )
      ]

    result = \
      {
      'body': content,
      'code': 200,
      'headers': headers,
      'type': 'text/html'
      }
    time.sleep(5)
    return result


  def process( self ):
    result = self.doTest()
    return self.sendResponse( **result )

  def sendResponse( self, **kwargs ):
    """
@param  body
@param  code
@param  headers
@param  type
"""
    code = kwargs.get( 'code', 200 )
    status = '%d %s' % ( code, STATUS_TEXT[ code ] )

    body = kwargs.get( 'body', '' )
    mime_type = kwargs.get( 'type', 'text/plain' )
    headers = \
      [
        ( 'Content-Length', str( len( body ) ) ),
        ( 'Content-Type', mime_type )
      ]
    headers_in = kwargs.get( 'headers' )
    if headers_in != None and len( headers_in ) > 0:
      headers += headers_in

    self.startResponse_( status, headers )
    return [body]


  @staticmethod
  def processRequest( env, start_response ):
    server = AmyTest( env, start_response )
    return  server.process()

Everything works fine on Python 2 but when I try to run on Python 3 I get errors about write() argument must be bytes instance and NoneType object is not subscriptable. Errors from the console are:

Traceback (most recent call last):
  File "/Users/gavin/miniconda3/lib/python3.6/wsgiref/handlers.py", line 138, in run
    self.finish_response()
  File "/Users/gavin/miniconda3/lib/python3.6/wsgiref/handlers.py", line 180, in finish_response
    self.write(data)
  File "/Users/gavin/miniconda3/lib/python3.6/wsgiref/handlers.py", line 266, in write
    "write() argument must be a bytes instance"
AssertionError: write() argument must be a bytes instance

Traceback (most recent call last):
  File "/Users/gavin/miniconda3/lib/python3.6/wsgiref/handlers.py", line 141, in run
    self.handle_error()
  File "/Users/gavin/miniconda3/lib/python3.6/wsgiref/handlers.py", line 368, in handle_error
    self.finish_response()
  File "/Users/gavin/miniconda3/lib/python3.6/wsgiref/handlers.py", line 180, in finish_response
    self.write(data)
  File "/Users/gavin/miniconda3/lib/python3.6/wsgiref/handlers.py", line 274, in write
    self.send_headers()
  File "/Users/gavin/miniconda3/lib/python3.6/wsgiref/handlers.py", line 331, in send_headers
    if not self.origin_server or self.client_is_modern():
  File "/Users/gavin/miniconda3/lib/python3.6/wsgiref/handlers.py", line 344, in client_is_modern
    return self.environ['SERVER_PROTOCOL'].upper() != 'HTTP/0.9'
TypeError: 'NoneType' object is not subscriptable

Any suggestions on how to get this working in Python 3 would be very helpful.

1

There are 1 best solutions below

5
Blaine Lafreniere On

The write() function expecting bytes can be fixed by calling your_arg.encode(), if your_arg is a string. The encode() function converts a string to bytes, and decode() will convert bytes to a string.

The encode/decode functions are not available in python 3, converting bytes to strings and vice versa is handled differently in python 2.

The other error saying not subscriptable means it doesn't support subscript notation. E.g. foo[i]. So where you're getting that error, that object doesn't support [] subscripting.