Tuesday, June 8, 2010

Avoid Retail Markup

Access to Thousands of Products
To stop further mailing, visit here or write:
DlRECTBUY, PO Box 1572, Boulder, Colorado 80306
On Saturday 05 December 2009 20:09:21 Luke Plant wrote: I'm not likely to able to look at this before Tuesday. If anyone wants to look at it, I think the right approach is something like the following: (without the globals, obviously, they can be converted to instance variables/methods). Cancel that - I unexpectedly had free time this evening, and I implemented this. It's a nice replacement I think (credit to Vaughan Pratt and Fredrik Lundh for the basic approach and Python implementation respectively). The new implementation is pretty much the same length as the old one, and hopefully easier to read, with a much smaller core parser. Precedence is specified directly, rather than implicitly, so it's much easier to check that it's the same as Python's. Latest patch attached to this e-mail. Regards, Luke -- "Idiocy: Never underestimate the power of stupid people in large groups." () Luke Plant || diff -r 70e75e8cd224 django/template/defaulttags.py --- a/django/template/defaulttags.py Thu Dec 03 15:11:14 2009 +0000 +++ b/django/template/defaulttags.py Sun Dec 06 01:04:04 2009 +0000 @@ -11,6 +11,7 @@ from django.template import Node, NodeList, Template, Context, Variable from django.template import TemplateSyntaxError, VariableDoesNotExist, BLOCK_TAG_START, BLOCK_TAG_END, VARIABLE_TAG_START, VARIABLE_TAG_END, SINGLE_BRACE_START, SINGLE_BRACE_END, COMMENT_TAG_START, COMMENT_TAG_END from django.template import get_library, Library, InvalidTemplateLibrary +from django.template.smartif import IfParser, Literal from django.conf import settings from django.utils.encoding import smart_str, smart_unicode from django.utils.itercompat import groupby @@ -227,10 +228,9 @@ return self.nodelist_false.render(context) class IfNode(Node): - def __init__(self, bool_exprs, nodelist_true, nodelist_false, link_type): - self.bool_exprs = bool_exprs + def __init__(self, var, nodelist_true, nodelist_false=None): self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false - self.link_type = link_type + self.var = var def __repr__(self): return "" @@ -250,28 +250,11 @@ return nodes def render(self, context): - if self.link_type == IfNode.LinkTypes.or_: - for ifnot, bool_expr in self.bool_exprs: - try: - value = bool_expr.resolve(context, True) - except VariableDoesNotExist: - value = None - if (value and not ifnot) or (ifnot and not value): - return self.nodelist_true.render(context) + if self.var.resolve(context): + return self.nodelist_true.render(context) + if self.nodelist_false: return self.nodelist_false.render(context) - else: - for ifnot, bool_expr in self.bool_exprs: - try: - value = bool_expr.resolve(context, True) - except VariableDoesNotExist: - value = None - if not ((value and not ifnot) or (ifnot and not value)): - return self.nodelist_false.render(context) - return self.nodelist_true.render(context) - - class LinkTypes: - and_ = 0, - or_ = 1 + return '' class RegroupNode(Node): def __init__(self, target, expression, var_name): @@ -761,6 +744,27 @@ return do_ifequal(parser, token, True) ifnotequal = register.tag(ifnotequal) +class TemplateLiteral(Literal): + def __init__(self, value, text): + self.value = value + self.text = text # for better error messages + + def display(self): + return self.text + + def resolve(self, context): + return self.value.resolve(context, ignore_failures=True) + +class TemplateIfParser(IfParser): + error_class = TemplateSyntaxError + + def __init__(self, parser, *args, **kwargs): + self.template_parser = parser + return super(TemplateIfParser, self).__init__(*args, **kwargs) + + def create_var(self, value): + return TemplateLiteral(self.template_parser.compile_filter(value), value) + #...@register.tag(name="if") def do_if(parser, token): """ @@ -805,47 +809,21 @@ There are some athletes and absolutely no coaches. {% endif %} - ``if`` tags do not allow ``and`` and ``or`` clauses with the same tag, - because the order of logic would be ambigous. For example, this is - invalid:: + Comparison operators are also available, and the use of filters is also + allowed, for example: - {% if athlete_list and coach_list or cheerleader_list %} + {% if articles|length >= 5 %}...{% endif %} - If you need to combine ``and`` and ``or`` to do advanced logic, just use - nested if tags. For example:: + Arguments and operators _must_ have a space between them, so + ``{% if 1>2 %}`` is not a valid if tag. - {% if athlete_list %} - {% if coach_list or cheerleader_list %} - We have athletes, and either coaches or cheerleaders! - {% endif %} - {% endif %} + All supported operators are: ``or``, ``and``, ``in``, ``=`` (or ``==``), + ``!=``, ``>``, ``>=``, ``<`` and ``<=``. + + Operator precedence follows Python. """ - bits = token.contents.split() - del bits[0] - if not bits: - raise TemplateSyntaxError("'if' statement requires at least one argument") - # Bits now looks something like this: ['a', 'or', 'not', 'b', 'or', 'c.d'] - bitstr = ' '.join(bits) - boolpairs = bitstr.split(' and ') - boolvars = [] - if len(boolpairs) == 1: - link_type = IfNode.LinkTypes.or_ - boolpairs = bitstr.split(' or ') - else: - link_type = IfNode.LinkTypes.and_ - if ' or ' in bitstr: - raise TemplateSyntaxError, "'if' tags can't mix 'and' and 'or'" - for boolpair in boolpairs: - if ' ' in boolpair: - try: - not_, boolvar = boolpair.split() - except ValueError: - raise TemplateSyntaxError, "'if' statement improperly formatted" - if not_ != 'not': - raise TemplateSyntaxError, "Expected 'not' in if statement" - boolvars.append((True, parser.compile_filter(boolvar))) - else: - boolvars.append((False, parser.compile_filter(boolpair))) + bits = token.split_contents()[1:] + var = TemplateIfParser(parser, bits).parse() nodelist_true = parser.parse(('else', 'endif')) token = parser.next_token() if token.contents == 'else': @@ -853,7 +831,7 @@ parser.delete_first_token() else: nodelist_false = NodeList() - return IfNode(boolvars, nodelist_true, nodelist_false, link_type) + return IfNode(var, nodelist_true, nodelist_false) do_if = register.tag("if", do_if) #...@register.tag diff -r 70e75e8cd224 django/template/smartif.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/django/template/smartif.py Sun Dec 06 01:04:04 2009 +0000 @@ -0,0 +1,181 @@ +""" +Parser and utilities for the smart 'if' tag +""" + +# Using a simple top down parser, as described here: +# . +# 'led' = left denotation +# 'nud' = null denotation +# 'bp' = binding power (left = lbp, right = rbp) + +class TokenBase(object): + """ + Base class for operators and literals, mainly for debugging and for throwing + syntax errors. + """ + id = None # node/token type name + value = None # used by literals + first = second = None # used by tree nodes + + def nud(self, parser): + # Null denotation - called in prefix context + raise parser.error_class( + "Not expecting '%s' in this position in if tag." % selfid + ) + + def led(self, left, parser): + # Left denotation - called in infix context + raise parser.error_class( + "Not expecting '%s' as infix operator in if tag." % selfid + ) + + def display(self): + """ + What to display in error messages for this node + """ + return selfid + + def __repr__(self): + if selfid == "literal": + return "(%s %r)" % (selfid, self.value) + out = [selfid, self.first, self.second] + out = map(str, filter(None, out)) + return "(" + " ".join(out) + ")" + + +def infix(bp, func): + """ + Create an infix operator, given a binding power and a function that + evaluates the node + """ + class Operator(TokenBase): + lbp = bp + + def led(self, left, parser): + self.first = left + self.second = parser.expression(bp) + return self + + def resolve(self, context): + return func(self.first.resolve(context), self.second.resolve(context)) + + return Operator + + +def prefix(bp, func): + """ + Create a prefix operator, given a binding power and a function that + evaluates the node. + """ + class Operator(TokenBase): + lbp = bp + + def nud(self, parser): + self.first = parser.expression(bp) + self.second = None + return self + + def resolve(self, context): + return func(self.first.resolve(context)) + + return Operator + + +# Operator precedence follows Python. +# NB - we can get slightly more accurate syntax error messages by not using the +# same object for '==' and '='. + +OPERATORS = { + 'or': infix(6, lambda x, y: x or y), + 'and': infix(7, lambda x, y: x and y), + 'not': prefix(8, lambda x: not x), + 'in': infix(9, lambda x, y: y is not None and x in y), + '=': infix(10, lambda x, y: x == y), + '==': infix(10, lambda x, y: x == y), + '!=': infix(10, lambda x, y: x != y), + '>': infix(10, lambda x, y: x > y), + '>=': infix(10, lambda x, y: x >= y), + '<': infix(10, lambda x, y: x < y), + '<=': infix(10, lambda x, y: x <= y), +} + +# Assign 'id' to each: +for key, op in OPERATORS.items(): + opid = key + + +class Literal(TokenBase): + """ + A basic self-resolvable object similar to a Django template variable. + """ + # IfParser uses Literal in create_var, but TemplateIfParser overrides + # create_var so that a proper implementation that actually resolves + # variables, filters etc are used. + id = "literal" + lbp = 0 + def __init__(self, value): + self.value = value + + def display(self): + return repr(self.value) + + def nud(self, parser): + return self + + def resolve(self, context): + return self.value + + +class EndToken(TokenBase): + lbp = 0 + + def nud(self, parser): + raise parser.error_class("Unexpected end of expression in if tag.") + +EndToken = EndToken() + + +class IfParser(object): + error_class = ValueError + + def __init__(self, tokens): + self.tokens = map(self.translate_tokens, tokens) + self.pos = 0 + self.current_token = self.next() + + def translate_tokens(self, token): + try: + op = OPERATORS[token] + except (KeyError, TypeError): + return self.create_var(token) + else: + return op() + + def next(self): + if self.pos >= len(self.tokens): + return EndToken + else: + retval = self.tokens[self.pos] + self.pos += 1 + return retval + + def parse(self): + retval = self.expression() + # Check that we have exhausted all the tokens + if self.current_token is not EndToken: + raise self.error_class("Unused '%s' at end of if expression." % + self.current_token.display()) + return retval + + def expression(self, rbp=0): + t = self.current_token + self.current_token = self.next() + left = t.nud(self) + while rbp < self.current_token.lbp: + t = self.current_token + self.current_token = self.next() + left = t.led(left, self) + return left + + def create_var(self, value): + return Literal(value) diff -r 70e75e8cd224 docs/ref/templates/builtins.txt --- a/docs/ref/templates/builtins.txt Thu Dec 03 15:11:14 2009 +0000 +++ b/docs/ref/templates/builtins.txt Sun Dec 06 01:04:04 2009 +0000 @@ -313,6 +313,9 @@ As you can see, the ``if`` tag can take an optional ``{% else %}`` clause that will be displayed if the test fails. +Boolean operators +^^^^^^^^^^^^^^^^^ + ``if`` tags may use ``and``, ``or`` or ``not`` to test a number of variables or to negate a given variable:: @@ -338,24 +341,153 @@ There are some athletes and absolutely no coaches. {% endif %} -``if`` tags don't allow ``and`` and ``or`` clauses within the same tag, because -the order of logic would be ambiguous. For example, this is invalid:: +.. versionchanged:: 1.2 + +If you use ``and`` and ``or`` clauses within the same tag, it is interpreted +left to right, e.g.:: {% if athlete_list and coach_list or cheerleader_list %} -If you need to combine ``and`` and ``or`` to do advanced logic, just use nested -``if`` tags. For example:: +will be interpreted like: - {% if athlete_list %} - {% if coach_list or cheerleader_list %} - We have athletes, and either coaches or cheerleaders! - {% endif %} +.. code-block:: python + + if (athlete_list and coach_list) or cheerleader_list + +Use of actual brackets in the ``if`` tag is invalid syntax. If you need them to +indicate precedence, you should use nested ``if`` tags. + +.. versionadded:: 1.2 + + +``if`` tags may also use the operators ``==``, ``!=``, ``<``, ``>``, +``<=``, ``>=`` and ``in`` which work as follows: + + +``==`` operator +^^^^^^^^^^^^^^^ + +Equality. Example:: + + {% if somevar == "x" %} + This appears if variable somevar equals the string "x" {% endif %} -Multiple uses of the same logical operator are fine, as long as you use the -same operator. For example, this is valid:: +``!=`` operator +^^^^^^^^^^^^^^^ - {% if athlete_list or coach_list or parent_list or teacher_list %} +Inequality. Example:: + + {% if somevar != "x" %} + This appears if variable somevar does not equal the string "x", + or if somevar is not found in the context + {% endif %} + +``<`` operator +^^^^^^^^^^^^^^ + +Less than. Example:: + + {% if somevar < 100 %} + This appears if variable somevar is less than 100. + {% endif %} + +``>`` operator +^^^^^^^^^^^^^^ + +Greater than. Example:: + + {% if somevar > 0 %} + This appears if variable somevar is greater than 0. + {% endif %} + +``<=`` operator +^^^^^^^^^^^^^^^ + +Less than or equal to. Example:: + + {% if somevar <= 100 %} + This appears if variable somevar is less than 100 or equal to 100. + {% endif %} + +``>=`` operator +^^^^^^^^^^^^^^^ + +Greater than or equal to. Example:: + + {% if somevar >= 1 %} + This appears if variable somevar is greater than 1 or equal to 1. + {% endif %} + +``in`` operator +^^^^^^^^^^^^^^^ + +Contained within. This operator is supported by many Python containers to test +whether the given value is in the container. The following are some examples of +how ``x in y`` will be interpreted:: + + {% if "bc" in "abcdef" %} + This appears since "bc" is a substring of "abcdef" + {% endif %} + + {% if "hello" in greetings %} + If greetings is a list or set, one element of which is the string + "hello", this will appear. + {% endif %} + + {% if user in users %} + If users is a QuerySet, this will appear if user is an + instance that belongs to the QuerySet. + {% endif %} + + +The comparison operators cannot be 'chained' like in Python or in mathematical +notation. For example, instead of using:: + + {% if a > b > c %} (WRONG) + +you should use:: + + {% if a > b and b > c %} + + +Filters +^^^^^^^ + +You can also use filters in the ``if`` expression. For example:: + + {% if messages|length >= 100 %} + You have lots of messages today! + {% endif %} + +Complex expressions +^^^^^^^^^^^^^^^^^^^ + +All of the above can be combined to form complex expressions. For such +expressions, it can be important to know how the operators are grouped when the +expression is evaluated - that is, the precedence rules. The precedence of the +operators, from lowest to highest, is as follows: + + * ``or`` + * ``and`` + * ``not`` + * ``in`` + * ``==``, ``!=``, ``<``, ``>``,``<=``, ``>=`` + +(This follows Python exactly). So, for example, the following complex if tag: + + {% if a == b or c == d and e %} + +...will be interpreted as: + +.. code-block:: python + + (a == b) or ((c == d) and e) + +If you need different precedence, you will need to use nested if tags. Sometimes +that is better for clarity anyway, for the sake of those who do not know the +precedence rules. + .. templatetag:: ifchanged @@ -427,6 +559,9 @@ ``False``. If you need to test if something is true or false, use the ``if`` tag instead. +.. versionadded:: 1.2 + An alternative to the ``ifequal`` tag is to use the :ttag:`if` tag and the ``==`` operator. + .. templatetag:: ifnotequal ifnotequal @@ -434,6 +569,9 @@ Just like ``ifequal``, except it tests that the two arguments are not equal. +.. versionadded:: 1.2 + An alternative to the ``ifnotequal`` tag is to use the :ttag:`if` tag and the ``!=`` operator. + .. templatetag:: include include diff -r 70e75e8cd224 docs/releases/1.2.txt --- a/docs/releases/1.2.txt Thu Dec 03 15:11:14 2009 +0000 +++ b/docs/releases/1.2.txt Sun Dec 06 01:04:04 2009 +0000 @@ -42,6 +42,15 @@ * All of the CSRF has moved from contrib to core (with backwards compatible imports in the old locations, which are deprecated). +:ttag:`if` tag changes +---------------------- + +Due to new features in the :ttag:`if` template tag, it no longer accepts 'and', +'or' and 'not' as valid **variable** names. Previously that worked in some +cases even though these strings were normally treated as keywords. Now, the +keyword status is always enforced, and template code like ``{% if not %}`` or +``{% if and %}`` will throw a TemplateSyntaxError. + ``LazyObject`` -------------- @@ -155,3 +164,36 @@ :ref:`memory` - you can even configure all e-mail to be :ref:`thrown away`. +'Smart' if tag +-------------- + +The :ttag:`if` tag has been upgraded to be much more powerful. First, support +for comparison operators has been added. No longer will you have to type: + +.. code-block:: html+django + + {% ifnotequal a b %} + ... + {% endifnotequal %} + +...as you can now do: + +.. code-block:: html+django + + {% if a != b %} + ... + {% endif %} + +The operators supported are ``==``, ``!=``, ``<``, ``>``, ``<=``, ``>=`` and +``in``, all of which work like the Python operators, in addition to ``and``, +``or`` and ``not`` which were already supported. + +Also, filters may now be used in the ``if`` expression. For example: + +.. code-block:: html+django + + {{ message }} # diff -r 70e75e8cd224 docs/topics/templates.txt --- a/docs/topics/templates.txt Thu Dec 03 15:11:14 2009 +0000 +++ b/docs/topics/templates.txt Sun Dec 06 01:04:04 2009 +0000 @@ -187,8 +187,8 @@ {{ athletename }} {% endfor %} - - :ttag:`if` and :ttag:`else` + + :ttag:`if` and ``else`` Evaluates a variable, and if that variable is "true" the contents of the block are displayed:: @@ -200,20 +200,15 @@ In the above, if ``athlete_list`` is not empty, the number of athletes will be displayed by the ``{{ athlete_list|length }}`` variable. - - :ttag:`ifequal` and :ttag:`ifnotequal` - Display some contents if two arguments are or are not equal. For example:: - {% ifequal athletename coachname %} - ... - {% endifequal %} + You can also use filters and various operators in the ``if`` tag:: - Or:: + {% if athlete_list|length > 1 %} + Team: {% for athlete in athlete_list %} ... {% endfor %} + {% else %} + Athlete: {{ athlete_list.0name }} + {% endif %} - {% ifnotequal athletename "Joe" %} - ... - {% endifnotequal %} - :ttag:`block` and :ttag:`extends` Set up `template inheritance`_ (see below), a powerful way of cutting down on "boilerplate" in templates. diff -r 70e75e8cd224 tests/regressiontests/templates/smartif.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/regressiontests/templates/smartif.py Sun Dec 06 01:04:04 2009 +0000 @@ -0,0 +1,46 @@ +import unittest +from django.template.smartif import IfParser, Literal + +class SmartIfTests(unittest.TestCase): + + def assertCalcEqual(self, expected, tokens): + self.assertEqual(expected, IfParser(tokens).parse().resolve({})) + + # We only test things here that are difficult to test elsewhere + # Many other tests are found in the main tests for builtin template tags + # Test parsing via the printed parse tree + def test_not(self): + var = IfParser(["not", False]).parse() + self.assertEqual("(not (literal False))", repr(var)) + self.assert_(var.resolve({})) + + self.assertFalse(IfParser(["not", True]).parse().resolve({})) + + def test_or(self): + var = IfParser([True, "or", False]).parse() + self.assertEqual("(or (literal True) (literal False))", repr(var)) + self.assert_(var.resolve({})) + + def test_in(self): + list_ = [1,2,3] + self.assertCalcEqual(True, [1, 'in', list_]) + self.assertCalcEqual(False, [1, 'in', None]) + self.assertCalcEqual(False, [None, 'in', list_]) + + def test_precedence(self): + # (False and False) or True == True <- we want this one, like Python + # False and (False or True) == False + self.assertCalcEqual(True, [False, 'and', False, 'or', True]) + + # True or (False and False) == True <- we want this one, like Python + # (True or False) and False == False + self.assertCalcEqual(True, [True, 'or', False, 'and', False]) + + # (1 or 1) == 2 -> False + # 1 or (1 == 2) -> True <- we want this one + self.assertCalcEqual(True, [1, 'or', 1, '==', 2]) + + self.assertCalcEqual(True, [True, '==', True, 'or', True, '==', False]) + + self.assertEqual("(or (and (== (literal 1) (literal 2)) (literal 3)) (literal 4))", + repr(IfParser([1, '==', 2, 'and', 3, 'or', 4]).parse())) diff -r 70e75e8cd224 tests/regressiontests/templates/tests.py --- a/tests/regressiontests/templates/tests.py Thu Dec 03 15:11:14 2009 +0000 +++ b/tests/regressiontests/templates/tests.py Sun Dec 06 01:04:04 2009 +0000 @@ -24,6 +24,7 @@ from custom import custom_filters from parser import filter_parsing, variable_parsing from unicode import unicode_tests +from smartif import * try: from loaders import * @@ -534,6 +535,27 @@ 'if-tag02': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": False}, "no"), 'if-tag03': ("{% if foo %}yes{% else %}no{% endif %}", {}, "no"), + # Filters + 'if-tag-filter01': ("{% if foo|length == 5 %}yes{% else %}no{% endif %}", {'foo': 'abcde'}, "yes"), + 'if-tag-filter02': ("{% if foo|upper == 'ABC' %}yes{% else %}no{% endif %}", {}, "no"), + + # Equality + 'if-tag-eq01': ("{% if foo == bar %}yes{% else %}no{% endif %}", {}, "yes"), + 'if-tag-eq02': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1}, "no"), + 'if-tag-eq03': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1, 'bar': 1}, "yes"), + 'if-tag-eq04': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1, 'bar': 2}, "no"), + 'if-tag-eq05': ("{% if foo == '' %}yes{% else %}no{% endif %}", {}, "no"), + + # Comparison + 'if-tag-gt-01': ("{% if 2 > 1 %}yes{% else %}no{% endif %}", {}, "yes"), + 'if-tag-gt-02': ("{% if 1 > 1 %}yes{% else %}no{% endif %}", {}, "no"), + 'if-tag-gte-01': ("{% if 1 >= 1 %}yes{% else %}no{% endif %}", {}, "yes"), + 'if-tag-gte-02': ("{% if 1 >= 2 %}yes{% else %}no{% endif %}", {}, "no"), + 'if-tag-lt-01': ("{% if 1 < 2 %}yes{% else %}no{% endif %}", {}, "yes"), + 'if-tag-lt-02': ("{% if 1 < 1 %}yes{% else %}no{% endif %}", {}, "no"), + 'if-tag-lte-01': ("{% if 1 <= 1 %}yes{% else %}no{% endif %}", {}, "yes"), + 'if-tag-lte-02': ("{% if 2 <= 1 %}yes{% else %}no{% endif %}", {}, "no"), + # AND 'if-tag-and01': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'), 'if-tag-and02': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'), @@ -554,14 +576,13 @@ 'if-tag-or07': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True}, 'yes'), 'if-tag-or08': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': True}, 'yes'), - # TODO: multiple ORs + # multiple ORs + 'if-tag-or09': ("{% if foo or bar or baz %}yes{% else %}no{% endif %}", {'baz': True}, 'yes'), # NOT 'if-tag-not01': ("{% if not foo %}no{% else %}yes{% endif %}", {'foo': True}, 'yes'), - 'if-tag-not02': ("{% if not %}yes{% else %}no{% endif %}", {'foo': True}, 'no'), - 'if-tag-not03': ("{% if not %}yes{% else %}no{% endif %}", {'not': True}, 'yes'), - 'if-tag-not04': ("{% if not not %}no{% else %}yes{% endif %}", {'not': True}, 'yes'), - 'if-tag-not05': ("{% if not not %}no{% else %}yes{% endif %}", {}, 'no'), + 'if-tag-not02': ("{% if not not foo %}no{% else %}yes{% endif %}", {'foo': True}, 'no'), + # not03 to not05 removed, now TemplateSyntaxErrors 'if-tag-not06': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {}, 'no'), 'if-tag-not07': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'), @@ -599,12 +620,21 @@ 'if-tag-not34': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'), 'if-tag-not35': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'), - # AND and OR raises a TemplateSyntaxError - 'if-tag-error01': ("{% if foo or bar and baz %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, template.TemplateSyntaxError), + # Various syntax errors + 'if-tag-error01': ("{% if %}yes{% endif %}", {}, template.TemplateSyntaxError), 'if-tag-error02': ("{% if foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError), 'if-tag-error03': ("{% if foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError), 'if-tag-error04': ("{% if not foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError), 'if-tag-error05': ("{% if not foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError), + 'if-tag-error06': ("{% if abc def %}yes{% endif %}", {}, template.TemplateSyntaxError), + 'if-tag-error07': ("{% if not %}yes{% endif %}", {}, template.TemplateSyntaxError), + 'if-tag-error08': ("{% if and %}yes{% endif %}", {}, template.TemplateSyntaxError), + 'if-tag-error09': ("{% if or %}yes{% endif %}", {}, template.TemplateSyntaxError), + 'if-tag-error10': ("{% if == %}yes{% endif %}", {}, template.TemplateSyntaxError), + 'if-tag-error11': ("{% if 1 == %}yes{% endif %}", {}, template.TemplateSyntaxError), + 'if-tag-error12': ("{% if a not b %}yes{% endif %}", {}, template.TemplateSyntaxError), + + # Additional, more precise parsing tests are in SmartIfTests ### IFCHANGED TAG ######################################################### 'ifchanged01': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', {'num': (1,2,3)}, '123'), On Mon, Jan 19, 2009 at 01:39:51PM +0000, Daniel P. Berrange wrote: + vm->stdout_fd = vm->stderr_fd = vm->logfile; If nothing is actaully using the stdout/err FDs anymore we can just leave them at -1. Fixed in the attached patch. -- Guido From 88002890116720af5dc4c331af6c2b7e90670639 Mon Sep 17 00:00:00 2001 From: =?utf-8?q?Guido=20G=C3=BCnther?= < ag ... @ > Date: Sun, 18 Jan 2009 19:15:21 +0100 Subject: [PATCH] read saved vm status on libvirtd startup and connect back to running vms changes since last time: * reconnect: don't use stdin_fd, stdout_fd anymore --- src/qemu_driver.c | 110 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 94 insertions(+), 16 deletions(-) diff --git a/src/qemu_driver.c b/src/qemu_driver.c index deffb3f..cde65a9 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -297,6 +297,84 @@ cleanup: } +static int qemudOpenMonitor(virConnectPtr conn, + struct qemud_driver* driver, + virDomainObjPtr vm, + const char *monitor, + int reconnect); + +/** + * qemudReconnectVMs + * + * Reconnect running vms to the daemon process + */ +static int +qemudReconnectVMs(struct qemud_driver *driver) +{ + int i; + + for (i = 0 ; i < driver->domains.count ; i++) { + virDomainObjPtr vm = driver->domains.objs[i]; + qemudDomainStatusPtr status = NULL; + char *config = NULL; + int rc; + + virDomainObjLock(vm); + if ((rc = virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) == 0) + DEBUG("Found pid %d for '%s'", vm->pid, vm->def->name); + else + goto next; + + if ((config = virDomainConfigFile(NULL, + driver->stateDir, + vm->def->name)) == NULL) { + qemudLog(QEMUD_ERR, _("Failed to read domain status for %s\n"), + vm->def->name); + goto next_error; + } + + status = qemudDomainStatusParseFile(NULL, driver->caps, config, 0); + if (status) { + vm->newDef = vm->def; + vm->def = status->def; + } else { + qemudLog(QEMUD_ERR, _("Failed to parse domain status for %s\n"), + vm->def->name); + goto next_error; + } + + if ((rc = qemudOpenMonitor(NULL, driver, vm, status->monitorpath, 1)) != 0) { + qemudLog(QEMUD_ERR, _("Failed to reconnect monitor for %s: %d\n"), + vm->def->name, rc); + goto next_error; + } else + vm->monitorpath = status->monitorpath; + + if((vm->logfile = qemudLogFD(NULL, driver->logDir, vm->def->name)) < 0) + return -1; + + if (vm->def->id >= driver->nextvmid) + driver->nextvmid = vm->def->id + 1; + + vm->state = status->state; + goto next; + +next_error: + /* we failed to reconnect the vm so remove it's traces */ + vm->def->id = -1; + qemudRemoveDomainStatus(NULL, driver, vm); + virDomainDefFree(vm->def); + vm->def = vm->newDef; + vm->newDef = NULL; +next: + virDomainObjUnlock(vm); + VIR_FREE(status); + VIR_FREE(config); + } + return 0; +} + + /** * qemudStartup: * @@ -396,6 +474,7 @@ qemudStartup(void) { qemu_driver->autostartDir, NULL, NULL) < 0) goto error; + qemudReconnectVMs(qemu_driver); qemudAutostartConfigs(qemu_driver); qemuDriverUnlock(qemu_driver); @@ -488,7 +567,6 @@ qemudActive(void) { */ static int qemudShutdown(void) { - unsigned int i; if (!qemu_driver) return -1; @@ -496,15 +574,6 @@ qemudShutdown(void) { qemuDriverLock(qemu_driver); virCapabilitiesFree(qemu_driver->caps); - /* shutdown active VMs */ - for (i = 0 ; i < qemu_driver->domains.count ; i++) { - virDomainObjPtr dom = qemu_driver->domains.objs[i]; - virDomainObjLock(dom); - if (virDomainIsActive(dom)) - qemudShutdownVMDaemon(NULL, qemu_driver, dom); - virDomainObjUnlock(dom); - } - virDomainObjListFree(&qemu_driver->domains); VIR_FREE(qemu_driver->logDir); @@ -621,7 +690,8 @@ qemudCheckMonitorPrompt(virConnectPtr conn ATTRIBUTE_UNUSED, static int qemudOpenMonitor(virConnectPtr conn, struct qemud_driver* driver, virDomainObjPtr vm, - const char *monitor) { + const char *monitor, + int reconnect) { int monfd; char buf[1024]; int ret = -1; @@ -642,11 +712,19 @@ static int qemudOpenMonitor(virConnectPtr conn, goto error; } - ret = qemudReadMonitorOutput(conn, - vm, monfd, - buf, sizeof(buf), - qemudCheckMonitorPrompt, - "monitor", 10000); + if (!reconnect) { + ret = qemudReadMonitorOutput(conn, + vm, monfd, + buf, sizeof(buf), + qemudCheckMonitorPrompt, + "monitor", 10000); + } else { + vm->monitor = monfd; + ret = 0; + } + + if (ret != 0) + goto error; if (!(vm->monitorpath = strdup(monitor))) { qemudReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, -- 1.6.0.6 -- Libvir-list mailing list Libv ... @ Hi everyone I'm looking for some inspiration because I am drawing a complete blank! A few years ago I set up my company's squid boxes - a pair of servers both identically configured and using LDAP authentication against an Active Directory domain. It took quite some time to get it all working properly but eventually it all got going and ran smoothly. I then left the company for a spell and now find myself back and detailed with rebuilding the squid servers onto newer boxes because the hardware is a little long in the tooth and now the software is too - the original servers were on RHEL 3 boxes and the subscriptions to get updates were never renewed. One of the servers failed and has been replaced with a newer box which I have built, as instructed, with CentOS 5. All seems okay but when I transplant the config file from the (now very hard-working) live machine I am getting an LDAP error with the lookup. Clearly I either missed installing something fundamental when I built the server (the idiot scenario) or something has changed syntactically with the options but I have searched diligently through the man pages and can't find any reason why what did work no longer works. Here is the line from squid.conf working on the live box (2.5.STABLE3): auth_param basic program /usr/lib/squid/squid_ldap_auth -b "dc=cs-plc,dc=salvesen,dc=com" -D "cn=Ldap User,ou=users,ou=ND House (slh / wel),ou=UK,dc=cs-plc,dc=salvesen,dc=com" -w (password) -f "(&(sAMAccountName=%s)(memberOf=CN=InternetUsers,OU=Groups,OU=ND House (slh / wel),OU=UK,DC=cs-plc,DC=salvesen,DC=com))" -h 10.1.2.1 -p 3268 The new box where this doesn't work is at 2.6.STABLE6. Attempts to authenticate result in the logfile showing: squid_ldap_auth: WARNING, LDAP search error 'Bad search filter' Has anybody got any insight? At present I've cut back the filter to -f "sAMAccountName=%s" which is at least forcing authentication but not checking the group membership. Ian Large Please consider your environmental responsibility: Before printing this e-mail or any other document, ask yourself whether you need a hard copy. -------------------------------------------------------------------------------- For information on Norbert Dentressangle visit our website at . The information contained in this e-mail is strictly confidential and for the use of the addressee only; it may also be legally privileged and / or price sensitive. Notice is hereby given that any disclosure, use or copying of the information by anyone other than the intended recipient is prohibited and may be illegal. If you have received this message in error, please notify the sender immediately by return e-mail. We have taken every reasonable precaution to ensure that any attachment to this e-mail has been swept for viruses. However, we cannot accept liability for any damage sustained as a result of software viruses and would advise that you carry out your own virus checks before opening any attachment. Groupe Norbert Dentressangle SA (RCS Romans 309 645 539 00037) is the ultimate holding company within the Norbert Dentressangle group of companies, whose registered office is at Les Pierrelles BP98 - Beausemblant 26241 Saint-Vallier-sur-Rhone Cedex, France. On Friday, January 1, 2010, Diego Moya wrote: Ok, now I've installed it and used it to play some MIDI files. Some comments: - First, my usage for a MIDI tool like this is as a media player - I won't use it to control MIDI instruments, just to play MIDI files. Totem can do this, but it doesn't give control over pitch, tempo or individual channels. KMID2 can be a good alternative for these use cases. Yes, this is a good point, and there is a recent post of A.Seigo [1] about general audience vs. advanced audience. KMid2 is intended for advanced audiences, wanting to control the MIDI rendering details. Probably using it as a tool for studying music, or learning songs, or something like that. KMid can be used as a simple player for general audience, of course. But in this case, I would prefer to add a good MIDI rendering feature to Phonon backends, using Amarok as the player frontend. This is the approach of Totem, using the timidity ugly (or evil, I don't remember) plugin for GStreamer. You can also try the VLC player and VLC Phonon backend: it can be optionally compiled linking the fluidsynth library providing good MIDI rendering to VLC. - Said that, the setup can be a bit too hard. Both MIDI devices at my Ubuntu 9.10 box recommended at startup (Midi Through:0 and OSS sequencer:0) produce no sound - I couldn't play any music until I installed Timidity through a different software channel, I would like to complain towards your distribution, starting the ALSA sequencer with those two clients activated by default. They can be disabled at ALSA compile time, or even better: installed without activating them until the user requests the functionality. Midi Through is a very advanced function, that most people will never need at all (I seldom use it). The OSS sequencer emulation is only useful for people using hardware MIDI ports with ancient OSS MIDI software, uncommon these days. I've blogged recently about this: MIDI on Linux is not easy [2]. At least, you could install and setup TiMidity. and changed it through Settings -> Configure Shortcuts -> Configure Toolbars -> Configure kmid2-> MIDI (a really hard to find option). This would be a showstopper for a "naive" user. Yes, I agree. I will try to explain it in the initial connections dialog, as you already suggested. I could filter the list of MIDI ports in the initial dialog, discarding the more advanced ones (and perhaps adding a switch in the configuration dialog for the people who needs them). What do you think? KMID2 would benefit from a tighter integration with a MIDI player (for example having Timidity linked by default with KMID2 in the installation package) and having it configured by default to play sound. The "select MIDI device at startup" scenario has no place in a usable application - things should "just work". Of course all this would be easier to program if KMid2 was already integrated in a Linux distro. I don't know if you have tried VanBasco in Windows, and after that in your Linux distro (with the help of Wine, of course). This would probably show you an interesting perspective. And yes, in windows it "just works". Timidity has a very weird architecture, and it is not very actively maintained (the last release was in 2004). It is a monolithic program, that loads the GUI interfaces as plugins. This makes it hard to integrate it into another program. It expects that other programs integrate under its own rules. But the good news is that there is another soft synth: FluidSynth. It is actively maintained: last release was two weeks ago. It is a dynamic library, that makes it easy to integrate on different programs. It has several bindings and interfaces: CLI program, QSynth based on Qt4, and several others. It is modular and multiplatform. My plan is to make a KMid backend based on this library, for several operating systems, without losing any functionality. This is already in the TODO list. But returning again to the advanced audience: users of hardware external synths, common among musicians and aficionados probably want support for their equipment instead of soft synths. This is already done for Linux, but it requires native MIDI backends for Windows and Mac OSX, which are also in my TODO list. - Music reproduction works fine, but I couldn't figure out how to use the "karaoke" functions. There's a difficulty - conceptually, music and lyrics are a single thing (a song) but they're handled through different files: a MID file for the sound, and a txt for the lyrics. No. For MP3 and other digital audio formats, that is true: two files, one with the music and another file providing the lyrics. Karaoke files (*.kar) have the lyrics included, embedded into the file. There are four .kar files included in the source KMid2 tarball. MIDI files (*.mid) may have lyrics or not. The two examples included in the KMid2 distribution ( and installed, maybe in /usr/share/kde4/apps/kmid2 ) have lyrics, but this is an optional feature of the MIDI files, ignored by many authors. There should be a way to show that distinction. At least, if there's no text present then the "text encoding" and "save lyrics" options should be disabled to avoid confusion. Added to my TODO list, thanks! I don't know whether mid files can have text integrated or if it's always a separate file. Anyway, I found at the web a .mid file that didn't have words included but the same webpage displayed the lyrics. I tried to copy that text into the application and synchronize it, but I couldn't find out how this could be done. No, this can't be done. It is required a MIDI editor like Rosegarden to modify the MIDI file and include the lyrics at the right times. You usually need to transcribe a sheet music reference. [1] [2] Regards, Pedro _______________________________________________ kde-usability mailing list kde- ... @