Reputation: 3744
This has been vexing me for a while now. I am trying to create a very simple REST-like interface (without using third-party libraries, which I know are available).
The idea behind it is that I can have a directory, for example mylib
, where I can drop in python files, like do_something.py
and, by POSTing to http://localhost/do_something the code will spring into life and do something!
I think I have managed to get somewhere near to my goal with the following structure:
The contents of the files are as follows.
example.py
from http.server import HTTPServer
from http.server import BaseHTTPRequestHandler
import json, logging
from mylib import my_module
class MyRequestHandler (BaseHTTPRequestHandler):
# Send JSON responses
# -----------
def send_json(self, json_message, response_code=200):
self.send_response(response_code)
self.send_header('Content-type', 'application/json')
self.end_headers()
self.request.sendall(json.dumps(json_message).encode())
# Get JSON requests
# ----------
def get_json(self):
body = self.rfile.read(int(self.headers.get('Content-Length')))
if (body):
try:
receivedData = json.loads(body.decode())
except:
self.send_json({"Status": "Error", "Message": "Invalid JSON received"}, 400)
receivedData = None
else:
receivedData = None
return receivedData
# POST
# ---------
def do_POST(self):
module_to_call = (self.path).replace('/', '.')[1:]
if module_to_call.endswith('.'): # Remove trailing dot
module_to_call = module_to_call[:-1]
print("Path is: '" + module_to_call + "'")
# invoke function
module_to_call = getattr(self, module_to_call)
response = module_to_call()
self.send_json(response)
# GET
# --------
def do_GET(self):
pass
# -----------------------------------------------------------------------------
# Server startup code
# -------------------
def start_server():
# Begin serving
# -------------
port = 8003
server = HTTPServer(('', port), MyRequestHandler)
print(("Server now running on port {0} ...").format(port))
server.serve_forever()
# -----------------------------------------------------------------------------
# Start the Server
# ----------------
if __name__ == '__main__':
start_server()
my_module.py
def my_module():
print("Hello World!")
return{'Greeting': 'Hello World!'}
When I fire up the server and attempt to POST to http://localhost:8003/my_module, I get the following output:
Server now running on port 8003 ...
Path is: 'my_module'
----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 59541)
Traceback (most recent call last):
File "C:\Users\Test\AppData\Local\Programs\Python\Python35-32\lib\socketserver.py", line 313, in _handle_request_noblock
self.process_request(request, client_address)
File "C:\Users\Test\AppData\Local\Programs\Python\Python35-32\lib\socketserver.py", line 341, in process_request
self.finish_request(request, client_address)
File "C:\Users\Test\AppData\Local\Programs\Python\Python35-32\lib\socketserver.py", line 354, in finish_request
self.RequestHandlerClass(request, client_address, self)
File "C:\Users\Test\AppData\Local\Programs\Python\Python35-32\lib\socketserver.py", line 681, in __init__
self.handle()
File "C:\Users\Test\AppData\Local\Programs\Python\Python35-32\lib\http\server.py", line 422, in handle
self.handle_one_request()
File "C:\Users\Test\AppData\Local\Programs\Python\Python35-32\lib\http\server.py", line 410, in handle_one_request
method()
File ".\example.py", line 43, in do_POST
module_to_call = getattr(self, module_to_call)
AttributeError: 'MyRequestHandler' object has no attribute 'my_module'
----------------------------------------
This makes perfect sense, since 'MyRequestHandler" does not have an attribute "my_module"! What I can't wrap my head around, is how to fix this?
Should I pass "mylib" into MyRequestHandler? Should I perform the import within the class (but then the functionality would only be available within the class)?
I'm trying to keep things clean and simple, so that even a Python novice (like I seem to be!) can just write a standalone script, drop it into "mylib" and everything "just works". The novice could visit the web address of their script and have it magically run.
Any help or advice would be gratefully received.
Upvotes: 0
Views: 316
Reputation: 323
Use the __import__()
method:
temp = __import__('mylib', globals(), locals(), ['module_to_call'], -1)
response = temp.module_to_call()
I use 2.6 at work, and this is usually used by those using even 2.7 because the importlib module is far more robust in 3. If you are using 3 you can do the following:
from importlib import import_module
temp = import_module('mylib')
but now you have to use getattr to get the function you want to be called
func_to_call = getattr(temp, 'module_to_call')
response = func()
Or you can have a dictionary of functions in another module, but that will require a lot of work as that dictionary grows.
Upvotes: 2