Как расширить класс Python3 BaseHTTPRequestHandler, чтобы можно было вызывать функцию с переменной-членом - PullRequest
0 голосов
/ 09 октября 2018

Я пытаюсь расширить BaseHTTPRequestHandler с помощью моих собственных переменных-членов.Я передаю эти переменные-члены свободным функциям, но когда я делаю это, я получаю:

Exception happened during processing of request from ('127.0.0.1', 30006)
Traceback (most recent call last):
  File "c:\Python37\lib\socketserver.py", line 313, in _handle_request_noblock
self.process_request(request, client_address)
  File "c:\Python37\lib\socketserver.py", line 344, in process_request
    self.finish_request(request, client_address)
  File "c:\Python37\lib\socketserver.py", line 357, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "fruiterer.py", line 41, in __init__
    BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
  File "c:\Python37\lib\socketserver.py", line 712, in __init__
    self.handle()
  File "c:\Python37\lib\http\server.py", line 426, in handle
    self.handle_one_request()
  File "c:\Python37\lib\http\server.py", line 414, in handle_one_request
    method()
  File "fruiterer.py", line 62, in do_POST
    fruit_handler(val, Event_dictionary_list, cached_timestamp)
NameError: name 'Event_dictionary_list' is not defined

Вот исходный код python3:

# sample python HTTP POST handler

__metaclass__= type
from http.server import BaseHTTPRequestHandler, HTTPServer  
import os  

import time
import threading    
import requests
import json

def calculate(Event_dictionary_list, cached_timestamp):
    # do a calculation here based on fruits in Event_dictionary_list
    print("there are: %i items" % len(Event_dictionary_list))
    Event_dictionary_list.clear() #- empty Event_dictionary_list
    cached_timestamp = 0   

def Async_func(Event_dictionary_list, cached_timestamp):
    calculate(Event_dictionary_list, cached_timestamp)

def fruit_handler(event, Event_dictionary_list, cached_timestamp):
    if not Event_dictionary_list: #Checking if Event_dictionary_list is empty 
        # cache first item added
        cached_timestamp = event['timestamp']
        #- set a 30 second timer
        threading.Timer(60,Async_func, Event_dictionary_list, cached_timestamp).start()

    # make decision as to when to calculate
    if event['timestamp'] - cached_timestamp < 60*1000: #- in milliseconds
        # append event to list
        Event_dictionary_list.append(event)


#############################################################################################################
# we create a server to handle POST requests from Xovis
class fruiterer(BaseHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
         BaseHTTPRequestHandler.__init__(self, *args, **kwargs)
         self.Event_dictionary_list = []
         self.cached_timestamp = 0

     # we only need to support he POST http method
    def do_POST(self):
        #print("post msg received");
        self.data_string = self.rfile.read(int(self.headers['Content-Length']))

        self.send_response(200) # 200 = success - thanks for message response
        #self.send_header() #'Content-type','text-html')
        self.end_headers()

        data = json.loads(self.data_string)

        # we assume json will be decoded as object, eg:
        # {"fruit":{"timestamp":1538688902037,"name":"apple","colour":"red","weight":100}}
        if type(data) is dict:
               for key, val in data.items():
                 # we are only interested in fruits
                 if key == "fruit":
                   fruit_handler(val, Event_dictionary_list, cached_timestamp)
                   break

        return

def run():
    print('http server is starting...')
    # change port to listen on here - arbitrarily using port 7777
    port = 7777
    server_address = ('127.0.0.1', port)  
    #use the code from here .start()
    httpd = HTTPServer(server_address, fruiterer)  
    print('http server is listening on port %d' % port)  

    httpd.serve_forever()    

if __name__ == '__main__':  
  run()

Это можно проверить, отправив следующий jsonформат в теле сообщения:

{"fruit":{"timestamp":1538688902037,"name":"apple","colour":"red","weight":100}}

Что я делаю не так?

1 Ответ

0 голосов
/ 09 октября 2018

Вам необходимо передать self.Event_dictionary_list, а не Event_dictionary_list, из вашего do_POST() метода.То же самое относится и к cached_timestamp.

. Затем вы обнаружите, что получите AttributeError на self.Event_dictionary_list, поскольку этот атрибут никогда не устанавливается в вашем методе __init__.Документация немного загадочна по этому поводу, но вызов BaseHTTPRequestHandler.__init__(self, *args, **kwargs) запускает обработку отдельного запроса, поэтому метод не возвращается до после обработки запросов.

Moveустановив для этих атрибутов значение до , вы вызываете базовый метод __init__:

class fruiterer(BaseHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
         self.Event_dictionary_list = []
         self.cached_timestamp = 0
         super().__init__(*args, **kwargs)

Я использовал здесь синтаксис super().__init__(), чтобы учесть дальнейшее совместное наследование в будущем,Вы по-прежнему можете использовать BaseHTTPRequestHandler.__init__(self, *args, **kwargs), если действительно хотите, но приведенное выше менее многословно.

Следует учитывать, что self.Event_dictionary_list = [] запускается для каждого отдельного запроса.

Если этот объектдолжен существовать в течение всей вашей программы на Python (с работающим сервером), вместо этого сделайте его атрибутом класса:

class fruiterer(BaseHTTPRequestHandler):
    Event_dictionary_list = []
    cached_timestamp = 0

Другая проблема заключается в том, что установка cached_timestamp с cached_timestamp = ... в других функциях только изменитлокальная переменная.Целые числа являются неизменяемыми объектами, поэтому вы можете назначать новое целое число только переменным, но локальные переменные в вызываемых функциях не делят пространство имен с обработчиком запросов.

Но если вы хотите, чтобы это значение сохранялось в запросах в любом случаетогда вы можете просто ссылаться на fruiterer.cached_timestamp везде и напрямую назначать этому атрибуту.

...