summaryrefslogtreecommitdiff
path: root/docs/designdefense.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/designdefense.rst')
-rw-r--r--docs/designdefense.rst191
1 files changed, 93 insertions, 98 deletions
diff --git a/docs/designdefense.rst b/docs/designdefense.rst
index 1ed4f65a4..bfde25246 100644
--- a/docs/designdefense.rst
+++ b/docs/designdefense.rst
@@ -7,98 +7,94 @@ From time to time, challenges to various aspects of :app:`Pyramid` design are
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
-obviously you can't please everyone all of the time.
+to improve it. In others 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
-------------------------------------------
A canon of Python popular culture is "TIOOWTDI" ("there is only one way to do
-it", a slighting, tongue-in-cheek reference to Perl's "TIMTOWTDI", which is
-an acronym for "there is more than one way to do it").
-
-:app:`Pyramid` is, for better or worse, a "TIMTOWTDI" system. For example,
-it includes more than one way to resolve a URL to a :term:`view callable`:
-via :term:`url dispatch` or :term:`traversal`. Multiple methods of
-configuration exist: :term:`imperative configuration`, :term:`configuration
-decoration`, and :term:`ZCML` (optionally via :term:`pyramid_zcml`). It works
-with multiple different kinds of persistence and templating systems. And so
-on. However, the existence of most of these overlapping ways to do things
-are not without reason and purpose: we have a number of audiences to serve,
-and we believe that TIMTOWTI at the web framework level actually *prevents* a
-much more insidious and harmful set of duplication at higher levels in the
-Python web community.
-
-:app:`Pyramid` began its life as :mod:`repoze.bfg`, written by a team of
-people with many years of prior :term:`Zope` experience. The idea of
+it", a slighting, tongue-in-cheek reference to Perl's "TIMTOWTDI", which is an
+acronym for "there is more than one way to do it").
+
+:app:`Pyramid` is, for better or worse, a "TIMTOWTDI" system. For example, it
+includes more than one way to resolve a URL to a :term:`view callable`: via
+:term:`url dispatch` or :term:`traversal`. Multiple methods of configuration
+exist: :term:`imperative configuration`, :term:`configuration decoration`, and
+:term:`ZCML` (optionally via :term:`pyramid_zcml`). It works with multiple
+different kinds of persistence and templating systems. And so on. However, the
+existence of most of these overlapping ways to do things are not without reason
+and purpose: we have a number of audiences to serve, and we believe that
+TIMTOWTDI at the web framework level actually *prevents* a much more insidious
+and harmful set of duplication at higher levels in the Python web community.
+
+:app:`Pyramid` began its life as :mod:`repoze.bfg`, written by a team of people
+with many years of prior :term:`Zope` experience. The idea of
:term:`traversal` and the way :term:`view lookup` works was stolen entirely
from Zope. The authorization subsystem provided by :app:`Pyramid` is a
derivative of Zope's. The idea that an application can be *extended* without
forking is also a Zope derivative.
Implementations of these features were *required* to allow the :app:`Pyramid`
-authors to build the bread-and-butter CMS-type systems for customers in the
-way in which they were accustomed. No other system, save for Zope itself,
-had such features, and Zope itself was beginning to show signs of its age.
-We were becoming hampered by consequences of its early design mistakes.
-Zope's lack of documentation was also difficult to work around: it was hard
-to hire smart people to work on Zope applications, because there was no
-comprehensive documentation set to point them at which explained "it all" in
-one consumable place, and it was too large and self-inconsistent to document
-properly. Before :mod:`repoze.bfg` went under development, its authors
-obviously looked around for other frameworks that fit the bill. But no
-non-Zope framework did. So we embarked on building :mod:`repoze.bfg`.
+authors to build the bread-and-butter CMS-type systems for customers in the way
+in which they were accustomed. No other system, save for Zope itself, had such
+features, and Zope itself was beginning to show signs of its age. We were
+becoming hampered by consequences of its early design mistakes. Zope's lack of
+documentation was also difficult to work around. It was hard to hire smart
+people to work on Zope applications because there was no comprehensive
+documentation set which explained "it all" in one consumable place, and it was
+too large and self-inconsistent to document properly. Before :mod:`repoze.bfg`
+went under development, its authors obviously looked around for other
+frameworks that fit the bill. But no non-Zope framework did. So we embarked on
+building :mod:`repoze.bfg`.
As the result of our research, however, it became apparent that, despite the
-fact that no *one* framework had all the features we required, lots of
-existing frameworks had good, and sometimes very compelling ideas. In
-particular, :term:`URL dispatch` is a more direct mechanism to map URLs to
-code.
+fact that no *one* framework had all the features we required, lots of existing
+frameworks had good, and sometimes very compelling ideas. In particular,
+:term:`URL dispatch` is a more direct mechanism to map URLs to code.
So, although we couldn't find a framework, save for Zope, that fit our needs,
and while we incorporated a lot of Zope ideas into BFG, we also emulated the
features we found compelling in other frameworks (such as :term:`url
-dispatch`). After the initial public release of BFG, as time went on,
-features were added to support people allergic to various Zope-isms in the
-system, such as the ability to configure the application using
-:term:`imperative configuration` and :term:`configuration decoration` rather
-than solely using :term:`ZCML`, and the elimination of the required use of
-:term:`interface` objects. It soon became clear that we had a system that
-was very generic, and was beginning to appeal to non-Zope users as well as
-ex-Zope users.
+dispatch`). After the initial public release of BFG, as time went on, features
+were added to support people allergic to various Zope-isms in the system, such
+as the ability to configure the application using :term:`imperative
+configuration` and :term:`configuration decoration`, rather than solely using
+:term:`ZCML`, and the elimination of the required use of :term:`interface`
+objects. It soon became clear that we had a system that was very generic, and
+was beginning to appeal to non-Zope users as well as ex-Zope users.
As the result of this generalization, it became obvious BFG shared 90% of its
-featureset with the featureset of Pylons 1, and thus had a very similar
-target market. Because they were so similar, choosing between the two
-systems was an exercise in frustration for an otherwise non-partisan
-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`.
-
-The Python web framework space is currently notoriously balkanized. We're
-truly hoping that the amalgamation of components in :app:`Pyramid` will
-appeal to at least two currently very distinct sets of users: Pylons and BFG
-users. By unifying the best concepts from Pylons and BFG into a single
-codebase and leaving the bad concepts from their ancestors behind, we'll be
-able to consolidate our efforts better, share more code, and promote our
-efforts as a unit rather than competing pointlessly. We hope to be able to
-shortcut the pack mentality which results in a *much larger* duplication of
-effort, represented by competing but incredibly similar applications and
-libraries, each built upon a specific low level stack that is incompatible
-with the other. We'll also shrink the choice of credible Python web
-frameworks down by at least one. We're also hoping to attract users from
-other communities (such as Zope's and TurboGears') by providing the features
-they require, while allowing enough flexibility to do things in a familiar
-fashion. Some overlap of functionality to achieve these goals is expected
-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.
-
-Pyramid Uses A Zope Component Architecture ("ZCA") Registry
+feature set with the feature set of Pylons 1, and thus had a very similar
+target market. Because they were so similar, choosing between the two systems
+was an exercise in frustration for an otherwise non-partisan 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`.
+
+The Python web framework space is currently notoriously balkanized. We're truly
+hoping that the amalgamation of components in :app:`Pyramid` will appeal to at
+least two currently very distinct sets of users: Pylons and BFG users. By
+unifying the best concepts from Pylons and BFG into a single codebase, and
+leaving the bad concepts from their ancestors behind, we'll be able to
+consolidate our efforts better, share more code, and promote our efforts as a
+unit rather than competing pointlessly. We hope to be able to shortcut the pack
+mentality which results in a *much larger* duplication of effort, represented
+by competing but incredibly similar applications and libraries, each built upon
+a specific low level stack that is incompatible with the other. We'll also
+shrink the choice of credible Python web frameworks down by at least one. We're
+also hoping to attract users from other communities (such as Zope's and
+TurboGears') by providing the features they require, while allowing enough
+flexibility to do things in a familiar fashion. Some overlap of functionality
+to achieve these goals is expected 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.
+
+Pyramid Uses a Zope Component Architecture ("ZCA") Registry
-----------------------------------------------------------
:app:`Pyramid` uses a :term:`Zope Component Architecture` (ZCA) "component
@@ -146,7 +142,7 @@ dictionary API, but that's not very important in this context. That's
problem number two.
Third of all, what does the ``getUtility`` function do? It's performing a
-lookup for the ``ISettings`` "utility" that should return.. well, a utility.
+lookup for the ``ISettings`` "utility" that should return... well, a utility.
Note how we've already built up a dependency on the understanding of an
:term:`interface` and the concept of "utility" to answer this question: a bad
sign so far. Note also that the answer is circular, a *really* bad sign.
@@ -156,12 +152,12 @@ registry" of course. What's a component registry? Problem number four.
Fifth, assuming you buy that there's some magical registry hanging around,
where *is* this registry? *Homina homina*... "around"? That's sort of the
-best answer in this context (a more specific answer would require knowledge
-of internals). Can there be more than one registry? Yes. So *which*
-registry does it find the registration in? Well, the "current" registry of
-course. In terms of :app:`Pyramid`, the current registry is a thread local
-variable. Using an API that consults a thread local makes understanding how
-it works non-local.
+best answer in this context (a more specific answer would require knowledge of
+internals). Can there be more than one registry? Yes. So in *which* registry
+does it find the registration? Well, the "current" registry of course. In
+terms of :app:`Pyramid`, the current registry is a thread local 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
@@ -170,10 +166,10 @@ 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
-the ZCA, even if he or she is already an expert Python programmer and whom is
-an expert in the domain of web applications. This is suboptimal.
+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 the
+ZCA, even if they are already an expert Python programmer and an expert in the
+domain of web applications. This is suboptimal.
Ameliorations
+++++++++++++
@@ -907,23 +903,22 @@ creating a more Zope3-like environment without much effort.
.. _http_exception_hierarchy:
-Pyramid Uses its Own HTTP Exception Class Hierarchy Rather Than ``webob.exc``
------------------------------------------------------------------------------
+Pyramid uses its own HTTP exception class hierarchy rather than :mod:`webob.exc`
+--------------------------------------------------------------------------------
.. versionadded:: 1.1
The HTTP exception classes defined in :mod:`pyramid.httpexceptions` are very
-much like the ones defined in ``webob.exc``
-(e.g. :class:`~pyramid.httpexceptions.HTTPNotFound`,
-:class:`~pyramid.httpexceptions.HTTPForbidden`, etc). They have the same
-names and largely the same behavior and all have a very similar
-implementation, but not the same identity. Here's why they have a separate
-identity:
+much like the ones defined in :mod:`webob.exc`, (e.g.,
+:class:`~pyramid.httpexceptions.HTTPNotFound` or
+:class:`~pyramid.httpexceptions.HTTPForbidden`). They have the same names and
+largely the same behavior, and all have a very similar implementation, but not
+the same identity. Here's why they have a separate identity:
- Making them separate allows the HTTP exception classes to subclass
:class:`pyramid.response.Response`. This speeds up response generation
- slightly due to the way the Pyramid router works. The same speedup could
- be gained by monkeypatching ``webob.response.Response`` but it's usually
+ slightly due to the way the Pyramid router works. The same speedup could be
+ gained by monkeypatching :class:`webob.response.Response`, but it's usually
the case that monkeypatching turns out to be evil and wrong.
- Making them separate allows them to provide alternate ``__call__`` logic
@@ -933,7 +928,7 @@ identity:
value of ``RequestClass`` (:class:`pyramid.request.Request`).
- Making them separate allows us freedom from having to think about backwards
- compatibility code present in ``webob.exc`` having to do with Python 2.4,
+ compatibility code present in :mod:`webob.exc` having to do with Python 2.4,
which we no longer support in Pyramid 1.1+.
- We change the behavior of two classes
@@ -944,9 +939,9 @@ identity:
- Making them separate allows us to influence the docstrings of the exception
classes to provide Pyramid-specific documentation.
-- Making them separate allows us to silence a stupid deprecation warning
- under Python 2.6 when the response objects are used as exceptions (related
- to ``self.message``).
+- Making them separate allows us to silence a stupid deprecation warning under
+ Python 2.6 when the response objects are used as exceptions (related to
+ ``self.message``).
.. _simpler_traversal_model: