Package: pibble
PibbleThe Pibble framework turbocharges Python web applications with a huge array of features and easy-to-use interface.InstallationThepibblepackage is available on PYPI. Simply run:pip install pibbleFeaturesAPI Integration LayerAPIs are broken up into server and client modules.ServerAll web server APIs should be extended frompibble.api.server.webservice.base.WebServiceAPIServerBase. For the most part, each implementation must only register some handlers using thepibble.api.server.webservice.base.WebServiceAPIHandlerRegistry, which will handle all requests using a method and path. A class is not recommended to use the parent handler function, as this will provider handlers for all classes in this module that extend frompibble.api.webservice.base.WebServiceAPIServerBase, instead defining their own. For example, if we simply wanted to serve files from a directory over HTTP, we could use something like this:importosfromtypingimportOptionalfromwebobimportRequest,Responsefrompibble.api.exceptionsimportNotFoundErrorfrompibble.api.server.webservice.baseimport(WebServiceAPIServerBase,WebServiceAPIHandlerRegistry)classHelloWorldServer(WebServiceAPIServerBase):"""This class provides a single endpoint at the root URL displaying a simple message.It creates a handler registry, then uses the registries decorators to configure the handler."""handlers=WebServiceAPIHandlerRegistry()@handlers.path("^$")@handlers.methods("GET")defhello_world(self,request:Request,response:Response)->None:"""Handles the request at the root and sends a simple message.:param request webob.Request: The request object.:param response webob.Response: The response object."""response.text="<!DOCTYPE html><html lang='en_US'><body>Hello, world!</body></html>"classSimpleFileServer(HelloWorldServer):"""This class creates a more complicated handler than allows for downloading files.It also extends the class above, inheriting the handlers above.Classes should always name their handler registry 'handlers'. If you want to name your registrysomething else, you need to add a `get_handlers()` classmethod that returns the registry for itto be recognized by the dispatcher."""handlers=WebServiceAPIHandlerRegistry()base_directory="/var/www/html"@handlers.path("(?P<file_path>.*)")@handlers.methods("GET")defretrieve_file(self,request:Request,response:Response,file_path:Optional[str]=None)->None:"""Handles the request by looking for the path in ``self.base_directory``.:param request webob.Request: The request object.:param response webob.Response: The response object.:param file_path str: The file path, captured from the URI.:throws: :class:`pibble.api.exceptions.NotFoundError`"""file_path=os.path.join(self.base_directory,file_path)ifnotos.path.isfile(file_path):raiseNotFoundError("Could not find file at{0}".format(file_path))response.body=open(file_path,"r").read()The request and response parameters are webob.Request and webob.Response objects, respectively. SeeThe WebOb Documentationfor help with their usage. Note the method name does not matter, so use a naming schema relevant to your project.Deploying the API can be done using anything that conforms to wsgi standards. For development, we can deploy a server using werkzeug or gunicorn, binding to a local port with a configured listening host. Using the above server definition, we can do this with::server=SimpleFileServer()server.configure(server={"driver":"werkzeug","host":"0.0.0.0","port":9090})# Serve synchronouslyserver.serve()# Serve asynchronouslyserver.start()# Use the serverserver.stop()For production, most servers will simply import a file and look for a globally-availableapplicationthat conforms to WSGI standards. The easiest way to configure this is thusly:# wsgi.pyfrommypackage.serverimportSimpleFileServerserver=SimpleFileServer()application=server.wsgi()Pointing something like Apache'smod_wsgito thiswsgi.pyfile will allow Pibble to be ran through Apache.RPCUsing one of the RPC servers is as simple as defining functions and registering them to the server:frompibble.api.server.webservice.rpc.xml.serverimportXMLRPCServerserver=XMLRPCServer()@server.register@server.sign_request(int,int)@server.sign_response(int)defadd(x,y):returnx+y@server.register@server.sign_request(int,int)@server.sign_response(int)defsubtract(x,y):returnx-y@server.register@server.sign_request(int,int)@server.sign_response(int)defmultiply(x,y):returnx*y@server.register@server.sign_request(int,int)@server.sign_response(float)defdivide(x,y):returnx/yserver.configure(server={"driver":"werkzeug","host":"0.0.0.0","port":9090})print("Running server, listening on 0.0.0.0:9090. Hit Ctrl+C to exit.")server.serve()The base server defines methods for registration and dispatching of requests. The two implementations (XML and JSON) are responsible for parsing and formatting of requests and responses.ClientThe simplest client possible is one that simply communicates with a webserver, and doesn't need to parse the response in any meaningful way. Unlike with servers, the base webservice API client is instantiable.frompibble.api.client.webservice.baseimportWebServiceAPIClientBasebase=WebServiceAPIClientBase("google.com")print(base.get().text)When executing any methods via .get(), .post(), etc., you will receive arequests.models.Responseobject. See the the requests documentation for assistance with these objects. Clients use a session (requests.models.Session) object to maintain some state (cookies, etc.), but should generally assume themselves to be stateless.RPCUsing an XML RPC Client is very simple. Once a client is instantiated, it will queue up a call to system.listMethods, a built-in RPC function that will list the methods of a client. After that, calling them is as simple as calling the method with the appropriate variablesfrompibble.api.client.webservice.rpc.xml.clientimportXMLRPCClientfrompibble.api.exceptionsimport(BadRequestError,UnsupportedMethodError)client=XMLRPCClient("127.0.0.1")# Use a method with reserved charactersmethods=client["system.listMethods"]()formethodinmethods:# Get method signature - note this is automatically retrieved and checked when calling any function, but you can retrieve it for yourself if you need to.signature=client["system.methodSignature"](method)return_type,parameter_types=signature[0],signature[1:]# RPC specificationprint("Method{0}takes ({1}) and returns ({2})".format(method,", ".join(parameter_types),return_type))try:method.pow(1,2,3)exceptBadRequestError:# Wrong parametersexceptBadResponseError:# Wrong response timeexceptUnsupportedMethodError:# Method does not existThe base client has handlers for introspection and dispatching requests. Implementations are responsible for formatting requests and parsing responses.