Name | fruition JSON |
Version |
0.9.1
JSON |
| download |
home_page | https://github.com/painebenjamin/fruition |
Summary | A framework for developing webapps quickly and easily using Python, SQLAlchemy, and Jinja2. Supports numerous protocols, databases, and web drivers. |
upload_time | 2025-01-07 23:39:12 |
maintainer | None |
docs_url | None |
author | Benjamin Paine |
requires_python | None |
license | gpl-3.0 |
keywords |
|
VCS |
|
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# Fruition
The Fruition framework turbocharges Python web applications with a huge array of features and easy-to-use interface.
# Installation
The `fruition` package is available on PYPI. Simply run:
```
pip install fruition
```
# Features
## API Integration Layer
APIs are broken up into server and client modules.
### Server
All web server APIs should be extended from `fruition.api.server.webservice.base.WebServiceAPIServerBase`. For the most part, each implementation must only register some handlers using the `fruition.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 from `fruition.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:
```python3
import os
from typing import Optional
from webob import Request, Response
from fruition.api.exceptions import NotFoundError
from fruition.api.server.webservice.base import (
WebServiceAPIServerBase,
WebServiceAPIHandlerRegistry
)
class HelloWorldServer(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")
def hello_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>"
class SimpleFileServer(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 registry
something else, you need to add a `get_handlers()` classmethod that returns the registry for it
to be recognized by the dispatcher.
"""
handlers = WebServiceAPIHandlerRegistry()
base_directory = "/var/www/html"
@handlers.path("(?P<file_path>.*)")
@handlers.methods("GET")
def retrieve_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:`fruition.api.exceptions.NotFoundError`
"""
file_path = os.path.join(self.base_directory, file_path)
if not os.path.isfile(file_path):
raise NotFoundError("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. See [The WebOb Documentation](https://docs.pylonsproject.org/projects/webob/en/stable/) for 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::
```python
server = SimpleFileServer()
server.configure(server = {"driver": "werkzeug", "host": "0.0.0.0", "port": 9090})
# Serve synchronously
server.serve()
# Serve asynchronously
server.start()
# Use the server
server.stop()
```
For production, most servers will simply import a file and look for a globally-available `application` that conforms to WSGI standards. The easiest way to configure this is thusly:
```python
# wsgi.py
from mypackage.server import SimpleFileServer
server = SimpleFileServer()
application = server.wsgi()
```
Pointing something like Apache's `mod_wsgi` to this `wsgi.py` file will allow Fruition to be ran through Apache.
#### RPC
Using one of the RPC servers is as simple as defining functions and registering them to the server:
```python
from fruition.api.server.webservice.rpc.xml.server import XMLRPCServer
server = XMLRPCServer()
@server.register
@server.sign_request(int, int)
@server.sign_response(int)
def add(x, y):
return x + y
@server.register
@server.sign_request(int, int)
@server.sign_response(int)
def subtract(x, y):
return x - y
@server.register
@server.sign_request(int, int)
@server.sign_response(int)
def multiply(x, y):
return x * y
@server.register
@server.sign_request(int, int)
@server.sign_response(float)
def divide(x, y):
return x / y
server.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.
### Client
The 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.
```python
from fruition.api.client.webservice.base import WebServiceAPIClientBase
base = WebServiceAPIClientBase("google.com")
print(base.get().text)
```
When executing any methods via .get(), .post(), etc., you will receive a `requests.models.Response` object. See the the requests documentation for assistance with these objects. Clients use a session (r`equests.models.Session`) object to maintain some state (cookies, etc.), but should generally assume themselves to be stateless.
#### RPC
Using 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 variables
```python
from fruition.api.client.webservice.rpc.xml.client import XMLRPCClient
from fruition.api.exceptions import (
BadRequestError,
UnsupportedMethodError
)
client = XMLRPCClient("127.0.0.1")
# Use a method with reserved characters
methods = client["system.listMethods"]()
for method in methods:
# 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 specification
print("Method {0} takes ({1}) and returns ({2})".format(method, ", ".join(parameter_types), return_type))
try:
method.pow(1, 2, 3)
except BadRequestError:
# Wrong parameters
except BadResponseError:
# Wrong response time
except UnsupportedMethodError:
# Method does not exist
```
The base client has handlers for introspection and dispatching requests. Implementations are responsible for formatting requests and parsing responses.
Raw data
{
"_id": null,
"home_page": "https://github.com/painebenjamin/fruition",
"name": "fruition",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": null,
"author": "Benjamin Paine",
"author_email": "painebenjamin@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/b6/1c/8814af257fcaf06fa6dc25040475b1eaed3d9364d545d9dc53c897974d3a/fruition-0.9.1.tar.gz",
"platform": null,
"description": "# Fruition\n\nThe Fruition framework turbocharges Python web applications with a huge array of features and easy-to-use interface.\n\n# Installation\n\nThe `fruition` package is available on PYPI. Simply run:\n\n```\npip install fruition\n```\n# Features\n## API Integration Layer\n\nAPIs are broken up into server and client modules.\n\n### Server\n\nAll web server APIs should be extended from `fruition.api.server.webservice.base.WebServiceAPIServerBase`. For the most part, each implementation must only register some handlers using the `fruition.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 from `fruition.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:\n\n```python3\nimport os\nfrom typing import Optional\nfrom webob import Request, Response\nfrom fruition.api.exceptions import NotFoundError\nfrom fruition.api.server.webservice.base import (\n WebServiceAPIServerBase,\n WebServiceAPIHandlerRegistry\n)\n\nclass HelloWorldServer(WebServiceAPIServerBase):\n \"\"\"\n This class provides a single endpoint at the root URL displaying a simple message.\n It creates a handler registry, then uses the registries decorators to configure the handler.\n \"\"\"\n handlers = WebServiceAPIHandlerRegistry()\n\n @handlers.path(\"^$\")\n @handlers.methods(\"GET\")\n def hello_world(self, request: Request, response: Response) -> None:\n \"\"\"\n Handles the request at the root and sends a simple message.\n \n :param request webob.Request: The request object.\n :param response webob.Response: The response object.\n \"\"\"\n response.text = \"<!DOCTYPE html><html lang='en_US'><body>Hello, world!</body></html>\"\n\nclass SimpleFileServer(HelloWorldServer):\n \"\"\"\n This class creates a more complicated handler than allows for downloading files.\n\n It also extends the class above, inheriting the handlers above.\n\n Classes should always name their handler registry 'handlers'. If you want to name your registry\n something else, you need to add a `get_handlers()` classmethod that returns the registry for it\n to be recognized by the dispatcher.\n \"\"\"\n handlers = WebServiceAPIHandlerRegistry()\n base_directory = \"/var/www/html\"\n\n @handlers.path(\"(?P<file_path>.*)\")\n @handlers.methods(\"GET\")\n def retrieve_file(self, request: Request, response: Response, file_path: Optional[str] = None) -> None:\n \"\"\"\n Handles the request by looking for the path in ``self.base_directory``.\n \n :param request webob.Request: The request object.\n :param response webob.Response: The response object.\n :param file_path str: The file path, captured from the URI.\n :throws: :class:`fruition.api.exceptions.NotFoundError`\n \"\"\"\n \n file_path = os.path.join(self.base_directory, file_path)\n if not os.path.isfile(file_path):\n raise NotFoundError(\"Could not find file at {0}\".format(file_path))\n \n response.body = open(file_path, \"r\").read()\n```\n\nThe request and response parameters are webob.Request and webob.Response objects, respectively. See [The WebOb Documentation](https://docs.pylonsproject.org/projects/webob/en/stable/) for help with their usage. Note the method name does not matter, so use a naming schema relevant to your project.\n\nDeploying 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::\n\n```python\nserver = SimpleFileServer()\nserver.configure(server = {\"driver\": \"werkzeug\", \"host\": \"0.0.0.0\", \"port\": 9090})\n\n# Serve synchronously\nserver.serve()\n\n# Serve asynchronously\nserver.start()\n# Use the server\nserver.stop()\n```\n\nFor production, most servers will simply import a file and look for a globally-available `application` that conforms to WSGI standards. The easiest way to configure this is thusly:\n\n```python\n# wsgi.py\nfrom mypackage.server import SimpleFileServer\nserver = SimpleFileServer()\napplication = server.wsgi()\n```\n\nPointing something like Apache's `mod_wsgi` to this `wsgi.py` file will allow Fruition to be ran through Apache.\n\n#### RPC\n\nUsing one of the RPC servers is as simple as defining functions and registering them to the server:\n\n```python\nfrom fruition.api.server.webservice.rpc.xml.server import XMLRPCServer\n\nserver = XMLRPCServer()\n\n@server.register\n@server.sign_request(int, int)\n@server.sign_response(int)\ndef add(x, y):\n return x + y\n\n@server.register\n@server.sign_request(int, int)\n@server.sign_response(int)\ndef subtract(x, y):\n return x - y\n\n@server.register\n@server.sign_request(int, int)\n@server.sign_response(int)\ndef multiply(x, y):\n return x * y\n\n@server.register\n@server.sign_request(int, int)\n@server.sign_response(float)\ndef divide(x, y):\n return x / y\n\nserver.configure(server = {\"driver\": \"werkzeug\", \"host\": \"0.0.0.0\", \"port\": 9090})\nprint(\"Running server, listening on 0.0.0.0:9090. Hit Ctrl+C to exit.\")\nserver.serve()\n```\n\nThe 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.\n\n### Client\n\nThe 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.\n\n```python\nfrom fruition.api.client.webservice.base import WebServiceAPIClientBase\n\nbase = WebServiceAPIClientBase(\"google.com\")\nprint(base.get().text)\n```\nWhen executing any methods via .get(), .post(), etc., you will receive a `requests.models.Response` object. See the the requests documentation for assistance with these objects. Clients use a session (r`equests.models.Session`) object to maintain some state (cookies, etc.), but should generally assume themselves to be stateless.\n\n#### RPC\n\nUsing 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 variables\n\n```python\nfrom fruition.api.client.webservice.rpc.xml.client import XMLRPCClient\nfrom fruition.api.exceptions import (\n BadRequestError,\n UnsupportedMethodError\n)\n\nclient = XMLRPCClient(\"127.0.0.1\")\n\n# Use a method with reserved characters\nmethods = client[\"system.listMethods\"]()\n\nfor method in methods:\n # 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.\n signature = client[\"system.methodSignature\"](method)\n return_type, parameter_types = signature[0], signature[1:] # RPC specification\n print(\"Method {0} takes ({1}) and returns ({2})\".format(method, \", \".join(parameter_types), return_type))\n\ntry:\n method.pow(1, 2, 3)\nexcept BadRequestError:\n # Wrong parameters\nexcept BadResponseError:\n # Wrong response time\nexcept UnsupportedMethodError:\n # Method does not exist\n```\n\nThe base client has handlers for introspection and dispatching requests. Implementations are responsible for formatting requests and parsing responses.\n",
"bugtrack_url": null,
"license": "gpl-3.0",
"summary": "A framework for developing webapps quickly and easily using Python, SQLAlchemy, and Jinja2. Supports numerous protocols, databases, and web drivers.",
"version": "0.9.1",
"project_urls": {
"Homepage": "https://github.com/painebenjamin/fruition"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "b61c8814af257fcaf06fa6dc25040475b1eaed3d9364d545d9dc53c897974d3a",
"md5": "8a2287d871369b03f0f6cf2a8cd6039a",
"sha256": "da7e0ba1268a12a05cc2812d842516caf3b0065edc6b6bf62c6fda308361cf81"
},
"downloads": -1,
"filename": "fruition-0.9.1.tar.gz",
"has_sig": false,
"md5_digest": "8a2287d871369b03f0f6cf2a8cd6039a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 187451,
"upload_time": "2025-01-07T23:39:12",
"upload_time_iso_8601": "2025-01-07T23:39:12.468643Z",
"url": "https://files.pythonhosted.org/packages/b6/1c/8814af257fcaf06fa6dc25040475b1eaed3d9364d545d9dc53c897974d3a/fruition-0.9.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-01-07 23:39:12",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "painebenjamin",
"github_project": "fruition",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "fruition"
}