| | |
| | | lodged. To give context to discussions that follow, we detail some of the |
| | | design decisions and trade-offs here. In some cases, we acknowledge that the |
| | | framework can be made better and we describe future steps which will be taken |
| | | to improve it; in some cases we just file the challenge as "noted", as |
| | | to improve it; in some cases we just file the challenge as noted, as |
| | | obviously you can't please everyone all of the time. |
| | | |
| | | Pyramid Provides More Than One Way to Do It |
| | |
| | | developer. It was also strange for the Pylons and BFG development |
| | | communities to be in competition for the same set of users, given how similar |
| | | the two frameworks were. So the Pylons and BFG teams began to work together |
| | | to form a plan to "merge". The features missing from BFG (notably |
| | | :term:`view handler` classes, flash messaging, and other minor missing bits), |
| | | were added, to provide familiarity to ex-Pylons users. The result is |
| | | :app:`Pyramid`. |
| | | to form a plan to merge. The features missing from BFG (notably :term:`view |
| | | handler` classes, flash messaging, and other minor missing bits), were added, |
| | | to provide familiarity to ex-Pylons users. The result is :app:`Pyramid`. |
| | | |
| | | The Python web framework space is currently notoriously balkanized. We're |
| | | truly hoping that the amalgamation of components in :app:`Pyramid` will |
| | |
| | | and unavoidable, at least if we aim to prevent pointless duplication at |
| | | higher levels. If we've done our job well enough, the various audiences will |
| | | be able to coexist and cooperate rather than firing at each other across some |
| | | imaginary web framework "DMZ". |
| | | imaginary web framework DMZ. |
| | | |
| | | Pyramid Uses A Zope Component Architecture ("ZCA") Registry |
| | | ----------------------------------------------------------- |
| | |
| | | Problems |
| | | ++++++++ |
| | | |
| | | The "global" API that may be used to access data in a ZCA "component |
| | | registry" is not particularly pretty or intuitive, and sometimes it's just |
| | | plain obtuse. Likewise, the conceptual load on a casual source code reader |
| | | of code that uses the ZCA global API is somewhat high. Consider a ZCA |
| | | neophyte reading the code that performs a typical "unnamed utility" lookup |
| | | using the :func:`zope.component.getUtility` global API: |
| | | The global API that may be used to access data in a ZCA component registry |
| | | is not particularly pretty or intuitive, and sometimes it's just plain |
| | | obtuse. Likewise, the conceptual load on a casual source code reader of code |
| | | that uses the ZCA global API is somewhat high. Consider a ZCA neophyte |
| | | reading the code that performs a typical "unnamed utility" lookup using the |
| | | :func:`zope.component.getUtility` global API: |
| | | |
| | | .. ignore-next-block |
| | | .. code-block:: python |
| | |
| | | settings = getUtility(ISettings) |
| | | |
| | | After this code runs, ``settings`` will be a Python dictionary. But it's |
| | | unlikely that any "civilian" would know that just by reading the code. There |
| | | unlikely that any civilian would know that just by reading the code. There |
| | | are a number of comprehension issues with the bit of code above that are |
| | | obvious. |
| | | |
| | |
| | | problem number one. |
| | | |
| | | Second, what's this ``ISettings`` thing? It's an :term:`interface`. Is that |
| | | important here? Not really, we're just using it as a "key" for some lookup |
| | | important here? Not really, we're just using it as a key for some lookup |
| | | based on its identity as a marker: it represents an object that has the |
| | | dictionary API, but that's not very important in this context. That's |
| | | problem number two. |
| | |
| | | variable. Using an API that consults a thread local makes understanding how |
| | | it works non-local. |
| | | |
| | | You've now bought in to the fact that there's a registry that is just |
| | | "hanging around". But how does the registry get populated? Why, via code |
| | | that calls directives like ``config.add_view``. In this particular case, |
| | | however, the registration of ``ISettings`` is made by the framework itself |
| | | "under the hood": it's not present in any user configuration. This is |
| | | extremely hard to comprehend. Problem number six. |
| | | You've now bought in to the fact that there's a registry that is just hanging |
| | | around. But how does the registry get populated? Why, via code that calls |
| | | directives like ``config.add_view``. In this particular case, however, the |
| | | registration of ``ISettings`` is made by the framework itself under the hood: |
| | | it's not present in any user configuration. This is extremely hard to |
| | | comprehend. Problem number six. |
| | | |
| | | Clearly there's some amount of cognitive load here that needs to be borne by |
| | | a reader of code that extends the :app:`Pyramid` framework due to its use of |
| | |
| | | needn't understand the ZCA API either. |
| | | |
| | | Hiding the ZCA API from application developers and code readers is a form of |
| | | enhancing "domain specificity". No application developer wants to need to |
| | | enhancing domain specificity. No application developer wants to need to |
| | | understand the small, detailed mechanics of how a web framework does its |
| | | thing. People want to deal in concepts that are closer to the domain they're |
| | | working in: for example, web developers want to know about *users*, not |
| | |
| | | it. So, for the purposes of being kind to third-party :app:`Pyramid` |
| | | framework developers in, we've drawn some lines in the sand. |
| | | |
| | | In all "core" code, We've made use of ZCA global API functions such as |
| | | In all core code, We've made use of ZCA global API functions such as |
| | | ``zope.component.getUtility`` and ``zope.component.getAdapter`` the exception |
| | | instead of the rule. So instead of: |
| | | |
| | |
| | | mapping is done. |
| | | |
| | | - Features. The ZCA component registry essentially provides what can be |
| | | considered something like a "superdictionary", which allows for more |
| | | complex lookups than retrieving a value based on a single key. Some of |
| | | this lookup capability is very useful for end users, such as being able to |
| | | register a view that is only found when the context is some class of |
| | | object, or when the context implements some :term:`interface`. |
| | | considered something like a superdictionary, which allows for more complex |
| | | lookups than retrieving a value based on a single key. Some of this lookup |
| | | capability is very useful for end users, such as being able to register a |
| | | view that is only found when the context is some class of object, or when |
| | | the context implements some :term:`interface`. |
| | | |
| | | - Singularity. There's only one "place" where "application configuration" |
| | | - Singularity. There's only one place where "application configuration" |
| | | lives in a :app:`Pyramid` application: in a component registry. The |
| | | component registry answers questions made to it by the framework at runtime |
| | | based on the configuration of *an application*. Note: "an application" is |
| | |
| | | via a well-defined and widely understood plugin architecture. As long as |
| | | framework developers and extenders understand the ZCA registry, it's |
| | | possible to extend :app:`Pyramid` almost arbitrarily. For example, it's |
| | | relatively easy to build a directive that registers several views "all at |
| | | once", allowing app developers to use that directive as a "macro" in code |
| | | relatively easy to build a directive that registers several views all at |
| | | once, allowing app developers to use that directive as a "macro" in code |
| | | that they write. This is somewhat of a differentiating feature from other |
| | | (non-Zope) frameworks. |
| | | |
| | |
| | | need to use only the documented APIs; that's why we document them as APIs. |
| | | |
| | | If you *extend* or *develop* :app:`Pyramid` (create new directives, use some |
| | | of the more obscure "hooks" as described in :ref:`hooks_chapter`, or work on |
| | | of the more obscure hooks as described in :ref:`hooks_chapter`, or work on |
| | | the :app:`Pyramid` core code), you will be faced with needing to understand |
| | | at least some ZCA concepts. In some places it's used unabashedly, and will |
| | | be forever. We know it's quirky, but it's also useful and fundamentally |
| | |
| | | ``application/json`` (or, for that matter, ``application/*``) in its |
| | | ``Accept`` request header. |
| | | |
| | | "Under the hood", these features make no use of interfaces. |
| | | Under the hood, these features make no use of interfaces. |
| | | |
| | | Many "prebaked" predicates exist. However, use of only "prebaked" predicates, |
| | | Many prebaked predicates exist. However, use of only prebaked predicates, |
| | | however, doesn't entirely meet Ian's criterion. He would like to be able to |
| | | match a request using a lambda or another function which interrogates the |
| | | request imperatively. In :mod:`repoze.bfg` version 1.2, we acommodate this by |
| | | allowing people to define "custom" view predicates: |
| | | request imperatively. In :mod:`repoze.bfg` version 1.2, we acommodate this |
| | | by allowing people to define custom view predicates: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | resource tree, you needn't use it at all. Instead, use :term:`URL dispatch` |
| | | to map URL paths to views. |
| | | |
| | | The idea that some folks believe traversal is unilaterally "wrong" is |
| | | The idea that some folks believe traversal is unilaterally wrong is |
| | | understandable. The people who believe it is wrong almost invariably have |
| | | all of their data in a relational database. Relational databases aren't |
| | | naturally hierarchical, so "traversing" one like a tree is not possible. |
| | | naturally hierarchical, so traversing one like a tree is not possible. |
| | | |
| | | However, folks who deem traversal unilaterally wrong are neglecting to take |
| | | into account that many persistence mechanisms *are* hierarchical. Examples |
| | |
| | | Traversal also offers better composability of applications than URL dispatch, |
| | | because it doesn't rely on a fixed ordering of URL matching. You can compose |
| | | a set of disparate functionality (and add to it later) around a mapping of |
| | | view to resource more predictably than trying to get "the right" ordering of |
| | | view to resource more predictably than trying to get the right ordering of |
| | | URL pattern matching. |
| | | |
| | | But the point is ultimately moot. If you don't want to use traversal, you |
| | |
| | | |
| | | Many web frameworks (Zope, TurboGears, Pylons 1.X, Django) allow for their |
| | | variant of a :term:`view callable` to accept arbitrary keyword or positional |
| | | arguments, which are "filled in" using values present in the ``request.POST`` |
| | | or ``request.GET`` dictionaries or by values present in the "route match |
| | | dictionary". For example, a Django view will accept positional arguments |
| | | arguments, which are filled in using values present in the ``request.POST`` |
| | | or ``request.GET`` dictionaries or by values present in the route match |
| | | dictionary. For example, a Django view will accept positional arguments |
| | | which match information in an associated "urlconf" such as |
| | | ``r'^polls/(?P<poll_id>\d+)/$``: |
| | | |
| | |
| | | Pyramid Provides Too Few "Rails" |
| | | -------------------------------- |
| | | |
| | | By design, :app:`Pyramid` is not a particularly "opinionated" web framework. |
| | | By design, :app:`Pyramid` is not a particularly opinionated web framework. |
| | | It has a relatively parsimonious feature set. It contains no built in ORM |
| | | nor any particular database bindings. It contains no form generation |
| | | framework. It has no administrative web user interface. It has no built in |
| | |
| | | |
| | | :app:`Pyramid` provides some features that other web frameworks do not. |
| | | These are features meant for use cases that might not make sense to you if |
| | | you're building a simple "bespoke" web application: |
| | | you're building a simple bespoke web application: |
| | | |
| | | - An optional way to map URLs to code using :term:`traversal` which implies a |
| | | walk of a :term:`resource tree`. |
| | |
| | | - A declarative :term:`authorization` system. |
| | | |
| | | - Multiple separate I18N :term:`translation string` factories, each of which |
| | | can name its own "domain". |
| | | can name its own domain. |
| | | |
| | | These features are important to the authors of :app:`Pyramid`. The |
| | | :app:`Pyramid` authors are often commissioned to build CMS-style |
| | | applications. Such applications are often "frameworky" because they have |
| | | more than one deployment. Each deployment requires a slightly different |
| | | applications. Such applications are often frameworky because they have more |
| | | than one deployment. Each deployment requires a slightly different |
| | | composition of sub-applications, and the framework and sub-applications often |
| | | need to be *extensible*. Because the application has more than one |
| | | deployment, pluggability and extensibility is important, as maintaining |
| | | multiple forks of the application, one per deployment, is extremely |
| | | undesirable. Because it's easier to extend a system that uses |
| | | :term:`traversal` "from the outside" than it is to do the same in a system |
| | | that uses :term:`URL dispatch`, each deployment uses a :term:`resource tree` |
| | | :term:`traversal` from the outside than it is to do the same in a system that |
| | | uses :term:`URL dispatch`, each deployment uses a :term:`resource tree` |
| | | composed of a persistent tree of domain model objects, and uses |
| | | :term:`traversal` to map :term:`view callable` code to resources in the tree. |
| | | The resource tree contains very granular security declarations, as resources |
| | |
| | | making sure you don't need to know about them up-front. You can build |
| | | "Pylons-1.X-style" applications using :app:`Pyramid` that are purely bespoke |
| | | by ignoring the features above. You may find these features handy later |
| | | after building a "bespoke" web application that suddenly becomes popular and |
| | | after building a bespoke web application that suddenly becomes popular and |
| | | requires extensibility because it must be deployed in multiple locations. |
| | | |
| | | Pyramid Is Too Big |
| | |
| | | expect that models are ORM models, controllers are classes that have methods |
| | | that map to URLs, and views are templates. :app:`Pyramid` indeed has each of |
| | | these concepts, and each probably *works* almost exactly like your existing |
| | | "MVC" web framework. We just don't use the "MVC" terminology, as we can't |
| | | "MVC" web framework. We just don't use the MVC terminology, as we can't |
| | | square its usage in the web framework space with historical reality. |
| | | |
| | | People very much want to give web applications the same properties as common |
| | |
| | | tortuously couching concepts in the most generic terms possible in order to |
| | | account for the use of the term "MVC" by current web frameworks. I doubt |
| | | such a broad definition would ever be agreed to by the original authors of |
| | | the MVC pattern. But *even so*, it seems most "MVC" web frameworks fail to |
| | | the MVC pattern. But *even so*, it seems most MVC web frameworks fail to |
| | | meet even this falsely generic definition. |
| | | |
| | | For example, do your templates (views) always query models directly as is |
| | |
| | | ZCA, the original developer does not need to think terribly hard |
| | | about the mechanics of introducing such a plugpoint. |
| | | |
| | | Many developers seem to believe that creating extensible applications is "not |
| | | worth it". They instead suggest that modifying the source of a given |
| | | Many developers seem to believe that creating extensible applications is not |
| | | worth it. They instead suggest that modifying the source of a given |
| | | application for each deployment to override behavior is more reasonable. |
| | | Much discussion about version control branching and merging typically ensues. |
| | | |
| | |
| | | majority of web applications only have a single deployment, and thus needn't |
| | | be extensible at all. However, some web applications have multiple |
| | | deployments, and some have *many* deployments. For example, a generic |
| | | "content management" system (CMS) may have basic functionality that needs to |
| | | be extended for a particular deployment. That CMS system may be deployed for |
| | | content management system (CMS) may have basic functionality that needs to be |
| | | extended for a particular deployment. That CMS system may be deployed for |
| | | many organizations at many places. Some number of deployments of this CMS |
| | | may be deployed centrally by a third party and managed as a group. It's |
| | | useful to be able to extend such a system for each deployment via preordained |
| | |
| | | system. |
| | | |
| | | Ultimately, any argument about whether the extensibility features lent to |
| | | applications by :app:`Pyramid` are "good" or "bad" is mostly pointless. You |
| | | applications by :app:`Pyramid` are good or bad is mostly pointless. You |
| | | needn't take advantage of the extensibility features provided by a particular |
| | | :app:`Pyramid` application in order to affect a modification for a particular |
| | | set of its deployments. You can ignore the application's extensibility |
| | |
| | | +++++++ |
| | | |
| | | :app:`Pyramid` was developed by folks familiar with Zope 2, which has a |
| | | "through the web" security model. This "TTW" security model was the |
| | | precursor to Zope 3's security proxies. Over time, as the :app:`Pyramid` |
| | | developers (working in Zope 2) created such sites, we found authorization |
| | | checks during code interpretation extremely useful in a minority of projects. |
| | | But much of the time, TTW authorization checks usually slowed down the |
| | | development velocity of projects that had no delegation requirements. In |
| | | particular, if we weren't allowing "untrusted" users to write arbitrary |
| | | Python code to be executed by our application, the burden of "through the |
| | | web" security checks proved too costly to justify. We (collectively) haven't |
| | | written an application on top of which untrusted developers are allowed to |
| | | write code in many years, so it seemed to make sense to drop this model by |
| | | default in a new web framework. |
| | | "through the web" security model. This TTW security model was the precursor |
| | | to Zope 3's security proxies. Over time, as the :app:`Pyramid` developers |
| | | (working in Zope 2) created such sites, we found authorization checks during |
| | | code interpretation extremely useful in a minority of projects. But much of |
| | | the time, TTW authorization checks usually slowed down the development |
| | | velocity of projects that had no delegation requirements. In particular, if |
| | | we weren't allowing untrusted users to write arbitrary Python code to be |
| | | executed by our application, the burden of through the web security checks |
| | | proved too costly to justify. We (collectively) haven't written an |
| | | application on top of which untrusted developers are allowed to write code in |
| | | many years, so it seemed to make sense to drop this model by default in a new |
| | | web framework. |
| | | |
| | | And since we tend to use the same toolkit for all web applications, it's just |
| | | never been a concern to be able to use the same set of restricted-execution |
| | | code under two web different frameworks. |
| | | |
| | | Justifications for disabling security proxies by default notwithstanding, |
| | | given that Zope 3 security proxies are "viral" by nature, the only |
| | | requirement to use one is to make sure you wrap a single object in a security |
| | | proxy and make sure to access that object normally when you want proxy |
| | | security checks to happen. It is possible to override the :app:`Pyramid` |
| | | "traverser" for a given application (see :ref:`changing_the_traverser`). To |
| | | get Zope3-like behavior, it is possible to plug in a different traverser |
| | | which returns Zope3-security-proxy-wrapped objects for each traversed object |
| | | (including the :term:`context` and the :term:`root`). This would have the |
| | | effect of creating a more Zope3-like environment without much effort. |
| | | given that Zope 3 security proxies are viral by nature, the only requirement |
| | | to use one is to make sure you wrap a single object in a security proxy and |
| | | make sure to access that object normally when you want proxy security checks |
| | | to happen. It is possible to override the :app:`Pyramid` traverser for a |
| | | given application (see :ref:`changing_the_traverser`). To get Zope3-like |
| | | behavior, it is possible to plug in a different traverser which returns |
| | | Zope3-security-proxy-wrapped objects for each traversed object (including the |
| | | :term:`context` and the :term:`root`). This would have the effect of |
| | | creating a more Zope3-like environment without much effort. |
| | | |
| | | .. _http_exception_hierarchy: |
| | | |
| | |
| | | - Allows developers to mutate the traversal name stack while traversing (they |
| | | can add and remove path elements). |
| | | |
| | | - Attempts to use an adaptation to obtain the "next" element in the path from |
| | | - Attempts to use an adaptation to obtain the next element in the path from |
| | | the currently traversed object, falling back to ``__bobo_traverse__``, |
| | | ``__getitem__`` and eventually ``__getattr__``. |
| | | |
| | |
| | | when one traversal method failed. It is also slow. |
| | | |
| | | - The *entire traverser* should be replaceable, not just elements of the |
| | | traversal machinery. Pyramid has a few "big" components rather than a |
| | | traversal machinery. Pyramid has a few big components rather than a |
| | | plethora of small ones. If the entire traverser is replaceable, it's an |
| | | antipattern to make portions of the default traverser replaceable. Doing |
| | | so is a "knobs on knobs" pattern, which is unfortunately somewhat endemic |
| | |
| | | to either replace the larger component entirely or turn knobs on the |
| | | default implementation of the larger component, no one understands when (or |
| | | whether) they should ever override the larger component entrirely. This |
| | | results, over time, in a "rusting together" of the larger "replaceable" |
| | | results, over time, in a rusting together of the larger "replaceable" |
| | | component and the framework itself, because people come to depend on the |
| | | availability of the default component in order just to turn its knobs. The |
| | | default component effectively becomes part of the framework, which entirely |
| | | subverts the goal of making it replaceable. In Pyramid, typically if a |
| | | component is replaceable, it will itself have no knobs (it will be "solid |
| | | state"). If you want to influence behavior controlled by that component, |
| | | component is replaceable, it will itself have no knobs (it will be solid |
| | | state). If you want to influence behavior controlled by that component, |
| | | you will replace the component instead of turning knobs attached to the |
| | | component. |
| | | |
| | |
| | | time, and application complexity. |
| | | |
| | | If microframework authors do admit that the circumstance isn't contrived, |
| | | they might then argue that "real" damage will never happen as the result of |
| | | the double-execution (or triple-execution, etc) of module scope code. You |
| | | would be wise to disbelieve this assertion. The potential outcomes of |
| | | multiple execution are too numerous to predict because they involve delicate |
| | | they might then argue that real damage will never happen as the result of the |
| | | double-execution (or triple-execution, etc) of module scope code. You would |
| | | be wise to disbelieve this assertion. The potential outcomes of multiple |
| | | execution are too numerous to predict because they involve delicate |
| | | relationships between application and framework code as well as chronology of |
| | | code execution. It's literally impossible for a framework author to know |
| | | what will happen in all circumstances. But even if given the gift of |
| | |
| | | Aha! Don't these decorators have the same problems? No. These decorators |
| | | do not populate an external Python module when they are executed. They |
| | | only mutate the functions (and classes and methods) they're attached to. |
| | | These mutations must later be found during a "scan" process that has a |
| | | These mutations must later be found during a scan process that has a |
| | | predictable and structured import phase. Module-localized mutation is |
| | | actually the best-case circumstance for double-imports; if a module only |
| | | mutates itself and its contents at import time, if it is imported twice, |
| | |
| | | if __name__ == '__main__': |
| | | app.run() |
| | | |
| | | If you run this application and visit the URL ``/admin``, you will see |
| | | If you run this application and visit the URL ``/admin``, you will see the |
| | | "admin" page. This is the intended result. However, what if you rearrange |
| | | the order of the function definitions in the file? |
| | | |
| | |
| | | matches first. A 404 error is raised. This is not what we wanted; it just |
| | | happened due to the order in which we defined our view functions. |
| | | |
| | | This is because "Groundhog" routes are added to the routing map in import |
| | | This is because Groundhog routes are added to the routing map in import |
| | | order, and matched in the same order when a request comes in. Bottle, like |
| | | Groundhog, as of this writing, matches routes in the order in which they're |
| | | defined at Python execution time. Flask, on the other hand, does not order |
| | |
| | | controlling route ordering, especially in applications that require |
| | | extensibility. |
| | | |
| | | If your microframework orders route matching based on "complexity", you'll |
| | | need to understand what that "complexity" ordering is and attempt to inject a |
| | | "less complex" route to have it get matched before any "more complex" one to |
| | | ensure that it's tried first. |
| | | If your microframework orders route matching based on complexity, you'll need |
| | | to understand what is meant by "complexity", and you'll need to attempt to |
| | | inject a "less complex" route to have it get matched before any "more |
| | | complex" one to ensure that it's tried first. |
| | | |
| | | If your microframework orders its route matching based on relative |
| | | import/execution of function decorator definitions, you will need to ensure |
| | |
| | | :ref:`you_dont_own_modulescope`. |
| | | |
| | | Pyramid uses neither decorator import time ordering nor does it attempt to |
| | | divine the relative "complexity" of one route to another in order to define a |
| | | divine the relative complexity of one route to another in order to define a |
| | | route match ordering. In Pyramid, you have to maintain relative route |
| | | ordering imperatively via the chronology of multiple executions of the |
| | | :meth:`pyramid.config.Configurator.add_route` method. The order in which you |
| | |
| | | "Stacked Object Proxies" Are Too Clever / Thread Locals Are A Nuisance |
| | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| | | |
| | | In another manifestation of "import fascination", some microframeworks use |
| | | the ``import`` statement to get a handle to an object which *is not logically |
| | | global*: |
| | | Some microframeworks use the ``import`` statement to get a handle to an |
| | | object which *is not logically global*: |
| | | |
| | | .. code-block:: python |
| | | :linenos: |
| | |
| | | |
| | | Import statements in Python (``import foo``, ``from bar import baz``) are |
| | | most frequently performed to obtain a reference to an object defined globally |
| | | within an external Python module. However, in "normal" programs, they are |
| | | within an external Python module. However, in normal programs, they are |
| | | never used to obtain a reference to an object that has a lifetime measured by |
| | | the scope of the body of a function. It would be absurd to try to import, |
| | | for example, a variable named ``i`` representing a loop counter defined in |
| | |
| | | running the framework. A request object created by a web framework actually |
| | | has more similarity to the ``i`` loop counter in our example above than it |
| | | has to any comparable importable object defined in the Python standard |
| | | library or in "normal" library code. |
| | | library or in normal library code. |
| | | |
| | | However, systems which use stacked object proxies promote locally scoped |
| | | objects such as ``request`` out to module scope, for the purpose of being |
| | | able to offer users a "nice" spelling involving ``import``. They, for what I |
| | | able to offer users a nice spelling involving ``import``. They, for what I |
| | | consider dubious reasons, would rather present to their users the canonical |
| | | way of getting at a ``request`` as ``from framework import request`` instead |
| | | of a saner ``from myframework.threadlocals import get_request; request = |
| | |
| | | |
| | | As a result, for single-file apps, we currently don't bother to offer a |
| | | ``run()`` shortcut; we tell folks to import their WSGI server of choice and |
| | | run it "by hand". For the people who want a server abstraction layer, we |
| | | run it by hand. For the people who want a server abstraction layer, we |
| | | suggest that they use PasteDeploy. In PasteDeploy-based systems, the onus |
| | | for making sure that the server can interface with a WSGI application is |
| | | placed on the server developer, not the web framework developer, making it |
| | |
| | | include that can be used outside of one (such as |
| | | :meth:`~pyramid.config.Configurator.add_view`, etc). |
| | | |
| | | Pyramid has a conflict detection system that will throw an error if two |
| | | included externals try to add "the same" configuration in a conflicting |
| | | way (such as both externals trying to add a route using the same name, |
| | | or both externals trying to add a view with the same set of predicates). |
| | | It's awful tempting to call this set of features something that can be |
| | | used to compose a system out of "pluggable applications". But in |
| | | reality, there are a number of problems with claiming this: |
| | | Pyramid has a conflict detection system that will throw an error if two |
| | | included externals try to add the same configuration in a conflicting way |
| | | (such as both externals trying to add a route using the same name, or both |
| | | externals trying to add a view with the same set of predicates). It's awful |
| | | tempting to call this set of features something that can be used to compose a |
| | | system out of "pluggable applications". But in reality, there are a number |
| | | of problems with claiming this: |
| | | |
| | | - The terminology is strained. Pyramid really has no notion of a |
| | | plurality of "applications", just a way to compose configuration |
| | |
| | | the boundaries of one "application" (in the sense of configuration |
| | | from an external that adds routes, views, etc) from another. |
| | | |
| | | - Pyramid doesn't provide enough "rails" to make it possible to |
| | | integrate truly honest-to-god, download-an-app-from-a-random-place |
| | | and-plug-it-in-to-create-a-system "pluggable" applications. |
| | | Because Pyramid itself isn't opinionated (it doesn't mandate a |
| | | particular kind of database, it offers multiple ways to map URLs |
| | | to code, etc), it's unlikely that someone who creates something |
| | | "application-like" will be able to casually redistribute it |
| | | to J. Random Pyramid User and have it "just work" by asking him |
| | | to config.include a function from the package. |
| | | This is particularly true of very high level components such |
| | | as blogs, wikis, twitter clones, commenting systems, etc. |
| | | The "integrator" (the Pyramid developer who has downloaded a |
| | | package advertised as a "pluggable app") will almost certainly |
| | | have made different choices about e.g. what type of persistence |
| | | system he's using, and for the integrator to appease the |
| | | requirements of the "pluggable application", he may be required |
| | | to set up a different database, make changes to his own code |
| | | to prevent his application from "shadowing" the pluggable |
| | | app (or vice versa), and any other number of arbitrary |
| | | changes. |
| | | - Pyramid doesn't provide enough "rails" to make it possible to integrate |
| | | truly honest-to-god, download-an-app-from-a-random-place |
| | | and-plug-it-in-to-create-a-system "pluggable" applications. Because |
| | | Pyramid itself isn't opinionated (it doesn't mandate a particular kind of |
| | | database, it offers multiple ways to map URLs to code, etc), it's unlikely |
| | | that someone who creates something application-like will be able to |
| | | casually redistribute it to J. Random Pyramid User and have it just work by |
| | | asking him to config.include a function from the package. This is |
| | | particularly true of very high level components such as blogs, wikis, |
| | | twitter clones, commenting systems, etc. The integrator (the Pyramid |
| | | developer who has downloaded a package advertised as a "pluggable app") |
| | | will almost certainly have made different choices about e.g. what type of |
| | | persistence system he's using, and for the integrator to appease the |
| | | requirements of the "pluggable application", he may be required to set up a |
| | | different database, make changes to his own code to prevent his application |
| | | from shadowing the pluggable app (or vice versa), and any other number of |
| | | arbitrary changes. |
| | | |
| | | For this reason, we claim that Pyramid has "extensible" applications, |
| | | not pluggable applications. Any Pyramid application can be extended |
| | | without forking it as long as its configuration statements have been |
| | | composed into things that can be pulled in via "config.include". |
| | | composed into things that can be pulled in via ``config.include``. |
| | | |
| | | It's also perfectly reasonable for a single developer or team to create a set |
| | | of interoperating components which can be enabled or disabled by using |
| | |
| | | |
| | | Truly pluggable applications need to be created at a much higher level than a |
| | | web framework, as no web framework can offer enough constraints to really |
| | | make them "work out of the box". They really need to plug into an |
| | | application, instead. It would be a noble goal to build an application with |
| | | Pyramid that provides these constraints and which truly does offer a way to |
| | | plug in applications (Joomla, Plone, Drupal come to mind). |
| | | make them work out of the box. They really need to plug into an application, |
| | | instead. It would be a noble goal to build an application with Pyramid that |
| | | provides these constraints and which truly does offer a way to plug in |
| | | applications (Joomla, Plone, Drupal come to mind). |
| | | |
| | | Pyramid Has Zope Things In It, So It's Too Complex |
| | | -------------------------------------------------- |