diff --git a/Cheetah/legacy_compiler.py b/Cheetah/legacy_compiler.py index 49576b65..c9be01af 100644 --- a/Cheetah/legacy_compiler.py +++ b/Cheetah/legacy_compiler.py @@ -36,6 +36,8 @@ # All #import statements are hoisted to the top of the module 'useLegacyImportMode': True, 'gettextTokens': ['_', 'gettext', 'ngettext', 'pgettext', 'npgettext'], + # Can $foo mean both self.foo and NS['foo']? + 'enable_auto_self': True, } CLASS_NAME = 'YelpCheetahTemplate' @@ -47,10 +49,13 @@ def genPlainVar(nameChunks): return '.'.join(name + rest for name, rest in nameChunks) -def genNameMapperVar(nameChunks): +def genNameMapperVar(nameChunks, auto_self): name, remainder = nameChunks[0] namept1, dot, rest = name.partition('.') - start = 'VFFSL("{}", locals(), globals(), self, NS){}{}{}'.format(namept1, dot, rest, remainder) + if auto_self: + start = 'VFFSL("{}", locals(), globals(), self, NS){}{}{}'.format(namept1, dot, rest, remainder) + else: + start = 'VFFNS("{}", locals(), globals(), NS){}{}{}'.format(namept1, dot, rest, remainder) tail = genPlainVar(nameChunks[1:]) return start + ('.' if tail else '') + tail @@ -437,10 +442,11 @@ def __init__(self, source, settings=None): ) self._importStatements = [ 'import io', + 'from Cheetah.NameMapper import value_from_frame_or_namespace as VFFNS', 'from Cheetah.NameMapper import value_from_frame_or_search_list as VFFSL', 'from Cheetah.Template import NO_CONTENT', ] - self._global_vars = {'io', 'NO_CONTENT', 'VFFSL'} + self._global_vars = {'io', 'NO_CONTENT', 'VFFNS', 'VFFSL'} self._gettext_scannables = [] @@ -492,7 +498,9 @@ def genCheetahVar(self, nameChunks, lineCol): if plain: return genPlainVar(nameChunks) else: - return genNameMapperVar(nameChunks) + return genNameMapperVar( + nameChunks, auto_self=self.setting('enable_auto_self'), + ) def addGetTextVar(self, nameChunks, lineCol): """Output something that gettext can recognize. diff --git a/bench/bench_lookup_builtin_no_auto_self.py b/bench/bench_lookup_builtin_no_auto_self.py new file mode 100644 index 00000000..f04dc4f5 --- /dev/null +++ b/bench/bench_lookup_builtin_no_auto_self.py @@ -0,0 +1,7 @@ +from Cheetah.compile import compile_to_class +from constants import BUILTIN_SRC +from constants import NO_AUTO_SELF + + +tmpl = compile_to_class(NO_AUTO_SELF + BUILTIN_SRC)() +run = tmpl.respond diff --git a/bench/bench_lookup_dotted_sl_no_auto_self.py b/bench/bench_lookup_dotted_sl_no_auto_self.py new file mode 100644 index 00000000..553a52d6 --- /dev/null +++ b/bench/bench_lookup_dotted_sl_no_auto_self.py @@ -0,0 +1,11 @@ +from Cheetah.compile import compile_to_class +from constants import DOTTED_SL_SRC +from constants import NO_AUTO_SELF + + +class fooobj: + bar = 'baz' + + +tmpl = compile_to_class(NO_AUTO_SELF + DOTTED_SL_SRC)({'foo': fooobj}) +run = tmpl.respond diff --git a/bench/bench_lookup_global_no_auto_self.py b/bench/bench_lookup_global_no_auto_self.py new file mode 100644 index 00000000..43f25cb6 --- /dev/null +++ b/bench/bench_lookup_global_no_auto_self.py @@ -0,0 +1,7 @@ +from Cheetah.compile import compile_to_class +from constants import GLOBAL_SRC +from constants import NO_AUTO_SELF + + +tmpl = compile_to_class(NO_AUTO_SELF + GLOBAL_SRC)() +run = tmpl.respond diff --git a/bench/bench_lookup_local_no_auto_self.py b/bench/bench_lookup_local_no_auto_self.py new file mode 100644 index 00000000..94d0ba83 --- /dev/null +++ b/bench/bench_lookup_local_no_auto_self.py @@ -0,0 +1,7 @@ +from Cheetah.compile import compile_to_class +from constants import LOCAL_SRC +from constants import NO_AUTO_SELF + + +tmpl = compile_to_class(NO_AUTO_SELF + LOCAL_SRC)() +run = tmpl.respond diff --git a/bench/bench_lookup_sl_no_auto_self.py b/bench/bench_lookup_sl_no_auto_self.py new file mode 100644 index 00000000..e4ebd87f --- /dev/null +++ b/bench/bench_lookup_sl_no_auto_self.py @@ -0,0 +1,7 @@ +from Cheetah.compile import compile_to_class +from constants import NO_AUTO_SELF +from constants import SL_SRC + + +tmpl = compile_to_class(NO_AUTO_SELF + SL_SRC)({'foo': 'bar'}) +run = tmpl.respond diff --git a/bench/constants.py b/bench/constants.py index 44cef3c4..db27aec3 100644 --- a/bench/constants.py +++ b/bench/constants.py @@ -42,3 +42,10 @@ '$x\n' '#end for\n' ) + + +NO_AUTO_SELF = ( + '#compiler-settings\n' + 'enable_auto_self = False\n' + '#end compiler-settings\n' +) diff --git a/bench/log/2016-06-01_14:55:33.txt b/bench/log/2016-06-01_14:55:33.txt new file mode 100644 index 00000000..1c1b0233 --- /dev/null +++ b/bench/log/2016-06-01_14:55:33.txt @@ -0,0 +1,31 @@ +================================================================================ +SHA = 8029956fa22330c173246082860ea99d4a0c6820 +BEST_OF = 5 +ITERATIONS = 10 +TIME_PER_TEST = 200 +bogomips : 5400.00 +bogomips : 5400.00 +bogomips : 5400.00 +bogomips : 5400.00 +bogomips : 5400.00 +bogomips : 5400.00 +-------------------------------------------------------------------------------- +lookup_builtin_no_auto_self 135387 iterations +lookup_builtin_opt_on 138447 iterations +lookup_dotted_sl_no_auto_self 17749 iterations +lookup_dotted_sl_opt_on 15045 iterations +lookup_global_no_auto_self 139531 iterations +lookup_global_opt_on 135761 iterations +lookup_local_no_auto_self 121347 iterations +lookup_local_opt_on 125481 iterations +lookup_sl_no_auto_self 21191 iterations +lookup_sl_opt_on 16228 iterations +vffns_frame 40882 iterations +vffns_sl 22944 iterations +vffns_template 22382 iterations +vffsl_frame 38382 iterations +vffsl_sl 18030 iterations +vffsl_template 17824 iterations +write_markup 13481 iterations +write_text 8009 iterations +-------------------------------------------------------------------------------- diff --git a/tests/SyntaxAndOutput_test.py b/tests/SyntaxAndOutput_test.py index 633a83ab..15d17039 100644 --- a/tests/SyntaxAndOutput_test.py +++ b/tests/SyntaxAndOutput_test.py @@ -12,6 +12,7 @@ from Cheetah import filters from Cheetah.compile import compile_to_class from Cheetah.legacy_parser import ParseError +from Cheetah.NameMapper import NotFound def dummydecorator(func): @@ -1696,3 +1697,25 @@ def test_allow_getvar_of_underscored_things(): # Regression test for v0.11.0 cls = compile_to_class('$self.getVar("foo_BAR1")') assert cls({'foo_BAR1': 'baz'}).respond() == 'baz' + + +def test_allows_autoself(): + cls = compile_to_class( + '#def foo():\n' + 'ohai\n' + '#end def\n' + '$foo()\n', + ) + assert cls().respond() == 'ohai\n\n' + + +def test_does_not_allow_autoself(): + cls = compile_to_class( + '#def foo():\n' + 'ohai\n' + '#end def\n' + '$foo()\n', + settings={'enable_auto_self': False}, + ) + with pytest.raises(NotFound): + cls().respond()