From e9a71dfd96bb42791ff97a5e6da5b0c8aa4a606e Mon Sep 17 00:00:00 2001 From: Klee Dienes Date: Mon, 31 Oct 2011 01:41:11 +0000 Subject: Properly process MIME encoding. --- CONTRIBUTORS.txt | 2 ++ pyramid/static.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index a368fb4d2..648f67c72 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -148,3 +148,5 @@ Contributors - Shane Hathaway, 2011/07/22 - Manuel Hermann, 2011/07/11 + +- Klee Dienes, 2011/10/30 diff --git a/pyramid/static.py b/pyramid/static.py index 50a8b918b..2a9abbd8f 100644 --- a/pyramid/static.py +++ b/pyramid/static.py @@ -43,10 +43,11 @@ class _FileResponse(Response): def __init__(self, path, cache_max_age): super(_FileResponse, self).__init__(conditional_response=True) self.last_modified = getmtime(path) - content_type = mimetypes.guess_type(path, strict=False)[0] + content_type, content_encoding = mimetypes.guess_type(path, strict=False) if content_type is None: content_type = 'application/octet-stream' self.content_type = content_type + self.content_encoding = content_encoding content_length = getsize(path) self.app_iter = _FileIter(open(path, 'rb'), content_length) # assignment of content_length must come after assignment of app_iter -- cgit v1.2.3 From a735d65d1d28af75371307c3d9bb52b18ee44ef0 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Wed, 9 Nov 2011 17:11:03 -0600 Subject: Made pyramid.settings.aslist public. --- docs/api/settings.rst | 2 ++ pyramid/settings.py | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/api/settings.rst b/docs/api/settings.rst index ac1cd3f9c..6b12c038c 100644 --- a/docs/api/settings.rst +++ b/docs/api/settings.rst @@ -9,4 +9,6 @@ .. autofunction:: asbool + .. autofunction:: aslist + diff --git a/pyramid/settings.py b/pyramid/settings.py index de91042eb..11587a8be 100644 --- a/pyramid/settings.py +++ b/pyramid/settings.py @@ -44,8 +44,13 @@ def aslist_cronly(value): value = filter(None, [x.strip() for x in value.splitlines()]) return list(value) -def aslist(value): +def aslist(value, flatten=True): + """ Return a list of strings, separating the input based on newlines + and, if flatten=True (the default), also split on spaces within + each line.""" values = aslist_cronly(value) + if not flatten: + return values result = [] for value in values: subvalues = value.split() -- cgit v1.2.3 From ddc4062fe5415db5be7a01ea65c7311fe20eef15 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 17 Nov 2011 00:34:01 -0600 Subject: added tests so I don't get yelled at --- pyramid/tests/test_settings.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pyramid/tests/test_settings.py b/pyramid/tests/test_settings.py index d02b3cd3e..2ef15f62a 100644 --- a/pyramid/tests/test_settings.py +++ b/pyramid/tests/test_settings.py @@ -81,9 +81,9 @@ class Test_aslist_cronly(unittest.TestCase): self.assertEqual(result, ['abc', 'def']) class Test_aslist(unittest.TestCase): - def _callFUT(self, val): + def _callFUT(self, val, **kw): from pyramid.settings import aslist - return aslist(val) + return aslist(val, **kw) def test_with_list(self): result = self._callFUT(['abc', 'def']) @@ -100,3 +100,7 @@ class Test_aslist(unittest.TestCase): def test_with_string_crsep_spacesep(self): result = self._callFUT(' abc\n def ghi') self.assertEqual(result, ['abc', 'def', 'ghi']) + + def test_with_string_crsep_spacesep_no_flatten(self): + result = self._callFUT(' abc\n def ghi ', flatten=False) + self.assertEqual(result, ['abc', 'def ghi']) -- cgit v1.2.3 From d386e17eab3292a7fd4a69a8f3ff84dcf86cf8f3 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 17 Nov 2011 00:55:24 -0600 Subject: Fixed issue #353. --- pyramid/scripts/proutes.py | 7 +++++-- pyramid/tests/test_scripts/test_proutes.py | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pyramid/scripts/proutes.py b/pyramid/scripts/proutes.py index 9b25ed169..570417e95 100644 --- a/pyramid/scripts/proutes.py +++ b/pyramid/scripts/proutes.py @@ -64,14 +64,17 @@ class PRoutesCommand(object): self.out( fmt % ('-'*len('Name'), '-'*len('Pattern'), '-'*len('View'))) for route in routes: + pattern = route.pattern + if not pattern.startswith('/'): + pattern = '/' + pattern request_iface = registry.queryUtility(IRouteRequest, name=route.name) view_callable = None if (request_iface is None) or (route.factory is not None): - self.out(fmt % (route.name, route.pattern, '')) + self.out(fmt % (route.name, pattern, '')) else: view_callable = registry.adapters.lookup( (IViewClassifier, request_iface, Interface), IView, name='', default=None) - self.out(fmt % (route.name, route.pattern, view_callable)) + self.out(fmt % (route.name, pattern, view_callable)) diff --git a/pyramid/tests/test_scripts/test_proutes.py b/pyramid/tests/test_scripts/test_proutes.py index af6ff19d0..328d1001d 100644 --- a/pyramid/tests/test_scripts/test_proutes.py +++ b/pyramid/tests/test_scripts/test_proutes.py @@ -43,6 +43,18 @@ class TestPRoutesCommand(unittest.TestCase): self.assertEqual(len(L), 3) self.assertEqual(L[-1].split(), ['a', '/a', '']) + def test_route_with_no_slash_prefix(self): + command = self._makeOne() + route = dummy.DummyRoute('a', 'a') + mapper = dummy.DummyMapper(route) + command._get_mapper = lambda *arg: mapper + L = [] + command.out = L.append + result = command.run() + self.assertEqual(result, None) + self.assertEqual(len(L), 3) + self.assertEqual(L[-1].split(), ['a', '/a', '']) + def test_single_route_no_views_registered(self): from zope.interface import Interface from pyramid.registry import Registry -- cgit v1.2.3 From d79d133699846b5bb90c1df7359af6624ca62e82 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 17 Nov 2011 01:46:57 -0600 Subject: Allow creating Configurator in an interpreter. Fixes #328. --- pyramid/path.py | 2 +- pyramid/tests/test_path.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pyramid/path.py b/pyramid/path.py index 9c7be4c57..86c1c5857 100644 --- a/pyramid/path.py +++ b/pyramid/path.py @@ -25,7 +25,7 @@ def package_name(pkg_or_module): package name of the package in which the module lives. If this function is passed a package, return the dotted Python package name of the package itself.""" - if pkg_or_module is None: + if pkg_or_module is None or pkg_or_module.__name__ == '__main__': return '__main__' pkg_filename = pkg_or_module.__file__ pkg_name = pkg_or_module.__name__ diff --git a/pyramid/tests/test_path.py b/pyramid/tests/test_path.py index c2261d223..29b9baf1f 100644 --- a/pyramid/tests/test_path.py +++ b/pyramid/tests/test_path.py @@ -162,6 +162,11 @@ class TestPackageName(unittest.TestCase): def test_it_None(self): result = self._callFUT(None) self.assertEqual(result, '__main__') + + def test_it_main(self): + import __main__ + result = self._callFUT(__main__) + self.assertEqual(result, '__main__') class DummyPackageOrModule: def __init__(self, real_package_or_module, raise_exc=None): -- cgit v1.2.3 From 0e5441806857e3c1ef59d1b2075d6e4238587804 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Fri, 18 Nov 2011 05:48:30 -0500 Subject: add tests for content encoding feature --- pyramid/static.py | 3 +- pyramid/tests/fixtures/static/arcs.svg.tgz | 73 ++++++++++++++++++++++++++++++ pyramid/tests/test_static.py | 18 ++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 pyramid/tests/fixtures/static/arcs.svg.tgz diff --git a/pyramid/static.py b/pyramid/static.py index 2a9abbd8f..ba4ac0fa1 100644 --- a/pyramid/static.py +++ b/pyramid/static.py @@ -43,7 +43,8 @@ class _FileResponse(Response): def __init__(self, path, cache_max_age): super(_FileResponse, self).__init__(conditional_response=True) self.last_modified = getmtime(path) - content_type, content_encoding = mimetypes.guess_type(path, strict=False) + content_type, content_encoding = mimetypes.guess_type(path, + strict=False) if content_type is None: content_type = 'application/octet-stream' self.content_type = content_type diff --git a/pyramid/tests/fixtures/static/arcs.svg.tgz b/pyramid/tests/fixtures/static/arcs.svg.tgz new file mode 100644 index 000000000..376c42ac8 --- /dev/null +++ b/pyramid/tests/fixtures/static/arcs.svg.tgz @@ -0,0 +1,73 @@ + + + + + + + + diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py index a04a47397..bd2c2adef 100644 --- a/pyramid/tests/test_static.py +++ b/pyramid/tests/test_static.py @@ -160,6 +160,24 @@ class Test_static_view_use_subpath_False(unittest.TestCase): response = inst(context, request) self.assertEqual(response.status, '404 Not Found') + def test_resource_with_content_encoding(self): + inst = self._makeOne('pyramid.tests:fixtures/static') + request = self._makeRequest({'PATH_INFO':'/arcs.svg.tgz'}) + context = DummyContext() + response = inst(context, request) + self.assertEqual(response.status, '200 OK') + self.assertEqual(response.content_type, 'application/x-tar') + self.assertEqual(response.content_encoding, 'gzip') + + def test_resource_no_content_encoding(self): + inst = self._makeOne('pyramid.tests:fixtures/static') + request = self._makeRequest({'PATH_INFO':'/index.html'}) + context = DummyContext() + response = inst(context, request) + self.assertEqual(response.status, '200 OK') + self.assertEqual(response.content_type, 'text/html') + self.assertEqual(response.content_encoding, None) + class Test_static_view_use_subpath_True(unittest.TestCase): def _getTargetClass(self): from pyramid.static import static_view -- cgit v1.2.3 From 1c39aeac52ab43afc57b11cfdb50513bdf87b762 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Fri, 18 Nov 2011 07:38:06 -0500 Subject: - ``request.static_url`` now generates URL-quoted URLs when fed a ``path`` argument which contains characters that are unsuitable for URLs. --- CHANGES.txt | 3 +++ pyramid/config/views.py | 3 ++- pyramid/tests/test_config/test_views.py | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 42f07a275..6e664f077 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -33,6 +33,9 @@ Bug Fixes - The DummySession in ``pyramid.testing`` now generates a new CSRF token if one doesn't yet exist. +- ``request.static_url`` now generates URL-quoted URLs when fed a ``path`` + argument which contains characters that are unsuitable for URLs. + Backwards Incompatibilities --------------------------- diff --git a/pyramid/config/views.py b/pyramid/config/views.py index a88c22b12..cf27c3514 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -30,6 +30,7 @@ from pyramid import renderers from pyramid.compat import string_types from pyramid.compat import urlparse from pyramid.compat import im_func +from pyramid.compat import url_quote from pyramid.exceptions import ConfigurationError from pyramid.exceptions import PredicateMismatch from pyramid.httpexceptions import HTTPForbidden @@ -1429,7 +1430,7 @@ class StaticURLInfo(object): registry = get_current_registry() for (url, spec, route_name) in self._get_registrations(registry): if path.startswith(spec): - subpath = path[len(spec):] + subpath = url_quote(path[len(spec):]) if url is None: kw['subpath'] = subpath return request.route_url(route_name, **kw) diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py index fa263a311..a9a4d5836 100644 --- a/pyramid/tests/test_config/test_views.py +++ b/pyramid/tests/test_config/test_views.py @@ -3362,6 +3362,27 @@ class TestStaticURLInfo(unittest.TestCase): result = inst.generate('package:path/abc', request, a=1) self.assertEqual(result, 'url') + def test_generate_url_quoted_local(self): + inst = self._makeOne() + registrations = [(None, 'package:path/', '__viewname/')] + inst._get_registrations = lambda *x: registrations + def route_url(n, **kw): + self.assertEqual(n, '__viewname/') + self.assertEqual(kw, {'subpath':'abc%20def', 'a':1}) + return 'url' + request = self._makeRequest() + request.route_url = route_url + result = inst.generate('package:path/abc def', request, a=1) + self.assertEqual(result, 'url') + + def test_generate_url_quoted_remote(self): + inst = self._makeOne() + registrations = [('http://example.com/', 'package:path/', None)] + inst._get_registrations = lambda *x: registrations + request = self._makeRequest() + result = inst.generate('package:path/abc def', request, a=1) + self.assertEqual(result, 'http://example.com/abc%20def') + def test_add_already_exists(self): inst = self._makeOne() config = self._makeConfig( -- cgit v1.2.3 From a5512e603365ce9e4a698095d6114e107c25d8d9 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Fri, 18 Nov 2011 07:38:58 -0500 Subject: reference github issue --- CHANGES.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 6e664f077..f40c32eec 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -34,7 +34,8 @@ Bug Fixes one doesn't yet exist. - ``request.static_url`` now generates URL-quoted URLs when fed a ``path`` - argument which contains characters that are unsuitable for URLs. + argument which contains characters that are unsuitable for URLs. See + https://github.com/Pylons/pyramid/issues/349 for more info. Backwards Incompatibilities --------------------------- -- cgit v1.2.3 From 9ffa36dde53afda0a5a15b096e2fcd9c54a88ea2 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Fri, 18 Nov 2011 07:43:44 -0500 Subject: garden --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index f40c32eec..33008cbea 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -41,7 +41,7 @@ Backwards Incompatibilities --------------------------- - Pyramid no longer runs on Python 2.5 (which includes the most recent - release of Jython, and the current version of GAE as of this writing). + release of Jython and the Python 2.5 version of GAE as of this writing). - The ``paster`` command is no longer the documented way to create projects, start the server, or run debugging commands. To create projects from -- cgit v1.2.3 From 319806b49a8d2e8c8bbf61a1886eb206790d18e4 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Fri, 18 Nov 2011 07:51:48 -0500 Subject: add instructions about how to build docs (closes #348) --- HACKING.txt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/HACKING.txt b/HACKING.txt index d122a7a25..daa4f614b 100644 --- a/HACKING.txt +++ b/HACKING.txt @@ -121,6 +121,16 @@ Documentation Coverage that API or behavior must change to reflect the bug fix, ideally in the same commit that fixes the bug or adds the feature. +- To build and review docs: + + 1. Install ``tests_require`` dependencies from Pyramid's setup.py into your + virtualenv. + + 2. From the ``docs`` directory of the Pyramid checkout run ``make html + SPHINXBUILD=/path/to/your/virtualenv/bin/sphinx-build``. + + 3. Open the _build/html/index.html file to see the resulting rendering. + Change Log ---------- -- cgit v1.2.3 From 17863d878b4cc47342eb1e7606df99178700beb3 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Fri, 18 Nov 2011 09:47:06 -0500 Subject: paster template -> scaffold --- HACKING.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/HACKING.txt b/HACKING.txt index daa4f614b..24d1b48a1 100644 --- a/HACKING.txt +++ b/HACKING.txt @@ -73,10 +73,10 @@ In order to add a feature to Pyramid: "unnecessary" is of course subjective, but new dependencies should be discussed). -The above requirements are relaxed for paster template dependencies. -If a paster template has an install-time dependency on something that -doesn't work on a particular platform, that caveat should be spelled -out clearly in *its* documentation (within its ``docs/`` directory). +The above requirements are relaxed for scaffolding dependencies. If a +scaffold has an install-time dependency on something that doesn't work on a +particular platform, that caveat should be spelled out clearly in *its* +documentation (within its ``docs/`` directory). Coding Style ------------ -- cgit v1.2.3 From 40b3a8a15e972a7d1dc0b0c7909d3e2a24f91afe Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Fri, 18 Nov 2011 09:47:40 -0500 Subject: remove jython --- HACKING.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/HACKING.txt b/HACKING.txt index 24d1b48a1..593e89ac1 100644 --- a/HACKING.txt +++ b/HACKING.txt @@ -61,10 +61,10 @@ In order to add a feature to Pyramid: - The feature must work on the latest version of PyPy. -- The feature must not cause installation or runtime failure on Jython - or App Engine. If it doesn't cause installation or runtime failure, - but doesn't actually *work* on these platforms, that caveat should be - spelled out in the documentation. +- The feature must not cause installation or runtime failure on App Engine. + If it doesn't cause installation or runtime failure, but doesn't actually + *work* on these platforms, that caveat should be spelled out in the + documentation. - The feature must not depend on any particular persistence layer (filesystem, SQL, etc). -- cgit v1.2.3 From 0694f092797ddc4a1821e89e776adb17bc89f52d Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Fri, 18 Nov 2011 10:03:16 -0500 Subject: - Fix ZODB tutorial docs to match ZODB tutorial code (I removed program name from ``scan``). --- TODO.txt | 3 --- docs/tutorials/wiki/authorization.rst | 21 ++++++--------- docs/tutorials/wiki/basiclayout.rst | 30 +++++++++++++--------- docs/tutorials/wiki/definingviews.rst | 2 +- .../wiki/src/basiclayout/tutorial/views.py | 3 +-- docs/tutorials/wiki/src/views/tutorial/views.py | 3 +-- 6 files changed, 29 insertions(+), 33 deletions(-) diff --git a/TODO.txt b/TODO.txt index 5a030a84c..2a5239629 100644 --- a/TODO.txt +++ b/TODO.txt @@ -4,9 +4,6 @@ Pyramid TODOs Must-Have --------- -- Fix ZODB tutorial docs to match ZODB tutorial code (I removed program name - from ``scan``). - - Fix SQLA tutorial to match ZODB tutorial. - Fix routesalchemy scaffold to match ZODB scaffold. diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index bf88c3bd8..d900f17a3 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -42,7 +42,7 @@ declarative security checking. We need to import the new policies: Then, we'll add those policies to the configuration: .. literalinclude:: src/authorization/tutorial/__init__.py - :lines: 20-25 + :lines: 17-22 :linenos: :language: python @@ -60,10 +60,6 @@ look like so: :linenos: :language: python -.. note:: - (Your ``config.scan('tutorial')`` needs the package name you used - instead of "tutorial", if you used a different name.) - Add ``security.py`` ~~~~~~~~~~~~~~~~~~~ @@ -77,14 +73,13 @@ content: The ``groupfinder`` function defined here is an :term:`authentication policy` "callback"; it is a callable that accepts a userid and a request. If the -userid exists in the system, the callback will -return a sequence of group identifiers (or an empty sequence if the user -isn't a member of any groups). If the userid *does not* exist in the system, -the callback will return ``None``. In a production system, user and group data will -most often come from a database, but here we use "dummy" data to represent -user and groups sources. Note that the ``editor`` user is a member of the -``group:editors`` group in our dummy group data (the ``GROUPS`` data -structure). +userid exists in the system, the callback will return a sequence of group +identifiers (or an empty sequence if the user isn't a member of any groups). +If the userid *does not* exist in the system, the callback will return +``None``. In a production system, user and group data will most often come +from a database, but here we use "dummy" data to represent user and groups +sources. Note that the ``editor`` user is a member of the ``group:editors`` +group in our dummy group data (the ``GROUPS`` data structure). Give Our Root Resource an ACL ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/tutorials/wiki/basiclayout.rst b/docs/tutorials/wiki/basiclayout.rst index 897775428..439da24d9 100644 --- a/docs/tutorials/wiki/basiclayout.rst +++ b/docs/tutorials/wiki/basiclayout.rst @@ -45,16 +45,19 @@ point happens to be the ``main`` function within the file named ``http://localhost:6543/static/`` and below. The first argument is the "name" ``static``, which indicates that the URL path prefix of the view will be ``/static``. the The second argument of this tag is the "path", - which is an :term:`asset specification`, so it finds the resources it - should serve within the ``static`` directory inside the ``tutorial`` - package. + which is a relative :term:`asset specification`, so it finds the resources + it should serve within the ``static`` directory inside the ``tutorial`` + package. The scaffold could have alternately used an *absolute* asset + specification as the path (``tutorial:static``) but it does not. #. *Line 14*. Perform a :term:`scan`. A scan will find :term:`configuration - decoration`, such as view configuration decorators - (e.g. ``@view_config``) in the source code of the ``tutorial`` package and - will take actions based on these decorators. The argument to - :meth:`~pyramid.config.Configurator.scan` is the package name to scan, - which is ``tutorial``. + decoration`, such as view configuration decorators (e.g. ``@view_config``) + in the source code of the ``tutorial`` package and will take actions based + on these decorators. We don't pass any arguments to + :meth:`~pyramid.config.Configurator.scan`, which implies that the scan + should take place in the current package (in this case, ``tutorial``). + The scaffold could have equivalently said ``config.scan('tutorial')`` but + it chose to omit the package name argument. #. *Line 15*. Use the :meth:`pyramid.config.Configurator.make_wsgi_app` method @@ -119,7 +122,7 @@ Let's try to understand the components in this module: decoration` to perform a :term:`view configuration` registration. This view configuration registration will be activated when the application is started. It will be activated by virtue of it being found as the result - of a :term:`scan` (when Line 17 of ``__init__.py`` is run). + of a :term:`scan` (when Line 14 of ``__init__.py`` is run). The ``@view_config`` decorator accepts a number of keyword arguments. We use two keyword arguments here: ``context`` and ``renderer``. @@ -131,12 +134,15 @@ Let's try to understand the components in this module: model, this view callable will be invoked. The ``renderer`` argument names an :term:`asset specification` of - ``tutorial:templates/mytemplate.pt``. This asset specification points at - a :term:`Chameleon` template which lives in the ``mytemplate.pt`` file + ``templates/mytemplate.pt``. This asset specification points at a + :term:`Chameleon` template which lives in the ``mytemplate.pt`` file within the ``templates`` directory of the ``tutorial`` package. And indeed if you look in the ``templates`` directory of this package, you'll see a ``mytemplate.pt`` template file, which renders the default home page - of the generated project. + of the generated project. This asset specification is *relative* (to the + view.py's current package). We could have alternately an used the + absolute asset specification ``tutorial:templates/mytemplate.pt``, but + chose to use the relative version. Since this call to ``@view_config`` doesn't pass a ``name`` argument, the ``my_view`` function which it decorates represents the "default" view diff --git a/docs/tutorials/wiki/definingviews.rst b/docs/tutorials/wiki/definingviews.rst index 15b9f13ab..371cae8eb 100644 --- a/docs/tutorials/wiki/definingviews.rst +++ b/docs/tutorials/wiki/definingviews.rst @@ -293,7 +293,7 @@ replicate within the body of this guide, however it is available `online `_. This CSS file will be accessed via -e.g. ``http://localhost:6543/static/pylons.css`` by virtue of the call to +e.g. ``/static/pylons.css`` by virtue of the call to ``add_static_view`` directive we've made in the ``__init__.py`` file. Any number and type of static assets can be placed in this directory (or subdirectories) and are just referred to by URL. diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/views.py b/docs/tutorials/wiki/src/basiclayout/tutorial/views.py index 9dfdee3f1..4265b6bf7 100644 --- a/docs/tutorials/wiki/src/basiclayout/tutorial/views.py +++ b/docs/tutorials/wiki/src/basiclayout/tutorial/views.py @@ -1,7 +1,6 @@ from pyramid.view import view_config from .models import MyModel -@view_config(context=MyModel, - renderer='templates/mytemplate.pt') +@view_config(context=MyModel, renderer='templates/mytemplate.pt') def my_view(request): return {'project':'tutorial'} diff --git a/docs/tutorials/wiki/src/views/tutorial/views.py b/docs/tutorials/wiki/src/views/tutorial/views.py index 862bd0da9..016f5b6bb 100644 --- a/docs/tutorials/wiki/src/views/tutorial/views.py +++ b/docs/tutorials/wiki/src/views/tutorial/views.py @@ -13,8 +13,7 @@ wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") def view_wiki(context, request): return HTTPFound(location=request.resource_url(context, 'FrontPage')) -@view_config(context='.models.Page', - renderer='templates/view.pt') +@view_config(context='.models.Page', renderer='templates/view.pt') def view_page(context, request): wiki = context.__parent__ -- cgit v1.2.3 From bb18ff68d0607b1b4a088d56b3df5696eb37f146 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 21 Nov 2011 09:27:53 -0500 Subject: vb --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index c78285840..20236842b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -94,7 +94,7 @@ copyright = '%s, Agendaless Consulting' % datetime.datetime.now().year # other places throughout the built documents. # # The short X.Y version. -version = '1.2' +version = '1.3dev' # The full version, including alpha/beta/rc tags. release = version -- cgit v1.2.3 From 38369531194f8df0273378b73cb58010d89f3524 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 21 Nov 2011 09:42:35 -0500 Subject: degenshi --- docs/glossary.rst | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/glossary.rst b/docs/glossary.rst index fc282b2da..c98637698 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -342,14 +342,13 @@ Glossary file. It was developed by Ian Bicking. Chameleon - `chameleon `_ is an attribute - language template compiler which supports both the :term:`ZPT` and - :term:`Genshi` templating specifications. It is written and - maintained by Malthe Borch. It has several extensions, such as - the ability to use bracketed (Genshi-style) ``${name}`` syntax, - even within ZPT. It is also much faster than the reference - implementations of both ZPT and Genshi. :app:`Pyramid` offers - Chameleon templating out of the box in ZPT and text flavors. + `chameleon `_ is an attribute language + template compiler which supports the :term:`ZPT` templating + specification. It is written and maintained by Malthe Borch. It has + several extensions, such as the ability to use bracketed (Mako-style) + ``${name}`` syntax. It is also much faster than the reference + implementation of ZPT. :app:`Pyramid` offers Chameleon templating out + of the box in ZPT and text flavors. ZPT The `Zope Page Template `_ -- cgit v1.2.3 From df913d0a5cb3774daf7a71210cdbb2a0c49d2b60 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 21 Nov 2011 12:01:27 -0500 Subject: dont skip this test on pypy (chameleon 2 works on pypy) --- pyramid/tests/test_chameleon_zpt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyramid/tests/test_chameleon_zpt.py b/pyramid/tests/test_chameleon_zpt.py index 1a8e6767e..e7a1499e6 100644 --- a/pyramid/tests/test_chameleon_zpt.py +++ b/pyramid/tests/test_chameleon_zpt.py @@ -121,7 +121,7 @@ class ZPTTemplateRendererTests(Base, unittest.TestCase): instance = self._makeOne(minimal, lookup) self.assertRaises(ValueError, instance, None, {}) - @skip_on('java', 'pypy') + @skip_on('java') def test_implementation(self): minimal = self._getTemplatePath('minimal.pt') lookup = DummyLookup() -- cgit v1.2.3 From 56f1d2ea13f93b46161d12112e75125efee2120b Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 21 Nov 2011 12:03:47 -0500 Subject: de-jythonify --- docs/narr/install.rst | 30 +++++------------------------- docs/narr/introduction.rst | 10 +++++----- docs/narr/templates.rst | 8 -------- 3 files changed, 10 insertions(+), 38 deletions(-) diff --git a/docs/narr/install.rst b/docs/narr/install.rst index 66bcea706..3de4d6e27 100644 --- a/docs/narr/install.rst +++ b/docs/narr/install.rst @@ -9,19 +9,18 @@ Installing :app:`Pyramid` Before You Install ------------------ -You will need `Python `_ version 2.5 or better to +You will need `Python `_ version 2.6 or better to run :app:`Pyramid`. .. sidebar:: Python Versions - As of this writing, :app:`Pyramid` has been tested under Python 2.5.5, - Python 2.6.6, and Python 2.7.2. :app:`Pyramid` does not run under any - version of Python before 2.5, and does not yet run under Python 3.X. + As of this writing, :app:`Pyramid` has been tested under Python 2.6.6, + Python 2.7.2, and Python 3.2. :app:`Pyramid` does not run under any + version of Python before 2.6. :app:`Pyramid` is known to run on all popular UNIX-like systems such as Linux, MacOS X, and FreeBSD as well as on Windows platforms. It is also -known to run on Google's App Engine, :term:`PyPy` (1.5 and 1.6), and -:term:`Jython` (2.5.2). +known to run on Google's App Engine, and :term:`PyPy` (1.6+). :app:`Pyramid` installation does not require the compilation of any C code, so you need only a Python interpreter that meets the @@ -325,25 +324,6 @@ Installing :app:`Pyramid` on Google App Engine :ref:`appengine_tutorial` documents the steps required to install a :app:`Pyramid` application on Google App Engine. -.. index:: - single: installing on Jython - -Installing :app:`Pyramid` on Jython --------------------------------------- - -:app:`Pyramid` is known to work under :term:`Jython` version 2.5.1. -Install :term:`Jython`, and then follow the installation steps for -:app:`Pyramid` on your platform described in one of the sections -entitled :ref:`installing_unix` or :ref:`installing_windows` above, -replacing the ``python`` command with ``jython`` as necessary. The -steps are exactly the same except you should use the ``jython`` -command name instead of the ``python`` command name. - -One caveat exists to using :app:`Pyramid` under Jython: the :term:`Chameleon` -templating engine does not work on Jython. However, the :term:`Mako` -templating system, which is also included with Pyramid, does work under -Jython; use it instead. - What Gets Installed ------------------- diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst index 547f88ef3..2387db6a7 100644 --- a/docs/narr/introduction.rst +++ b/docs/narr/introduction.rst @@ -808,11 +808,11 @@ Every release of Pyramid has 100% statement coverage via unit and integration tests, as measured by the ``coverage`` tool available on PyPI. It also has greater than 95% decision/condition coverage as measured by the ``instrumental`` tool available on PyPI. It is automatically tested by the -Jenkins tool on Python 2.5, Python 2.6, Python 2.7, Jython and PyPy after -each commit to its GitHub repository. Official Pyramid add-ons are held to a -similar testing standard. We still find bugs in Pyramid and its official -add-ons, but we've noticed we find a lot more of them while working on other -projects that don't have a good testing regime. +Jenkins tool on Python 2.6, Python 2.7, Python 3.2 and PyPy after each commit +to its GitHub repository. Official Pyramid add-ons are held to a similar +testing standard. We still find bugs in Pyramid and its official add-ons, +but we've noticed we find a lot more of them while working on other projects +that don't have a good testing regime. Example: http://jenkins.pylonsproject.org/ diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst index fb9dd56c2..11318d9eb 100644 --- a/docs/narr/templates.rst +++ b/docs/narr/templates.rst @@ -403,14 +403,6 @@ The language definition documentation for Chameleon ZPT-style templates is available from `the Chameleon website `_. -.. warning:: - - :term:`Chameleon` only works on :term:`CPython` platforms and - :term:`Google App Engine`. On :term:`Jython` and other non-CPython - platforms, you should use Mako (see :ref:`mako_templates`) or - ``pyramid_jinja2`` instead. See - :ref:`available_template_system_bindings`. - Given a :term:`Chameleon` ZPT template named ``foo.pt`` in a directory in your application named ``templates``, you can render the template as a :term:`renderer` like so: -- cgit v1.2.3 From c96ca8631d7899b2fe6770180b066fbec031473d Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 21 Nov 2011 13:31:23 -0500 Subject: rejigger alchemy scaffold as per convo with Mike --- TODO.txt | 4 +--- pyramid/scaffolds/alchemy/+package+/__init__.py | 16 +++++++++++++ .../scaffolds/alchemy/+package+/__init__.py_tmpl | 18 --------------- pyramid/scaffolds/alchemy/+package+/models.py | 18 --------------- .../alchemy/+package+/scripts/__init__.py | 1 + .../alchemy/+package+/scripts/populate.py | 27 ++++++++++++++++++++++ pyramid/scaffolds/alchemy/+package+/tests.py_tmpl | 26 +++++++++++++-------- pyramid/scaffolds/alchemy/+package+/views.py_tmpl | 12 ++++++---- pyramid/scaffolds/alchemy/setup.py_tmpl | 2 ++ 9 files changed, 71 insertions(+), 53 deletions(-) create mode 100644 pyramid/scaffolds/alchemy/+package+/__init__.py delete mode 100644 pyramid/scaffolds/alchemy/+package+/__init__.py_tmpl create mode 100644 pyramid/scaffolds/alchemy/+package+/scripts/__init__.py create mode 100644 pyramid/scaffolds/alchemy/+package+/scripts/populate.py diff --git a/TODO.txt b/TODO.txt index 2a5239629..f13cd5c6c 100644 --- a/TODO.txt +++ b/TODO.txt @@ -6,9 +6,7 @@ Must-Have - Fix SQLA tutorial to match ZODB tutorial. -- Fix routesalchemy scaffold to match ZODB scaffold. - -- Remove alchemy scaffold. +- Fix SQLA tutorial to match alchemy scaffold. Nice-to-Have ------------ diff --git a/pyramid/scaffolds/alchemy/+package+/__init__.py b/pyramid/scaffolds/alchemy/+package+/__init__.py new file mode 100644 index 000000000..253341563 --- /dev/null +++ b/pyramid/scaffolds/alchemy/+package+/__init__.py @@ -0,0 +1,16 @@ +from pyramid.config import Configurator +from sqlalchemy import engine_from_config + +from .models import DBSession + +def main(global_config, **settings): + """ This function returns a Pyramid WSGI application. + """ + engine = engine_from_config(settings, 'sqlalchemy.') + DBSession.configure(bind=engine) + config = Configurator(settings=settings) + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('home', '/') + config.scan() + return config.make_wsgi_app() + diff --git a/pyramid/scaffolds/alchemy/+package+/__init__.py_tmpl b/pyramid/scaffolds/alchemy/+package+/__init__.py_tmpl deleted file mode 100644 index 24201912b..000000000 --- a/pyramid/scaffolds/alchemy/+package+/__init__.py_tmpl +++ /dev/null @@ -1,18 +0,0 @@ -from pyramid.config import Configurator -from sqlalchemy import engine_from_config - -from {{package}}.models import initialize_sql - -def main(global_config, **settings): - """ This function returns a Pyramid WSGI application. - """ - engine = engine_from_config(settings, 'sqlalchemy.') - initialize_sql(engine) - config = Configurator(settings=settings) - config.add_static_view('static', '{{package}}:static', cache_max_age=3600) - config.add_route('home', '/') - config.add_view('{{package}}.views.my_view', - route_name='home', - renderer='templates/mytemplate.pt') - return config.make_wsgi_app() - diff --git a/pyramid/scaffolds/alchemy/+package+/models.py b/pyramid/scaffolds/alchemy/+package+/models.py index bef483d3a..000dadd91 100644 --- a/pyramid/scaffolds/alchemy/+package+/models.py +++ b/pyramid/scaffolds/alchemy/+package+/models.py @@ -1,10 +1,7 @@ -import transaction - from sqlalchemy import Column from sqlalchemy import Integer from sqlalchemy import Unicode -from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import scoped_session @@ -25,18 +22,3 @@ class MyModel(Base): self.name = name self.value = value -def populate(): - session = DBSession() - model = MyModel(name='root', value=55) - session.add(model) - session.flush() - transaction.commit() - -def initialize_sql(engine): - DBSession.configure(bind=engine) - Base.metadata.bind = engine - Base.metadata.create_all(engine) - try: - populate() - except IntegrityError: - transaction.abort() diff --git a/pyramid/scaffolds/alchemy/+package+/scripts/__init__.py b/pyramid/scaffolds/alchemy/+package+/scripts/__init__.py new file mode 100644 index 000000000..5bb534f79 --- /dev/null +++ b/pyramid/scaffolds/alchemy/+package+/scripts/__init__.py @@ -0,0 +1 @@ +# package diff --git a/pyramid/scaffolds/alchemy/+package+/scripts/populate.py b/pyramid/scaffolds/alchemy/+package+/scripts/populate.py new file mode 100644 index 000000000..5d367c987 --- /dev/null +++ b/pyramid/scaffolds/alchemy/+package+/scripts/populate.py @@ -0,0 +1,27 @@ +import os +import sys + +from paste.deploy.loadwsgi import appconfig +from sqlalchemy import engine_from_config +import transaction + +from ..models import DBSession +from ..models import MyModel +from ..models import Base + +def usage(argv): + print('usage: %s ' % os.path.basename(argv[0])) + sys.exit(1) + +def main(argv=sys.argv): + try: + config_filename = argv[1] + except IndexError: + usage(argv) + settings = appconfig('config:' + os.path.abspath(config_filename)) + engine = engine_from_config(settings, 'sqlalchemy.') + DBSession.configure(bind=engine) + Base.metadata.create_all(engine) + with transaction.manager: + model = MyModel(name=u'first', value=55) + DBSession.add(model) diff --git a/pyramid/scaffolds/alchemy/+package+/tests.py_tmpl b/pyramid/scaffolds/alchemy/+package+/tests.py_tmpl index 29aea7258..229caa8d8 100644 --- a/pyramid/scaffolds/alchemy/+package+/tests.py_tmpl +++ b/pyramid/scaffolds/alchemy/+package+/tests.py_tmpl @@ -1,24 +1,32 @@ import unittest -from pyramid.config import Configurator +import transaction + from pyramid import testing -def _initTestingDB(): - from sqlalchemy import create_engine - from {{package}}.models import initialize_sql - session = initialize_sql(create_engine('sqlite://')) - return session +from .models import DBSession class TestMyView(unittest.TestCase): def setUp(self): self.config = testing.setUp() - _initTestingDB() + from sqlalchemy import create_engine + engine = create_engine('sqlite://') + from .models import ( + Base, + MyModel, + ) + DBSession.configure(bind=engine) + Base.metadata.create_all(engine) + with transaction.manager: + model = MyModel(name=u'first', value=55) + DBSession.add(model) def tearDown(self): + DBSession.remove() testing.tearDown() def test_it(self): - from {{package}}.views import my_view + from .views import my_view request = testing.DummyRequest() info = my_view(request) - self.assertEqual(info['root'].name, 'root') + self.assertEqual(info['first'].name, 'first') self.assertEqual(info['project'], '{{project}}') diff --git a/pyramid/scaffolds/alchemy/+package+/views.py_tmpl b/pyramid/scaffolds/alchemy/+package+/views.py_tmpl index 45532b47b..69dbfd9ba 100644 --- a/pyramid/scaffolds/alchemy/+package+/views.py_tmpl +++ b/pyramid/scaffolds/alchemy/+package+/views.py_tmpl @@ -1,7 +1,9 @@ -from {{package}}.models import DBSession -from {{package}}.models import MyModel +from pyramid.view import view_config +from .models import DBSession +from .models import MyModel + +@view_config(route_name='home', renderer='templates/mytemplate.pt') def my_view(request): - dbsession = DBSession() - root = dbsession.query(MyModel).filter(MyModel.name=='root').first() - return {'root':root, 'project':'{{project}}'} + first = DBSession.query(MyModel).filter(MyModel.name==u'first').first() + return {'first':first, 'project':'{{project}}'} diff --git a/pyramid/scaffolds/alchemy/setup.py_tmpl b/pyramid/scaffolds/alchemy/setup.py_tmpl index a2cdaac60..a2766547d 100644 --- a/pyramid/scaffolds/alchemy/setup.py_tmpl +++ b/pyramid/scaffolds/alchemy/setup.py_tmpl @@ -41,6 +41,8 @@ setup(name='{{project}}', entry_points = """\ [paste.app_factory] main = {{package}}:main + [console_scripts] + populate_{{project}} = {{package}}.scripts.populate:main """, ) -- cgit v1.2.3 From 25b8a96b7825d0c3218bc1e3f90eaebd9b024371 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 21 Nov 2011 20:06:14 -0500 Subject: use reprs for py3 compat (py3 has no unicode literal syntax using u) --- pyramid/scaffolds/__init__.py | 4 ++++ .../alchemy/+package+/scripts/populate.py | 27 ---------------------- .../alchemy/+package+/scripts/populate.py_tmpl | 27 ++++++++++++++++++++++ pyramid/scaffolds/alchemy/+package+/tests.py_tmpl | 4 ++-- pyramid/scaffolds/alchemy/+package+/views.py_tmpl | 4 ++-- 5 files changed, 35 insertions(+), 31 deletions(-) delete mode 100644 pyramid/scaffolds/alchemy/+package+/scripts/populate.py create mode 100644 pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl diff --git a/pyramid/scaffolds/__init__.py b/pyramid/scaffolds/__init__.py index 3fba05390..20840a5d4 100644 --- a/pyramid/scaffolds/__init__.py +++ b/pyramid/scaffolds/__init__.py @@ -2,12 +2,16 @@ import binascii import os from pyramid.compat import native_ +from pyramid.compat import text_ from pyramid.scaffolds.template import Template class PyramidTemplate(Template): def pre(self, command, output_dir, vars): vars['random_string'] = native_(binascii.hexlify(os.urandom(20))) + # placeholder text values + vars['one'] = text_('one') + vars['two'] = text_('two') package_logger = vars['package'] if package_logger == 'root': # Rename the app logger in the rare case a project is named 'root' diff --git a/pyramid/scaffolds/alchemy/+package+/scripts/populate.py b/pyramid/scaffolds/alchemy/+package+/scripts/populate.py deleted file mode 100644 index 5d367c987..000000000 --- a/pyramid/scaffolds/alchemy/+package+/scripts/populate.py +++ /dev/null @@ -1,27 +0,0 @@ -import os -import sys - -from paste.deploy.loadwsgi import appconfig -from sqlalchemy import engine_from_config -import transaction - -from ..models import DBSession -from ..models import MyModel -from ..models import Base - -def usage(argv): - print('usage: %s ' % os.path.basename(argv[0])) - sys.exit(1) - -def main(argv=sys.argv): - try: - config_filename = argv[1] - except IndexError: - usage(argv) - settings = appconfig('config:' + os.path.abspath(config_filename)) - engine = engine_from_config(settings, 'sqlalchemy.') - DBSession.configure(bind=engine) - Base.metadata.create_all(engine) - with transaction.manager: - model = MyModel(name=u'first', value=55) - DBSession.add(model) diff --git a/pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl b/pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl new file mode 100644 index 000000000..7af326be9 --- /dev/null +++ b/pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl @@ -0,0 +1,27 @@ +import os +import sys + +from paste.deploy.loadwsgi import appconfig +from sqlalchemy import engine_from_config +import transaction + +from ..models import DBSession +from ..models import MyModel +from ..models import Base + +def usage(argv): + print('usage: %s ' % os.path.basename(argv[0])) + sys.exit(1) + +def main(argv=sys.argv): + try: + config_filename = argv[1] + except IndexError: + usage(argv) + settings = appconfig('config:' + os.path.abspath(config_filename)) + engine = engine_from_config(settings, 'sqlalchemy.') + DBSession.configure(bind=engine) + Base.metadata.create_all(engine) + with transaction.manager: + model = MyModel(name={{repr(one)}}, value=1) + DBSession.add(model) diff --git a/pyramid/scaffolds/alchemy/+package+/tests.py_tmpl b/pyramid/scaffolds/alchemy/+package+/tests.py_tmpl index 229caa8d8..3b4b028a9 100644 --- a/pyramid/scaffolds/alchemy/+package+/tests.py_tmpl +++ b/pyramid/scaffolds/alchemy/+package+/tests.py_tmpl @@ -17,7 +17,7 @@ class TestMyView(unittest.TestCase): DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: - model = MyModel(name=u'first', value=55) + model = MyModel(name={{repr(one)}}, value=55) DBSession.add(model) def tearDown(self): @@ -28,5 +28,5 @@ class TestMyView(unittest.TestCase): from .views import my_view request = testing.DummyRequest() info = my_view(request) - self.assertEqual(info['first'].name, 'first') + self.assertEqual(info['one'].name, 'one') self.assertEqual(info['project'], '{{project}}') diff --git a/pyramid/scaffolds/alchemy/+package+/views.py_tmpl b/pyramid/scaffolds/alchemy/+package+/views.py_tmpl index 69dbfd9ba..568b73c18 100644 --- a/pyramid/scaffolds/alchemy/+package+/views.py_tmpl +++ b/pyramid/scaffolds/alchemy/+package+/views.py_tmpl @@ -5,5 +5,5 @@ from .models import MyModel @view_config(route_name='home', renderer='templates/mytemplate.pt') def my_view(request): - first = DBSession.query(MyModel).filter(MyModel.name==u'first').first() - return {'first':first, 'project':'{{project}}'} + one = DBSession.query(MyModel).filter(MyModel.name=={{repr(one)}}).first() + return {'one':one, 'project':'{{project}}'} -- cgit v1.2.3 From f1013bebcdd013cd1cb47cf7585c0eaa34ec3b75 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Camguilhem Date: Sat, 19 Nov 2011 16:09:41 +0100 Subject: add bpython support for pshell --- docs/narr/commandline.rst | 14 ++++++++++++++ pyramid/scripts/pshell.py | 18 ++++++++++++++++++ pyramid/tests/test_scripts/dummy.py | 9 +++++++-- pyramid/tests/test_scripts/test_pshell.py | 9 +++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/docs/narr/commandline.rst b/docs/narr/commandline.rst index 0dc41e919..dc2b75ed6 100644 --- a/docs/narr/commandline.rst +++ b/docs/narr/commandline.rst @@ -111,6 +111,7 @@ For a URL that doesn't match any views, ``pviews`` will simply print out a single: interactive shell single: IPython single: pshell + single: bpython .. _interactive_shell: @@ -284,6 +285,19 @@ standard Python interpreter shell unconditionally. development.ini#MyProject +bpython +~~~~~~~ + +If you have `bpython `_ installed in +the interpreter you use to invoke the ``pshell`` command, ``pshell`` will use +a bpython interactive shell instead of a standard Python if you pass the ``-b`` +or ``--enable-bpython`` flag to the ``pshell`` command. + +.. code-block:: text + + [chrism@vitaminf shellenv]$ ../bin/pshell --enable-bpython \ + development.ini#MyProject + .. index:: pair: routes; printing single: proutes diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py index 499d96aca..d04d9faff 100644 --- a/pyramid/scripts/pshell.py +++ b/pyramid/scripts/pshell.py @@ -40,6 +40,10 @@ class PShellCommand(object): action='store_true', dest='disable_ipython', help="Don't use IPython even if it is available") + parser.add_option('-b', '--enable-bpython', + action='store_true', + dest='enable_bpython', + help="Use bpython as pshell") parser.add_option('--setup', dest='setup', help=("A callable that will be passed the environment " @@ -142,6 +146,9 @@ class PShellCommand(object): for var in sorted(self.object_help.keys()): help += '\n %-12s %s' % (var, self.object_help[var]) + if shell is None and self.options.enable_bpython: + shell = self.make_bpython_shell() + if shell is None and not self.options.disable_ipython: shell = self.make_ipython_v0_11_shell() if shell is None: @@ -163,6 +170,17 @@ class PShellCommand(object): interact(banner, local=env) return shell + def make_bpython_shell(self, BPShellFactory=None): + if BPShellFactory is None: # pragma: no cover + try: + from bpython import embed + BPShellFactory = embed + except ImportError: + return None + def shell(env, help): + BPShell = BPShellFactory(locals_=env, banner=help + '\n') + return shell + def make_ipython_v0_11_shell(self, IPShellFactory=None): if IPShellFactory is None: # pragma: no cover try: diff --git a/pyramid/tests/test_scripts/dummy.py b/pyramid/tests/test_scripts/dummy.py index 3275f7804..d580203af 100644 --- a/pyramid/tests/test_scripts/dummy.py +++ b/pyramid/tests/test_scripts/dummy.py @@ -5,7 +5,7 @@ class DummyTweens(object): self.name_to_alias = {} def implicit(self): return self._implicit - + class Dummy: pass @@ -31,6 +31,11 @@ class DummyInteractor: self.banner = banner self.local = local +class DummyBPythonShell: + def __call__(self, locals_, banner): + self.locals_ = locals_ + self.banner = banner + class DummyIPShell(object): IP = Dummy() IP.BANNER = 'foo' @@ -72,7 +77,7 @@ class DummyRoute(object): def match(self, route): return self.matchdict - + class DummyRequest: application_url = 'http://example.com:5432' script_name = '' diff --git a/pyramid/tests/test_scripts/test_pshell.py b/pyramid/tests/test_scripts/test_pshell.py index e38da2077..95bdce463 100644 --- a/pyramid/tests/test_scripts/test_pshell.py +++ b/pyramid/tests/test_scripts/test_pshell.py @@ -22,6 +22,7 @@ class TestPShellCommand(unittest.TestCase): class Options(object): pass self.options = Options() self.options.disable_ipython = True + self.options.enable_bpython = False self.options.setup = None cmd.options = self.options return cmd @@ -34,6 +35,14 @@ class TestPShellCommand(unittest.TestCase): self.assertEqual(interact.local, {'foo': 'bar'}) self.assertTrue('a help message' in interact.banner) + def test_make_bpython_shell(self): + command = self._makeOne() + bpython = dummy.DummyBPythonShell() + shell = command.make_bpython_shell(bpython) + shell({'foo': 'bar'}, 'a help message') + self.assertEqual(bpython.locals_, {'foo': 'bar'}) + self.assertTrue('a help message' in bpython.banner) + def test_make_ipython_v0_11_shell(self): command = self._makeOne() ipshell_factory = dummy.DummyIPShellFactory() -- cgit v1.2.3 From 7bae406b11df0e3039798898e7951a09f31c0e53 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Camguilhem Date: Sun, 20 Nov 2011 21:45:21 +0100 Subject: add test_command_loads_bpython_shell --- pyramid/tests/test_scripts/test_pshell.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pyramid/tests/test_scripts/test_pshell.py b/pyramid/tests/test_scripts/test_pshell.py index 95bdce463..4f3853f91 100644 --- a/pyramid/tests/test_scripts/test_pshell.py +++ b/pyramid/tests/test_scripts/test_pshell.py @@ -82,6 +82,25 @@ class TestPShellCommand(unittest.TestCase): self.assertTrue(self.bootstrap.closer.called) self.assertTrue(shell.help) + def test_command_loads_bpython_shell(self): + command = self._makeOne() + shell = dummy.DummyBPythonShell() + command.make_bpython_shell = lambda: shell + command.options.enable_bpython = True + command.run() + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.locals_, { + 'app':self.bootstrap.app, 'root':self.bootstrap.root, + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.banner) + def test_command_loads_default_shell_with_ipython_disabled(self): command = self._makeOne() shell = dummy.DummyShell() -- cgit v1.2.3 From 2cf5d280866e8936b0fee0952c89ebde164337ee Mon Sep 17 00:00:00 2001 From: Jean-Philippe Camguilhem Date: Tue, 22 Nov 2011 00:20:07 +0100 Subject: add bpython support to pshell with raydeo remarks and design --- docs/narr/commandline.rst | 34 +++++++------------ pyramid/scripts/pshell.py | 26 ++++++++------- pyramid/tests/test_scripts/test_pshell.py | 55 +++++++++++++++++-------------- 3 files changed, 56 insertions(+), 59 deletions(-) diff --git a/docs/narr/commandline.rst b/docs/narr/commandline.rst index dc2b75ed6..0f0e17ca6 100644 --- a/docs/narr/commandline.rst +++ b/docs/narr/commandline.rst @@ -268,34 +268,22 @@ exposed, and the request is configured to generate urls from the host .. index:: single: IPython + single: bpython -IPython -~~~~~~~ - -If you have `IPython `_ installed in -the interpreter you use to invoke the ``pshell`` command, ``pshell`` will use -an IPython interactive shell instead of a standard Python interpreter shell. -If you don't want this to happen, even if you have IPython installed, you can -pass the ``--disable-ipython`` flag to the ``pshell`` command to use a -standard Python interpreter shell unconditionally. - -.. code-block:: text - - [chrism@vitaminf shellenv]$ ../bin/pshell --disable-ipython \ - development.ini#MyProject - - -bpython -~~~~~~~ +IPython or bpython +~~~~~~~~~~~~~~~~~~ -If you have `bpython `_ installed in -the interpreter you use to invoke the ``pshell`` command, ``pshell`` will use -a bpython interactive shell instead of a standard Python if you pass the ``-b`` -or ``--enable-bpython`` flag to the ``pshell`` command. +If you have `IPython `_ or +`bpython `_ or both installed in +the interpreter you use to invoke the ``pshell`` command, ``pshell`` will +autodiscover them and use the first respectively found in this order : +IPython, bpython, standard Python interpreter. However you could +specifically invoke one of your choice with the ``-p choice`` or +``--python-shell choice`` option. .. code-block:: text - [chrism@vitaminf shellenv]$ ../bin/pshell --enable-bpython \ + [chrism@vitaminf shellenv]$ ../bin/pshell -p ipython | bpython | python \ development.ini#MyProject .. index:: diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py index d04d9faff..3fbfa5e62 100644 --- a/pyramid/scripts/pshell.py +++ b/pyramid/scripts/pshell.py @@ -36,14 +36,9 @@ class PShellCommand(object): summary = "Open an interactive shell with a Pyramid application loaded" parser = optparse.OptionParser() - parser.add_option('-d', '--disable-ipython', - action='store_true', - dest='disable_ipython', - help="Don't use IPython even if it is available") - parser.add_option('-b', '--enable-bpython', - action='store_true', - dest='enable_bpython', - help="Use bpython as pshell") + parser.add_option('-p', '--python-shell', + action='store', type='string', dest='python_shell', + default = '', help='ipython | bpython | python') parser.add_option('--setup', dest='setup', help=("A callable that will be passed the environment " @@ -146,14 +141,23 @@ class PShellCommand(object): for var in sorted(self.object_help.keys()): help += '\n %-12s %s' % (var, self.object_help[var]) - if shell is None and self.options.enable_bpython: - shell = self.make_bpython_shell() + user_shell = self.options.python_shell.lower() + if not user_shell: + if shell is None: + shell = self.make_ipython_v0_11_shell() + if shell is None: + shell = self.make_ipython_v0_10_shell() + if shell is None: + shell = self.make_bpython_shell() - if shell is None and not self.options.disable_ipython: + if shell is None and user_shell == 'ipython': shell = self.make_ipython_v0_11_shell() if shell is None: shell = self.make_ipython_v0_10_shell() + if shell is None and user_shell == 'bpython': + shell = self.make_bpython_shell() + if shell is None: shell = self.make_default_shell() diff --git a/pyramid/tests/test_scripts/test_pshell.py b/pyramid/tests/test_scripts/test_pshell.py index 4f3853f91..4e5deb31d 100644 --- a/pyramid/tests/test_scripts/test_pshell.py +++ b/pyramid/tests/test_scripts/test_pshell.py @@ -21,8 +21,7 @@ class TestPShellCommand(unittest.TestCase): if patch_options: class Options(object): pass self.options = Options() - self.options.disable_ipython = True - self.options.enable_bpython = False + self.options.python_shell = '' self.options.setup = None cmd.options = self.options return cmd @@ -67,6 +66,7 @@ class TestPShellCommand(unittest.TestCase): shell = dummy.DummyShell() command.make_ipython_v0_11_shell = lambda: None command.make_ipython_v0_10_shell = lambda: None + command.make_bpython_shell = lambda: None command.make_default_shell = lambda: shell command.run() self.assertTrue(self.config_factory.parser) @@ -82,33 +82,15 @@ class TestPShellCommand(unittest.TestCase): self.assertTrue(self.bootstrap.closer.called) self.assertTrue(shell.help) - def test_command_loads_bpython_shell(self): - command = self._makeOne() - shell = dummy.DummyBPythonShell() - command.make_bpython_shell = lambda: shell - command.options.enable_bpython = True - command.run() - self.assertTrue(self.config_factory.parser) - self.assertEqual(self.config_factory.parser.filename, - '/foo/bar/myapp.ini') - self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') - self.assertEqual(shell.locals_, { - 'app':self.bootstrap.app, 'root':self.bootstrap.root, - 'registry':self.bootstrap.registry, - 'request':self.bootstrap.request, - 'root_factory':self.bootstrap.root_factory, - }) - self.assertTrue(self.bootstrap.closer.called) - self.assertTrue(shell.banner) - - def test_command_loads_default_shell_with_ipython_disabled(self): + def test_command_loads_default_shell_with_unknow_shell(self): command = self._makeOne() shell = dummy.DummyShell() bad_shell = dummy.DummyShell() command.make_ipython_v0_11_shell = lambda: bad_shell command.make_ipython_v0_10_shell = lambda: bad_shell + command.make_bpython_shell = lambda: bad_shell command.make_default_shell = lambda: shell - command.options.disable_ipython = True + command.options.python_shell = 'unknow_python_shell' command.run() self.assertTrue(self.config_factory.parser) self.assertEqual(self.config_factory.parser.filename, @@ -129,8 +111,9 @@ class TestPShellCommand(unittest.TestCase): shell = dummy.DummyShell() command.make_ipython_v0_11_shell = lambda: shell command.make_ipython_v0_10_shell = lambda: None + command.make_bpython_shell = lambda: None command.make_default_shell = lambda: None - command.options.disable_ipython = False + command.options.python_shell = 'ipython' command.run() self.assertTrue(self.config_factory.parser) self.assertEqual(self.config_factory.parser.filename, @@ -150,8 +133,9 @@ class TestPShellCommand(unittest.TestCase): shell = dummy.DummyShell() command.make_ipython_v0_11_shell = lambda: None command.make_ipython_v0_10_shell = lambda: shell + command.make_bpython_shell = lambda: None command.make_default_shell = lambda: None - command.options.disable_ipython = False + command.options.python_shell = 'ipython' command.run() self.assertTrue(self.config_factory.parser) self.assertEqual(self.config_factory.parser.filename, @@ -166,6 +150,27 @@ class TestPShellCommand(unittest.TestCase): self.assertTrue(self.bootstrap.closer.called) self.assertTrue(shell.help) + def test_command_loads_bpython_shell(self): + command = self._makeOne() + shell = dummy.DummyBPythonShell() + command.make_ipython_v0_11_shell = lambda: None + command.make_ipython_v0_10_shell = lambda: None + command.make_bpython_shell = lambda: shell + command.options.python_shell = 'bpython' + command.run() + self.assertTrue(self.config_factory.parser) + self.assertEqual(self.config_factory.parser.filename, + '/foo/bar/myapp.ini') + self.assertEqual(self.bootstrap.a[0], '/foo/bar/myapp.ini#myapp') + self.assertEqual(shell.locals_, { + 'app':self.bootstrap.app, 'root':self.bootstrap.root, + 'registry':self.bootstrap.registry, + 'request':self.bootstrap.request, + 'root_factory':self.bootstrap.root_factory, + }) + self.assertTrue(self.bootstrap.closer.called) + self.assertTrue(shell.banner) + def test_command_loads_custom_items(self): command = self._makeOne() model = dummy.Dummy() -- cgit v1.2.3 From ce1d0f0ea126fc8d1e90e0a85a5e735ac34afbae Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 21 Nov 2011 19:01:18 -0600 Subject: bpython doesn't actually use a factory --- pyramid/scripts/pshell.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py index 3fbfa5e62..8cf1a4b39 100644 --- a/pyramid/scripts/pshell.py +++ b/pyramid/scripts/pshell.py @@ -178,11 +178,11 @@ class PShellCommand(object): if BPShellFactory is None: # pragma: no cover try: from bpython import embed - BPShellFactory = embed + BPShell = embed except ImportError: return None def shell(env, help): - BPShell = BPShellFactory(locals_=env, banner=help + '\n') + BPShell(locals_=env, banner=help + '\n') return shell def make_ipython_v0_11_shell(self, IPShellFactory=None): -- cgit v1.2.3 From 3808f7fe58f9e5724fbf9ab7e213cde4f21e26f2 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 21 Nov 2011 19:30:40 -0600 Subject: Added tests for shell ordering in pshell. --- pyramid/scripts/pshell.py | 34 ++++++++++++--------- pyramid/tests/test_scripts/test_pshell.py | 49 +++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py index 8cf1a4b39..72e2c0a89 100644 --- a/pyramid/scripts/pshell.py +++ b/pyramid/scripts/pshell.py @@ -38,7 +38,7 @@ class PShellCommand(object): parser = optparse.OptionParser() parser.add_option('-p', '--python-shell', action='store', type='string', dest='python_shell', - default = '', help='ipython | bpython | python') + default='', help='ipython | bpython | python') parser.add_option('--setup', dest='setup', help=("A callable that will be passed the environment " @@ -141,30 +141,36 @@ class PShellCommand(object): for var in sorted(self.object_help.keys()): help += '\n %-12s %s' % (var, self.object_help[var]) + if shell is None: + shell = self.make_shell() + + try: + shell(env, help) + finally: + closer() + + def make_shell(self): + shell = None user_shell = self.options.python_shell.lower() if not user_shell: + shell = self.make_ipython_v0_11_shell() if shell is None: - shell = self.make_ipython_v0_11_shell() - if shell is None: - shell = self.make_ipython_v0_10_shell() - if shell is None: - shell = self.make_bpython_shell() + shell = self.make_ipython_v0_10_shell() + if shell is None: + shell = self.make_bpython_shell() - if shell is None and user_shell == 'ipython': + elif user_shell == 'ipython': shell = self.make_ipython_v0_11_shell() if shell is None: shell = self.make_ipython_v0_10_shell() - if shell is None and user_shell == 'bpython': + elif user_shell == 'bpython': shell = self.make_bpython_shell() if shell is None: shell = self.make_default_shell() - try: - shell(env, help) - finally: - closer() + return shell def make_default_shell(self, interact=interact): def shell(env, help): @@ -174,8 +180,8 @@ class PShellCommand(object): interact(banner, local=env) return shell - def make_bpython_shell(self, BPShellFactory=None): - if BPShellFactory is None: # pragma: no cover + def make_bpython_shell(self, BPShell=None): + if BPShell is None: # pragma: no cover try: from bpython import embed BPShell = embed diff --git a/pyramid/tests/test_scripts/test_pshell.py b/pyramid/tests/test_scripts/test_pshell.py index 4e5deb31d..c1f648b6f 100644 --- a/pyramid/tests/test_scripts/test_pshell.py +++ b/pyramid/tests/test_scripts/test_pshell.py @@ -171,6 +171,55 @@ class TestPShellCommand(unittest.TestCase): self.assertTrue(self.bootstrap.closer.called) self.assertTrue(shell.banner) + def test_shell_ipython_ordering(self): + command = self._makeOne() + shell0_11 = dummy.DummyShell() + shell0_10 = dummy.DummyShell() + command.make_ipython_v0_11_shell = lambda: shell0_11 + command.make_ipython_v0_10_shell = lambda: shell0_10 + command.make_bpython_shell = lambda: None + shell = command.make_shell() + self.assertEqual(shell, shell0_11) + + command.options.python_shell = 'ipython' + shell = command.make_shell() + self.assertEqual(shell, shell0_11) + + def test_shell_ordering(self): + command = self._makeOne() + ipshell = dummy.DummyShell() + bpshell = dummy.DummyShell() + dshell = dummy.DummyShell() + command.make_ipython_v0_11_shell = lambda: None + command.make_ipython_v0_10_shell = lambda: None + command.make_bpython_shell = lambda: None + command.make_default_shell = lambda: dshell + + shell = command.make_shell() + self.assertEqual(shell, dshell) + + command.options.python_shell = 'ipython' + shell = command.make_shell() + self.assertEqual(shell, dshell) + + command.options.python_shell = 'bpython' + shell = command.make_shell() + self.assertEqual(shell, dshell) + + command.make_ipython_v0_11_shell = lambda: ipshell + command.make_bpython_shell = lambda: bpshell + command.options.python_shell = 'ipython' + shell = command.make_shell() + self.assertEqual(shell, ipshell) + + command.options.python_shell = 'bpython' + shell = command.make_shell() + self.assertEqual(shell, bpshell) + + command.options.python_shell = 'python' + shell = command.make_shell() + self.assertEqual(shell, dshell) + def test_command_loads_custom_items(self): command = self._makeOne() model = dummy.Dummy() -- cgit v1.2.3 From 995133918877809aeea06bce93149104ba800304 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 21 Nov 2011 20:44:42 -0600 Subject: typo --- pyramid/tests/test_scripts/test_pshell.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyramid/tests/test_scripts/test_pshell.py b/pyramid/tests/test_scripts/test_pshell.py index c1f648b6f..765042152 100644 --- a/pyramid/tests/test_scripts/test_pshell.py +++ b/pyramid/tests/test_scripts/test_pshell.py @@ -82,7 +82,7 @@ class TestPShellCommand(unittest.TestCase): self.assertTrue(self.bootstrap.closer.called) self.assertTrue(shell.help) - def test_command_loads_default_shell_with_unknow_shell(self): + def test_command_loads_default_shell_with_unknown_shell(self): command = self._makeOne() shell = dummy.DummyShell() bad_shell = dummy.DummyShell() -- cgit v1.2.3 From 8a5db43e9b7671f49e118cbb888019445df0ae14 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Mon, 21 Nov 2011 21:59:53 -0500 Subject: Garden (ref commit #2cf5d28). --- CHANGES.txt | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 33008cbea..183c7406b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -14,6 +14,9 @@ Features spec. An entire application can be written with such asset specs, requiring no ordered lookup path. +- ``bpython`` interpreter compatibility in ``pshell``. See the "Command-Line + Pyramid" narrative docs chapter for more information. + Bug Fixes --------- @@ -57,6 +60,11 @@ Backwards Incompatibilities ``paste.httpserver`` server. Rationale: Rationale: the Paste and PasteScript packages do not run under Python 3. +- The ``pshell`` command (nee "paster pshell") no longer accepts a + ``--disable-ipython`` command-line argument. Instead, it accepts a ``-p`` + or ``--python-shell`` argument, which can be any of the values ``python``, + ``ipython`` or ``bpython``. + Dependencies ------------ @@ -77,7 +85,6 @@ Scaffolds - Rendered scaffolds have now been changed to be more relocatable (fewer mentions of the package name within files in the package). -- The ``alchemy`` scaffold has been removed. - -- The ``routesalchemy`` scaffold has been renamed ``alchemy``. +- The ``routesalchemy`` scaffold has been renamed ``alchemy``, replacing the + older (traversal-based) ``alchemy`` scaffold (which has been retired). -- cgit v1.2.3 From 4c6fa2f4ef0bc7ad24759648cae1dadaab087bd1 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 21 Nov 2011 22:52:59 -0600 Subject: typo --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 183c7406b..effce62b9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -60,7 +60,7 @@ Backwards Incompatibilities ``paste.httpserver`` server. Rationale: Rationale: the Paste and PasteScript packages do not run under Python 3. -- The ``pshell`` command (nee "paster pshell") no longer accepts a +- The ``pshell`` command (see "paster pshell") no longer accepts a ``--disable-ipython`` command-line argument. Instead, it accepts a ``-p`` or ``--python-shell`` argument, which can be any of the values ``python``, ``ipython`` or ``bpython``. -- cgit v1.2.3 From 61ae5201544f71971c31d1b2a88daa10191163c0 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Fri, 25 Nov 2011 22:26:59 -0500 Subject: note suggestions from mike in issue #359 --- TODO.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/TODO.txt b/TODO.txt index f13cd5c6c..a3de49e2f 100644 --- a/TODO.txt +++ b/TODO.txt @@ -6,7 +6,9 @@ Must-Have - Fix SQLA tutorial to match ZODB tutorial. -- Fix SQLA tutorial to match alchemy scaffold. +- Fix SQLA tutorial to match alchemy scaffold. See + https://github.com/Pylons/pyramid/issues/359 for additional suggestions + from Mike. Nice-to-Have ------------ -- cgit v1.2.3 From 3bfb4068c2e17a282b53d1b7f14f653f128cced4 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 26 Nov 2011 12:30:39 -0500 Subject: fix to match starter package --- docs/narr/MyProject/myproject/static/pylons.css | 15 +++-- .../MyProject/myproject/templates/mytemplate.pt | 76 +++++++--------------- 2 files changed, 31 insertions(+), 60 deletions(-) diff --git a/docs/narr/MyProject/myproject/static/pylons.css b/docs/narr/MyProject/myproject/static/pylons.css index 33b21ac1a..c54499ddd 100644 --- a/docs/narr/MyProject/myproject/static/pylons.css +++ b/docs/narr/MyProject/myproject/static/pylons.css @@ -23,7 +23,7 @@ h2{font-size:1.5em;line-height:1.7em;font-family:helvetica,verdana;} h3{font-size:1.25em;line-height:1.7em;font-family:helvetica,verdana;} h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;} html,body{width:100%;height:100%;} -body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;} +body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "NobileRegular","Lucida Grande",Lucida,Verdana,sans-serif;} a{color:#1b61d6;text-decoration:none;} a:hover{color:#e88f00;text-decoration:underline;} body h1, @@ -31,19 +31,20 @@ body h2, body h3, body h4, body h5, -body h6{font-family:"Neuton","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;} +body h6{font-family:"NeutonRegular","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#373839;font-style:normal;} #wrap{min-height:100%;} #header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;vertical-align:middle;} #header{background:#000000;top:0;font-size:14px;} #footer{bottom:0;background:#000000 url(footerbg.png) repeat-x 0 top;position:relative;margin-top:-40px;clear:both;} .header,.footer{width:750px;margin-right:auto;margin-left:auto;} .wrapper{width:100%} -#top,#bottom{width:100%;} -#top{color:#000000;height:230px; -background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;} +#top,#top-small,#bottom{width:100%;} +#top{color:#000000;height:230px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;} +#top-small{color:#000000;height:60px;background:#ffffff url(headerbg.png) repeat-x 0 top;position:relative;} #bottom{color:#222;background-color:#ffffff;} -.top,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;} +.top,.top-small,.middle,.bottom{width:750px;margin-right:auto;margin-left:auto;} .top{padding-top:40px;} +.top-small{padding-top:10px;} #middle{width:100%;height:100px;background:url(middlebg.png) repeat-x;border-top:2px solid #ffffff;border-bottom:2px solid #b2b2b2;} .app-welcome{margin-top:25px;} .app-name{color:#000000;font-weight:bold;} @@ -58,7 +59,7 @@ ul.links li{list-style-type:none;font-size:14px;} form{border-style:none;} fieldset{border-style:none;} input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;} -input[type=text]{width:205px;} +input[type=text],input[type=password]{width:205px;} input[type=submit]{background-color:#ddd;font-weight:bold;} /*Opera Fix*/ body:before{content:"";height:100%;float:left;width:0;margin-top:-32767px;} diff --git a/docs/narr/MyProject/myproject/templates/mytemplate.pt b/docs/narr/MyProject/myproject/templates/mytemplate.pt index 97f1e1aa3..ab698123e 100644 --- a/docs/narr/MyProject/myproject/templates/mytemplate.pt +++ b/docs/narr/MyProject/myproject/templates/mytemplate.pt @@ -1,42 +1,29 @@ - - + + The Pyramid Web Application Development Framework - - - + + + +
-
- pyramid -
+
pyramid

