Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

PyWeb

4.78/5 (5 votes)
16 Nov 2016MIT5 min read 18.9K   139  
Provides a webserver solution for all operating systems without a boat-load of instruction or management

Image 1

Introduction

PyWeb provides a webserver solution for all operating systems without a boat-load of instructions or management. This article is about setting it up and how to implement your own plugins.

Setup Instructions

PyWeb requires Python 2.7. Once Python 2.7 is installed, create a folder wherever you would like to run the server, then clone the github repo, or extract the provided project zip, then launch __init__.py. It's that simple!

Using the Code

Using the code involves making plugins. You should not edit the source without advanced knowledge in what it is doing and how webservers communicate. I used PyCharm Community IDE to program everything, and I would suggest you use the same.

I use certain terminology to describe certain things. These include, but are not limited to:

  • Delegate/Delegate function - The function to be called included in the plug in, registered using "@delegate.delegate_type"
  • Delegated/Delegated function - The original function to be modified. Added using "Plugin.create_delegate"
  • "Original" - Delegated function
  • Return object of... - Although this should be obvious, this is the object that is returned when the "..." is called.

PyWeb works by using plugins to manipulate return objects. In order to override a function, you have to use a delegate. Delegates come in three forms:

  • Callbefore - Calls the delegate function BEFORE the delegated function WITH the same arguments used to call the delegated function.
  • Callback - Calls the delegate function AFTER the delegated function WITH the delegated function's return object.
  • Callwith - Calls the delegate function AFTER the delegated function WITH the same arguments used to call the delegated function.

Callback calls the function AFTER the original has been called. This call's parameters include the return object of the delegated function. For example:

Python
# I have object "Response" that I want to manipulate.
# Always import Plugin for "create_delegate" function.
import Plugin
response = Plugin.create_delegate("Responses", "Response")  # I create a delegate 
                                            # for HTTP.Response, a class inside PyWeb's HTTP module.

@response.callback
def response_callback(responseobj):  # Whatever this function returns will be what Response() 
                                     # returns in all cases.
	responseobj.method = "OPTIONS"  # No matter what, I always want the method to give "OPTIONS"
	return responseobj  # Overwrite the Response object.

The Response callback in that case will always give "OPTIONS" as the method used, regardless of any other parameters. This is important to note because when someone makes a request, the response will always be OPTIONS, even if the request was for a file using the method "GET." One must always use "import plugin" to add a delegate. All plugins located in "./plugins" will be executed on server startup, but they won't be persistent unless you make it. This could be accomplished by creating a while loop, which would block all threads, or you could just register a delegate, like I did above, and whenever a Response object is created, the "response_callback" function will be called.

The next example is a callbefore function. This can be used to change the arguments as the delegated function will use whatever is returned from the callbefore. Example:

Python
# First, you must import the Plugin
import Plugin
openfunc = Plugin.create_delegate("FileManager", "open")  # This time, I want to 
                                                # edit the FileManager's "open" function.

@openfunc.callbefore
def change_path(path):  # The path is the argument that will be passed to the delegate 
                        # after this callbefore.
	path = "C:\\Users\\Brandon\\Documents\\I_want_this_file_instead_always.jpg"
	return path

The example above will always provide the new path for every time "open" is called in the FileManager. It is important to note that this will happen on all occasions. So, now, when someone makes a GET request, the file opened will be "C:\\Users\\Brandon\\Documents\\I_want_this_file_instead_always.jpg"
Caution: Doing this will make it so EVERY get request made will give the same path. It's not particularly useful, but you can see why it's necessary.

Lastly, we have callwith. Callwith is essentially the same as a callback, but instead of the object returned as a parameter, it will use the arguments that were originally passed to the original object. Whatever callwith returns will replace the object as a whole. For example:

Python
# Always import Plugin
import Plugin
from HTTP import Response
replaced_response = Plugin.create_delegate("Responses", "Response")

@replaced_response.callwith
def replace_response(requestobj):  # The "requestobj" is the Request 
                                   # that a Response would be normally be created with.
	requestobj.connection("terminate")
	return Response(requestobj)

In this case, we have a Response object returned, just like it normally would be, but instead, we are using "requestobj" with a modified connection header. Since we are changing the connection header of the request to "Terminate", the Response will send the "Connection: Terminated" header, to terminate the connection. This could potentially be useful for getting information from the Request and changing it to return a better-tailored Response. Another great way of using callwith is for tracing output. Say you are getting a "rogue" message from a client connection. You could output what it is sending so you could then create a way of handling it.

Caution: When using "callwith", all objects that use the overwritten object still call the same methods. For example: "Response.generate()" is called when generating a Response to send back to the host. If you replace the Response object with a string, it will result in a fatal error as strings do not have the function "generate."

Note: None of these delegates MUST return anything. If a delegate doesn't return a new object, the default will be used. If any of these functions do return an object, however, the object that is returned will be used respectively.

Summary

As you can see, the possibilities are virtually endless. If you don't complicate things by editing the source and just use the plugin framework, you shouldn't have any problems. The Webserver isn't 100% completed, there are a lot of methods yet to be implemented, but basic functionality still works.

Points of Interest

The Hyper-Text Transfer Protocol is an advanced and standardized system. Communication between server and client is very strict and any messages that are transferred without proper knowledge will either result in error or funky things.

Want to help develop PyWeb?

Contact me either on forums, github, or by email at beemerwt@gmail.com

I'm looking for programmers to help develop PyWeb.

History

  • 11/16/2016
    • Updated <pre> tags for Python
    • Repository was updated, changes to code accordingly
    • Added setup instructions
  • 11/17/2016
    • Updated <pre> tags, again, changed "plug in" to "Plugin" - for some reason it was changed
    • If you want to develop, contact me

License

This article, along with any associated source code and files, is licensed under The MIT License