{"id":13725556,"url":"https://github.com/mahmoud/clastic","last_synced_at":"2025-04-05T23:07:15.280Z","repository":{"id":5613470,"uuid":"6820956","full_name":"mahmoud/clastic","owner":"mahmoud","description":"🏔️ A functional web framework that streamlines explicit development practices while eliminating global state.","archived":false,"fork":false,"pushed_at":"2024-06-24T23:07:50.000Z","size":1299,"stargazers_count":155,"open_issues_count":2,"forks_count":19,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-04-02T02:54:25.672Z","etag":null,"topics":["middleware","python","web-framework","werkzeug","wsgi"],"latest_commit_sha":null,"homepage":"https://python-clastic.readthedocs.io/en/latest/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mahmoud.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2012-11-23T01:24:04.000Z","updated_at":"2024-12-09T08:43:40.000Z","dependencies_parsed_at":"2023-12-17T21:30:05.650Z","dependency_job_id":"98ec89da-c400-4de9-8012-02d7c716494f","html_url":"https://github.com/mahmoud/clastic","commit_stats":{"total_commits":815,"total_committers":14,"mean_commits":"58.214285714285715","dds":0.354601226993865,"last_synced_commit":"2a2612bc5a2f71e894bebbdbf962fdff988130f0"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mahmoud%2Fclastic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mahmoud%2Fclastic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mahmoud%2Fclastic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mahmoud%2Fclastic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mahmoud","download_url":"https://codeload.github.com/mahmoud/clastic/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247411231,"owners_count":20934653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["middleware","python","web-framework","werkzeug","wsgi"],"created_at":"2024-08-03T01:02:27.245Z","updated_at":"2025-04-05T23:07:15.261Z","avatar_url":"https://github.com/mahmoud.png","language":"Python","funding_links":[],"categories":["Web framework for Python","Python"],"sub_categories":[],"readme":"Clastic\n=======\n\n.. raw:: html\n\n   \u003ca href=\"https://pypi.org/project/clastic/\"\u003e\u003cimg src=\"https://img.shields.io/pypi/v/clastic.svg\"\u003e\u003c/a\u003e\n   \u003ca href=\"https://calver.org/\"\u003e\u003cimg src=\"https://img.shields.io/badge/calver-YY.MINOR.MICRO-22bfda.svg\"\u003e\u003c/a\u003e\n\nA functional Python web framework that streamlines explicit\ndevelopment practices while eliminating global state.\n\nClastic is pure-Python, tested on Python 3.7+, and\n`documented \u003chttps://python-clastic.readthedocs.io/\u003e`_,\nwith `tutorials \u003chttps://python-clastic.readthedocs.io/en/latest/tutorial.html\u003e`_.\n\n.. contents::\n   :depth: 2\n   :backlinks: top\n   :local:\n\n\nQuick Start Guide\n-----------------\n\nInstallation\n^^^^^^^^^^^^\n\nClastic is available `on\nPyPI \u003chttps://pypi.python.org/pypi/clastic\u003e`_. You can install it by\nrunning this command::\n\n  easy_install clastic\n\n(``pip`` works, too.)\n\n\nHello World!\n^^^^^^^^^^^^\n\nGetting up and running with Clastic is exceedingly difficult. Just try\nand create a file called ``hello.py`` with the following\nindecipherable runes:\n\n.. code-block:: python\n\n  from clastic import Application, render_basic\n\n  def hello(name='world'):\n      return 'Hello, %s!' % name\n\n  routes = [('/', hello, render_basic),\n            ('/\u003cname\u003e', hello, render_basic)]\n\n  app = Application(routes)\n  app.serve()\n\nIf you run ``python hello.py`` at the command line and visit\nhttp://localhost:5000 in your browser, you will see the text\n``Hello, world!``. If instead, you visit http://localhost:5000/Ben\nthen you will see the text ``Hello, Ben!``. Madness.\n\n\nGetting fancy with request objects\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIf we add the ``request`` argument to any endpoint function, we get\naccess to all of the request data, including any GET or POST\nparameters or cookies that may have been sent with the request.:\n\n.. code-block:: python\n\n  from clastic import Application, render_basic\n\n  def fancy(request):\n      result = ''\n      # iterate through all of the GET and POST variables\n      for k in request.values:\n          result += \"Found argument '%s' with value '%s'\\n\" % (k, request.values[k])\n      # iterate through all of the cookies\n      for k in request.cookies:\n          result += \"Found cookie '%s' with value '%s'\\n\" % (k, request.cookies[k])\n      return result\n\n  routes = [('/fancy', fancy, render_basic)]\n\n  app = Application(routes)\n  app.serve()\n\nSince we're being fancy, let's create a ``curl`` request which sends a\nGET parameter, a POST parameter, and a cookie::\n\n  curl -X POST --data \"post=posted\" --cookie \"cookie_crisp=delicious\" --url \"http://0.0.0.0:5000/fancy?get=gotten\"\n\nIn response, Clastic sends the following::\n\n  Found argument 'post' with value 'posted'\n  Found argument 'get' with value 'gotten'\n  Found cookie 'cookie_crisp' with value 'delicious'\n\nSo fancy.\n\nIf you're curious how ``request`` got there, read past the end of the\nQuickstart.\n\nPushing the envelope with Response objects\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIn the previous examples, we have been returning strings from our\nendpoints, letting the trusty ``render_basic`` handle the rest. If\nwe want more control, then we can remove ``render_basic`` from the\nroute, opting to instantiate and return our own ``Response`` object\ndirectly.\n\nIn the following example, we alter the response headers and status\ncode to forward the browser back to the main page:\n\n.. code-block:: python\n\n  from clastic import Application, render_basic, Response, redirect\n\n  def home():\n      return 'Home, Sweet Home!'\n\n  def return_home():\n      response = Response()\n\n      # Forward the client browser to the home page.\n      response.headers['Location'] = '/'\n      response.status_code = 301\n\n      return response\n\n  def redirect_home():\n      return redirect('/')\n\n  routes = [('/', home, render_basic),\n            ('/return-home', return_home),\n            ('/redirect-home', redirect_home]\n\n  app = Application(routes)\n  app.serve()\n\nIf you visit the page http://localhost:5000/return-home in your\nbrowser, it will immediately redirect you to the root URL and show the\ntext ``Home, Sweet Home!``.\n\nThe ``Response`` object gives you complete control over all HTTP\nheaders, enabling you to set and delete cookies, play with page\ncaching, set page encoding, and so forth. If that sort of fine-grained\nresponsibility sounds daunting or tedious, you're not alone, which is why\nthe most common operations usually have convenience functions, like\n``redirect()``, which is demonstrated in ``redirect_home()``\nabove. Clastic also has no-nonsense drop-ins for cookies, HTTP\ncaching, and more.\n\nTestimonials\n------------\n\nWhile originally built to host `a simple train schedule site\n\u003chttps://github.com/mahmoud/etavta\u003e`_ and `a few Wikipedia-related\nprojects \u003chttps://github.com/hatnote\u003e`_, Clastic is also used\nfor both internal and production-grade applications at PayPal.\n\n(If your project or company uses Clastic, feel free to file an issue or\nsubmit a pull request to get added to this section.)\n\nMotivation\n----------\n\nClastic was created to fill the need for a minimalist web framework\nthat does exactly what you tell it to, while eliminating common\npitfalls and delays in error discovery. The result is a streamlined\nand deterministic web development experience.\n\nTo put it another way, Clastic is designed such that, by the time your\napplication has loaded, the framework has done all its work and gotten\nout of the way. It doesn't wait until the first request or the first\ntime a URL is hit to raise an exception.\n\nWhat is a web application?\n--------------------------\n\nIn a way, every web framework is a systematic answer to the age-old\nquestion that has baffled humankind until just a few years ago.\n\n.. note::\n   The following is a conceptual introduction, not class\n   reference. Also, don't be fooled by Capital Letters, Clastic really\n   isn't type-heavy.\n\nRequest\n   A single incoming communication from a client (to your\n   application). Encapsulates the WSGI environ, which is just Python's\n   representation of an HTTP request.\n\nResponse\n   An outgoing reply from your application to the client.\n\nA web application exists to accept Requests and produce Responses.\n(Clastic knows that every Request has its Response \u003c3)::\n\n  Request --\u003e [Application] --\u003e Response\n\nRoute\n   A regex-like URL pattern, as associated with an endpoint (and\n   optional renderer).\n\nEndpoint\n   The function or callable that is called when an incoming\n   request matches its associated Route. In Django, this is called a\n   *view*, in most MVC frameworks this is called a *controller*.\n\nRenderer\n   A function that usually takes a dictionary of values and\n   produces a Response. For a typical website, the content of the\n   response is usually the result of a templating engine, JSON\n   encoder, or file reader.\n\nA web application matches a Request's URL to its Routes' patterns. If\nthere are no matches, it returns a 404 Response. If a matching Route\nis found, the Route's endpoint is called. If it returns a Response or\nthe Route doesn't have a Renderer, the Response is sent back\ndirectly. Otherwise, the endpoint's return value is fed into the\nRenderer, which produces the actual Response::\n\n  Request --\u003e Routes --\u003e Endpoint --\u003e (Renderer) --\u003e Response\n\n.. admonition:: A bit of *context*\n\n   It can be useful to think of an application's behavior in terms of\n   overlapping contexts, each with its own lifespan. For instance, a\n   logged-in user's session is a context which can span multiple\n   requests. A database connection has a context, which may be shorter\n   than a Request's context, or longer if your application uses\n   connection pooling.\n\n   Application code can introduce dozens of logical contexts, specific\n   to its function, but at the Clastic level, there are two primary\n   contexts to consider:\n\n   - The Request context, which begins when the Request is constructed\n     by the framework, and usually ends when the Response has been\n     sent back to the client.\n   - The Application context, which begins once an Application is\n     successfully constructed at server startup, and ends when the\n     server running the Application shuts down.\n\n   Concepts discussed above were more oriented to the Request context,\n   the following items are more Application focused.\n\n.. _Resources:\n\nResources\n   A *resource* is a value that is valid for the lifespan of the\n   Application. An example might be a database connection factory, a\n   logger object, or the path of a configuration file. An\n   Application's *resources* refers to a map that gives each resource\n   a name.\n\nRender Factory\n   A callable which, when called with an argument, returns a suitable\n   *renderer*. Consider a ``TemplateRenderFactory``, which, when called\n   with the template filename ``index.html``, returns a function that\n   can be passed a dictionary to render the application's home page.\n\n   A Render Factory is optional. Here are some cases where a Render Factory can be omitted:\n\n   - an application's endpoints return Responses directly (as many\n     applications based directly on Werkzeug do)\n   - render functions are specified explicitly on a per-route basis\n   - the application is using some fancy middleware to generate\n     Responses\n\nMiddleware_\n   Middleware is a way of splitting up and ordering logic in\n   discrete layers. When installed in an Application, Middleware has\n   access to the Request before and after the endpoint and render\n   steps. In Python itself, decorators could be thought of as a form\n   of function middleware.\n\n   There's a lot more to middleware in Clastic, so check out the\n   Middleware_ section for more information, including diagrams of\n   middleware's role in the request flow.\n\nArmed with this information, it's now possible to define what\nconstitutes a web application, and indeed a Clastic Application:\n\nApplication\n   A collection of Resources, list of Routes, and list of Middleware\n   instances, with an optional Render Factory to create the rendering\n   step for each of the routes.\n\nAnd with any luck this simple Application should be even simpler:\n\n.. code-block:: python\n\n   resources = {'start_time': time.time()}\n   middlewares = [CookieSessionMiddleware()]\n   render_factory = TemplateRenderFactory('/path/to/templates/')\n   routes = [('/', hello_world, 'home.html')]\n\n   hello_world_app = Application(routes, resources, middlewares, render_factory)\n\n``hello_world_app`` is a full-blown WSGI application ready for serving\nto any users needing some greeting.\n\n.. note::\n   For the record, the ``Application`` instantiation seen above is exactly\n   what is meant by 'constructing' or 'initializing' an\n   Application. It's just instantiation, nothing more nothing less.\n\nDynamic binding\n---------------\n\nDynamic binding, or dynamic *argument* binding, is the process of\nresolving the arguments and dependencies of endpoints and middlewares\nto produce a rock-solid application. Basically, if a certain endpoint\nfunction takes an argument, Clastic will make sure that argument is\navailable at Application initialization time.\n\nA simple example\n^^^^^^^^^^^^^^^^\n\nArguments are simply checked by name. Consider the following\n\"Hello, World!\" Application:\n\n.. code-block:: python\n\n  from clastic import Application, render_basic\n\n  def hello(name='world'):\n      return 'Hello, %s!' % name\n\n  routes = [('/', hello, render_basic),\n            ('/\u003cname\u003e', hello, render_basic)]\n\n  app = Application(routes)\n  app.serve()\n\nThe ``hello()`` function acts as an endpoint for two Routes, one for\nthe root URL, and one which takes a ``name`` as a URL path segment. On\nvisiting the root URL, one sees ``Hello, world!``, and if a ``name`` is\nprovided, ``Hello, (whatever-was-in-the-URL)``.\n\nIf the ``hello()`` function was changed to read:\n\n.. code-block:: python\n\n  def hello(first_name):\n      return 'Hello, %s!' % first_name\n\nAnd the code was run without other changes, an exception would be\nraised, originating from line 9, ``app = Application(routes)``::\n\n  NameError: unresolved endpoint middleware arguments: set(['first_name'])\n\nHmm, looks like we've got a bug, but at least we caught it early. In\nthe future we should probably use a message bus or maybe Cassandra??\nActually, let's write a quick test:\n\n.. code-block:: python\n\n  def test_hello():\n      assert hello() == 'Hello, world!'\n      assert hello('Justin') == 'Hello, Justin!'\n\nA nice side-effect of Clastic's argument binding is that endpoints\nonly take what they need, meaning endpoint functions can have\neasy-to-test signatures like ``hello(name)``, instead of\n``hello(request, name)``. No need for test clients and mock requests\nand other contrivances where unnecessary.\n\nSources and built-ins\n^^^^^^^^^^^^^^^^^^^^^\n\nThe \"Hello, World!\" example used an argument bound in from the URL, one\nof the four sources for arguments:\n\n- **Route URL pattern**\n- **Application resources** - As `mentioned above`_, arguments which\n  are valid for the lifespan of the Application.\n- **Middleware provides** - Arguments provided by an Application's\n  middleware. See Middleware_ for more information.\n- **Clastic built-ins** - Special arguments that are always made\n  available by Clastic. These arguments are also reserved, and\n  conflicting names will raise an exception. `A list of these arguments\n  and their meanings is below.`__\n\n.. _mentioned above: Resources_\n__ `List of built-ins`_\n\nList of built-ins\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nClastic provides a small, but powerful set of six built-in arguments\nfor every occasion. These arguments are reserved by Clastic, so know\nthem well.\n\n``request``\n   Probably the most commonly used built-in, ``request`` is the\n   current ``Request`` object being handled by the Application. It has\n   the URL arguments, POST parameters, cookies, user agent, other HTTP\n   headers, and everything from the WSGI environ.\n\n``next``\n   ``next`` is only for use by Middleware, and represents the\n   next function in the execution chain. It is called with the\n   arguments the middleware class declared that it would provide. If\n   the middleware does not provide any arguments, then it is called\n   with no arguments.\n\n   ``next`` allows a middleware to not worry about what middleware or\n   function comes after it in the chain. All the middleware knows is\n   that the result of (or exception raised by) the ``next`` function\n   is the Response that a client would receive.\n\n   Middleware functions must accept ``next`` as the first argument. If\n   a middleware function does not accept the ``next`` argument, or if\n   a non-middleware function accepts the ``next`` argument, an\n   exception is raised at Application initialization.\n\n``context``\n   ``context`` is the output of the endpoint side of the middleware\n   chain. By convention, it is almost always a dictionary of values\n   meant to be used in templating or other sorts of Response\n   serialization.\n\n   Accepting the ``context`` built-in outside of the render branch of\n   middleware will cause an exception to be raised at Application\n   initialization.\n\nThe following built-ins are considered primarily for internal and\nadvanced usage, and are thus prefixed with an underscore.\n\n``_application``\n   The ``Application`` instance in which this middleware or endpoint\n   is currently embedded. The Application has access to all routes,\n   endpoints, middlewares, and other fun stuff, which makes\n   ``_application`` useful for introspective activities, like those\n   provided by Clastic's built-in ``MetaApplication``.\n\n``_route``\n   The Route which was matched by the URL and is currently being\n   executed. Also mostly introspective in nature. ``_route`` has a lot\n   of useful attributes, such as ``endpoint``, which can be used to\n   shortcut execution in an extreme case.\n\nAnd, that's it! All other argument names are unreserved and yours for\nthe binding.\n\nConstraints\n^^^^^^^^^^^\n\nClastic's dynamic binding system makes for concise, testable web\napplications, free of global state and whole classes of common bugs,\nbut there are a couple implications.\n\nNo anonymous arguments\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nThis means that Clastic does not support functions which use ``*args``\nor ``**kwargs`` as part of a Route's function chain. In practice, such\nsignatures reduce testability, introspectability, and debuggability,\nwhile providing little benefit to endpoints and middlewares. As a\nresult, Clastic actively discourages their use; currently the presence\nof such functions does not raise an exception, but this behavior may\nchange.\n\nThere is one substantial exception to this assertion, which is that of\nfunction decorators, which make extensive use of ``*args`` and\n``**kwargs``, and of which Clastic is a close cousin. To use\ndecorators, simply import ``clastic_decorator`` and decorate your\ndecorator, like so:\n\n.. code-block:: python\n\n  from clastic.decorators import clastic_decorator\n  cl_my_deco = clastic_decorator(my_deco)\n\n``clastic_decorator`` simply wraps another decorator in a way that\nlifts the eventually decorated function's signature so that it remains\nvisible to the rest of the Clastic system.\n\nNamed URL parameters\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nAs a corallary to the above, all parameters in the URL pattern are\nrequired to be named, which in practice, makes for a cleaner and more\ntestable application. For the few Routes that might actually use such\nURLs, simply use a ``path`` converter to capture arbitrarily long\nsegments and split it in middleware or the endpoint itself.\n\nNaming conflicts\n\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\"\n\nAlmost every system has the potential for naming conflicts and Clastic\nis no exception. The good news is that Clastic actively checks for\nsuch conflicts at Application initialization. This early-warning\nsystem means naming conflicts are only ever encountered during\ndevelopment, circumventing the much worse and much more common\nscenario of accidental overriding in production.\n\nBecause each Route is independent, and there is no global state,\nthere's no way for one Route's URL parameters to get intermingled with\none another, but it is possible for a URL parameter to conflict with\nan Application's resources or middleware-provided arguments. in the\nevent of such a conflict an error like the following would be raised\nat Application initialization::\n\n   NameError: found conflicting provides: [('name', (u'url', u'resources'))]\n\nWhich means that ``name`` was provided by both the Route's URL and the\nApplication's resources.\n\nIn practice, Clastic naming conflicts are rare and easily\nresolvable. Resolution leads to less ambiguous, more maintainable\ncode, and the application developer lives to see another day.\n\n\nMiddleware\n----------\n\nMiddleware can be a very useful way to provide separation of\nhorizontal concerns from the actual application logic. Common uses\ninclude logging, caching, request serialization/deserialization,\nperformance profiling, and even compression. Including these functions\nin all endpoint functions would be bad design, not to mention a\ndownright tedious task.\n\nOne of Clastic's most defining features may well be its interpretation\nof middleware. As opposed to simple pre- and post-request hooks,\nClastic middlewares use real function-nesting scope. Furthermore,\nmiddlewares are dependency-checked to minimize breakage caused by\nordering or accidental omission.\n\nFlow\n^^^^\n\nA request flows from the client, to the server, through the\nmiddlewares, to the endpoint/render functions, which produce a\nresponse. The response then travels back through the middlewares, in\nreverse order, to the server, which relays it to the client.\n\nMiddleware is often described using an onion analogy, wherein the\nfirst middleware gets first say on the request and last say on the\nresponse. For example, given middlewares \"A\" and \"B\"::\n\n  --Request--\u003e A --\u003e B --\u003e Endpoint --\u003e B --\u003e A --Response--\u003e\n\nWithin each individual middleware class (e.g., \"A\"), there are three\nfunctions which Clastic will look for and call:\n\n- ``request()`` - most commonly used\n- ``endpoint()`` - post-routing, pre-logic\n- ``render()`` - post-logic, pre-response, when applicable\n  (e.g., template context processing)\n\nThose are terse descriptions, but that's ok, because all you need to\nremember is: **\"Dial 'M' for Middleware\"**::\n\n\n\n            (endpoint)   (render)\n                |\\         /|\n                | \\       / |\n  mw.endpoint() |  \\     /  |  mw.render()\n                ^   \\   /   v\n                |    \\ /    |\n        -- -- --|-- --*-- --|-- -- --\n                |           |\n  mw.request()  ^           v  mw.request()\n                |           |\n                |           |\n           (Request)     (Response)\n\n\nTo summarize, if a middleware has a ``request`` function, it will be\ncalled such that it wraps both endpoint and render steps, whereas\n``endpoint`` and ``render`` functions only wrap their respective\ndomains. A middleware class can implement all or none of these\nfunctions.\n\nBecause Clastic middlewares use nested function scopes, Clastic's\nmiddleware system is essentially a dynamic and specialized decorator\nsystem. Middleware effectively provides hooks for decorating many\nendpoints at once.\n\n.. note::\n\n   The ***** at the center vertex of the 'M' represents a checkpoint\n   of sorts: If the return value of the endpoint + endpoint\n   middlewares is a ``Response`` object, it will be returned directly,\n   skipping the ``render`` vertex of the M completely, but still\n   executing the outgoing request middlewares.\n\nState\n^^^^^\n\nIn any framework, all but the simplest middlewares serve some stateful\npurpose. Even a simple timer middleware needs to associate a request\nwith a response to calculate how much time elapsed in between. In\nother middleware paradigms, this state usually ends up attached to the\n``request`` object, or worse, somewhere in global state:\n\n.. code-block:: python\n\n   class DjangoTimingMiddleware(object):\n       # Django-like, might be somewhat simplified\n\n       def process_request(self, request):\n           request.start_time = time.time()\n\n       def process_response(self, request, response):\n           total_time = time.time() - request.start_time\n           return response\n\n       def process_exception(self, request, exception):\n           ...  # TODO: exception handling\n\nIn Clastic, this would look like:\n\n.. code-block:: python\n\n   class TimingMiddleware(Middleware):\n       def request(self, next):\n           start_time = time.time()\n           try:\n               ret = next()\n           except:\n               raise  # TODO: exception handling\n           total_time = time.time() - start_time\n           return ret\n\nIn this case, local function scope suffices for our calculation, no\nneed to mutate the request. However, if the middleware did want to\nprovide something new, it could use the provides system to do so.\n\nProvides\n^^^^^^^^\n\nOften, well-intentioned middlewares want to give a little something\nback. Clastic let's them do this with *provides*. For an example of\nthis, here's an ever-so-slightly simplified version of Clastic's basic\nbuilt-in cookie session middleware:\n\n.. code-block:: python\n\n    class CookieSessionMiddleware(Middleware):\n        provides = ('session',)\n\n        def __init__(self, cookie_name='clastic_session', secret_key=None):\n            self.cookie_name = cookie_name\n            self.secret_key = secret_key or os.urandom(20)\n\n        def request(self, next, request):\n            session = load_cookie(request, self.cookie_name, self.secret_key)\n            response = next(session=session)\n            session.save_cookie(response, key=self.cookie_name)\n            return response\n\nNotice how the ``provides`` class variable, and how the ``next()``\nfunction is called with the ``session`` keyword argument. The endpoint\nand nested middlewares now have access to the session, should they\nneed it, while middlewares before ``CookieSessionMiddleware`` do not.\n\n.. admonition:: Middleware provides vs. resources\n\n   Should a value come from middleware or from the resources? Reading\n   the conceptual overview should make this distinction much easier:\n   provides are for the lifetime of the *request*, whereas resources\n   are for the lifetime of an *application*. A session-store\n   connection *factory* is a good resource, but the session retrieved\n   is best provided by middleware (if not in the application logic).\n\n\nEnhancing reusability and testability\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nDevelopers using Clastic to its fullest can use middleware to\ndrastically increase the reusability of their code. Middlewares can be\nused to extract variables from the ``request`` and any other complex\nobjects, then provided to endpoints with much more reusable and\ntestable usage patterns.\n\nOther frameworks require ``request`` to be passed in as an argument,\neven when the endpoint doesn't need it. Still other frameworks provide\n``request`` as a threadlocal (thread-**global** anyone?), but this\nstill makes for harder-to-test code when an endpoint actually does use\na resource provided by request.\n\nClastic lets you lift nearly anything into a wrapping middleware, so\nit's even possible to make Routes that use builtins like ``abs()`` and\n``dict()`` as endpoints.\n\n\nCompared to Django\n------------------\n\nClastic is intentionally much less comprehensive of a web development\nsuite. Django can be great for beginners or prototypes, and can be\nmade to work for larger projects, but experienced developers know what\nworks for them, and Django can get in the way. (Fun Fact:\nfunction-based view deprecation was the straw that led to Clastic)\n\nHere are some Clastic features that might appeal to fellow veteran\nDjangonauts:\n\nProactive URL route checking\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nFor an example of the aggressive checking Clastic provides, consider\nthe following Django URL route:\n\n.. code-block:: python\n\n   (r'^articles/(?P\u003cyear\u003e\\d{4})/$', 'news.views.year_archive')\n\nAnd view function:\n\n.. code-block:: python\n\n    def year_archive(year, month):\n        pass\n\nThe URL routing rule arguments and view function signature don't\nmatch, but a Django application will happily start up without\ncomplaints, only to 500 on the first access of that URL.\n\nIn Clastic, this sort of mismatch will raise an exception when the\nApplication is constructed.\n\nBetter control around application initialization\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nIn Django, applications and middleware have no way to detect when they\nare fully loaded by the server. Django's lazy loading means middleware\naren't even initialized until the first request. For more information,\nsee `this Django bug report`_ which led to corrected Django documentation.\n\n.. _this Django bug report:\n   https://code.djangoproject.com/ticket/18577\n\nImproved middleware paradigm\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nClastic is all about middleware. Middleware provides modularity with\nnesting semantics. Clastic takes the most literal approach to this\npossible, using actual function nesting, while Django attempts to\nmimic this with a set of hooks. During the context of a request,\nmiddleware calls are not actually nested, and there is no middleware\nscope, which usually results in the request object becoming a dumping\nground for middleware context.\n\nThere are also certain conditions under which the Django framework\nitself may cause an error or reraise an exception in such a way that a\nmiddleware's exception hook is called without having its\nprocess_request hook called. Not only does this make tracking down a\nparticular bug difficult, but unless middleware is built extremely\nconservatively (i.e., assuming nothing; doing an excess of checks),\nmiddleware errors can mask the original exception.\n\nNo global state\n^^^^^^^^^^^^^^^\n\nDjango is beyond dependent on global state. One need look no further\nthan ``settings.py``; while allegedly modular, Django's ORM and\ntemplating systems cannot be used independently without a settings\nmodule, sometimes an environment variable. Furthermore, it's not\npossible or safe to have more than one Django project in one\nprocess. The settings and models would overwrite one another.\n\nThis makes Django much less flexible for highly-concurrent or\nprogrammatic usage, but to be fair, other than settings.py filling up\nwith loggers and other globals, Django's global state isn't the direct\nconcern of most developers.\n\nThat said, Clastic was built 100% free of global state, and provides a\nmodel for application developers to do the same. In addition,\nClastic's model offers some neat functional features, such as\napplication composability, the ability to embed an application within\nanother, and dependency checking.\n\nORM usage\n^^^^^^^^^\n\nDjango has an ORM. Clastic is ORM-agnostic.\n\nThere is an excess of discussion on the pros and cons of ORMs, so\nsuffice to say that a large portion of experienced engineers find ORM\nusage to be detrimental in larger projects. The usual reasoning is\nthat ORMs make CRUD operations easy, but eventually get in the way of\nconstructing and tuning more advanced queries.\n\nPortability is a common concern, but very rarely does a real project\nswitch their RDBMS, if they use relational storage at all. There are\nexceptions, but practically speaking, a project runs one of MySQL,\nOracle, or Postgres in production and that or SQLite in\nstaging/test/local. In fact, for every sizable project that eventually\nmigrates from MySQL to PostgreSQL, there are at least two which would\nbenefit from learning and using proprietary features specific to their\nchosen database.\n\nWithout getting too deep into the dangers of lazy query execution,\nlet's just say that ORMs, while handy for the short-term and alluring\nin the long-term, can make some things appear too easy, resulting in a\ntemplate accidentally issuing thousands of queries. It's because of\nthe obvious nuances that Clastic is not anti-ORM, per se, but doesn't\nconsider an ORM to be a feature. Every developer has an opinion, and\nevery project has its needs, so feel free to use Clastic with straight\nSQL, SQLAlchemy, your non-relational backend of choice, or even\nDjango's ORM.\n\nEasier WSGI integration\n^^^^^^^^^^^^^^^^^^^^^^^\n\nFor as many claims as its docs make to being standard Python, Django\nmakes `WSGI slightly choreful`_, which is a shame, because `WSGI`_ has\nblessed Python with so many neat servers that work with any WSGI\napplication.\n\nClastic applications are themselves WSGI applications. There's no need\nfor special one-off modules or imports.\n\n.. _WSGI slightly choreful:\n   https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/\n\n.. _WSGI: http://wsgi.readthedocs.org/en/latest/what.html\n\n\nThanks\n------\n\nThanks to the following folks for helping make Clastic:\n\n- `Kurt Rose`_ - Design review and implementation\n- `Justin van Winkle`_ - Inspiration\n- Pocoo_ and the Werkzeug_ team - For a very great WSGI toolkit\n\nAnd thanks to *you* for making it this far in the docs!\n\n.. _Kurt Rose: //github.com/doublereedkurt\n.. _Justin van Winkle: //twitter.com/jvantastic\n.. _Pocoo: //pocoo.org\n.. _Werkzeug: //werkzeug.pocoo.org\n\n\nMisc\n----\n\n- `Tarball of Clastic 0.3.0 \u003chttps://pypi.python.org/packages/source/c/clastic/clastic-0.3.0.tar.gz#md5=3672ea706921353458fce7714140bde2\u003e`_\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmahmoud%2Fclastic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmahmoud%2Fclastic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmahmoud%2Fclastic/lists"}