Getting Started¶
Installation¶
FeretUI needs some dependencies system:
sudo npm install -g less
Install released versions of FeretUI from the Python package index with pip or a similar tool:
pip install feretui (Not ready yet)
Installation via source distribution is via the pyproject.toml
script:
pip install .
Installation will add the feretui
commands to the environment.
Note
FeretUI use Python version >= 3.10
Install your favorite web server¶
FeretUI come without any web server. The web serving is not this job.
The routes to serve FeretUI¶
We need 3 routes :
[GET] client route
[GET] static files route
[GET, POST, PATCH, PUT, DELETE] action route.
Note
The GET, POST, DELETE is the minimum method to use the actions. The other is to add only if you need custom form with theses methods.
Request, Response and Session¶
The objects Request, Response and Session are wrapper class. The goal is to séparate and link the three object come from web server to FeretUI.
FeretUI can not know all the web server and can not be adapted for each. We need a link between them. The responsability to do the link is yours.
Some exemple exist and can copy it or adapt it for your projects.
Client route¶
The client route is the route that return the FeretUI client. the route (name and url) is free.
The route should
session = ... # get the feretui session
myferet = ... # get the instance client
request = Request(session=session, ...) # get the feretui request
response = myferet.render(request) # get the client page
# return the response formated for the web server
static files route¶
The static files route is the route that return the javascript, css, font, images.
the url is /feretui base url/static/filepath
The route should
filepath = ... # get the file path
myferet = ... # get the instance client
return myferet.get_static_file_path(filepath) # return the static file
Action route¶
The action route is used to do action on and with the FeretUI client.
render page
execute a page
…
the url is /feretui base url/static/action
The route should
action = ... # get the action to call
session = ... # get the feretui session
myferet = ... # get the instance client
request = Request(session=session, ...) # get the feretui request
response = myferet.execute_action(request, action) # execute the action
# return the response formated for the web server
Serve FeretUI with bottle¶
Bottle is a fast, simple and lightweight WSGI micro web-framework for Python. It is distributed as a single file module and has no dependencies other than the Python Standard Library.
See the bottle documentation.
For this example you need to install some additional package
pip install bottle BottleSessions
import logging
from contextlib import contextmanager
from os import path
from bottle import abort, app, request, response, route, run, static_file
from BottleSessions import BottleSessions
from multidict import MultiDict
from feretui import FeretUI, Request, Session
logging.basicConfig(level=logging.DEBUG)
@contextmanager
def feretui_session(cls):
session = None
try:
session = cls(**request.session)
yield session
finally:
if session:
request.session.update(session.to_dict())
def add_response_headers(headers) -> None:
for k, v in headers.items():
response.set_header(k, v)
myferet = FeretUI()
# Here define your feretui stuff.
@route('/')
def index():
with feretui_session(Session) as session:
frequest = Request(
method=Request.GET,
querystring=request.query_string,
headers=dict(request.headers),
session=session,
)
res = myferet.render(frequest)
add_response_headers(res.headers)
return res.body
@route('/feretui/static/<filepath:path>')
def feretui_static_file(filepath):
filepath = myferet.get_static_file_path(filepath)
if filepath:
root, name = path.split(filepath)
return static_file(name, root)
abort(404)
@route('/feretui/action/<action>', method=['DELETE', 'GET', 'POST'])
def call_action(action):
with feretui_session(Session) as session:
frequest = Request(
method=getattr(Request, request.method),
querystring=request.query_string,
form=MultiDict(request.forms),
params=MultiDict(request.params),
headers=dict(request.headers),
session=session,
)
res = myferet.execute_action(frequest, action)
add_response_headers(res.headers)
return res.body
if __name__ == "__main__":
app = app()
cache_config = {
'cache_type': 'FileSystem',
'cache_dir': './sess_dir',
'threshold': 2000,
}
BottleSessions(
app, session_backing=cache_config, session_cookie='appcookie')
run(host="localhost", port=8080, debug=True, reloader=1)
Serve FeretUI with flask¶
Flask is a lightweight WSGI web application framework. It is designed to make getting started quick and easy, with the ability to scale up to complex applications. It began as a simple wrapper around Werkzeug and Jinja, and has become one of the most popular Python web application frameworks.
See the flask documentation.
For this example you need to install some additional package
pip install flask
import logging
import urllib
from contextlib import contextmanager
from wsgiref.simple_server import make_server
from flask import Flask, abort, make_response, request, send_file
from multidict import MultiDict
from feretui import FeretUI, Request, Session
logging.basicConfig(level=logging.DEBUG)
app = Flask(__name__)
app.secret_key = b'secret'
@contextmanager
def feretui_session(cls):
from flask import session
fsession = None
try:
fsession = cls(**session)
yield fsession
finally:
if fsession:
session.update(fsession.to_dict())
def response(fresponse):
resp = make_response(fresponse.body)
resp.headers.update(fresponse.headers)
return resp
myferet = FeretUI()
# Here define your feretui stuff.
@app.route('/')
def index():
with feretui_session(Session) as session:
frequest = Request(
method=Request.GET,
querystring=request.query_string.decode('utf-8'),
headers=dict(request.headers),
session=session,
)
return response(myferet.render(frequest))
@app.route('/feretui/static/<path:filepath>')
def feretui_static_file(filepath):
filepath = myferet.get_static_file_path(filepath)
if filepath:
return send_file(filepath)
abort(404)
return None
@app.route('/feretui/action/<action>', methods=['DELETE', 'GET', 'POST'])
def call_action(action):
params = {}
if request.method in ['DELETE', 'POST']:
params = {
x: request.form.getlist(x)
for x in request.form
}
params.update(urllib.parse.parse_qs(
request.query_string.decode('utf-8'),
))
with feretui_session(Session) as session:
frequest = Request(
method=getattr(Request, request.method),
querystring=request.query_string.decode('utf-8'),
form=MultiDict(request.form),
params=params,
headers=dict(request.headers),
session=session,
)
return response(myferet.execute_action(frequest, action))
if __name__ == "__main__":
with make_server('', 8080, app) as httpd:
logging.info("Serving on port 8080...")
httpd.serve_forever()
Serve FeretUI with pyramid¶
Pyramid is a small, fast, down-to-earth, open source Python web framework. It makes real-world web application development and deployment more fun, more predictable, and more productive. Try Pyramid, browse its add-ons and documentation, and get an overview.
See the pyramid documentation.
For this example you need to install some additional package
pip install pyramid, pyramid_beaker
import logging
from contextlib import contextmanager
from wsgiref.simple_server import make_server
from multidict import MultiDict
from pyramid.config import Configurator
from pyramid.httpexceptions import exception_response
from pyramid.response import FileResponse, Response
from pyramid.view import view_config
from pyramid_beaker import session_factory_from_settings
from feretui import FeretUI, Request, Session
logging.basicConfig(level=logging.DEBUG)
@contextmanager
def feretui_session(feretui_session_cls, pyramid_session):
fsession = None
try:
fsession = feretui_session_cls(**pyramid_session)
yield fsession
finally:
if fsession:
pyramid_session.update(fsession.to_dict())
pyramid_session.save()
myferet = FeretUI()
# Here define your feretui stuff.
@view_config(route_name='feretui', request_method='GET')
def feretui(request):
with feretui_session(Session, request.session) as session:
frequest = Request(
method=Request.GET,
querystring=request.query_string,
headers=dict(request.headers),
session=session,
)
response = myferet.render(frequest)
return Response(
response.body,
headers=response.headers,
)
@view_config(route_name='feretui_static_file', request_method='GET')
def feretui_static_file(request):
filepath = myferet.get_static_file_path(
'/'.join(request.matchdict['filepath']),
)
if filepath:
return FileResponse(filepath)
raise exception_response(404)
@view_config(
route_name='call_action',
request_method=('DELETE', 'GET', 'POST'),
)
def call_action(request):
action = request.matchdict['action']
with feretui_session(Session, request.session) as session:
frequest = Request(
method=getattr(Request, request.method),
querystring=request.query_string,
form=MultiDict(request.POST),
params=request.params.dict_of_lists(),
headers=dict(request.headers),
session=session,
)
response = myferet.execute_action(frequest, action)
return Response(
response.body,
headers=response.headers,
)
if __name__ == "__main__":
session_factory = session_factory_from_settings({})
with Configurator() as config:
config.include('pyramid_beaker')
config.set_session_factory(session_factory)
config.add_route('feretui', '/')
config.add_route('feretui_static_file', '/feretui/static/*filepath')
config.add_route('call_action', '/feretui/action/{action}')
config.scan()
app = config.make_wsgi_app()
with make_server('', 8080, app) as httpd:
logging.info("Serving on port 8080...")
httpd.serve_forever()
Install your favorite ORM¶
FeretUI come without any ORM. It is not this job, It not required to have an ORM. You can do without directly with SQL or just with full static page.