From 3e2a692f4ee839d6c17850b51af9501a4d074a03 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Tue, 5 Jul 2016 11:25:00 -0600 Subject: Remove cachebust_match It was leftover from when this feature was removed in the 1.6 release cycle --- pyramid/static.py | 5 +---- pyramid/tests/test_static.py | 8 -------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/pyramid/static.py b/pyramid/static.py index 0965be95c..3abcd465c 100644 --- a/pyramid/static.py +++ b/pyramid/static.py @@ -83,7 +83,7 @@ class static_view(object): """ def __init__(self, root_dir, cache_max_age=3600, package_name=None, - use_subpath=False, index='index.html', cachebust_match=None): + use_subpath=False, index='index.html'): # package_name is for bw compat; it is preferred to pass in a # package-relative path as root_dir # (e.g. ``anotherpackage:foo/static``). @@ -96,15 +96,12 @@ class static_view(object): self.docroot = docroot self.norm_docroot = normcase(normpath(docroot)) self.index = index - self.cachebust_match = cachebust_match def __call__(self, context, request): if self.use_subpath: path_tuple = request.subpath else: path_tuple = traversal_path_info(request.environ['PATH_INFO']) - if self.cachebust_match: - path_tuple = self.cachebust_match(path_tuple) path = _secure_path(path_tuple) if path is None: diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py index 2ca86bc44..e264652dd 100644 --- a/pyramid/tests/test_static.py +++ b/pyramid/tests/test_static.py @@ -113,14 +113,6 @@ class Test_static_view_use_subpath_False(unittest.TestCase): response = inst(context, request) self.assertTrue(b'static' in response.body) - def test_cachebust_match(self): - inst = self._makeOne('pyramid.tests:fixtures/static') - inst.cachebust_match = lambda subpath: subpath[1:] - request = self._makeRequest({'PATH_INFO':'/foo/index.html'}) - context = DummyContext() - response = inst(context, request) - self.assertTrue(b'static' in response.body) - def test_resource_is_file_with_wsgi_file_wrapper(self): from pyramid.response import _BLOCK_SIZE inst = self._makeOne('pyramid.tests:fixtures/static') -- cgit v1.2.3 From 17fa5e3ce891064231707bf30413b38b89bd6d7f Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Fri, 30 Sep 2016 18:55:25 -0500 Subject: add a callback hook to set_default_csrf_options for disabling checks per-request fixes #2596 --- pyramid/config/security.py | 19 +++++++++++++++++-- pyramid/interfaces.py | 1 + pyramid/tests/test_config/test_security.py | 6 +++++- pyramid/tests/test_viewderivers.py | 28 ++++++++++++++++++++++++++++ pyramid/viewderivers.py | 7 ++++++- 5 files changed, 57 insertions(+), 4 deletions(-) diff --git a/pyramid/config/security.py b/pyramid/config/security.py index e387eade9..02732c042 100644 --- a/pyramid/config/security.py +++ b/pyramid/config/security.py @@ -169,6 +169,7 @@ class SecurityConfiguratorMixin(object): token='csrf_token', header='X-CSRF-Token', safe_methods=('GET', 'HEAD', 'OPTIONS', 'TRACE'), + callback=None, ): """ Set the default CSRF options used by subsequent view registrations. @@ -192,8 +193,20 @@ class SecurityConfiguratorMixin(object): never be automatically checked for CSRF tokens. Default: ``('GET', 'HEAD', 'OPTIONS', TRACE')``. + If ``callback`` is set, it must be a callable accepting ``(request)`` + and returning ``True`` if the request should be checked for a valid + CSRF token. This callback allows an application to support + alternate authentication methods that do not rely on cookies which + are not subject to CSRF attacks. For example, if a request is + authenticated using the ``Authorization`` header instead of a cookie, + this may return ``False`` for that request so that clients do not + need to send the ``X-CSRF-Token` header. The callback is only tested + for non-safe methods as defined by ``safe_methods``. + """ - options = DefaultCSRFOptions(require_csrf, token, header, safe_methods) + options = DefaultCSRFOptions( + require_csrf, token, header, safe_methods, callback, + ) def register(): self.registry.registerUtility(options, IDefaultCSRFOptions) intr = self.introspectable('default csrf view options', @@ -204,13 +217,15 @@ class SecurityConfiguratorMixin(object): intr['token'] = token intr['header'] = header intr['safe_methods'] = as_sorted_tuple(safe_methods) + intr['callback'] = callback self.action(IDefaultCSRFOptions, register, order=PHASE1_CONFIG, introspectables=(intr,)) @implementer(IDefaultCSRFOptions) class DefaultCSRFOptions(object): - def __init__(self, require_csrf, token, header, safe_methods): + def __init__(self, require_csrf, token, header, safe_methods, callback): self.require_csrf = require_csrf self.token = token self.header = header self.safe_methods = frozenset(safe_methods) + self.callback = callback diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index 114f802aa..c1ddea63f 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -925,6 +925,7 @@ class IDefaultCSRFOptions(Interface): token = Attribute('The key to be matched in the body of the request.') header = Attribute('The header to be matched with the CSRF token.') safe_methods = Attribute('A set of safe methods that skip CSRF checks.') + callback = Attribute('A callback to disable CSRF checks per-request.') class ISessionFactory(Interface): """ An interface representing a factory which accepts a request object and diff --git a/pyramid/tests/test_config/test_security.py b/pyramid/tests/test_config/test_security.py index e461bfd4a..5db8e21fc 100644 --- a/pyramid/tests/test_config/test_security.py +++ b/pyramid/tests/test_config/test_security.py @@ -108,14 +108,18 @@ class ConfiguratorSecurityMethodsTests(unittest.TestCase): self.assertEqual(result.header, 'X-CSRF-Token') self.assertEqual(list(sorted(result.safe_methods)), ['GET', 'HEAD', 'OPTIONS', 'TRACE']) + self.assertTrue(result.callback is None) def test_changing_set_default_csrf_options(self): from pyramid.interfaces import IDefaultCSRFOptions config = self._makeOne(autocommit=True) + def callback(request): return True config.set_default_csrf_options( - require_csrf=False, token='DUMMY', header=None, safe_methods=('PUT',)) + require_csrf=False, token='DUMMY', header=None, + safe_methods=('PUT',), callback=callback) result = config.registry.getUtility(IDefaultCSRFOptions) self.assertEqual(result.require_csrf, False) self.assertEqual(result.token, 'DUMMY') self.assertEqual(result.header, None) self.assertEqual(list(sorted(result.safe_methods)), ['PUT']) + self.assertTrue(result.callback is callback) diff --git a/pyramid/tests/test_viewderivers.py b/pyramid/tests/test_viewderivers.py index 676c6f66a..51d0bd367 100644 --- a/pyramid/tests/test_viewderivers.py +++ b/pyramid/tests/test_viewderivers.py @@ -1291,6 +1291,34 @@ class TestDeriveView(unittest.TestCase): view = self.config._derive_view(inner_view) self.assertRaises(BadCSRFToken, lambda: view(None, request)) + def test_csrf_view_enabled_via_callback(self): + def callback(request): + return True + from pyramid.exceptions import BadCSRFToken + def inner_view(request): pass + request = self._makeRequest() + request.scheme = "http" + request.method = 'POST' + request.session = DummySession({'csrf_token': 'foo'}) + self.config.set_default_csrf_options(require_csrf=True, callback=callback) + view = self.config._derive_view(inner_view) + self.assertRaises(BadCSRFToken, lambda: view(None, request)) + + def test_csrf_view_disabled_via_callback(self): + def callback(request): + return False + response = DummyResponse() + def inner_view(request): + return response + request = self._makeRequest() + request.scheme = "http" + request.method = 'POST' + request.session = DummySession({'csrf_token': 'foo'}) + self.config.set_default_csrf_options(require_csrf=True, callback=callback) + view = self.config._derive_view(inner_view) + result = view(None, request) + self.assertTrue(result is response) + def test_csrf_view_uses_custom_csrf_token(self): response = DummyResponse() def inner_view(request): diff --git a/pyramid/viewderivers.py b/pyramid/viewderivers.py index 513ddf022..4eb0ce704 100644 --- a/pyramid/viewderivers.py +++ b/pyramid/viewderivers.py @@ -481,11 +481,13 @@ def csrf_view(view, info): token = 'csrf_token' header = 'X-CSRF-Token' safe_methods = frozenset(["GET", "HEAD", "OPTIONS", "TRACE"]) + callback = None else: default_val = defaults.require_csrf token = defaults.token header = defaults.header safe_methods = defaults.safe_methods + callback = defaults.callback enabled = ( explicit_val is True or @@ -501,7 +503,10 @@ def csrf_view(view, info): wrapped_view = view if enabled: def csrf_view(context, request): - if request.method not in safe_methods: + if ( + request.method not in safe_methods and + (callback is None or callback(request)) + ): check_csrf_origin(request, raises=True) check_csrf_token(request, token, header, raises=True) return view(context, request) -- cgit v1.2.3 From 84b4962b2bb6ed065cb5bcc35d6302811f5637cd Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 2 Oct 2016 12:04:22 -0700 Subject: update HACKING.txt (somehow this got overlooked back on Apr 14, 2016) --- HACKING.txt | 221 +++++++++++++++++++++++++++++++----------------------------- 1 file changed, 115 insertions(+), 106 deletions(-) diff --git a/HACKING.txt b/HACKING.txt index 4b237b56c..73de79014 100644 --- a/HACKING.txt +++ b/HACKING.txt @@ -3,14 +3,16 @@ Hacking on Pyramid Here are some guidelines for hacking on Pyramid. + Using a Development Checkout ---------------------------- You'll have to create a development environment to hack on Pyramid, using a Pyramid checkout. You can either do this by hand, or if you have ``tox`` -installed (it's on PyPI), you can use tox to set up a working development +installed (it's on PyPI), you can use ``tox`` to set up a working development environment. Each installation method is described below. + By Hand +++++++ @@ -26,180 +28,186 @@ By Hand substituting your account username and specifying the destination as "hack-on-pyramid". - $ cd ~ - $ git clone git@github.com:USERNAME/pyramid.git hack-on-pyramid - $ cd hack-on-pyramid - # Configure remotes such that you can pull changes from the Pyramid - # repository into your local repository. - $ git remote add upstream https://github.com/Pylons/pyramid.git - # fetch and merge changes from upstream into master - $ git fetch upstream - $ git merge upstream/master + $ cd ~ + $ git clone git@github.com:USERNAME/pyramid.git hack-on-pyramid + $ cd hack-on-pyramid + # Configure remotes such that you can pull changes from the Pyramid + # repository into your local repository. + $ git remote add upstream https://github.com/Pylons/pyramid.git + # fetch and merge changes from upstream into master + $ git fetch upstream + $ git merge upstream/master Now your local repo is set up such that you will push changes to your GitHub repo, from which you can submit a pull request. -- Create a virtualenv in which to install Pyramid: - - $ cd ~/hack-on-pyramid - $ virtualenv -ppython2.7 env +- Create a virtual environment in which to install Pyramid: - Note that very old versions of virtualenv (virtualenv versions below, say, - 1.10 or thereabouts) require you to pass a ``--no-site-packages`` flag to - get a completely isolated environment. - - You can choose which Python version you want to use by passing a ``-p`` - flag to ``virtualenv``. For example, ``virtualenv -ppython2.7`` - chooses the Python 2.7 interpreter to be installed. + $ cd ~/hack-on-pyramid + $ python3 -m venv env From here on in within these instructions, the ``~/hack-on-pyramid/env`` virtual environment you created above will be referred to as ``$VENV``. To use the instructions in the steps that follow literally, use the ``export VENV=~/hack-on-pyramid/env`` command. -- Install ``setuptools-git`` into the virtualenv (for good measure, as we're - using git to do version control): +- Install ``setuptools-git`` into the virtual environment (for good measure, as + we're using git to do version control): - $ $VENV/bin/easy_install setuptools-git + $ $VENV/bin/pip install setuptools-git -- Install Pyramid from the checkout into the virtualenv using ``setup.py - dev``. ``setup.py dev`` is an alias for "setup.py develop" which also - installs testing requirements such as nose and coverage. Running - ``setup.py dev`` *must* be done while the current working directory is the - ``pyramid`` checkout directory: +- Install Pyramid from the checkout into the virtual environment, where the + current working directory is the ``pyramid`` checkout directory. We will + install Pyramid in editable (development) mode as well as its testing + requirements. - $ cd ~/hack-on-pyramid - $ $VENV/bin/python setup.py dev + $ cd ~/hack-on-pyramid + $ $VENV/bin/pip install -e ".[testing,docs]" - Optionally create a new Pyramid project using ``pcreate``: - $ cd $VENV - $ bin/pcreate -s starter starter + $ cd $VENV + $ bin/pcreate -s starter starter + +- ...and install the new project into the virtual environment: -- ...and install the new project (also using ``setup.py develop``) into the - virtualenv: + $ cd $VENV/starter + $ $VENV/bin/pip install -e . - $ cd $VENV/starter - $ $VENV/bin/python setup.py develop -Using Tox -+++++++++ +Using ``Tox`` ++++++++++++++ Alternatively, if you already have ``tox`` installed, there is an easier way to get going. - Create a new directory somewhere and ``cd`` to it: - $ mkdir ~/hack-on-pyramid - $ cd ~/hack-on-pyramid + $ mkdir ~/hack-on-pyramid + $ cd ~/hack-on-pyramid - Check out a read-only copy of the Pyramid source: - $ git clone git://github.com/Pylons/pyramid.git . + $ git clone git://github.com/Pylons/pyramid.git . - (alternately, create a writeable fork on GitHub and check that out). + Alternatively, create a writeable fork on GitHub and check that out. -Since Pyramid is a framework and not an application, it can be -convenient to work against a sample application, preferably in its own -virtualenv. A quick way to achieve this is to (ab-)use ``tox`` -(http://tox.readthedocs.org/en/latest/) with a custom configuration -file that's part of the checkout: +Since Pyramid is a framework and not an application, it can be convenient to +work against a sample application, preferably in its own virtual environment. A +quick way to achieve this is to use `tox +`_ with a custom configuration file +that is part of the checkout: - tox -c hacking-tox.ini + $ tox -c hacking-tox.ini -This will create a python-2.7 based virtualenv named ``env27`` (Pyramid's -``.gitconfig` ignores all top-level folders that start with ``env`` specifically -for this use case) and inside that a simple pyramid application named -``hacking`` that you can then fire up like so: +This will create a python-2.7 based virtual environment named ``env27`` +(Pyramid's ``.gitconfig` ignores all top-level folders that start with ``env`` +specifically in our use case), and inside that a simple pyramid application +named ``hacking`` that you can then fire up like so: + + $ cd env27/hacking + $ ../bin/pip install -e ".[testing,docs]" + $ ../bin/pserve development.ini - cd env27/hacking - ../bin/python setup.py develop - ../bin/pserve development.ini Adding Features --------------- In order to add a feature to Pyramid: -- The feature must be documented in both the API and narrative - documentation (in ``docs/``). +- The feature must be documented in both the API and narrative documentation + (in ``docs/``). - The feature must work fully on the following CPython versions: 2.7, 3.4, and 3.5 on both UNIX and Windows. - The feature must work on the latest version of PyPy. -- 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). -- The feature must not depend on any particular persistence layer - (filesystem, SQL, etc). +- The feature must not add unnecessary dependencies (where "unnecessary" is of + course subjective, but new dependencies should be discussed). -- The feature must not add unnecessary dependencies (where - "unnecessary" is of course subjective, but new dependencies should - be discussed). +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). -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 ------------ -- PEP8 compliance. Whitespace rules are relaxed: not necessary to put - 2 newlines between classes. But 79-column lines, in particular, are - mandatory. See - http://docs.pylonsproject.org/en/latest/community/codestyle.html for more +- PEP8 compliance. Whitespace rules are relaxed: not necessary to put two + newlines between classes. But 79-column lines, in particular, are mandatory. + See http://docs.pylonsproject.org/en/latest/community/codestyle.html for more information. - Please do not remove trailing whitespace. Configure your editor to reduce diff noise. See https://github.com/Pylons/pyramid/issues/788 for more. + Running Tests -------------- -- To run all tests for Pyramid on a single Python version, run ``nosetests`` - from your development virtualenv (See *Using a Development Checkout* above). +- To run all tests for Pyramid on a single Python version from your development + virtual environment (See *Using a Development Checkout* above), run + ``nosetests``: -- To run individual tests (i.e. during development) you can use a regular + $ $VENV/bin/nosetests + +- To run individual tests (i.e., during development) you can use a regular expression with the ``-t`` parameter courtesy of the `nose-selecttests - `_ plugin that's been - installed (along with nose itself) via ``python setup.py dev``. The - easiest usage is to simply provide the verbatim name of the test you're - working on. - -- To run the full set of Pyramid tests on all platforms, install ``tox`` - (http://codespeak.net/~hpk/tox/) into a system Python. The ``tox`` console - script will be installed into the scripts location for that Python. While + `_ plugin that was installed + (along with nose itself) via ``pip install -e .``. The easiest usage is to + simply provide the verbatim name of the test you're working on. + +- The ``tox.ini`` uses ``nose`` and ``coverage``. As such ``tox`` may be used + to run groups of tests or only a specific version of Python. For example, the + following command will run tests on Python 2.7 only without coverage: + + $ tox -e py27 + + This command will run tests on the latest versions of Python 2 and 3 with + coverage totaled for both versions. + + $ tox -e py2-cover,py3-cover,coverage + +- To run the full set of Pyramid tests on all platforms, install `tox + `_ into a system Python. The ``tox`` console + script will be installed into the scripts location for that Python. While ``cd``'ed to the Pyramid checkout root directory (it contains ``tox.ini``), - invoke the ``tox`` console script. This will read the ``tox.ini`` file and - execute the tests on multiple Python versions and platforms; while it runs, - it creates a virtualenv for each version/platform combination. For - example:: + invoke the ``tox`` console script. This will read the ``tox.ini`` file and + execute the tests on multiple Python versions and platforms. While it runs, + it creates a virtual environment for each version/platform combination. For + example: + + $ sudo /usr/bin/pip install tox + $ cd ~/hack-on-pyramid/ + $ /usr/bin/tox - $ sudo /usr/bin/easy_install tox - $ cd ~/hack-on-pyramid/ - $ /usr/bin/tox +- The tests can also be run using `pytest `_. This is + intended as a convenience for people who are more used to or fond of + ``pytest``. Run the tests like so: -- The tests can also be run using ``pytest`` (http://pytest.org/). This is - intended as a convenience for people who are more used or fond of ``pytest``. - Run the tests like so:: + $ $VENV/bin/pip install pytest + $ $VENV/bin/py.test --strict pyramid/ - $ $VENV/bin/easy_install pytest - $ $VENV/bin/py.test --strict pyramid/ + To run individual tests (i.e., during development), see "py.test usage - + Specifying tests / selecting tests": + http://pytest.org/latest/usage.html#specifying-tests-selecting-tests - Functional tests related to the "scaffolds" (starter, zodb, alchemy) which - create a virtualenv, install the scaffold package and its dependencies, start - a server, and hit a URL on the server can be run like so:: + create a virtual environment, install the scaffold package and its + dependencies, start a server, and hit a URL on the server, can be run like + so: - $ ./scaffoldtests.sh + $ ./scaffoldtests.sh - Alternately:: + Alternatively: + + $ tox -e{py27,py34,py35,pypy}-scaffolds, - $ tox -e{py27,py34,py35,pypy}-scaffolds, Test Coverage ------------- @@ -208,6 +216,7 @@ Test Coverage can test coverage via ``./coverage.sh`` (which itself just executes ``tox -epy2-cover,py3-cover,coverage``). + Documentation Coverage and Building HTML Documentation ------------------------------------------------------ @@ -217,13 +226,14 @@ changed to reflect the bug fix, ideally in the same commit that fixes the bug or adds the feature. To build and review docs, use the following steps. 1. In the main Pyramid checkout directory, run ``./builddocs.sh`` (which just - turns around and runs ``tox -e docs``):: + turns around and runs ``tox -e docs``): - $ ./builddocs.sh + $ ./builddocs.sh 2. Open the ``docs/_build/html/index.html`` file to see the resulting HTML rendering. + Change Log ---------- @@ -231,4 +241,3 @@ Change Log file in the prevailing style. Changelog entries should be long and descriptive, not cryptic. Other developers should be able to know what your changelog entry means. - -- cgit v1.2.3 From 24c63558d04dc9ad2a1ee2243a63ace0d355ffe5 Mon Sep 17 00:00:00 2001 From: goodwillcoding Date: Sun, 2 Oct 2016 18:12:32 -0700 Subject: Add --package-name option to pcreate. This solves the problem of scaffold creating an existing directory where the package created should not be named after the base folder. For example if I am in I run pcreate in ~/code/trypyramid.com and would like to create package called tpc this is currently impossible. This solves the issues by allowing me to specify the package name on the command line --- pyramid/scripts/pcreate.py | 17 ++++++++++++++--- pyramid/tests/test_scripts/test_pcreate.py | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/pyramid/scripts/pcreate.py b/pyramid/scripts/pcreate.py index f3121a915..7da501c49 100644 --- a/pyramid/scripts/pcreate.py +++ b/pyramid/scripts/pcreate.py @@ -45,6 +45,13 @@ class PCreateCommand(object): action='store_true', help=("A backwards compatibility alias for -l/--list. " "List all available scaffold names.")) + parser.add_option('--package-name', + dest='package_name', + action='store', + type='string', + help='Package name to use. Has to be a valid python ' + 'package name. (By default package name is derived ' + 'from output_directory base folder name)') parser.add_option('--simulate', dest='simulate', action='store_true', @@ -99,9 +106,13 @@ class PCreateCommand(object): def project_vars(self): output_dir = self.output_path project_name = os.path.basename(os.path.split(output_dir)[1]) - pkg_name = _bad_chars_re.sub( - '', project_name.lower().replace('-', '_')) - safe_name = pkg_resources.safe_name(project_name) + if self.options.package_name is None: + pkg_name = _bad_chars_re.sub( + '', project_name.lower().replace('-', '_')) + safe_name = pkg_resources.safe_name(project_name) + else: + pkg_name = self.options.package_name + safe_name = pkg_name egg_name = pkg_resources.to_filename(safe_name) # get pyramid package version diff --git a/pyramid/tests/test_scripts/test_pcreate.py b/pyramid/tests/test_scripts/test_pcreate.py index eaa7c1464..b7013bc73 100644 --- a/pyramid/tests/test_scripts/test_pcreate.py +++ b/pyramid/tests/test_scripts/test_pcreate.py @@ -80,6 +80,27 @@ class TestPCreateCommand(unittest.TestCase): {'project': 'Distro', 'egg': 'Distro', 'package': 'distro', 'pyramid_version': '0.1', 'pyramid_docs_branch':'0.1-branch'}) + def test_scaffold_with_package_name(self): + import os + cmd = self._makeOne('-s', 'dummy', '--package-name', 'dummy_package', + 'Distro') + scaffold = DummyScaffold('dummy') + cmd.scaffolds = [scaffold] + cmd.pyramid_dist = DummyDist("0.1") + result = cmd.run() + + self.assertEqual(result, 0) + self.assertEqual( + scaffold.output_dir, + os.path.normpath(os.path.join(os.getcwd(), 'Distro')) + ) + self.assertEqual( + scaffold.vars, + {'project': 'Distro', 'egg': 'dummy_package', + 'package': 'dummy_package', 'pyramid_version': '0.1', + 'pyramid_docs_branch':'0.1-branch'}) + + def test_scaffold_with_hyphen_in_project_name(self): import os cmd = self._makeOne('-s', 'dummy', 'Distro-') -- cgit v1.2.3 From 5aa7573db77281bd09981ae29d4700d839a864b8 Mon Sep 17 00:00:00 2001 From: goodwillcoding Date: Sun, 2 Oct 2016 18:33:20 -0700 Subject: update pcreate --package-name help with better verbiage which more explicitly states that package name is assumed to be safe --- pyramid/scripts/pcreate.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyramid/scripts/pcreate.py b/pyramid/scripts/pcreate.py index 7da501c49..5cae5cc5d 100644 --- a/pyramid/scripts/pcreate.py +++ b/pyramid/scripts/pcreate.py @@ -49,8 +49,9 @@ class PCreateCommand(object): dest='package_name', action='store', type='string', - help='Package name to use. Has to be a valid python ' - 'package name. (By default package name is derived ' + help='Package name to use. Named provided is assumed to ' + 'be a valid python package name and will not be ' + 'validated. (By default package name is derived ' 'from output_directory base folder name)') parser.add_option('--simulate', dest='simulate', -- cgit v1.2.3 From 44d0f6ae8d706b542f0bcc9f96b9fb9c502187b7 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sun, 2 Oct 2016 20:46:19 -0600 Subject: Add CHANGES.txt for #2783 --- CHANGES.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index d4afe5f7a..63ad37c81 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -25,6 +25,10 @@ Backward Incompatibilities Features -------- +- pcreate learned about --package-name to allow you to create a new project in + an existing folder with a different package name than the project name. See + https://github.com/Pylons/pyramid/pull/2783 + - The `_get_credentials` private method of `BasicAuthAuthenticationPolicy` has been extracted into standalone function ``extract_http_basic_credentials` in `pyramid.authentication` module, this function extracts HTTP Basic -- cgit v1.2.3 From a897b56fef11df0c1691cd21e990dcb6027cba1a Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sun, 2 Oct 2016 22:27:37 -0600 Subject: All of the tests assume that there is a Content-Type set But the application in this case doesn't actually send a Content-Type, and thus there is no charset, and thus the tests should be assuming the output is binary not text. Add a Content-Type to be sent back from the app. This is required to pass all tests on WebOb >1.7 where Request.get_response(app) will no longer add the default_content_type if a headerlist is passed in to Response.__init__, this allows the Response to match what was provided by the app. --- pyramid/tests/test_scripts/test_prequest.py | 43 +++++++++++++++++++---------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/pyramid/tests/test_scripts/test_prequest.py b/pyramid/tests/test_scripts/test_prequest.py index 95cec0518..45db0dbaf 100644 --- a/pyramid/tests/test_scripts/test_prequest.py +++ b/pyramid/tests/test_scripts/test_prequest.py @@ -34,7 +34,8 @@ class TestPRequestCommand(unittest.TestCase): self.assertEqual(self._out, ['You must provide at least two arguments']) def test_command_two_args(self): - command = self._makeOne(['', 'development.ini', '/']) + command = self._makeOne(['', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')]) command.run() self.assertEqual(self._path_info, '/') self.assertEqual(self._spec, 'development.ini') @@ -42,7 +43,8 @@ class TestPRequestCommand(unittest.TestCase): self.assertEqual(self._out, ['abc']) def test_command_path_doesnt_start_with_slash(self): - command = self._makeOne(['', 'development.ini', 'abc']) + command = self._makeOne(['', 'development.ini', 'abc'], + [('Content-Type', 'text/html; charset=UTF-8')]) command.run() self.assertEqual(self._path_info, '/abc') self.assertEqual(self._spec, 'development.ini') @@ -60,7 +62,8 @@ class TestPRequestCommand(unittest.TestCase): def test_command_has_good_header_var(self): command = self._makeOne( - ['', '--header=name:value','development.ini', '/']) + ['', '--header=name:value','development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')]) command.run() self.assertEqual(self._environ['HTTP_NAME'], 'value') self.assertEqual(self._path_info, '/') @@ -71,7 +74,8 @@ class TestPRequestCommand(unittest.TestCase): def test_command_w_basic_auth(self): command = self._makeOne( ['', '--login=user:password', - '--header=name:value','development.ini', '/']) + '--header=name:value','development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')]) command.run() self.assertEqual(self._environ['HTTP_NAME'], 'value') self.assertEqual(self._environ['HTTP_AUTHORIZATION'], @@ -83,7 +87,8 @@ class TestPRequestCommand(unittest.TestCase): def test_command_has_content_type_header_var(self): command = self._makeOne( - ['', '--header=content-type:app/foo','development.ini', '/']) + ['', '--header=content-type:app/foo','development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')]) command.run() self.assertEqual(self._environ['CONTENT_TYPE'], 'app/foo') self.assertEqual(self._path_info, '/') @@ -97,7 +102,9 @@ class TestPRequestCommand(unittest.TestCase): '--header=name:value', '--header=name2:value2', 'development.ini', - '/']) + '/'], + [('Content-Type', 'text/html; charset=UTF-8')] + ) command.run() self.assertEqual(self._environ['HTTP_NAME'], 'value') self.assertEqual(self._environ['HTTP_NAME2'], 'value2') @@ -107,7 +114,8 @@ class TestPRequestCommand(unittest.TestCase): self.assertEqual(self._out, ['abc']) def test_command_method_get(self): - command = self._makeOne(['', '--method=GET', 'development.ini', '/']) + command = self._makeOne(['', '--method=GET', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')]) command.run() self.assertEqual(self._environ['REQUEST_METHOD'], 'GET') self.assertEqual(self._path_info, '/') @@ -117,7 +125,8 @@ class TestPRequestCommand(unittest.TestCase): def test_command_method_post(self): from pyramid.compat import NativeIO - command = self._makeOne(['', '--method=POST', 'development.ini', '/']) + command = self._makeOne(['', '--method=POST', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')]) stdin = NativeIO() command.stdin = stdin command.run() @@ -131,7 +140,8 @@ class TestPRequestCommand(unittest.TestCase): def test_command_method_put(self): from pyramid.compat import NativeIO - command = self._makeOne(['', '--method=PUT', 'development.ini', '/']) + command = self._makeOne(['', '--method=PUT', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')]) stdin = NativeIO() command.stdin = stdin command.run() @@ -145,7 +155,8 @@ class TestPRequestCommand(unittest.TestCase): def test_command_method_patch(self): from pyramid.compat import NativeIO - command = self._makeOne(['', '--method=PATCH', 'development.ini', '/']) + command = self._makeOne(['', '--method=PATCH', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')]) stdin = NativeIO() command.stdin = stdin command.run() @@ -160,7 +171,8 @@ class TestPRequestCommand(unittest.TestCase): def test_command_method_propfind(self): from pyramid.compat import NativeIO command = self._makeOne(['', '--method=PROPFIND', 'development.ini', - '/']) + '/'], + [('Content-Type', 'text/html; charset=UTF-8')]) stdin = NativeIO() command.stdin = stdin command.run() @@ -173,7 +185,8 @@ class TestPRequestCommand(unittest.TestCase): def test_command_method_options(self): from pyramid.compat import NativeIO command = self._makeOne(['', '--method=OPTIONS', 'development.ini', - '/']) + '/'], + [('Content-Type', 'text/html; charset=UTF-8')]) stdin = NativeIO() command.stdin = stdin command.run() @@ -184,7 +197,8 @@ class TestPRequestCommand(unittest.TestCase): self.assertEqual(self._out, ['abc']) def test_command_with_query_string(self): - command = self._makeOne(['', 'development.ini', '/abc?a=1&b=2&c']) + command = self._makeOne(['', 'development.ini', '/abc?a=1&b=2&c'], + [('Content-Type', 'text/html; charset=UTF-8')]) command.run() self.assertEqual(self._environ['QUERY_STRING'], 'a=1&b=2&c') self.assertEqual(self._path_info, '/abc') @@ -194,7 +208,8 @@ class TestPRequestCommand(unittest.TestCase): def test_command_display_headers(self): command = self._makeOne( - ['', '--display-headers', 'development.ini', '/']) + ['', '--display-headers', 'development.ini', '/'], + [('Content-Type', 'text/html; charset=UTF-8')]) command.run() self.assertEqual(self._path_info, '/') self.assertEqual(self._spec, 'development.ini') -- cgit v1.2.3 From e39cc85a8573148889a82318fb7c727361835790 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 3 Oct 2016 01:50:06 -0700 Subject: per @mmerickel comment https://github.com/Pylons/pyramid/pull/2782#pullrequestreview-2456763 --- HACKING.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HACKING.txt b/HACKING.txt index 73de79014..cdfe5cf9c 100644 --- a/HACKING.txt +++ b/HACKING.txt @@ -90,7 +90,7 @@ way to get going. $ git clone git://github.com/Pylons/pyramid.git . - Alternatively, create a writeable fork on GitHub and check that out. + Alternatively, create a writeable fork on GitHub and clone it. Since Pyramid is a framework and not an application, it can be convenient to work against a sample application, preferably in its own virtual environment. A -- cgit v1.2.3 From 8edd76825a37708c74e6e7d6055b6cca73fd8f47 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 3 Oct 2016 02:03:39 -0700 Subject: Update HACKING.txt for running individual tests - nose-selecttests is optional, not pre-installed - Closes #2781 --- HACKING.txt | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/HACKING.txt b/HACKING.txt index cdfe5cf9c..953c386f9 100644 --- a/HACKING.txt +++ b/HACKING.txt @@ -148,7 +148,7 @@ Coding Style Running Tests --------------- +------------- - To run all tests for Pyramid on a single Python version from your development virtual environment (See *Using a Development Checkout* above), run @@ -156,11 +156,21 @@ Running Tests $ $VENV/bin/nosetests -- To run individual tests (i.e., during development) you can use a regular - expression with the ``-t`` parameter courtesy of the `nose-selecttests - `_ plugin that was installed - (along with nose itself) via ``pip install -e .``. The easiest usage is to - simply provide the verbatim name of the test you're working on. +- To run individual tests (i.e., during development), you can use ``nosetests`` + syntax as follows: + + # run a single test + $ $VENV/bin/nosetests pyramid.tests.test_module:ClassName.test_mytestname + + # run all tests in a class + $ $VENV/bin/nosetests pyramid.tests.test_module:ClassName + + Optionally you can install a nose plugin, `nose-selecttests + `_, and use a regular + expression with the ``-t`` parameter to run tests. + + # run a single test + $ $VENV/bin/nosetests -t test_mytestname - The ``tox.ini`` uses ``nose`` and ``coverage``. As such ``tox`` may be used to run groups of tests or only a specific version of Python. For example, the -- cgit v1.2.3 From f27435e8feecc78b29c58b1bd4bfd9f0b52e7e2c Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 3 Oct 2016 02:38:25 -0700 Subject: Update changes for #2782 --- CHANGES.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 63ad37c81..77129bdb1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -97,6 +97,9 @@ Deprecations Documentation Changes --------------------- +- Update HACKING.txt from stale branch that was never merged to master. + See https://github.com/Pylons/pyramid/pull/2782 + - Updated Windows installation instructions and related bits. See https://github.com/Pylons/pyramid/issues/2661 -- cgit v1.2.3 From 70876d093da872bc0246fcec3a2cc02cbf59807d Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 3 Oct 2016 02:47:08 -0700 Subject: Minor grammar fixes to pcreate --package-name help --- pyramid/scripts/pcreate.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyramid/scripts/pcreate.py b/pyramid/scripts/pcreate.py index 5cae5cc5d..a954d3be6 100644 --- a/pyramid/scripts/pcreate.py +++ b/pyramid/scripts/pcreate.py @@ -49,10 +49,10 @@ class PCreateCommand(object): dest='package_name', action='store', type='string', - help='Package name to use. Named provided is assumed to ' - 'be a valid python package name and will not be ' - 'validated. (By default package name is derived ' - 'from output_directory base folder name)') + help='Package name to use. The name provided is assumed ' + 'to be a valid Python package name, and will not ' + 'be validated. By default the package name is ' + 'derived from the value of output_directory.') parser.add_option('--simulate', dest='simulate', action='store_true', -- cgit v1.2.3 From bf22edc86d0f8dc9b072b974cfd5b4604688efa6 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Thu, 6 Oct 2016 11:43:22 -0700 Subject: Add pyramid_nacl_session to session factories - closes #2791 --- docs/narr/sessions.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst index a1319e45f..5b24201a9 100644 --- a/docs/narr/sessions.rst +++ b/docs/narr/sessions.rst @@ -157,6 +157,12 @@ The following session factories exist at the time of this writing. ======================= ======= ============================= Session Factory Backend Description ======================= ======= ============================= +pyramid_nacl_session_ PyNaCl_ Defines an encrypting, + pickle-based cookie + serializer, using PyNaCl to + generate the symmetric + encryption for the cookie + state. pyramid_redis_sessions_ Redis_ Server-side session library for Pyramid, using Redis for storage. @@ -165,6 +171,9 @@ pyramid_beaker_ Beaker_ Session factory for Pyramid sessioning system. ======================= ======= ============================= +.. _pyramid_nacl_session: https://pypi.python.org/pypi/pyramid_nacl_session +.. _PyNaCl: https://pynacl.readthedocs.io/en/latest/secret/ + .. _pyramid_redis_sessions: https://pypi.python.org/pypi/pyramid_redis_sessions .. _Redis: http://redis.io/ -- cgit v1.2.3 From 4c5e14f3a843f588e380c865ac362bcc017b69a6 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Thu, 6 Oct 2016 11:44:57 -0700 Subject: Update changes with pyramid_nacl_session --- CHANGES.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 77129bdb1..19708fde3 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -97,6 +97,9 @@ Deprecations Documentation Changes --------------------- +- Add pyramid_nacl_session to session factories. + See https://github.com/Pylons/pyramid/issues/2791 + - Update HACKING.txt from stale branch that was never merged to master. See https://github.com/Pylons/pyramid/pull/2782 -- cgit v1.2.3 From 814f19943abfb4643add4d2454325616acb080b3 Mon Sep 17 00:00:00 2001 From: Jonathan Ballet Date: Mon, 10 Oct 2016 10:42:29 +0200 Subject: doc: fix typo --- pyramid/renderers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyramid/renderers.py b/pyramid/renderers.py index 9b3f19510..47705d5d9 100644 --- a/pyramid/renderers.py +++ b/pyramid/renderers.py @@ -194,7 +194,7 @@ class JSON(object): Once this renderer is registered as above, you can use ``myjson`` as the ``renderer=`` parameter to ``@view_config`` or - :meth:`~pyramid.config.Configurator.add_view``: + :meth:`~pyramid.config.Configurator.add_view`: .. code-block:: python -- cgit v1.2.3 From b716bb4beed508ae8ccd859c82f5b8672dd165a6 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Fri, 14 Oct 2016 01:41:40 -0500 Subject: fix pserve to work with gevent workers - refactored code to remove ``pyramid.threadlocal`` from pserve's import tree. It was coming from the ``pyramid.paster`` module which imports ``pyramid.scripting`` which imports ``pyramid.threadlocal``. - dropped ``pyramid.scripts.common.logging_file_config`` as it appears to be unused and almost identical to ``setup_logging`` except without ``global_conf`` support. --- pyramid/paster.py | 27 +-------------------------- pyramid/scripts/common.py | 26 ++++++++++++++++---------- pyramid/scripts/prequest.py | 3 ++- pyramid/scripts/pserve.py | 3 +-- pyramid/scripts/pshell.py | 3 +-- pyramid/tests/test_scripts/test_common.py | 30 ------------------------------ 6 files changed, 21 insertions(+), 71 deletions(-) diff --git a/pyramid/paster.py b/pyramid/paster.py index 1b7afb5dc..5429a7860 100644 --- a/pyramid/paster.py +++ b/pyramid/paster.py @@ -5,9 +5,8 @@ from paste.deploy import ( appconfig, ) -from pyramid.compat import configparser -from logging.config import fileConfig from pyramid.scripting import prepare +from pyramid.scripts.common import setup_logging # noqa, api def get_app(config_uri, name=None, options=None, loadapp=loadapp): """ Return the WSGI application named ``name`` in the PasteDeploy @@ -52,30 +51,6 @@ def get_appsettings(config_uri, name=None, options=None, appconfig=appconfig): relative_to=here_dir, global_conf=options) -def setup_logging(config_uri, global_conf=None, - fileConfig=fileConfig, - configparser=configparser): - """ - Set up logging via :func:`logging.config.fileConfig` with the filename - specified via ``config_uri`` (a string in the form - ``filename#sectionname``). - - ConfigParser defaults are specified for the special ``__file__`` - and ``here`` variables, similar to PasteDeploy config loading. - Extra defaults can optionally be specified as a dict in ``global_conf``. - """ - path, _ = _getpathsec(config_uri, None) - parser = configparser.ConfigParser() - parser.read([path]) - if parser.has_section('loggers'): - config_file = os.path.abspath(path) - full_global_conf = dict( - __file__=config_file, - here=os.path.dirname(config_file)) - if global_conf: - full_global_conf.update(global_conf) - return fileConfig(config_file, full_global_conf) - def _getpathsec(config_uri, name): if '#' in config_uri: path, section = config_uri.split('#', 1) diff --git a/pyramid/scripts/common.py b/pyramid/scripts/common.py index cbc172e9b..fc141f6e2 100644 --- a/pyramid/scripts/common.py +++ b/pyramid/scripts/common.py @@ -17,20 +17,26 @@ def parse_vars(args): result[name] = value return result -def logging_file_config(config_file, fileConfig=fileConfig, - configparser=configparser): +def setup_logging(config_uri, global_conf=None, + fileConfig=fileConfig, + configparser=configparser): """ - Setup logging via the logging module's fileConfig function with the - specified ``config_file``, if applicable. + Set up logging via :func:`logging.config.fileConfig` with the filename + specified via ``config_uri`` (a string in the form + ``filename#sectionname``). ConfigParser defaults are specified for the special ``__file__`` and ``here`` variables, similar to PasteDeploy config loading. + Extra defaults can optionally be specified as a dict in ``global_conf``. """ + path = config_uri.split('#', 1)[0] parser = configparser.ConfigParser() - parser.read([config_file]) + parser.read([path]) if parser.has_section('loggers'): - config_file = os.path.abspath(config_file) - return fileConfig( - config_file, - dict(__file__=config_file, here=os.path.dirname(config_file)) - ) + config_file = os.path.abspath(path) + full_global_conf = dict( + __file__=config_file, + here=os.path.dirname(config_file)) + if global_conf: + full_global_conf.update(global_conf) + return fileConfig(config_file, full_global_conf) diff --git a/pyramid/scripts/prequest.py b/pyramid/scripts/prequest.py index e07f9d10e..14a132bdb 100644 --- a/pyramid/scripts/prequest.py +++ b/pyramid/scripts/prequest.py @@ -5,8 +5,9 @@ import textwrap from pyramid.compat import url_unquote from pyramid.request import Request -from pyramid.paster import get_app, setup_logging +from pyramid.paster import get_app from pyramid.scripts.common import parse_vars +from pyramid.scripts.common import setup_logging def main(argv=sys.argv, quiet=False): command = PRequestCommand(argv, quiet) diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py index ec7f31704..0d22c9f3f 100644 --- a/pyramid/scripts/pserve.py +++ b/pyramid/scripts/pserve.py @@ -30,9 +30,8 @@ from paste.deploy.loadwsgi import loadcontext, SERVER from pyramid.compat import PY2 from pyramid.compat import WIN -from pyramid.paster import setup_logging - from pyramid.scripts.common import parse_vars +from pyramid.scripts.common import setup_logging MAXFD = 1024 diff --git a/pyramid/scripts/pshell.py b/pyramid/scripts/pshell.py index 0a7cfbbe5..56b1a15fa 100644 --- a/pyramid/scripts/pshell.py +++ b/pyramid/scripts/pshell.py @@ -10,11 +10,10 @@ from pyramid.compat import exec_ from pyramid.util import DottedNameResolver from pyramid.paster import bootstrap -from pyramid.paster import setup_logging - from pyramid.settings import aslist from pyramid.scripts.common import parse_vars +from pyramid.scripts.common import setup_logging def main(argv=sys.argv, quiet=False): command = PShellCommand(argv, quiet) diff --git a/pyramid/tests/test_scripts/test_common.py b/pyramid/tests/test_scripts/test_common.py index 13ab0ae6a..60741db92 100644 --- a/pyramid/tests/test_scripts/test_common.py +++ b/pyramid/tests/test_scripts/test_common.py @@ -1,22 +1,5 @@ -import os import unittest -class Test_logging_file_config(unittest.TestCase): - def _callFUT(self, config_file): - from pyramid.scripts.common import logging_file_config - dummy_cp = DummyConfigParserModule - return logging_file_config(config_file, self.fileConfig, dummy_cp) - - def test_it(self): - config_file, dict = self._callFUT('/abc') - # use of os.path.abspath here is a sop to Windows - self.assertEqual(config_file, os.path.abspath('/abc')) - self.assertEqual(dict['__file__'], os.path.abspath('/abc')) - self.assertEqual(dict['here'], os.path.abspath('/')) - - def fileConfig(self, config_file, dict): - return config_file, dict - class TestParseVars(unittest.TestCase): def test_parse_vars_good(self): from pyramid.scripts.common import parse_vars @@ -28,16 +11,3 @@ class TestParseVars(unittest.TestCase): from pyramid.scripts.common import parse_vars vars = ['a'] self.assertRaises(ValueError, parse_vars, vars) - - -class DummyConfigParser(object): - def read(self, x): - pass - - def has_section(self, name): - return True - -class DummyConfigParserModule(object): - ConfigParser = DummyConfigParser - - -- cgit v1.2.3 From 325fc180ccf93716cdd1c959257a9864fcbee359 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 17 Oct 2016 22:06:45 -0500 Subject: changelog for #2797 --- CHANGES.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 19708fde3..434557f89 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -62,6 +62,11 @@ Features - the pipeline can be optimized at config-time. See https://github.com/Pylons/pyramid/pull/2660 +- ``pserve`` should now work with ``gevent`` and other workers that need + to monkeypatch the process, assuming the server and / or the app do so + as soon as possible before importing the rest of pyramid. + See https://github.com/Pylons/pyramid/pull/2797 + Bug Fixes --------- -- cgit v1.2.3 From f362c7f88ead25e110caff7423c268c6c5370ef3 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sat, 22 Oct 2016 16:46:19 -0400 Subject: remove trailing comma on tox command --- HACKING.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HACKING.txt b/HACKING.txt index 953c386f9..f240492e7 100644 --- a/HACKING.txt +++ b/HACKING.txt @@ -216,7 +216,7 @@ Running Tests Alternatively: - $ tox -e{py27,py34,py35,pypy}-scaffolds, + $ tox -e{py27,py34,py35,pypy}-scaffolds Test Coverage -- cgit v1.2.3 From 38a10a0e8ea7ad8165f5d8d264c409ff531ac56d Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sat, 22 Oct 2016 16:47:38 -0400 Subject: remove trailing comma on tox command --- scaffoldtests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scaffoldtests.sh b/scaffoldtests.sh index 84fd8e072..69d8ad561 100755 --- a/scaffoldtests.sh +++ b/scaffoldtests.sh @@ -1,3 +1,3 @@ #!/bin/bash -tox -e{py27,py33,py34,pypy}-scaffolds, +tox -e{py27,py33,py34,pypy}-scaffolds -- cgit v1.2.3 From c9ee30029221626e9f65c7cb9a3ccb850ec85ff3 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Tue, 1 Nov 2016 00:16:52 -0500 Subject: enable py27 on appveyor ci --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 1350507b2..e10dbc580 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,6 +2,8 @@ environment: matrix: - PYTHON: "C:\\Python35" TOXENV: "py35" + - PYTHON: "C:\\Python27" + TOXENV: "py27" install: - "%PYTHON%\\python.exe -m pip install tox" -- cgit v1.2.3 From 568ce319ef097f1e909398f9a5ec890e4de91601 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Tue, 1 Nov 2016 11:15:18 -0500 Subject: switch to 3.6-dev for py36 and update nightly to py37 on travis --- .travis.yml | 5 ++++- tox.ini | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b46f677a6..02e844196 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,10 +18,13 @@ matrix: env: TOXENV=docs - python: 3.5 env: TOXENV=pep8 - - python: nightly + - python: 3.6-dev env: TOXENV=py36 + - python: nightly + env: TOXENV=py37 allow_failures: - env: TOXENV=py36 + - env: TOXENV=py37 install: - travis_retry pip install tox diff --git a/tox.ini b/tox.ini index 8ceb142cb..884b78c68 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py27,py34,py35,py36,pypy, + py27,py34,py35,py36,py37,pypy, docs,pep8, {py2,py3}-cover,coverage, skip-missing-interpreters = True @@ -13,6 +13,7 @@ basepython = py34: python3.4 py35: python3.5 py36: python3.6 + py37: python3.7 pypy: pypy py2: python2.7 py3: python3.5 -- cgit v1.2.3 From ed06c68b7b622cf43dc09b0e475ad6c978ec3de4 Mon Sep 17 00:00:00 2001 From: David Glick Date: Wed, 9 Nov 2016 23:31:24 -0800 Subject: Avoid setting Content-Encoding header for static view responses. This was causing clients to decode the content of gzipped files when downloading them. We discussed this on IRC and @mmerickel pointed out that it may be a goal in some cases to serve gzipped precompiled assets (i.e. CSS/Javascript) with this header. But that's a new feature that would require thought about how to specify which files to serve that way. And it can already be implemented for a project using a tween. This just aims to fix the existing use case of serving files for download. --- CHANGES.txt | 5 +++++ pyramid/response.py | 25 +++++++++++++++---------- pyramid/static.py | 12 ++++++++++-- pyramid/tests/test_static.py | 4 ++-- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 434557f89..e6a015736 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -97,6 +97,11 @@ Bug Fixes from previous orders have executed. See https://github.com/Pylons/pyramid/pull/2757 +- Fix static view to avoid setting the ``Content-Encoding`` response header + to an encoding guessed using Python's ``mimetypes`` module. + This was causing clients to decode the content of gzipped files + when downloading them. + Deprecations ------------ diff --git a/pyramid/response.py b/pyramid/response.py index 892e5dfff..1d9daae7d 100644 --- a/pyramid/response.py +++ b/pyramid/response.py @@ -54,16 +54,7 @@ class FileResponse(Response): def __init__(self, path, request=None, cache_max_age=None, content_type=None, content_encoding=None): if content_type is None: - content_type, content_encoding = mimetypes.guess_type( - path, - strict=False - ) - if content_type is None: - content_type = 'application/octet-stream' - # str-ifying content_type is a workaround for a bug in Python 2.7.7 - # on Windows where mimetypes.guess_type returns unicode for the - # content_type. - content_type = str(content_type) + content_type, content_encoding = _guess_type(path) super(FileResponse, self).__init__( conditional_response=True, content_type=content_type, @@ -180,3 +171,17 @@ def _get_response_factory(registry): ) return response_factory + + +def _guess_type(path): + content_type, content_encoding = mimetypes.guess_type( + path, + strict=False + ) + if content_type is None: + content_type = 'application/octet-stream' + # str-ifying content_type is a workaround for a bug in Python 2.7.7 + # on Windows where mimetypes.guess_type returns unicode for the + # content_type. + content_type = str(content_type) + return content_type, content_encoding diff --git a/pyramid/static.py b/pyramid/static.py index 0965be95c..31e500e70 100644 --- a/pyramid/static.py +++ b/pyramid/static.py @@ -32,7 +32,12 @@ from pyramid.httpexceptions import ( ) from pyramid.path import caller_package -from pyramid.response import FileResponse + +from pyramid.response import ( + _guess_type, + FileResponse, +) + from pyramid.traversal import traversal_path_info slash = text_('/') @@ -134,7 +139,10 @@ class static_view(object): if not exists(filepath): raise HTTPNotFound(request.url) - return FileResponse(filepath, request, self.cache_max_age) + content_type, content_encoding = _guess_type(filepath) + return FileResponse( + filepath, request, self.cache_max_age, + content_type, content_encoding=None) def add_slash_redirect(self, request): url = request.path_url + '/' diff --git a/pyramid/tests/test_static.py b/pyramid/tests/test_static.py index 2ca86bc44..2b200d72b 100644 --- a/pyramid/tests/test_static.py +++ b/pyramid/tests/test_static.py @@ -186,14 +186,14 @@ class Test_static_view_use_subpath_False(unittest.TestCase): from pyramid.httpexceptions import HTTPNotFound self.assertRaises(HTTPNotFound, inst, context, request) - def test_resource_with_content_encoding(self): + def test_gz_resource_no_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') + self.assertEqual(response.content_encoding, None) response.app_iter.close() def test_resource_no_content_encoding(self): -- cgit v1.2.3 From 734f241a961b6b1e4b2a43c0c4b5a4d972b65b80 Mon Sep 17 00:00:00 2001 From: David Glick Date: Thu, 10 Nov 2016 08:42:39 -0800 Subject: add link to PR --- CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.txt b/CHANGES.txt index e6a015736..1d69471f1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -101,6 +101,7 @@ Bug Fixes to an encoding guessed using Python's ``mimetypes`` module. This was causing clients to decode the content of gzipped files when downloading them. + See https://github.com/Pylons/pyramid/pull/2810 Deprecations ------------ -- cgit v1.2.3 From ece7e52ec48c8a0b41ba05d793f3fd803a654e9a Mon Sep 17 00:00:00 2001 From: Moriyoshi Koizumi Date: Sat, 12 Nov 2016 16:42:32 +0900 Subject: Mark a few more characters as safe when escaping path components in route path generation, according to pyramid.url.PATH_SAFE --- pyramid/tests/test_urldispatch.py | 4 ++++ pyramid/urldispatch.py | 10 +++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/pyramid/tests/test_urldispatch.py b/pyramid/tests/test_urldispatch.py index 2d20b24c3..06f4ad793 100644 --- a/pyramid/tests/test_urldispatch.py +++ b/pyramid/tests/test_urldispatch.py @@ -485,11 +485,15 @@ class TestCompileRouteFunctional(unittest.TestCase): def test_generator_functional_newstyle(self): self.generates('/{x}', {'x':''}, '/') self.generates('/{x}', {'x':'a'}, '/a') + self.generates('/{x}', {'x':'a/b/c'}, '/a/b/c') + self.generates('/{x}', {'x':':@&+$,'}, '/:@&+$,') self.generates('zzz/{x}', {'x':'abc'}, '/zzz/abc') self.generates('zzz/{x}*traverse', {'x':'abc', 'traverse':''}, '/zzz/abc') self.generates('zzz/{x}*traverse', {'x':'abc', 'traverse':'/def/g'}, '/zzz/abc/def/g') + self.generates('zzz/{x}*traverse', {'x':':@&+$,', 'traverse':'/:@&+$,'}, + '/zzz/:@&+$,/:@&+$,') self.generates('/{x}', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8')}, '//La%20Pe%C3%B1a') self.generates('/{x}*y', {'x':text_(b'/La Pe\xc3\xb1a', 'utf-8'), diff --git a/pyramid/urldispatch.py b/pyramid/urldispatch.py index c88ad9590..0f7dd1490 100644 --- a/pyramid/urldispatch.py +++ b/pyramid/urldispatch.py @@ -207,6 +207,10 @@ def _compile_route(route): return d gen = ''.join(gen) + + def q(v): + return quote_path_segment(v, safe='/:@&+$,') + def generator(dict): newdict = {} for k, v in dict.items(): @@ -223,17 +227,17 @@ def _compile_route(route): # a stararg argument if is_nonstr_iter(v): v = '/'.join( - [quote_path_segment(x, safe='/') for x in v] + [q(x) for x in v] ) # native else: if v.__class__ not in string_types: v = str(v) - v = quote_path_segment(v, safe='/') + v = q(v) else: if v.__class__ not in string_types: v = str(v) # v may be bytes (py2) or native string (py3) - v = quote_path_segment(v, safe='/') + v = q(v) # at this point, the value will be a native string newdict[k] = v -- cgit v1.2.3 From 46bb00f493300f251cacb27d3ab6a221fbcb4473 Mon Sep 17 00:00:00 2001 From: Oladipo Odumosu Date: Mon, 14 Nov 2016 00:47:21 -0800 Subject: should be loaded from the virtualenv (cherry picked from commit 2552d88) --- docs/quick_tour.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst index 39b4cafb3..451830687 100644 --- a/docs/quick_tour.rst +++ b/docs/quick_tour.rst @@ -504,7 +504,7 @@ Pyramid's ``pcreate`` command can list the available scaffolds: .. code-block:: bash - $ pcreate --list + $ $VENV/bin/pcreate --list Available scaffolds: alchemy: Pyramid project using SQLAlchemy, SQLite, URL dispatch, and Jinja2 pyramid_jinja2_starter: Pyramid Jinja2 starter project @@ -517,7 +517,7 @@ that scaffold to make our project: .. code-block:: bash - $ pcreate --scaffold pyramid_jinja2_starter hello_world + $ $VENV/bin/pcreate --scaffold pyramid_jinja2_starter hello_world We next use the normal Python command to set up our package for development: -- cgit v1.2.3 From d84bc45c582cb8398608efebe21ccea957cc583b Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Mon, 14 Nov 2016 10:40:53 -0500 Subject: Fix spelling of 'skip_missing_interpreters' option. See: http://tox.readthedocs.io/en/latest/config.html#confval-skip_missing_interpreters=BOOL --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 884b78c68..251e43406 100644 --- a/tox.ini +++ b/tox.ini @@ -3,7 +3,7 @@ envlist = py27,py34,py35,py36,py37,pypy, docs,pep8, {py2,py3}-cover,coverage, -skip-missing-interpreters = True +skip_missing_interpreters = True [testenv] # Most of these are defaults but if you specify any you can't fall back -- cgit v1.2.3 From c67be02ed80bb4f304914380287998c20c65f717 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Mon, 14 Nov 2016 10:42:17 -0500 Subject: Silence pip spew. --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 251e43406..d1df0f031 100644 --- a/tox.ini +++ b/tox.ini @@ -19,7 +19,7 @@ basepython = py3: python3.5 commands = - pip install pyramid[testing] + pip install -q pyramid[testing] nosetests --with-xunit --xunit-file=nosetests-{envname}.xml {posargs:} [testenv:py27-scaffolds] @@ -72,7 +72,7 @@ commands = # combination of versions of coverage and nosexcover that i can find. [testenv:py2-cover] commands = - pip install pyramid[testing] + pip install -q pyramid[testing] coverage run --source=pyramid {envbindir}/nosetests coverage xml -o coverage-py2.xml setenv = @@ -80,7 +80,7 @@ setenv = [testenv:py3-cover] commands = - pip install pyramid[testing] + pip install -q pyramid[testing] coverage run --source=pyramid {envbindir}/nosetests coverage xml -o coverage-py3.xml setenv = -- cgit v1.2.3 From 3a21f7dfd350ea16808e7408c194328630471962 Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Mon, 14 Nov 2016 11:50:31 -0500 Subject: Annotate skipped flake8 rules. --- setup.cfg | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index b52d24bdb..85b6dd367 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,6 +14,48 @@ docs = develop easy_install pyramid[docs] universal = 1 [flake8] -ignore = E301,E302,E731,E261,E123,E121,E128,E129,E125,W291,E501,W293,E303,W391,E266,E231,E201,E202,E127,E262,E265 +ignore = + # E121: continuation line under-indented for hanging indent + E121, + # E123: closing bracket does not match indentation of opening bracket's line + E123, + # E125: continuation line with same indent as next logical line + E125, + # E127: continuation line over-indented for visual indent + E127, + # E128: continuation line under-indented for visual indent + E128, + # E129: visually indented line with same indent as next logical line + E129, + # E201: whitespace after ‘(‘ + E201, + # E202: whitespace before ‘)’ + E202, + # E231: missing whitespace after ‘,’, ‘;’, or ‘:’ + E231, + # E261: at least two spaces before inline comment + E261, + # E262: inline comment should start with ‘# ‘ + E262, + # E265: block comment should start with ‘# ‘ + E265, + # E266: too many leading ‘#’ for block comment + E266, + # E301: expected 1 blank line, found 0 + E301, + # E302: expected 2 blank lines, found 0 + E302, + # E303: too many blank lines (3) + E303, + # E501: line too long (82 > 79 characters) + E501, + # E731: do not assign a lambda expression, use a def + E731, + # W291: trailing whitespace + W291, + # W293: blank line contains whitespace + W293, + # W391: blank line at end of file + W391 exclude = pyramid/tests/,pyramid/compat.py,pyramid/resource.py show-source = True -- cgit v1.2.3 From 65e30325765c2816683a7432c80930becaa588bd Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Mon, 14 Nov 2016 11:53:05 -0500 Subject: Suppress E305 and E306 errors, which were newly failing. --- setup.cfg | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.cfg b/setup.cfg index 85b6dd367..df4013f6e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -47,6 +47,10 @@ ignore = E302, # E303: too many blank lines (3) E303, + # E305: expected 2 blank lines after class or function definition, found 1 + E305, + # E306: expected 1 blank line before a nested definition, found 0 + E306, # E501: line too long (82 > 79 characters) E501, # E731: do not assign a lambda expression, use a def -- cgit v1.2.3 From 3c96b5bc898574d37ee3fc071503cd6daa800c4b Mon Sep 17 00:00:00 2001 From: Tres Seaver Date: Mon, 14 Nov 2016 13:14:54 -0500 Subject: Strip allegedly-smart quotes in comments. They break installing on a LANG=C system. --- setup.cfg | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/setup.cfg b/setup.cfg index df4013f6e..9a241ddf5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,19 +27,19 @@ ignore = E128, # E129: visually indented line with same indent as next logical line E129, - # E201: whitespace after ‘(‘ + # E201: whitespace after '(' E201, - # E202: whitespace before ‘)’ + # E202: whitespace before ')' E202, - # E231: missing whitespace after ‘,’, ‘;’, or ‘:’ + # E231: missing whitespace after ',', ';', or ':' E231, # E261: at least two spaces before inline comment E261, - # E262: inline comment should start with ‘# ‘ + # E262: inline comment should start with '# ' E262, - # E265: block comment should start with ‘# ‘ + # E265: block comment should start with '# ' E265, - # E266: too many leading ‘#’ for block comment + # E266: too many leading '#' for block comment E266, # E301: expected 1 blank line, found 0 E301, -- cgit v1.2.3 From 9c8d43a7101c2977f6bf388758f14c7c7fadcf71 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 14 Nov 2016 22:34:28 -0600 Subject: turn on warnings by default for ``pyramid.deprecation.RemoveInVersion19Warning`` --- pyramid/config/factories.py | 9 +++- pyramid/config/routes.py | 4 +- pyramid/config/settings.py | 17 ------- pyramid/config/views.py | 9 +++- pyramid/deprecation.py | 9 ++++ pyramid/i18n.py | 18 +++++++ pyramid/interfaces.py | 12 +++-- pyramid/scripts/common.py | 12 ++++- pyramid/security.py | 19 +++++--- pyramid/session.py | 112 -------------------------------------------- pyramid/traversal.py | 10 ++-- 11 files changed, 83 insertions(+), 148 deletions(-) create mode 100644 pyramid/deprecation.py diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py index f0b6252ae..5c72ea73e 100644 --- a/pyramid/config/factories.py +++ b/pyramid/config/factories.py @@ -1,6 +1,10 @@ -from zope.deprecation import deprecated from zope.interface import implementer +from pyramid.deprecation import ( + RemoveInPyramid19Warning, + deprecated, +) + from pyramid.interfaces import ( IDefaultRootFactory, IRequestFactory, @@ -229,7 +233,8 @@ class FactoriesConfiguratorMixin(object): deprecated( set_request_property, 'set_request_propery() is deprecated as of Pyramid 1.5; use ' - 'add_request_method() with the property=True argument instead') + 'add_request_method() with the property=True argument instead', + RemoveInPyramid19Warning) @implementer(IRequestExtensions) diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py index 90d4d47d2..5ec8fe2c0 100644 --- a/pyramid/config/routes.py +++ b/pyramid/config/routes.py @@ -1,5 +1,7 @@ import warnings +from pyramid.deprecation import RemoveInPyramid19Warning + from pyramid.compat import urlparse from pyramid.interfaces import ( IRequest, @@ -285,7 +287,7 @@ class RoutesConfiguratorMixin(object): 'instead. See "Adding A Third Party View, Route, or ' 'Subscriber Predicate" in the "Hooks" chapter of the ' 'documentation for more information.'), - DeprecationWarning, + RemoveInPyramid19Warning, stacklevel=3 ) # these are route predicates; if they do not match, the next route diff --git a/pyramid/config/settings.py b/pyramid/config/settings.py index f9dbd752e..af2b359c7 100644 --- a/pyramid/config/settings.py +++ b/pyramid/config/settings.py @@ -1,5 +1,4 @@ import os -import warnings from zope.interface import implementer @@ -153,19 +152,3 @@ class Settings(dict): self.update(update) - def __getattr__(self, name): - try: - val = self[name] - # only deprecate on success; a probing getattr/hasattr should not - # print this warning - warnings.warn( - 'Obtaining settings via attributes of the settings dictionary ' - 'is deprecated as of Pyramid 1.2; use settings["foo"] instead ' - 'of settings.foo', - DeprecationWarning, - 2 - ) - return val - except KeyError: - raise AttributeError(name) - diff --git a/pyramid/config/views.py b/pyramid/config/views.py index acdc00704..104254217 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -11,6 +11,11 @@ from zope.interface import ( ) from zope.interface.interfaces import IInterface +from pyramid.deprecation import ( + RemoveInPyramid19Warning, + RemoveInPyramid110Warning, +) + from pyramid.interfaces import ( IExceptionViewClassifier, IException, @@ -724,7 +729,7 @@ class ViewsConfiguratorMixin(object): 'See "Adding A Third Party View, Route, or Subscriber ' 'Predicate" in the "Hooks" chapter of the documentation ' 'for more information.'), - DeprecationWarning, + RemoveInPyramid19Warning, stacklevel=4, ) @@ -735,7 +740,7 @@ class ViewsConfiguratorMixin(object): 'instead or see "Checking CSRF Tokens Automatically" in the ' '"Sessions" chapter of the documentation for more ' 'information.'), - DeprecationWarning, + RemoveInPyramid110Warning, stacklevel=4, ) diff --git a/pyramid/deprecation.py b/pyramid/deprecation.py new file mode 100644 index 000000000..dbb02720f --- /dev/null +++ b/pyramid/deprecation.py @@ -0,0 +1,9 @@ +from zope.deprecation import deprecated # noqa, internal api + +class RemoveInPyramid19Warning(DeprecationWarning): + pass + +class RemoveInPyramid110Warning(DeprecationWarning): + pass + +RemovedInNextVersionWarning = RemoveInPyramid19Warning diff --git a/pyramid/i18n.py b/pyramid/i18n.py index 79209d342..560be83d7 100644 --- a/pyramid/i18n.py +++ b/pyramid/i18n.py @@ -10,6 +10,10 @@ from translationstring import ( from pyramid.compat import PY2 from pyramid.decorator import reify +from pyramid.deprecation import ( + RemoveInPyramid19Warning, + deprecated, +) from pyramid.interfaces import ( ILocalizer, @@ -166,6 +170,13 @@ def get_locale_name(request): """ return request.locale_name +deprecated( + 'get_locale_name', + 'As of Pyramid 1.5 the "pyramid.i18n.get_locale_name" function is ' + 'scheduled to be removed. Use "request.locale_name" instead.', + RemoveInPyramid19Warning, +) + def make_localizer(current_locale_name, translation_directories): """ Create a :class:`pyramid.i18n.Localizer` object corresponding to the provided locale name from the @@ -218,6 +229,13 @@ def get_localizer(request): """ return request.localizer +deprecated( + 'get_localizer', + 'As of Pyramid 1.5 the "pyramid.i18n.get_localizer" method is scheduled ' + 'to be removed. Use "request.locale_name" instead.', + RemoveInPyramid19Warning, +) + class Translations(gettext.GNUTranslations, object): """An extended translation catalog class (ripped off from Babel) """ diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index c1ddea63f..33cf5408d 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -1,11 +1,13 @@ -from zope.deprecation import deprecated - from zope.interface import ( Attribute, Interface, ) from pyramid.compat import PY2 +from pyramid.deprecation import ( + RemoveInPyramid19Warning, + deprecated, +) # public API interfaces @@ -424,7 +426,8 @@ deprecated( 'ITemplateRenderer', 'As of Pyramid 1.5 the, "pyramid.interfaces.ITemplateRenderer" interface ' 'is scheduled to be removed. It was used by the Mako and Chameleon ' - 'renderers which have been split into their own packages.' + 'renderers which have been split into their own packages.', + RemoveInPyramid19Warning, ) class IViewMapper(Interface): @@ -848,7 +851,8 @@ deprecated( 'scheduled to be removed. Use the ' '"pyramid.config.Configurator.add_resource_url_adapter" method to register ' 'a class that implements "pyramid.interfaces.IResourceURL" instead. ' - 'See the "What\'s new In Pyramid 1.3" document for more details.' + 'See the "What\'s new In Pyramid 1.3" document for more details.', + RemoveInPyramid19Warning, ) class IPEP302Loader(Interface): diff --git a/pyramid/scripts/common.py b/pyramid/scripts/common.py index fc141f6e2..f219e6d9a 100644 --- a/pyramid/scripts/common.py +++ b/pyramid/scripts/common.py @@ -1,6 +1,11 @@ import os -from pyramid.compat import configparser +import logging from logging.config import fileConfig +import sys +import warnings + +from pyramid.compat import configparser +from pyramid.deprecation import RemoveInNextVersionWarning def parse_vars(args): """ @@ -29,6 +34,11 @@ def setup_logging(config_uri, global_conf=None, and ``here`` variables, similar to PasteDeploy config loading. Extra defaults can optionally be specified as a dict in ``global_conf``. """ + logging.captureWarnings(True) + + if not sys.warnoptions: + warnings.simplefilter('default', RemoveInNextVersionWarning) + path = config_uri.split('#', 1)[0] parser = configparser.ConfigParser() parser.read([path]) diff --git a/pyramid/security.py b/pyramid/security.py index 82e6b73a9..f3ad9ef65 100644 --- a/pyramid/security.py +++ b/pyramid/security.py @@ -1,6 +1,9 @@ -from zope.deprecation import deprecated from zope.interface import providedBy +from pyramid.deprecation import ( + RemoveInPyramid19Warning, + deprecated, +) from pyramid.interfaces import ( IAuthenticationPolicy, IAuthorizationPolicy, @@ -62,7 +65,8 @@ deprecated( 'has_permission', 'As of Pyramid 1.5 the "pyramid.security.has_permission" API is now ' 'deprecated. It will be removed in Pyramid 1.8. Use the ' - '"has_permission" method of the Pyramid request instead.' + '"has_permission" method of the Pyramid request instead.', + RemoveInPyramid19Warning, ) @@ -80,7 +84,8 @@ deprecated( 'authenticated_userid', 'As of Pyramid 1.5 the "pyramid.security.authenticated_userid" API is now ' 'deprecated. It will be removed in Pyramid 1.8. Use the ' - '"authenticated_userid" attribute of the Pyramid request instead.' + '"authenticated_userid" attribute of the Pyramid request instead.', + RemoveInPyramid19Warning, ) def unauthenticated_userid(request): @@ -97,7 +102,8 @@ deprecated( 'unauthenticated_userid', 'As of Pyramid 1.5 the "pyramid.security.unauthenticated_userid" API is ' 'now deprecated. It will be removed in Pyramid 1.8. Use the ' - '"unauthenticated_userid" attribute of the Pyramid request instead.' + '"unauthenticated_userid" attribute of the Pyramid request instead.', + RemoveInPyramid19Warning, ) def effective_principals(request): @@ -114,7 +120,8 @@ deprecated( 'effective_principals', 'As of Pyramid 1.5 the "pyramid.security.effective_principals" API is ' 'now deprecated. It will be removed in Pyramid 1.8. Use the ' - '"effective_principals" attribute of the Pyramid request instead.' + '"effective_principals" attribute of the Pyramid request instead.', + RemoveInPyramid19Warning, ) def remember(request, userid=_marker, **kw): @@ -156,7 +163,7 @@ def remember(request, userid=_marker, **kw): 'principal', 'The "principal" argument was deprecated in Pyramid 1.6. ' 'It will be removed in Pyramid 1.9. Use the "userid" ' - 'argument instead.') + 'argument instead.', RemoveInPyramid19Warning) userid = principal policy = _get_authentication_policy(request) if policy is None: diff --git a/pyramid/session.py b/pyramid/session.py index a3cbe5172..d00902f2a 100644 --- a/pyramid/session.py +++ b/pyramid/session.py @@ -5,7 +5,6 @@ import hmac import os import time -from zope.deprecation import deprecated from zope.interface import implementer from webob.cookies import SignedSerializer @@ -519,117 +518,6 @@ def BaseCookieSessionFactory( return CookieSession - -def UnencryptedCookieSessionFactoryConfig( - secret, - timeout=1200, - cookie_name='session', - cookie_max_age=None, - cookie_path='/', - cookie_domain=None, - cookie_secure=False, - cookie_httponly=False, - cookie_on_exception=True, - signed_serialize=signed_serialize, - signed_deserialize=signed_deserialize, - ): - """ - .. deprecated:: 1.5 - Use :func:`pyramid.session.SignedCookieSessionFactory` instead. - Caveat: Cookies generated using ``SignedCookieSessionFactory`` are not - compatible with cookies generated using - ``UnencryptedCookieSessionFactory``, so existing user session data - will be destroyed if you switch to it. - - Configure a :term:`session factory` which will provide unencrypted - (but signed) cookie-based sessions. The return value of this - function is a :term:`session factory`, which may be provided as - the ``session_factory`` argument of a - :class:`pyramid.config.Configurator` constructor, or used - as the ``session_factory`` argument of the - :meth:`pyramid.config.Configurator.set_session_factory` - method. - - The session factory returned by this function will create sessions - which are limited to storing fewer than 4000 bytes of data (as the - payload must fit into a single cookie). - - Parameters: - - ``secret`` - A string which is used to sign the cookie. - - ``timeout`` - A number of seconds of inactivity before a session times out. - - ``cookie_name`` - The name of the cookie used for sessioning. - - ``cookie_max_age`` - The maximum age of the cookie used for sessioning (in seconds). - Default: ``None`` (browser scope). - - ``cookie_path`` - The path used for the session cookie. - - ``cookie_domain`` - The domain used for the session cookie. Default: ``None`` (no domain). - - ``cookie_secure`` - The 'secure' flag of the session cookie. - - ``cookie_httponly`` - The 'httpOnly' flag of the session cookie. - - ``cookie_on_exception`` - If ``True``, set a session cookie even if an exception occurs - while rendering a view. - - ``signed_serialize`` - A callable which takes more or less arbitrary Python data structure and - a secret and returns a signed serialization in bytes. - Default: ``signed_serialize`` (using pickle). - - ``signed_deserialize`` - A callable which takes a signed and serialized data structure in bytes - and a secret and returns the original data structure if the signature - is valid. Default: ``signed_deserialize`` (using pickle). - """ - - class SerializerWrapper(object): - def __init__(self, secret): - self.secret = secret - - def loads(self, bstruct): - return signed_deserialize(bstruct, secret) - - def dumps(self, appstruct): - return signed_serialize(appstruct, secret) - - serializer = SerializerWrapper(secret) - - return BaseCookieSessionFactory( - serializer, - cookie_name=cookie_name, - max_age=cookie_max_age, - path=cookie_path, - domain=cookie_domain, - secure=cookie_secure, - httponly=cookie_httponly, - timeout=timeout, - reissue_time=0, # to keep session.accessed == session.renewed - set_on_exception=cookie_on_exception, - ) - -deprecated( - 'UnencryptedCookieSessionFactoryConfig', - 'The UnencryptedCookieSessionFactoryConfig callable is deprecated as of ' - 'Pyramid 1.5. Use ``pyramid.session.SignedCookieSessionFactory`` instead.' - ' Caveat: Cookies generated using SignedCookieSessionFactory are not ' - 'compatible with cookies generated using UnencryptedCookieSessionFactory, ' - 'so existing user session data will be destroyed if you switch to it.' - ) - def SignedCookieSessionFactory( secret, cookie_name='session', diff --git a/pyramid/traversal.py b/pyramid/traversal.py index 963a76bb5..5d49dce1d 100644 --- a/pyramid/traversal.py +++ b/pyramid/traversal.py @@ -1,12 +1,15 @@ import warnings -from zope.deprecation import deprecated - from zope.interface import implementer from zope.interface.interfaces import IInterface from repoze.lru import lru_cache +from pyramid.deprecation import ( + RemoveInPyramid19Warning, + deprecated, +) + from pyramid.interfaces import ( IResourceURL, IRequestFactory, @@ -811,7 +814,8 @@ deprecated( 'scheduled to be removed. Use the ' '"pyramid.config.Configurator.add_resource_url_adapter" method to register ' 'a class that implements "pyramid.interfaces.IResourceURL" instead. ' - 'See the "What\'s new In Pyramid 1.3" document for a further description.' + 'See the "What\'s new In Pyramid 1.3" document for a further description.', + RemoveInPyramid19Warning, ) @lru_cache(1000) -- cgit v1.2.3 From b13b1c8de94a55ac99865c06598743899806c689 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Tue, 15 Nov 2016 19:50:24 -0600 Subject: fix docstring on check_csrf_token --- pyramid/session.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyramid/session.py b/pyramid/session.py index d00902f2a..513628ce7 100644 --- a/pyramid/session.py +++ b/pyramid/session.py @@ -214,9 +214,9 @@ def check_csrf_token(request, supplied by ``request.session.get_csrf_token()``, and ``raises`` is ``True``, this function will raise an :exc:`pyramid.exceptions.BadCSRFToken` exception. - If the check does succeed and ``raises`` is ``False``, this - function will return ``False``. If the CSRF check is successful, this - function will return ``True`` unconditionally. + If the values differ and ``raises`` is ``False``, this function will + return ``False``. If the CSRF check is successful, this function will + return ``True`` unconditionally. Note that using this function requires that a :term:`session factory` is configured. -- cgit v1.2.3 From c151adb1ee83eb34005a9883e748831e2b79de21 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Tue, 15 Nov 2016 20:17:22 -0600 Subject: Revert "turn on warnings by default for ``pyramid.deprecation.RemoveInVersion19Warning``" This reverts commit 9c8d43a7101c2977f6bf388758f14c7c7fadcf71. --- pyramid/config/factories.py | 9 +--- pyramid/config/routes.py | 4 +- pyramid/config/settings.py | 17 +++++++ pyramid/config/views.py | 9 +--- pyramid/deprecation.py | 9 ---- pyramid/i18n.py | 18 ------- pyramid/interfaces.py | 12 ++--- pyramid/scripts/common.py | 12 +---- pyramid/security.py | 19 +++----- pyramid/session.py | 112 ++++++++++++++++++++++++++++++++++++++++++++ pyramid/traversal.py | 10 ++-- 11 files changed, 148 insertions(+), 83 deletions(-) delete mode 100644 pyramid/deprecation.py diff --git a/pyramid/config/factories.py b/pyramid/config/factories.py index 5c72ea73e..f0b6252ae 100644 --- a/pyramid/config/factories.py +++ b/pyramid/config/factories.py @@ -1,10 +1,6 @@ +from zope.deprecation import deprecated from zope.interface import implementer -from pyramid.deprecation import ( - RemoveInPyramid19Warning, - deprecated, -) - from pyramid.interfaces import ( IDefaultRootFactory, IRequestFactory, @@ -233,8 +229,7 @@ class FactoriesConfiguratorMixin(object): deprecated( set_request_property, 'set_request_propery() is deprecated as of Pyramid 1.5; use ' - 'add_request_method() with the property=True argument instead', - RemoveInPyramid19Warning) + 'add_request_method() with the property=True argument instead') @implementer(IRequestExtensions) diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py index 5ec8fe2c0..90d4d47d2 100644 --- a/pyramid/config/routes.py +++ b/pyramid/config/routes.py @@ -1,7 +1,5 @@ import warnings -from pyramid.deprecation import RemoveInPyramid19Warning - from pyramid.compat import urlparse from pyramid.interfaces import ( IRequest, @@ -287,7 +285,7 @@ class RoutesConfiguratorMixin(object): 'instead. See "Adding A Third Party View, Route, or ' 'Subscriber Predicate" in the "Hooks" chapter of the ' 'documentation for more information.'), - RemoveInPyramid19Warning, + DeprecationWarning, stacklevel=3 ) # these are route predicates; if they do not match, the next route diff --git a/pyramid/config/settings.py b/pyramid/config/settings.py index af2b359c7..f9dbd752e 100644 --- a/pyramid/config/settings.py +++ b/pyramid/config/settings.py @@ -1,4 +1,5 @@ import os +import warnings from zope.interface import implementer @@ -152,3 +153,19 @@ class Settings(dict): self.update(update) + def __getattr__(self, name): + try: + val = self[name] + # only deprecate on success; a probing getattr/hasattr should not + # print this warning + warnings.warn( + 'Obtaining settings via attributes of the settings dictionary ' + 'is deprecated as of Pyramid 1.2; use settings["foo"] instead ' + 'of settings.foo', + DeprecationWarning, + 2 + ) + return val + except KeyError: + raise AttributeError(name) + diff --git a/pyramid/config/views.py b/pyramid/config/views.py index 104254217..acdc00704 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -11,11 +11,6 @@ from zope.interface import ( ) from zope.interface.interfaces import IInterface -from pyramid.deprecation import ( - RemoveInPyramid19Warning, - RemoveInPyramid110Warning, -) - from pyramid.interfaces import ( IExceptionViewClassifier, IException, @@ -729,7 +724,7 @@ class ViewsConfiguratorMixin(object): 'See "Adding A Third Party View, Route, or Subscriber ' 'Predicate" in the "Hooks" chapter of the documentation ' 'for more information.'), - RemoveInPyramid19Warning, + DeprecationWarning, stacklevel=4, ) @@ -740,7 +735,7 @@ class ViewsConfiguratorMixin(object): 'instead or see "Checking CSRF Tokens Automatically" in the ' '"Sessions" chapter of the documentation for more ' 'information.'), - RemoveInPyramid110Warning, + DeprecationWarning, stacklevel=4, ) diff --git a/pyramid/deprecation.py b/pyramid/deprecation.py deleted file mode 100644 index dbb02720f..000000000 --- a/pyramid/deprecation.py +++ /dev/null @@ -1,9 +0,0 @@ -from zope.deprecation import deprecated # noqa, internal api - -class RemoveInPyramid19Warning(DeprecationWarning): - pass - -class RemoveInPyramid110Warning(DeprecationWarning): - pass - -RemovedInNextVersionWarning = RemoveInPyramid19Warning diff --git a/pyramid/i18n.py b/pyramid/i18n.py index 560be83d7..79209d342 100644 --- a/pyramid/i18n.py +++ b/pyramid/i18n.py @@ -10,10 +10,6 @@ from translationstring import ( from pyramid.compat import PY2 from pyramid.decorator import reify -from pyramid.deprecation import ( - RemoveInPyramid19Warning, - deprecated, -) from pyramid.interfaces import ( ILocalizer, @@ -170,13 +166,6 @@ def get_locale_name(request): """ return request.locale_name -deprecated( - 'get_locale_name', - 'As of Pyramid 1.5 the "pyramid.i18n.get_locale_name" function is ' - 'scheduled to be removed. Use "request.locale_name" instead.', - RemoveInPyramid19Warning, -) - def make_localizer(current_locale_name, translation_directories): """ Create a :class:`pyramid.i18n.Localizer` object corresponding to the provided locale name from the @@ -229,13 +218,6 @@ def get_localizer(request): """ return request.localizer -deprecated( - 'get_localizer', - 'As of Pyramid 1.5 the "pyramid.i18n.get_localizer" method is scheduled ' - 'to be removed. Use "request.locale_name" instead.', - RemoveInPyramid19Warning, -) - class Translations(gettext.GNUTranslations, object): """An extended translation catalog class (ripped off from Babel) """ diff --git a/pyramid/interfaces.py b/pyramid/interfaces.py index 33cf5408d..c1ddea63f 100644 --- a/pyramid/interfaces.py +++ b/pyramid/interfaces.py @@ -1,13 +1,11 @@ +from zope.deprecation import deprecated + from zope.interface import ( Attribute, Interface, ) from pyramid.compat import PY2 -from pyramid.deprecation import ( - RemoveInPyramid19Warning, - deprecated, -) # public API interfaces @@ -426,8 +424,7 @@ deprecated( 'ITemplateRenderer', 'As of Pyramid 1.5 the, "pyramid.interfaces.ITemplateRenderer" interface ' 'is scheduled to be removed. It was used by the Mako and Chameleon ' - 'renderers which have been split into their own packages.', - RemoveInPyramid19Warning, + 'renderers which have been split into their own packages.' ) class IViewMapper(Interface): @@ -851,8 +848,7 @@ deprecated( 'scheduled to be removed. Use the ' '"pyramid.config.Configurator.add_resource_url_adapter" method to register ' 'a class that implements "pyramid.interfaces.IResourceURL" instead. ' - 'See the "What\'s new In Pyramid 1.3" document for more details.', - RemoveInPyramid19Warning, + 'See the "What\'s new In Pyramid 1.3" document for more details.' ) class IPEP302Loader(Interface): diff --git a/pyramid/scripts/common.py b/pyramid/scripts/common.py index f219e6d9a..fc141f6e2 100644 --- a/pyramid/scripts/common.py +++ b/pyramid/scripts/common.py @@ -1,11 +1,6 @@ import os -import logging -from logging.config import fileConfig -import sys -import warnings - from pyramid.compat import configparser -from pyramid.deprecation import RemoveInNextVersionWarning +from logging.config import fileConfig def parse_vars(args): """ @@ -34,11 +29,6 @@ def setup_logging(config_uri, global_conf=None, and ``here`` variables, similar to PasteDeploy config loading. Extra defaults can optionally be specified as a dict in ``global_conf``. """ - logging.captureWarnings(True) - - if not sys.warnoptions: - warnings.simplefilter('default', RemoveInNextVersionWarning) - path = config_uri.split('#', 1)[0] parser = configparser.ConfigParser() parser.read([path]) diff --git a/pyramid/security.py b/pyramid/security.py index f3ad9ef65..82e6b73a9 100644 --- a/pyramid/security.py +++ b/pyramid/security.py @@ -1,9 +1,6 @@ +from zope.deprecation import deprecated from zope.interface import providedBy -from pyramid.deprecation import ( - RemoveInPyramid19Warning, - deprecated, -) from pyramid.interfaces import ( IAuthenticationPolicy, IAuthorizationPolicy, @@ -65,8 +62,7 @@ deprecated( 'has_permission', 'As of Pyramid 1.5 the "pyramid.security.has_permission" API is now ' 'deprecated. It will be removed in Pyramid 1.8. Use the ' - '"has_permission" method of the Pyramid request instead.', - RemoveInPyramid19Warning, + '"has_permission" method of the Pyramid request instead.' ) @@ -84,8 +80,7 @@ deprecated( 'authenticated_userid', 'As of Pyramid 1.5 the "pyramid.security.authenticated_userid" API is now ' 'deprecated. It will be removed in Pyramid 1.8. Use the ' - '"authenticated_userid" attribute of the Pyramid request instead.', - RemoveInPyramid19Warning, + '"authenticated_userid" attribute of the Pyramid request instead.' ) def unauthenticated_userid(request): @@ -102,8 +97,7 @@ deprecated( 'unauthenticated_userid', 'As of Pyramid 1.5 the "pyramid.security.unauthenticated_userid" API is ' 'now deprecated. It will be removed in Pyramid 1.8. Use the ' - '"unauthenticated_userid" attribute of the Pyramid request instead.', - RemoveInPyramid19Warning, + '"unauthenticated_userid" attribute of the Pyramid request instead.' ) def effective_principals(request): @@ -120,8 +114,7 @@ deprecated( 'effective_principals', 'As of Pyramid 1.5 the "pyramid.security.effective_principals" API is ' 'now deprecated. It will be removed in Pyramid 1.8. Use the ' - '"effective_principals" attribute of the Pyramid request instead.', - RemoveInPyramid19Warning, + '"effective_principals" attribute of the Pyramid request instead.' ) def remember(request, userid=_marker, **kw): @@ -163,7 +156,7 @@ def remember(request, userid=_marker, **kw): 'principal', 'The "principal" argument was deprecated in Pyramid 1.6. ' 'It will be removed in Pyramid 1.9. Use the "userid" ' - 'argument instead.', RemoveInPyramid19Warning) + 'argument instead.') userid = principal policy = _get_authentication_policy(request) if policy is None: diff --git a/pyramid/session.py b/pyramid/session.py index 513628ce7..47b80f617 100644 --- a/pyramid/session.py +++ b/pyramid/session.py @@ -5,6 +5,7 @@ import hmac import os import time +from zope.deprecation import deprecated from zope.interface import implementer from webob.cookies import SignedSerializer @@ -518,6 +519,117 @@ def BaseCookieSessionFactory( return CookieSession + +def UnencryptedCookieSessionFactoryConfig( + secret, + timeout=1200, + cookie_name='session', + cookie_max_age=None, + cookie_path='/', + cookie_domain=None, + cookie_secure=False, + cookie_httponly=False, + cookie_on_exception=True, + signed_serialize=signed_serialize, + signed_deserialize=signed_deserialize, + ): + """ + .. deprecated:: 1.5 + Use :func:`pyramid.session.SignedCookieSessionFactory` instead. + Caveat: Cookies generated using ``SignedCookieSessionFactory`` are not + compatible with cookies generated using + ``UnencryptedCookieSessionFactory``, so existing user session data + will be destroyed if you switch to it. + + Configure a :term:`session factory` which will provide unencrypted + (but signed) cookie-based sessions. The return value of this + function is a :term:`session factory`, which may be provided as + the ``session_factory`` argument of a + :class:`pyramid.config.Configurator` constructor, or used + as the ``session_factory`` argument of the + :meth:`pyramid.config.Configurator.set_session_factory` + method. + + The session factory returned by this function will create sessions + which are limited to storing fewer than 4000 bytes of data (as the + payload must fit into a single cookie). + + Parameters: + + ``secret`` + A string which is used to sign the cookie. + + ``timeout`` + A number of seconds of inactivity before a session times out. + + ``cookie_name`` + The name of the cookie used for sessioning. + + ``cookie_max_age`` + The maximum age of the cookie used for sessioning (in seconds). + Default: ``None`` (browser scope). + + ``cookie_path`` + The path used for the session cookie. + + ``cookie_domain`` + The domain used for the session cookie. Default: ``None`` (no domain). + + ``cookie_secure`` + The 'secure' flag of the session cookie. + + ``cookie_httponly`` + The 'httpOnly' flag of the session cookie. + + ``cookie_on_exception`` + If ``True``, set a session cookie even if an exception occurs + while rendering a view. + + ``signed_serialize`` + A callable which takes more or less arbitrary Python data structure and + a secret and returns a signed serialization in bytes. + Default: ``signed_serialize`` (using pickle). + + ``signed_deserialize`` + A callable which takes a signed and serialized data structure in bytes + and a secret and returns the original data structure if the signature + is valid. Default: ``signed_deserialize`` (using pickle). + """ + + class SerializerWrapper(object): + def __init__(self, secret): + self.secret = secret + + def loads(self, bstruct): + return signed_deserialize(bstruct, secret) + + def dumps(self, appstruct): + return signed_serialize(appstruct, secret) + + serializer = SerializerWrapper(secret) + + return BaseCookieSessionFactory( + serializer, + cookie_name=cookie_name, + max_age=cookie_max_age, + path=cookie_path, + domain=cookie_domain, + secure=cookie_secure, + httponly=cookie_httponly, + timeout=timeout, + reissue_time=0, # to keep session.accessed == session.renewed + set_on_exception=cookie_on_exception, + ) + +deprecated( + 'UnencryptedCookieSessionFactoryConfig', + 'The UnencryptedCookieSessionFactoryConfig callable is deprecated as of ' + 'Pyramid 1.5. Use ``pyramid.session.SignedCookieSessionFactory`` instead.' + ' Caveat: Cookies generated using SignedCookieSessionFactory are not ' + 'compatible with cookies generated using UnencryptedCookieSessionFactory, ' + 'so existing user session data will be destroyed if you switch to it.' + ) + def SignedCookieSessionFactory( secret, cookie_name='session', diff --git a/pyramid/traversal.py b/pyramid/traversal.py index 5d49dce1d..963a76bb5 100644 --- a/pyramid/traversal.py +++ b/pyramid/traversal.py @@ -1,15 +1,12 @@ import warnings +from zope.deprecation import deprecated + from zope.interface import implementer from zope.interface.interfaces import IInterface from repoze.lru import lru_cache -from pyramid.deprecation import ( - RemoveInPyramid19Warning, - deprecated, -) - from pyramid.interfaces import ( IResourceURL, IRequestFactory, @@ -814,8 +811,7 @@ deprecated( 'scheduled to be removed. Use the ' '"pyramid.config.Configurator.add_resource_url_adapter" method to register ' 'a class that implements "pyramid.interfaces.IResourceURL" instead. ' - 'See the "What\'s new In Pyramid 1.3" document for a further description.', - RemoveInPyramid19Warning, + 'See the "What\'s new In Pyramid 1.3" document for a further description.' ) @lru_cache(1000) -- cgit v1.2.3 From 4db295a08c931328fa6bc0d15c7b9aa57ddad86c Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Tue, 15 Nov 2016 20:58:40 -0600 Subject: add changelog for #2681 --- CHANGES.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1d69471f1..f2f412359 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -16,12 +16,16 @@ Backward Incompatibilities See https://github.com/Pylons/pyramid/pull/2615 -- ``pcreate`` is now interactive by default. You will be prompted if it +- ``pcreate`` is now interactive by default. You will be prompted if a file already exists with different content. Previously if there were similar files it would silently skip them unless you specified ``--interactive`` or ``--overwrite``. See https://github.com/Pylons/pyramid/pull/2775 +- Removed undocumented argument ``cachebust_match`` from + ``pyramid.static.static_view``. This argument was shipped accidentally + in Pyramid 1.6. See https://github.com/Pylons/pyramid/pull/2681 + Features -------- -- cgit v1.2.3 From 371d1115aed2e0f99948e09d06e96e7ba4846609 Mon Sep 17 00:00:00 2001 From: Oladipo Odumosu Date: Wed, 16 Nov 2016 05:12:01 +0100 Subject: The updated pyramid-jinja2 scaffold already sets up test requirments in `setup.py` --- docs/quick_tour.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst index 451830687..47fdb0a88 100644 --- a/docs/quick_tour.rst +++ b/docs/quick_tour.rst @@ -678,10 +678,10 @@ egregious, as Pyramid has had a deep commitment to full test coverage since before its release. Our ``pyramid_jinja2_starter`` scaffold generated a ``tests.py`` module with -one unit test in it. To run it, let's install the handy ``pytest`` test runner -by editing ``setup.py``. While we're at it, we'll throw in the ``pytest-cov`` -tool which yells at us for code that isn't tested. Insert and edit the -following lines as shown: +one unit test in it. It also setup ``setup.py`` with test requirements: +``py.test`` as the test runner, ``WebTest`` for running view tests, and the +``pytest-cov`` tool which yells at us for code that isn't tested. The +highlighted lines show this: .. code-block:: python :linenos: @@ -711,7 +711,7 @@ following lines as shown: 'testing': tests_require, }, -We changed ``setup.py`` which means we need to rerun ``$VENV/bin/pip install -e +To install the test requirements, run ``$VENV/bin/pip install -e ".[testing]"``. We can now run all our tests: .. code-block:: bash -- cgit v1.2.3 From b824f093bf3c9088ea3ca968fa85f508ebac79c7 Mon Sep 17 00:00:00 2001 From: Oladipo Odumosu Date: Wed, 16 Nov 2016 05:25:58 +0100 Subject: `pyramid-jinja2` 2.7 configured `setup.py` with test requirements out of the box --- docs/quick_tour.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst index 47fdb0a88..45c706b0d 100644 --- a/docs/quick_tour.rst +++ b/docs/quick_tour.rst @@ -678,7 +678,7 @@ egregious, as Pyramid has had a deep commitment to full test coverage since before its release. Our ``pyramid_jinja2_starter`` scaffold generated a ``tests.py`` module with -one unit test in it. It also setup ``setup.py`` with test requirements: +one unit test in it. It also configured ``setup.py`` with test requirements: ``py.test`` as the test runner, ``WebTest`` for running view tests, and the ``pytest-cov`` tool which yells at us for code that isn't tested. The highlighted lines show this: -- cgit v1.2.3 From 8dfcfb9cf823175bb91ba39565524ff95a920809 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Wed, 16 Nov 2016 00:17:37 -0600 Subject: clarify the changes in #2810 as bw-incompatible --- CHANGES.txt | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index f2f412359..1939ad125 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -26,6 +26,15 @@ Backward Incompatibilities ``pyramid.static.static_view``. This argument was shipped accidentally in Pyramid 1.6. See https://github.com/Pylons/pyramid/pull/2681 +- Change static view to avoid setting the ``Content-Encoding`` response header + to an encoding guessed using Python's ``mimetypes`` module. This was causing + clients to decode the content of gzipped files when downloading them. The + client would end up with a ``foo.txt.gz`` file on disk that was already + decoded, thus should really be ``foo.txt``. Also, the ``Content-Encoding`` + should only have been used if the client itself broadcast support for the + encoding via ``Accept-Encoding`` request headers. + See https://github.com/Pylons/pyramid/pull/2810 + Features -------- @@ -101,12 +110,6 @@ Bug Fixes from previous orders have executed. See https://github.com/Pylons/pyramid/pull/2757 -- Fix static view to avoid setting the ``Content-Encoding`` response header - to an encoding guessed using Python's ``mimetypes`` module. - This was causing clients to decode the content of gzipped files - when downloading them. - See https://github.com/Pylons/pyramid/pull/2810 - Deprecations ------------ -- cgit v1.2.3 From f52759bf3149e197481c954ae94cc04e5555f9ba Mon Sep 17 00:00:00 2001 From: Moriyoshi Koizumi Date: Sat, 19 Nov 2016 11:36:46 +0900 Subject: Split PATH_SAFE into PATH_SEGMENT_SAFE in addition to moving it to pyramid.traversal and apply each to the applicable. --- pyramid/tests/test_traversal.py | 12 +++++++++++- pyramid/traversal.py | 7 +++++-- pyramid/url.py | 7 ++++--- pyramid/urldispatch.py | 3 ++- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/pyramid/tests/test_traversal.py b/pyramid/tests/test_traversal.py index 0decd04d6..5fc878a32 100644 --- a/pyramid/tests/test_traversal.py +++ b/pyramid/tests/test_traversal.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import unittest import warnings @@ -839,7 +840,7 @@ class QuotePathSegmentTests(unittest.TestCase): def test_string(self): s = '/ hello!' result = self._callFUT(s) - self.assertEqual(result, '%2F%20hello%21') + self.assertEqual(result, '%2F%20hello!') def test_int(self): s = 12345 @@ -1299,6 +1300,15 @@ class Test__join_path_tuple(unittest.TestCase): result = self._callFUT(('x',)) self.assertEqual(result, 'x') + def test_segments_with_unsafes(self): + safe_segments = tuple(u"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~!$&'()*+,;=:@") + result = self._callFUT(safe_segments) + self.assertEqual(result, u'/'.join(safe_segments)) + unsafe_segments = tuple(chr(i) for i in range(0x20, 0x80) if not chr(i) in safe_segments) + (u'あ',) + result = self._callFUT(unsafe_segments) + self.assertEqual(result, u'/'.join(''.join('%%%02X' % (ord(c) if isinstance(c, str) else c) for c in unsafe_segment.encode('utf-8')) for unsafe_segment in unsafe_segments)) + + def make_traverser(result): class DummyTraverser(object): def __init__(self, context): diff --git a/pyramid/traversal.py b/pyramid/traversal.py index 963a76bb5..1ca52692a 100644 --- a/pyramid/traversal.py +++ b/pyramid/traversal.py @@ -35,6 +35,9 @@ with warnings.catch_warnings(): warnings.filterwarnings('ignore') from pyramid.interfaces import IContextURL +PATH_SEGMENT_SAFE = "~!$&'()*+,;=:@" # from webob +PATH_SAFE = PATH_SEGMENT_SAFE + "/" + empty = text_('') def find_root(resource): @@ -577,7 +580,7 @@ the ``safe`` argument to this function. This corresponds to the if PY2: # special-case on Python 2 for speed? unchecked - def quote_path_segment(segment, safe=''): + def quote_path_segment(segment, safe=PATH_SEGMENT_SAFE): """ %s """ % quote_path_segment_doc # The bit of this code that deals with ``_segment_cache`` is an # optimization: we cache all the computation of URL path segments @@ -596,7 +599,7 @@ if PY2: _segment_cache[(segment, safe)] = result return result else: - def quote_path_segment(segment, safe=''): + def quote_path_segment(segment, safe=PATH_SEGMENT_SAFE): """ %s """ % quote_path_segment_doc # The bit of this code that deals with ``_segment_cache`` is an # optimization: we cache all the computation of URL path segments diff --git a/pyramid/url.py b/pyramid/url.py index fd62f0057..0214d35ad 100644 --- a/pyramid/url.py +++ b/pyramid/url.py @@ -25,10 +25,11 @@ from pyramid.threadlocal import get_current_registry from pyramid.traversal import ( ResourceURL, quote_path_segment, + PATH_SAFE, + PATH_SEGMENT_SAFE, ) -PATH_SAFE = '/:@&+$,' # from webob -QUERY_SAFE = '/?:@!$&\'()*+,;=' # RFC 3986 +QUERY_SAFE = "/?:@!$&'()*+,;=" # RFC 3986 ANCHOR_SAFE = QUERY_SAFE def parse_url_overrides(kw): @@ -947,4 +948,4 @@ def current_route_path(request, *elements, **kw): @lru_cache(1000) def _join_elements(elements): - return '/'.join([quote_path_segment(s, safe=':@&+$,') for s in elements]) + return '/'.join([quote_path_segment(s, safe=PATH_SEGMENT_SAFE) for s in elements]) diff --git a/pyramid/urldispatch.py b/pyramid/urldispatch.py index 0f7dd1490..a61071845 100644 --- a/pyramid/urldispatch.py +++ b/pyramid/urldispatch.py @@ -22,6 +22,7 @@ from pyramid.exceptions import URLDecodeError from pyramid.traversal import ( quote_path_segment, split_path_info, + PATH_SAFE, ) _marker = object() @@ -209,7 +210,7 @@ def _compile_route(route): gen = ''.join(gen) def q(v): - return quote_path_segment(v, safe='/:@&+$,') + return quote_path_segment(v, safe=PATH_SAFE) def generator(dict): newdict = {} -- cgit v1.2.3 From 663c2648e2a946f44eeec0e490effb2ebdb8a11c Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Nov 2016 00:05:07 -0600 Subject: improve view decorator return type documentation Fixes #2770. --- docs/narr/viewconfig.rst | 8 +++++--- pyramid/config/views.py | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst index 7cb8e0306..3b683ff79 100644 --- a/docs/narr/viewconfig.rst +++ b/docs/narr/viewconfig.rst @@ -252,7 +252,7 @@ Non-Predicate Arguments def myview(request): ... - Is similar to doing:: + Is similar to decorating the view callable directly:: @view_config(...) @decorator2 @@ -260,8 +260,10 @@ Non-Predicate Arguments def myview(request): ... - All view callables in the decorator chain must return a response object - implementing :class:`pyramid.interfaces.IResponse` or raise an exception: + An important distinction is that each decorator will receive a response + object implementing :class:`pyramid.interfaces.IResponse` instead of the + raw value returned from the view callable. All decorators in the chain must + return a response object or raise an exception: .. code-block:: python diff --git a/pyramid/config/views.py b/pyramid/config/views.py index acdc00704..6082d8b48 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -444,9 +444,11 @@ class ViewsConfiguratorMixin(object): think about preserving function attributes such as ``__name__`` and ``__module__`` within decorator logic). - All view callables in the decorator chain must return a response - object implementing :class:`pyramid.interfaces.IResponse` or raise - an exception: + An important distinction is that each decorator will receive a + response object implementing :class:`pyramid.interfaces.IResponse` + instead of the raw value returned from the view callable. All + decorators in the chain must return a response object or raise an + exception: .. code-block:: python -- cgit v1.2.3 From 7764d4ab81e52b6702ae1921fbe5ebde2e2e4b2e Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Nov 2016 00:11:10 -0600 Subject: fix variable names in request.resource_url examples to match Fixes #2658. --- pyramid/url.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyramid/url.py b/pyramid/url.py index fd62f0057..7c8e401d0 100644 --- a/pyramid/url.py +++ b/pyramid/url.py @@ -454,7 +454,7 @@ class URLMethodsMixin(object): ``resource_url(someresource, 'element1', 'element2', query={'a':1}, route_name='blogentry')`` is roughly equivalent to doing:: - remainder_path = request.resource_path(someobject) + traversal_path = request.resource_path(someobject) url = request.route_url( 'blogentry', 'element1', @@ -486,7 +486,7 @@ class URLMethodsMixin(object): 'element2', route_name='blogentry', route_kw={'id':'4'}, _query={'a':'1'})`` is roughly equivalent to:: - remainder_path = request.resource_path_tuple(someobject) + traversal_path = request.resource_path_tuple(someobject) kw = {'id':'4', '_query':{'a':'1'}, 'traverse':traversal_path} url = request.route_url( 'blogentry', -- cgit v1.2.3 From 7fe6c3435999bf9d6cebefeee4268846dff9b444 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Nov 2016 00:27:51 -0600 Subject: fix function reference in request.resource_url docs --- pyramid/url.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyramid/url.py b/pyramid/url.py index 7c8e401d0..646cc857d 100644 --- a/pyramid/url.py +++ b/pyramid/url.py @@ -364,7 +364,7 @@ class URLMethodsMixin(object): of ``query`` may be a sequence of two-tuples *or* a data structure with an ``.items()`` method that returns a sequence of two-tuples (presumably a dictionary). This data structure will be turned into a - query string per the documentation of :func:``pyramid.url.urlencode`` + query string per the documentation of :func:`pyramid.url.urlencode` function. This will produce a query string in the ``x-www-form-urlencoded`` encoding. A non-``x-www-form-urlencoded`` query string may be used by passing a *string* value as ``query`` in -- cgit v1.2.3 From f6fb4be7c8b0924688a9a057afcd8bb602d32643 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Nov 2016 00:59:51 -0600 Subject: add warnings about how notfound/forbidden views are only invoked for raised exceptions fixes #1531 --- docs/narr/hooks.rst | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index b22b31bf9..28d9bc801 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -116,14 +116,6 @@ callable: .. note:: - Both :meth:`pyramid.config.Configurator.add_notfound_view` and - :class:`pyramid.view.notfound_view_config` are new as of Pyramid 1.3. - Older Pyramid documentation instructed users to use ``add_view`` instead, - with a ``context`` of ``HTTPNotFound``. This still works; the convenience - method and decorator are just wrappers around this functionality. - -.. warning:: - When a Not Found View callable accepts an argument list as described in :ref:`request_and_context_view_definitions`, the ``context`` passed as the first argument to the view callable will be the @@ -131,6 +123,13 @@ callable: available, the resource context will still be available as ``request.context``. +.. warning:: + + The :term:`Not Found View` callables are only invoked when a + :exc:`~pyramid.httpexceptions.HTTPNotFound` exception is raised. If the + exception is returned from a view then it will be treated as a regular + response object and it will not trigger the custom view. + .. index:: single: forbidden view @@ -210,6 +209,13 @@ Here's some sample code that implements a minimal forbidden view: whether the ``pyramid.debug_authorization`` environment setting is true or false. +.. warning:: + + The :term:`forbidden view` callables are only invoked when a + :exc:`~pyramid.httpexceptions.HTTPForbidden` exception is raised. If the + exception is returned from a view then it will be treated as a regular + response object and it will not trigger the custom view. + .. index:: single: request factory -- cgit v1.2.3 From 44563d94a71587ba33a055df1e9008027af23497 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Nov 2016 01:11:21 -0600 Subject: require tuple variants on IResourceURL as well --- docs/narr/hooks.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst index 28d9bc801..d21edc7b4 100644 --- a/docs/narr/hooks.rst +++ b/docs/narr/hooks.rst @@ -750,7 +750,9 @@ The API that must be implemented by a class that provides """ Accept the resource and request and set self.physical_path and self.virtual_path """ self.virtual_path = some_function_of(resource, request) + self.virtual_path_tuple = some_function_of(resource, request) self.physical_path = some_other_function_of(resource, request) + self.physical_path_tuple = some_function_of(resource, request) The default context URL generator is available for perusal as the class :class:`pyramid.traversal.ResourceURL` in the `traversal module -- cgit v1.2.3 From 85672acc4da0335defc2a77e5d2121ab9195871c Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Nov 2016 11:45:38 -0600 Subject: update todo --- TODO.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/TODO.txt b/TODO.txt index 797f8acef..d62efee5a 100644 --- a/TODO.txt +++ b/TODO.txt @@ -120,9 +120,7 @@ Future - 1.6: Remove IContextURL and TraversalContextURL. -- 1.8: Remove set_request_property. -- 1.8: Drop Python 3.3 support. - +- 1.9: Remove set_request_property. - 1.9: Remove extra code enabling ``pyramid.security.remember(principal=...)`` and force use of ``userid``. -- cgit v1.2.3 From d35851138f3b7d1888893efbf0ec991dcd8cf566 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Nov 2016 13:11:16 -0600 Subject: make settings only accessible using dictionary lookup Remove the deprecated ability to access settings using attribute lookup. --- TODO.txt | 4 - pyramid/config/settings.py | 169 +++++++++-------------------- pyramid/tests/test_config/test_settings.py | 20 ---- 3 files changed, 52 insertions(+), 141 deletions(-) diff --git a/TODO.txt b/TODO.txt index d62efee5a..b064cd8e8 100644 --- a/TODO.txt +++ b/TODO.txt @@ -114,10 +114,6 @@ Nice-to-Have Future ------ -- 1.6: turn ``pyramid.settings.Settings`` into a function that returns the - original dict (after ``__getattr__`` deprecation period, it was deprecated - in 1.2). - - 1.6: Remove IContextURL and TraversalContextURL. - 1.9: Remove set_request_property. diff --git a/pyramid/config/settings.py b/pyramid/config/settings.py index f9dbd752e..5684f1867 100644 --- a/pyramid/config/settings.py +++ b/pyramid/config/settings.py @@ -1,11 +1,6 @@ import os -import warnings -from zope.interface import implementer - -from pyramid.interfaces import ISettings - -from pyramid.settings import asbool +from pyramid.settings import asbool, aslist class SettingsConfiguratorMixin(object): def _set_settings(self, mapping): @@ -54,118 +49,58 @@ class SettingsConfiguratorMixin(object): return self.registry.settings -@implementer(ISettings) -class Settings(dict): +def Settings(d=None, _environ_=os.environ, **kw): """ Deployment settings. Update application settings (usually from PasteDeploy keywords) with framework-specific key/value pairs (e.g. find ``PYRAMID_DEBUG_AUTHORIZATION`` in os.environ and jam into keyword args).""" - # _environ_ is dep inj for testing - def __init__(self, d=None, _environ_=os.environ, **kw): - if d is None: - d = {} - dict.__init__(self, d, **kw) - eget = _environ_.get - config_debug_all = self.get('debug_all', '') - config_debug_all = self.get('pyramid.debug_all', config_debug_all) - eff_debug_all = asbool(eget('PYRAMID_DEBUG_ALL', config_debug_all)) - config_reload_all = self.get('reload_all', '') - config_reload_all = self.get('pyramid.reload_all', config_reload_all) - eff_reload_all = asbool(eget('PYRAMID_RELOAD_ALL', config_reload_all)) - config_debug_auth = self.get('debug_authorization', '') - config_debug_auth = self.get('pyramid.debug_authorization', - config_debug_auth) - eff_debug_auth = asbool(eget('PYRAMID_DEBUG_AUTHORIZATION', - config_debug_auth)) - config_debug_notfound = self.get('debug_notfound', '') - config_debug_notfound = self.get('pyramid.debug_notfound', - config_debug_notfound) - eff_debug_notfound = asbool(eget('PYRAMID_DEBUG_NOTFOUND', - config_debug_notfound)) - config_debug_routematch = self.get('debug_routematch', '') - config_debug_routematch = self.get('pyramid.debug_routematch', - config_debug_routematch) - eff_debug_routematch = asbool(eget('PYRAMID_DEBUG_ROUTEMATCH', - config_debug_routematch)) - config_debug_templates = self.get('debug_templates', '') - config_debug_templates = self.get('pyramid.debug_templates', - config_debug_templates) - eff_debug_templates = asbool(eget('PYRAMID_DEBUG_TEMPLATES', - config_debug_templates)) - config_reload_templates = self.get('reload_templates', '') - config_reload_templates = self.get('pyramid.reload_templates', - config_reload_templates) - eff_reload_templates = asbool(eget('PYRAMID_RELOAD_TEMPLATES', - config_reload_templates)) - config_reload_assets = self.get('reload_assets', '') - config_reload_assets = self.get('pyramid.reload_assets', - config_reload_assets) - reload_assets = asbool(eget('PYRAMID_RELOAD_ASSETS', - config_reload_assets)) - config_reload_resources = self.get('reload_resources', '') - config_reload_resources = self.get('pyramid.reload_resources', - config_reload_resources) - reload_resources = asbool(eget('PYRAMID_RELOAD_RESOURCES', - config_reload_resources)) - # reload_resources is an older alias for reload_assets - eff_reload_assets = reload_assets or reload_resources - locale_name = self.get('default_locale_name', 'en') - locale_name = self.get('pyramid.default_locale_name', locale_name) - eff_locale_name = eget('PYRAMID_DEFAULT_LOCALE_NAME', locale_name) - config_prevent_http_cache = self.get('prevent_http_cache', '') - config_prevent_http_cache = self.get('pyramid.prevent_http_cache', - config_prevent_http_cache) - eff_prevent_http_cache = asbool(eget('PYRAMID_PREVENT_HTTP_CACHE', - config_prevent_http_cache)) - config_prevent_cachebust = self.get('prevent_cachebust', '') - config_prevent_cachebust = self.get('pyramid.prevent_cachebust', - config_prevent_cachebust) - eff_prevent_cachebust = asbool(eget('PYRAMID_PREVENT_CACHEBUST', - config_prevent_cachebust)) - csrf_trusted_origins = self.get("pyramid.csrf_trusted_origins", []) - eff_csrf_trusted_origins = csrf_trusted_origins - - update = { - 'debug_authorization': eff_debug_all or eff_debug_auth, - 'debug_notfound': eff_debug_all or eff_debug_notfound, - 'debug_routematch': eff_debug_all or eff_debug_routematch, - 'debug_templates': eff_debug_all or eff_debug_templates, - 'reload_templates': eff_reload_all or eff_reload_templates, - 'reload_resources':eff_reload_all or eff_reload_assets, - 'reload_assets':eff_reload_all or eff_reload_assets, - 'default_locale_name':eff_locale_name, - 'prevent_http_cache':eff_prevent_http_cache, - 'prevent_cachebust':eff_prevent_cachebust, - 'csrf_trusted_origins':eff_csrf_trusted_origins, - - 'pyramid.debug_authorization': eff_debug_all or eff_debug_auth, - 'pyramid.debug_notfound': eff_debug_all or eff_debug_notfound, - 'pyramid.debug_routematch': eff_debug_all or eff_debug_routematch, - 'pyramid.debug_templates': eff_debug_all or eff_debug_templates, - 'pyramid.reload_templates': eff_reload_all or eff_reload_templates, - 'pyramid.reload_resources':eff_reload_all or eff_reload_assets, - 'pyramid.reload_assets':eff_reload_all or eff_reload_assets, - 'pyramid.default_locale_name':eff_locale_name, - 'pyramid.prevent_http_cache':eff_prevent_http_cache, - 'pyramid.prevent_cachebust':eff_prevent_cachebust, - 'pyramid.csrf_trusted_origins':eff_csrf_trusted_origins, - } - - self.update(update) - - def __getattr__(self, name): - try: - val = self[name] - # only deprecate on success; a probing getattr/hasattr should not - # print this warning - warnings.warn( - 'Obtaining settings via attributes of the settings dictionary ' - 'is deprecated as of Pyramid 1.2; use settings["foo"] instead ' - 'of settings.foo', - DeprecationWarning, - 2 - ) - return val - except KeyError: - raise AttributeError(name) - + if d is None: + d = {} + d.update(**kw) + + eget = _environ_.get + def expand_key(key): + keys = [key] + if not key.startswith('pyramid.'): + keys.append('pyramid.' + key) + return keys + def S(settings_key, env_key=None, type_=str, default=False): + value = default + keys = expand_key(settings_key) + for key in keys: + value = d.get(key, value) + if env_key: + value = eget(env_key, value) + value = type_(value) + d.update({k: value for k in keys}) + def O(settings_key, override_key): + for key in expand_key(settings_key): + d[key] = d[key] or d[override_key] + + S('debug_all', 'PYRAMID_DEBUG_ALL', asbool) + S('debug_authorization', 'PYRAMID_DEBUG_AUTHORIZATION', asbool) + O('debug_authorization', 'debug_all') + S('debug_notfound', 'PYRAMID_DEBUG_NOTFOUND', asbool) + O('debug_notfound', 'debug_all') + S('debug_routematch', 'PYRAMID_DEBUG_ROUTEMATCH', asbool) + O('debug_routematch', 'debug_all') + S('debug_templates', 'PYRAMID_DEBUG_TEMPLATES', asbool) + O('debug_templates', 'debug_all') + + S('reload_all', 'PYRAMID_RELOAD_ALL', asbool) + S('reload_templates', 'PYRAMID_RELOAD_TEMPLATES', asbool) + O('reload_templates', 'reload_all') + S('reload_assets', 'PYRAMID_RELOAD_ASSETS', asbool) + O('reload_assets', 'reload_all') + S('reload_resources', 'PYRAMID_RELOAD_RESOURCES', asbool) + O('reload_resources', 'reload_all') + # reload_resources is an older alias for reload_assets + for k in expand_key('reload_assets') + expand_key('reload_resources'): + d[k] = d['reload_assets'] or d['reload_resources'] + + S('default_locale_name', 'PYRAMID_DEFAULT_LOCALE_NAME', str, 'en') + S('prevent_http_cache', 'PYRAMID_PREVENT_HTTP_CACHE', asbool) + S('prevent_cachebust', 'PYRAMID_PREVENT_CACHEBUST', asbool) + S('csrf_trusted_origins', 'PYRAMID_CSRF_TRUSTED_ORIGINS', aslist, []) + + return d diff --git a/pyramid/tests/test_config/test_settings.py b/pyramid/tests/test_config/test_settings.py index d2a98b347..78198298b 100644 --- a/pyramid/tests/test_config/test_settings.py +++ b/pyramid/tests/test_config/test_settings.py @@ -68,26 +68,6 @@ class TestSettings(unittest.TestCase): klass = self._getTargetClass() return klass(d, _environ_=environ) - def test_getattr_success(self): - import warnings - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always') - settings = self._makeOne({'reload_templates':False}) - self.assertEqual(settings.reload_templates, False) - self.assertEqual(len(w), 1) - - def test_getattr_fail(self): - import warnings - with warnings.catch_warnings(record=True) as w: - warnings.filterwarnings('always') - settings = self._makeOne({}) - self.assertRaises(AttributeError, settings.__getattr__, 'wontexist') - self.assertEqual(len(w), 0) - - def test_getattr_raises_attribute_error(self): - settings = self._makeOne() - self.assertRaises(AttributeError, settings.__getattr__, 'mykey') - def test_noargs(self): settings = self._makeOne() self.assertEqual(settings['debug_authorization'], False) -- cgit v1.2.3 From 7b58c0f003107aba5fde6cde57f13491d5248c76 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Nov 2016 14:51:40 -0600 Subject: update changelog for #2823 --- CHANGES.txt | 13 +++++++++++++ pyramid/config/settings.py | 2 +- pyramid/tests/test_config/test_settings.py | 19 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1939ad125..a0a928f83 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -35,6 +35,10 @@ Backward Incompatibilities encoding via ``Accept-Encoding`` request headers. See https://github.com/Pylons/pyramid/pull/2810 +- Settings are no longer accessible as attributes on the settings object + (e.g. ``request.registry.settings.foo``). This was deprecated in Pyramid 1.2. + See https://github.com/Pylons/pyramid/pull/2823 + Features -------- @@ -80,6 +84,15 @@ Features as soon as possible before importing the rest of pyramid. See https://github.com/Pylons/pyramid/pull/2797 +- Pyramid no longer copies the settings object passed to the + ``pyramid.config.Configurator(settings=)``. The original ``dict`` is kept. + See https://github.com/Pylons/pyramid/pull/2823 + +- The csrf trusted origins setting may now be a whitespace-separated list of + domains. Previously only a python list was allowed. Also, it can now be set + using the ``PYRAMID_CSRF_TRUSTED_ORIGINS`` environment variable similar to + other settings. See https://github.com/Pylons/pyramid/pull/2823 + Bug Fixes --------- diff --git a/pyramid/config/settings.py b/pyramid/config/settings.py index 5684f1867..26eb48951 100644 --- a/pyramid/config/settings.py +++ b/pyramid/config/settings.py @@ -4,7 +4,7 @@ from pyramid.settings import asbool, aslist class SettingsConfiguratorMixin(object): def _set_settings(self, mapping): - if not mapping: + if mapping is None: mapping = {} settings = Settings(mapping) self.registry.settings = settings diff --git a/pyramid/tests/test_config/test_settings.py b/pyramid/tests/test_config/test_settings.py index 78198298b..2dbe9b1bb 100644 --- a/pyramid/tests/test_config/test_settings.py +++ b/pyramid/tests/test_config/test_settings.py @@ -11,6 +11,13 @@ class TestSettingsConfiguratorMixin(unittest.TestCase): settings = config._set_settings(None) self.assertTrue(settings) + def test__set_settings_uses_original_dict(self): + config = self._makeOne() + dummy = {} + result = config._set_settings(dummy) + self.assertTrue(dummy is result) + self.assertEqual(dummy['pyramid.debug_all'], False) + def test__set_settings_as_dictwithvalues(self): config = self._makeOne() settings = config._set_settings({'a':'1'}) @@ -537,6 +544,18 @@ class TestSettings(unittest.TestCase): self.assertEqual(result['default_locale_name'], 'abc') self.assertEqual(result['pyramid.default_locale_name'], 'abc') + def test_csrf_trusted_origins(self): + result = self._makeOne({}) + self.assertEqual(result['pyramid.csrf_trusted_origins'], []) + result = self._makeOne({'pyramid.csrf_trusted_origins': 'example.com'}) + self.assertEqual(result['pyramid.csrf_trusted_origins'], ['example.com']) + result = self._makeOne({'pyramid.csrf_trusted_origins': ['example.com']}) + self.assertEqual(result['pyramid.csrf_trusted_origins'], ['example.com']) + result = self._makeOne({'pyramid.csrf_trusted_origins': ( + 'example.com foo.example.com\nasdf.example.com')}) + self.assertEqual(result['pyramid.csrf_trusted_origins'], [ + 'example.com', 'foo.example.com', 'asdf.example.com']) + def test_originals_kept(self): result = self._makeOne({'a':'i am so a'}) self.assertEqual(result['a'], 'i am so a') -- cgit v1.2.3 From 8034f05953669f0c0d03315adb8345e7b79f850c Mon Sep 17 00:00:00 2001 From: Moriyoshi Koizumi Date: Sun, 20 Nov 2016 11:48:50 +0900 Subject: Add myself to CONTRIBUTORS.txt --- CONTRIBUTORS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index bb21337e2..98e243c1f 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -284,3 +284,5 @@ Contributors - Jon Davidson, 2016/07/18 - Keith Yang, 2016/07/22 + +- Moriyoshi Koizumi, 2016/11/20 -- cgit v1.2.3 From fb88f053a2a6ffaf3d597d49790fb19e956358e0 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 20 Nov 2016 02:15:23 -0800 Subject: Rename Typographical Conventions to Style Guide --- docs/conventions.rst | 107 -------------------------------------------------- docs/index.rst | 6 +-- docs/latexindex.rst | 2 +- docs/style-guide.rst | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 111 deletions(-) delete mode 100644 docs/conventions.rst create mode 100644 docs/style-guide.rst diff --git a/docs/conventions.rst b/docs/conventions.rst deleted file mode 100644 index de041da04..000000000 --- a/docs/conventions.rst +++ /dev/null @@ -1,107 +0,0 @@ -Typographical Conventions -========================= - -Literals, filenames, and function arguments are presented using the -following style: - - ``argument1`` - -Warnings which represent limitations and need-to-know information -related to a topic or concept are presented in the following style: - - .. warning:: - - This is a warning. - -Notes which represent additional information related to a topic or -concept are presented in the following style: - - .. note:: - - This is a note. - -We present Python method names using the following style: - - :meth:`pyramid.config.Configurator.add_view` - -We present Python class names, module names, attributes, and global -variables using the following style: - - :class:`pyramid.config.Configurator.registry` - -References to glossary terms are presented using the following style: - - :term:`Pylons` - -URLs are presented using the following style: - - `Pylons `_ - -References to sections and chapters are presented using the following -style: - - :ref:`traversal_chapter` - -Code and configuration file blocks are presented in the following style: - - .. code-block:: python - :linenos: - - def foo(abc): - pass - -Example blocks representing UNIX shell commands are prefixed with a ``$`` -character, e.g.: - - .. code-block:: bash - - $ $VENV/bin/py.test -q - -See :term:`venv` for the meaning of ``$VENV``. - -Example blocks representing Windows commands are prefixed with a drive letter -with an optional directory name, e.g.: - - .. code-block:: doscon - - c:\examples> %VENV%\Scripts\py.test -q - -See :term:`venv` for the meaning of ``%VENV%``. - -When a command that should be typed on one line is too long to fit on a page, -the backslash ``\`` is used to indicate that the following printed line should -be part of the command: - - .. code-block:: bash - - $VENV/bin/py.test tutorial/tests.py --cov-report term-missing \ - --cov=tutorial -q - -A sidebar, which presents a concept tangentially related to content discussed -on a page, is rendered like so: - -.. sidebar:: This is a sidebar - - Sidebar information. - -When multiple objects are imported from the same package, the following -convention is used: - - .. code-block:: python - - from foo import ( - bar, - baz, - ) - -It may look unusual, but it has advantages: - -* It allows one to swap out the higher-level package ``foo`` for something else - that provides the similar API. An example would be swapping out one database - for another (e.g., graduating from SQLite to PostgreSQL). - -* Looks more neat in cases where a large number of objects get imported from - that package. - -* Adding or removing imported objects from the package is quicker and results - in simpler diffs. diff --git a/docs/index.rst b/docs/index.rst index 02c35866a..f41154c19 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -213,13 +213,13 @@ Copyright, Trademarks, and Attributions copyright -Typographical Conventions -========================= +Style Guide +=========== .. toctree:: :maxdepth: 1 - conventions + style-guide Index and Glossary diff --git a/docs/latexindex.rst b/docs/latexindex.rst index 05199d313..83a139917 100644 --- a/docs/latexindex.rst +++ b/docs/latexindex.rst @@ -15,7 +15,7 @@ Front Matter :maxdepth: 1 copyright - conventions + style-guide authorintro designdefense diff --git a/docs/style-guide.rst b/docs/style-guide.rst new file mode 100644 index 000000000..856d6ea85 --- /dev/null +++ b/docs/style-guide.rst @@ -0,0 +1,109 @@ +.. _style_guide: + +Style Guide +=========== + +Literals, filenames, and function arguments are presented using the +following style: + + ``argument1`` + +Warnings which represent limitations and need-to-know information +related to a topic or concept are presented in the following style: + + .. warning:: + + This is a warning. + +Notes which represent additional information related to a topic or +concept are presented in the following style: + + .. note:: + + This is a note. + +We present Python method names using the following style: + + :meth:`pyramid.config.Configurator.add_view` + +We present Python class names, module names, attributes, and global +variables using the following style: + + :class:`pyramid.config.Configurator.registry` + +References to glossary terms are presented using the following style: + + :term:`Pylons` + +URLs are presented using the following style: + + `Pylons `_ + +References to sections and chapters are presented using the following +style: + + :ref:`traversal_chapter` + +Code and configuration file blocks are presented in the following style: + + .. code-block:: python + :linenos: + + def foo(abc): + pass + +Example blocks representing UNIX shell commands are prefixed with a ``$`` +character, e.g.: + + .. code-block:: bash + + $ $VENV/bin/py.test -q + +See :term:`venv` for the meaning of ``$VENV``. + +Example blocks representing Windows commands are prefixed with a drive letter +with an optional directory name, e.g.: + + .. code-block:: doscon + + c:\examples> %VENV%\Scripts\py.test -q + +See :term:`venv` for the meaning of ``%VENV%``. + +When a command that should be typed on one line is too long to fit on a page, +the backslash ``\`` is used to indicate that the following printed line should +be part of the command: + + .. code-block:: bash + + $VENV/bin/py.test tutorial/tests.py --cov-report term-missing \ + --cov=tutorial -q + +A sidebar, which presents a concept tangentially related to content discussed +on a page, is rendered like so: + +.. sidebar:: This is a sidebar + + Sidebar information. + +When multiple objects are imported from the same package, the following +convention is used: + + .. code-block:: python + + from foo import ( + bar, + baz, + ) + +It may look unusual, but it has advantages: + +* It allows one to swap out the higher-level package ``foo`` for something else + that provides the similar API. An example would be swapping out one database + for another (e.g., graduating from SQLite to PostgreSQL). + +* Looks more neat in cases where a large number of objects get imported from + that package. + +* Adding or removing imported objects from the package is quicker and results + in simpler diffs. -- cgit v1.2.3 From 8cc4f7891056c085a4060ffe7ec9ce580943133e Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 20 Nov 2016 03:33:56 -0800 Subject: Add Introduction - add how to update and contribute - add file conventions - add line conventions --- docs/style-guide.rst | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 856d6ea85..ae56ad9ba 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -1,8 +1,55 @@ -.. _style_guide: +.. _style-guide: Style Guide =========== +This chapter describes how to edit, update, and build the :app:`Pyramid` documentation. + + +.. _style-guide-contribute: + +How to update and contribute to documentation +--------------------------------------------- + +All projects under the Pylons Projects, including this one, follow the guidelines established at `How to Contribute `_ and `Coding Style and Standards `_. + +By building the documentation locally, you can preview the output before committing and pushing your changes to the repository. Follow the instructions for `Building documentation for a Pylons Project project `_. These instructions also include how to install packages required to build the documentation, and how to follow our recommended git workflow. + +When submitting a pull request for the first time in a project, sign `CONTRIBUTORS.txt `_ and commit it along with your pull request. + + +.. _style-guide-file-conventions: + +Location, referencing, and naming of files +------------------------------------------ + +* reStructuredText (reST) files must be located in ``docs/`` and its subdirectories. +* Image files must be located in ``docs/_static/``. +* reST directives must refer to files either relative to the source file or absolute from the top source directory. For example, in ``docs/narr/source.rst``, you could refer to a file in a different directory as either ``.. include:: ../diff-dir/diff-source.rst`` or ``.. include:: /diff-dir/diff-source.rst``. +* File names should be lower-cased and have words separated with either a hyphen "-" or an underscore "_". +* reST files must have an extension of ``.rst``. +* Image files may be any format but must have lower-cased file names and have standard file extensions that consist three letters (``.gif``, ``.jpg``, ``.png``, ``.svg``). ``.gif`` and ``.svg`` are not currently supported by PDF builders in Sphinx, but you can allow the Sphinx builder to automatically select the correct image format for the desired output by replacing the three-letter file extension with ``*``. For example: + + .. code-block:: rst + + .. image:: ../_static/pyramid_request_processing.* + + will select the image ``pyramid_request_processing.svg`` for the HTML documentation builder, and ``pyramid_request_processing.png`` for the PDF builder. See the related [Stack Overflow post](http://stackoverflow.com/questions/6473660/using-sphinx-docs-how-can-i-specify-png-image-formats-for-html-builds-and-pdf-im/6486713#6486713). + + +.. _style-guide-lines: + +Line length and paragraphs +-------------------------- + +Narrative documentation is not code, and should therefore not adhere to PEP8 or other line length conventions. When a translator sees only part of a sentence or paragraph, it makes it more difficult to translate the concept. Text editors can soft wrap lines for display to avoid horizontal scrolling. We admit, we boofed it by using arbitrary 79-character line lengths in our own documentation, but we have seen the error of our ways and wish to correct this going forward. + +A paragraph should be on one line. Paragraphs must be separated by two line feeds. + + + + + Literals, filenames, and function arguments are presented using the following style: -- cgit v1.2.3 From 18a6375cadb8cbf7e44f494c36e0d6c3f696f141 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 20 Nov 2016 05:06:36 -0800 Subject: Add moar sections - trailing white space - indentation - split line lengths and paragraphs --- docs/style-guide.rst | 53 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index ae56ad9ba..5946314e3 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -23,30 +23,55 @@ When submitting a pull request for the first time in a project, sign `CONTRIBUTO Location, referencing, and naming of files ------------------------------------------ -* reStructuredText (reST) files must be located in ``docs/`` and its subdirectories. -* Image files must be located in ``docs/_static/``. -* reST directives must refer to files either relative to the source file or absolute from the top source directory. For example, in ``docs/narr/source.rst``, you could refer to a file in a different directory as either ``.. include:: ../diff-dir/diff-source.rst`` or ``.. include:: /diff-dir/diff-source.rst``. -* File names should be lower-cased and have words separated with either a hyphen "-" or an underscore "_". -* reST files must have an extension of ``.rst``. -* Image files may be any format but must have lower-cased file names and have standard file extensions that consist three letters (``.gif``, ``.jpg``, ``.png``, ``.svg``). ``.gif`` and ``.svg`` are not currently supported by PDF builders in Sphinx, but you can allow the Sphinx builder to automatically select the correct image format for the desired output by replacing the three-letter file extension with ``*``. For example: +- reStructuredText (reST) files must be located in ``docs/`` and its subdirectories. +- Image files must be located in ``docs/_static/``. +- reST directives must refer to files either relative to the source file or absolute from the top source directory. For example, in ``docs/narr/source.rst``, you could refer to a file in a different directory as either ``.. include:: ../diff-dir/diff-source.rst`` or ``.. include:: /diff-dir/diff-source.rst``. +- File names should be lower-cased and have words separated with either a hyphen "-" or an underscore "_". +- reST files must have an extension of ``.rst``. +- Image files may be any format but must have lower-cased file names and have standard file extensions that consist three letters (``.gif``, ``.jpg``, ``.png``, ``.svg``). ``.gif`` and ``.svg`` are not currently supported by PDF builders in Sphinx, but you can allow the Sphinx builder to automatically select the correct image format for the desired output by replacing the three-letter file extension with ``*``. For example: .. code-block:: rst - .. image:: ../_static/pyramid_request_processing.* + .. image:: ../_static/pyramid_request_processing.- will select the image ``pyramid_request_processing.svg`` for the HTML documentation builder, and ``pyramid_request_processing.png`` for the PDF builder. See the related [Stack Overflow post](http://stackoverflow.com/questions/6473660/using-sphinx-docs-how-can-i-specify-png-image-formats-for-html-builds-and-pdf-im/6486713#6486713). -.. _style-guide-lines: +.. _style-guide-line-lengths: -Line length and paragraphs --------------------------- +Line lengths +------------ -Narrative documentation is not code, and should therefore not adhere to PEP8 or other line length conventions. When a translator sees only part of a sentence or paragraph, it makes it more difficult to translate the concept. Text editors can soft wrap lines for display to avoid horizontal scrolling. We admit, we boofed it by using arbitrary 79-character line lengths in our own documentation, but we have seen the error of our ways and wish to correct this going forward. +Narrative documentation is not code, and should therefore not adhere to PEP8 or other line length conventions. When a translator sees only part of a sentence or paragraph, it makes it more difficult to translate the concept. Line lengths make ``diff`` more difficult. Text editors can soft wrap lines for display to avoid horizontal scrolling. We admit, we boofed it by using arbitrary 79-character line lengths in our own documentation, but we have seen the error of our ways and wish to correct this going forward. + + +.. _style-guide-trailing-white-space: + +Trailing white spaces +--------------------- + +- No trailing white spaces. +- Always use a line feed or carriage return at the end of a file. + + +.. _style-guide-paragraphs: + +Paragraphs +---------- A paragraph should be on one line. Paragraphs must be separated by two line feeds. +.. _style-guide-indentation: + +Indentation +----------- + +- Indent using four spaces. +- Do not use tabs to indent. + + + @@ -145,12 +170,12 @@ convention is used: It may look unusual, but it has advantages: -* It allows one to swap out the higher-level package ``foo`` for something else +- It allows one to swap out the higher-level package ``foo`` for something else that provides the similar API. An example would be swapping out one database for another (e.g., graduating from SQLite to PostgreSQL). -* Looks more neat in cases where a large number of objects get imported from +- Looks more neat in cases where a large number of objects get imported from that package. -* Adding or removing imported objects from the package is quicker and results +- Adding or removing imported objects from the package is quicker and results in simpler diffs. -- cgit v1.2.3 From 1b7d853a09d97968af708c7ebe10ff26827090ed Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Fri, 30 Sep 2016 18:28:14 -0500 Subject: replace the reloader with the hupper package --- pyramid/scripts/pserve.py | 451 +----------------------------- pyramid/tests/test_scripts/test_pserve.py | 70 ----- setup.py | 1 + 3 files changed, 11 insertions(+), 511 deletions(-) diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py index 0d22c9f3f..eb0c0b126 100644 --- a/pyramid/scripts/pserve.py +++ b/pyramid/scripts/pserve.py @@ -8,50 +8,21 @@ # Code taken also from QP: http://www.mems-exchange.org/software/qp/ From # lib/site.py -import atexit -import ctypes import optparse import os -import py_compile import re -import subprocess import sys -import tempfile import textwrap -import threading -import time -import traceback -import webbrowser +import hupper from paste.deploy import loadserver from paste.deploy import loadapp -from paste.deploy.loadwsgi import loadcontext, SERVER from pyramid.compat import PY2 -from pyramid.compat import WIN from pyramid.scripts.common import parse_vars from pyramid.scripts.common import setup_logging -MAXFD = 1024 - -try: - import termios -except ImportError: # pragma: no cover - termios = None - -if WIN and not hasattr(os, 'kill'): # pragma: no cover - # py 2.6 on windows - def kill(pid, sig=None): - """kill function for Win32""" - # signal is ignored, semibogus raise message - kernel32 = ctypes.windll.kernel32 - handle = kernel32.OpenProcess(1, 0, pid) - if (0 == kernel32.TerminateProcess(handle, 0)): - raise OSError('No such process %s' % pid) -else: - kill = os.kill - def main(argv=sys.argv, quiet=False): command = PServeCommand(argv, quiet=quiet) return command.run() @@ -119,9 +90,6 @@ class PServeCommand(object): _scheme_re = re.compile(r'^[a-z][a-z]+:', re.I) - _reloader_environ_key = 'PYTHON_RELOADER_SHOULD_RUN' - _monitor_environ_key = 'PASTE_MONITOR_SHOULD_RUN' - def __init__(self, argv, quiet=False): self.options, self.args = self.parser.parse_args(argv[1:]) if quiet: @@ -141,15 +109,15 @@ class PServeCommand(object): return 2 app_spec = self.args[0] - if self.options.reload: - if os.environ.get(self._reloader_environ_key): - if self.options.verbose > 1: - self.out('Running reloading file monitor') - install_reloader(int(self.options.reload_interval), [app_spec]) - # if self.requires_config_file: - # watch_file(self.args[0]) - else: - return self.restart_with_reloader() + if self.options.reload and not hupper.is_active(): + if self.options.verbose > 1: + self.out('Running reloading file monitor') + hupper.start_reloader( + 'pyramid.scripts.pserve.main', + reload_interval=int(self.options.reload_interval), + verbose=self.options.verbose, + ) + return 0 app_name = self.options.app_name @@ -200,17 +168,6 @@ class PServeCommand(object): msg = '' self.out('Exiting%s (-v to see traceback)' % msg) - if self.options.browser: - def open_browser(): - context = loadcontext(SERVER, app_spec, name=server_name, relative_to=base, - global_conf=vars) - url = 'http://127.0.0.1:{port}/'.format(**context.config()) - time.sleep(1) - webbrowser.open(url) - t = threading.Thread(target=open_browser) - t.setDaemon(True) - t.start() - serve() def loadapp(self, app_spec, name, relative_to, **kw): # pragma: no cover @@ -220,394 +177,6 @@ class PServeCommand(object): return loadserver( server_spec, name=name, relative_to=relative_to, **kw) - def quote_first_command_arg(self, arg): # pragma: no cover - """ - There's a bug in Windows when running an executable that's - located inside a path with a space in it. This method handles - that case, or on non-Windows systems or an executable with no - spaces, it just leaves well enough alone. - """ - if (sys.platform != 'win32' or ' ' not in arg): - # Problem does not apply: - return arg - try: - import win32api - except ImportError: - raise ValueError( - "The executable %r contains a space, and in order to " - "handle this issue you must have the win32api module " - "installed" % arg) - arg = win32api.GetShortPathName(arg) - return arg - - def find_script_path(self, name): # pragma: no cover - """ - Return the path to the script being invoked by the python interpreter. - - There's an issue on Windows when running the executable from - a console_script causing the script name (sys.argv[0]) to - not end with .exe or .py and thus cannot be run via popen. - """ - if sys.platform == 'win32': - if not name.endswith('.exe') and not name.endswith('.py'): - name += '.exe' - return name - - def restart_with_reloader(self): # pragma: no cover - self.restart_with_monitor(reloader=True) - - def restart_with_monitor(self, reloader=False): # pragma: no cover - if self.options.verbose > 0: - if reloader: - self.out('Starting subprocess with file monitor') - else: - self.out('Starting subprocess with monitor parent') - while 1: - args = [ - self.quote_first_command_arg(sys.executable), - self.find_script_path(sys.argv[0]), - ] + sys.argv[1:] - new_environ = os.environ.copy() - if reloader: - new_environ[self._reloader_environ_key] = 'true' - else: - new_environ[self._monitor_environ_key] = 'true' - proc = None - try: - try: - _turn_sigterm_into_systemexit() - proc = subprocess.Popen(args, env=new_environ) - exit_code = proc.wait() - proc = None - except KeyboardInterrupt: - self.out('^C caught in monitor process') - if self.options.verbose > 1: - raise - return 1 - finally: - if proc is not None: - import signal - try: - kill(proc.pid, signal.SIGTERM) - except (OSError, IOError): - pass - - if reloader: - # Reloader always exits with code 3; but if we are - # a monitor, any exit code will restart - if exit_code != 3: - return exit_code - if self.options.verbose > 0: - self.out('%s %s %s' % ('-' * 20, 'Restarting', '-' * 20)) - -class LazyWriter(object): - - """ - File-like object that opens a file lazily when it is first written - to. - """ - - def __init__(self, filename, mode='w'): - self.filename = filename - self.fileobj = None - self.lock = threading.Lock() - self.mode = mode - - def open(self): - if self.fileobj is None: - with self.lock: - self.fileobj = open(self.filename, self.mode) - return self.fileobj - - def close(self): - fileobj = self.fileobj - if fileobj is not None: - fileobj.close() - - def __del__(self): - self.close() - - def write(self, text): - fileobj = self.open() - fileobj.write(text) - fileobj.flush() - - def writelines(self, text): - fileobj = self.open() - fileobj.writelines(text) - fileobj.flush() - - def flush(self): - self.open().flush() - -def ensure_port_cleanup( - bound_addresses, maxtries=30, sleeptime=2): # pragma: no cover - """ - This makes sure any open ports are closed. - - Does this by connecting to them until they give connection - refused. Servers should call like:: - - ensure_port_cleanup([80, 443]) - """ - atexit.register(_cleanup_ports, bound_addresses, maxtries=maxtries, - sleeptime=sleeptime) - -def _cleanup_ports( - bound_addresses, maxtries=30, sleeptime=2): # pragma: no cover - # Wait for the server to bind to the port. - import socket - import errno - for bound_address in bound_addresses: - for attempt in range(maxtries): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - sock.connect(bound_address) - except socket.error as e: - if e.args[0] != errno.ECONNREFUSED: - raise - break - else: - time.sleep(sleeptime) - else: - raise SystemExit('Timeout waiting for port.') - sock.close() - -def _turn_sigterm_into_systemexit(): # pragma: no cover - """ - Attempts to turn a SIGTERM exception into a SystemExit exception. - """ - try: - import signal - except ImportError: - return - def handle_term(signo, frame): - raise SystemExit - signal.signal(signal.SIGTERM, handle_term) - -def ensure_echo_on(): # pragma: no cover - if termios: - fd = sys.stdin - if fd.isatty(): - attr_list = termios.tcgetattr(fd) - if not attr_list[3] & termios.ECHO: - attr_list[3] |= termios.ECHO - termios.tcsetattr(fd, termios.TCSANOW, attr_list) - -def install_reloader(poll_interval=1, extra_files=None): # pragma: no cover - """ - Install the reloading monitor. - - On some platforms server threads may not terminate when the main - thread does, causing ports to remain open/locked. - """ - ensure_echo_on() - mon = Monitor(poll_interval=poll_interval) - if extra_files is None: - extra_files = [] - mon.extra_files.extend(extra_files) - t = threading.Thread(target=mon.periodic_reload) - t.setDaemon(True) - t.start() - -class classinstancemethod(object): - """ - Acts like a class method when called from a class, like an - instance method when called by an instance. The method should - take two arguments, 'self' and 'cls'; one of these will be None - depending on how the method was called. - """ - - def __init__(self, func): - self.func = func - self.__doc__ = func.__doc__ - - def __get__(self, obj, type=None): - return _methodwrapper(self.func, obj=obj, type=type) - -class _methodwrapper(object): - - def __init__(self, func, obj, type): - self.func = func - self.obj = obj - self.type = type - - def __call__(self, *args, **kw): - assert 'self' not in kw and 'cls' not in kw, ( - "You cannot use 'self' or 'cls' arguments to a " - "classinstancemethod") - return self.func(*((self.obj, self.type) + args), **kw) - - -class Monitor(object): # pragma: no cover - """ - A file monitor and server restarter. - - Use this like: - - ..code-block:: Python - - install_reloader() - - Then make sure your server is installed with a shell script like:: - - err=3 - while test "$err" -eq 3 ; do - python server.py - err="$?" - done - - or is run from this .bat file (if you use Windows):: - - @echo off - :repeat - python server.py - if %errorlevel% == 3 goto repeat - - or run a monitoring process in Python (``pserve --reload`` does - this). - - Use the ``watch_file(filename)`` function to cause a reload/restart for - other non-Python files (e.g., configuration files). If you have - a dynamic set of files that grows over time you can use something like:: - - def watch_config_files(): - return CONFIG_FILE_CACHE.keys() - add_file_callback(watch_config_files) - - Then every time the reloader polls files it will call - ``watch_config_files`` and check all the filenames it returns. - """ - instances = [] - global_extra_files = [] - global_file_callbacks = [] - - def __init__(self, poll_interval): - self.module_mtimes = {} - self.keep_running = True - self.poll_interval = poll_interval - self.extra_files = list(self.global_extra_files) - self.instances.append(self) - self.syntax_error_files = set() - self.pending_reload = False - self.file_callbacks = list(self.global_file_callbacks) - temp_pyc_fp = tempfile.NamedTemporaryFile(delete=False) - self.temp_pyc = temp_pyc_fp.name - temp_pyc_fp.close() - - def _exit(self): - try: - os.unlink(self.temp_pyc) - except IOError: - # not worried if the tempfile can't be removed - pass - # use os._exit() here and not sys.exit() since within a - # thread sys.exit() just closes the given thread and - # won't kill the process; note os._exit does not call - # any atexit callbacks, nor does it do finally blocks, - # flush open files, etc. In otherwords, it is rude. - os._exit(3) - - def periodic_reload(self): - while True: - if not self.check_reload(): - self._exit() - break - time.sleep(self.poll_interval) - - def check_reload(self): - filenames = list(self.extra_files) - for file_callback in self.file_callbacks: - try: - filenames.extend(file_callback()) - except: - print( - "Error calling reloader callback %r:" % file_callback) - traceback.print_exc() - for module in list(sys.modules.values()): - try: - filename = module.__file__ - except (AttributeError, ImportError): - continue - if filename is not None: - filenames.append(filename) - new_changes = False - for filename in filenames: - try: - stat = os.stat(filename) - if stat: - mtime = stat.st_mtime - else: - mtime = 0 - except (OSError, IOError): - continue - if filename.endswith('.pyc') and os.path.exists(filename[:-1]): - mtime = max(os.stat(filename[:-1]).st_mtime, mtime) - pyc = True - else: - pyc = False - old_mtime = self.module_mtimes.get(filename) - self.module_mtimes[filename] = mtime - if old_mtime is not None and old_mtime < mtime: - new_changes = True - if pyc: - filename = filename[:-1] - is_valid = True - if filename.endswith('.py'): - is_valid = self.check_syntax(filename) - if is_valid: - print("%s changed ..." % filename) - if new_changes: - self.pending_reload = True - if self.syntax_error_files: - for filename in sorted(self.syntax_error_files): - print("%s has a SyntaxError; NOT reloading." % filename) - if self.pending_reload and not self.syntax_error_files: - self.pending_reload = False - return False - return True - - def check_syntax(self, filename): - # check if a file has syntax errors. - # If so, track it until it's fixed. - try: - py_compile.compile(filename, cfile=self.temp_pyc, doraise=True) - except py_compile.PyCompileError as ex: - print(ex.msg) - self.syntax_error_files.add(filename) - return False - else: - if filename in self.syntax_error_files: - self.syntax_error_files.remove(filename) - return True - - def watch_file(self, cls, filename): - """Watch the named file for changes""" - filename = os.path.abspath(filename) - if self is None: - for instance in cls.instances: - instance.watch_file(filename) - cls.global_extra_files.append(filename) - else: - self.extra_files.append(filename) - - watch_file = classinstancemethod(watch_file) - - def add_file_callback(self, cls, callback): - """Add a callback -- a function that takes no parameters -- that will - return a list of filenames to watch for changes.""" - if self is None: - for instance in cls.instances: - instance.add_file_callback(callback) - cls.global_file_callbacks.append(callback) - else: - self.file_callbacks.append(callback) - - add_file_callback = classinstancemethod(add_file_callback) - -watch_file = Monitor.watch_file -add_file_callback = Monitor.add_file_callback - # For paste.deploy server instantiation (egg:pyramid#wsgiref) def wsgiref_server_runner(wsgi_app, global_conf, **kw): # pragma: no cover from wsgiref.simple_server import make_server diff --git a/pyramid/tests/test_scripts/test_pserve.py b/pyramid/tests/test_scripts/test_pserve.py index bf4763602..e84de92d4 100644 --- a/pyramid/tests/test_scripts/test_pserve.py +++ b/pyramid/tests/test_scripts/test_pserve.py @@ -1,5 +1,3 @@ -import os -import tempfile import unittest class TestPServeCommand(unittest.TestCase): @@ -69,71 +67,3 @@ class Test_main(unittest.TestCase): def test_it(self): result = self._callFUT(['pserve']) self.assertEqual(result, 2) - -class TestLazyWriter(unittest.TestCase): - def _makeOne(self, filename, mode='w'): - from pyramid.scripts.pserve import LazyWriter - return LazyWriter(filename, mode) - - def test_open(self): - filename = tempfile.mktemp() - try: - inst = self._makeOne(filename) - fp = inst.open() - self.assertEqual(fp.name, filename) - finally: - fp.close() - os.remove(filename) - - def test_write(self): - filename = tempfile.mktemp() - try: - inst = self._makeOne(filename) - inst.write('hello') - finally: - with open(filename) as f: - data = f.read() - self.assertEqual(data, 'hello') - inst.close() - os.remove(filename) - - def test_writeline(self): - filename = tempfile.mktemp() - try: - inst = self._makeOne(filename) - inst.writelines('hello') - finally: - with open(filename) as f: - data = f.read() - self.assertEqual(data, 'hello') - inst.close() - os.remove(filename) - - def test_flush(self): - filename = tempfile.mktemp() - try: - inst = self._makeOne(filename) - inst.flush() - fp = inst.fileobj - self.assertEqual(fp.name, filename) - finally: - fp.close() - os.remove(filename) - -class Test__methodwrapper(unittest.TestCase): - def _makeOne(self, func, obj, type): - from pyramid.scripts.pserve import _methodwrapper - return _methodwrapper(func, obj, type) - - def test___call__succeed(self): - def foo(self, cls, a=1): return 1 - class Bar(object): pass - wrapper = self._makeOne(foo, Bar, None) - result = wrapper(a=1) - self.assertEqual(result, 1) - - def test___call__fail(self): - def foo(self, cls, a=1): return 1 - class Bar(object): pass - wrapper = self._makeOne(foo, Bar, None) - self.assertRaises(AssertionError, wrapper, cls=1) diff --git a/setup.py b/setup.py index f738ee623..36615f36b 100644 --- a/setup.py +++ b/setup.py @@ -46,6 +46,7 @@ install_requires = [ 'venusian >= 1.0a3', # ``ignore`` 'translationstring >= 0.4', # py3 compat 'PasteDeploy >= 1.5.0', # py3 compat + 'hupper', ] tests_require = [ -- cgit v1.2.3 From 38fc72ca66f718414b66782518c3698d7a5d8f2f Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Fri, 4 Nov 2016 02:20:53 -0500 Subject: restore --browser support and watch the ini file --- pyramid/scripts/pserve.py | 58 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py index eb0c0b126..969bc07f1 100644 --- a/pyramid/scripts/pserve.py +++ b/pyramid/scripts/pserve.py @@ -13,10 +13,19 @@ import os import re import sys import textwrap +import threading +import time +import webbrowser import hupper -from paste.deploy import loadserver -from paste.deploy import loadapp +from paste.deploy import ( + loadapp, + loadserver, +) +from paste.deploy.loadwsgi import ( + SERVER, + loadcontext, +) from pyramid.compat import PY2 @@ -109,19 +118,8 @@ class PServeCommand(object): return 2 app_spec = self.args[0] - if self.options.reload and not hupper.is_active(): - if self.options.verbose > 1: - self.out('Running reloading file monitor') - hupper.start_reloader( - 'pyramid.scripts.pserve.main', - reload_interval=int(self.options.reload_interval), - verbose=self.options.verbose, - ) - return 0 - - app_name = self.options.app_name - vars = self.get_options() + app_name = self.options.app_name if not self._scheme_re.search(app_spec): app_spec = 'config:' + app_spec @@ -134,6 +132,34 @@ class PServeCommand(object): server_spec = app_spec base = os.getcwd() + # do not open the browser on each reload so check hupper first + if self.options.browser and not hupper.is_active(): + def open_browser(): + context = loadcontext( + SERVER, app_spec, name=server_name, relative_to=base, + global_conf=vars) + url = 'http://127.0.0.1:{port}/'.format(**context.config()) + time.sleep(1) + webbrowser.open(url) + t = threading.Thread(target=open_browser) + t.setDaemon(True) + t.start() + + if self.options.reload and not hupper.is_active(): + if self.options.verbose > 1: + self.out('Running reloading file monitor') + hupper.start_reloader( + 'pyramid.scripts.pserve.main', + reload_interval=int(self.options.reload_interval), + verbose=self.options.verbose, + ) + return 0 + + if hupper.is_active(): + reloader = hupper.get_reloader() + if app_spec.startswith('config:'): + reloader.watch_files([app_spec[len('config:'):]]) + log_fn = app_spec if log_fn.startswith('config:'): log_fn = app_spec[len('config:'):] @@ -146,8 +172,8 @@ class PServeCommand(object): server = self.loadserver(server_spec, name=server_name, relative_to=base, global_conf=vars) - app = self.loadapp(app_spec, name=app_name, relative_to=base, - global_conf=vars) + app = self.loadapp( + app_spec, name=app_name, relative_to=base, global_conf=vars) if self.options.verbose > 0: if hasattr(os, 'getpid'): -- cgit v1.2.3 From 1644efffa25dcc70c9e01f709fbd203351e2f6ba Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Nov 2016 15:49:19 -0600 Subject: link to documentation on hupper fixes #2806 --- docs/narr/project.rst | 20 ++++++++++++++++++++ docs/tutorials/wiki/installation.rst | 3 ++- docs/tutorials/wiki2/installation.rst | 3 ++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 71bd176f6..6c42881f4 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -1045,3 +1045,23 @@ Another good production alternative is :term:`Green Unicorn` (aka mod_wsgi, although it depends, in its default configuration, on having a buffering HTTP proxy in front of it. It does not, as of this writing, work on Windows. + +Automatically Reloading Your Code +--------------------------------- + +During development, it can be really useful to automatically have the +webserver restart when you make changes. ``pserve`` has a ``--reload`` switch +to enable this. It uses the +`hupper ` package +to enable this behavior. When your code crashes, ``hupper`` will wait for +another change or the ``SIGHUP`` signal before restarting again. + +inotify support +~~~~~~~~~~~~~~~ + +By default, ``hupper`` will poll the filesystem for changes to all python +code. This can be pretty inefficient in larger projects. To be nicer to your +hard drive, you should install the +`watchdog ` package in development. +``hupper`` will automatically use ``watchdog`` to more efficiently poll the +filesystem. diff --git a/docs/tutorials/wiki/installation.rst b/docs/tutorials/wiki/installation.rst index 6172b122b..03e183739 100644 --- a/docs/tutorials/wiki/installation.rst +++ b/docs/tutorials/wiki/installation.rst @@ -370,7 +370,8 @@ coverage. Start the application --------------------- -Start the application. +Start the application. See :ref:`what_is_this_pserve_thing` for more +information on ``pserve``. On UNIX ^^^^^^^ diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst index 0440c2d1d..75d5d4abd 100644 --- a/docs/tutorials/wiki2/installation.rst +++ b/docs/tutorials/wiki2/installation.rst @@ -457,7 +457,8 @@ working directory. This is an SQLite database with a single table defined in it Start the application --------------------- -Start the application. +Start the application. See :ref:`what_is_this_pserve_thing` for more +information on ``pserve``. On UNIX ^^^^^^^ -- cgit v1.2.3 From 067ce0528a4c108e502c968b0865d213dbdda271 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 19 Nov 2016 15:54:49 -0600 Subject: add changes for #2805 --- CHANGES.txt | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index a0a928f83..dac61678d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -93,6 +93,29 @@ Features using the ``PYRAMID_CSRF_TRUSTED_ORIGINS`` environment variable similar to other settings. See https://github.com/Pylons/pyramid/pull/2823 +- ``pserve --reload`` now uses the + `hupper ` + library to monitor file changes. This comes with many improvements: + + - If the `watchdog `_ package is + installed then monitoring will be done using inotify instead of + cpu and disk-intensive polling. + + - The monitor is now a separate process that will not crash and starts up + before any of your code. + + - The monitor will not restart the process after a crash until a file is + saved. + + - The monitor works on windows. + + - You can now trigger a reload manually from a pyramid view or any other + code via ``hupper.get_reloader().trigger_reload()``. Kind of neat. + + - You can trigger a reload by issuing a ``SIGHUP`` to the monitor process. + + See https://github.com/Pylons/pyramid/pull/2805 + Bug Fixes --------- -- cgit v1.2.3 From f69e9035d16110d67de5b5a5c0c94bde5bd6b359 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 20 Nov 2016 15:32:42 -0800 Subject: Add Page structure section - add description - add introduction - add headings - add grammar, spelling, capitalization preferences --- docs/style-guide.rst | 87 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 6 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 5946314e3..3f2b630d3 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -3,7 +3,17 @@ Style Guide =========== -This chapter describes how to edit, update, and build the :app:`Pyramid` documentation. +.. admonition:: description + + This chapter describes how to edit, update, and build the :app:`Pyramid` documentation. + + +.. _style-guide-introduction: + +Introduction +------------ + +This chapter provides details of how to contribute updates to the documentation following style guidelines and conventions. We provide examples, including reStructuredText code and its rendered output for both visual and technical reference. .. _style-guide-contribute: @@ -37,6 +47,43 @@ Location, referencing, and naming of files will select the image ``pyramid_request_processing.svg`` for the HTML documentation builder, and ``pyramid_request_processing.png`` for the PDF builder. See the related [Stack Overflow post](http://stackoverflow.com/questions/6473660/using-sphinx-docs-how-can-i-specify-png-image-formats-for-html-builds-and-pdf-im/6486713#6486713). +.. _style-guide-page-structure: + +Page structure +-------------- + +Each page should contain in order the following. + +- The main heading. This will be visible in the table of contents. + + .. code-block:: rst + + ================ + The main heading + ================ + +- The description of the page. This text will be displayed to the reader below the main heading as well as be inserted into the description metadata field of the document. It will be displayed in search engine listings for the page. This is created using the reST ``admonition`` directive. A single paragraph of text consisting of no more than three sentences is recommended, so that the same text fits into search engine results: + + .. code-block:: rst + + .. admonition:: description + + This is a description of the page, which will appear inline and in the description metadata field. + + .. note:: The ``description`` metadata field is not yet implemented in the documentation's Sphinx theme, but it is a `feature request `_, so it is helpful to start using the ``description`` admonition now. + +- Introduction paragraph. + + .. code-block:: rst + + Introduction + ------------ + + This chapter is an introduction. + +- Finally the content of the document page, consisting of reST elements such as headings, paragraphs, tables, and so on. + + .. _style-guide-line-lengths: Line lengths @@ -54,6 +101,23 @@ Trailing white spaces - Always use a line feed or carriage return at the end of a file. +.. _style-guide-indentation: + +Indentation +----------- + +- Indent using four spaces. +- Do not use tabs to indent. + + +.. _style-guide-headings: + +Headings +-------- + +Capitalize only the first letter in a heading, unless other words are proper nouns or acronyms, e.g., "Pyramid" or "HTML". + + .. _style-guide-paragraphs: Paragraphs @@ -62,14 +126,25 @@ Paragraphs A paragraph should be on one line. Paragraphs must be separated by two line feeds. -.. _style-guide-indentation: +.. _style-guide-grammar-spelling-preferences: -Indentation ------------ +Grammar, spelling, and capitalization preferences +------------------------------------------------- -- Indent using four spaces. -- Do not use tabs to indent. +Use any commercial or free professional style guide in general. Use a spell- and grammar-checker. The following table lists the preferred grammar, spelling, and capitalization of words and phrases for frequently used items in the documentation. +========== ====================== +Preferred Avoid +========== ====================== +add-on addon +and so on etc. +GitHub Github, github +JavaScript Javascript, javascript +plug-in plugin +select check, tick (checkbox) +such as like +verify be sure +========== ====================== -- cgit v1.2.3 From 9089d33cfa59d84cf76b9f52f8bf181c98c6a121 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 20 Nov 2016 18:54:52 -0600 Subject: cache pip artifacts --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index e10dbc580..5691130eb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,6 +5,9 @@ environment: - PYTHON: "C:\\Python27" TOXENV: "py27" +cache: + - '%LOCALAPPDATA%\pip\Cache' + install: - "%PYTHON%\\python.exe -m pip install tox" -- cgit v1.2.3 From 52bfe0a8f2f6d305f0e3d8cead290427fd71e926 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 20 Nov 2016 19:37:24 -0600 Subject: automate build names for appveyor --- appveyor.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 5691130eb..4c684bfc6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,6 +8,8 @@ environment: cache: - '%LOCALAPPDATA%\pip\Cache' +version: '{branch}.{build}' + install: - "%PYTHON%\\python.exe -m pip install tox" -- cgit v1.2.3 From 7285651ecf3458c61158b8fcf7dbd269f067ffaf Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 20 Nov 2016 21:39:35 -0800 Subject: Add section structure --- docs/style-guide.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 3f2b630d3..49c9d5950 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -47,6 +47,26 @@ Location, referencing, and naming of files will select the image ``pyramid_request_processing.svg`` for the HTML documentation builder, and ``pyramid_request_processing.png`` for the PDF builder. See the related [Stack Overflow post](http://stackoverflow.com/questions/6473660/using-sphinx-docs-how-can-i-specify-png-image-formats-for-html-builds-and-pdf-im/6486713#6486713). +.. _style-guide-section-structure: + +Section structure +----------------- + +Each section, or a subdirectory of reST files, such as a tutorial, must contain an ``index.rst`` file. ``index.rst`` must contain the following. + +- A section heading. This will be visible in the table of contents. +- A single paragraph describing this section. +- A Sphinx ``toctree`` directive, with a ``maxdepth`` of 2. Each ``.rst`` file in the folder should be linked to this ``toctree``. + + .. code-block:: rst + + .. toctree:: + :maxdepth: 2 + + chapter1 + chapter2 + chapter3 + .. _style-guide-page-structure: Page structure -- cgit v1.2.3 From 2954362448347a6a3efd55222a50ece8e2fae6a0 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 20 Nov 2016 21:53:39 -0800 Subject: update headings --- docs/style-guide.rst | 45 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 49c9d5950..4a17e1d2b 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -67,6 +67,7 @@ Each section, or a subdirectory of reST files, such as a tutorial, must contain chapter2 chapter3 + .. _style-guide-page-structure: Page structure @@ -104,10 +105,18 @@ Each page should contain in order the following. - Finally the content of the document page, consisting of reST elements such as headings, paragraphs, tables, and so on. +.. _style-guide-page-content: + +Page content +------------ + +Within a page, content should adhere to specific guidelines. + + .. _style-guide-line-lengths: Line lengths ------------- +^^^^^^^^^^^^ Narrative documentation is not code, and should therefore not adhere to PEP8 or other line length conventions. When a translator sees only part of a sentence or paragraph, it makes it more difficult to translate the concept. Line lengths make ``diff`` more difficult. Text editors can soft wrap lines for display to avoid horizontal scrolling. We admit, we boofed it by using arbitrary 79-character line lengths in our own documentation, but we have seen the error of our ways and wish to correct this going forward. @@ -115,7 +124,7 @@ Narrative documentation is not code, and should therefore not adhere to PEP8 or .. _style-guide-trailing-white-space: Trailing white spaces ---------------------- +^^^^^^^^^^^^^^^^^^^^^ - No trailing white spaces. - Always use a line feed or carriage return at the end of a file. @@ -124,7 +133,7 @@ Trailing white spaces .. _style-guide-indentation: Indentation ------------ +^^^^^^^^^^^ - Indent using four spaces. - Do not use tabs to indent. @@ -133,15 +142,37 @@ Indentation .. _style-guide-headings: Headings --------- +^^^^^^^^ + +Capitalize only the first letter in a heading (sentence-case), unless other words are proper nouns or acronyms, e.g., "Pyramid" or "HTML". + +For consistent heading characters throughout the documentation, follow the guidelines stated in the `Python Developer's Guide `_. Specifically: + +- =, for sections +- -, for subsections +- ^, for subsubsections +- ", for paragraphs + +As individual files do not have so-called "parts" or "chapters", the headings would be underlined with characters as shown. + + .. code-block:: rst + + Heading Level 1 + =============== + + Heading Level 2 + --------------- -Capitalize only the first letter in a heading, unless other words are proper nouns or acronyms, e.g., "Pyramid" or "HTML". + Heading Level 3 + ^^^^^^^^^^^^^^^ + Heading Level 4 + ``````````````` .. _style-guide-paragraphs: Paragraphs ----------- +^^^^^^^^^^ A paragraph should be on one line. Paragraphs must be separated by two line feeds. @@ -149,7 +180,7 @@ A paragraph should be on one line. Paragraphs must be separated by two line feed .. _style-guide-grammar-spelling-preferences: Grammar, spelling, and capitalization preferences -------------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use any commercial or free professional style guide in general. Use a spell- and grammar-checker. The following table lists the preferred grammar, spelling, and capitalization of words and phrases for frequently used items in the documentation. -- cgit v1.2.3 From c13e6cc241d1f52382435a56a18e791b6b9bbe1a Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 20 Nov 2016 21:58:43 -0800 Subject: add links --- docs/style-guide.rst | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 4a17e1d2b..911949862 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -139,6 +139,27 @@ Indentation - Do not use tabs to indent. +.. _style-guide-grammar-spelling-preferences: + +Grammar, spelling, and capitalization preferences +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Use any commercial or free professional style guide in general. Use a spell- and grammar-checker. The following table lists the preferred grammar, spelling, and capitalization of words and phrases for frequently used items in the documentation. + +========== ====================== +Preferred Avoid +========== ====================== +add-on addon +and so on etc. +GitHub Github, github +JavaScript Javascript, javascript +plug-in plugin +select check, tick (checkbox) +such as like +verify be sure +========== ====================== + + .. _style-guide-headings: Headings @@ -177,25 +198,14 @@ Paragraphs A paragraph should be on one line. Paragraphs must be separated by two line feeds. -.. _style-guide-grammar-spelling-preferences: +Links +^^^^^ -Grammar, spelling, and capitalization preferences -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use inline links to keep the context or link label together with the URL. Do not use targets and links at the end of the page, because the separation makes it difficult to update and translate. Here is an example of inline links, our required method. -Use any commercial or free professional style guide in general. Use a spell- and grammar-checker. The following table lists the preferred grammar, spelling, and capitalization of words and phrases for frequently used items in the documentation. + .. code-block:: rst -========== ====================== -Preferred Avoid -========== ====================== -add-on addon -and so on etc. -GitHub Github, github -JavaScript Javascript, javascript -plug-in plugin -select check, tick (checkbox) -such as like -verify be sure -========== ====================== + `Example `_ -- cgit v1.2.3 From e33b2ce88af4fb6d9412757ca0ac4f6a95bfc578 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 20 Nov 2016 22:01:15 -0800 Subject: add topic --- docs/style-guide.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 911949862..2304e9d6b 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -198,6 +198,8 @@ Paragraphs A paragraph should be on one line. Paragraphs must be separated by two line feeds. +.. _style-guide-links: + Links ^^^^^ @@ -208,7 +210,22 @@ Use inline links to keep the context or link label together with the URL. Do not `Example `_ +.. _style-guide-topic: + +Topic +^^^^^ + +A topic is similar to a block quote with a title, or a self-contained section with no subsections. Use the ``topic`` directive to indicate a self-contained idea that is separate from the flow of the document. Topics may occur anywhere a section or transition may occur. Body elements and topics may not contain nested topics. + +The directive's sole argument is interpreted as the topic title, and next line must be blank. All subsequent lines make up the topic body, interpreted as body elements. + + .. code-block:: rst + + .. topic:: Topic Title + Subsequent indented lines comprise + the body of the topic, and are + interpreted as body elements. Literals, filenames, and function arguments are presented using the -- cgit v1.2.3 From ff8acd6f545ca55f9c9588ee77aee559d68ab194 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sun, 20 Nov 2016 22:15:19 -0800 Subject: add syntax highlighting --- docs/style-guide.rst | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 2304e9d6b..cb1a15be1 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -228,6 +228,96 @@ The directive's sole argument is interpreted as the topic title, and next line m interpreted as body elements. +.. _style-guide-syntax-highlighting: + +Syntax highlighting +^^^^^^^^^^^^^^^^^^^ + +Sphinx does syntax highlighting using the `Pygments `_ library. + +Do not use two colons "::" at the end of a line, followed by a blank line, then indented code. Always specify the language to be used for syntax highlighting by using the ``code-block`` directive and indenting the code. + + .. code-block:: rst + + .. code-block:: python + + if "foo" == "bar": + # This is Python code + pass + +XML: + + .. code-block:: rst + + .. code-block:: xml + + Some XML + +Unix shell: + + .. code-block:: rst + + .. code-block:: bash + + # Start Plone in foreground mode for a test run + cd ~/Plone/zinstance + bin/plonectl fg + +Windows console: + + .. code-block:: rst + + .. code-block:: doscon + + c:\> %VENV%\Scripts\pcreate -s starter MyProject + +cfg: + + .. code-block:: rst + + .. code-block:: cfg + + [some-part] + # A random part in the buildout + recipe = collective.recipe.foo + option = value + +ini: + + .. code-block:: rst + + .. code-block:: ini + + [nosetests] + match=^test + where=pyramid + nocapture=1 + +Interactive Python: + + .. code-block:: rst + + .. code-block:: pycon + + >>> class Foo: + ... bar = 100 + ... + >>> f = Foo() + >>> f.bar + 100 + >>> f.bar / 0 + Traceback (most recent call last): + File "", line 1, in + ZeroDivisionError: integer division or modulo by zero + +If syntax highlighting is not enabled for your code block, you probably have a syntax error and Pygments will fail silently. + +View the `full list of lexers and associated short names `_. + + + + + Literals, filenames, and function arguments are presented using the following style: -- cgit v1.2.3 From 3f130937bcce073e9933e28c332e59c559025e07 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 21 Nov 2016 00:14:26 -0600 Subject: support a [pserve] config section with a list of files to watch fixes #2732 --- docs/narr/project.rst | 20 +++++++++++ pyramid/scripts/pserve.py | 58 ++++++++++++++++++++----------- pyramid/tests/test_scripts/test_pserve.py | 24 ++++++++----- 3 files changed, 73 insertions(+), 29 deletions(-) diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 6c42881f4..b4ad6948e 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -1065,3 +1065,23 @@ hard drive, you should install the `watchdog ` package in development. ``hupper`` will automatically use ``watchdog`` to more efficiently poll the filesystem. + +Monitoring Custom Files +~~~~~~~~~~~~~~~~~~~~~~~ + +By default, ``pserve --reload`` will monitor all imported Python code +(everything in ``sys.modules``) as well as the config file passed to +``pserve`` (e.g. ``development.ini``). You can instruct ``pserve`` to watch +other files for changes as well by defining a ``[pserve]`` section in your +configuration file. For example, let's say your application loads the +``favicon.ico`` file at startup and stores it in memory to efficiently +serve it many times. When you change it you want ``pserve`` to restart: + +.. code-block:: ini + + [pserve] + watch_files = + myapp/static/favicon.ico + +Paths are relative to the configuration file and are passed to ``hupper`` +to watch. Currently it does not support globs but this may change. diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py index 969bc07f1..c67dc86ba 100644 --- a/pyramid/scripts/pserve.py +++ b/pyramid/scripts/pserve.py @@ -28,9 +28,11 @@ from paste.deploy.loadwsgi import ( ) from pyramid.compat import PY2 +from pyramid.compat import configparser from pyramid.scripts.common import parse_vars from pyramid.scripts.common import setup_logging +from pyramid.settings import aslist def main(argv=sys.argv, quiet=False): command = PServeCommand(argv, quiet=quiet) @@ -97,12 +99,17 @@ class PServeCommand(object): dest='verbose', help="Suppress verbose output") + ConfigParser = configparser.ConfigParser # testing + loadapp = staticmethod(loadapp) # testing + loadserver = staticmethod(loadserver) # testing + _scheme_re = re.compile(r'^[a-z][a-z]+:', re.I) def __init__(self, argv, quiet=False): self.options, self.args = self.parser.parse_args(argv[1:]) if quiet: self.options.verbose = 0 + self.watch_files = [] def out(self, msg): # pragma: no cover if self.options.verbose > 0: @@ -112,6 +119,24 @@ class PServeCommand(object): restvars = self.args[1:] return parse_vars(restvars) + def pserve_file_config(self, filename): + config = self.ConfigParser() + config.optionxform = str + config.read(filename) + try: + items = dict(config.items('pserve')) + except configparser.NoSectionError: + return + + watch_files = aslist(items.get('watch_files', ''), flatten=False) + + # track file paths relative to the ini file + basedir = os.path.dirname(filename) + for file in watch_files: + if not os.path.isabs(file): + file = os.path.join(basedir, file) + self.watch_files.append(os.path.normpath(file)) + def run(self): # pragma: no cover if not self.args: self.out('You must give a config file') @@ -121,8 +146,12 @@ class PServeCommand(object): vars = self.get_options() app_name = self.options.app_name + base = os.getcwd() if not self._scheme_re.search(app_spec): + config_path = os.path.join(base, app_spec) app_spec = 'config:' + app_spec + else: + config_path = None server_name = self.options.server_name if self.options.server: server_spec = 'egg:pyramid' @@ -130,7 +159,6 @@ class PServeCommand(object): server_name = self.options.server else: server_spec = app_spec - base = os.getcwd() # do not open the browser on each reload so check hupper first if self.options.browser and not hupper.is_active(): @@ -155,22 +183,17 @@ class PServeCommand(object): ) return 0 + if config_path: + setup_logging(config_path, global_conf=vars) + self.watch_files.append(config_path) + self.pserve_file_config(config_path) + if hupper.is_active(): reloader = hupper.get_reloader() - if app_spec.startswith('config:'): - reloader.watch_files([app_spec[len('config:'):]]) - - log_fn = app_spec - if log_fn.startswith('config:'): - log_fn = app_spec[len('config:'):] - elif log_fn.startswith('egg:'): - log_fn = None - if log_fn: - log_fn = os.path.join(base, log_fn) - setup_logging(log_fn, global_conf=vars) + reloader.watch_files(self.watch_files) - server = self.loadserver(server_spec, name=server_name, - relative_to=base, global_conf=vars) + server = self.loadserver( + server_spec, name=server_name, relative_to=base, global_conf=vars) app = self.loadapp( app_spec, name=app_name, relative_to=base, global_conf=vars) @@ -196,13 +219,6 @@ class PServeCommand(object): serve() - def loadapp(self, app_spec, name, relative_to, **kw): # pragma: no cover - return loadapp(app_spec, name=name, relative_to=relative_to, **kw) - - def loadserver(self, server_spec, name, relative_to, **kw):# pragma:no cover - return loadserver( - server_spec, name=name, relative_to=relative_to, **kw) - # For paste.deploy server instantiation (egg:pyramid#wsgiref) def wsgiref_server_runner(wsgi_app, global_conf, **kw): # pragma: no cover from wsgiref.simple_server import make_server diff --git a/pyramid/tests/test_scripts/test_pserve.py b/pyramid/tests/test_scripts/test_pserve.py index e84de92d4..cf9426943 100644 --- a/pyramid/tests/test_scripts/test_pserve.py +++ b/pyramid/tests/test_scripts/test_pserve.py @@ -1,9 +1,12 @@ +import os import unittest +from pyramid.tests.test_scripts import dummy class TestPServeCommand(unittest.TestCase): def setUp(self): from pyramid.compat import NativeIO self.out_ = NativeIO() + self.config_factory = dummy.DummyConfigParserFactory() def out(self, msg): self.out_.write(msg) @@ -11,7 +14,6 @@ class TestPServeCommand(unittest.TestCase): def _get_server(*args, **kwargs): def server(app): return '' - return server def _getTargetClass(self): @@ -23,6 +25,7 @@ class TestPServeCommand(unittest.TestCase): effargs.extend(args) cmd = self._getTargetClass()(effargs) cmd.out = self.out + cmd.ConfigParser = self.config_factory return cmd def test_run_no_args(self): @@ -38,20 +41,15 @@ class TestPServeCommand(unittest.TestCase): self.assertEqual(result, {'a': '1', 'b': '2'}) def test_parse_vars_good(self): - from pyramid.tests.test_scripts.dummy import DummyApp - inst = self._makeOne('development.ini', 'a=1', 'b=2') inst.loadserver = self._get_server - - app = DummyApp() - + app = dummy.DummyApp() def get_app(*args, **kwargs): app.global_conf = kwargs.get('global_conf', None) - inst.loadapp = get_app - inst.run() + inst.run() self.assertEqual(app.global_conf, {'a': '1', 'b': '2'}) def test_parse_vars_bad(self): @@ -59,6 +57,16 @@ class TestPServeCommand(unittest.TestCase): inst.loadserver = self._get_server self.assertRaises(ValueError, inst.run) + def test_config_file_finds_watch_files(self): + inst = self._makeOne('development.ini') + self.config_factory.items = [('watch_files', 'foo\nbar\n/baz')] + inst.pserve_file_config('/base/path.ini') + self.assertEqual(inst.watch_files, [ + os.path.normpath('/base/foo'), + os.path.normpath('/base/bar'), + os.path.normpath('/baz'), + ]) + class Test_main(unittest.TestCase): def _callFUT(self, argv): from pyramid.scripts.pserve import main -- cgit v1.2.3 From abfb2f61458b111008fd1d9de1553380715889a3 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 21 Nov 2016 01:01:47 -0600 Subject: add changelog for #2827 --- CHANGES.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index dac61678d..0686e1ad1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -116,6 +116,10 @@ Features See https://github.com/Pylons/pyramid/pull/2805 +- A new ``[pserve]`` section is supported in your config files with a + ``watch_files`` key that can configure ``pserve --reload`` to monitor custom + file paths. See https://github.com/Pylons/pyramid/pull/2827 + Bug Fixes --------- -- cgit v1.2.3 From 90db2bc106822be4ba3ca92a51a932f17d5ce4f7 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 21 Nov 2016 02:09:01 -0800 Subject: add code block options - add literal includes - add displaying long commands - add sphinx to intersphinx - garden --- docs/conf.py | 1 + docs/style-guide.rst | 276 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 194 insertions(+), 83 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index c3a7170fc..ace921247 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -68,6 +68,7 @@ intersphinx_mapping = { 'pylonswebframework': ('http://docs.pylonsproject.org/projects/pylons-webframework/en/latest/', None), 'python': ('https://docs.python.org/3', None), 'pytest': ('http://pytest.org/latest/', None), + 'sphinx': ('http://www.sphinx-doc.org/en/latest', None), 'sqla': ('http://docs.sqlalchemy.org/en/latest', None), 'tm': ('http://docs.pylonsproject.org/projects/pyramid-tm/en/latest/', None), 'toolbar': ('http://docs.pylonsproject.org/projects/pyramid-debugtoolbar/en/latest', None), diff --git a/docs/style-guide.rst b/docs/style-guide.rst index cb1a15be1..c30b199a6 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -5,7 +5,7 @@ Style Guide .. admonition:: description - This chapter describes how to edit, update, and build the :app:`Pyramid` documentation. + This chapter describes how to edit, update, and build the :app:`Pyramid` documentation. For coding style guidelines, see `Coding Style `_. .. _style-guide-introduction: @@ -146,18 +146,18 @@ Grammar, spelling, and capitalization preferences Use any commercial or free professional style guide in general. Use a spell- and grammar-checker. The following table lists the preferred grammar, spelling, and capitalization of words and phrases for frequently used items in the documentation. -========== ====================== -Preferred Avoid -========== ====================== -add-on addon -and so on etc. -GitHub Github, github -JavaScript Javascript, javascript -plug-in plugin -select check, tick (checkbox) -such as like -verify be sure -========== ====================== +========== ====================== +Preferred Avoid +========== ====================== +add-on addon +and so on etc. +GitHub Github, github +JavaScript Javascript, javascript +plug-in plugin +select check, tick (checkbox) +such as like +verify be sure +========== ====================== .. _style-guide-headings: @@ -228,12 +228,22 @@ The directive's sole argument is interpreted as the topic title, and next line m interpreted as body elements. +.. _style-guide-displaying-code: + +Displaying code +^^^^^^^^^^^^^^^ + +Code may be displayed in blocks or inline. You can include blocks of code from other source files. Blocks of code should use syntax highlighting. + +.. seealso:: See also the Sphinx documentation for :ref:`Showing code examples `. + + .. _style-guide-syntax-highlighting: Syntax highlighting -^^^^^^^^^^^^^^^^^^^ +``````````````````` -Sphinx does syntax highlighting using the `Pygments `_ library. +Sphinx does syntax highlighting of code blocks using the `Pygments `_ library. Do not use two colons "::" at the end of a line, followed by a blank line, then indented code. Always specify the language to be used for syntax highlighting by using the ``code-block`` directive and indenting the code. @@ -253,17 +263,15 @@ XML: Some XML -Unix shell: +Unix shell commands are prefixed with a ``$`` character. (See :term:`venv` for the meaning of ``$VENV``.) .. code-block:: rst .. code-block:: bash - # Start Plone in foreground mode for a test run - cd ~/Plone/zinstance - bin/plonectl fg + $ $VENV/bin/pip install -e . -Windows console: +Windows commands are prefixed with a drive letter with an optional directory name. (See :term:`venv` for the meaning of ``%VENV%``.) .. code-block:: rst @@ -271,6 +279,8 @@ Windows console: c:\> %VENV%\Scripts\pcreate -s starter MyProject + .. code-block:: doscon + cfg: .. code-block:: rst @@ -315,110 +325,210 @@ If syntax highlighting is not enabled for your code block, you probably have a s View the `full list of lexers and associated short names `_. +.. _style-guide-long-commands: +Displaying long commands +```````````````````````` +When a command that should be typed on one line is too long to fit on the displayed width of a page, the backslash character ``\`` is used to indicate that the subsequent printed line should be part of the command: -Literals, filenames, and function arguments are presented using the -following style: + .. code-block:: rst - ``argument1`` + .. code-block:: bash -Warnings which represent limitations and need-to-know information -related to a topic or concept are presented in the following style: + $ $VENV/bin/py.test tutorial/tests.py --cov-report term-missing \ + --cov=tutorial -q - .. warning:: - This is a warning. +.. _style-guide-code-block-options: -Notes which represent additional information related to a topic or -concept are presented in the following style: +Code block options +`````````````````` - .. note:: +To emphasize lines (give the appearance that a highlighting pen has been used on the code), use the ``emphasize-lines`` option. The argument passed to ``emphasize-lines`` must be a comma-separated list of either single or ranges of line numbers. - This is a note. +.. code-block:: rst -We present Python method names using the following style: + .. code-block:: python + :emphasize-lines: 1,3 - :meth:`pyramid.config.Configurator.add_view` + if "foo" == "bar": + # This is Python code + pass -We present Python class names, module names, attributes, and global -variables using the following style: +The above code renders as follows. - :class:`pyramid.config.Configurator.registry` +.. code-block:: python + :emphasize-lines: 1,3 -References to glossary terms are presented using the following style: + if "foo" == "bar": + # This is Python code + pass - :term:`Pylons` +To display a code block with line numbers, use the ``linenos`` option. -URLs are presented using the following style: +.. code-block:: rst - `Pylons `_ + .. code-block:: python + :linenos: -References to sections and chapters are presented using the following -style: + if "foo" == "bar": + # This is Python code + pass - :ref:`traversal_chapter` +The above code renders as follows. -Code and configuration file blocks are presented in the following style: +.. code-block:: python + :linenos: - .. code-block:: python - :linenos: + if "foo" == "bar": + # This is Python code + pass - def foo(abc): - pass +Code blocks may be given a caption, which may serve as a filename or other description, using the ``caption`` option. They may also be given a ``name`` option, providing an implicit target name that can be referenced by using ``ref``. -Example blocks representing UNIX shell commands are prefixed with a ``$`` -character, e.g.: +.. code-block:: rst - .. code-block:: bash + .. code-block:: python + :caption: sample.py + :name: sample-py - $ $VENV/bin/py.test -q + if "foo" == "bar": + # This is Python code + pass -See :term:`venv` for the meaning of ``$VENV``. +The above code renders as follows. -Example blocks representing Windows commands are prefixed with a drive letter -with an optional directory name, e.g.: +.. code-block:: python + :caption: sample.py + :name: sample-py - .. code-block:: doscon + if "foo" == "bar": + # This is Python code + pass - c:\examples> %VENV%\Scripts\py.test -q +To specify the starting number to use for line numbering, use the ``lineno-start`` directive. -See :term:`venv` for the meaning of ``%VENV%``. +.. code-block:: rst -When a command that should be typed on one line is too long to fit on a page, -the backslash ``\`` is used to indicate that the following printed line should -be part of the command: + .. code-block:: python + :lineno-start: 2 - .. code-block:: bash + if "foo" == "bar": + # This is Python code + pass - $VENV/bin/py.test tutorial/tests.py --cov-report term-missing \ - --cov=tutorial -q +The above code renders as follows. As you can see, ``lineno-start`` is not altogether meaningful. -A sidebar, which presents a concept tangentially related to content discussed -on a page, is rendered like so: +.. code-block:: python + :lineno-start: 2 -.. sidebar:: This is a sidebar + if "foo" == "bar": + # This is Python code + pass - Sidebar information. -When multiple objects are imported from the same package, the following -convention is used: +.. _style-guide-includes: - .. code-block:: python +Includes +```````` + +Longer displays of verbatim text may be included by storing the example text in an external file containing only plain text or code. The file may be included using the ``literalinclude`` directive. The file name follows the conventions of :ref:`style-guide-file-conventions`. + +.. code-block:: rst + + .. literalinclude:: narr/helloworld.py + :language: python + +The above code renders as follows. + +.. literalinclude:: narr/helloworld.py + :language: python + +Like code blocks, ``literalinclude`` supports the following options. + +- ``language`` to select a language for syntax highlighting +- ``linenos`` to switch on line numbers +- ``lineno-start`` to specify the starting number to use for line numbering +- ``emphasize-lines`` to emphasize particular lines + +.. code-block:: rst + + .. literalinclude:: narr/helloworld.py + :language: python + :linenos: + :lineno-start: 11 + :emphasize-lines: 1,6-7,9- + +The above code renders as follows. Note that ``lineno-start`` and ``emphasize-lines`` do not align. The former displays numbering starting from the *arbitrarily provided value*, whereas the latter emphasizes the line numbers of the *source file*. - from foo import ( - bar, - baz, - ) +.. literalinclude:: narr/helloworld.py + :language: python + :linenos: + :lineno-start: 11 + :emphasize-lines: 1,6-7,9- -It may look unusual, but it has advantages: +Additional options include the following. -- It allows one to swap out the higher-level package ``foo`` for something else - that provides the similar API. An example would be swapping out one database - for another (e.g., graduating from SQLite to PostgreSQL). +.. literalinclude:: narr/helloworld.py + :lines: 1-3 + :emphasize-lines: 3 + :lineno-match: + :language: python -- Looks more neat in cases where a large number of objects get imported from - that package. +.. literalinclude:: narr/helloworld.py + :linenos: + :pyobject: hello_world -- Adding or removing imported objects from the package is quicker and results - in simpler diffs. +.. literalinclude:: quick_tour/sqla_demo/sqla_demo/models/mymodel.py + :language: python + :start-after: Start Sphinx Include + :end-before: End Sphinx Include + + +.. _style-guide-inline-code: + +Inline code +``````````` + + + + + + +Literals, filenames, and function arguments are presented using the +following style: + + ``argument1`` + +Warnings which represent limitations and need-to-know information +related to a topic or concept are presented in the following style: + + .. warning:: + + This is a warning. + +Notes which represent additional information related to a topic or +concept are presented in the following style: + + .. note:: + + This is a note. + +We present Python method names using the following style: + + :meth:`pyramid.config.Configurator.add_view` + +We present Python class names, module names, attributes, and global +variables using the following style: + + :class:`pyramid.config.Configurator.registry` + +References to glossary terms are presented using the following style: + + :term:`Pylons` + +References to sections and chapters are presented using the following +style: + + :ref:`traversal_chapter` -- cgit v1.2.3 From 60ff6ab9d2c1fcbbe97a39e8e9535d167605d6de Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 21 Nov 2016 04:15:54 -0800 Subject: add literalinclude options --- docs/style-guide.rst | 61 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 9 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index c30b199a6..7868f97e7 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -468,22 +468,65 @@ The above code renders as follows. Note that ``lineno-start`` and ``emphasize-li :lineno-start: 11 :emphasize-lines: 1,6-7,9- -Additional options include the following. +``literalinclude`` also supports including only parts of a file. If it is a Python module, you can select a class, function, or method to include using the ``pyobject`` option. + +.. code-block:: rst + + .. literalinclude:: narr/helloworld.py + :language: python + :pyobject: hello_world + +The above code renders as follows. It returns the function ``hello_world`` in the source file. .. literalinclude:: narr/helloworld.py - :lines: 1-3 - :emphasize-lines: 3 - :lineno-match: :language: python + :pyobject: hello_world + +Alternatively, you can specify exactly which lines to include by giving a ``lines`` option. + +.. code-block:: rst + + .. literalinclude:: narr/helloworld.py + :language: python + :lines: 6-7 + +The above code renders as follows. .. literalinclude:: narr/helloworld.py - :linenos: - :pyobject: hello_world + :language: python + :lines: 6-7 + +Another way to control which part of the file is included is to use the ``start-after`` and ``end-before`` options (or only one of them). If ``start-after`` is given as a string option, only lines that follow the first line containing that string are included. If ``end-before`` is given as a string option, only lines that precede the first lines containing that string are included. -.. literalinclude:: quick_tour/sqla_demo/sqla_demo/models/mymodel.py +.. code-block:: rst + + .. literalinclude:: narr/helloworld.py + :language: python + :start-after: from pyramid.response import Response + :end-before: if __name__ == '__main__': + +The above code renders as follows. + +.. literalinclude:: narr/helloworld.py + :language: python + :start-after: from pyramid.response import Response + :end-before: if __name__ == '__main__': + +When specifying particular parts of a file to display, it can be useful to display exactly which lines are being presented. This can be done using the ``lineno-match`` option. + +.. code-block:: rst + + .. literalinclude:: narr/helloworld.py + :language: python + :lines: 6-7 + :lineno-match: + +The above code renders as follows. + +.. literalinclude:: narr/helloworld.py :language: python - :start-after: Start Sphinx Include - :end-before: End Sphinx Include + :lines: 6-7 + :lineno-match: .. _style-guide-inline-code: -- cgit v1.2.3 From 9cab0e7cc24dc95e357db026cead2507111ee93a Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 21 Nov 2016 21:31:41 -0600 Subject: support asset specs in watch_files and variable interpolation --- pyramid/scripts/pserve.py | 46 +++++++++++++++++-------------- pyramid/tests/test_scripts/dummy.py | 8 ++++-- pyramid/tests/test_scripts/test_pserve.py | 19 +++++++++---- 3 files changed, 45 insertions(+), 28 deletions(-) diff --git a/pyramid/scripts/pserve.py b/pyramid/scripts/pserve.py index c67dc86ba..b8776d44f 100644 --- a/pyramid/scripts/pserve.py +++ b/pyramid/scripts/pserve.py @@ -32,6 +32,7 @@ from pyramid.compat import configparser from pyramid.scripts.common import parse_vars from pyramid.scripts.common import setup_logging +from pyramid.path import AssetResolver from pyramid.settings import aslist def main(argv=sys.argv, quiet=False): @@ -119,8 +120,14 @@ class PServeCommand(object): restvars = self.args[1:] return parse_vars(restvars) - def pserve_file_config(self, filename): - config = self.ConfigParser() + def pserve_file_config(self, filename, global_conf=None): + here = os.path.abspath(os.path.dirname(filename)) + defaults = {} + if global_conf: + defaults.update(global_conf) + defaults['here'] = here + + config = self.ConfigParser(defaults=defaults) config.optionxform = str config.read(filename) try: @@ -131,11 +138,13 @@ class PServeCommand(object): watch_files = aslist(items.get('watch_files', ''), flatten=False) # track file paths relative to the ini file - basedir = os.path.dirname(filename) + resolver = AssetResolver(package=None) for file in watch_files: - if not os.path.isabs(file): - file = os.path.join(basedir, file) - self.watch_files.append(os.path.normpath(file)) + if ':' in file: + file = resolver.resolve(file).abspath() + elif not os.path.isabs(file): + file = os.path.join(here, file) + self.watch_files.append(os.path.abspath(file)) def run(self): # pragma: no cover if not self.args: @@ -185,8 +194,8 @@ class PServeCommand(object): if config_path: setup_logging(config_path, global_conf=vars) + self.pserve_file_config(config_path, global_conf=vars) self.watch_files.append(config_path) - self.pserve_file_config(config_path) if hupper.is_active(): reloader = hupper.get_reloader() @@ -205,19 +214,16 @@ class PServeCommand(object): msg = 'Starting server.' self.out(msg) - def serve(): - try: - server(app) - except (SystemExit, KeyboardInterrupt) as e: - if self.options.verbose > 1: - raise - if str(e): - msg = ' ' + str(e) - else: - msg = '' - self.out('Exiting%s (-v to see traceback)' % msg) - - serve() + try: + server(app) + except (SystemExit, KeyboardInterrupt) as e: + if self.options.verbose > 1: + raise + if str(e): + msg = ' ' + str(e) + else: + msg = '' + self.out('Exiting%s (-v to see traceback)' % msg) # For paste.deploy server instantiation (egg:pyramid#wsgiref) def wsgiref_server_runner(wsgi_app, global_conf, **kw): # pragma: no cover diff --git a/pyramid/tests/test_scripts/dummy.py b/pyramid/tests/test_scripts/dummy.py index f3aa20e7c..ced09d0b0 100644 --- a/pyramid/tests/test_scripts/dummy.py +++ b/pyramid/tests/test_scripts/dummy.py @@ -82,8 +82,9 @@ class DummyMultiView(object): self.__request_attrs__ = attrs class DummyConfigParser(object): - def __init__(self, result): + def __init__(self, result, defaults=None): self.result = result + self.defaults = defaults def read(self, filename): self.filename = filename @@ -98,8 +99,9 @@ class DummyConfigParser(object): class DummyConfigParserFactory(object): items = None - def __call__(self): - self.parser = DummyConfigParser(self.items) + def __call__(self, defaults=None): + self.defaults = defaults + self.parser = DummyConfigParser(self.items, defaults) return self.parser class DummyCloser(object): diff --git a/pyramid/tests/test_scripts/test_pserve.py b/pyramid/tests/test_scripts/test_pserve.py index cf9426943..18f7c8c2f 100644 --- a/pyramid/tests/test_scripts/test_pserve.py +++ b/pyramid/tests/test_scripts/test_pserve.py @@ -2,6 +2,8 @@ import os import unittest from pyramid.tests.test_scripts import dummy +here = os.path.abspath(os.path.dirname(__file__)) + class TestPServeCommand(unittest.TestCase): def setUp(self): from pyramid.compat import NativeIO @@ -59,12 +61,19 @@ class TestPServeCommand(unittest.TestCase): def test_config_file_finds_watch_files(self): inst = self._makeOne('development.ini') - self.config_factory.items = [('watch_files', 'foo\nbar\n/baz')] - inst.pserve_file_config('/base/path.ini') + self.config_factory.items = [( + 'watch_files', + 'foo\n/baz\npyramid.tests.test_scripts:*.py', + )] + inst.pserve_file_config('/base/path.ini', global_conf={'a': '1'}) + self.assertEqual(self.config_factory.defaults, { + 'a': '1', + 'here': os.path.abspath('/base'), + }) self.assertEqual(inst.watch_files, [ - os.path.normpath('/base/foo'), - os.path.normpath('/base/bar'), - os.path.normpath('/baz'), + os.path.abspath('/base/foo'), + os.path.abspath('/baz'), + os.path.abspath(os.path.join(here, '*.py')), ]) class Test_main(unittest.TestCase): -- cgit v1.2.3 From ff0da73a8922b5e82c676715078c7b9e60a6a1da Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 21 Nov 2016 22:42:55 -0600 Subject: document globbing --- docs/narr/project.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/narr/project.rst b/docs/narr/project.rst index b4ad6948e..77c637571 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -1083,5 +1083,7 @@ serve it many times. When you change it you want ``pserve`` to restart: watch_files = myapp/static/favicon.ico -Paths are relative to the configuration file and are passed to ``hupper`` -to watch. Currently it does not support globs but this may change. +Paths may be absolute or relative to the configuration file. They may also +be an :term:`asset specification`. These paths are passed to ``hupper`` which +has some basic support for globbing. Acceptable glob patterns depend on the +version of Python being used. -- cgit v1.2.3 From cdee90a86d50667054d140c0a77bbc94a2b788ec Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Tue, 22 Nov 2016 05:36:33 -0800 Subject: improve include section - start work on inline and block reST sections --- docs/style-guide.rst | 85 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 20 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 7868f97e7..f665846d2 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -468,7 +468,9 @@ The above code renders as follows. Note that ``lineno-start`` and ``emphasize-li :lineno-start: 11 :emphasize-lines: 1,6-7,9- -``literalinclude`` also supports including only parts of a file. If it is a Python module, you can select a class, function, or method to include using the ``pyobject`` option. +``literalinclude`` also supports including only parts of a file. + +If the source code is a Python module, you can select a class, function, or method to include using the ``pyobject`` option. .. code-block:: rst @@ -482,35 +484,35 @@ The above code renders as follows. It returns the function ``hello_world`` in th :language: python :pyobject: hello_world -Alternatively, you can specify exactly which lines to include by giving a ``lines`` option. +Another way to control which part of the file is included is to use the ``start-after`` and ``end-before`` options (or only one of them). If ``start-after`` is given as a string option, only lines that follow the first line containing that string are included. If ``end-before`` is given as a string option, only lines that precede the first lines containing that string are included. .. code-block:: rst .. literalinclude:: narr/helloworld.py :language: python - :lines: 6-7 + :start-after: from pyramid.response import Response + :end-before: if __name__ == '__main__': The above code renders as follows. .. literalinclude:: narr/helloworld.py :language: python - :lines: 6-7 + :start-after: from pyramid.response import Response + :end-before: if __name__ == '__main__': -Another way to control which part of the file is included is to use the ``start-after`` and ``end-before`` options (or only one of them). If ``start-after`` is given as a string option, only lines that follow the first line containing that string are included. If ``end-before`` is given as a string option, only lines that precede the first lines containing that string are included. +You can specify exactly which lines to include by giving a ``lines`` option. .. code-block:: rst .. literalinclude:: narr/helloworld.py :language: python - :start-after: from pyramid.response import Response - :end-before: if __name__ == '__main__': + :lines: 6-7 The above code renders as follows. .. literalinclude:: narr/helloworld.py :language: python - :start-after: from pyramid.response import Response - :end-before: if __name__ == '__main__': + :lines: 6-7 When specifying particular parts of a file to display, it can be useful to display exactly which lines are being presented. This can be done using the ``lineno-match`` option. @@ -528,42 +530,86 @@ The above code renders as follows. :lines: 6-7 :lineno-match: +.. literalinclude:: narr/helloworld.py + :language: python + :lines: 6-7 + :linenos: + +Out of all the ways to include parts of a file, ``pyobject`` is the most preferred option because if you change your code and add or remove lines, you don't need to adjust line numbering, whereas with ``lines`` you would have to adjust. ``start-after`` and ``end-before`` are less desirable because they depend on source code not changing, or you can insert comments into your source code to act as the delimiters but that just adds comments that have nothing to do with the functionality of your code. + +Above all with includes, if you use line numbering, it's much preferred to use ``lineno-match`` over ``linenos`` with ``lineno-start`` because it "just works" without thinking. + .. _style-guide-inline-code: Inline code ``````````` +Inline code is surrounded by double backtick marks. Literals, filenames, and function arguments are presented using this style. +.. code-block:: rst + Install requirements for building documentation: ``pip install -e ".[docs]"`` +The above code renders as follows. +Install requirements for building documentation: ``pip install -e ".[docs]"`` -Literals, filenames, and function arguments are presented using the -following style: - ``argument1`` +.. _style-guide-block-rest-markup: -Warnings which represent limitations and need-to-know information -related to a topic or concept are presented in the following style: +Block reST markup +----------------- + +Warnings which represent limitations and need-to-know information related to a topic or concept are presented in the following style: .. warning:: This is a warning. -Notes which represent additional information related to a topic or -concept are presented in the following style: +Notes which represent additional information related to a topic or concept are presented in the following style: .. note:: This is a note. + + +.. _style-guide-inline-rest-markup: + +Inline reST markup +------------------ + +Italics. + +.. code-block:: rst + + This *word* is italicized. + +The above code renders as follows. + +This *word* is italicized. + +Strong. + +.. code-block:: rst + + This **word** is in bold text. + +The above code renders as follows. + +This **word** is in bold text. + + + + + + We present Python method names using the following style: :meth:`pyramid.config.Configurator.add_view` -We present Python class names, module names, attributes, and global -variables using the following style: +We present Python class names, module names, attributes, and global variables using the following style: :class:`pyramid.config.Configurator.registry` @@ -571,7 +617,6 @@ References to glossary terms are presented using the following style: :term:`Pylons` -References to sections and chapters are presented using the following -style: +References to sections and chapters are presented using the following style: :ref:`traversal_chapter` -- cgit v1.2.3 From 732c748a392a28294769d5413ef383501c3c40c9 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Wed, 23 Nov 2016 11:40:57 -0600 Subject: clarify comment fixes #2831 --- pyramid/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyramid/asset.py b/pyramid/asset.py index e6a145341..9d7a3ee63 100644 --- a/pyramid/asset.py +++ b/pyramid/asset.py @@ -33,7 +33,7 @@ def asset_spec_from_abspath(abspath, package): relpath.replace(os.path.sep, '/')) return abspath -# bw compat only; use pyramid.path.AssetDescriptor.abspath() instead +# bw compat only; use pyramid.path.AssetResolver().resolve(spec).abspath() def abspath_from_asset_spec(spec, pname='__main__'): if pname is None: return spec -- cgit v1.2.3 From a7f3065da246480e674077786d0ab707a9c1c964 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Thu, 24 Nov 2016 01:09:16 -0800 Subject: improve links section - add rendering of headings for testing - add topic directive - dedent displaying code - add italics, strong - add Pytyon modules, classes, methods, functions - add tables heading (WIP) - add API documenation (WIP) --- docs/style-guide.rst | 233 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 174 insertions(+), 59 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index f665846d2..45eaed634 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -190,6 +190,20 @@ As individual files do not have so-called "parts" or "chapters", the headings wo Heading Level 4 ``````````````` +The above code renders as follows. + +Heading Level 1 +=============== + +Heading Level 2 +--------------- + +Heading Level 3 +^^^^^^^^^^^^^^^ + +Heading Level 4 +``````````````` + .. _style-guide-paragraphs: Paragraphs @@ -205,9 +219,43 @@ Links Use inline links to keep the context or link label together with the URL. Do not use targets and links at the end of the page, because the separation makes it difficult to update and translate. Here is an example of inline links, our required method. - .. code-block:: rst +.. code-block:: rst + + `TryPyramid `_ + +The above code renders as follows. + +`TryPyramid `_ + +To link to pages within this documentation: + +.. code-block:: rst + + :doc:`quick_tour` + +The above code renders as follows. + +:doc:`quick_tour` + +To link to a section within a page in this documentation: + +.. code-block:: rst + + :ref:`quick_tour` + +The above code renders as follows. - `Example `_ +:ref:`quick_tour` + +To link to pages configured via intersphinx: + +.. code-block:: rst + + :ref:`Deform ` + +The above code renders as follows. + +:ref:`Deform ` .. _style-guide-topic: @@ -227,6 +275,13 @@ The directive's sole argument is interpreted as the topic title, and next line m the body of the topic, and are interpreted as body elements. +The above code renders as follows. + +.. topic:: Topic Title + + Subsequent indented lines comprise + the body of the topic, and are + interpreted as body elements. .. _style-guide-displaying-code: @@ -247,78 +302,76 @@ Sphinx does syntax highlighting of code blocks using the `Pygments Some XML + Some XML Unix shell commands are prefixed with a ``$`` character. (See :term:`venv` for the meaning of ``$VENV``.) - .. code-block:: rst +.. code-block:: rst - .. code-block:: bash + .. code-block:: bash - $ $VENV/bin/pip install -e . + $ $VENV/bin/pip install -e . Windows commands are prefixed with a drive letter with an optional directory name. (See :term:`venv` for the meaning of ``%VENV%``.) - .. code-block:: rst - - .. code-block:: doscon +.. code-block:: rst - c:\> %VENV%\Scripts\pcreate -s starter MyProject + .. code-block:: doscon - .. code-block:: doscon + c:\> %VENV%\Scripts\pcreate -s starter MyProject cfg: - .. code-block:: rst +.. code-block:: rst - .. code-block:: cfg + .. code-block:: cfg - [some-part] - # A random part in the buildout - recipe = collective.recipe.foo - option = value + [some-part] + # A random part in the buildout + recipe = collective.recipe.foo + option = value ini: - .. code-block:: rst +.. code-block:: rst - .. code-block:: ini + .. code-block:: ini - [nosetests] - match=^test - where=pyramid - nocapture=1 + [nosetests] + match=^test + where=pyramid + nocapture=1 Interactive Python: - .. code-block:: rst +.. code-block:: rst - .. code-block:: pycon + .. code-block:: pycon - >>> class Foo: - ... bar = 100 - ... - >>> f = Foo() - >>> f.bar - 100 - >>> f.bar / 0 - Traceback (most recent call last): - File "", line 1, in - ZeroDivisionError: integer division or modulo by zero + >>> class Foo: + ... bar = 100 + ... + >>> f = Foo() + >>> f.bar + 100 + >>> f.bar / 0 + Traceback (most recent call last): + File "", line 1, in + ZeroDivisionError: integer division or modulo by zero If syntax highlighting is not enabled for your code block, you probably have a syntax error and Pygments will fail silently. @@ -332,12 +385,12 @@ Displaying long commands When a command that should be typed on one line is too long to fit on the displayed width of a page, the backslash character ``\`` is used to indicate that the subsequent printed line should be part of the command: - .. code-block:: rst +.. code-block:: rst - .. code-block:: bash + .. code-block:: bash - $ $VENV/bin/py.test tutorial/tests.py --cov-report term-missing \ - --cov=tutorial -q + $ $VENV/bin/py.test tutorial/tests.py --cov-report term-missing \ + --cov=tutorial -q .. _style-guide-code-block-options: @@ -530,14 +583,9 @@ The above code renders as follows. :lines: 6-7 :lineno-match: -.. literalinclude:: narr/helloworld.py - :language: python - :lines: 6-7 - :linenos: - -Out of all the ways to include parts of a file, ``pyobject`` is the most preferred option because if you change your code and add or remove lines, you don't need to adjust line numbering, whereas with ``lines`` you would have to adjust. ``start-after`` and ``end-before`` are less desirable because they depend on source code not changing, or you can insert comments into your source code to act as the delimiters but that just adds comments that have nothing to do with the functionality of your code. +Out of all the ways to include parts of a file, ``pyobject`` is the most preferred option because if you change your code and add or remove lines, you don't need to adjust line numbering, whereas with ``lines`` you would have to adjust. ``start-after`` and ``end-before`` are less desirable because they depend on source code not changing. Alternatively you can insert comments into your source code to act as the delimiters, but that just adds comments that have nothing to do with the functionality of your code. -Above all with includes, if you use line numbering, it's much preferred to use ``lineno-match`` over ``linenos`` with ``lineno-start`` because it "just works" without thinking. +Above all with includes, if you use line numbering, it's much preferred to use ``lineno-match`` over ``linenos`` with ``lineno-start`` because it "just works" without thinking and with less markup. .. _style-guide-inline-code: @@ -580,7 +628,13 @@ Notes which represent additional information related to a topic or concept are p Inline reST markup ------------------ -Italics. +Within a block of content, inline markup is useful to apply styles and links to other files. + + +.. _style-guide-italics: + +Italics +^^^^^^^ .. code-block:: rst @@ -590,7 +644,11 @@ The above code renders as follows. This *word* is italicized. -Strong. + +.. _style-guide-strong: + +Strong +^^^^^^ .. code-block:: rst @@ -601,17 +659,59 @@ The above code renders as follows. This **word** is in bold text. +.. _style-guide-python: + +Python modules, classes, methods, and functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Python module names use the ``mod`` directive, with the module name as the argument. + +.. code-block:: rst + + :mod:`pyramid.config` + +The above code renders as follows. + +:mod:`pyramid.config` + +Python class names use the ``class`` directive, with the class name as the argument. + +.. code-block:: rst + + :class:`pyramid.config.Configurator` + +The above code renders as follows. + +:class:`pyramid.config.Configurator` + +Python method names use the ``meth`` directive, with the method name as the argument. + +.. code-block:: rst + + :meth:`pyramid.config.Configurator.add_view` + +The above code renders as follows. + +:meth:`pyramid.config.Configurator.add_view` + +Python function names use the ``func`` directive, with the function name as the argument. + +.. code-block:: rst + + :func:`pyramid.renderers.render_to_response` + +The above code renders as follows. +:func:`pyramid.renderers.render_to_response` -We present Python method names using the following style: - :meth:`pyramid.config.Configurator.add_view` -We present Python class names, module names, attributes, and global variables using the following style: - :class:`pyramid.config.Configurator.registry` +:app:`Pyramid` + +:ref:`i18n_chapter` References to glossary terms are presented using the following style: @@ -620,3 +720,18 @@ References to glossary terms are presented using the following style: References to sections and chapters are presented using the following style: :ref:`traversal_chapter` + +.. _style-guide-tables: + +Tables +^^^^^^ + +API documentation +----------------- + +.. automodule:: pyramid.i18n + +.. autoclass:: TranslationString + +.. autofunction:: TranslationStringFactory + -- cgit v1.2.3 From 88dd1fc665b0f2f87e276dde85f4df53d88c6898 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Thu, 24 Nov 2016 01:40:58 -0800 Subject: comment out autodoc of TranslationString to get docs to build on Travis-CI https://travis-ci.org/Pylons/pyramid/jobs/178536008#L406 --- docs/api/i18n.rst | 1 + docs/style-guide.rst | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/api/i18n.rst b/docs/api/i18n.rst index 3b9abbc1d..7a61246df 100644 --- a/docs/api/i18n.rst +++ b/docs/api/i18n.rst @@ -6,6 +6,7 @@ .. automodule:: pyramid.i18n .. autoclass:: TranslationString + :noindex: .. autofunction:: TranslationStringFactory diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 45eaed634..641d0fad5 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -731,7 +731,7 @@ API documentation .. automodule:: pyramid.i18n -.. autoclass:: TranslationString +.. .. autoclass:: TranslationString -.. autofunction:: TranslationStringFactory +.. .. autofunction:: TranslationStringFactory -- cgit v1.2.3 From bf5c9eaa7e9041fe76cafddf7bad0f397899a6e9 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 25 Nov 2016 14:58:20 -0800 Subject: add todo Sphinx extension for todo blocks - add lists, tables, danger, todo, comments --- docs/conf.py | 8 +- docs/style-guide.rst | 252 +++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 239 insertions(+), 21 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index ace921247..0e3f03068 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,9 +51,10 @@ book = os.environ.get('BOOK') extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', - 'repoze.sphinx.autointerface', - 'sphinx.ext.viewcode', 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.viewcode', + 'repoze.sphinx.autointerface', 'sphinxcontrib.programoutput', # enable pylons_sphinx_latesturl when this branch is no longer "latest" # 'pylons_sphinx_latesturl', @@ -120,6 +121,9 @@ exclude_patterns = ['_themes/README.rst', ] # unit titles (such as .. function::). add_module_names = False +# Add support for todo items +todo_include_todos = True + # The name of the Pygments (syntax highlighting) style to use. #pygments_style = book and 'bw' or 'tango' if book: diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 641d0fad5..73cba0859 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -146,9 +146,9 @@ Grammar, spelling, and capitalization preferences Use any commercial or free professional style guide in general. Use a spell- and grammar-checker. The following table lists the preferred grammar, spelling, and capitalization of words and phrases for frequently used items in the documentation. -========== ====================== +========== ===== Preferred Avoid -========== ====================== +========== ===== add-on addon and so on etc. GitHub Github, github @@ -157,7 +157,7 @@ plug-in plugin select check, tick (checkbox) such as like verify be sure -========== ====================== +========== ===== .. _style-guide-headings: @@ -604,31 +604,244 @@ The above code renders as follows. Install requirements for building documentation: ``pip install -e ".[docs]"`` -.. _style-guide-block-rest-markup: +.. _style-guide-rest-block-markup: -Block reST markup +reST block markup ----------------- -Warnings which represent limitations and need-to-know information related to a topic or concept are presented in the following style: +This section contains miscellaneous reST block markup for items not already covered. - .. warning:: - This is a warning. +.. _style-guide-lists: -Notes which represent additional information related to a topic or concept are presented in the following style: +Lists +^^^^^ + +Bulleted lists use an asterisk "``*``". + +.. code-block:: rst + + * This is an item in a bulleted list. + * This is another item in a bulleted list. + +The above code renders as follows. + +* This is an item in a bulleted list. +* This is another item in a bulleted list. + +Numbered lists should use a number sign followed by a period "``#.``" and will be numbered automatically. + +.. code-block:: rst + + #. This is an item in a numbered list. + #. This is another item in a numbered list. + +The above code renders as follows. + +#. This is an item in a numbered list. +#. This is another item in a numbered list. + +The appearance of nested lists can be created by separating the child lists from their parent list by blank lines, and indenting by two spaces. Note that Sphinx renders the reST markup not as nested HTML lists, but instead merely indents the children using ``
``. + +.. code-block:: rst + + #. This is a list item in the parent list. + #. This is another list item in the parent list. + + #. This is a list item in the child list. + #. This is another list item in the child list. + + #. This is one more list item in the parent list. + +The above code renders as follows. + +#. This is a list item in the parent list. +#. This is another list item in the parent list. + + #. This is a list item in the child list. + #. This is another list item in the child list. + +#. This is one more list item in the parent list. + + +.. _style-guide-tables: - .. note:: +Tables +^^^^^^ + +Two forms of tables are supported, `simple `_ and `grid `_. + +Simple tables require less markup but have fewer features and some constraints compared to grid tables. The right-most column in simple tables is unbound to the length of the underline in the column header. + +.. code-block:: rst + + ===== ===== + col 1 col 2 + ===== ===== + 1 Second column of row 1. + 2 Second column of row 2. + Second line of paragraph. + 3 - Second column of row 3. + + - Second item in bullet + list (row 3, column 2). + \ Row 4; column 1 will be empty. + ===== ===== + +The above code renders as follows. + +===== ===== +col 1 col 2 +===== ===== +1 Second column of row 1. +2 Second column of row 2. + Second line of paragraph. +3 - Second column of row 3. + + - Second item in bullet + list (row 3, column 2). +\ Row 4; column 1 will be empty. +===== ===== + +Grid tables have much more cumbersome markup, although Emacs' table mode may lessen the tedium. + +.. code-block:: rst + + +------------------------+------------+----------+----------+ + | Header row, column 1 | Header 2 | Header 3 | Header 4 | + | (header rows optional) | | | | + +========================+============+==========+==========+ + | body row 1, column 1 | column 2 | column 3 | column 4 | + +------------------------+------------+----------+----------+ + | body row 2 | Cells may span columns. | + +------------------------+------------+---------------------+ + | body row 3 | Cells may | - Table cells | + +------------------------+ span rows. | - contain | + | body row 4 | | - body elements. | + +------------------------+------------+---------------------+ + +The above code renders as follows. + ++------------------------+------------+----------+----------+ +| Header row, column 1 | Header 2 | Header 3 | Header 4 | +| (header rows optional) | | | | ++========================+============+==========+==========+ +| body row 1, column 1 | column 2 | column 3 | column 4 | ++------------------------+------------+----------+----------+ +| body row 2 | Cells may span columns. | ++------------------------+------------+---------------------+ +| body row 3 | Cells may | - Table cells | ++------------------------+ span rows. | - contain | +| body row 4 | | - body elements. | ++------------------------+------------+---------------------+ + + +.. _style-guide-danger-errors: + +Danger and errors +^^^^^^^^^^^^^^^^^ + +Danger and errors represent critical information related to a topic or concept, and should recommend to the user "don't do this dangerous thing". ``danger`` and ``error`` appear similarly when rendered, but the HTML generated has the appropriate semantic context. + +.. code-block:: rst + + .. danger:: + + This is danger or an error. + +The above code renders as follows. - This is a note. +.. danger:: + This is danger or an error. +.. todo:: -.. _style-guide-inline-rest-markup: + The style for ``danger`` and ``error`` has not yet been created. -Inline reST markup + +.. _style-guide-warnings: + +Warnings +^^^^^^^^ + +Warnings represent limitations and advice related to a topic or concept. + +.. code-block:: rst + + .. warning:: + + This is a warning. + +The above code renders as follows. + +.. warning:: + + This is a warning. + + +.. _style-guide-notes: + +Notes +^^^^^ + +Notes represent additional information related to a topic or concept. + +.. code-block:: rst + + .. note:: + + This is a note. + +The above code renders as follows. + +.. note:: + + This is a note. + + +.. _style-guide-todo: + +Todo +^^^^ + +Todo items designated tasks that require further work. + +.. code-block:: rst + + .. todo:: + + This is a todo item. + +The above code renders as follows. + +.. todo:: + + This is a todo item. + +.. todo:: + + The todo style is not yet implemented and needs further work. + + +.. _style-guide-comments: + +Comments +^^^^^^^^ + +Comments of the documentation within the documentation may be generated with two periods ``..``. Comments are not rendered, but provide information to documentation authors. + +.. code-block:: rst + + .. This is an example comment. + + +.. _style-guide-rest-inline-markup: + +reST inline markup ------------------ -Within a block of content, inline markup is useful to apply styles and links to other files. +This section contains miscellaneous reST inline markup for items not already covered. Within a block of content, inline markup is useful to apply styles and links to other files. .. _style-guide-italics: @@ -704,8 +917,9 @@ The above code renders as follows. :func:`pyramid.renderers.render_to_response` +.. seealso:: - + See also the Sphinx documentation for the :ref:`reStructuredText Primer `. @@ -717,14 +931,14 @@ References to glossary terms are presented using the following style: :term:`Pylons` +Glossary terms appear in the Glossary + +.. glossary:: :sorted: + References to sections and chapters are presented using the following style: :ref:`traversal_chapter` -.. _style-guide-tables: - -Tables -^^^^^^ API documentation ----------------- -- cgit v1.2.3 From f4518e72c82ad3ef7aeba65ad1221924767b32ea Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 25 Nov 2016 15:45:27 -0800 Subject: replace description with meta - add parsed-literals --- docs/style-guide.rst | 55 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 73cba0859..f13671704 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -3,9 +3,9 @@ Style Guide =========== -.. admonition:: description - - This chapter describes how to edit, update, and build the :app:`Pyramid` documentation. For coding style guidelines, see `Coding Style `_. +.. meta:: + :description: This chapter describes how to edit, update, and build the Pyramid documentation. + :keywords: Pyramid, Style Guide .. _style-guide-introduction: @@ -15,6 +15,8 @@ Introduction This chapter provides details of how to contribute updates to the documentation following style guidelines and conventions. We provide examples, including reStructuredText code and its rendered output for both visual and technical reference. +For coding style guidelines, see `Coding Style `_. + .. _style-guide-contribute: @@ -75,7 +77,7 @@ Page structure Each page should contain in order the following. -- The main heading. This will be visible in the table of contents. +#. The main heading. This will be visible in the table of contents. .. code-block:: rst @@ -83,17 +85,22 @@ Each page should contain in order the following. The main heading ================ -- The description of the page. This text will be displayed to the reader below the main heading as well as be inserted into the description metadata field of the document. It will be displayed in search engine listings for the page. This is created using the reST ``admonition`` directive. A single paragraph of text consisting of no more than three sentences is recommended, so that the same text fits into search engine results: +#. Meta tag information. The "meta" directive is used to specify HTML metadata stored in HTML META tags. "Metadata" is data about data, in this case data about web pages. Metadata is used to describe and classify web pages in the World Wide Web, in a form that is easy for search engines to extract and collate. .. code-block:: rst - .. admonition:: description + .. meta:: + :description: This chapter describes how to edit, update, and build the Pyramid documentation. + :keywords: Pyramid, Style Guide - This is a description of the page, which will appear inline and in the description metadata field. + The above code renders as follows. - .. note:: The ``description`` metadata field is not yet implemented in the documentation's Sphinx theme, but it is a `feature request `_, so it is helpful to start using the ``description`` admonition now. + .. code-block:: xml -- Introduction paragraph. + + + +#. Introduction paragraph. .. code-block:: rst @@ -102,7 +109,7 @@ Each page should contain in order the following. This chapter is an introduction. -- Finally the content of the document page, consisting of reST elements such as headings, paragraphs, tables, and so on. +#. Finally the content of the document page, consisting of reST elements such as headings, paragraphs, tables, and so on. .. _style-guide-page-content: @@ -378,6 +385,26 @@ If syntax highlighting is not enabled for your code block, you probably have a s View the `full list of lexers and associated short names `_. +.. _style-guide-parsed-literals: + +Parsed literals +``````````````` + +Parsed literals are used to render, for example, a specific version number of the application in code blocks. Use the directive ``parsed-literal``. Note that syntax highlighting is not supported and code is rendered as plain text. + +.. code-block:: rst + + .. parsed-literal:: + + $ $VENV/bin/pip install "pyramid==\ |release|\ " + +The above code renders as follows. + +.. parsed-literal:: + + $ $VENV/bin/pip install "pyramid==\ |release|\ " + + .. _style-guide-long-commands: Displaying long commands @@ -736,12 +763,12 @@ The above code renders as follows. +------------------------+------------+---------------------+ -.. _style-guide-danger-errors: +.. _style-guide-danger: -Danger and errors -^^^^^^^^^^^^^^^^^ +Danger +^^^^^^ -Danger and errors represent critical information related to a topic or concept, and should recommend to the user "don't do this dangerous thing". ``danger`` and ``error`` appear similarly when rendered, but the HTML generated has the appropriate semantic context. +Danger represents critical information related to a topic or concept, and should recommend to the user "don't do this dangerous thing". .. code-block:: rst -- cgit v1.2.3 From 78254d48a39e2b4938297dd568b02ab055b54c3f Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 25 Nov 2016 19:20:34 -0800 Subject: add feature versioning, seealso --- docs/style-guide.rst | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index f13671704..51a2efde5 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -763,6 +763,70 @@ The above code renders as follows. +------------------------+------------+---------------------+ +.. _style-guide-feature-versioning: + +Feature versioning +^^^^^^^^^^^^^^^^^^ + +Three directives designate the version in which something is added, changed, or deprecated in the project. + + +.. _style-guide-version-added: + +Version added +````````````` + +To indicate the version in which a feature is added to a project, use the ``versionadded`` directive. If the feature is an entire module, then the directive should be placed at the top of the module section before any prose. + +The first argument is the version. An optional second argument must appear upon a subsequent line, without blank lines in between, and indented. + +.. code-block:: rst + + .. versionadded:: 1.1 + :func:`pyramid.paster.bootstrap` + +The above code renders as follows. + +.. versionadded:: 1.1 + :func:`pyramid.paster.bootstrap` + + +.. _style-guide-version-changed: + +Version changed +``````````````` + +To indicate the version in which a feature is changed in a project, use the ``versionchanged`` directive. Its arguments are the same as ``versionadded``. + +.. code-block:: rst + + .. versionchanged:: 1.8 + Added the ability for ``bootstrap`` to cleanup automatically via the ``with`` statement. + +The above code renders as follows. + +.. versionchanged:: 1.8 + Added the ability for ``bootstrap`` to cleanup automatically via the ``with`` statement. + + +.. _style-guide-deprecated: + +Deprecated +`````````` + +Similar to ``versionchanged``, ``deprecated`` describes when the feature was deprecated. An explanation can also be given, for example, to inform the reader what should be used instead. + +.. code-block:: rst + + .. deprecated:: 1.7 + Use the ``require_csrf`` option or read :ref:`auto_csrf_checking` instead to have :class:`pyramid.exceptions.BadCSRFToken` exceptions raised. + +The above code renders as follows. + +.. deprecated:: 1.7 + Use the ``require_csrf`` option or read :ref:`auto_csrf_checking` instead to have :class:`pyramid.exceptions.BadCSRFToken` exceptions raised. + + .. _style-guide-danger: Danger @@ -827,6 +891,26 @@ The above code renders as follows. This is a note. +.. _style-guide-see-also: + +See also +^^^^^^^^ + +"See also" messages refer to topics that are related to the current topic, but have a narrative tone to them instead of merely a link without explanation. "See also" is rendered in a block as well, so that it stands out for the reader's attention. + +.. code-block:: rst + + .. seealso:: + + See :ref:`Quick Tutorial section on Requirements `. + +The above code renders as follows. + +.. seealso:: + + See :ref:`Quick Tutorial section on Requirements `. + + .. _style-guide-todo: Todo -- cgit v1.2.3 From d586ff5c7f22e3bc8e206d865a0760fb28efdb40 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 25 Nov 2016 19:45:42 -0800 Subject: add glossary --- docs/style-guide.rst | 119 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 41 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 51a2efde5..95bfcccfa 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -35,18 +35,60 @@ When submitting a pull request for the first time in a project, sign `CONTRIBUTO Location, referencing, and naming of files ------------------------------------------ -- reStructuredText (reST) files must be located in ``docs/`` and its subdirectories. -- Image files must be located in ``docs/_static/``. -- reST directives must refer to files either relative to the source file or absolute from the top source directory. For example, in ``docs/narr/source.rst``, you could refer to a file in a different directory as either ``.. include:: ../diff-dir/diff-source.rst`` or ``.. include:: /diff-dir/diff-source.rst``. -- File names should be lower-cased and have words separated with either a hyphen "-" or an underscore "_". -- reST files must have an extension of ``.rst``. -- Image files may be any format but must have lower-cased file names and have standard file extensions that consist three letters (``.gif``, ``.jpg``, ``.png``, ``.svg``). ``.gif`` and ``.svg`` are not currently supported by PDF builders in Sphinx, but you can allow the Sphinx builder to automatically select the correct image format for the desired output by replacing the three-letter file extension with ``*``. For example: +* reStructuredText (reST) files must be located in ``docs/`` and its subdirectories. +* Image files must be located in ``docs/_static/``. +* reST directives must refer to files either relative to the source file or absolute from the top source directory. For example, in ``docs/narr/source.rst``, you could refer to a file in a different directory as either ``.. include:: ../diff-dir/diff-source.rst`` or ``.. include:: /diff-dir/diff-source.rst``. +* File names should be lower-cased and have words separated with either a hyphen "-" or an underscore "_". +* reST files must have an extension of ``.rst``. +* Image files may be any format but must have lower-cased file names and have standard file extensions that consist three letters (``.gif``, ``.jpg``, ``.png``, ``.svg``). ``.gif`` and ``.svg`` are not currently supported by PDF builders in Sphinx, but you can allow the Sphinx builder to automatically select the correct image format for the desired output by replacing the three-letter file extension with ``*``. For example: .. code-block:: rst - .. image:: ../_static/pyramid_request_processing.- + .. image:: ../_static/pyramid_request_processing. - will select the image ``pyramid_request_processing.svg`` for the HTML documentation builder, and ``pyramid_request_processing.png`` for the PDF builder. See the related [Stack Overflow post](http://stackoverflow.com/questions/6473660/using-sphinx-docs-how-can-i-specify-png-image-formats-for-html-builds-and-pdf-im/6486713#6486713). + will select the image ``pyramid_request_processing.svg`` for the HTML documentation builder, and ``pyramid_request_processing.png`` for the PDF builder. See the related `Stack Overflow post `_. + + +.. _style-guide-glossary: + +Glossary +-------- + +A glossary defines terms used throughout the documentation. + +The glossary file must be named ``glossary.rst``. Its content must begin with the directive ``glossary``. An optional ``sorted`` argument should be used to sort the terms alphabetically when rendered, making it easier for the user to find a given term. Without the argument ``sorted``, terms will appear in the order of the ``glossary`` source file. + +.. code-block:: rst + + .. glossary:: + :sorted: + + voom + Theoretically, the sound a parrot makes when four-thousand volts of electricity pass through it. + + pining + What the Norwegien Blue does when it misses its homeland, e.g., pining for the fjords. + +The above code renders as follows. + +.. glossary:: + :sorted: + + voom + Theoretically, the sound a parrot makes when four-thousand volts of electricity pass through it. + + pining + What the Norwegien Blue does when it misses its homeland, e.g., pining for the fjords. + +References to glossary terms use the ``term`` directive. + +.. code-block:: rst + + :term:`voom` + +The above code renders as follows. Note it is hyperlinked, and when clicked it will take the user to the term in the Glossary and highlight the term. + +:term:`voom` .. _style-guide-section-structure: @@ -56,9 +98,9 @@ Section structure Each section, or a subdirectory of reST files, such as a tutorial, must contain an ``index.rst`` file. ``index.rst`` must contain the following. -- A section heading. This will be visible in the table of contents. -- A single paragraph describing this section. -- A Sphinx ``toctree`` directive, with a ``maxdepth`` of 2. Each ``.rst`` file in the folder should be linked to this ``toctree``. +* A section heading. This will be visible in the table of contents. +* A single paragraph describing this section. +* A Sphinx ``toctree`` directive, with a ``maxdepth`` of 2. Each ``.rst`` file in the folder should be linked to this ``toctree``. .. code-block:: rst @@ -133,8 +175,8 @@ Narrative documentation is not code, and should therefore not adhere to PEP8 or Trailing white spaces ^^^^^^^^^^^^^^^^^^^^^ -- No trailing white spaces. -- Always use a line feed or carriage return at the end of a file. +* No trailing white spaces. +* Always use a line feed or carriage return at the end of a file. .. _style-guide-indentation: @@ -142,8 +184,8 @@ Trailing white spaces Indentation ^^^^^^^^^^^ -- Indent using four spaces. -- Do not use tabs to indent. +* Indent using four spaces. +* Do not use tabs to indent. .. _style-guide-grammar-spelling-preferences: @@ -151,7 +193,7 @@ Indentation Grammar, spelling, and capitalization preferences ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use any commercial or free professional style guide in general. Use a spell- and grammar-checker. The following table lists the preferred grammar, spelling, and capitalization of words and phrases for frequently used items in the documentation. +Use any commercial or free professional style guide in general. Use a spell* and grammar-checker. The following table lists the preferred grammar, spelling, and capitalization of words and phrases for frequently used items in the documentation. ========== ===== Preferred Avoid @@ -176,10 +218,10 @@ Capitalize only the first letter in a heading (sentence-case), unless other word For consistent heading characters throughout the documentation, follow the guidelines stated in the `Python Developer's Guide `_. Specifically: -- =, for sections -- -, for subsections -- ^, for subsubsections -- ", for paragraphs +* =, for sections +* -, for subsections +* ^, for subsubsections +* ", for paragraphs As individual files do not have so-called "parts" or "chapters", the headings would be underlined with characters as shown. @@ -527,10 +569,10 @@ The above code renders as follows. Like code blocks, ``literalinclude`` supports the following options. -- ``language`` to select a language for syntax highlighting -- ``linenos`` to switch on line numbers -- ``lineno-start`` to specify the starting number to use for line numbering -- ``emphasize-lines`` to emphasize particular lines +* ``language`` to select a language for syntax highlighting +* ``linenos`` to switch on line numbers +* ``lineno-start`` to specify the starting number to use for line numbering +* ``emphasize-lines`` to emphasize particular lines .. code-block:: rst @@ -708,9 +750,9 @@ Simple tables require less markup but have fewer features and some constraints c 1 Second column of row 1. 2 Second column of row 2. Second line of paragraph. - 3 - Second column of row 3. + 3 * Second column of row 3. - - Second item in bullet + * Second item in bullet list (row 3, column 2). \ Row 4; column 1 will be empty. ===== ===== @@ -723,9 +765,9 @@ col 1 col 2 1 Second column of row 1. 2 Second column of row 2. Second line of paragraph. -3 - Second column of row 3. +3 * Second column of row 3. - - Second item in bullet + * Second item in bullet list (row 3, column 2). \ Row 4; column 1 will be empty. ===== ===== @@ -742,9 +784,9 @@ Grid tables have much more cumbersome markup, although Emacs' table mode may les +------------------------+------------+----------+----------+ | body row 2 | Cells may span columns. | +------------------------+------------+---------------------+ - | body row 3 | Cells may | - Table cells | - +------------------------+ span rows. | - contain | - | body row 4 | | - body elements. | + | body row 3 | Cells may | * Table cells | + +------------------------+ span rows. | * contain | + | body row 4 | | * body elements. | +------------------------+------------+---------------------+ The above code renders as follows. @@ -757,9 +799,9 @@ The above code renders as follows. +------------------------+------------+----------+----------+ | body row 2 | Cells may span columns. | +------------------------+------------+---------------------+ -| body row 3 | Cells may | - Table cells | -+------------------------+ span rows. | - contain | -| body row 4 | | - body elements. | +| body row 3 | Cells may | * Table cells | ++------------------------+ span rows. | * contain | +| body row 4 | | * body elements. | +------------------------+------------+---------------------+ @@ -1034,17 +1076,12 @@ The above code renders as follows. -:app:`Pyramid` - -:ref:`i18n_chapter` -References to glossary terms are presented using the following style: - :term:`Pylons` -Glossary terms appear in the Glossary +:app:`Pyramid` -.. glossary:: :sorted: +:ref:`i18n_chapter` References to sections and chapters are presented using the following style: -- cgit v1.2.3 From d1d6a7d9617461ce79bf224ef47aa6af308ff645 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 25 Nov 2016 20:10:00 -0800 Subject: add toctree --- docs/style-guide.rst | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 95bfcccfa..166421eaf 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -49,6 +49,72 @@ Location, referencing, and naming of files will select the image ``pyramid_request_processing.svg`` for the HTML documentation builder, and ``pyramid_request_processing.png`` for the PDF builder. See the related `Stack Overflow post `_. +.. _style-guide-table-of-contents-tree: + +Table of contents tree +---------------------- + +To insert a table of contents (TOC), use the ``toctree`` directive. Entries listed under the ``toctree`` directive follow :ref:`location conventions `. A numeric ``maxdepth`` option may be given to indicate the depth of the tree; by default, all levels are included. + +.. code-block:: rst + + .. toctree:: + :maxdepth: 2 + + narr/introduction + narr/install + +The above code renders as follows. + +.. toctree:: + :maxdepth: 2 + + narr/introduction + narr/install + +Globbing can be used. + +.. code-block:: rst + + .. toctree:: + :maxdepth: 1 + :glob: + + pscripts/index + pscripts/* + +The above code renders as follows. + +.. toctree:: + :maxdepth: 1 + :glob: + + pscripts/index + pscripts/* + +To notify Sphinx of the document hierarchy, but not insert links into the document at the location of the directive, use the option ``hidden``. This makes sense when you want to insert these links yourself, in a different style, or in the HTML sidebar. + +.. code-block:: rst + + .. toctree:: + :hidden: + + quick_tour + + * :doc:`quick_tour` gives an overview of the major features in Pyramid, covering a little about a lot. + +The above code renders as follows. + +.. toctree:: + :hidden: + + quick_tour + +* :doc:`quick_tour` gives an overview of the major features in Pyramid, covering a little about a lot. + +.. seealso:: Sphinx documentation of :ref:`The TOC tree `. + + .. _style-guide-glossary: Glossary -- cgit v1.2.3 From d7a6a905d55d6c21953812991dafb4163bbf0db1 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 25 Nov 2016 21:05:51 -0800 Subject: add cross-referencing --- docs/style-guide.rst | 93 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 58 insertions(+), 35 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 166421eaf..02f8c18fa 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -112,7 +112,7 @@ The above code renders as follows. * :doc:`quick_tour` gives an overview of the major features in Pyramid, covering a little about a lot. -.. seealso:: Sphinx documentation of :ref:`The TOC tree `. +.. seealso:: Sphinx documentation of :ref:`toctree-directive`. .. _style-guide-glossary: @@ -342,35 +342,7 @@ The above code renders as follows. `TryPyramid `_ -To link to pages within this documentation: - -.. code-block:: rst - - :doc:`quick_tour` - -The above code renders as follows. - -:doc:`quick_tour` - -To link to a section within a page in this documentation: - -.. code-block:: rst - - :ref:`quick_tour` - -The above code renders as follows. - -:ref:`quick_tour` - -To link to pages configured via intersphinx: - -.. code-block:: rst - - :ref:`Deform ` - -The above code renders as follows. - -:ref:`Deform ` +.. seealso:: See also :ref:`style-guide-cross-references` for generating links throughout the entire documentation. .. _style-guide-topic: @@ -405,7 +377,7 @@ Displaying code Code may be displayed in blocks or inline. You can include blocks of code from other source files. Blocks of code should use syntax highlighting. -.. seealso:: See also the Sphinx documentation for :ref:`Showing code examples `. +.. seealso:: See also the Sphinx documentation for :ref:`code-examples`. .. _style-guide-syntax-highlighting: @@ -1138,20 +1110,71 @@ The above code renders as follows. .. seealso:: - See also the Sphinx documentation for the :ref:`reStructuredText Primer `. + See also the Sphinx documentation for the :ref:`rst-primer`. +.. _style-guide-cross-references: +Cross-references +^^^^^^^^^^^^^^^^ +To create cross-references to an arbitrary location, object, document, or other items, use variations of the following syntax. +* ``:role:`target``` creates a link to the item named ``target`` of the type indicated by ``role``, with the link's text as the title of the target. ``target`` may need to be disambiguated between documentation sets linked through intersphinx, in which case the syntax would be ``deform:overview``. +* ``:role:`~target``` displays the link as only the last component of the target. +* ``:role:`title ``` creates a custom title, instead of the default title of the target. -:app:`Pyramid` + +.. _style-guide-cross-referencing-documents: + +Cross-referencing documents +``````````````````````````` + +To link to pages within this documentation: + +.. code-block:: rst + + :doc:`quick_tour` + +The above code renders as follows. + +:doc:`quick_tour` + + +.. _style-guide-cross-referencing-arbitrary-locations: + +Cross-referencing arbitrary locations +````````````````````````````````````` + +To support cross-referencing to arbitrary locations in any document and between documentation sets via intersphinx, the standard reST labels are used. For this to work, label names must be unique throughout the entire documentation including externally linked intersphinx references. There are two ways in which you can refer to labels, if they are placed directly before a section title, a figure, or table with a caption, or at any other location. The following section has a label with the syntax ``.. _label_name:`` followed by the section title. + +.. code-block:: rst + + .. _i18n_chapter: + + Internationalization and Localization + ===================================== + +To generate a link to that section with its title, use the following syntax. + +.. code-block:: rst + + :ref:`i18n_chapter` + +The above code renders as follows. :ref:`i18n_chapter` -References to sections and chapters are presented using the following style: +The same syntax works figures and tables with captions. + +For labels that are not placed as mentioned, the link must be given an explicit title, such as ``:ref:`Link title ```. + +.. seealso:: See also the Sphinx documentation, :ref:`inline-markup`. + - :ref:`traversal_chapter` + + +:app:`Pyramid` API documentation -- cgit v1.2.3 From d7150db81721df046017632e597193f3250b08e3 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 25 Nov 2016 23:02:18 -0800 Subject: add sphinx extensions --- docs/style-guide.rst | 161 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 121 insertions(+), 40 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 02f8c18fa..0adea0e38 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -1062,11 +1062,74 @@ The above code renders as follows. This **word** is in bold text. +.. seealso:: + + See also the Sphinx documentation for the :ref:`rst-primer`. + + +.. _style-guide-cross-references: + +Cross-references +^^^^^^^^^^^^^^^^ + +To create cross-references to a document, arbitrary location, object, or other items, use variations of the following syntax. + +* ``:role:`target``` creates a link to the item named ``target`` of the type indicated by ``role``, with the link's text as the title of the target. ``target`` may need to be disambiguated between documentation sets linked through intersphinx, in which case the syntax would be ``deform:overview``. +* ``:role:`~target``` displays the link as only the last component of the target. +* ``:role:`title ``` creates a custom title, instead of the default title of the target. + + +.. _style-guide-cross-referencing-documents: + +Cross-referencing documents +``````````````````````````` + +To link to pages within this documentation: + +.. code-block:: rst + + :doc:`quick_tour` + +The above code renders as follows. -.. _style-guide-python: +:doc:`quick_tour` + + +.. _style-guide-cross-referencing-arbitrary-locations: + +Cross-referencing arbitrary locations +````````````````````````````````````` + +To support cross-referencing to arbitrary locations in any document and between documentation sets via intersphinx, the standard reST labels are used. For this to work, label names must be unique throughout the entire documentation including externally linked intersphinx references. There are two ways in which you can refer to labels, if they are placed directly before a section title, a figure, or table with a caption, or at any other location. The following section has a label with the syntax ``.. _label_name:`` followed by the section title. + +.. code-block:: rst + + .. _i18n_chapter: + + Internationalization and Localization + ===================================== + +To generate a link to that section with its title, use the following syntax. + +.. code-block:: rst + + :ref:`i18n_chapter` + +The above code renders as follows. + +:ref:`i18n_chapter` + +The same syntax works figures and tables with captions. + +For labels that are not placed as mentioned, the link must be given an explicit title, such as ``:ref:`Link title ```. + +.. seealso:: See also the Sphinx documentation, :ref:`inline-markup`. + + +.. _style-guide-cross-referencing-python: Python modules, classes, methods, and functions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +``````````````````````````````````````````````` Python module names use the ``mod`` directive, with the module name as the argument. @@ -1108,81 +1171,99 @@ The above code renders as follows. :func:`pyramid.renderers.render_to_response` -.. seealso:: - - See also the Sphinx documentation for the :ref:`rst-primer`. +Note that you can use either or combine the ``~`` and ``.`` prefixes. However, we prefer not to use the ``.`` prefix because Sphinx might generate an error if it cannot disambiguate the reference. +.. code-block:: rst -.. _style-guide-cross-references: + :func:`~.render_to_response` -Cross-references -^^^^^^^^^^^^^^^^ +The above code renders as follows. -To create cross-references to an arbitrary location, object, document, or other items, use variations of the following syntax. +:func:`~.render_to_response` -* ``:role:`target``` creates a link to the item named ``target`` of the type indicated by ``role``, with the link's text as the title of the target. ``target`` may need to be disambiguated between documentation sets linked through intersphinx, in which case the syntax would be ``deform:overview``. -* ``:role:`~target``` displays the link as only the last component of the target. -* ``:role:`title ``` creates a custom title, instead of the default title of the target. +.. _style-guide-role-app-pyramid: -.. _style-guide-cross-referencing-documents: +The role ``:app:`Pyramid``` +^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Cross-referencing documents -``````````````````````````` +We use the special role ``app`` to refer to the application "Pyramid". -To link to pages within this documentation: .. code-block:: rst - :doc:`quick_tour` + :app:`Pyramid` The above code renders as follows. -:doc:`quick_tour` +:app:`Pyramid` -.. _style-guide-cross-referencing-arbitrary-locations: +.. _style-guide-sphinx-extensions: -Cross-referencing arbitrary locations -````````````````````````````````````` +Sphinx extensions +----------------- -To support cross-referencing to arbitrary locations in any document and between documentation sets via intersphinx, the standard reST labels are used. For this to work, label names must be unique throughout the entire documentation including externally linked intersphinx references. There are two ways in which you can refer to labels, if they are placed directly before a section title, a figure, or table with a caption, or at any other location. The following section has a label with the syntax ``.. _label_name:`` followed by the section title. +We use several Sphinx extensions to add features to our documentation. Extensions need to be enabled and configured in ``docs/conf.py`` before they can be used. -.. code-block:: rst - .. _i18n_chapter: +.. _style-guide-sphinx-extension-autodoc: - Internationalization and Localization - ===================================== +:mod:`sphinx.ext.autodoc` +------------------------- -To generate a link to that section with its title, use the following syntax. +API documentation uses the Sphinx extension :mod:`sphinx.ext.autodoc` to include documentation from docstrings. -.. code-block:: rst +See the source of any documentation within the ``docs/api/`` directory for conventions and usage, as well as the Sphinx extension's :mod:`documentation `. - :ref:`i18n_chapter` -The above code renders as follows. +.. _style-guide-sphinx-extension-doctest: -:ref:`i18n_chapter` +:mod:`sphinx.ext.doctest` +------------------------- -The same syntax works figures and tables with captions. +:mod:`sphinx.ext.doctest` allows you to test code snippets in the documentation in a natural way. It works by collecting specially-marked up code blocks and running them as doctest tests. We have only a few tests in our Pyramid documentation which can be found in ``narr/sessions.rst`` and ``narr/hooks.rst``. -For labels that are not placed as mentioned, the link must be given an explicit title, such as ``:ref:`Link title ```. -.. seealso:: See also the Sphinx documentation, :ref:`inline-markup`. +.. _style-guide-sphinx-extension-intersphinx: +:mod:`sphinx.ext.intersphinx` +----------------------------- +:mod:`sphinx.ext.intersphinx` generates links to the documentation of objects in other projects. -:app:`Pyramid` +.. _style-guide-sphinx-extension-todo: +:mod:`sphinx.ext.todo` +---------------------- + +:mod:`sphinx.ext.todo` adds support for todo items. + + +.. _style-guide-sphinx-extension-viewcode: + +:mod:`sphinx.ext.viewcode` +-------------------------- + +:mod:`sphinx.ext.viewcode` looks at your Python object descriptions and tries to find the source files where the objects are contained. When found, a separate HTML page will be output for each module with a highlighted version of the source code, and a link will be added to all object descriptions that leads to the source code of the described object. A link back from the source to the description will also be inserted. -API documentation ------------------ -.. automodule:: pyramid.i18n +.. _style-guide-sphinx-extension-repoze-sphinx-autointerface: -.. .. autoclass:: TranslationString +`repoze.sphinx.autointerface `_ +----------------------------------------------------------------------------------------- -.. .. autofunction:: TranslationStringFactory +`repoze.sphinx.autointerface `_ auto-generates API docs from Zope interfaces. + + +.. _style-guide-script-documentation: + +Script documentation +-------------------- + +We currently use `sphinxcontrib-programoutput `_ to generate program output of the p* scripts. It is no longer maintained and may cause future builds of the documentation to fail. + +.. todo:: + See `issue #2804 `_ for further discussion. -- cgit v1.2.3 From 0eb59ec16a05171e0a6e5e4649b7c03448413d76 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Fri, 25 Nov 2016 23:43:25 -0800 Subject: eat dog food: use " not ` for headings --- docs/style-guide.rst | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 0adea0e38..ca6d7279b 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -259,7 +259,7 @@ Indentation Grammar, spelling, and capitalization preferences ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Use any commercial or free professional style guide in general. Use a spell* and grammar-checker. The following table lists the preferred grammar, spelling, and capitalization of words and phrases for frequently used items in the documentation. +Use any commercial or free professional style guide in general. Use a spell- and grammar-checker. The following table lists the preferred grammar, spelling, and capitalization of words and phrases for frequently used items in the documentation. ========== ===== Preferred Avoid @@ -303,7 +303,7 @@ As individual files do not have so-called "parts" or "chapters", the headings wo ^^^^^^^^^^^^^^^ Heading Level 4 - ``````````````` + """"""""""""""" The above code renders as follows. @@ -317,7 +317,7 @@ Heading Level 3 ^^^^^^^^^^^^^^^ Heading Level 4 -``````````````` +""""""""""""""" .. _style-guide-paragraphs: @@ -383,7 +383,7 @@ Code may be displayed in blocks or inline. You can include blocks of code from o .. _style-guide-syntax-highlighting: Syntax highlighting -``````````````````` +""""""""""""""""""" Sphinx does syntax highlighting of code blocks using the `Pygments `_ library. @@ -468,7 +468,7 @@ View the `full list of lexers and associated short names Date: Sat, 26 Nov 2016 00:06:41 -0800 Subject: minor gardening --- docs/style-guide.rst | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/style-guide.rst b/docs/style-guide.rst index ca6d7279b..70f899651 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -250,7 +250,7 @@ Trailing white spaces Indentation ^^^^^^^^^^^ -* Indent using four spaces. +* Indent using four spaces, except for :ref:`nested lists `. * Do not use tabs to indent. @@ -387,7 +387,7 @@ Syntax highlighting Sphinx does syntax highlighting of code blocks using the `Pygments `_ library. -Do not use two colons "::" at the end of a line, followed by a blank line, then indented code. Always specify the language to be used for syntax highlighting by using the ``code-block`` directive and indenting the code. +Do not use two colons "::" at the end of a line, followed by a blank line, then code. Always specify the language to be used for syntax highlighting by using a language argument in the ``code-block`` directive. Always indent the subsequent code. .. code-block:: rst @@ -449,16 +449,16 @@ Interactive Python: .. code-block:: pycon - >>> class Foo: - ... bar = 100 - ... - >>> f = Foo() - >>> f.bar - 100 - >>> f.bar / 0 - Traceback (most recent call last): - File "", line 1, in - ZeroDivisionError: integer division or modulo by zero + >>> class Foo: + ... bar = 100 + ... + >>> f = Foo() + >>> f.bar + 100 + >>> f.bar / 0 + Traceback (most recent call last): + File "", line 1, in + ZeroDivisionError: integer division or modulo by zero If syntax highlighting is not enabled for your code block, you probably have a syntax error and Pygments will fail silently. @@ -545,7 +545,7 @@ The above code renders as follows. # This is Python code pass -Code blocks may be given a caption, which may serve as a filename or other description, using the ``caption`` option. They may also be given a ``name`` option, providing an implicit target name that can be referenced by using ``ref``. +Code blocks may be given a caption, which may serve as a filename or other description, using the ``caption`` option. They may also be given a ``name`` option, providing an implicit target name that can be referenced by using ``ref`` (see :ref:`style-guide-cross-referencing-arbitrary-locations`). .. code-block:: rst @@ -578,7 +578,7 @@ To specify the starting number to use for line numbering, use the ``lineno-start # This is Python code pass -The above code renders as follows. As you can see, ``lineno-start`` is not altogether meaningful. +The above code renders as follows. As you can see, ``lineno-start`` is not altogether accurate. .. code-block:: python :lineno-start: 2 @@ -1171,15 +1171,15 @@ The above code renders as follows. :func:`pyramid.renderers.render_to_response` -Note that you can use either or combine the ``~`` and ``.`` prefixes. However, we prefer not to use the ``.`` prefix because Sphinx might generate an error if it cannot disambiguate the reference. +Note that you can use the ``~`` prefix to show only the last segment of a Python object's name. We prefer not to use the ``.`` prefix, even though it may seem to be a convenience to documentation authors, because Sphinx might generate an error if it cannot disambiguate the reference. .. code-block:: rst - :func:`~.render_to_response` + :func:`~pyramid.renderers.render_to_response` The above code renders as follows. -:func:`~.render_to_response` +:func:`~pyramid.renderers.render_to_response` .. _style-guide-role-app-pyramid: -- cgit v1.2.3 From 5f5e757dadae19a0c69069733a139eaf217f3ddd Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sat, 26 Nov 2016 00:10:34 -0800 Subject: add change note --- CHANGES.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 1939ad125..59f77368f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -115,6 +115,9 @@ Deprecations Documentation Changes --------------------- +- Replace Typographical Conventions with an enhanced Style Guide. + https://github.com/Pylons/pyramid/pull/2838 + - Add pyramid_nacl_session to session factories. See https://github.com/Pylons/pyramid/issues/2791 -- cgit v1.2.3 From c8a5e024478d274309935251d59cd20908a95067 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Sat, 26 Nov 2016 00:31:54 -0800 Subject: add 3.6 support to documentation - See #2835 --- CHANGES.txt | 3 +++ HACKING.txt | 4 ++-- RELEASING.txt | 4 ++-- docs/narr/install.rst | 2 +- docs/narr/introduction.rst | 2 +- docs/quick_tour.rst | 4 ++-- docs/quick_tutorial/requirements.rst | 6 +++--- docs/tutorials/wiki/installation.rst | 6 +++--- docs/tutorials/wiki2/installation.rst | 6 +++--- setup.py | 1 + 10 files changed, 21 insertions(+), 17 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index dac61678d..65a1f15cd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -42,6 +42,9 @@ Backward Incompatibilities Features -------- +- Python 3.6 compatibility. + https://github.com/Pylons/pyramid/issues/2835 + - pcreate learned about --package-name to allow you to create a new project in an existing folder with a different package name than the project name. See https://github.com/Pylons/pyramid/pull/2783 diff --git a/HACKING.txt b/HACKING.txt index f240492e7..bbebb5165 100644 --- a/HACKING.txt +++ b/HACKING.txt @@ -118,8 +118,8 @@ In order to add a feature to Pyramid: - The feature must be documented in both the API and narrative documentation (in ``docs/``). -- The feature must work fully on the following CPython versions: 2.7, 3.4, - and 3.5 on both UNIX and Windows. +- The feature must work fully on the following CPython versions: 2.7, 3.4, 3.5, + and 3.6 on both UNIX and Windows. - The feature must work on the latest version of PyPy. diff --git a/RELEASING.txt b/RELEASING.txt index 4690fbd37..73cf38aa7 100644 --- a/RELEASING.txt +++ b/RELEASING.txt @@ -33,8 +33,8 @@ Prepare new release branch - Run tests on Windows if feasible. -- Make sure all scaffold tests pass (CPython 2.7, 3.4, and 3.5, and PyPy on - UNIX; this doesn't work on Windows): +- Make sure all scaffold tests pass (CPython 2.7, 3.4, 3.5, and 3.6, and PyPy + on UNIX; this doesn't work on Windows): $ ./scaffoldtests.sh diff --git a/docs/narr/install.rst b/docs/narr/install.rst index 570cb2285..c3c2ba64c 100644 --- a/docs/narr/install.rst +++ b/docs/narr/install.rst @@ -22,7 +22,7 @@ the following sections. .. sidebar:: Python Versions As of this writing, :app:`Pyramid` is tested against Python 2.7, - Python 3.4, Python 3.5, PyPy. + Python 3.4, Python 3.5, Python 3.6, and PyPy. :app:`Pyramid` is known to run on all popular UNIX-like systems such as Linux, Mac OS X, and FreeBSD, as well as on Windows platforms. It is also known to diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst index 47638579b..adad196e4 100644 --- a/docs/narr/introduction.rst +++ b/docs/narr/introduction.rst @@ -860,7 +860,7 @@ 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 Travis, -and Jenkins on Python 2.7, Python 3.4, Python 3.5, and PyPy +and Jenkins on Python 2.7, Python 3.4, Python 3.5, Python 3.6, 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 diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst index 45c706b0d..5dc7d8816 100644 --- a/docs/quick_tour.rst +++ b/docs/quick_tour.rst @@ -26,7 +26,7 @@ To save a little bit of typing and to be certain that we use the modules, scripts, and packages installed in our virtual environment, we'll set an environment variable, too. -As an example, for Python 3.5+ on Linux: +As an example, for Python 3.6+ on Linux: .. parsed-literal:: @@ -729,7 +729,7 @@ This yields the following output. collected 1 items hello_world/tests.py . - ------------- coverage: platform darwin, python 3.5.0-final-0 ------------- + ------------- coverage: platform darwin, python 3.6.0-final-0 ------------- Name Stmts Miss Cover Missing -------------------------------------------------------- hello_world/__init__.py 11 8 27% 11-23 diff --git a/docs/quick_tutorial/requirements.rst b/docs/quick_tutorial/requirements.rst index afa8ed104..913e08a62 100644 --- a/docs/quick_tutorial/requirements.rst +++ b/docs/quick_tutorial/requirements.rst @@ -19,11 +19,11 @@ virtual environment.) This *Quick Tutorial* is based on: -* **Python 3.5**. Pyramid fully supports Python 3.4+ and Python 2.7+. This - tutorial uses **Python 3.5** but runs fine under Python 2.7. +* **Python 3.6**. Pyramid fully supports Python 3.4+ and Python 2.7+. This + tutorial uses **Python 3.6** but runs fine under Python 2.7. * **venv**. We believe in virtual environments. For this tutorial, we use - Python 3.5's built-in solution :term:`venv`. For Python 2.7, you can install + Python 3.6's built-in solution :term:`venv`. For Python 2.7, you can install :term:`virtualenv`. * **pip**. We use :term:`pip` for package management. diff --git a/docs/tutorials/wiki/installation.rst b/docs/tutorials/wiki/installation.rst index 03e183739..ec79a4e9c 100644 --- a/docs/tutorials/wiki/installation.rst +++ b/docs/tutorials/wiki/installation.rst @@ -66,7 +66,7 @@ Python 2.7: c:\> c:\Python27\Scripts\virtualenv %VENV% -Python 3.5: +Python 3.6: .. code-block:: doscon @@ -310,13 +310,13 @@ If successful, you will see output something like this: .. code-block:: bash ======================== test session starts ======================== - platform Python 3.5.1, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 + platform Python 3.6.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 rootdir: /Users/stevepiercy/projects/pyramidtut/tutorial, inifile: plugins: cov-2.2.1 collected 1 items tutorial/tests.py . - ------------------ coverage: platform Python 3.5.1 ------------------ + ------------------ coverage: platform Python 3.6.0 ------------------ Name Stmts Miss Cover Missing ---------------------------------------------------- tutorial/__init__.py 12 7 42% 7-8, 14-18 diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst index 75d5d4abd..fa990fb01 100644 --- a/docs/tutorials/wiki2/installation.rst +++ b/docs/tutorials/wiki2/installation.rst @@ -66,7 +66,7 @@ Python 2.7: c:\> c:\Python27\Scripts\virtualenv %VENV% -Python 3.5: +Python 3.6: .. code-block:: doscon @@ -327,13 +327,13 @@ If successful, you will see output something like this: .. code-block:: bash ======================== test session starts ======================== - platform Python 3.5.1, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 + platform Python 3.6.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 rootdir: /Users/stevepiercy/projects/pyramidtut/tutorial, inifile: plugins: cov-2.2.1 collected 2 items tutorial/tests.py .. - ------------------ coverage: platform Python 3.5.1 ------------------ + ------------------ coverage: platform Python 3.6.0 ------------------ Name Stmts Miss Cover Missing ---------------------------------------------------------------- tutorial/__init__.py 8 6 25% 7-12 diff --git a/setup.py b/setup.py index 36615f36b..450529bc4 100644 --- a/setup.py +++ b/setup.py @@ -83,6 +83,7 @@ setup(name='pyramid', "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Framework :: Pyramid", -- cgit v1.2.3 From fa0da6844171e4d983092de1421a385f81f604d2 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sat, 26 Nov 2016 15:19:52 -0600 Subject: only warn about py33, do not error this is squashed by pip unfortunately but it's better than a hard error --- setup.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 36615f36b..d437bb546 100644 --- a/setup.py +++ b/setup.py @@ -14,19 +14,21 @@ import os import sys +import warnings from setuptools import setup, find_packages py_version = sys.version_info[:2] -PY3 = py_version[0] == 3 +PY2 = py_version[0] == 2 -if PY3: - if py_version < (3, 4): - raise RuntimeError('On Python 3, Pyramid requires Python 3.4 or better') -else: - if py_version < (2, 7): - raise RuntimeError('On Python 2, Pyramid requires Python 2.7 or better') +if (3, 0) <= py_version < (3, 4): + warnings.warn( + 'On Python 3, Pyramid only supports Python 3.4 or better', + UserWarning, + ) +elif py_version < (2, 7): + raise RuntimeError('On Python 2, Pyramid requires Python 2.7 or better') here = os.path.abspath(os.path.dirname(__file__)) try: @@ -53,7 +55,7 @@ tests_require = [ 'WebTest >= 1.3.1', # py3 compat ] -if not PY3: +if PY2: tests_require.append('zope.component>=3.11.0') docs_extras = [ -- cgit v1.2.3 From af01a5ac5e31a778be8d634a560983fe818746bf Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Sun, 27 Nov 2016 17:09:32 -0600 Subject: fix nocover statements, should be "no cover" --- pyramid/tests/test_config/test_views.py | 4 ++-- pyramid/tests/test_httpexceptions.py | 2 +- pyramid/util.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyramid/tests/test_config/test_views.py b/pyramid/tests/test_config/test_views.py index f020485de..211632730 100644 --- a/pyramid/tests/test_config/test_views.py +++ b/pyramid/tests/test_config/test_views.py @@ -2309,9 +2309,9 @@ class TestViewsConfigurationMixin(unittest.TestCase): # Since Python 3 has to be all cool and fancy and different... def _assertBody(self, response, value): from pyramid.compat import text_type - if isinstance(value, text_type): # pragma: nocover + if isinstance(value, text_type): # pragma: no cover self.assertEqual(response.text, value) - else: # pragma: nocover + else: # pragma: no cover self.assertEqual(response.body, value) def test_add_notfound_view_with_renderer(self): diff --git a/pyramid/tests/test_httpexceptions.py b/pyramid/tests/test_httpexceptions.py index 6c6e16d55..224fa4cf0 100644 --- a/pyramid/tests/test_httpexceptions.py +++ b/pyramid/tests/test_httpexceptions.py @@ -348,7 +348,7 @@ class TestHTTPException(unittest.TestCase): exc = cls(body_template='${REQUEST_METHOD}') environ = _makeEnviron() class Choke(object): - def __str__(self): # pragma nocover + def __str__(self): # pragma no cover raise ValueError environ['gardentheory.user'] = Choke() start_response = DummyStartResponse() diff --git a/pyramid/util.py b/pyramid/util.py index 4936dcb24..d5b3c6d72 100644 --- a/pyramid/util.py +++ b/pyramid/util.py @@ -3,7 +3,7 @@ import functools try: # py2.7.7+ and py3.3+ have native comparison support from hmac import compare_digest -except ImportError: # pragma: nocover +except ImportError: # pragma: no cover compare_digest = None import inspect import traceback -- cgit v1.2.3 From b99ada66150f9155ed959338f2053e544a6c8265 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 28 Nov 2016 23:14:16 -0800 Subject: restore Typographical Conventions, an edited and condensed version of the Style Guide --- docs/index.rst | 5 +- docs/style-guide.rst | 34 ++- docs/typographical-conventions.rst | 427 +++++++++++++++++++++++++++++++++++++ 3 files changed, 442 insertions(+), 24 deletions(-) create mode 100644 docs/typographical-conventions.rst diff --git a/docs/index.rst b/docs/index.rst index f41154c19..a783e8a70 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -213,12 +213,13 @@ Copyright, Trademarks, and Attributions copyright -Style Guide -=========== +Typographical Conventions and Style Guide +========================================= .. toctree:: :maxdepth: 1 + typographical-conventions style-guide diff --git a/docs/style-guide.rst b/docs/style-guide.rst index 70f899651..bdca45a06 100644 --- a/docs/style-guide.rst +++ b/docs/style-guide.rst @@ -293,31 +293,21 @@ As individual files do not have so-called "parts" or "chapters", the headings wo .. code-block:: rst - Heading Level 1 - =============== + ================================== + The main heading or web page title + ================================== - Heading Level 2 + Heading Level 1 --------------- - Heading Level 3 + Heading Level 2 ^^^^^^^^^^^^^^^ - Heading Level 4 + Heading Level 3 """"""""""""""" -The above code renders as follows. - -Heading Level 1 -=============== - -Heading Level 2 ---------------- +Note, we do not render heading levels here because doing so causes a loss in page structure. -Heading Level 3 -^^^^^^^^^^^^^^^ - -Heading Level 4 -""""""""""""""" .. _style-guide-paragraphs: @@ -375,7 +365,7 @@ The above code renders as follows. Displaying code ^^^^^^^^^^^^^^^ -Code may be displayed in blocks or inline. You can include blocks of code from other source files. Blocks of code should use syntax highlighting. +Code may be displayed in blocks or inline. You can include blocks of code from other source files. Blocks of code should use syntax highlighting, and may use line numbering or emphasis. .. seealso:: See also the Sphinx documentation for :ref:`code-examples`. @@ -427,10 +417,10 @@ cfg: .. code-block:: cfg - [some-part] - # A random part in the buildout - recipe = collective.recipe.foo - option = value + [some-part] + # A random part in the buildout + recipe = collective.recipe.foo + option = value ini: diff --git a/docs/typographical-conventions.rst b/docs/typographical-conventions.rst new file mode 100644 index 000000000..cd4de8800 --- /dev/null +++ b/docs/typographical-conventions.rst @@ -0,0 +1,427 @@ +.. _typographical-conventions: + +Typographical Conventions +========================= + +.. meta:: + :description: This chapter describes typographical conventions used in the Pyramid documentation. + :keywords: Pyramid, Typographical Conventions + + +.. _typographical-conventions-introduction: + +Introduction +------------ + +This chapter describes typographical conventions used in the Pyramid documentation. Documentation authors and contributors should review the :ref:`style-guide`. + + +.. _typographical-conventions-glossary: + +Glossary +-------- + +A glossary defines terms used throughout the documentation. References to glossary terms appear as follows. + +:term:`request` + +Note it is hyperlinked, and when clicked it will take the user to the term in the Glossary and highlight the term. + + +.. _typographical-conventions-headings: + +Headings +-------- + +Sections, sub-sections, and sub-sub-sections within a web page or chapter are denoted with headings at various levels. The immediately preceding heading "Headings" is a section heading. Sub-section and sub-sub-section headings are shown as follows. + +Heading Level 2 +^^^^^^^^^^^^^^^ + +sub-section + +Heading Level 3 +""""""""""""""" + +sub-sub-section + + +.. _typographical-conventions-paragraphs: + +Paragraphs +---------- + +A paragraph of text looks exactly like this paragraph. + + +.. _typographical-conventions-links: + +Links +----- + +Links are presented as follows, and may be clickable. + +`TryPyramid `_ + +.. seealso:: See also :ref:`typographical-conventions-cross-references` for other links within the documentation. + + +.. _typographical-conventions-topic: + +Topic +----- + +A topic is similar to a block quote with a title, or a self-contained section with no subsections. A topic indicates a self-contained idea that is separate from the flow of the document. Topics may occur anywhere a section or transition may occur. + +.. topic:: Topic Title + + Subsequent indented lines comprise + the body of the topic, and are + interpreted as body elements. + + +.. _typographical-conventions-displaying-code: + +Code +---- + +Code may be displayed in blocks or inline. Blocks of code may use syntax highlighting, line numbering, and emphasis. + + +.. _typographical-conventions-syntax-highlighting: + +Syntax highlighting +^^^^^^^^^^^^^^^^^^^ + +XML: + +.. code-block:: xml + + Some XML + +Unix shell commands are prefixed with a ``$`` character. (See :term:`venv` for the meaning of ``$VENV``.) + +.. code-block:: bash + + $ $VENV/bin/pip install -e . + +Windows commands are prefixed with a drive letter with an optional directory name. (See :term:`venv` for the meaning of ``%VENV%``.) + +.. code-block:: doscon + + c:\> %VENV%\Scripts\pcreate -s starter MyProject + +cfg: + +.. code-block:: cfg + + [some-part] + # A random part in the buildout + recipe = collective.recipe.foo + option = value + +ini: + +.. code-block:: ini + + [nosetests] + match=^test + where=pyramid + nocapture=1 + +Interactive Python: + +.. code-block:: pycon + + >>> class Foo: + ... bar = 100 + ... + >>> f = Foo() + >>> f.bar + 100 + >>> f.bar / 0 + Traceback (most recent call last): + File "", line 1, in + ZeroDivisionError: integer division or modulo by zero + + +.. _typographical-conventions-long-commands: + +Displaying long commands +^^^^^^^^^^^^^^^^^^^^^^^^ + +When a command that should be typed on one line is too long to fit on the displayed width of a page, the backslash character ``\`` is used to indicate that the subsequent printed line should be part of the command: + +.. code-block:: bash + + $ $VENV/bin/py.test tutorial/tests.py --cov-report term-missing \ + --cov=tutorial -q + + +.. _typographical-conventions-code-block-options: + +Code block options +^^^^^^^^^^^^^^^^^^ + +To emphasize lines, we give the appearance that a highlighting pen has been used on the code. + +.. code-block:: python + :emphasize-lines: 1,3 + + if "foo" == "bar": + # This is Python code + pass + +A code block with line numbers. + +.. code-block:: python + :linenos: + + if "foo" == "bar": + # This is Python code + pass + +Some code blocks may be given a caption. + +.. code-block:: python + :caption: sample.py + :name: sample-py + + if "foo" == "bar": + # This is Python code + pass + + +.. _typographical-conventions-inline-code: + +Inline code +^^^^^^^^^^^ + +Inline code is displayed as follows, where the inline code is 'pip install -e ".[docs]"'. + +Install requirements for building documentation: ``pip install -e ".[docs]"`` + + +.. _typographical-conventions-lists: + +Lists +----- + +Bulleted lists display as follows. + +* This is an item in a bulleted list. +* This is another item in a bulleted list. + +Numbered lists display as follows. + +#. This is an item in a numbered list. +#. This is another item in a numbered list. + +Nested lists display as follows. + +#. This is a list item in the parent list. +#. This is another list item in the parent list. + + #. This is a list item in the child list. + #. This is another list item in the child list. + +#. This is one more list item in the parent list. + + +.. _typographical-conventions-tables: + +Tables +------ + +Tables display as follows. + +===== ===== +col 1 col 2 +===== ===== +1 Second column of row 1. +2 Second column of row 2. + Second line of paragraph. +3 * Second column of row 3. + + * Second item in bullet + list (row 3, column 2). +\ Row 4; column 1 will be empty. +===== ===== + + +.. _typographical-conventions-feature-versioning: + +Feature versioning +------------------ + +We designate the version in which something is added, changed, or deprecated in the project. + + +.. _typographical-conventions-version-added: + +Version added +^^^^^^^^^^^^^ + +The version in which a feature is added to a project is displayed as follows. + +.. versionadded:: 1.1 + :func:`pyramid.paster.bootstrap` + + +.. _typographical-conventions-version-changed: + +Version changed +^^^^^^^^^^^^^^^ + +The version in which a feature is changed in a project is displayed as follows. + +.. versionchanged:: 1.8 + Added the ability for ``bootstrap`` to cleanup automatically via the ``with`` statement. + + +.. _typographical-conventions-deprecated: + +Deprecated +^^^^^^^^^^ + +The version in which a feature is deprecated in a project is displayed as follows. + +.. deprecated:: 1.7 + Use the ``require_csrf`` option or read :ref:`auto_csrf_checking` instead to have :class:`pyramid.exceptions.BadCSRFToken` exceptions raised. + + +.. _typographical-conventions-danger: + +Danger +------ + +Danger represents critical information related to a topic or concept, and should recommend to the user "don't do this dangerous thing". + +.. danger:: + + This is danger or an error. + + +.. _typographical-conventions-warnings: + +Warnings +-------- + +Warnings represent limitations and advice related to a topic or concept. + +.. warning:: + + This is a warning. + + +.. _typographical-conventions-notes: + +Notes +----- + +Notes represent additional information related to a topic or concept. + +.. note:: + + This is a note. + + +.. _typographical-conventions-see-also: + +See also +-------- + +"See also" messages refer to topics that are related to the current topic, but have a narrative tone to them instead of merely a link without explanation. "See also" is rendered in a block as well, so that it stands out for the reader's attention. + +.. seealso:: + + See :ref:`Quick Tutorial section on Requirements `. + + +.. _typographical-conventions-todo: + +Todo +---- + +Todo items designated tasks that require further work. + +.. todo:: + + This is a todo item. + + +.. _typographical-conventions-italics: + +Italics +------- + +This *word* is italicized. + + +.. _typographical-conventions-strong: + +Strong +------ + +This **word** is in bold text. + + +.. _typographical-conventions-cross-references: + +Cross-references +---------------- + +Cross-references are links that may be to a document, arbitrary location, object, or other items. + + +.. _typographical-conventions-cross-referencing-documents: + +Cross-referencing documents +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Links to pages within this documentation display as follows. + +:doc:`quick_tour` + + +.. _typographical-conventions-cross-referencing-arbitrary-locations: + +Cross-referencing arbitrary locations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Links to sections, and tables and figures with captions, within this documentation display as follows. + +:ref:`i18n_chapter` + + +.. _typographical-conventions-cross-referencing-python: + +Python modules, classes, methods, and functions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +All of the following are clickable links to Python modules, classes, methods, and functions. + +Python module names display as follows. + +:mod:`pyramid.config` + +Python class names display as follows. + +:class:`pyramid.config.Configurator` + +Python method names display as follows. + +:meth:`pyramid.config.Configurator.add_view` + +Python function names display as follows. + +:func:`pyramid.renderers.render_to_response` + +Sometimes we show only the last segment of a Python object's name, which displays as follows. + +:func:`~pyramid.renderers.render_to_response` + +The application "Pyramid" itself displays as follows. + +:app:`Pyramid` + -- cgit v1.2.3 From fa842555b41f807c8f4a8528113d7742eb7633e8 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Mon, 28 Nov 2016 23:21:18 -0800 Subject: fix warning about duplicate name --- docs/typographical-conventions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/typographical-conventions.rst b/docs/typographical-conventions.rst index cd4de8800..5c7efea31 100644 --- a/docs/typographical-conventions.rst +++ b/docs/typographical-conventions.rst @@ -185,7 +185,7 @@ Some code blocks may be given a caption. .. code-block:: python :caption: sample.py - :name: sample-py + :name: sample-py-typographical-conventions if "foo" == "bar": # This is Python code -- cgit v1.2.3 From c7974fe0a3363d84afd0db506dbc71b97cb84247 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 5 Dec 2016 22:08:37 -0600 Subject: move predicates into top-level package instead of inside config this better mirrors the view derivers design where the config package uses implementations from the pyramid namespace --- pyramid/config/predicates.py | 303 +-------------- pyramid/config/routes.py | 10 +- pyramid/config/security.py | 2 +- pyramid/config/tweens.py | 2 +- pyramid/config/util.py | 12 - pyramid/config/views.py | 6 +- pyramid/predicates.py | 300 +++++++++++++++ pyramid/tests/test_config/test_predicates.py | 526 --------------------------- pyramid/tests/test_config/test_util.py | 11 +- pyramid/tests/test_predicates.py | 526 +++++++++++++++++++++++++++ pyramid/util.py | 15 +- 11 files changed, 860 insertions(+), 853 deletions(-) create mode 100644 pyramid/predicates.py delete mode 100644 pyramid/tests/test_config/test_predicates.py create mode 100644 pyramid/tests/test_predicates.py diff --git a/pyramid/config/predicates.py b/pyramid/config/predicates.py index 0b76bbd70..5d99d6564 100644 --- a/pyramid/config/predicates.py +++ b/pyramid/config/predicates.py @@ -1,301 +1,2 @@ -import re - -from pyramid.exceptions import ConfigurationError - -from pyramid.compat import is_nonstr_iter - -from pyramid.traversal import ( - find_interface, - traversal_path, - resource_path_tuple - ) - -from pyramid.urldispatch import _compile_route -from pyramid.util import object_description -from pyramid.session import check_csrf_token - -from .util import as_sorted_tuple - -_marker = object() - -class XHRPredicate(object): - def __init__(self, val, config): - self.val = bool(val) - - def text(self): - return 'xhr = %s' % self.val - - phash = text - - def __call__(self, context, request): - return bool(request.is_xhr) is self.val - -class RequestMethodPredicate(object): - def __init__(self, val, config): - request_method = as_sorted_tuple(val) - if 'GET' in request_method and 'HEAD' not in request_method: - # GET implies HEAD too - request_method = as_sorted_tuple(request_method + ('HEAD',)) - self.val = request_method - - def text(self): - return 'request_method = %s' % (','.join(self.val)) - - phash = text - - def __call__(self, context, request): - return request.method in self.val - -class PathInfoPredicate(object): - def __init__(self, val, config): - self.orig = val - try: - val = re.compile(val) - except re.error as why: - raise ConfigurationError(why.args[0]) - self.val = val - - def text(self): - return 'path_info = %s' % (self.orig,) - - phash = text - - def __call__(self, context, request): - return self.val.match(request.upath_info) is not None - -class RequestParamPredicate(object): - def __init__(self, val, config): - val = as_sorted_tuple(val) - reqs = [] - for p in val: - k = p - v = None - if p.startswith('='): - if '=' in p[1:]: - k, v = p[1:].split('=', 1) - k = '=' + k - k, v = k.strip(), v.strip() - elif '=' in p: - k, v = p.split('=', 1) - k, v = k.strip(), v.strip() - reqs.append((k, v)) - self.val = val - self.reqs = reqs - - def text(self): - return 'request_param %s' % ','.join( - ['%s=%s' % (x,y) if y else x for x, y in self.reqs] - ) - - phash = text - - def __call__(self, context, request): - for k, v in self.reqs: - actual = request.params.get(k) - if actual is None: - return False - if v is not None and actual != v: - return False - return True - -class HeaderPredicate(object): - def __init__(self, val, config): - name = val - v = None - if ':' in name: - name, val_str = name.split(':', 1) - try: - v = re.compile(val_str) - except re.error as why: - raise ConfigurationError(why.args[0]) - if v is None: - self._text = 'header %s' % (name,) - else: - self._text = 'header %s=%s' % (name, val_str) - self.name = name - self.val = v - - def text(self): - return self._text - - phash = text - - def __call__(self, context, request): - if self.val is None: - return self.name in request.headers - val = request.headers.get(self.name) - if val is None: - return False - return self.val.match(val) is not None - -class AcceptPredicate(object): - def __init__(self, val, config): - self.val = val - - def text(self): - return 'accept = %s' % (self.val,) - - phash = text - - def __call__(self, context, request): - return self.val in request.accept - -class ContainmentPredicate(object): - def __init__(self, val, config): - self.val = config.maybe_dotted(val) - - def text(self): - return 'containment = %s' % (self.val,) - - phash = text - - def __call__(self, context, request): - ctx = getattr(request, 'context', context) - return find_interface(ctx, self.val) is not None - -class RequestTypePredicate(object): - def __init__(self, val, config): - self.val = val - - def text(self): - return 'request_type = %s' % (self.val,) - - phash = text - - def __call__(self, context, request): - return self.val.providedBy(request) - -class MatchParamPredicate(object): - def __init__(self, val, config): - val = as_sorted_tuple(val) - self.val = val - reqs = [ p.split('=', 1) for p in val ] - self.reqs = [ (x.strip(), y.strip()) for x, y in reqs ] - - def text(self): - return 'match_param %s' % ','.join( - ['%s=%s' % (x,y) for x, y in self.reqs] - ) - - phash = text - - def __call__(self, context, request): - if not request.matchdict: - # might be None - return False - for k, v in self.reqs: - if request.matchdict.get(k) != v: - return False - return True - -class CustomPredicate(object): - def __init__(self, func, config): - self.func = func - - def text(self): - return getattr( - self.func, - '__text__', - 'custom predicate: %s' % object_description(self.func) - ) - - def phash(self): - # using hash() here rather than id() is intentional: we - # want to allow custom predicates that are part of - # frameworks to be able to define custom __hash__ - # functions for custom predicates, so that the hash output - # of predicate instances which are "logically the same" - # may compare equal. - return 'custom:%r' % hash(self.func) - - def __call__(self, context, request): - return self.func(context, request) - - -class TraversePredicate(object): - # Can only be used as a *route* "predicate"; it adds 'traverse' to the - # matchdict if it's specified in the routing args. This causes the - # ResourceTreeTraverser to use the resolved traverse pattern as the - # traversal path. - def __init__(self, val, config): - _, self.tgenerate = _compile_route(val) - self.val = val - - def text(self): - return 'traverse matchdict pseudo-predicate' - - def phash(self): - # This isn't actually a predicate, it's just a infodict modifier that - # injects ``traverse`` into the matchdict. As a result, we don't - # need to update the hash. - return '' - - def __call__(self, context, request): - if 'traverse' in context: - return True - m = context['match'] - tvalue = self.tgenerate(m) # tvalue will be urlquoted string - m['traverse'] = traversal_path(tvalue) - # This isn't actually a predicate, it's just a infodict modifier that - # injects ``traverse`` into the matchdict. As a result, we just - # return True. - return True - -class CheckCSRFTokenPredicate(object): - - check_csrf_token = staticmethod(check_csrf_token) # testing - - def __init__(self, val, config): - self.val = val - - def text(self): - return 'check_csrf = %s' % (self.val,) - - phash = text - - def __call__(self, context, request): - val = self.val - if val: - if val is True: - val = 'csrf_token' - return self.check_csrf_token(request, val, raises=False) - return True - -class PhysicalPathPredicate(object): - def __init__(self, val, config): - if is_nonstr_iter(val): - self.val = tuple(val) - else: - val = tuple(filter(None, val.split('/'))) - self.val = ('',) + val - - def text(self): - return 'physical_path = %s' % (self.val,) - - phash = text - - def __call__(self, context, request): - if getattr(context, '__name__', _marker) is not _marker: - return resource_path_tuple(context) == self.val - return False - -class EffectivePrincipalsPredicate(object): - def __init__(self, val, config): - if is_nonstr_iter(val): - self.val = set(val) - else: - self.val = set((val,)) - - def text(self): - return 'effective_principals = %s' % sorted(list(self.val)) - - phash = text - - def __call__(self, context, request): - req_principals = request.effective_principals - if is_nonstr_iter(req_principals): - rpset = set(req_principals) - if self.val.issubset(rpset): - return True - return False - +import zope.deprecation +zope.deprecation.moved('pyramid.predicates', 'Pyramid 2.0') diff --git a/pyramid/config/routes.py b/pyramid/config/routes.py index 90d4d47d2..203baa128 100644 --- a/pyramid/config/routes.py +++ b/pyramid/config/routes.py @@ -13,12 +13,10 @@ from pyramid.registry import predvalseq from pyramid.request import route_request_iface from pyramid.urldispatch import RoutesMapper -from pyramid.config.util import ( - action_method, - as_sorted_tuple, - ) +from pyramid.config.util import action_method +from pyramid.util import as_sorted_tuple -import pyramid.config.predicates +import pyramid.predicates class RoutesConfiguratorMixin(object): @action_method @@ -446,7 +444,7 @@ class RoutesConfiguratorMixin(object): ) def add_default_route_predicates(self): - p = pyramid.config.predicates + p = pyramid.predicates for (name, factory) in ( ('xhr', p.XHRPredicate), ('request_method', p.RequestMethodPredicate), diff --git a/pyramid/config/security.py b/pyramid/config/security.py index 02732c042..33593376b 100644 --- a/pyramid/config/security.py +++ b/pyramid/config/security.py @@ -9,9 +9,9 @@ from pyramid.interfaces import ( PHASE2_CONFIG, ) -from pyramid.config.util import as_sorted_tuple from pyramid.exceptions import ConfigurationError from pyramid.util import action_method +from pyramid.util import as_sorted_tuple class SecurityConfiguratorMixin(object): @action_method diff --git a/pyramid/config/tweens.py b/pyramid/config/tweens.py index 0aeb01fe3..16712ab16 100644 --- a/pyramid/config/tweens.py +++ b/pyramid/config/tweens.py @@ -17,9 +17,9 @@ from pyramid.tweens import ( from pyramid.config.util import ( action_method, - is_string_or_iterable, TopologicalSorter, ) +from pyramid.util import is_string_or_iterable class TweensConfiguratorMixin(object): def add_tween(self, tween_factory, under=None, over=None): diff --git a/pyramid/config/util.py b/pyramid/config/util.py index 626e8d5fe..b70dae7f3 100644 --- a/pyramid/config/util.py +++ b/pyramid/config/util.py @@ -24,18 +24,6 @@ ActionInfo = ActionInfo # support bw compat imports MAX_ORDER = 1 << 30 DEFAULT_PHASH = md5().hexdigest() -def is_string_or_iterable(v): - if isinstance(v, string_types): - return True - if hasattr(v, '__iter__'): - return True - -def as_sorted_tuple(val): - if not is_nonstr_iter(val): - val = (val,) - val = tuple(sorted(val)) - return val - class not_(object): """ diff --git a/pyramid/config/views.py b/pyramid/config/views.py index 6082d8b48..65c9da585 100644 --- a/pyramid/config/views.py +++ b/pyramid/config/views.py @@ -70,10 +70,11 @@ import pyramid.util from pyramid.util import ( viewdefaults, action_method, + as_sorted_tuple, TopologicalSorter, ) -import pyramid.config.predicates +import pyramid.predicates import pyramid.viewderivers from pyramid.viewderivers import ( @@ -89,7 +90,6 @@ from pyramid.viewderivers import ( from pyramid.config.util import ( DEFAULT_PHASH, MAX_ORDER, - as_sorted_tuple, ) urljoin = urlparse.urljoin @@ -1143,7 +1143,7 @@ class ViewsConfiguratorMixin(object): ) def add_default_view_predicates(self): - p = pyramid.config.predicates + p = pyramid.predicates for (name, factory) in ( ('xhr', p.XHRPredicate), ('request_method', p.RequestMethodPredicate), diff --git a/pyramid/predicates.py b/pyramid/predicates.py new file mode 100644 index 000000000..7c3a778ca --- /dev/null +++ b/pyramid/predicates.py @@ -0,0 +1,300 @@ +import re + +from pyramid.exceptions import ConfigurationError + +from pyramid.compat import is_nonstr_iter + +from pyramid.session import check_csrf_token +from pyramid.traversal import ( + find_interface, + traversal_path, + resource_path_tuple + ) + +from pyramid.urldispatch import _compile_route +from pyramid.util import object_description +from pyramid.util import as_sorted_tuple + +_marker = object() + +class XHRPredicate(object): + def __init__(self, val, config): + self.val = bool(val) + + def text(self): + return 'xhr = %s' % self.val + + phash = text + + def __call__(self, context, request): + return bool(request.is_xhr) is self.val + +class RequestMethodPredicate(object): + def __init__(self, val, config): + request_method = as_sorted_tuple(val) + if 'GET' in request_method and 'HEAD' not in request_method: + # GET implies HEAD too + request_method = as_sorted_tuple(request_method + ('HEAD',)) + self.val = request_method + + def text(self): + return 'request_method = %s' % (','.join(self.val)) + + phash = text + + def __call__(self, context, request): + return request.method in self.val + +class PathInfoPredicate(object): + def __init__(self, val, config): + self.orig = val + try: + val = re.compile(val) + except re.error as why: + raise ConfigurationError(why.args[0]) + self.val = val + + def text(self): + return 'path_info = %s' % (self.orig,) + + phash = text + + def __call__(self, context, request): + return self.val.match(request.upath_info) is not None + +class RequestParamPredicate(object): + def __init__(self, val, config): + val = as_sorted_tuple(val) + reqs = [] + for p in val: + k = p + v = None + if p.startswith('='): + if '=' in p[1:]: + k, v = p[1:].split('=', 1) + k = '=' + k + k, v = k.strip(), v.strip() + elif '=' in p: + k, v = p.split('=', 1) + k, v = k.strip(), v.strip() + reqs.append((k, v)) + self.val = val + self.reqs = reqs + + def text(self): + return 'request_param %s' % ','.join( + ['%s=%s' % (x,y) if y else x for x, y in self.reqs] + ) + + phash = text + + def __call__(self, context, request): + for k, v in self.reqs: + actual = request.params.get(k) + if actual is None: + return False + if v is not None and actual != v: + return False + return True + +class HeaderPredicate(object): + def __init__(self, val, config): + name = val + v = None + if ':' in name: + name, val_str = name.split(':', 1) + try: + v = re.compile(val_str) + except re.error as why: + raise ConfigurationError(why.args[0]) + if v is None: + self._text = 'header %s' % (name,) + else: + self._text = 'header %s=%s' % (name, val_str) + self.name = name + self.val = v + + def text(self): + return self._text + + phash = text + + def __call__(self, context, request): + if self.val is None: + return self.name in request.headers + val = request.headers.get(self.name) + if val is None: + return False + return self.val.match(val) is not None + +class AcceptPredicate(object): + def __init__(self, val, config): + self.val = val + + def text(self): + return 'accept = %s' % (self.val,) + + phash = text + + def __call__(self, context, request): + return self.val in request.accept + +class ContainmentPredicate(object): + def __init__(self, val, config): + self.val = config.maybe_dotted(val) + + def text(self): + return 'containment = %s' % (self.val,) + + phash = text + + def __call__(self, context, request): + ctx = getattr(request, 'context', context) + return find_interface(ctx, self.val) is not None + +class RequestTypePredicate(object): + def __init__(self, val, config): + self.val = val + + def text(self): + return 'request_type = %s' % (self.val,) + + phash = text + + def __call__(self, context, request): + return self.val.providedBy(request) + +class MatchParamPredicate(object): + def __init__(self, val, config): + val = as_sorted_tuple(val) + self.val = val + reqs = [ p.split('=', 1) for p in val ] + self.reqs = [ (x.strip(), y.strip()) for x, y in reqs ] + + def text(self): + return 'match_param %s' % ','.join( + ['%s=%s' % (x,y) for x, y in self.reqs] + ) + + phash = text + + def __call__(self, context, request): + if not request.matchdict: + # might be None + return False + for k, v in self.reqs: + if request.matchdict.get(k) != v: + return False + return True + +class CustomPredicate(object): + def __init__(self, func, config): + self.func = func + + def text(self): + return getattr( + self.func, + '__text__', + 'custom predicate: %s' % object_description(self.func) + ) + + def phash(self): + # using hash() here rather than id() is intentional: we + # want to allow custom predicates that are part of + # frameworks to be able to define custom __hash__ + # functions for custom predicates, so that the hash output + # of predicate instances which are "logically the same" + # may compare equal. + return 'custom:%r' % hash(self.func) + + def __call__(self, context, request): + return self.func(context, request) + + +class TraversePredicate(object): + # Can only be used as a *route* "predicate"; it adds 'traverse' to the + # matchdict if it's specified in the routing args. This causes the + # ResourceTreeTraverser to use the resolved traverse pattern as the + # traversal path. + def __init__(self, val, config): + _, self.tgenerate = _compile_route(val) + self.val = val + + def text(self): + return 'traverse matchdict pseudo-predicate' + + def phash(self): + # This isn't actually a predicate, it's just a infodict modifier that + # injects ``traverse`` into the matchdict. As a result, we don't + # need to update the hash. + return '' + + def __call__(self, context, request): + if 'traverse' in context: + return True + m = context['match'] + tvalue = self.tgenerate(m) # tvalue will be urlquoted string + m['traverse'] = traversal_path(tvalue) + # This isn't actually a predicate, it's just a infodict modifier that + # injects ``traverse`` into the matchdict. As a result, we just + # return True. + return True + +class CheckCSRFTokenPredicate(object): + + check_csrf_token = staticmethod(check_csrf_token) # testing + + def __init__(self, val, config): + self.val = val + + def text(self): + return 'check_csrf = %s' % (self.val,) + + phash = text + + def __call__(self, context, request): + val = self.val + if val: + if val is True: + val = 'csrf_token' + return self.check_csrf_token(request, val, raises=False) + return True + +class PhysicalPathPredicate(object): + def __init__(self, val, config): + if is_nonstr_iter(val): + self.val = tuple(val) + else: + val = tuple(filter(None, val.split('/'))) + self.val = ('',) + val + + def text(self): + return 'physical_path = %s' % (self.val,) + + phash = text + + def __call__(self, context, request): + if getattr(context, '__name__', _marker) is not _marker: + return resource_path_tuple(context) == self.val + return False + +class EffectivePrincipalsPredicate(object): + def __init__(self, val, config): + if is_nonstr_iter(val): + self.val = set(val) + else: + self.val = set((val,)) + + def text(self): + return 'effective_principals = %s' % sorted(list(self.val)) + + phash = text + + def __call__(self, context, request): + req_principals = request.effective_principals + if is_nonstr_iter(req_principals): + rpset = set(req_principals) + if self.val.issubset(rpset): + return True + return False + diff --git a/pyramid/tests/test_config/test_predicates.py b/pyramid/tests/test_config/test_predicates.py deleted file mode 100644 index 9cd8f2734..000000000 --- a/pyramid/tests/test_config/test_predicates.py +++ /dev/null @@ -1,526 +0,0 @@ -import unittest - -from pyramid import testing - -from pyramid.compat import text_ - -class TestXHRPredicate(unittest.TestCase): - def _makeOne(self, val): - from pyramid.config.predicates import XHRPredicate - return XHRPredicate(val, None) - - def test___call___true(self): - inst = self._makeOne(True) - request = Dummy() - request.is_xhr = True - result = inst(None, request) - self.assertTrue(result) - - def test___call___false(self): - inst = self._makeOne(True) - request = Dummy() - request.is_xhr = False - result = inst(None, request) - self.assertFalse(result) - - def test_text(self): - inst = self._makeOne(True) - self.assertEqual(inst.text(), 'xhr = True') - - def test_phash(self): - inst = self._makeOne(True) - self.assertEqual(inst.phash(), 'xhr = True') - -class TestRequestMethodPredicate(unittest.TestCase): - def _makeOne(self, val): - from pyramid.config.predicates import RequestMethodPredicate - return RequestMethodPredicate(val, None) - - def test_ctor_get_but_no_head(self): - inst = self._makeOne('GET') - self.assertEqual(inst.val, ('GET', 'HEAD')) - - def test___call___true_single(self): - inst = self._makeOne('GET') - request = Dummy() - request.method = 'GET' - result = inst(None, request) - self.assertTrue(result) - - def test___call___true_multi(self): - inst = self._makeOne(('GET','HEAD')) - request = Dummy() - request.method = 'GET' - result = inst(None, request) - self.assertTrue(result) - - def test___call___false(self): - inst = self._makeOne(('GET','HEAD')) - request = Dummy() - request.method = 'POST' - result = inst(None, request) - self.assertFalse(result) - - def test_text(self): - inst = self._makeOne(('HEAD','GET')) - self.assertEqual(inst.text(), 'request_method = GET,HEAD') - - def test_phash(self): - inst = self._makeOne(('HEAD','GET')) - self.assertEqual(inst.phash(), 'request_method = GET,HEAD') - -class TestPathInfoPredicate(unittest.TestCase): - def _makeOne(self, val): - from pyramid.config.predicates import PathInfoPredicate - return PathInfoPredicate(val, None) - - def test_ctor_compilefail(self): - from pyramid.exceptions import ConfigurationError - self.assertRaises(ConfigurationError, self._makeOne, '\\') - - def test___call___true(self): - inst = self._makeOne(r'/\d{2}') - request = Dummy() - request.upath_info = text_('/12') - result = inst(None, request) - self.assertTrue(result) - - def test___call___false(self): - inst = self._makeOne(r'/\d{2}') - request = Dummy() - request.upath_info = text_('/n12') - result = inst(None, request) - self.assertFalse(result) - - def test_text(self): - inst = self._makeOne('/') - self.assertEqual(inst.text(), 'path_info = /') - - def test_phash(self): - inst = self._makeOne('/') - self.assertEqual(inst.phash(), 'path_info = /') - -class TestRequestParamPredicate(unittest.TestCase): - def _makeOne(self, val): - from pyramid.config.predicates import RequestParamPredicate - return RequestParamPredicate(val, None) - - def test___call___true_exists(self): - inst = self._makeOne('abc') - request = Dummy() - request.params = {'abc':1} - result = inst(None, request) - self.assertTrue(result) - - def test___call___true_withval(self): - inst = self._makeOne('abc=1') - request = Dummy() - request.params = {'abc':'1'} - result = inst(None, request) - self.assertTrue(result) - - def test___call___true_multi(self): - inst = self._makeOne(('abc', '=def =2= ')) - request = Dummy() - request.params = {'abc':'1', '=def': '2='} - result = inst(None, request) - self.assertTrue(result) - - def test___call___false_multi(self): - inst = self._makeOne(('abc=3', 'def =2 ')) - request = Dummy() - request.params = {'abc':'3', 'def': '1'} - result = inst(None, request) - self.assertFalse(result) - - def test___call___false(self): - inst = self._makeOne('abc') - request = Dummy() - request.params = {} - result = inst(None, request) - self.assertFalse(result) - - def test_text_exists(self): - inst = self._makeOne('abc') - self.assertEqual(inst.text(), 'request_param abc') - - def test_text_exists_equal_sign(self): - inst = self._makeOne('=abc') - self.assertEqual(inst.text(), 'request_param =abc') - - def test_text_withval(self): - inst = self._makeOne('abc= 1') - self.assertEqual(inst.text(), 'request_param abc=1') - - def test_text_multi(self): - inst = self._makeOne(('abc= 1', 'def')) - self.assertEqual(inst.text(), 'request_param abc=1,def') - - def test_text_multi_equal_sign(self): - inst = self._makeOne(('abc= 1', '=def= 2')) - self.assertEqual(inst.text(), 'request_param =def=2,abc=1') - - def test_phash_exists(self): - inst = self._makeOne('abc') - self.assertEqual(inst.phash(), 'request_param abc') - - def test_phash_exists_equal_sign(self): - inst = self._makeOne('=abc') - self.assertEqual(inst.phash(), 'request_param =abc') - - def test_phash_withval(self): - inst = self._makeOne('abc= 1') - self.assertEqual(inst.phash(), "request_param abc=1") - -class TestMatchParamPredicate(unittest.TestCase): - def _makeOne(self, val): - from pyramid.config.predicates import MatchParamPredicate - return MatchParamPredicate(val, None) - - def test___call___true_single(self): - inst = self._makeOne('abc=1') - request = Dummy() - request.matchdict = {'abc':'1'} - result = inst(None, request) - self.assertTrue(result) - - - def test___call___true_multi(self): - inst = self._makeOne(('abc=1', 'def=2')) - request = Dummy() - request.matchdict = {'abc':'1', 'def':'2'} - result = inst(None, request) - self.assertTrue(result) - - def test___call___false(self): - inst = self._makeOne('abc=1') - request = Dummy() - request.matchdict = {} - result = inst(None, request) - self.assertFalse(result) - - def test___call___matchdict_is_None(self): - inst = self._makeOne('abc=1') - request = Dummy() - request.matchdict = None - result = inst(None, request) - self.assertFalse(result) - - def test_text(self): - inst = self._makeOne(('def= 1', 'abc =2')) - self.assertEqual(inst.text(), 'match_param abc=2,def=1') - - def test_phash(self): - inst = self._makeOne(('def= 1', 'abc =2')) - self.assertEqual(inst.phash(), 'match_param abc=2,def=1') - -class TestCustomPredicate(unittest.TestCase): - def _makeOne(self, val): - from pyramid.config.predicates import CustomPredicate - return CustomPredicate(val, None) - - def test___call___true(self): - def func(context, request): - self.assertEqual(context, None) - self.assertEqual(request, None) - return True - inst = self._makeOne(func) - result = inst(None, None) - self.assertTrue(result) - - def test___call___false(self): - def func(context, request): - self.assertEqual(context, None) - self.assertEqual(request, None) - return False - inst = self._makeOne(func) - result = inst(None, None) - self.assertFalse(result) - - def test_text_func_has___text__(self): - pred = predicate() - pred.__text__ = 'text' - inst = self._makeOne(pred) - self.assertEqual(inst.text(), 'text') - - def test_text_func_repr(self): - pred = predicate() - inst = self._makeOne(pred) - self.assertEqual(inst.text(), 'custom predicate: object predicate') - - def test_phash(self): - pred = predicate() - inst = self._makeOne(pred) - self.assertEqual(inst.phash(), 'custom:1') - -class TestTraversePredicate(unittest.TestCase): - def _makeOne(self, val): - from pyramid.config.predicates import TraversePredicate - return TraversePredicate(val, None) - - def test___call__traverse_has_remainder_already(self): - inst = self._makeOne('/1/:a/:b') - info = {'traverse':'abc'} - request = Dummy() - result = inst(info, request) - self.assertEqual(result, True) - self.assertEqual(info, {'traverse':'abc'}) - - def test___call__traverse_matches(self): - inst = self._makeOne('/1/:a/:b') - info = {'match':{'a':'a', 'b':'b'}} - request = Dummy() - result = inst(info, request) - self.assertEqual(result, True) - self.assertEqual(info, {'match': - {'a':'a', 'b':'b', 'traverse':('1', 'a', 'b')}}) - - def test___call__traverse_matches_with_highorder_chars(self): - inst = self._makeOne(text_(b'/La Pe\xc3\xb1a/{x}', 'utf-8')) - info = {'match':{'x':text_(b'Qu\xc3\xa9bec', 'utf-8')}} - request = Dummy() - result = inst(info, request) - self.assertEqual(result, True) - self.assertEqual( - info['match']['traverse'], - (text_(b'La Pe\xc3\xb1a', 'utf-8'), - text_(b'Qu\xc3\xa9bec', 'utf-8')) - ) - - def test_text(self): - inst = self._makeOne('/abc') - self.assertEqual(inst.text(), 'traverse matchdict pseudo-predicate') - - def test_phash(self): - inst = self._makeOne('/abc') - self.assertEqual(inst.phash(), '') - -class Test_CheckCSRFTokenPredicate(unittest.TestCase): - def _makeOne(self, val, config): - from pyramid.config.predicates import CheckCSRFTokenPredicate - return CheckCSRFTokenPredicate(val, config) - - def test_text(self): - inst = self._makeOne(True, None) - self.assertEqual(inst.text(), 'check_csrf = True') - - def test_phash(self): - inst = self._makeOne(True, None) - self.assertEqual(inst.phash(), 'check_csrf = True') - - def test_it_call_val_True(self): - inst = self._makeOne(True, None) - request = Dummy() - def check_csrf_token(req, val, raises=True): - self.assertEqual(req, request) - self.assertEqual(val, 'csrf_token') - self.assertEqual(raises, False) - return True - inst.check_csrf_token = check_csrf_token - result = inst(None, request) - self.assertEqual(result, True) - - def test_it_call_val_str(self): - inst = self._makeOne('abc', None) - request = Dummy() - def check_csrf_token(req, val, raises=True): - self.assertEqual(req, request) - self.assertEqual(val, 'abc') - self.assertEqual(raises, False) - return True - inst.check_csrf_token = check_csrf_token - result = inst(None, request) - self.assertEqual(result, True) - - def test_it_call_val_False(self): - inst = self._makeOne(False, None) - request = Dummy() - result = inst(None, request) - self.assertEqual(result, True) - -class TestHeaderPredicate(unittest.TestCase): - def _makeOne(self, val): - from pyramid.config.predicates import HeaderPredicate - return HeaderPredicate(val, None) - - def test___call___true_exists(self): - inst = self._makeOne('abc') - request = Dummy() - request.headers = {'abc':1} - result = inst(None, request) - self.assertTrue(result) - - def test___call___true_withval(self): - inst = self._makeOne('abc:1') - request = Dummy() - request.headers = {'abc':'1'} - result = inst(None, request) - self.assertTrue(result) - - def test___call___true_withregex(self): - inst = self._makeOne(r'abc:\d+') - request = Dummy() - request.headers = {'abc':'1'} - result = inst(None, request) - self.assertTrue(result) - - def test___call___false_withregex(self): - inst = self._makeOne(r'abc:\d+') - request = Dummy() - request.headers = {'abc':'a'} - result = inst(None, request) - self.assertFalse(result) - - def test___call___false(self): - inst = self._makeOne('abc') - request = Dummy() - request.headers = {} - result = inst(None, request) - self.assertFalse(result) - - def test_text_exists(self): - inst = self._makeOne('abc') - self.assertEqual(inst.text(), 'header abc') - - def test_text_withval(self): - inst = self._makeOne('abc:1') - self.assertEqual(inst.text(), 'header abc=1') - - def test_text_withregex(self): - inst = self._makeOne(r'abc:\d+') - self.assertEqual(inst.text(), r'header abc=\d+') - - def test_phash_exists(self): - inst = self._makeOne('abc') - self.assertEqual(inst.phash(), 'header abc') - - def test_phash_withval(self): - inst = self._makeOne('abc:1') - self.assertEqual(inst.phash(), "header abc=1") - - def test_phash_withregex(self): - inst = self._makeOne(r'abc:\d+') - self.assertEqual(inst.phash(), r'header abc=\d+') - -class Test_PhysicalPathPredicate(unittest.TestCase): - def _makeOne(self, val, config): - from pyramid.config.predicates import PhysicalPathPredicate - return PhysicalPathPredicate(val, config) - - def test_text(self): - inst = self._makeOne('/', None) - self.assertEqual(inst.text(), "physical_path = ('',)") - - def test_phash(self): - inst = self._makeOne('/', None) - self.assertEqual(inst.phash(), "physical_path = ('',)") - - def test_it_call_val_tuple_True(self): - inst = self._makeOne(('', 'abc'), None) - root = Dummy() - root.__name__ = '' - root.__parent__ = None - context = Dummy() - context.__name__ = 'abc' - context.__parent__ = root - self.assertTrue(inst(context, None)) - - def test_it_call_val_list_True(self): - inst = self._makeOne(['', 'abc'], None) - root = Dummy() - root.__name__ = '' - root.__parent__ = None - context = Dummy() - context.__name__ = 'abc' - context.__parent__ = root - self.assertTrue(inst(context, None)) - - def test_it_call_val_str_True(self): - inst = self._makeOne('/abc', None) - root = Dummy() - root.__name__ = '' - root.__parent__ = None - context = Dummy() - context.__name__ = 'abc' - context.__parent__ = root - self.assertTrue(inst(context, None)) - - def test_it_call_False(self): - inst = self._makeOne('/', None) - root = Dummy() - root.__name__ = '' - root.__parent__ = None - context = Dummy() - context.__name__ = 'abc' - context.__parent__ = root - self.assertFalse(inst(context, None)) - - def test_it_call_context_has_no_name(self): - inst = self._makeOne('/', None) - context = Dummy() - self.assertFalse(inst(context, None)) - -class Test_EffectivePrincipalsPredicate(unittest.TestCase): - def setUp(self): - self.config = testing.setUp() - - def tearDown(self): - testing.tearDown() - - def _makeOne(self, val, config): - from pyramid.config.predicates import EffectivePrincipalsPredicate - return EffectivePrincipalsPredicate(val, config) - - def test_text(self): - inst = self._makeOne(('verna', 'fred'), None) - self.assertEqual(inst.text(), - "effective_principals = ['fred', 'verna']") - - def test_text_noniter(self): - inst = self._makeOne('verna', None) - self.assertEqual(inst.text(), - "effective_principals = ['verna']") - - def test_phash(self): - inst = self._makeOne(('verna', 'fred'), None) - self.assertEqual(inst.phash(), - "effective_principals = ['fred', 'verna']") - - def test_it_call_no_authentication_policy(self): - request = testing.DummyRequest() - inst = self._makeOne(('verna', 'fred'), None) - context = Dummy() - self.assertFalse(inst(context, request)) - - def test_it_call_authentication_policy_provides_superset(self): - request = testing.DummyRequest() - self.config.testing_securitypolicy('fred', groupids=('verna', 'bambi')) - inst = self._makeOne(('verna', 'fred'), None) - context = Dummy() - self.assertTrue(inst(context, request)) - - def test_it_call_authentication_policy_provides_superset_implicit(self): - from pyramid.security import Authenticated - request = testing.DummyRequest() - self.config.testing_securitypolicy('fred', groupids=('verna', 'bambi')) - inst = self._makeOne(Authenticated, None) - context = Dummy() - self.assertTrue(inst(context, request)) - - def test_it_call_authentication_policy_doesnt_provide_superset(self): - request = testing.DummyRequest() - self.config.testing_securitypolicy('fred') - inst = self._makeOne(('verna', 'fred'), None) - context = Dummy() - self.assertFalse(inst(context, request)) - -class predicate(object): - def __repr__(self): - return 'predicate' - def __hash__(self): - return 1 - -class Dummy(object): - def __init__(self, **kw): - self.__dict__.update(**kw) - diff --git a/pyramid/tests/test_config/test_util.py b/pyramid/tests/test_config/test_util.py index ccf7fa260..398b6fba8 100644 --- a/pyramid/tests/test_config/test_util.py +++ b/pyramid/tests/test_config/test_util.py @@ -5,7 +5,7 @@ class TestPredicateList(unittest.TestCase): def _makeOne(self): from pyramid.config.util import PredicateList - from pyramid.config import predicates + from pyramid import predicates inst = PredicateList() for name, factory in ( ('xhr', predicates.XHRPredicate), @@ -594,6 +594,15 @@ class TestNotted(unittest.TestCase): self.assertEqual(inst.phash(), '') self.assertEqual(inst(None, None), True) + +class TestDeprecatedPredicates(unittest.TestCase): + def test_it(self): + import warnings + with warnings.catch_warnings(record=True) as w: + warnings.filterwarnings('always') + from pyramid.config.predicates import XHRPredicate + self.assertEqual(len(w), 1) + class DummyPredicate(object): def __init__(self, result): self.result = result diff --git a/pyramid/tests/test_predicates.py b/pyramid/tests/test_predicates.py new file mode 100644 index 000000000..8a002c24e --- /dev/null +++ b/pyramid/tests/test_predicates.py @@ -0,0 +1,526 @@ +import unittest + +from pyramid import testing + +from pyramid.compat import text_ + +class TestXHRPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import XHRPredicate + return XHRPredicate(val, None) + + def test___call___true(self): + inst = self._makeOne(True) + request = Dummy() + request.is_xhr = True + result = inst(None, request) + self.assertTrue(result) + + def test___call___false(self): + inst = self._makeOne(True) + request = Dummy() + request.is_xhr = False + result = inst(None, request) + self.assertFalse(result) + + def test_text(self): + inst = self._makeOne(True) + self.assertEqual(inst.text(), 'xhr = True') + + def test_phash(self): + inst = self._makeOne(True) + self.assertEqual(inst.phash(), 'xhr = True') + +class TestRequestMethodPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import RequestMethodPredicate + return RequestMethodPredicate(val, None) + + def test_ctor_get_but_no_head(self): + inst = self._makeOne('GET') + self.assertEqual(inst.val, ('GET', 'HEAD')) + + def test___call___true_single(self): + inst = self._makeOne('GET') + request = Dummy() + request.method = 'GET' + result = inst(None, request) + self.assertTrue(result) + + def test___call___true_multi(self): + inst = self._makeOne(('GET','HEAD')) + request = Dummy() + request.method = 'GET' + result = inst(None, request) + self.assertTrue(result) + + def test___call___false(self): + inst = self._makeOne(('GET','HEAD')) + request = Dummy() + request.method = 'POST' + result = inst(None, request) + self.assertFalse(result) + + def test_text(self): + inst = self._makeOne(('HEAD','GET')) + self.assertEqual(inst.text(), 'request_method = GET,HEAD') + + def test_phash(self): + inst = self._makeOne(('HEAD','GET')) + self.assertEqual(inst.phash(), 'request_method = GET,HEAD') + +class TestPathInfoPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import PathInfoPredicate + return PathInfoPredicate(val, None) + + def test_ctor_compilefail(self): + from pyramid.exceptions import ConfigurationError + self.assertRaises(ConfigurationError, self._makeOne, '\\') + + def test___call___true(self): + inst = self._makeOne(r'/\d{2}') + request = Dummy() + request.upath_info = text_('/12') + result = inst(None, request) + self.assertTrue(result) + + def test___call___false(self): + inst = self._makeOne(r'/\d{2}') + request = Dummy() + request.upath_info = text_('/n12') + result = inst(None, request) + self.assertFalse(result) + + def test_text(self): + inst = self._makeOne('/') + self.assertEqual(inst.text(), 'path_info = /') + + def test_phash(self): + inst = self._makeOne('/') + self.assertEqual(inst.phash(), 'path_info = /') + +class TestRequestParamPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import RequestParamPredicate + return RequestParamPredicate(val, None) + + def test___call___true_exists(self): + inst = self._makeOne('abc') + request = Dummy() + request.params = {'abc':1} + result = inst(None, request) + self.assertTrue(result) + + def test___call___true_withval(self): + inst = self._makeOne('abc=1') + request = Dummy() + request.params = {'abc':'1'} + result = inst(None, request) + self.assertTrue(result) + + def test___call___true_multi(self): + inst = self._makeOne(('abc', '=def =2= ')) + request = Dummy() + request.params = {'abc':'1', '=def': '2='} + result = inst(None, request) + self.assertTrue(result) + + def test___call___false_multi(self): + inst = self._makeOne(('abc=3', 'def =2 ')) + request = Dummy() + request.params = {'abc':'3', 'def': '1'} + result = inst(None, request) + self.assertFalse(result) + + def test___call___false(self): + inst = self._makeOne('abc') + request = Dummy() + request.params = {} + result = inst(None, request) + self.assertFalse(result) + + def test_text_exists(self): + inst = self._makeOne('abc') + self.assertEqual(inst.text(), 'request_param abc') + + def test_text_exists_equal_sign(self): + inst = self._makeOne('=abc') + self.assertEqual(inst.text(), 'request_param =abc') + + def test_text_withval(self): + inst = self._makeOne('abc= 1') + self.assertEqual(inst.text(), 'request_param abc=1') + + def test_text_multi(self): + inst = self._makeOne(('abc= 1', 'def')) + self.assertEqual(inst.text(), 'request_param abc=1,def') + + def test_text_multi_equal_sign(self): + inst = self._makeOne(('abc= 1', '=def= 2')) + self.assertEqual(inst.text(), 'request_param =def=2,abc=1') + + def test_phash_exists(self): + inst = self._makeOne('abc') + self.assertEqual(inst.phash(), 'request_param abc') + + def test_phash_exists_equal_sign(self): + inst = self._makeOne('=abc') + self.assertEqual(inst.phash(), 'request_param =abc') + + def test_phash_withval(self): + inst = self._makeOne('abc= 1') + self.assertEqual(inst.phash(), "request_param abc=1") + +class TestMatchParamPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import MatchParamPredicate + return MatchParamPredicate(val, None) + + def test___call___true_single(self): + inst = self._makeOne('abc=1') + request = Dummy() + request.matchdict = {'abc':'1'} + result = inst(None, request) + self.assertTrue(result) + + + def test___call___true_multi(self): + inst = self._makeOne(('abc=1', 'def=2')) + request = Dummy() + request.matchdict = {'abc':'1', 'def':'2'} + result = inst(None, request) + self.assertTrue(result) + + def test___call___false(self): + inst = self._makeOne('abc=1') + request = Dummy() + request.matchdict = {} + result = inst(None, request) + self.assertFalse(result) + + def test___call___matchdict_is_None(self): + inst = self._makeOne('abc=1') + request = Dummy() + request.matchdict = None + result = inst(None, request) + self.assertFalse(result) + + def test_text(self): + inst = self._makeOne(('def= 1', 'abc =2')) + self.assertEqual(inst.text(), 'match_param abc=2,def=1') + + def test_phash(self): + inst = self._makeOne(('def= 1', 'abc =2')) + self.assertEqual(inst.phash(), 'match_param abc=2,def=1') + +class TestCustomPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import CustomPredicate + return CustomPredicate(val, None) + + def test___call___true(self): + def func(context, request): + self.assertEqual(context, None) + self.assertEqual(request, None) + return True + inst = self._makeOne(func) + result = inst(None, None) + self.assertTrue(result) + + def test___call___false(self): + def func(context, request): + self.assertEqual(context, None) + self.assertEqual(request, None) + return False + inst = self._makeOne(func) + result = inst(None, None) + self.assertFalse(result) + + def test_text_func_has___text__(self): + pred = predicate() + pred.__text__ = 'text' + inst = self._makeOne(pred) + self.assertEqual(inst.text(), 'text') + + def test_text_func_repr(self): + pred = predicate() + inst = self._makeOne(pred) + self.assertEqual(inst.text(), 'custom predicate: object predicate') + + def test_phash(self): + pred = predicate() + inst = self._makeOne(pred) + self.assertEqual(inst.phash(), 'custom:1') + +class TestTraversePredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import TraversePredicate + return TraversePredicate(val, None) + + def test___call__traverse_has_remainder_already(self): + inst = self._makeOne('/1/:a/:b') + info = {'traverse':'abc'} + request = Dummy() + result = inst(info, request) + self.assertEqual(result, True) + self.assertEqual(info, {'traverse':'abc'}) + + def test___call__traverse_matches(self): + inst = self._makeOne('/1/:a/:b') + info = {'match':{'a':'a', 'b':'b'}} + request = Dummy() + result = inst(info, request) + self.assertEqual(result, True) + self.assertEqual(info, {'match': + {'a':'a', 'b':'b', 'traverse':('1', 'a', 'b')}}) + + def test___call__traverse_matches_with_highorder_chars(self): + inst = self._makeOne(text_(b'/La Pe\xc3\xb1a/{x}', 'utf-8')) + info = {'match':{'x':text_(b'Qu\xc3\xa9bec', 'utf-8')}} + request = Dummy() + result = inst(info, request) + self.assertEqual(result, True) + self.assertEqual( + info['match']['traverse'], + (text_(b'La Pe\xc3\xb1a', 'utf-8'), + text_(b'Qu\xc3\xa9bec', 'utf-8')) + ) + + def test_text(self): + inst = self._makeOne('/abc') + self.assertEqual(inst.text(), 'traverse matchdict pseudo-predicate') + + def test_phash(self): + inst = self._makeOne('/abc') + self.assertEqual(inst.phash(), '') + +class Test_CheckCSRFTokenPredicate(unittest.TestCase): + def _makeOne(self, val, config): + from pyramid.predicates import CheckCSRFTokenPredicate + return CheckCSRFTokenPredicate(val, config) + + def test_text(self): + inst = self._makeOne(True, None) + self.assertEqual(inst.text(), 'check_csrf = True') + + def test_phash(self): + inst = self._makeOne(True, None) + self.assertEqual(inst.phash(), 'check_csrf = True') + + def test_it_call_val_True(self): + inst = self._makeOne(True, None) + request = Dummy() + def check_csrf_token(req, val, raises=True): + self.assertEqual(req, request) + self.assertEqual(val, 'csrf_token') + self.assertEqual(raises, False) + return True + inst.check_csrf_token = check_csrf_token + result = inst(None, request) + self.assertEqual(result, True) + + def test_it_call_val_str(self): + inst = self._makeOne('abc', None) + request = Dummy() + def check_csrf_token(req, val, raises=True): + self.assertEqual(req, request) + self.assertEqual(val, 'abc') + self.assertEqual(raises, False) + return True + inst.check_csrf_token = check_csrf_token + result = inst(None, request) + self.assertEqual(result, True) + + def test_it_call_val_False(self): + inst = self._makeOne(False, None) + request = Dummy() + result = inst(None, request) + self.assertEqual(result, True) + +class TestHeaderPredicate(unittest.TestCase): + def _makeOne(self, val): + from pyramid.predicates import HeaderPredicate + return HeaderPredicate(val, None) + + def test___call___true_exists(self): + inst = self._makeOne('abc') + request = Dummy() + request.headers = {'abc':1} + result = inst(None, request) + self.assertTrue(result) + + def test___call___true_withval(self): + inst = self._makeOne('abc:1') + request = Dummy() + request.headers = {'abc':'1'} + result = inst(None, request) + self.assertTrue(result) + + def test___call___true_withregex(self): + inst = self._makeOne(r'abc:\d+') + request = Dummy() + request.headers = {'abc':'1'} + result = inst(None, request) + self.assertTrue(result) + + def test___call___false_withregex(self): + inst = self._makeOne(r'abc:\d+') + request = Dummy() + request.headers = {'abc':'a'} + result = inst(None, request) + self.assertFalse(result) + + def test___call___false(self): + inst = self._makeOne('abc') + request = Dummy() + request.headers = {} + result = inst(None, request) + self.assertFalse(result) + + def test_text_exists(self): + inst = self._makeOne('abc') + self.assertEqual(inst.text(), 'header abc') + + def test_text_withval(self): + inst = self._makeOne('abc:1') + self.assertEqual(inst.text(), 'header abc=1') + + def test_text_withregex(self): + inst = self._makeOne(r'abc:\d+') + self.assertEqual(inst.text(), r'header abc=\d+') + + def test_phash_exists(self): + inst = self._makeOne('abc') + self.assertEqual(inst.phash(), 'header abc') + + def test_phash_withval(self): + inst = self._makeOne('abc:1') + self.assertEqual(inst.phash(), "header abc=1") + + def test_phash_withregex(self): + inst = self._makeOne(r'abc:\d+') + self.assertEqual(inst.phash(), r'header abc=\d+') + +class Test_PhysicalPathPredicate(unittest.TestCase): + def _makeOne(self, val, config): + from pyramid.predicates import PhysicalPathPredicate + return PhysicalPathPredicate(val, config) + + def test_text(self): + inst = self._makeOne('/', None) + self.assertEqual(inst.text(), "physical_path = ('',)") + + def test_phash(self): + inst = self._makeOne('/', None) + self.assertEqual(inst.phash(), "physical_path = ('',)") + + def test_it_call_val_tuple_True(self): + inst = self._makeOne(('', 'abc'), None) + root = Dummy() + root.__name__ = '' + root.__parent__ = None + context = Dummy() + context.__name__ = 'abc' + context.__parent__ = root + self.assertTrue(inst(context, None)) + + def test_it_call_val_list_True(self): + inst = self._makeOne(['', 'abc'], None) + root = Dummy() + root.__name__ = '' + root.__parent__ = None + context = Dummy() + context.__name__ = 'abc' + context.__parent__ = root + self.assertTrue(inst(context, None)) + + def test_it_call_val_str_True(self): + inst = self._makeOne('/abc', None) + root = Dummy() + root.__name__ = '' + root.__parent__ = None + context = Dummy() + context.__name__ = 'abc' + context.__parent__ = root + self.assertTrue(inst(context, None)) + + def test_it_call_False(self): + inst = self._makeOne('/', None) + root = Dummy() + root.__name__ = '' + root.__parent__ = None + context = Dummy() + context.__name__ = 'abc' + context.__parent__ = root + self.assertFalse(inst(context, None)) + + def test_it_call_context_has_no_name(self): + inst = self._makeOne('/', None) + context = Dummy() + self.assertFalse(inst(context, None)) + +class Test_EffectivePrincipalsPredicate(unittest.TestCase): + def setUp(self): + self.config = testing.setUp() + + def tearDown(self): + testing.tearDown() + + def _makeOne(self, val, config): + from pyramid.predicates import EffectivePrincipalsPredicate + return EffectivePrincipalsPredicate(val, config) + + def test_text(self): + inst = self._makeOne(('verna', 'fred'), None) + self.assertEqual(inst.text(), + "effective_principals = ['fred', 'verna']") + + def test_text_noniter(self): + inst = self._makeOne('verna', None) + self.assertEqual(inst.text(), + "effective_principals = ['verna']") + + def test_phash(self): + inst = self._makeOne(('verna', 'fred'), None) + self.assertEqual(inst.phash(), + "effective_principals = ['fred', 'verna']") + + def test_it_call_no_authentication_policy(self): + request = testing.DummyRequest() + inst = self._makeOne(('verna', 'fred'), None) + context = Dummy() + self.assertFalse(inst(context, request)) + + def test_it_call_authentication_policy_provides_superset(self): + request = testing.DummyRequest() + self.config.testing_securitypolicy('fred', groupids=('verna', 'bambi')) + inst = self._makeOne(('verna', 'fred'), None) + context = Dummy() + self.assertTrue(inst(context, request)) + + def test_it_call_authentication_policy_provides_superset_implicit(self): + from pyramid.security import Authenticated + request = testing.DummyRequest() + self.config.testing_securitypolicy('fred', groupids=('verna', 'bambi')) + inst = self._makeOne(Authenticated, None) + context = Dummy() + self.assertTrue(inst(context, request)) + + def test_it_call_authentication_policy_doesnt_provide_superset(self): + request = testing.DummyRequest() + self.config.testing_securitypolicy('fred') + inst = self._makeOne(('verna', 'fred'), None) + context = Dummy() + self.assertFalse(inst(context, request)) + +class predicate(object): + def __repr__(self): + return 'predicate' + def __hash__(self): + return 1 + +class Dummy(object): + def __init__(self, **kw): + self.__dict__.update(**kw) + diff --git a/pyramid/util.py b/pyramid/util.py index d5b3c6d72..3337d410d 100644 --- a/pyramid/util.py +++ b/pyramid/util.py @@ -28,13 +28,24 @@ from pyramid.compat import ( from pyramid.interfaces import IActionInfo from pyramid.path import DottedNameResolver as _DottedNameResolver +_marker = object() + class DottedNameResolver(_DottedNameResolver): def __init__(self, package=None): # default to package = None for bw compat _DottedNameResolver.__init__(self, package) -_marker = object() - +def is_string_or_iterable(v): + if isinstance(v, string_types): + return True + if hasattr(v, '__iter__'): + return True + +def as_sorted_tuple(val): + if not is_nonstr_iter(val): + val = (val,) + val = tuple(sorted(val)) + return val class InstancePropertyHelper(object): """A helper object for assigning properties and descriptors to instances. -- cgit v1.2.3 From 1bf417e4c1b2865f44357a61ca16fadde310077e Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 6 Dec 2016 10:43:22 +0100 Subject: Changed 'host' and 'port' configuration to a new 'listen' style that is now supported by waitress server. --- docs/narr/MyProject/development.ini | 3 +-- docs/narr/MyProject/production.ini | 3 +-- docs/narr/project.rst | 11 +++++------ docs/narr/startup.rst | 4 ++-- docs/quick_tour/package/development.ini | 3 +-- docs/quick_tour/sqla_demo/development.ini | 3 +-- docs/quick_tour/sqla_demo/production.ini | 3 +-- docs/quick_tutorial/authentication/development.ini | 3 +-- docs/quick_tutorial/authorization/development.ini | 3 +-- docs/quick_tutorial/databases/development.ini | 3 +-- docs/quick_tutorial/debugtoolbar/development.ini | 3 +-- docs/quick_tutorial/forms/development.ini | 3 +-- docs/quick_tutorial/functional_testing/development.ini | 3 +-- docs/quick_tutorial/ini/development.ini | 3 +-- docs/quick_tutorial/jinja2/development.ini | 3 +-- docs/quick_tutorial/json/development.ini | 3 +-- docs/quick_tutorial/logging/development.ini | 3 +-- docs/quick_tutorial/more_view_classes/development.ini | 3 +-- docs/quick_tutorial/request_response/development.ini | 3 +-- docs/quick_tutorial/retail_forms/development.ini | 3 +-- docs/quick_tutorial/routing/development.ini | 3 +-- docs/quick_tutorial/scaffolds/development.ini | 3 +-- docs/quick_tutorial/scaffolds/production.ini | 3 +-- docs/quick_tutorial/sessions/development.ini | 3 +-- docs/quick_tutorial/static_assets/development.ini | 3 +-- docs/quick_tutorial/templating/development.ini | 3 +-- docs/quick_tutorial/unit_testing/development.ini | 3 +-- docs/quick_tutorial/view_classes/development.ini | 3 +-- docs/quick_tutorial/views/development.ini | 3 +-- docs/tutorials/wiki/src/authorization/development.ini | 3 +-- docs/tutorials/wiki/src/authorization/production.ini | 3 +-- docs/tutorials/wiki/src/basiclayout/development.ini | 3 +-- docs/tutorials/wiki/src/basiclayout/production.ini | 3 +-- docs/tutorials/wiki/src/installation/development.ini | 3 +-- docs/tutorials/wiki/src/installation/production.ini | 3 +-- docs/tutorials/wiki/src/models/development.ini | 3 +-- docs/tutorials/wiki/src/models/production.ini | 3 +-- docs/tutorials/wiki/src/tests/development.ini | 3 +-- docs/tutorials/wiki/src/tests/production.ini | 3 +-- docs/tutorials/wiki/src/views/development.ini | 3 +-- docs/tutorials/wiki/src/views/production.ini | 3 +-- docs/tutorials/wiki2/src/authentication/development.ini | 3 +-- docs/tutorials/wiki2/src/authentication/production.ini | 3 +-- docs/tutorials/wiki2/src/authorization/development.ini | 3 +-- docs/tutorials/wiki2/src/authorization/production.ini | 3 +-- docs/tutorials/wiki2/src/basiclayout/development.ini | 3 +-- docs/tutorials/wiki2/src/basiclayout/production.ini | 3 +-- docs/tutorials/wiki2/src/installation/development.ini | 3 +-- docs/tutorials/wiki2/src/installation/production.ini | 3 +-- docs/tutorials/wiki2/src/models/development.ini | 3 +-- docs/tutorials/wiki2/src/models/production.ini | 3 +-- docs/tutorials/wiki2/src/tests/development.ini | 3 +-- docs/tutorials/wiki2/src/tests/production.ini | 3 +-- docs/tutorials/wiki2/src/views/development.ini | 3 +-- docs/tutorials/wiki2/src/views/production.ini | 3 +-- pyramid/scaffolds/alchemy/development.ini_tmpl | 3 +-- pyramid/scaffolds/alchemy/production.ini_tmpl | 3 +-- pyramid/scaffolds/starter/development.ini_tmpl | 3 +-- pyramid/scaffolds/starter/production.ini_tmpl | 3 +-- pyramid/scaffolds/zodb/development.ini_tmpl | 3 +-- pyramid/scaffolds/zodb/production.ini_tmpl | 3 +-- .../test_scaffolds/fixture_scaffold/development.ini_tmpl | 3 +-- .../tests/test_scaffolds/fixture_scaffold/production.ini_tmpl | 3 +-- 63 files changed, 68 insertions(+), 130 deletions(-) diff --git a/docs/narr/MyProject/development.ini b/docs/narr/MyProject/development.ini index 94fece8ce..42c514794 100644 --- a/docs/narr/MyProject/development.ini +++ b/docs/narr/MyProject/development.ini @@ -24,8 +24,7 @@ pyramid.includes = [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +listen = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/narr/MyProject/production.ini b/docs/narr/MyProject/production.ini index 1174b1cc7..3114b9d4d 100644 --- a/docs/narr/MyProject/production.ini +++ b/docs/narr/MyProject/production.ini @@ -18,8 +18,7 @@ pyramid.default_locale_name = en [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 6c42881f4..ec26b880e 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -295,16 +295,15 @@ Here's sample output from a run of ``pserve`` on UNIX: Access is restricted such that only a browser running on the same machine as Pyramid will be able to access your Pyramid application. However, if you want to open access to other machines on the same network, then edit the -``development.ini`` file, and replace the ``host`` value in the -``[server:main]`` section, changing it from ``127.0.0.1`` to ``0.0.0.0``. For +``development.ini`` file, and replace the ``listen`` value in the +``[server:main]`` section, changing it from ``127.0.0.1:6543`` to ``0.0.0.0:6543``. For example: .. code-block:: ini [server:main] use = egg:waitress#main - host = 0.0.0.0 - port = 6543 + listen = 0.0.0.0:6543 Now when you use ``pserve`` to start the application, it will respond to requests on *all* IP addresses possessed by your system, not just requests to @@ -320,8 +319,8 @@ browser by visiting ``http://192.168.1.50:6543/``. You can change the port on which the server runs on by changing the same portion of the ``development.ini`` file. For example, you can change the -``port = 6543`` line in the ``development.ini`` file's ``[server:main]`` -section to ``port = 8080`` to run the server on port 8080 instead of port 6543. +``listen = 127.0.0.1:6543`` line in the ``development.ini`` file's ``[server:main]`` +section to ``listen = 127:0.0.1:8080`` to run the server on port 8080 instead of port 6543. You can shut down a server started this way by pressing ``Ctrl-C`` (or ``Ctrl-Break`` on Windows). diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst index 3e168eaea..b71cff57d 100644 --- a/docs/narr/startup.rst +++ b/docs/narr/startup.rst @@ -130,8 +130,8 @@ Here's a high-level time-ordered overview of what happens when you press #. ``pserve`` starts the WSGI *server* defined within the ``[server:main]`` section. In our case, this is the Waitress server (``use = - egg:waitress#main``), and it will listen on all interfaces (``host = - 127.0.0.1``), on port number 6543 (``port = 6543``). The server code itself + egg:waitress#main``), and it will listen on all interfaces (``listen = + 127.0.0.1:6543``), on port number 6543. The server code itself is what prints ``serving on http://127.0.0.1:6543``. The server serves the application, and the application is running, waiting to receive requests. diff --git a/docs/quick_tour/package/development.ini b/docs/quick_tour/package/development.ini index 20f9817a9..05571183d 100644 --- a/docs/quick_tour/package/development.ini +++ b/docs/quick_tour/package/development.ini @@ -25,8 +25,7 @@ pyramid.includes = [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +listen = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/quick_tour/sqla_demo/development.ini b/docs/quick_tour/sqla_demo/development.ini index 0db0950a0..a14402769 100644 --- a/docs/quick_tour/sqla_demo/development.ini +++ b/docs/quick_tour/sqla_demo/development.ini @@ -27,8 +27,7 @@ sqlalchemy.url = sqlite:///%(here)s/sqla_demo.sqlite [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +listen = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/quick_tour/sqla_demo/production.ini b/docs/quick_tour/sqla_demo/production.ini index 38f3b6318..e0bfd8ea0 100644 --- a/docs/quick_tour/sqla_demo/production.ini +++ b/docs/quick_tour/sqla_demo/production.ini @@ -18,8 +18,7 @@ sqlalchemy.url = sqlite:///%(here)s/sqla_demo.sqlite [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/quick_tutorial/authentication/development.ini b/docs/quick_tutorial/authentication/development.ini index 8a39b2fe7..cbf694b2c 100644 --- a/docs/quick_tutorial/authentication/development.ini +++ b/docs/quick_tutorial/authentication/development.ini @@ -7,5 +7,4 @@ tutorial.secret = 98zd [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/authorization/development.ini b/docs/quick_tutorial/authorization/development.ini index 8a39b2fe7..cbf694b2c 100644 --- a/docs/quick_tutorial/authorization/development.ini +++ b/docs/quick_tutorial/authorization/development.ini @@ -7,5 +7,4 @@ tutorial.secret = 98zd [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/databases/development.ini b/docs/quick_tutorial/databases/development.ini index 5da87d602..6145b5817 100644 --- a/docs/quick_tutorial/databases/development.ini +++ b/docs/quick_tutorial/databases/development.ini @@ -9,8 +9,7 @@ sqlalchemy.url = sqlite:///%(here)s/sqltutorial.sqlite [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 # Begin logging configuration diff --git a/docs/quick_tutorial/debugtoolbar/development.ini b/docs/quick_tutorial/debugtoolbar/development.ini index 52b2a3a41..c4b1b3c1d 100644 --- a/docs/quick_tutorial/debugtoolbar/development.ini +++ b/docs/quick_tutorial/debugtoolbar/development.ini @@ -5,5 +5,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/forms/development.ini b/docs/quick_tutorial/forms/development.ini index 4d47e54a5..7114a48bb 100644 --- a/docs/quick_tutorial/forms/development.ini +++ b/docs/quick_tutorial/forms/development.ini @@ -6,5 +6,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 \ No newline at end of file diff --git a/docs/quick_tutorial/functional_testing/development.ini b/docs/quick_tutorial/functional_testing/development.ini index 52b2a3a41..c4b1b3c1d 100644 --- a/docs/quick_tutorial/functional_testing/development.ini +++ b/docs/quick_tutorial/functional_testing/development.ini @@ -5,5 +5,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/ini/development.ini b/docs/quick_tutorial/ini/development.ini index 8853e2c2b..14dfcd36d 100644 --- a/docs/quick_tutorial/ini/development.ini +++ b/docs/quick_tutorial/ini/development.ini @@ -3,5 +3,4 @@ use = egg:tutorial [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/jinja2/development.ini b/docs/quick_tutorial/jinja2/development.ini index 4d47e54a5..e2b176d9c 100644 --- a/docs/quick_tutorial/jinja2/development.ini +++ b/docs/quick_tutorial/jinja2/development.ini @@ -6,5 +6,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/json/development.ini b/docs/quick_tutorial/json/development.ini index 4d47e54a5..e2b176d9c 100644 --- a/docs/quick_tutorial/json/development.ini +++ b/docs/quick_tutorial/json/development.ini @@ -6,5 +6,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/logging/development.ini b/docs/quick_tutorial/logging/development.ini index 62e0c5123..fa22c0c51 100644 --- a/docs/quick_tutorial/logging/development.ini +++ b/docs/quick_tutorial/logging/development.ini @@ -6,8 +6,7 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 # Begin logging configuration diff --git a/docs/quick_tutorial/more_view_classes/development.ini b/docs/quick_tutorial/more_view_classes/development.ini index 4d47e54a5..e2b176d9c 100644 --- a/docs/quick_tutorial/more_view_classes/development.ini +++ b/docs/quick_tutorial/more_view_classes/development.ini @@ -6,5 +6,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/request_response/development.ini b/docs/quick_tutorial/request_response/development.ini index 4d47e54a5..e2b176d9c 100644 --- a/docs/quick_tutorial/request_response/development.ini +++ b/docs/quick_tutorial/request_response/development.ini @@ -6,5 +6,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/retail_forms/development.ini b/docs/quick_tutorial/retail_forms/development.ini index 4d47e54a5..7114a48bb 100644 --- a/docs/quick_tutorial/retail_forms/development.ini +++ b/docs/quick_tutorial/retail_forms/development.ini @@ -6,5 +6,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 \ No newline at end of file diff --git a/docs/quick_tutorial/routing/development.ini b/docs/quick_tutorial/routing/development.ini index 4d47e54a5..e2b176d9c 100644 --- a/docs/quick_tutorial/routing/development.ini +++ b/docs/quick_tutorial/routing/development.ini @@ -6,5 +6,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/scaffolds/development.ini b/docs/quick_tutorial/scaffolds/development.ini index b31d06194..a9ad1e60d 100644 --- a/docs/quick_tutorial/scaffolds/development.ini +++ b/docs/quick_tutorial/scaffolds/development.ini @@ -24,8 +24,7 @@ pyramid.includes = [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/quick_tutorial/scaffolds/production.ini b/docs/quick_tutorial/scaffolds/production.ini index 1418e6bf6..64f04dbec 100644 --- a/docs/quick_tutorial/scaffolds/production.ini +++ b/docs/quick_tutorial/scaffolds/production.ini @@ -18,8 +18,7 @@ pyramid.default_locale_name = en [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/quick_tutorial/sessions/development.ini b/docs/quick_tutorial/sessions/development.ini index 4d47e54a5..e2b176d9c 100644 --- a/docs/quick_tutorial/sessions/development.ini +++ b/docs/quick_tutorial/sessions/development.ini @@ -6,5 +6,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/static_assets/development.ini b/docs/quick_tutorial/static_assets/development.ini index 4d47e54a5..e2b176d9c 100644 --- a/docs/quick_tutorial/static_assets/development.ini +++ b/docs/quick_tutorial/static_assets/development.ini @@ -6,5 +6,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/templating/development.ini b/docs/quick_tutorial/templating/development.ini index 4d47e54a5..e2b176d9c 100644 --- a/docs/quick_tutorial/templating/development.ini +++ b/docs/quick_tutorial/templating/development.ini @@ -6,5 +6,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/unit_testing/development.ini b/docs/quick_tutorial/unit_testing/development.ini index 52b2a3a41..c4b1b3c1d 100644 --- a/docs/quick_tutorial/unit_testing/development.ini +++ b/docs/quick_tutorial/unit_testing/development.ini @@ -5,5 +5,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/view_classes/development.ini b/docs/quick_tutorial/view_classes/development.ini index 4d47e54a5..e2b176d9c 100644 --- a/docs/quick_tutorial/view_classes/development.ini +++ b/docs/quick_tutorial/view_classes/development.ini @@ -6,5 +6,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/quick_tutorial/views/development.ini b/docs/quick_tutorial/views/development.ini index 52b2a3a41..c4b1b3c1d 100644 --- a/docs/quick_tutorial/views/development.ini +++ b/docs/quick_tutorial/views/development.ini @@ -5,5 +5,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 diff --git a/docs/tutorials/wiki/src/authorization/development.ini b/docs/tutorials/wiki/src/authorization/development.ini index 6bf4b198e..3679f2dcd 100644 --- a/docs/tutorials/wiki/src/authorization/development.ini +++ b/docs/tutorials/wiki/src/authorization/development.ini @@ -29,8 +29,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +listen = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/authorization/production.ini b/docs/tutorials/wiki/src/authorization/production.ini index 4e9892e7b..41a5f9907 100644 --- a/docs/tutorials/wiki/src/authorization/production.ini +++ b/docs/tutorials/wiki/src/authorization/production.ini @@ -24,8 +24,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/basiclayout/development.ini b/docs/tutorials/wiki/src/basiclayout/development.ini index 6bf4b198e..3679f2dcd 100644 --- a/docs/tutorials/wiki/src/basiclayout/development.ini +++ b/docs/tutorials/wiki/src/basiclayout/development.ini @@ -29,8 +29,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +listen = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/basiclayout/production.ini b/docs/tutorials/wiki/src/basiclayout/production.ini index 4e9892e7b..41a5f9907 100644 --- a/docs/tutorials/wiki/src/basiclayout/production.ini +++ b/docs/tutorials/wiki/src/basiclayout/production.ini @@ -24,8 +24,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/installation/development.ini b/docs/tutorials/wiki/src/installation/development.ini index 6bf4b198e..3679f2dcd 100644 --- a/docs/tutorials/wiki/src/installation/development.ini +++ b/docs/tutorials/wiki/src/installation/development.ini @@ -29,8 +29,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +listen = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/installation/production.ini b/docs/tutorials/wiki/src/installation/production.ini index 4e9892e7b..41a5f9907 100644 --- a/docs/tutorials/wiki/src/installation/production.ini +++ b/docs/tutorials/wiki/src/installation/production.ini @@ -24,8 +24,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/models/development.ini b/docs/tutorials/wiki/src/models/development.ini index 6bf4b198e..3679f2dcd 100644 --- a/docs/tutorials/wiki/src/models/development.ini +++ b/docs/tutorials/wiki/src/models/development.ini @@ -29,8 +29,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +listen = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/models/production.ini b/docs/tutorials/wiki/src/models/production.ini index 4e9892e7b..41a5f9907 100644 --- a/docs/tutorials/wiki/src/models/production.ini +++ b/docs/tutorials/wiki/src/models/production.ini @@ -24,8 +24,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/tests/development.ini b/docs/tutorials/wiki/src/tests/development.ini index 6bf4b198e..3679f2dcd 100644 --- a/docs/tutorials/wiki/src/tests/development.ini +++ b/docs/tutorials/wiki/src/tests/development.ini @@ -29,8 +29,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +listen = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/tests/production.ini b/docs/tutorials/wiki/src/tests/production.ini index 4e9892e7b..41a5f9907 100644 --- a/docs/tutorials/wiki/src/tests/production.ini +++ b/docs/tutorials/wiki/src/tests/production.ini @@ -24,8 +24,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/views/development.ini b/docs/tutorials/wiki/src/views/development.ini index 6bf4b198e..3679f2dcd 100644 --- a/docs/tutorials/wiki/src/views/development.ini +++ b/docs/tutorials/wiki/src/views/development.ini @@ -29,8 +29,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +listen = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/views/production.ini b/docs/tutorials/wiki/src/views/production.ini index 4e9892e7b..41a5f9907 100644 --- a/docs/tutorials/wiki/src/views/production.ini +++ b/docs/tutorials/wiki/src/views/production.ini @@ -24,8 +24,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/authentication/development.ini b/docs/tutorials/wiki2/src/authentication/development.ini index 4a6c9325c..89ee6388b 100644 --- a/docs/tutorials/wiki2/src/authentication/development.ini +++ b/docs/tutorials/wiki2/src/authentication/development.ini @@ -29,8 +29,7 @@ auth.secret = seekrit [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +list = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/authentication/production.ini b/docs/tutorials/wiki2/src/authentication/production.ini index a13a0ca19..4a5023c67 100644 --- a/docs/tutorials/wiki2/src/authentication/production.ini +++ b/docs/tutorials/wiki2/src/authentication/production.ini @@ -18,8 +18,7 @@ auth.secret = real-seekrit [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +list = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini index 4a6c9325c..62a7edd48 100644 --- a/docs/tutorials/wiki2/src/authorization/development.ini +++ b/docs/tutorials/wiki2/src/authorization/development.ini @@ -29,8 +29,7 @@ auth.secret = seekrit [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +listen = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/authorization/production.ini b/docs/tutorials/wiki2/src/authorization/production.ini index a13a0ca19..ac9672654 100644 --- a/docs/tutorials/wiki2/src/authorization/production.ini +++ b/docs/tutorials/wiki2/src/authorization/production.ini @@ -18,8 +18,7 @@ auth.secret = real-seekrit [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/basiclayout/development.ini b/docs/tutorials/wiki2/src/basiclayout/development.ini index 22b733e10..c15f0a483 100644 --- a/docs/tutorials/wiki2/src/basiclayout/development.ini +++ b/docs/tutorials/wiki2/src/basiclayout/development.ini @@ -27,8 +27,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +list = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/basiclayout/production.ini b/docs/tutorials/wiki2/src/basiclayout/production.ini index d2ecfe22a..215cafbcd 100644 --- a/docs/tutorials/wiki2/src/basiclayout/production.ini +++ b/docs/tutorials/wiki2/src/basiclayout/production.ini @@ -16,8 +16,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +list = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/installation/development.ini b/docs/tutorials/wiki2/src/installation/development.ini index 22b733e10..c15f0a483 100644 --- a/docs/tutorials/wiki2/src/installation/development.ini +++ b/docs/tutorials/wiki2/src/installation/development.ini @@ -27,8 +27,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +list = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/installation/production.ini b/docs/tutorials/wiki2/src/installation/production.ini index d2ecfe22a..215cafbcd 100644 --- a/docs/tutorials/wiki2/src/installation/production.ini +++ b/docs/tutorials/wiki2/src/installation/production.ini @@ -16,8 +16,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +list = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/models/development.ini b/docs/tutorials/wiki2/src/models/development.ini index 22b733e10..c15f0a483 100644 --- a/docs/tutorials/wiki2/src/models/development.ini +++ b/docs/tutorials/wiki2/src/models/development.ini @@ -27,8 +27,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +list = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/models/production.ini b/docs/tutorials/wiki2/src/models/production.ini index d2ecfe22a..215cafbcd 100644 --- a/docs/tutorials/wiki2/src/models/production.ini +++ b/docs/tutorials/wiki2/src/models/production.ini @@ -16,8 +16,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +list = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/tests/development.ini b/docs/tutorials/wiki2/src/tests/development.ini index 4a6c9325c..89ee6388b 100644 --- a/docs/tutorials/wiki2/src/tests/development.ini +++ b/docs/tutorials/wiki2/src/tests/development.ini @@ -29,8 +29,7 @@ auth.secret = seekrit [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +list = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/tests/production.ini b/docs/tutorials/wiki2/src/tests/production.ini index a13a0ca19..4a5023c67 100644 --- a/docs/tutorials/wiki2/src/tests/production.ini +++ b/docs/tutorials/wiki2/src/tests/production.ini @@ -18,8 +18,7 @@ auth.secret = real-seekrit [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +list = 0.0.0.0:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/views/development.ini b/docs/tutorials/wiki2/src/views/development.ini index 22b733e10..c15f0a483 100644 --- a/docs/tutorials/wiki2/src/views/development.ini +++ b/docs/tutorials/wiki2/src/views/development.ini @@ -27,8 +27,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +list = 127.0.0.1:6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/views/production.ini b/docs/tutorials/wiki2/src/views/production.ini index d2ecfe22a..215cafbcd 100644 --- a/docs/tutorials/wiki2/src/views/production.ini +++ b/docs/tutorials/wiki2/src/views/production.ini @@ -16,8 +16,7 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +list = 0.0.0.0:6543 ### # logging configuration diff --git a/pyramid/scaffolds/alchemy/development.ini_tmpl b/pyramid/scaffolds/alchemy/development.ini_tmpl index f8ee290be..fd16826cb 100644 --- a/pyramid/scaffolds/alchemy/development.ini_tmpl +++ b/pyramid/scaffolds/alchemy/development.ini_tmpl @@ -26,8 +26,7 @@ sqlalchemy.url = sqlite:///%(here)s/{{project}}.sqlite [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +listen = 127.0.0.1:6543 ### # logging configuration diff --git a/pyramid/scaffolds/alchemy/production.ini_tmpl b/pyramid/scaffolds/alchemy/production.ini_tmpl index 4d9f835d4..d6a7c5a7b 100644 --- a/pyramid/scaffolds/alchemy/production.ini_tmpl +++ b/pyramid/scaffolds/alchemy/production.ini_tmpl @@ -16,8 +16,7 @@ sqlalchemy.url = sqlite:///%(here)s/{{project}}.sqlite [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/pyramid/scaffolds/starter/development.ini_tmpl b/pyramid/scaffolds/starter/development.ini_tmpl index ae9460b11..18b2d7a2a 100644 --- a/pyramid/scaffolds/starter/development.ini_tmpl +++ b/pyramid/scaffolds/starter/development.ini_tmpl @@ -24,8 +24,7 @@ pyramid.includes = [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +listen = 127.0.0.1:6543 ### # logging configuration diff --git a/pyramid/scaffolds/starter/production.ini_tmpl b/pyramid/scaffolds/starter/production.ini_tmpl index b2681c71d..a55b4d2e0 100644 --- a/pyramid/scaffolds/starter/production.ini_tmpl +++ b/pyramid/scaffolds/starter/production.ini_tmpl @@ -18,8 +18,7 @@ pyramid.default_locale_name = en [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/pyramid/scaffolds/zodb/development.ini_tmpl b/pyramid/scaffolds/zodb/development.ini_tmpl index a44b61686..3f5c3710b 100644 --- a/pyramid/scaffolds/zodb/development.ini_tmpl +++ b/pyramid/scaffolds/zodb/development.ini_tmpl @@ -29,8 +29,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 127.0.0.1 -port = 6543 +listen = 127.0.0.1:6543 ### # logging configuration diff --git a/pyramid/scaffolds/zodb/production.ini_tmpl b/pyramid/scaffolds/zodb/production.ini_tmpl index 522ff7651..05be225ed 100644 --- a/pyramid/scaffolds/zodb/production.ini_tmpl +++ b/pyramid/scaffolds/zodb/production.ini_tmpl @@ -24,8 +24,7 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 ### # logging configuration diff --git a/pyramid/tests/test_scaffolds/fixture_scaffold/development.ini_tmpl b/pyramid/tests/test_scaffolds/fixture_scaffold/development.ini_tmpl index 4d3a80286..d9fd3dd33 100644 --- a/pyramid/tests/test_scaffolds/fixture_scaffold/development.ini_tmpl +++ b/pyramid/tests/test_scaffolds/fixture_scaffold/development.ini_tmpl @@ -11,8 +11,7 @@ pyramid.includes = pyramid_debugtoolbar [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 # Begin logging configuration diff --git a/pyramid/tests/test_scaffolds/fixture_scaffold/production.ini_tmpl b/pyramid/tests/test_scaffolds/fixture_scaffold/production.ini_tmpl index 931cfa510..5406fca80 100644 --- a/pyramid/tests/test_scaffolds/fixture_scaffold/production.ini_tmpl +++ b/pyramid/tests/test_scaffolds/fixture_scaffold/production.ini_tmpl @@ -10,8 +10,7 @@ pyramid.default_locale_name = en [server:main] use = egg:pyramid#wsgiref -host = 0.0.0.0 -port = 6543 +listen = 0.0.0.0:6543 # Begin logging configuration -- cgit v1.2.3 From 8bb7a4ba3593895a12ded27260ece7c1d673de56 Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 6 Dec 2016 11:05:34 +0100 Subject: fixing no new line at the end of file --- docs/quick_tutorial/retail_forms/development.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quick_tutorial/retail_forms/development.ini b/docs/quick_tutorial/retail_forms/development.ini index 7114a48bb..e2b176d9c 100644 --- a/docs/quick_tutorial/retail_forms/development.ini +++ b/docs/quick_tutorial/retail_forms/development.ini @@ -6,4 +6,4 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 \ No newline at end of file +listen = 0.0.0.0:6543 -- cgit v1.2.3 From 7363ebfe8f0e9b65e493780628a7e558dbb74741 Mon Sep 17 00:00:00 2001 From: Mikko Ohtamaa Date: Tue, 6 Dec 2016 11:25:07 +0100 Subject: Fix docs for Sphinx 1.5 --- CONTRIBUTORS.txt | 2 ++ docs/conf.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 98e243c1f..b4e30e085 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -286,3 +286,5 @@ Contributors - Keith Yang, 2016/07/22 - Moriyoshi Koizumi, 2016/11/20 + +- Mikko Ohtamaa, 2016/12/6 diff --git a/docs/conf.py b/docs/conf.py index c3a7170fc..f35f7b34f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -191,7 +191,7 @@ latex_documents = [ # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -latex_use_parts = True +latex_toplevel_sectioning = "section" # If false, no module index is generated. latex_use_modindex = False -- cgit v1.2.3 From 9d1d5776c76d63af6241a995ff648059f12d15ea Mon Sep 17 00:00:00 2001 From: Mikko Ohtamaa Date: Tue, 6 Dec 2016 11:32:23 +0100 Subject: Another Sphinx 1.5 configuraition file change. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index f35f7b34f..84fd5cf1b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -194,7 +194,7 @@ latex_documents = [ latex_toplevel_sectioning = "section" # If false, no module index is generated. -latex_use_modindex = False +latex_domain_indices = False ## Say, for a moment that you have a twoside document that needs a 3cm ## inner margin to allow for binding and at least two centimetres the -- cgit v1.2.3 From 9c120b5e9983c89bd9824269801b4d463d5c892a Mon Sep 17 00:00:00 2001 From: Mikko Ohtamaa Date: Tue, 6 Dec 2016 11:35:58 +0100 Subject: Fix pep8 error on config/util.py --- pyramid/config/util.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pyramid/config/util.py b/pyramid/config/util.py index b70dae7f3..67bba9593 100644 --- a/pyramid/config/util.py +++ b/pyramid/config/util.py @@ -4,8 +4,7 @@ import inspect from pyramid.compat import ( bytes_, getargspec, - is_nonstr_iter, - string_types, + is_nonstr_iter ) from pyramid.compat import im_func -- cgit v1.2.3 From fb45a123a26f29ad391f27e4f6f6ccc0818a68eb Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 6 Dec 2016 12:14:42 +0100 Subject: Signed CONTRIBUTORS.txt --- CONTRIBUTORS.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index b4e30e085..126b286f8 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -288,3 +288,5 @@ Contributors - Moriyoshi Koizumi, 2016/11/20 - Mikko Ohtamaa, 2016/12/6 + +- Martin Frlin, 2016/12/6 -- cgit v1.2.3 From 0705eeaa820b32a4c3f2a05df21e99077586cf5e Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 6 Dec 2016 13:16:19 +0100 Subject: Added changes for #2656 --- CHANGES.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 65a1f15cd..1c5f1a00e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -119,6 +119,9 @@ Features See https://github.com/Pylons/pyramid/pull/2805 +- Scaffolds, documentation and tutorials now use ``listen`` option instead + of ``host`` and ``port`` to configure Waitress server. + Bug Fixes --------- -- cgit v1.2.3 From b01a0233aa03b4b5a9ddd640a7a114f68d1c763d Mon Sep 17 00:00:00 2001 From: Martin Date: Tue, 6 Dec 2016 16:36:56 +0100 Subject: Changed wiki tutorial to showcase passwrd hashing with bcrypt. Relates to #2204 --- docs/tutorials/wiki/authorization.rst | 35 +++++++++++++++++++++- docs/tutorials/wiki/definingviews.rst | 1 + docs/tutorials/wiki/src/authorization/setup.py | 1 + .../wiki/src/authorization/tutorial/security.py | 15 ++++++++-- .../wiki/src/authorization/tutorial/views.py | 4 +-- docs/tutorials/wiki/src/tests/setup.py | 1 + docs/tutorials/wiki/src/tests/tutorial/security.py | 15 ++++++++-- docs/tutorials/wiki/src/tests/tutorial/tests.py | 11 +++++++ docs/tutorials/wiki/src/tests/tutorial/views.py | 4 +-- 9 files changed, 78 insertions(+), 9 deletions(-) diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index 44097b35b..699e34355 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -18,6 +18,7 @@ require permission, instead of a default "403 Forbidden" page. We will implement the access control with the following steps: +* Add password hashing dependencies * Add users and groups (``security.py``, a new module). * Add an :term:`ACL` (``models.py``). * Add an :term:`authentication policy` and an :term:`authorization policy` @@ -38,6 +39,25 @@ Then we will add the login and logout feature: Access control -------------- +Add dependencies +~~~~~~~~~~~~~~~~ + +Just like in :ref:`wiki_defining_views` we need a new dependency. +We need to add the ``bcrypt`` package, to our tutorial package's +``setup.py`` file by assigning this dependency to the ``requires`` parameter +in the ``setup()`` function. + +Open ``setup.py`` and edit it to look like the following: + +.. literalinclude:: src/authorization/setup.py + :linenos: + :emphasize-lines: 21 + :language: python + +Only the highlighted line needs to be added. + +Do not forget to run ``pip install -e .`` just like in :ref:`wiki-running-pip-install`. + Add users and groups ~~~~~~~~~~~~~~~~~~~~ @@ -61,7 +81,20 @@ request)`` returns ``None``. We will use ``groupfinder()`` as an :term:`authentication policy` "callback" that will provide the :term:`principal` or principals for a user. -In a production system, user and group data will most often come from a +There are two helper methods that will help us later when loging-in users. +The first is ``hash_password`` which takes a raw password and transforms it using +bcrypt into an irreversible representation, a process known as "hashing". The +second method, ``check_password``, will allow us to compare the hashed value of the +submitted password against the hashed value of the password stored in the user's +record. If the two hashed values match, then the submitted +password is valid, and we can authenticate the user. + +We hash passwords so that it is impossible to decrypt them and use them to +authenticate in the application. If we stored passwords foolishly in clear text, +then anyone with access to the database could retrieve any password to authenticate +as any user. + +In a production system, user and group data will most often be saved and come from a database, but here we use "dummy" data to represent user and groups sources. Add an ACL diff --git a/docs/tutorials/wiki/definingviews.rst b/docs/tutorials/wiki/definingviews.rst index ac94d8059..3859d2cad 100644 --- a/docs/tutorials/wiki/definingviews.rst +++ b/docs/tutorials/wiki/definingviews.rst @@ -52,6 +52,7 @@ Open ``setup.py`` and edit it to look like the following: Only the highlighted line needs to be added. +.. _wiki-running-pip-install: Running ``pip install -e .`` ============================ diff --git a/docs/tutorials/wiki/src/authorization/setup.py b/docs/tutorials/wiki/src/authorization/setup.py index beeed75c9..68e3c0abd 100644 --- a/docs/tutorials/wiki/src/authorization/setup.py +++ b/docs/tutorials/wiki/src/authorization/setup.py @@ -18,6 +18,7 @@ requires = [ 'ZODB3', 'waitress', 'docutils', + 'bcrypt', ] tests_require = [ diff --git a/docs/tutorials/wiki/src/authorization/tutorial/security.py b/docs/tutorials/wiki/src/authorization/tutorial/security.py index d88c9c71f..4115c780c 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/security.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/security.py @@ -1,5 +1,16 @@ -USERS = {'editor':'editor', - 'viewer':'viewer'} +import bcrypt + + +def hash_password(pw): + return bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) + +def check_password(expected_hash, pw): + if expected_hash is not None: + return bcrypt.checkpw(pw.encode('utf-8'), expected_hash) + return False + +USERS = {'editor': hash_password('editor'), + 'viewer': hash_password('viewer')} GROUPS = {'editor':['group:editors']} def groupfinder(userid, request): diff --git a/docs/tutorials/wiki/src/authorization/tutorial/views.py b/docs/tutorials/wiki/src/authorization/tutorial/views.py index c271d2cc1..e4560dfe1 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/views.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/views.py @@ -14,7 +14,7 @@ from pyramid.security import ( ) -from .security import USERS +from .security import USERS, check_password from .models import Page # regular expression used to find WikiWords @@ -94,7 +94,7 @@ def login(request): if 'form.submitted' in request.params: login = request.params['login'] password = request.params['password'] - if USERS.get(login) == password: + if check_password(USERS.get(login), password): headers = remember(request, login) return HTTPFound(location=came_from, headers=headers) diff --git a/docs/tutorials/wiki/src/tests/setup.py b/docs/tutorials/wiki/src/tests/setup.py index beeed75c9..68e3c0abd 100644 --- a/docs/tutorials/wiki/src/tests/setup.py +++ b/docs/tutorials/wiki/src/tests/setup.py @@ -18,6 +18,7 @@ requires = [ 'ZODB3', 'waitress', 'docutils', + 'bcrypt', ] tests_require = [ diff --git a/docs/tutorials/wiki/src/tests/tutorial/security.py b/docs/tutorials/wiki/src/tests/tutorial/security.py index d88c9c71f..4115c780c 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/security.py +++ b/docs/tutorials/wiki/src/tests/tutorial/security.py @@ -1,5 +1,16 @@ -USERS = {'editor':'editor', - 'viewer':'viewer'} +import bcrypt + + +def hash_password(pw): + return bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) + +def check_password(expected_hash, pw): + if expected_hash is not None: + return bcrypt.checkpw(pw.encode('utf-8'), expected_hash) + return False + +USERS = {'editor': hash_password('editor'), + 'viewer': hash_password('viewer')} GROUPS = {'editor':['group:editors']} def groupfinder(userid, request): diff --git a/docs/tutorials/wiki/src/tests/tutorial/tests.py b/docs/tutorials/wiki/src/tests/tutorial/tests.py index 04beaea44..098e9c1bd 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/tests.py +++ b/docs/tutorials/wiki/src/tests/tutorial/tests.py @@ -122,6 +122,17 @@ class EditPageTests(unittest.TestCase): self.assertEqual(response.location, 'http://example.com/') self.assertEqual(context.data, 'Hello yo!') +class SecurityTests(unittest.TestCase): + def test_hashing(self): + from .security import hash_password, check_password + password = 'secretpassword' + hashed_password = hash_password(password) + self.assertTrue(check_password(hashed_password, password)) + + self.assertFalse(check_password(hashed_password, 'attackerpassword')) + + self.assertFalse(check_password(None, password)) + class FunctionalTests(unittest.TestCase): viewer_login = '/login?login=viewer&password=viewer' \ diff --git a/docs/tutorials/wiki/src/tests/tutorial/views.py b/docs/tutorials/wiki/src/tests/tutorial/views.py index c271d2cc1..e4560dfe1 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/views.py +++ b/docs/tutorials/wiki/src/tests/tutorial/views.py @@ -14,7 +14,7 @@ from pyramid.security import ( ) -from .security import USERS +from .security import USERS, check_password from .models import Page # regular expression used to find WikiWords @@ -94,7 +94,7 @@ def login(request): if 'form.submitted' in request.params: login = request.params['login'] password = request.params['password'] - if USERS.get(login) == password: + if check_password(USERS.get(login), password): headers = remember(request, login) return HTTPFound(location=came_from, headers=headers) -- cgit v1.2.3 From 988c1f789faf9662abead1e1be40969be37867a8 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Tue, 6 Dec 2016 16:00:56 -0800 Subject: =?UTF-8?q?Revert=20"Changed=20'host'=20and=20'port'=20configurati?= =?UTF-8?q?on=20to=20a=20new=20'listen'=20style=20that=20=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES.txt | 3 --- CONTRIBUTORS.txt | 2 -- docs/narr/MyProject/development.ini | 3 ++- docs/narr/MyProject/production.ini | 3 ++- docs/narr/project.rst | 11 ++++++----- docs/narr/startup.rst | 4 ++-- docs/quick_tour/package/development.ini | 3 ++- docs/quick_tour/sqla_demo/development.ini | 3 ++- docs/quick_tour/sqla_demo/production.ini | 3 ++- docs/quick_tutorial/authentication/development.ini | 3 ++- docs/quick_tutorial/authorization/development.ini | 3 ++- docs/quick_tutorial/databases/development.ini | 3 ++- docs/quick_tutorial/debugtoolbar/development.ini | 3 ++- docs/quick_tutorial/forms/development.ini | 3 ++- docs/quick_tutorial/functional_testing/development.ini | 3 ++- docs/quick_tutorial/ini/development.ini | 3 ++- docs/quick_tutorial/jinja2/development.ini | 3 ++- docs/quick_tutorial/json/development.ini | 3 ++- docs/quick_tutorial/logging/development.ini | 3 ++- docs/quick_tutorial/more_view_classes/development.ini | 3 ++- docs/quick_tutorial/request_response/development.ini | 3 ++- docs/quick_tutorial/retail_forms/development.ini | 3 ++- docs/quick_tutorial/routing/development.ini | 3 ++- docs/quick_tutorial/scaffolds/development.ini | 3 ++- docs/quick_tutorial/scaffolds/production.ini | 3 ++- docs/quick_tutorial/sessions/development.ini | 3 ++- docs/quick_tutorial/static_assets/development.ini | 3 ++- docs/quick_tutorial/templating/development.ini | 3 ++- docs/quick_tutorial/unit_testing/development.ini | 3 ++- docs/quick_tutorial/view_classes/development.ini | 3 ++- docs/quick_tutorial/views/development.ini | 3 ++- docs/tutorials/wiki/src/authorization/development.ini | 3 ++- docs/tutorials/wiki/src/authorization/production.ini | 3 ++- docs/tutorials/wiki/src/basiclayout/development.ini | 3 ++- docs/tutorials/wiki/src/basiclayout/production.ini | 3 ++- docs/tutorials/wiki/src/installation/development.ini | 3 ++- docs/tutorials/wiki/src/installation/production.ini | 3 ++- docs/tutorials/wiki/src/models/development.ini | 3 ++- docs/tutorials/wiki/src/models/production.ini | 3 ++- docs/tutorials/wiki/src/tests/development.ini | 3 ++- docs/tutorials/wiki/src/tests/production.ini | 3 ++- docs/tutorials/wiki/src/views/development.ini | 3 ++- docs/tutorials/wiki/src/views/production.ini | 3 ++- docs/tutorials/wiki2/src/authentication/development.ini | 3 ++- docs/tutorials/wiki2/src/authentication/production.ini | 3 ++- docs/tutorials/wiki2/src/authorization/development.ini | 3 ++- docs/tutorials/wiki2/src/authorization/production.ini | 3 ++- docs/tutorials/wiki2/src/basiclayout/development.ini | 3 ++- docs/tutorials/wiki2/src/basiclayout/production.ini | 3 ++- docs/tutorials/wiki2/src/installation/development.ini | 3 ++- docs/tutorials/wiki2/src/installation/production.ini | 3 ++- docs/tutorials/wiki2/src/models/development.ini | 3 ++- docs/tutorials/wiki2/src/models/production.ini | 3 ++- docs/tutorials/wiki2/src/tests/development.ini | 3 ++- docs/tutorials/wiki2/src/tests/production.ini | 3 ++- docs/tutorials/wiki2/src/views/development.ini | 3 ++- docs/tutorials/wiki2/src/views/production.ini | 3 ++- pyramid/scaffolds/alchemy/development.ini_tmpl | 3 ++- pyramid/scaffolds/alchemy/production.ini_tmpl | 3 ++- pyramid/scaffolds/starter/development.ini_tmpl | 3 ++- pyramid/scaffolds/starter/production.ini_tmpl | 3 ++- pyramid/scaffolds/zodb/development.ini_tmpl | 3 ++- pyramid/scaffolds/zodb/production.ini_tmpl | 3 ++- .../test_scaffolds/fixture_scaffold/development.ini_tmpl | 3 ++- .../tests/test_scaffolds/fixture_scaffold/production.ini_tmpl | 3 ++- 65 files changed, 130 insertions(+), 73 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 1c5f1a00e..65a1f15cd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -119,9 +119,6 @@ Features See https://github.com/Pylons/pyramid/pull/2805 -- Scaffolds, documentation and tutorials now use ``listen`` option instead - of ``host`` and ``port`` to configure Waitress server. - Bug Fixes --------- diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 126b286f8..b4e30e085 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -288,5 +288,3 @@ Contributors - Moriyoshi Koizumi, 2016/11/20 - Mikko Ohtamaa, 2016/12/6 - -- Martin Frlin, 2016/12/6 diff --git a/docs/narr/MyProject/development.ini b/docs/narr/MyProject/development.ini index 42c514794..94fece8ce 100644 --- a/docs/narr/MyProject/development.ini +++ b/docs/narr/MyProject/development.ini @@ -24,7 +24,8 @@ pyramid.includes = [server:main] use = egg:waitress#main -listen = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/narr/MyProject/production.ini b/docs/narr/MyProject/production.ini index 3114b9d4d..1174b1cc7 100644 --- a/docs/narr/MyProject/production.ini +++ b/docs/narr/MyProject/production.ini @@ -18,7 +18,8 @@ pyramid.default_locale_name = en [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/narr/project.rst b/docs/narr/project.rst index ec26b880e..6c42881f4 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -295,15 +295,16 @@ Here's sample output from a run of ``pserve`` on UNIX: Access is restricted such that only a browser running on the same machine as Pyramid will be able to access your Pyramid application. However, if you want to open access to other machines on the same network, then edit the -``development.ini`` file, and replace the ``listen`` value in the -``[server:main]`` section, changing it from ``127.0.0.1:6543`` to ``0.0.0.0:6543``. For +``development.ini`` file, and replace the ``host`` value in the +``[server:main]`` section, changing it from ``127.0.0.1`` to ``0.0.0.0``. For example: .. code-block:: ini [server:main] use = egg:waitress#main - listen = 0.0.0.0:6543 + host = 0.0.0.0 + port = 6543 Now when you use ``pserve`` to start the application, it will respond to requests on *all* IP addresses possessed by your system, not just requests to @@ -319,8 +320,8 @@ browser by visiting ``http://192.168.1.50:6543/``. You can change the port on which the server runs on by changing the same portion of the ``development.ini`` file. For example, you can change the -``listen = 127.0.0.1:6543`` line in the ``development.ini`` file's ``[server:main]`` -section to ``listen = 127:0.0.1:8080`` to run the server on port 8080 instead of port 6543. +``port = 6543`` line in the ``development.ini`` file's ``[server:main]`` +section to ``port = 8080`` to run the server on port 8080 instead of port 6543. You can shut down a server started this way by pressing ``Ctrl-C`` (or ``Ctrl-Break`` on Windows). diff --git a/docs/narr/startup.rst b/docs/narr/startup.rst index b71cff57d..3e168eaea 100644 --- a/docs/narr/startup.rst +++ b/docs/narr/startup.rst @@ -130,8 +130,8 @@ Here's a high-level time-ordered overview of what happens when you press #. ``pserve`` starts the WSGI *server* defined within the ``[server:main]`` section. In our case, this is the Waitress server (``use = - egg:waitress#main``), and it will listen on all interfaces (``listen = - 127.0.0.1:6543``), on port number 6543. The server code itself + egg:waitress#main``), and it will listen on all interfaces (``host = + 127.0.0.1``), on port number 6543 (``port = 6543``). The server code itself is what prints ``serving on http://127.0.0.1:6543``. The server serves the application, and the application is running, waiting to receive requests. diff --git a/docs/quick_tour/package/development.ini b/docs/quick_tour/package/development.ini index 05571183d..20f9817a9 100644 --- a/docs/quick_tour/package/development.ini +++ b/docs/quick_tour/package/development.ini @@ -25,7 +25,8 @@ pyramid.includes = [server:main] use = egg:waitress#main -listen = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/quick_tour/sqla_demo/development.ini b/docs/quick_tour/sqla_demo/development.ini index a14402769..0db0950a0 100644 --- a/docs/quick_tour/sqla_demo/development.ini +++ b/docs/quick_tour/sqla_demo/development.ini @@ -27,7 +27,8 @@ sqlalchemy.url = sqlite:///%(here)s/sqla_demo.sqlite [server:main] use = egg:waitress#main -listen = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/quick_tour/sqla_demo/production.ini b/docs/quick_tour/sqla_demo/production.ini index e0bfd8ea0..38f3b6318 100644 --- a/docs/quick_tour/sqla_demo/production.ini +++ b/docs/quick_tour/sqla_demo/production.ini @@ -18,7 +18,8 @@ sqlalchemy.url = sqlite:///%(here)s/sqla_demo.sqlite [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/quick_tutorial/authentication/development.ini b/docs/quick_tutorial/authentication/development.ini index cbf694b2c..8a39b2fe7 100644 --- a/docs/quick_tutorial/authentication/development.ini +++ b/docs/quick_tutorial/authentication/development.ini @@ -7,4 +7,5 @@ tutorial.secret = 98zd [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/authorization/development.ini b/docs/quick_tutorial/authorization/development.ini index cbf694b2c..8a39b2fe7 100644 --- a/docs/quick_tutorial/authorization/development.ini +++ b/docs/quick_tutorial/authorization/development.ini @@ -7,4 +7,5 @@ tutorial.secret = 98zd [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/databases/development.ini b/docs/quick_tutorial/databases/development.ini index 6145b5817..5da87d602 100644 --- a/docs/quick_tutorial/databases/development.ini +++ b/docs/quick_tutorial/databases/development.ini @@ -9,7 +9,8 @@ sqlalchemy.url = sqlite:///%(here)s/sqltutorial.sqlite [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 # Begin logging configuration diff --git a/docs/quick_tutorial/debugtoolbar/development.ini b/docs/quick_tutorial/debugtoolbar/development.ini index c4b1b3c1d..52b2a3a41 100644 --- a/docs/quick_tutorial/debugtoolbar/development.ini +++ b/docs/quick_tutorial/debugtoolbar/development.ini @@ -5,4 +5,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/forms/development.ini b/docs/quick_tutorial/forms/development.ini index 7114a48bb..4d47e54a5 100644 --- a/docs/quick_tutorial/forms/development.ini +++ b/docs/quick_tutorial/forms/development.ini @@ -6,4 +6,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 \ No newline at end of file +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/functional_testing/development.ini b/docs/quick_tutorial/functional_testing/development.ini index c4b1b3c1d..52b2a3a41 100644 --- a/docs/quick_tutorial/functional_testing/development.ini +++ b/docs/quick_tutorial/functional_testing/development.ini @@ -5,4 +5,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/ini/development.ini b/docs/quick_tutorial/ini/development.ini index 14dfcd36d..8853e2c2b 100644 --- a/docs/quick_tutorial/ini/development.ini +++ b/docs/quick_tutorial/ini/development.ini @@ -3,4 +3,5 @@ use = egg:tutorial [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/jinja2/development.ini b/docs/quick_tutorial/jinja2/development.ini index e2b176d9c..4d47e54a5 100644 --- a/docs/quick_tutorial/jinja2/development.ini +++ b/docs/quick_tutorial/jinja2/development.ini @@ -6,4 +6,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/json/development.ini b/docs/quick_tutorial/json/development.ini index e2b176d9c..4d47e54a5 100644 --- a/docs/quick_tutorial/json/development.ini +++ b/docs/quick_tutorial/json/development.ini @@ -6,4 +6,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/logging/development.ini b/docs/quick_tutorial/logging/development.ini index fa22c0c51..62e0c5123 100644 --- a/docs/quick_tutorial/logging/development.ini +++ b/docs/quick_tutorial/logging/development.ini @@ -6,7 +6,8 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 # Begin logging configuration diff --git a/docs/quick_tutorial/more_view_classes/development.ini b/docs/quick_tutorial/more_view_classes/development.ini index e2b176d9c..4d47e54a5 100644 --- a/docs/quick_tutorial/more_view_classes/development.ini +++ b/docs/quick_tutorial/more_view_classes/development.ini @@ -6,4 +6,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/request_response/development.ini b/docs/quick_tutorial/request_response/development.ini index e2b176d9c..4d47e54a5 100644 --- a/docs/quick_tutorial/request_response/development.ini +++ b/docs/quick_tutorial/request_response/development.ini @@ -6,4 +6,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/retail_forms/development.ini b/docs/quick_tutorial/retail_forms/development.ini index e2b176d9c..4d47e54a5 100644 --- a/docs/quick_tutorial/retail_forms/development.ini +++ b/docs/quick_tutorial/retail_forms/development.ini @@ -6,4 +6,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/routing/development.ini b/docs/quick_tutorial/routing/development.ini index e2b176d9c..4d47e54a5 100644 --- a/docs/quick_tutorial/routing/development.ini +++ b/docs/quick_tutorial/routing/development.ini @@ -6,4 +6,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/scaffolds/development.ini b/docs/quick_tutorial/scaffolds/development.ini index a9ad1e60d..b31d06194 100644 --- a/docs/quick_tutorial/scaffolds/development.ini +++ b/docs/quick_tutorial/scaffolds/development.ini @@ -24,7 +24,8 @@ pyramid.includes = [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/quick_tutorial/scaffolds/production.ini b/docs/quick_tutorial/scaffolds/production.ini index 64f04dbec..1418e6bf6 100644 --- a/docs/quick_tutorial/scaffolds/production.ini +++ b/docs/quick_tutorial/scaffolds/production.ini @@ -18,7 +18,8 @@ pyramid.default_locale_name = en [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/quick_tutorial/sessions/development.ini b/docs/quick_tutorial/sessions/development.ini index e2b176d9c..4d47e54a5 100644 --- a/docs/quick_tutorial/sessions/development.ini +++ b/docs/quick_tutorial/sessions/development.ini @@ -6,4 +6,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/static_assets/development.ini b/docs/quick_tutorial/static_assets/development.ini index e2b176d9c..4d47e54a5 100644 --- a/docs/quick_tutorial/static_assets/development.ini +++ b/docs/quick_tutorial/static_assets/development.ini @@ -6,4 +6,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/templating/development.ini b/docs/quick_tutorial/templating/development.ini index e2b176d9c..4d47e54a5 100644 --- a/docs/quick_tutorial/templating/development.ini +++ b/docs/quick_tutorial/templating/development.ini @@ -6,4 +6,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/unit_testing/development.ini b/docs/quick_tutorial/unit_testing/development.ini index c4b1b3c1d..52b2a3a41 100644 --- a/docs/quick_tutorial/unit_testing/development.ini +++ b/docs/quick_tutorial/unit_testing/development.ini @@ -5,4 +5,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/view_classes/development.ini b/docs/quick_tutorial/view_classes/development.ini index e2b176d9c..4d47e54a5 100644 --- a/docs/quick_tutorial/view_classes/development.ini +++ b/docs/quick_tutorial/view_classes/development.ini @@ -6,4 +6,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/quick_tutorial/views/development.ini b/docs/quick_tutorial/views/development.ini index c4b1b3c1d..52b2a3a41 100644 --- a/docs/quick_tutorial/views/development.ini +++ b/docs/quick_tutorial/views/development.ini @@ -5,4 +5,5 @@ pyramid.includes = [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 diff --git a/docs/tutorials/wiki/src/authorization/development.ini b/docs/tutorials/wiki/src/authorization/development.ini index 3679f2dcd..6bf4b198e 100644 --- a/docs/tutorials/wiki/src/authorization/development.ini +++ b/docs/tutorials/wiki/src/authorization/development.ini @@ -29,7 +29,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/authorization/production.ini b/docs/tutorials/wiki/src/authorization/production.ini index 41a5f9907..4e9892e7b 100644 --- a/docs/tutorials/wiki/src/authorization/production.ini +++ b/docs/tutorials/wiki/src/authorization/production.ini @@ -24,7 +24,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/basiclayout/development.ini b/docs/tutorials/wiki/src/basiclayout/development.ini index 3679f2dcd..6bf4b198e 100644 --- a/docs/tutorials/wiki/src/basiclayout/development.ini +++ b/docs/tutorials/wiki/src/basiclayout/development.ini @@ -29,7 +29,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/basiclayout/production.ini b/docs/tutorials/wiki/src/basiclayout/production.ini index 41a5f9907..4e9892e7b 100644 --- a/docs/tutorials/wiki/src/basiclayout/production.ini +++ b/docs/tutorials/wiki/src/basiclayout/production.ini @@ -24,7 +24,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/installation/development.ini b/docs/tutorials/wiki/src/installation/development.ini index 3679f2dcd..6bf4b198e 100644 --- a/docs/tutorials/wiki/src/installation/development.ini +++ b/docs/tutorials/wiki/src/installation/development.ini @@ -29,7 +29,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/installation/production.ini b/docs/tutorials/wiki/src/installation/production.ini index 41a5f9907..4e9892e7b 100644 --- a/docs/tutorials/wiki/src/installation/production.ini +++ b/docs/tutorials/wiki/src/installation/production.ini @@ -24,7 +24,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/models/development.ini b/docs/tutorials/wiki/src/models/development.ini index 3679f2dcd..6bf4b198e 100644 --- a/docs/tutorials/wiki/src/models/development.ini +++ b/docs/tutorials/wiki/src/models/development.ini @@ -29,7 +29,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/models/production.ini b/docs/tutorials/wiki/src/models/production.ini index 41a5f9907..4e9892e7b 100644 --- a/docs/tutorials/wiki/src/models/production.ini +++ b/docs/tutorials/wiki/src/models/production.ini @@ -24,7 +24,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/tests/development.ini b/docs/tutorials/wiki/src/tests/development.ini index 3679f2dcd..6bf4b198e 100644 --- a/docs/tutorials/wiki/src/tests/development.ini +++ b/docs/tutorials/wiki/src/tests/development.ini @@ -29,7 +29,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/tests/production.ini b/docs/tutorials/wiki/src/tests/production.ini index 41a5f9907..4e9892e7b 100644 --- a/docs/tutorials/wiki/src/tests/production.ini +++ b/docs/tutorials/wiki/src/tests/production.ini @@ -24,7 +24,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/views/development.ini b/docs/tutorials/wiki/src/views/development.ini index 3679f2dcd..6bf4b198e 100644 --- a/docs/tutorials/wiki/src/views/development.ini +++ b/docs/tutorials/wiki/src/views/development.ini @@ -29,7 +29,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki/src/views/production.ini b/docs/tutorials/wiki/src/views/production.ini index 41a5f9907..4e9892e7b 100644 --- a/docs/tutorials/wiki/src/views/production.ini +++ b/docs/tutorials/wiki/src/views/production.ini @@ -24,7 +24,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/authentication/development.ini b/docs/tutorials/wiki2/src/authentication/development.ini index 89ee6388b..4a6c9325c 100644 --- a/docs/tutorials/wiki2/src/authentication/development.ini +++ b/docs/tutorials/wiki2/src/authentication/development.ini @@ -29,7 +29,8 @@ auth.secret = seekrit [server:main] use = egg:waitress#main -list = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/authentication/production.ini b/docs/tutorials/wiki2/src/authentication/production.ini index 4a5023c67..a13a0ca19 100644 --- a/docs/tutorials/wiki2/src/authentication/production.ini +++ b/docs/tutorials/wiki2/src/authentication/production.ini @@ -18,7 +18,8 @@ auth.secret = real-seekrit [server:main] use = egg:waitress#main -list = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/authorization/development.ini b/docs/tutorials/wiki2/src/authorization/development.ini index 62a7edd48..4a6c9325c 100644 --- a/docs/tutorials/wiki2/src/authorization/development.ini +++ b/docs/tutorials/wiki2/src/authorization/development.ini @@ -29,7 +29,8 @@ auth.secret = seekrit [server:main] use = egg:waitress#main -listen = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/authorization/production.ini b/docs/tutorials/wiki2/src/authorization/production.ini index ac9672654..a13a0ca19 100644 --- a/docs/tutorials/wiki2/src/authorization/production.ini +++ b/docs/tutorials/wiki2/src/authorization/production.ini @@ -18,7 +18,8 @@ auth.secret = real-seekrit [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/basiclayout/development.ini b/docs/tutorials/wiki2/src/basiclayout/development.ini index c15f0a483..22b733e10 100644 --- a/docs/tutorials/wiki2/src/basiclayout/development.ini +++ b/docs/tutorials/wiki2/src/basiclayout/development.ini @@ -27,7 +27,8 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -list = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/basiclayout/production.ini b/docs/tutorials/wiki2/src/basiclayout/production.ini index 215cafbcd..d2ecfe22a 100644 --- a/docs/tutorials/wiki2/src/basiclayout/production.ini +++ b/docs/tutorials/wiki2/src/basiclayout/production.ini @@ -16,7 +16,8 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -list = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/installation/development.ini b/docs/tutorials/wiki2/src/installation/development.ini index c15f0a483..22b733e10 100644 --- a/docs/tutorials/wiki2/src/installation/development.ini +++ b/docs/tutorials/wiki2/src/installation/development.ini @@ -27,7 +27,8 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -list = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/installation/production.ini b/docs/tutorials/wiki2/src/installation/production.ini index 215cafbcd..d2ecfe22a 100644 --- a/docs/tutorials/wiki2/src/installation/production.ini +++ b/docs/tutorials/wiki2/src/installation/production.ini @@ -16,7 +16,8 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -list = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/models/development.ini b/docs/tutorials/wiki2/src/models/development.ini index c15f0a483..22b733e10 100644 --- a/docs/tutorials/wiki2/src/models/development.ini +++ b/docs/tutorials/wiki2/src/models/development.ini @@ -27,7 +27,8 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -list = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/models/production.ini b/docs/tutorials/wiki2/src/models/production.ini index 215cafbcd..d2ecfe22a 100644 --- a/docs/tutorials/wiki2/src/models/production.ini +++ b/docs/tutorials/wiki2/src/models/production.ini @@ -16,7 +16,8 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -list = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/tests/development.ini b/docs/tutorials/wiki2/src/tests/development.ini index 89ee6388b..4a6c9325c 100644 --- a/docs/tutorials/wiki2/src/tests/development.ini +++ b/docs/tutorials/wiki2/src/tests/development.ini @@ -29,7 +29,8 @@ auth.secret = seekrit [server:main] use = egg:waitress#main -list = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/tests/production.ini b/docs/tutorials/wiki2/src/tests/production.ini index 4a5023c67..a13a0ca19 100644 --- a/docs/tutorials/wiki2/src/tests/production.ini +++ b/docs/tutorials/wiki2/src/tests/production.ini @@ -18,7 +18,8 @@ auth.secret = real-seekrit [server:main] use = egg:waitress#main -list = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/views/development.ini b/docs/tutorials/wiki2/src/views/development.ini index c15f0a483..22b733e10 100644 --- a/docs/tutorials/wiki2/src/views/development.ini +++ b/docs/tutorials/wiki2/src/views/development.ini @@ -27,7 +27,8 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -list = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/docs/tutorials/wiki2/src/views/production.ini b/docs/tutorials/wiki2/src/views/production.ini index 215cafbcd..d2ecfe22a 100644 --- a/docs/tutorials/wiki2/src/views/production.ini +++ b/docs/tutorials/wiki2/src/views/production.ini @@ -16,7 +16,8 @@ sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite [server:main] use = egg:waitress#main -list = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/pyramid/scaffolds/alchemy/development.ini_tmpl b/pyramid/scaffolds/alchemy/development.ini_tmpl index fd16826cb..f8ee290be 100644 --- a/pyramid/scaffolds/alchemy/development.ini_tmpl +++ b/pyramid/scaffolds/alchemy/development.ini_tmpl @@ -26,7 +26,8 @@ sqlalchemy.url = sqlite:///%(here)s/{{project}}.sqlite [server:main] use = egg:waitress#main -listen = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/pyramid/scaffolds/alchemy/production.ini_tmpl b/pyramid/scaffolds/alchemy/production.ini_tmpl index d6a7c5a7b..4d9f835d4 100644 --- a/pyramid/scaffolds/alchemy/production.ini_tmpl +++ b/pyramid/scaffolds/alchemy/production.ini_tmpl @@ -16,7 +16,8 @@ sqlalchemy.url = sqlite:///%(here)s/{{project}}.sqlite [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/pyramid/scaffolds/starter/development.ini_tmpl b/pyramid/scaffolds/starter/development.ini_tmpl index 18b2d7a2a..ae9460b11 100644 --- a/pyramid/scaffolds/starter/development.ini_tmpl +++ b/pyramid/scaffolds/starter/development.ini_tmpl @@ -24,7 +24,8 @@ pyramid.includes = [server:main] use = egg:waitress#main -listen = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/pyramid/scaffolds/starter/production.ini_tmpl b/pyramid/scaffolds/starter/production.ini_tmpl index a55b4d2e0..b2681c71d 100644 --- a/pyramid/scaffolds/starter/production.ini_tmpl +++ b/pyramid/scaffolds/starter/production.ini_tmpl @@ -18,7 +18,8 @@ pyramid.default_locale_name = en [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/pyramid/scaffolds/zodb/development.ini_tmpl b/pyramid/scaffolds/zodb/development.ini_tmpl index 3f5c3710b..a44b61686 100644 --- a/pyramid/scaffolds/zodb/development.ini_tmpl +++ b/pyramid/scaffolds/zodb/development.ini_tmpl @@ -29,7 +29,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 127.0.0.1:6543 +host = 127.0.0.1 +port = 6543 ### # logging configuration diff --git a/pyramid/scaffolds/zodb/production.ini_tmpl b/pyramid/scaffolds/zodb/production.ini_tmpl index 05be225ed..522ff7651 100644 --- a/pyramid/scaffolds/zodb/production.ini_tmpl +++ b/pyramid/scaffolds/zodb/production.ini_tmpl @@ -24,7 +24,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000 [server:main] use = egg:waitress#main -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 ### # logging configuration diff --git a/pyramid/tests/test_scaffolds/fixture_scaffold/development.ini_tmpl b/pyramid/tests/test_scaffolds/fixture_scaffold/development.ini_tmpl index d9fd3dd33..4d3a80286 100644 --- a/pyramid/tests/test_scaffolds/fixture_scaffold/development.ini_tmpl +++ b/pyramid/tests/test_scaffolds/fixture_scaffold/development.ini_tmpl @@ -11,7 +11,8 @@ pyramid.includes = pyramid_debugtoolbar [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 # Begin logging configuration diff --git a/pyramid/tests/test_scaffolds/fixture_scaffold/production.ini_tmpl b/pyramid/tests/test_scaffolds/fixture_scaffold/production.ini_tmpl index 5406fca80..931cfa510 100644 --- a/pyramid/tests/test_scaffolds/fixture_scaffold/production.ini_tmpl +++ b/pyramid/tests/test_scaffolds/fixture_scaffold/production.ini_tmpl @@ -10,7 +10,8 @@ pyramid.default_locale_name = en [server:main] use = egg:pyramid#wsgiref -listen = 0.0.0.0:6543 +host = 0.0.0.0 +port = 6543 # Begin logging configuration -- cgit v1.2.3 From b4abcd1f596297eb083e855d5e9a158d9e108c81 Mon Sep 17 00:00:00 2001 From: Martin Date: Wed, 7 Dec 2016 09:43:22 +0100 Subject: Hashing helpers now deal in unicode. Fixed wording. Added link to bcrypt and a footnote from wiki2 example. --- docs/tutorials/wiki/authorization.rst | 16 ++++++++++++---- .../wiki/src/authorization/tutorial/security.py | 6 ++++-- docs/tutorials/wiki/src/tests/tutorial/security.py | 6 ++++-- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index 699e34355..523acc53b 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -43,7 +43,7 @@ Add dependencies ~~~~~~~~~~~~~~~~ Just like in :ref:`wiki_defining_views` we need a new dependency. -We need to add the ``bcrypt`` package, to our tutorial package's +We need to add the ``bcrypt`` [1]_ package, to our tutorial package's ``setup.py`` file by assigning this dependency to the ``requires`` parameter in the ``setup()`` function. @@ -81,15 +81,15 @@ request)`` returns ``None``. We will use ``groupfinder()`` as an :term:`authentication policy` "callback" that will provide the :term:`principal` or principals for a user. -There are two helper methods that will help us later when loging-in users. +There are two helper methods that will help us later to authenticate users. The first is ``hash_password`` which takes a raw password and transforms it using -bcrypt into an irreversible representation, a process known as "hashing". The +bcrypt_ into an irreversible representation, a process known as "hashing". The second method, ``check_password``, will allow us to compare the hashed value of the submitted password against the hashed value of the password stored in the user's record. If the two hashed values match, then the submitted password is valid, and we can authenticate the user. -We hash passwords so that it is impossible to decrypt them and use them to +We hash passwords so that it is impossible to decrypt and use them to authenticate in the application. If we stored passwords foolishly in clear text, then anyone with access to the database could retrieve any password to authenticate as any user. @@ -403,3 +403,11 @@ following URLs, checking that the result is as expected: the login form with the ``editor`` credentials), we'll see a Logout link in the upper right hand corner. When we click it, we're logged out, and redirected back to the front page. + + +.. _bcrypt: https://pypi.python.org/pypi/bcrypt + +.. [1] We are using the bcrypt_ package from PyPI to hash our passwords + securely. There are other one-way hash algorithms for passwords if + bcrypt is an issue on your system. Just make sure that it's an + algorithm approved for storing passwords versus a generic one-way hash. diff --git a/docs/tutorials/wiki/src/authorization/tutorial/security.py b/docs/tutorials/wiki/src/authorization/tutorial/security.py index 4115c780c..cbb3acd5d 100644 --- a/docs/tutorials/wiki/src/authorization/tutorial/security.py +++ b/docs/tutorials/wiki/src/authorization/tutorial/security.py @@ -2,11 +2,13 @@ import bcrypt def hash_password(pw): - return bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) + hashed_pw = bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) + # return unicode instead of bytes because databases handle it better + return hashed_pw.decode('utf-8') def check_password(expected_hash, pw): if expected_hash is not None: - return bcrypt.checkpw(pw.encode('utf-8'), expected_hash) + return bcrypt.checkpw(pw.encode('utf-8'), expected_hash.encode('utf-8')) return False USERS = {'editor': hash_password('editor'), diff --git a/docs/tutorials/wiki/src/tests/tutorial/security.py b/docs/tutorials/wiki/src/tests/tutorial/security.py index 4115c780c..cbb3acd5d 100644 --- a/docs/tutorials/wiki/src/tests/tutorial/security.py +++ b/docs/tutorials/wiki/src/tests/tutorial/security.py @@ -2,11 +2,13 @@ import bcrypt def hash_password(pw): - return bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) + hashed_pw = bcrypt.hashpw(pw.encode('utf-8'), bcrypt.gensalt()) + # return unicode instead of bytes because databases handle it better + return hashed_pw.decode('utf-8') def check_password(expected_hash, pw): if expected_hash is not None: - return bcrypt.checkpw(pw.encode('utf-8'), expected_hash) + return bcrypt.checkpw(pw.encode('utf-8'), expected_hash.encode('utf-8')) return False USERS = {'editor': hash_password('editor'), -- cgit v1.2.3 From fad00d3201d9debd8abc146355a82d9adb6d823e Mon Sep 17 00:00:00 2001 From: Nejc Zupan Date: Wed, 7 Dec 2016 14:38:32 +0100 Subject: bring wiki2 test coverage up to 100%, refs #2451 --- .../src/tests/tutorial/scripts/initializedb.py | 1 + .../src/tests/tutorial/tests/test_functional.py | 12 ++++ .../wiki2/src/tests/tutorial/tests/test_initdb.py | 20 +++++++ .../src/tests/tutorial/tests/test_security.py | 21 +++++++ .../src/tests/tutorial/tests/test_user_model.py | 67 ++++++++++++++++++++++ 5 files changed, 121 insertions(+) create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/tests/test_initdb.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/tests/test_security.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/tests/test_user_model.py diff --git a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py index f3c0a6fef..c860ef8cf 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py @@ -28,6 +28,7 @@ def usage(argv): def main(argv=sys.argv): if len(argv) < 2: usage(argv) + return config_uri = argv[1] options = parse_vars(argv[2:]) setup_logging(config_uri) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py index 715768b2e..0250e71c9 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py @@ -11,6 +11,9 @@ class FunctionalTests(unittest.TestCase): basic_wrong_login = ( '/login?login=basic&password=incorrect' '&next=FrontPage&form.submitted=Login') + basic_login_no_next = ( + '/login?login=basic&password=basic' + '&form.submitted=Login') editor_login = ( '/login?login=editor&password=editor' '&next=FrontPage&form.submitted=Login') @@ -68,6 +71,10 @@ class FunctionalTests(unittest.TestCase): res = self.testapp.get(self.basic_login, status=302) self.assertEqual(res.location, 'http://localhost/FrontPage') + def test_successful_log_in_no_next(self): + res = self.testapp.get(self.basic_login_no_next, status=302) + self.assertEqual(res.location, 'http://localhost/') + def test_failed_log_in(self): res = self.testapp.get(self.basic_wrong_login, status=200) self.assertTrue(b'login' in res.body) @@ -120,3 +127,8 @@ class FunctionalTests(unittest.TestCase): self.testapp.get(self.editor_login, status=302) res = self.testapp.get('/FrontPage', status=200) self.assertTrue(b'FrontPage' in res.body) + + def test_redirect_to_edit_for_existing_page(self): + self.testapp.get(self.editor_login, status=302) + res = self.testapp.get('/add_page/FrontPage', status=302) + self.assertTrue(b'FrontPage' in res.body) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_initdb.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_initdb.py new file mode 100644 index 000000000..97511d5e8 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_initdb.py @@ -0,0 +1,20 @@ +import mock +import unittest + + +class TestInitializeDB(unittest.TestCase): + + @mock.patch('tutorial.scripts.initializedb.sys') + def test_usage(self, mocked_sys): + from ..scripts.initializedb import main + main(argv=['foo']) + mocked_sys.exit.assert_called_with(1) + + @mock.patch('tutorial.scripts.initializedb.get_tm_session') + @mock.patch('tutorial.scripts.initializedb.sys') + def test_run(self, mocked_sys, mocked_session): + from ..scripts.initializedb import main + main(argv=['foo', 'development.ini']) + mocked_session.assert_called_once() + + diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_security.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_security.py new file mode 100644 index 000000000..4c3b72946 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_security.py @@ -0,0 +1,21 @@ +import mock +import unittest + + +class TestMyAuthenticationPolicy(unittest.TestCase): + + def test_no_user(self): + request = mock.Mock() + request.user = None + + from ..security import MyAuthenticationPolicy + policy = MyAuthenticationPolicy(None) + self.assertEqual(policy.authenticated_userid(request), None) + + def test_authenticated_user(self): + request = mock.Mock() + request.user.id = 'foo' + + from ..security import MyAuthenticationPolicy + policy = MyAuthenticationPolicy(None) + self.assertEqual(policy.authenticated_userid(request), 'foo') diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_user_model.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_user_model.py new file mode 100644 index 000000000..9490ac990 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_user_model.py @@ -0,0 +1,67 @@ +import unittest +import transaction + +from pyramid import testing + + +class BaseTest(unittest.TestCase): + + def setUp(self): + from ..models import get_tm_session + self.config = testing.setUp(settings={ + 'sqlalchemy.url': 'sqlite:///:memory:' + }) + self.config.include('..models') + self.config.include('..routes') + + session_factory = self.config.registry['dbsession_factory'] + self.session = get_tm_session(session_factory, transaction.manager) + + self.init_database() + + def init_database(self): + from ..models.meta import Base + session_factory = self.config.registry['dbsession_factory'] + engine = session_factory.kw['bind'] + Base.metadata.create_all(engine) + + def tearDown(self): + testing.tearDown() + transaction.abort() + + def makeUser(self, name, role): + from ..models import User + return User(name=name, role=role) + + +class TestSetPassword(BaseTest): + + def test_password_hash_saved(self): + user = self.makeUser(name='foo', role='bar') + self.assertFalse(user.password_hash) + + user.set_password('secret') + self.assertTrue(user.password_hash) + + +class TestCheckPassword(BaseTest): + + def test_password_hash_not_set(self): + user = self.makeUser(name='foo', role='bar') + self.assertFalse(user.password_hash) + + self.assertFalse(user.check_password('secret')) + + def test_correct_password(self): + user = self.makeUser(name='foo', role='bar') + user.set_password('secret') + self.assertTrue(user.password_hash) + + self.assertTrue(user.check_password('secret')) + + def test_incorrect_password(self): + user = self.makeUser(name='foo', role='bar') + user.set_password('secret') + self.assertTrue(user.password_hash) + + self.assertFalse(user.check_password('incorrect')) -- cgit v1.2.3 From cfbc73dc042d924503527add9b32c48aa3ba29bd Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Thu, 8 Dec 2016 03:25:24 -0800 Subject: update typographical conventions per @bertjwregeer --- docs/typographical-conventions.rst | 89 -------------------------------------- 1 file changed, 89 deletions(-) diff --git a/docs/typographical-conventions.rst b/docs/typographical-conventions.rst index 5c7efea31..19894775b 100644 --- a/docs/typographical-conventions.rst +++ b/docs/typographical-conventions.rst @@ -28,32 +28,6 @@ A glossary defines terms used throughout the documentation. References to glossa Note it is hyperlinked, and when clicked it will take the user to the term in the Glossary and highlight the term. -.. _typographical-conventions-headings: - -Headings --------- - -Sections, sub-sections, and sub-sub-sections within a web page or chapter are denoted with headings at various levels. The immediately preceding heading "Headings" is a section heading. Sub-section and sub-sub-section headings are shown as follows. - -Heading Level 2 -^^^^^^^^^^^^^^^ - -sub-section - -Heading Level 3 -""""""""""""""" - -sub-sub-section - - -.. _typographical-conventions-paragraphs: - -Paragraphs ----------- - -A paragraph of text looks exactly like this paragraph. - - .. _typographical-conventions-links: Links @@ -202,53 +176,6 @@ Inline code is displayed as follows, where the inline code is 'pip install -e ". Install requirements for building documentation: ``pip install -e ".[docs]"`` -.. _typographical-conventions-lists: - -Lists ------ - -Bulleted lists display as follows. - -* This is an item in a bulleted list. -* This is another item in a bulleted list. - -Numbered lists display as follows. - -#. This is an item in a numbered list. -#. This is another item in a numbered list. - -Nested lists display as follows. - -#. This is a list item in the parent list. -#. This is another list item in the parent list. - - #. This is a list item in the child list. - #. This is another list item in the child list. - -#. This is one more list item in the parent list. - - -.. _typographical-conventions-tables: - -Tables ------- - -Tables display as follows. - -===== ===== -col 1 col 2 -===== ===== -1 Second column of row 1. -2 Second column of row 2. - Second line of paragraph. -3 * Second column of row 3. - - * Second item in bullet - list (row 3, column 2). -\ Row 4; column 1 will be empty. -===== ===== - - .. _typographical-conventions-feature-versioning: Feature versioning @@ -350,22 +277,6 @@ Todo items designated tasks that require further work. This is a todo item. -.. _typographical-conventions-italics: - -Italics -------- - -This *word* is italicized. - - -.. _typographical-conventions-strong: - -Strong ------- - -This **word** is in bold text. - - .. _typographical-conventions-cross-references: Cross-references -- cgit v1.2.3 From 11d637f45e90c011ce15f7e016e909f0f300c705 Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Thu, 8 Dec 2016 03:50:59 -0800 Subject: add fixes for Sphinx 1.5 --- docs/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 0e3f03068..12dd27722 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -196,10 +196,10 @@ latex_documents = [ # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -latex_use_parts = True +latex_toplevel_sectioning = "section" # If false, no module index is generated. -latex_use_modindex = False +latex_domain_indices = False ## Say, for a moment that you have a twoside document that needs a 3cm ## inner margin to allow for binding and at least two centimetres the -- cgit v1.2.3 From 3adaf31276f9c671ec469dee7dd115723f1eec3b Mon Sep 17 00:00:00 2001 From: Steve Piercy Date: Thu, 8 Dec 2016 23:06:26 -0800 Subject: minor grammar and style fixes for wiki/authorization --- docs/tutorials/wiki/authorization.rst | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/docs/tutorials/wiki/authorization.rst b/docs/tutorials/wiki/authorization.rst index 523acc53b..67af83b25 100644 --- a/docs/tutorials/wiki/authorization.rst +++ b/docs/tutorials/wiki/authorization.rst @@ -18,7 +18,7 @@ require permission, instead of a default "403 Forbidden" page. We will implement the access control with the following steps: -* Add password hashing dependencies +* Add password hashing dependencies. * Add users and groups (``security.py``, a new module). * Add an :term:`ACL` (``models.py``). * Add an :term:`authentication policy` and an :term:`authorization policy` @@ -39,13 +39,11 @@ Then we will add the login and logout feature: Access control -------------- + Add dependencies ~~~~~~~~~~~~~~~~ -Just like in :ref:`wiki_defining_views` we need a new dependency. -We need to add the ``bcrypt`` [1]_ package, to our tutorial package's -``setup.py`` file by assigning this dependency to the ``requires`` parameter -in the ``setup()`` function. +Just like in :ref:`wiki_defining_views`, we need a new dependency. We need to add the `bcrypt `_ package, to our tutorial package's ``setup.py`` file by assigning this dependency to the ``requires`` parameter in the ``setup()`` function. Open ``setup.py`` and edit it to look like the following: @@ -58,11 +56,15 @@ Only the highlighted line needs to be added. Do not forget to run ``pip install -e .`` just like in :ref:`wiki-running-pip-install`. +.. note:: + + We are using the ``bcrypt`` package from PyPI to hash our passwords securely. There are other one-way hash algorithms for passwords if bcrypt is an issue on your system. Just make sure that it's an algorithm approved for storing passwords versus a generic one-way hash. + + Add users and groups ~~~~~~~~~~~~~~~~~~~~ -Create a new ``tutorial/security.py`` module with the -following content: +Create a new ``tutorial/security.py`` module with the following content: .. literalinclude:: src/authorization/tutorial/security.py :linenos: @@ -83,7 +85,7 @@ request)`` returns ``None``. We will use ``groupfinder()`` as an There are two helper methods that will help us later to authenticate users. The first is ``hash_password`` which takes a raw password and transforms it using -bcrypt_ into an irreversible representation, a process known as "hashing". The +bcrypt into an irreversible representation, a process known as "hashing". The second method, ``check_password``, will allow us to compare the hashed value of the submitted password against the hashed value of the password stored in the user's record. If the two hashed values match, then the submitted @@ -403,11 +405,3 @@ following URLs, checking that the result is as expected: the login form with the ``editor`` credentials), we'll see a Logout link in the upper right hand corner. When we click it, we're logged out, and redirected back to the front page. - - -.. _bcrypt: https://pypi.python.org/pypi/bcrypt - -.. [1] We are using the bcrypt_ package from PyPI to hash our passwords - securely. There are other one-way hash algorithms for passwords if - bcrypt is an issue on your system. Just make sure that it's an - algorithm approved for storing passwords versus a generic one-way hash. -- cgit v1.2.3 From 40ab35b67d2b563b096d99941b0ddb0aedd47524 Mon Sep 17 00:00:00 2001 From: Pavlo Kapyshin Date: Fri, 9 Dec 2016 17:34:15 +0200 Subject: Fix typo --- docs/whatsnew-1.7.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/whatsnew-1.7.rst b/docs/whatsnew-1.7.rst index 398b12f01..c5f611f04 100644 --- a/docs/whatsnew-1.7.rst +++ b/docs/whatsnew-1.7.rst @@ -126,7 +126,7 @@ Feature Additions - The :attr:`pyramid.tweens.EXCVIEW` tween will now re-raise the original exception if no exception view could be found to handle it. This allows - the exception to be handled upstream by another tween or middelware. + the exception to be handled upstream by another tween or middleware. See https://github.com/Pylons/pyramid/pull/2567 Deprecations -- cgit v1.2.3 From 80865ce4b2dee27d0be3868219fc447940ab1858 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Fri, 9 Dec 2016 22:16:49 -0700 Subject: Use has_body so we don't unravel app_iter This allows streaming responses. Closes #2625 --- pyramid/httpexceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyramid/httpexceptions.py b/pyramid/httpexceptions.py index 054917dfa..a22b088c6 100644 --- a/pyramid/httpexceptions.py +++ b/pyramid/httpexceptions.py @@ -246,7 +246,7 @@ ${body}''') 'title': self.title} def prepare(self, environ): - if not self.body and not self.empty_body: + if not self.has_body and not self.empty_body: html_comment = '' comment = self.comment or '' accept_value = environ.get('HTTP_ACCEPT', '') -- cgit v1.2.3 From 2ae320a4390940014dd049d2fc5f8312a31e2961 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Fri, 9 Dec 2016 22:18:21 -0700 Subject: Pin to a newer version of WebOb v1.7.0 has property Response.has_body --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a6dbc2824..383991d73 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ except IOError: install_requires = [ 'setuptools', - 'WebOb >= 1.3.1', # request.domain and CookieProfile + 'WebOb >= 1.7.0rc2', # Response.has_body 'repoze.lru >= 0.4', # py3 compat 'zope.interface >= 3.8.0', # has zope.interface.registry 'zope.deprecation >= 3.5.0', # py3 compat -- cgit v1.2.3 From 2d45def603f038a8533eb9790640982012c0be30 Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Fri, 9 Dec 2016 23:41:25 -0600 Subject: add changelog for #2863 --- CHANGES.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 9daddff48..f3883b557 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -119,6 +119,12 @@ Features See https://github.com/Pylons/pyramid/pull/2805 +- Allow streaming responses to be made from subclasses of + ``pyramid.httpexceptions.HTTPException``. Previously the response would + be unrolled while testing for a body, making it impossible to stream + a response. + See https://github.com/Pylons/pyramid/pull/2863 + Bug Fixes --------- -- cgit v1.2.3 From bff312df8c36ac3ef5389a89c7459b282f944f77 Mon Sep 17 00:00:00 2001 From: Matthew Wilkes Date: Thu, 8 Dec 2016 15:00:29 +0100 Subject: Fix bug where plural rules would not be loaded for the default 'messages' domain. --- CHANGES.txt | 5 +++++ pyramid/i18n.py | 12 +++++++++++- pyramid/tests/test_i18n.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index 65a1f15cd..2dc80c005 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -149,6 +149,11 @@ Bug Fixes from previous orders have executed. See https://github.com/Pylons/pyramid/pull/2757 +- Fix bug in i18n where the default domain would always use the Germanic plural + style, even if a different plural function is defined in the relevant messages + file. + See https://github.com/Pylons/pyramid/pull/2102 + Deprecations ------------ diff --git a/pyramid/i18n.py b/pyramid/i18n.py index 79209d342..1d11adfe3 100644 --- a/pyramid/i18n.py +++ b/pyramid/i18n.py @@ -22,6 +22,7 @@ from pyramid.threadlocal import get_current_registry TranslationString = TranslationString # PyFlakes TranslationStringFactory = TranslationStringFactory # PyFlakes +DEFAULT_PLURAL = lambda n: int(n != 1) class Localizer(object): """ @@ -233,7 +234,13 @@ class Translations(gettext.GNUTranslations, object): # GNUTranslations._parse (called as a side effect if fileobj is # passed to GNUTranslations.__init__) with a "real" self.plural for # this domain; see https://github.com/Pylons/pyramid/issues/235 - self.plural = lambda n: int(n != 1) + # It is only overridden the first time a new message file is found + # for a given domain, so all message files must have matching plural + # rules if they are in the same domain. We keep track of if we have + # overridden so we can special case the default domain, which is always + # instantiated before a message file is read. + # See also https://github.com/Pylons/pyramid/pull/2102 + self.plural = DEFAULT_PLURAL gettext.GNUTranslations.__init__(self, fp=fileobj) self.files = list(filter(None, [getattr(fileobj, 'name', None)])) self.domain = domain @@ -285,6 +292,9 @@ class Translations(gettext.GNUTranslations, object): :rtype: `Translations` """ domain = getattr(translations, 'domain', self.DEFAULT_DOMAIN) + if domain == self.DEFAULT_DOMAIN and self.plural is DEFAULT_PLURAL: + self.plural = translations.plural + if merge and domain == self.domain: return self.merge(translations) diff --git a/pyramid/tests/test_i18n.py b/pyramid/tests/test_i18n.py index 67b2ac356..d72d0d480 100644 --- a/pyramid/tests/test_i18n.py +++ b/pyramid/tests/test_i18n.py @@ -357,6 +357,36 @@ class TestTranslations(unittest.TestCase): inst.add(inst2) self.assertEqual(inst._catalog['a'], 'b') + def test_add_default_domain_replaces_plural_first_time(self): + # Create three empty message catalogs in the default domain + inst = self._getTargetClass()(None, domain='messages') + inst2 = self._getTargetClass()(None, domain='messages') + inst3 = self._getTargetClass()(None, domain='messages') + inst._catalog = {} + inst2._catalog = {} + inst3._catalog = {} + + # The default plural scheme is the germanic one + self.assertEqual(inst.plural(0), 1) + self.assertEqual(inst.plural(1), 0) + self.assertEqual(inst.plural(2), 1) + + # inst2 represents a message file that declares french plurals + inst2.plural = lambda n: n > 1 + inst.add(inst2) + # that plural rule should now apply to inst + self.assertEqual(inst.plural(0), 0) + self.assertEqual(inst.plural(1), 0) + self.assertEqual(inst.plural(2), 1) + + # We load a second message file with different plural rules + inst3.plural = lambda n: n > 0 + inst.add(inst3) + # It doesn't override the previously loaded rule + self.assertEqual(inst.plural(0), 0) + self.assertEqual(inst.plural(1), 0) + self.assertEqual(inst.plural(2), 1) + def test_dgettext(self): t = self._makeOne() self.assertEqual(t.dgettext('messages', 'foo'), 'Voh') -- cgit v1.2.3 From 6421f4c7559205e125a1c7218f711d6f6ecaf85c Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Mon, 12 Dec 2016 11:13:08 -0600 Subject: reference the correct PR for #2859 --- CHANGES.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 0a810a0d6..8a6713783 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -160,9 +160,8 @@ Bug Fixes See https://github.com/Pylons/pyramid/pull/2757 - Fix bug in i18n where the default domain would always use the Germanic plural - style, even if a different plural function is defined in the relevant messages - file. - See https://github.com/Pylons/pyramid/pull/2102 + style, even if a different plural function is defined in the relevant + messages file. See https://github.com/Pylons/pyramid/pull/2859 Deprecations ------------ -- cgit v1.2.3