diff options
| -rw-r--r-- | CHANGES.txt | 3 | ||||
| -rw-r--r-- | HACKING.txt | 42 | ||||
| -rw-r--r-- | TODO.txt | 3 | ||||
| m--------- | docs/_themes | 0 | ||||
| -rw-r--r-- | hacking-tox.ini | 25 | ||||
| -rw-r--r-- | pyramid/i18n.py | 17 | ||||
| -rw-r--r-- | pyramid/request.py | 8 | ||||
| -rw-r--r-- | pyramid/scaffolds/copydir.py | 8 | ||||
| -rw-r--r-- | pyramid/tests/test_config/test_init.py | 11 | ||||
| -rw-r--r-- | pyramid/tests/test_i18n.py | 54 | ||||
| -rw-r--r-- | pyramid/tests/test_integration.py | 10 | ||||
| -rw-r--r-- | pyramid/tests/test_scaffolds/test_copydir.py | 47 | ||||
| -rw-r--r-- | pyramid/tests/test_scripts/test_pdistreport.py | 1 | ||||
| -rw-r--r-- | pyramid/tests/test_url.py | 2 | ||||
| -rw-r--r-- | setup.py | 1 |
15 files changed, 169 insertions, 63 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 48efad6d4..b805a12a0 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -4,6 +4,9 @@ Next Release Features -------- +- Add ``localizer`` property (reified) to the request. + See https://github.com/Pylons/pyramid/issues/508. + - Add ``pdistreport`` script, which prints the Python version in use, the Pyramid version in use, and the version number and location of all Python distributions currently installed. diff --git a/HACKING.txt b/HACKING.txt index 5b5dcc458..abfed6dab 100644 --- a/HACKING.txt +++ b/HACKING.txt @@ -16,13 +16,32 @@ checkout. - 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). +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://codespeak.net/~hpk/tox/) with a custom configuration file that's part of +the checkout:: + + 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:: + + cd env27/hacking + ../bin/pserve development.ini + +Alternatively, if you don't want to install ``tox`` at this point, +you an achieve the same manually by following these steps: + - Create a virtualenv in which to install Pyramid:: - $ virtualenv2.6 --no-site-packages env + $ virtualenv env - Install ``setuptools-git`` into the virtualenv (for good measure, as we're using git to do version control):: @@ -93,9 +112,13 @@ Coding Style Running Tests -------------- -- To run tests for Pyramid on a single Python version, run ``python setup.py - test`` against the Python interpreter from virtualenv into which - you've ``setup.py develop``-ed Pyramid. +- 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 individual tests (i.e. during development) you can use a regular + expression with the ``-t`` parameter courtesy of the `nose-selecttests + <https://pypi.python.org/pypi/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 @@ -107,9 +130,16 @@ Running Tests example:: $ /usr/bin/easy_install tox - $ cd ~/hack-on-pyramid/pyramid + $ cd ~/hack-on-pyramid/ $ /usr/bin/tox +- The tests can also be run usign ``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/easy_install pytest + $ py.test --strict pyramid/ + Test Coverage ------------- @@ -56,9 +56,6 @@ Nice-to-Have app1" and "domain app1.localhost = app1"), ProxyPreserveHost and the nginx equivalent, preserving HTTPS URLs. -- Make "localizer" a property of request (instead of requiring - "get_localizer(request)"? - - Alias the stupid long default session factory name. - Debug option to print view matching decision (e.g. debug_viewlookup or so). diff --git a/docs/_themes b/docs/_themes -Subproject 8673c4a14f192c15f1949dc9862821e60f31604 +Subproject a181b85d4ca08fa109ee2786d4f1b5b17e9b458 diff --git a/hacking-tox.ini b/hacking-tox.ini new file mode 100644 index 000000000..9520b7aef --- /dev/null +++ b/hacking-tox.ini @@ -0,0 +1,25 @@ +[tox] +envlist = + env27,ttw27-create,ttw27-install + +[testenv:env27] +envdir = env27 +basepython = python2.7 +usedevelop = True +deps = setuptools-git +commands = + python setup.py dev + +[testenv:ttw27-create] +envdir = env27 +usedevelop = True +changedir = env27 +commands = + pcreate -s starter hacking + +[testenv:ttw27-install] +envdir = env27 +usedevelop = True +changedir = env27/hacking +commands = + python setup.py develop diff --git a/pyramid/i18n.py b/pyramid/i18n.py index b4bc0eaa7..dc2c25f18 100644 --- a/pyramid/i18n.py +++ b/pyramid/i18n.py @@ -197,17 +197,15 @@ def make_localizer(current_locale_name, translation_directories): def get_localizer(request): """ Retrieve a :class:`pyramid.i18n.Localizer` object corresponding to the current request's locale name. """ - localizer = getattr(request, 'localizer', None) - if localizer is None: - # no locale object cached on request - try: - registry = request.registry - except AttributeError: - registry = get_current_registry() + # no locale object cached on request + try: + registry = request.registry + except AttributeError: + registry = get_current_registry() - current_locale_name = get_locale_name(request) - localizer = registry.queryUtility(ILocalizer, name=current_locale_name) + current_locale_name = get_locale_name(request) + localizer = registry.queryUtility(ILocalizer, name=current_locale_name) if localizer is None: # no localizer utility registered yet @@ -216,7 +214,6 @@ def get_localizer(request): registry.registerUtility(localizer, ILocalizer, name=current_locale_name) - request.localizer = localizer return localizer diff --git a/pyramid/request.py b/pyramid/request.py index 5bca5b1f0..5dedee4f1 100644 --- a/pyramid/request.py +++ b/pyramid/request.py @@ -24,6 +24,7 @@ from pyramid.compat import ( ) from pyramid.decorator import reify +from pyramid.i18n import get_localizer from pyramid.response import Response from pyramid.url import URLMethodsMixin from pyramid.util import InstancePropertyMixin @@ -383,6 +384,13 @@ class Request(BaseRequest, DeprecatedRequestMethodsMixin, URLMethodsMixin, def json_body(self): return json.loads(text_(self.body, self.charset)) + @reify + def localizer(self): + """ Convenience property to return a localizer by calling + :func:`pyramid.i18n.get_localizer`. """ + return get_localizer(self) + + def route_request_iface(name, bases=()): # zope.interface treats the __name__ as the __doc__ and changes __name__ # to None for interfaces that contain spaces if you do not pass a diff --git a/pyramid/scaffolds/copydir.py b/pyramid/scaffolds/copydir.py index ba0988523..7864dd1a1 100644 --- a/pyramid/scaffolds/copydir.py +++ b/pyramid/scaffolds/copydir.py @@ -90,16 +90,16 @@ def copy_dir(source, dest, vars, verbosity, simulate, indent=0, if verbosity: out('%sRecursing into %s' % (pad, os.path.basename(full))) copy_dir((source[0], full), dest_full, vars, verbosity, simulate, - indent=indent+1, - sub_vars=sub_vars, interactive=interactive, + indent=indent+1, sub_vars=sub_vars, + interactive=interactive, overwrite=overwrite, template_renderer=template_renderer, out_=out_) continue elif not use_pkg_resources and os.path.isdir(full): if verbosity: out('%sRecursing into %s' % (pad, os.path.basename(full))) copy_dir(full, dest_full, vars, verbosity, simulate, - indent=indent+1, - sub_vars=sub_vars, interactive=interactive, + indent=indent+1, sub_vars=sub_vars, + interactive=interactive, overwrite=overwrite, template_renderer=template_renderer, out_=out_) continue elif use_pkg_resources: diff --git a/pyramid/tests/test_config/test_init.py b/pyramid/tests/test_config/test_init.py index 7c2880a18..b8cbbd676 100644 --- a/pyramid/tests/test_config/test_init.py +++ b/pyramid/tests/test_config/test_init.py @@ -1955,12 +1955,6 @@ class DummyRequest: self.params = {} self.cookies = {} -class DummyResponse: - status = '200 OK' - headerlist = () - app_iter = () - body = '' - class DummyThreadLocalManager(object): pushed = None popped = False @@ -1992,11 +1986,6 @@ class DummyRegistry(object): def queryUtility(self, *arg, **kw): return self.util -from pyramid.interfaces import IResponse -@implementer(IResponse) -class DummyResponse(object): - pass - from zope.interface import Interface class IOther(Interface): pass diff --git a/pyramid/tests/test_i18n.py b/pyramid/tests/test_i18n.py index bd4998b10..336e51b6a 100644 --- a/pyramid/tests/test_i18n.py +++ b/pyramid/tests/test_i18n.py @@ -230,39 +230,36 @@ class Test_get_localizer(unittest.TestCase): from pyramid.i18n import get_localizer return get_localizer(request) - def test_no_registry_on_request(self): + def test_default_localizer(self): + # `get_localizer` returns a default localizer for `en` + from pyramid.i18n import Localizer request = DummyRequest() - request.localizer = '123' result = self._callFUT(request) - self.assertEqual(result, '123') + self.assertEqual(result.__class__, Localizer) + self.assertEqual(result.locale_name, 'en') - def test_with_registry_on_request(self): + def test_custom_localizer_for_default_locale(self): from pyramid.threadlocal import get_current_registry + from pyramid.interfaces import ILocalizer registry = get_current_registry() + dummy = object() + registry.registerUtility(dummy, ILocalizer, name='en') request = DummyRequest() - request.localizer = '123' - request.registry = registry - result = self._callFUT(request) - self.assertEqual(result, '123') - - def test_locale_on_request(self): - request = DummyRequest() - request.localizer = 'abc' result = self._callFUT(request) - self.assertEqual(result, 'abc') + self.assertEqual(result, dummy) - def test_locale_from_registry(self): + def test_custom_localizer_for_custom_locale(self): from pyramid.threadlocal import get_current_registry from pyramid.interfaces import ILocalizer registry = get_current_registry() - locale = 'abc' - registry.registerUtility(locale, ILocalizer, name='en') + dummy = object() + registry.registerUtility(dummy, ILocalizer, name='ie') request = DummyRequest() - request.locale_name = 'en' + request.locale_name = 'ie' result = self._callFUT(request) - self.assertEqual(result, 'abc') + self.assertEqual(result, dummy) - def test_locale_from_mo(self): + def test_localizer_from_mo(self): from pyramid.threadlocal import get_current_registry from pyramid.interfaces import ITranslationDirectories from pyramid.i18n import Localizer @@ -278,7 +275,7 @@ class Test_get_localizer(unittest.TestCase): self.assertEqual(result.translate('Approve'), 'Approve') self.assertTrue(hasattr(result, 'pluralize')) - def test_locale_from_mo_bad_mo(self): + def test_localizer_from_mo_bad_mo(self): from pyramid.threadlocal import get_current_registry from pyramid.interfaces import ITranslationDirectories from pyramid.i18n import Localizer @@ -292,6 +289,23 @@ class Test_get_localizer(unittest.TestCase): self.assertEqual(result.translate('Approve', 'deformsite'), 'Approve') + def test_request_has_localizer(self): + from pyramid.threadlocal import get_current_registry + from pyramid.interfaces import ILocalizer + from pyramid.request import Request + # register mock localizer + dummy = object() + registry = get_current_registry() + registry.registerUtility(dummy, ILocalizer, name='en') + request = Request(environ={}) + self.assertEqual(request.localizer, dummy) + # `get_localizer` is only called once... + other = object() + registry.registerUtility(other, ILocalizer, name='en') + self.assertNotEqual(request.localizer, other) + self.assertEqual(request.localizer, dummy) + + class Test_default_locale_negotiator(unittest.TestCase): def setUp(self): cleanUp() diff --git a/pyramid/tests/test_integration.py b/pyramid/tests/test_integration.py index 66205e618..eda4ae9f3 100644 --- a/pyramid/tests/test_integration.py +++ b/pyramid/tests/test_integration.py @@ -72,7 +72,7 @@ class IntegrationBase(object): here = os.path.dirname(__file__) -class TestStaticAppBase(IntegrationBase): +class StaticAppBase(IntegrationBase): def test_basic(self): res = self.testapp.get('/minimal.pt', status=200) _assertBody(res.body, os.path.join(here, 'fixtures/minimal.pt')) @@ -198,10 +198,10 @@ class TestEventOnlySubscribers(IntegrationBase, unittest.TestCase): self.assertEqual(sorted(res.body.split()), [b'foobar', b'foobar2', b'foobaryup', b'foobaryup2']) -class TestStaticAppUsingAbsPath(TestStaticAppBase, unittest.TestCase): +class TestStaticAppUsingAbsPath(StaticAppBase, unittest.TestCase): package = 'pyramid.tests.pkgs.static_abspath' -class TestStaticAppUsingAssetSpec(TestStaticAppBase, unittest.TestCase): +class TestStaticAppUsingAssetSpec(StaticAppBase, unittest.TestCase): package = 'pyramid.tests.pkgs.static_assetspec' class TestStaticAppNoSubpath(unittest.TestCase): @@ -649,6 +649,10 @@ class AcceptContentTypeTest(unittest.TestCase): from webtest import TestApp self.testapp = TestApp(app) + def tearDown(self): + import pyramid.config + pyramid.config.global_registries.empty() + def test_ordering(self): res = self.testapp.get('/hello', headers={'Accept': 'application/json; q=1.0, text/plain; q=0.9'}, status=200) self.assertEqual(res.content_type, 'application/json') diff --git a/pyramid/tests/test_scaffolds/test_copydir.py b/pyramid/tests/test_scaffolds/test_copydir.py index d757b837c..1e92b3c36 100644 --- a/pyramid/tests/test_scaffolds/test_copydir.py +++ b/pyramid/tests/test_scaffolds/test_copydir.py @@ -103,16 +103,57 @@ class Test_copy_dir(unittest.TestCase): 1, False, overwrite=False, template_renderer=dummy_template_renderer) - target = os.path.join(self.dirname, 'mypackage', '__init__.py') - with open(target, 'w') as f: - f.write('These are not the words you are looking for.') + # toplevel file + toplevel = os.path.join(self.dirname, 'mypackage', '__init__.py') + with open(toplevel, 'w') as f: + f.write('These are the words you are looking for.') + # sub directory file + sub = os.path.join(self.dirname, 'mypackage', 'templates', 'mytemplate.pt') + with open(sub, 'w') as f: + f.write('These are the words you are looking for.') self._callFUT(source, self.dirname, vars, 1, False, overwrite=False, template_renderer=dummy_template_renderer) + with open(toplevel, 'r') as f: + tcontent = f.read() + self.assertEqual('These are the words you are looking for.', tcontent) + with open(sub, 'r') as f: + tcontent = f.read() + self.assertEqual('These are the words you are looking for.', tcontent) + def test_overwrite_true(self): + vars = {'package':'mypackage'} + source = pkg_resources.resource_filename(*self.fixturetuple) + self._callFUT(source, + self.dirname, + vars, + 1, False, + overwrite=True, + template_renderer=dummy_template_renderer) + # toplevel file + toplevel = os.path.join(self.dirname, 'mypackage', '__init__.py') + with open(toplevel, 'w') as f: + f.write('These are not the words you are looking for.') + # sub directory file + sub = os.path.join(self.dirname, 'mypackage', 'templates', 'mytemplate.pt') + with open(sub, 'w') as f: + f.write('These are not the words you are looking for.') + self._callFUT(source, + self.dirname, + vars, + 1, False, + overwrite=True, + template_renderer=dummy_template_renderer) + with open(toplevel, 'r') as f: + tcontent = f.read() + self.assertNotEqual('These are not the words you are looking for.', tcontent) + with open(sub, 'r') as f: + tcontent = f.read() + self.assertNotEqual('These are not the words you are looking for.', tcontent) + def test_detect_SkipTemplate(self): vars = {'package':'mypackage'} source = pkg_resources.resource_filename(*self.fixturetuple) diff --git a/pyramid/tests/test_scripts/test_pdistreport.py b/pyramid/tests/test_scripts/test_pdistreport.py index a8316c0e5..e229667c5 100644 --- a/pyramid/tests/test_scripts/test_pdistreport.py +++ b/pyramid/tests/test_scripts/test_pdistreport.py @@ -1,5 +1,4 @@ import unittest -from pyramid.tests.test_scripts import dummy class TestPDistReportCommand(unittest.TestCase): def _callFUT(self, **kw): diff --git a/pyramid/tests/test_url.py b/pyramid/tests/test_url.py index 9841c143e..bf1c514c5 100644 --- a/pyramid/tests/test_url.py +++ b/pyramid/tests/test_url.py @@ -611,7 +611,6 @@ class TestURLMethodsMixin(unittest.TestCase): ('pyramid.tests:static/foo.css', request, {}) ) def test_static_url_abspath_integration_with_staticurlinfo(self): - import os from pyramid.interfaces import IStaticURLInfo from pyramid.config.views import StaticURLInfo info = StaticURLInfo() @@ -626,7 +625,6 @@ class TestURLMethodsMixin(unittest.TestCase): 'http://example.com:5432/absstatic/test_url.py') def test_static_url_noscheme_uses_scheme_from_request(self): - import os from pyramid.interfaces import IStaticURLInfo from pyramid.config.views import StaticURLInfo info = StaticURLInfo() @@ -65,6 +65,7 @@ docs_extras = [ testing_extras = tests_require + [ 'nose', + 'nose-selecttests', 'coverage', 'virtualenv', # for scaffolding tests ] |