- Welcome to ${project}, - an application generated by
+ Welcome to ${project}, an application generated by
the Pyramid web application development framework.

@@ -45,62 +32,45 @@
-- cgit v1.2.3 From 602ac1bd027f0f78b1164f0180a9b74771bbbb3c Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 26 Nov 2011 13:09:05 -0600 Subject: garden --- pyramid/view.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyramid/view.py b/pyramid/view.py index 13d5cfe7b..f7d4d8945 100644 --- a/pyramid/view.py +++ b/pyramid/view.py @@ -156,11 +156,12 @@ class view_config(object): backwards compatibility purposes, as the name :class:`pyramid.view.bfg_view`. - The following arguments are supported as arguments to + The following arguments are supported to :class:`pyramid.view.view_config`: ``context``, ``permission``, ``name``, ``request_type``, ``route_name``, ``request_method``, ``request_param``, ``containment``, ``xhr``, ``accept``, ``header``, ``path_info``, - ``custom_predicates``, ``decorator``, ``mapper``, and ``http_cache``. + ``custom_predicates``, ``decorator``, ``mapper``, ``http_cache``, + and ``match_param``. The meanings of these arguments are the same as the arguments passed to :meth:`pyramid.config.Configurator.add_view`. -- cgit v1.2.3 From 6772a25160b0c996ae6e7baa012199b606407db4 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Sat, 26 Nov 2011 14:56:29 -0500 Subject: output warning to use populate command to console after alchemy scaffold rendering; emit logging statements when populate is run --- docs/tutorials/wiki/basiclayout.rst | 4 +- docs/tutorials/wiki2/basiclayout.rst | 159 +++++++++++++-------- docs/tutorials/wiki2/definingmodels.rst | 5 + docs/tutorials/wiki2/installation.rst | 148 ++++++++++++++++--- .../wiki2/src/basiclayout/tutorial/__init__.py | 11 +- .../wiki2/src/basiclayout/tutorial/views.py | 12 +- pyramid/scaffolds/__init__.py | 9 +- .../alchemy/+package+/scripts/populate.py_tmpl | 5 +- 8 files changed, 257 insertions(+), 96 deletions(-) diff --git a/docs/tutorials/wiki/basiclayout.rst b/docs/tutorials/wiki/basiclayout.rst index 439da24d9..56f817a85 100644 --- a/docs/tutorials/wiki/basiclayout.rst +++ b/docs/tutorials/wiki/basiclayout.rst @@ -10,8 +10,8 @@ The source code for this tutorial stage can be browsed via `http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki/src/basiclayout/ `_. -App Startup with ``__init__.py`` --------------------------------- +Appplication Configuration with ``__init__.py`` +------------------------------------------------ A directory on disk can be turned into a Python :term:`package` by containing an ``__init__.py`` file. Even if empty, this marks a directory as a Python diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst index db8ab1fbe..dc5664c5b 100644 --- a/docs/tutorials/wiki2/basiclayout.rst +++ b/docs/tutorials/wiki2/basiclayout.rst @@ -2,82 +2,96 @@ Basic Layout ============ -The starter files generated by the ``alchemy`` scaffold are -basic, but they provide a good orientation for the high-level patterns common -to most :term:`url dispatch` -based :app:`Pyramid` projects. +The starter files generated by the ``alchemy`` scaffold are very basic, but +they provide a good orientation for the high-level patterns common to most +:term:`url dispatch` -based :app:`Pyramid` projects. The source code for this tutorial stage can be browsed at `http://github.com/Pylons/pyramid/tree/master/docs/tutorials/wiki2/src/basiclayout/ `_. -App Startup with ``__init__.py`` --------------------------------- +Application Configuration with ``__init__.py`` +---------------------------------------------- A directory on disk can be turned into a Python :term:`package` by containing an ``__init__.py`` file. Even if empty, this marks a directory as a Python -package. We use ``__init__.py`` both as a package marker and to contain -configuration code. +package. We use ``__init__.py`` both as a marker indicating the directory +it's contained within is a package, and to contain configuration code. Our +``__init__.py`` file will look like this: -The generated ``development.ini`` file is read by ``pserve`` which looks for -the application module in the ``use`` variable of the ``app:main`` -section. The *entry point* is defined in the Setuptools configuration of this -module, specifically in the ``setup.py`` file. For this tutorial, the *entry -point* is defined as ``tutorial:main`` and points to a function named -``main``. + .. literalinclude:: src/basiclayout/tutorial/__init__.py + :linenos: + :language: py -First we need some imports to support later code: +Let's go over this piece-by-piece. First, we need some imports to support +later code: .. literalinclude:: src/basiclayout/tutorial/__init__.py :end-before: main :linenos: :language: py -Next we define the main function and create a SQLAlchemy database engine from -the ``sqlalchemy.`` prefixed settings in the ``development.ini`` file's -``[app:main]`` section. This will be a URI (something like -``sqlite://``): +``__init__.py`` defines a function named ``main``. Here is the entirety of +the ``main`` function we've defined in our ``__init__.py``: + + .. literalinclude:: src/basiclayout/tutorial/__init__.py + :pyobject: main + :linenos: + :language: py + +When you invoke the ``pserve development.ini`` command, the ``main`` function +above is executed. It accepts some settings and returns a :term:`WSGI` +application. You can read :ref:`startup_chapter` for details about *how* +this function is found and called when you run ``pserve``, but for purposes +of brevity, we'll elide the details here. + +The main function first creates a SQLAlchemy database engine using +``engine_from_config`` from the ``sqlalchemy.`` prefixed settings in the +``development.ini`` file's ``[app:main]`` section. This will be a URI +(something like ``sqlite://``): .. literalinclude:: src/basiclayout/tutorial/__init__.py - :lines: 6-9 + :lines: 9 :linenos: :language: py -We then initialize our SQL database using SQLAlchemy, passing -it the engine: +``main`` then initializes our SQL database using SQLAlchemy, passing it the +engine: .. literalinclude:: src/basiclayout/tutorial/__init__.py :lines: 10 :language: py -The next step is to construct a :term:`Configurator`: +The next step of ``main`` is to construct a :term:`Configurator` object: .. literalinclude:: src/basiclayout/tutorial/__init__.py :lines: 11 :language: py ``settings`` is passed to the Configurator as a keyword argument with the -dictionary values passed by PasteDeploy as the ``**settings`` argument. This -will be a dictionary of settings parsed from the ``.ini`` file, which -contains deployment-related values such as ``pyramid.reload_templates``, +dictionary values passed as the ``**settings`` argument. This will be a +dictionary of settings parsed from the ``.ini`` file, which contains +deployment-related values such as ``pyramid.reload_templates``, ``db_string``, etc. -We now can call :meth:`pyramid.config.Configurator.add_static_view` with the -arguments ``static`` (the name), and ``tutorial:static`` (the path): +``'main`` now calls :meth:`pyramid.config.Configurator.add_static_view` with +two arguments: ``static`` (the name), and ``static`` (the path): .. literalinclude:: src/basiclayout/tutorial/__init__.py :lines: 12 :language: py -This registers a static resource view which will match any URL that starts with -``/static/``. This will serve up static resources for us from within the -``static`` directory of our ``tutorial`` package, in this case, -via ``http://localhost:6543/static/`` and below. With this declaration, -we're saying that any URL that starts with ``/static`` should go to the -static view; any remainder of its path (e.g. the ``/foo`` in -``/static/foo``) will be used to compose a path to a static file resource, -such as a CSS file. +This registers a static resource view which will match any URL that starts +with the prefix ``/static`` (by virtue of the first argument to add_static +view). This will serve up static resources for us from within the ``static`` +directory of our ``tutorial`` package, in this case, via +``http://localhost:6543/static/`` and below (by virtue of the second argument +to add_static_view). With this declaration, we're saying that any URL that +starts with ``/static`` should go to the static view; any remainder of its +path (e.g. the ``/foo`` in ``/static/foo``) will be used to compose a path to +a static file resource, such as a CSS file. -Using the configurator we can also register a :term:`route configuration` +Using the configurator ``main`` also registers a :term:`route configuration` via the :meth:`pyramid.config.Configurator.add_route` method that will be used when the URL is ``/``: @@ -88,44 +102,71 @@ used when the URL is ``/``: Since this route has a ``pattern`` equalling ``/`` it is the route that will be matched when the URL ``/`` is visted, e.g. ``http://localhost:6543/``. -Mapping the ``home`` route to code is done by registering a view. You will -use :meth:`pyramid.config.Configurator.add_view` in :term:`URL dispatch` to -register views for the routes, mapping your patterns to code: +``main`` next calls the ``scan`` method of the configurator, which will +recursively scan our ``tutorial`` package, looking for ``@view_config`` (and +other special) decorators. When it finds a ``@view_config`` decorator, a +view configuration will be registered, which will allow one of our +application URLs to be mapped to some code. .. literalinclude:: src/basiclayout/tutorial/__init__.py - :lines: 14-15 + :lines: 14 :language: py -The first positional ``add_view`` argument ``tutorial.views.my_view`` is the -dotted name to a *function* we write (generated by the -``alchemy`` scaffold) that is given a ``request`` object and -which returns a response or a dictionary. This view also names a -``renderer``, which is a template which lives in the ``templates`` -subdirectory of the package. When the ``tutorial.views.my_view`` view -returns a dictionary, a :term:`renderer` will use this template to create a -response. - -Finally, we use the :meth:`pyramid.config.Configurator.make_wsgi_app` -method to return a :term:`WSGI` application: +Finally, ``main`` is finished configuring things, so it uses the +:meth:`pyramid.config.Configurator.make_wsgi_app` method to return a +:term:`WSGI` application: .. literalinclude:: src/basiclayout/tutorial/__init__.py - :lines: 16 + :lines: 15 :language: py -Our final ``__init__.py`` file will look like this: +View Declarations via ``views.py`` +---------------------------------- - .. literalinclude:: src/basiclayout/tutorial/__init__.py +Mapping a :term:`route` to code that will be executed when that route's +pattern matches is done by registering a :term:`view configuration`. Our +application uses the :meth:`pyramid.view.view_config` decorator to map view +callables to each route, thereby mapping URL patterns to code. + +Here is the code in the ``views.py`` file within our package: + + .. literalinclude:: src/basiclayout/tutorial/views.py :linenos: :language: py +The important part to point out here is the ``@view_config`` decorator. In +fact, ``@view_config`` is so important that we're going to ignore the rest of +the code in the module at this point just to explain it. The +``@view_config`` decorator associates the function it decorates with a +:term:`view configuration`. The view configuration names a ``route_name`` +(``home``), and names a ``renderer``, which is a template which lives in the +``templates`` subdirectory of the package. + +As the result of this view configuration, when the pattern associated with +the view named ``home`` is matched during a request, the function named +``my_view`` will be executed. The the function named ``my_view`` returns a +dictionary; the renderer will use the ``templates/mytemplate.pt`` template to +create a response based on the values in the dictionary. + +Note that the decorated function named ``my_view`` accepts a single argument +named ``request``. This is the standard call signature for a Pyramid +:term:`view callable`. + +Remember in our ``__init__.py`` when we executed the +:meth:`pyramid.config.Configurator.scan` method, e.g. ``config.scan()``? The +purpose of calling the scan method was to find and process this +``@view_config`` decorator in order to create a view configuration within our +application. Without being processed by ``scan``, the decorator effectively +does nothing. ``@view_config`` is inert without being detected via a +:term:`scan`. + Content Models with ``models.py`` --------------------------------- -In a SQLAlchemy-based application, a *model* object is an object -composed by querying the SQL database which backs an application. -SQLAlchemy is an "object relational mapper" (an ORM). The -``models.py`` file is where the ``alchemy`` scaffold -put the classes that implement our models. +In a SQLAlchemy-based application, a *model* object is an object composed by +querying the SQL database. SQLAlchemy is an "object relational mapper" (an +ORM). The ``models.py`` file is where the ``alchemy`` scaffold put the +classes that implement our models. Let's take a look. First, we need some imports to support later code. diff --git a/docs/tutorials/wiki2/definingmodels.rst b/docs/tutorials/wiki2/definingmodels.rst index 083ec0aa8..bc2c2de5f 100644 --- a/docs/tutorials/wiki2/definingmodels.rst +++ b/docs/tutorials/wiki2/definingmodels.rst @@ -80,6 +80,11 @@ something like this: :linenos: :language: python +Populating the Database +----------------------- + +XXX The ``populate_tutorial`` :term:`console script``. + Viewing the Application in a Browser ------------------------------------ diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst index 147f7f563..381e0a90f 100644 --- a/docs/tutorials/wiki2/installation.rst +++ b/docs/tutorials/wiki2/installation.rst @@ -40,13 +40,6 @@ Preparation, UNIX $ bin/easy_install pyramid -#. Use ``easy_install`` to install various packages from PyPI. - - .. code-block:: text - - $ bin/easy_install docutils nose coverage zope.sqlalchemy \ - SQLAlchemy pyramid_tm - Preparation, Windows -------------------- @@ -69,14 +62,6 @@ Preparation, Windows c:\pyramidtut> Scripts\easy_install pyramid -#. Use ``easy_install`` to install various packages from PyPI. - - .. code-block:: text - - c:\pyramidtut> Scripts\easy_install docutils \ - nose coverage zope.sqlalchemy SQLAlchemy pyramid_tm - - .. _sql_making_a_project: Making a Project @@ -108,6 +93,13 @@ On Windows: startup problems, try putting both the virtualenv and the project into directories that do not contain spaces in their paths. +Success executing this command will end with a line to the console something +like:: + + Please run the "populate_tutorial" script to set up the SQL + database before starting the application (e.g. + "$myvirtualenv/bin/populate_tutorial development.ini".) + Installing the Project in "Development Mode" ============================================ @@ -131,6 +123,11 @@ On Windows: c:\pyramidtut> cd tutorial c:\pyramidtut\tutorial> ..\Scripts\python setup.py develop +Success executing this command will end with a line to the console something +like:: + + Finished processing dependencies for tutorial==0.0 + .. _sql_running_tests: Running the Tests @@ -151,6 +148,14 @@ On Windows: c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q +For a successful test run, you should see output like this:: + + . + ---------------------------------------------------------------------- + Ran 1 test in 0.094s + + OK + Exposing Test Coverage Information ================================== @@ -191,8 +196,24 @@ On Windows: c:\pyramidtut\tutorial> ..\Scripts\nosetests --cover-package=tutorial ^ --cover-erase --with-coverage -Looks like our package's ``models`` module doesn't quite have 100% -test coverage. +If successful, you will see output something like this:: + + . + Name Stmts Miss Cover Missing + ------------------------------------------------ + tutorial 11 7 36% 9-15 + tutorial.models 17 0 100% + tutorial.scripts 0 0 100% + tutorial.tests 24 0 100% + tutorial.views 6 0 100% + ------------------------------------------------ + TOTAL 58 7 88% + ---------------------------------------------------------------------- + Ran 1 test in 0.459s + + OK + +Looks like our package doesn't quite have 100% test coverage. Starting the Application ======================== @@ -211,11 +232,96 @@ On Windows: c:\pyramidtut\tutorial> ..\Scripts\pserve development.ini --reload -Visit the Application in a Browser -================================== +If successful, you will see something like this on your console:: + + Starting subprocess with file monitor + Starting server in PID 8966. + Starting HTTP server on http://0.0.0.0:6543 + +This means the server is ready to accept requests. + +Populating the Database +======================= + +In a web browser, visit ``http://localhost:6543/``. + +You will see an error page with a title something like this:: + + sqlalchemy.exc.OperationalError + + OperationalError: (OperationalError) no such table: models ... + +Oh no! Something isn't working! + +This happens because we haven't populated the database with any table +information yet. We need to use the ``populate_tutorial`` :term:`console +script` to populate our database before we can see the page render correctly. + +Stop the running Pyramid application by pressing ``ctrl-C`` in the console. +Make sure you're still in the ``tutorial`` directory (the directory with a +``development.ini`` in it) and type the following command: + +On UNIX: + +.. code-block:: text + + $ ../bin/populate_tutorial development.ini + +On Windows: + +.. code-block:: text + + c:\pyramidtut\tutorial> ..\Scripts\populate_tutorial development.ini + +The output to your console should be something like this:: + + 2011-11-26 14:42:25,012 INFO [sqlalchemy.engine.base.Engine][MainThread] + PRAGMA table_info("models") + 2011-11-26 14:42:25,013 INFO [sqlalchemy.engine.base.Engine][MainThread] () + 2011-11-26 14:42:25,013 INFO [sqlalchemy.engine.base.Engine][MainThread] + CREATE TABLE models ( + id INTEGER NOT NULL, + name VARCHAR(255), + value INTEGER, + PRIMARY KEY (id), + UNIQUE (name) + ) + 2011-11-26 14:42:25,013 INFO [sqlalchemy.engine.base.Engine][MainThread] () + 2011-11-26 14:42:25,135 INFO [sqlalchemy.engine.base.Engine][MainThread] + COMMIT + 2011-11-26 14:42:25,137 INFO [sqlalchemy.engine.base.Engine][MainThread] + BEGIN (implicit) + 2011-11-26 14:42:25,138 INFO [sqlalchemy.engine.base.Engine][MainThread] + INSERT INTO models (name, value) VALUES (?, ?) + 2011-11-26 14:42:25,139 INFO [sqlalchemy.engine.base.Engine][MainThread] + (u'one', 1) + 2011-11-26 14:42:25,140 INFO [sqlalchemy.engine.base.Engine][MainThread] + COMMIT + +Success! You should now have a ``tutorial.db`` file in your current working +directory. This will be a SQLite database with a single table defined in it +(``models``). + +Starting the Application (Again) +================================ + +Start the application again. + +On UNIX: + +.. code-block:: text + + $ ../bin/pserve development.ini --reload + +On Windows: + +.. code-block:: text + + c:\pyramidtut\tutorial> ..\Scripts\pserve development.ini --reload -In a browser, visit ``http://localhost:6543/``. You will see the -generated application's default page. +At this point, when you visit ``http://localhost:6543/`` in your web browser, +you will no longer see an error; instead you will see the generated +application's default page. One thing you'll notice is the "debug toolbar" icon on right hand side of the page. You can read more about the purpose of the icon at diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py index b4038de3c..1f2cfd307 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py @@ -1,18 +1,15 @@ from pyramid.config import Configurator from sqlalchemy import engine_from_config -from tutorial.models import initialize_sql +from .models import DBSession def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ engine = engine_from_config(settings, 'sqlalchemy.') - initialize_sql(engine) + DBSession.configure(bind=engine) config = Configurator(settings=settings) - config.add_static_view('static', 'tutorial:static', cache_max_age=3600) + config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('home', '/') - config.add_view('tutorial.views.my_view', route_name='home', - renderer='templates/mytemplate.pt') + config.scan() return config.make_wsgi_app() - - diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py index e550e3257..631af9b6a 100644 --- a/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py +++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/views.py @@ -1,7 +1,9 @@ -from tutorial.models import DBSession -from tutorial.models import MyModel +from pyramid.view import view_config +from .models import DBSession +from .models import MyModel + +@view_config(route_name='home', renderer='templates/mytemplate.pt') def my_view(request): - dbsession = DBSession() - root = dbsession.query(MyModel).filter(MyModel.name==u'root').first() - return {'root':root, 'project':'tutorial'} + one = DBSession.query(MyModel).filter(MyModel.name==u'root').first() + return {'one':one, 'project':'tutorial'} diff --git a/pyramid/scaffolds/__init__.py b/pyramid/scaffolds/__init__.py index 20840a5d4..dfde9d855 100644 --- a/pyramid/scaffolds/__init__.py +++ b/pyramid/scaffolds/__init__.py @@ -37,4 +37,11 @@ class ZODBProjectTemplate(PyramidTemplate): class AlchemyProjectTemplate(PyramidTemplate): _template_dir = 'alchemy' summary = 'Pyramid SQLAlchemy project using url dispatch' - + def post(self, command, output_dir, vars): # pragma: no cover + val = PyramidTemplate.post(self, command, output_dir, vars) + self.out('') + self.out('Please run the "populate_%(package)s" script to set up the ' + 'SQL database before starting the application (e.g. ' + '"$myvirtualenv/bin/populate_%(package)s development.ini".)' + % vars) + return val diff --git a/pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl b/pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl index 7af326be9..dd11a64d7 100644 --- a/pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl +++ b/pyramid/scaffolds/alchemy/+package+/scripts/populate.py_tmpl @@ -1,5 +1,6 @@ import os import sys +import logging.config from paste.deploy.loadwsgi import appconfig from sqlalchemy import engine_from_config @@ -18,7 +19,9 @@ def main(argv=sys.argv): config_filename = argv[1] except IndexError: usage(argv) - settings = appconfig('config:' + os.path.abspath(config_filename)) + config_filename = os.path.abspath(config_filename) + logging.config.fileConfig(config_filename) + settings = appconfig('config:' + config_filename) engine = engine_from_config(settings, 'sqlalchemy.') DBSession.configure(bind=engine) Base.metadata.create_all(engine) -- cgit v1.2.3