みんなのPython Webアプリ編からの質問です。ローカルサーバーを立てて、Webアプリケーションに関するコードを試しているときに、どうしても解消できないエラーがあります。
以下のコードをcgi-binディレクトリに格納し、ブラウザからpicklepole.pyを実行するのが目標です。
サーバーはターミナルから py -m http.server 8080 --cgi という形で立ち上げられ、
index.htmlやcgi-binフォルダにあるほかのpythonアプリは問題なく動作している状況です。
前提・実現したいこと
エラーの解消をどうしたら実現できるでしょうか?
同じ/cgi-bin/ディレクトリに存在する、httphandler.pyをimportする際に問題が生じていることは
何となくわかるのですが、同じディレクトリ内部での相対参照では、from .httphandler import *
の様な形でimportするだけではまずいのでしょうか?
発生している問題・エラーメッセージ
以下の様なエラーが表示されてしまい、ブラウザ上には何も表示されず行き詰ってしまっています。
Python
1 File "C:\Users\hogehoge\Desktop\LocalServerTest\cgi-bin\picklepole.py", line 5, in <module> 2 from .httphandler import Request, Response, get_htmltemplate 3ImportError: attempted relative import with no known parent package 4
該当のソースコード
Python
1#!/usr/bin/env python 2# coding: utf-8 3 4import pickle 5from .httphandler import Request, Response, get_htmltemplate 6import cgitb 7 8cgitb.enable() 9 10form_body = """ 11 <form method="POST" action="/cgi-bin/picklepole.py"> 12 好きな軽量言語は?<br /> 13 %s 14 <input type="submit" /> 15 </form>""" 16 17radio_parts = """ 18<input type="radio" name="language" value="%s" />%s 19<div style="border-left: solid %sem red; ">%s</div> 20""" 21 22lang_dic = {} 23try: 24 f = open('./favorite_language.dat') 25 lang_dic = pickle.load(f) 26except IOError: 27 pass 28 29content = "" 30req = Request() 31if 'language' in req.form: 32 lang = req.form['language'].value 33 lang_dic[lang] = lang_dic.get(lang, 0) + 1 34 35f = open('./favorite_language.dat', 'w') 36pickle.dump(lang_dic, f) 37 38for lang in ['Perl', 'PHP', 'Python', 'Ruby']: 39 num = lang_dic.get(lang, 0) 40 content += radio_parts % (lang, lang, num, num) 41 42res = Response() 43body = form_body % content 44res.set_body(get_htmltemplate() % body) 45print(res) 46
httphandler.pyについて
httphandlerは以下の様なコードになります。
Python
1# 標準モジュールをimportする。 2import cgi 3import os 4 5 6class Request(object): 7 """ 8 HTTPのリクエストをハンドリングするクラスです。 9 CGI側でインスタンスを生成することによって利用する 10 クエリデータや環境変数へのアクセス、主要ヘッダへの 11 アクセス用メソッドを提供します。 12 """ 13 14 def __init__(self, environ=os.environ): 15 """ 16 インスタンスの初期化メソッド 17 クエリ、環境変数をアトリビュートとして保持する。 18 """ 19 self.form = cgi.FieldStorage() 20 self.environ = environ 21 22 23def get_htmltemplate(): 24 """ 25 レスポンスとして返すHTMLのうち,定型部分を返す 26 """ 27 html_body = u""" 28 <html> 29 <head> 30 <meta http-equiv="content-type" 31 content="text/html;charset=utf-8" /> 32 </head> 33 <body> 34 %s 35 </body> 36 </html>""" 37 return html_body 38 39 40class Response(object): 41 """ 42 HTTPのレスポンスをハンドリングするクラスです。 43 レスポンスを送信する前にインスタンスを生成して利用します。 44 レスポンスやヘッダの内容を保持し、ヘッダを含めたレスポンスの 45 送信を行います。 46 """ 47 48 def __init__(self, charset='utf-8'): 49 self.headers = {'Content-type': 'text/html;charset=%s' % charset} 50 self.body = "" 51 self.status = 200 52 self.status_message = '' 53 54 def set_header(self, name, value): 55 """ 56 レスポンスのヘッダを設定する。 57 :param name: 58 :param value: 59 :return: 60 """ 61 self.headers[name] = value 62 63 def get_header(self, name): 64 """ 65 設定済みのレスポンス用ヘッダを返す 66 :param name: 67 :return: 68 """ 69 return self.headers.get(name, None) 70 71 def set_body(self, bodystr): 72 """ 73 レスポンスとして出力する本文の文字列を返す 74 :param bodystr: 75 :return: 76 """ 77 self.body = bodystr 78 79 def make_output(self, timestamp=None): 80 """ 81 ヘッダと本文を含めたレスポンス文字列を作る 82 :param timestamp: 83 :return: 84 """ 85 import time 86 _weekdayname = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] 87 _monthname = [None, 88 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 89 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 90 if timestamp is None: 91 timestamp = time.time() 92 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp) 93 dtstr = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (_weekdayname[wd], day, _monthname[month], year, hh, mm, ss) 94 self.set_header("Last-Modified", dtstr) 95 headers = '¥n'.join(["%s: %s" % (k, v) 96 for k, v in self.headers.items()]) 97 return headers + '¥n¥n' + self.body 98 99 def __str__(self): 100 """ 101 リクエストを文字列に変換する。 102 :return: 103 """ 104 return self.make_output().encode('utf-8') 105 106