Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

# Copyright 2015-2016 Luc Saffre 

# License: BSD (see file COPYING for details) 

 

'''This defines the :class:`Counter` class, a utility used in Jinja 

templates to generate self-incrementing counters for sections, 

subsections and any other sequences. 

 

.. to run only this test: 

 

  $ python setup.py test -s tests.DocsTests.test_jinja 

 

 

Installation 

============ 

 

You can add the `Counter` class either to your local context or to the 

`global namespace 

<http://jinja.pocoo.org/docs/dev/api/#global-namespace>`__. 

 

>>> from jinja2 import Environment 

>>> from lino.utils.jinja import Counter 

>>> env = Environment() 

>>> env.globals.update(Counter=Counter) 

 

 

Basic usage in a template 

========================= 

 

Using the `Counter` in your template is easy: You instantiate a 

template variable of type :class:`Counter`, and then call that counter 

each time you want a new number.  For example: 

 

>>> s = """ 

... {%- set art = Counter() -%} 

... Easy as {{art()}}, {{art()}} and {{art()}}! 

... """ 

 

Here is how this template will render : 

 

>>> print(env.from_string(s).render()) 

Easy as 1, 2 and 3! 

 

 

Counter parameters 

================== 

 

When defining your counter, you can set optional parameters. 

 

>>> s = """ 

... {%- set art = Counter(start=17, step=2) -%} 

... A funny counter: {{art()}}, {{art()}} and {{art()}}! 

... """ 

>>> print(env.from_string(s).render()) 

A funny counter: 19, 21 and 23! 

 

 

Resetting a counter 

=================== 

 

When calling your counter, you can pass optional parameters. One of 

them is `value`, which you can use to restart numbering, or to start 

numbering at some arbitrary place: 

 

>>> s = """ 

... {%- set art = Counter() -%} 

... First use: {{art()}}, {{art()}} and {{art()}} 

... Reset: {{art(value=1)}}, {{art()}} and {{art()}} 

... Arbitrary start: {{art(value=10)}}, {{art()}} and {{art()}} 

... """ 

>>> print(env.from_string(s).render()) 

First use: 1, 2 and 3 

Reset: 1, 2 and 3 

Arbitrary start: 10, 11 and 12 

 

 

Nested counters 

=============== 

 

Counters can have another counter as parent. When a parent increases, 

all children are automatically reset to their start value. 

 

 

>>> s = """ 

... {%- set art = Counter() -%} 

... {%- set par = Counter(art) -%} 

... = Article {{art()}} 

... == # {{par()}} 

... == # {{par()}} 

... = Article {{art()}} 

... == # {{par()}} 

... == # {{par()}} 

... == # {{par()}} 

... Article {{art()}}. 

... == # {{par()}} 

... """ 

>>> print(env.from_string(s).render()) 

= Article 1 

== # 1 

== # 2 

= Article 2 

== # 1 

== # 2 

== # 3 

Article 3. 

== # 1 

 

''' 

 

from __future__ import print_function 

from builtins import object 

 

 

class Counter(object): 

    """Represents a counter. Usage see """ 

    def __init__(self, parent=None, start=0, step=1): 

        self.children = [] 

        self.start = start 

        self.step = step 

        self.current = start 

        self.named_items = dict() 

        if parent is not None: 

            parent.add_child(self) 

 

    def add_child(self, ch): 

        self.children.append(ch) 

 

    def reset(self): 

        self.current = self.start 

        for ch in self.children: 

            ch.reset() 

 

    def __call__(self, name=None, value=None): 

        if value is None: 

            self.current += self.step 

        else: 

            self.current = value 

        for ch in self.children: 

            ch.reset() 

        if name: 

            if name in self.named_items: 

                raise Exception("Cannot redefine name '{0}'.".format(name)) 

            self.named_items[name] = self.current 

        return self.current 

 

    def get(self, name): 

        return self.named_items[name]() 

 

 

def _test(): 

    import doctest 

    doctest.testmod() 

 

if __name__ == "__main__": 

    _test()