Package: lc-flask-routes
flask-routes-pyOverviewThe Flask web framework provides the decorator@app.routeto define handlers for routes, which is the recommended method for simple web apps. However, using this method requires
that route handlers be tied to a specific app, and can make it difficult to track/unit test routes for larger web apps.flask-routes-pyprovides mixins for defining routes as classes with static (classmethod) handlers, with optional extras
like integrated request parameter parsing withflask-reqparser-py, and
automated route discovery and registration withlc-registry.InstallationInstall from PyPi (preferred method)pipinstalllc-flask-routes
pipinstalllc-flask-routes[reqparser]# enable support for lc_flask_reqparserpipinstalllc-flask-routes[registry]# enable support for lc_registrypipinstalllc-flask-routes[all]# enable all optionsInstall from GitHub with Pippipinstallgit+https://github.com/libcommon/flask-routes-py.git@vx.x.x#egg=lc_flask_routeswherex.x.xis the version you want to download.Install by Manual DownloadTo download the source distribution and/or wheel files, navigate tohttps://github.com/libcommon/flask-routes-py/tree/releases/vx.x.x/dist, wherex.x.xis the version you want to install,
and download either via the UI or with a tool like wget. Then to install run:pipinstall<downloadedfile>Donotchange the name of the file after downloading, as Pip requires a specific naming convention for installation files.Dependenciesflask-routes-pydepends on, and is designed to work with, theFlask framework.  Optional dependencies also includeflask-reqparser-pyfor integrated request parameter parsing, andlc-registryfor route auto-discovery. Only Python versions >= 3.6 are
officially supported.Getting StartedDefine a base class that extends either theBaseRouteMixinorBaseRouteWithParserMixinclass
(if installed with[reqparser]or[all]options) that all other route classes will extend. This class could
contain any helper methods or class variables needed by all routes, but shouldnotdefine theROUTE_MAPclass
variable.fromtypingimportAny,Dict,Optionalimportflaskfromlc_flask_routesimport(BaseRouteMixin,BaseRouteWithParserMixin,RouteResponse,WerkzeugLocalProxy)fromlc_flask_reqparserimportRequestParserclassBaseRoute(BaseRouteMixin):"""Base route."""__slots__=()classBaseParserRoute(BaseRouteWithParserMixin):"""Base route with request parser."""__slots__=()@classmethoddefgen_request_parser(cls)->Optional[RequestParser]:return(RequestParser().add_argument("base_argument_for_all_routes"))classIndexRoute(BaseRoute):"""Route: /Endpoint: "index"Description: Splash page"""__slots__=()ROUTE_MAP={"/":{"endpoint":"index","methods":["GET","POST"]}}@classmethoddefget(cls,app:WerkzeugLocalProxy,request:WerkzeugLocalProxy,session:WerkzeugLocalProxy,route_kwargs:Dict[str,Any])->RouteResponse:return"<h1>Splash Page</h1>"@classmethoddefpost(cls,app:WerkzeugLocalProxy,request:WerkzeugLocalProxy,session:WerkzeugLocalProxy,route_kwargs:Dict[str,Any])->RouteResponse:returnflask.redirect(url_for("index"))if__name__=="__main__":app=flask.Flask(__name__)IndexRoute.register_route(app)app.run()TheROUTE_MAPclass variable should only be defined on non-base routes, and its structure mimics the parameters
for Flask's@app.routedecorator, which really callsadd_url_ruleunder the hood. Each key is a URI, and each corresponding value are the keyword arguments passed toadd_url_rule. See Flask's documentation
for expectations and limitations of that function.BaseRouteMixinhas aregister_routemethod that accomplishes the same result as@app.route.
Each route class must be registered individually (unless usingRouteRegistryMixin- see below).If installed with the[registry]or[all]options,flask-routes-pyalso exposes aRouteRegistryMixinclass to be used for auto-discovery
of route classes using metaprogramming. Define a Pythonmetaclassthat extendsRouteRegistryMixin, then define a base class that uses thismetaclass.fromabcimportABCMetafromtypingimportAny,Dictimportflaskfromlc_flask_routesimport(BaseRouteMixin,BaseRouteWithParserMixin,RouteResponse,WerkzeugLocalProxy)classRouteRegistry(RouteRegistryMixin,ABCMeta):"""Route registry."""__slots__=()_REGISTRY=dict()classBaseRoute(BaseRouteMixin,metaclass=RouteRegistry):"""Base route. Any child class will be registered inRouteRegistry's _REGISTRY class variable automatically(as long as it's in scope)."""classIndexRoute(BaseRoute):"""Route: /Endpoint: "index"Description: Splash page"""__slots__=()ROUTE_MAP={"/":{"endpoint":"index","methods":["GET"]}}@classmethoddefget(cls,app:WerkzeugLocalProxy,request:WerkzeugLocalProxy,session:WerkzeugLocalProxy,route_kwargs:Dict[str,Any])->RouteResponse:return"<h1>Splash Page</h1>"classLoginRoute(BaseRoute):"""Route: /loginEndpoint: "login"Description: Login workflow"""__slots__=()ROUTE_MAP={"/login":{"endpoint":"login","methods":["POST"]}}@classmethoddefpost(cls,app:WerkzeugLocalProxy,request:WerkzeugLocalProxy,session:WerkzeugLocalProxy,route_kwargs:Dict[str,Any])->RouteResponse://Dosomeworktoverifyidentityreturnflask.redirect(url_for("index"))if__name__=="__main__":app=flask.Flask(__name__)# Register IndexRoute and LoginRoute with appRouteRegistry.register_routes(app)app.run()RouteRegistryMixinexposes two methods for registering routes with an app:register_routesandregister_routes_where.
As shown above,register_routeswill register all routes in the registry without any filtering, whereasregister_routes_whereevaluates a provided predicate on reach route class before registering it. This could be useful, for example, if the same registry is being used
to initialize two different apps based on a class variable.Contributing/SuggestionsContributions and suggestions are welcome! To make a feature request, report a bug, or otherwise comment on existing
functionality, please file an issue. For contributions please submit a PR, but make sure to lint, type-check, and test
your code before doing so. Thanks in advance!