For loop using counter and array within in the loop

350 views
Skip to first unread message

Daniel Flick

unread,
Nov 8, 2017, 10:51:07 AM11/8/17
to Mako Templates for Python
I am building a router template and I want to be able to build the interfaces with the most efficient code.  Multilink interfaces are configured identically so I was hoping to build a loop.

The input file would have an integer with the number of interfaces to create which I called T1_NUM.  I am guessing that if T1_NUM=4 that the counter will start at zero so I thought I would have to take that value from the input and subtract 1 so that the loop runs from 0 to 3.

As an example,  if T1_NUM=4 I would run the following code:

% for T in %{T1_NUM-1}:
    %{INTF} = '0/0/0', '0/0/1', '0/1/0','0/1/1'
    controller T1 ${INTF(T)}
     cablelength long 0db
     channel-group 0 timeslots 1-24
    interface Serial ${INTF(T)}:0
     no ip address
     encapsulation ppp
     load-interval 30
     no fair-queue
     ppp multilink
     ppp multilink group 1
     no shut
% endfor

I would expect the output to look like this:

controller T1 0/0/0
 cablelength long 0db
 channel-group 0 timeslots 1-24
interface Serial 0/0/0:0
 no ip address
 encapsulation ppp
 load-interval 30
 no fair-queue
 ppp multilink
 ppp multilink group 1
 no shut
controller T1 0/0/1
 cablelength long 0db
 channel-group 0 timeslots 1-24
interface Serial 0/0/1:0
 no ip address
 encapsulation ppp
 load-interval 30
 no fair-queue
 ppp multilink
 ppp multilink group 1
 no shut
controller T1 0/1/0
 cablelength long 0db
 channel-group 0 timeslots 1-24
interface Serial 0/1/0:0
 no ip address
 encapsulation ppp
 load-interval 30
 no fair-queue
 ppp multilink
 ppp multilink group 1
 no shut
controller T1 0/1/1
 cablelength long 0db
 channel-group 0 timeslots 1-24
interface Serial 0/1/1:0
 no ip address
 encapsulation ppp
 load-interval 30
 no fair-queue
 ppp multilink
 ppp multilink group 1
 no shut

The template is tripping over line 1 with this error:
Template Syntax error: (SyntaxError) invalid syntax (<unknown>, line 1) ('for T in %{T1_NUM}:pass') at line: 1 char: 1

I have tried switching out the format a few different ways but I keep tripping over something.

Mike Bayer

unread,
Nov 8, 2017, 3:22:55 PM11/8/17
to mako-d...@googlegroups.com
On Wed, Nov 8, 2017 at 10:51 AM, Daniel Flick <daniel....@gmail.com> wrote:
> I am building a router template and I want to be able to build the
> interfaces with the most efficient code. Multilink interfaces are
> configured identically so I was hoping to build a loop.
>
> The input file would have an integer with the number of interfaces to create
> which I called T1_NUM. I am guessing that if T1_NUM=4 that the counter will
> start at zero so I thought I would have to take that value from the input
> and subtract 1 so that the loop runs from 0 to 3.
>
> As an example, if T1_NUM=4 I would run the following code:
>
> % for T in %{T1_NUM-1}:

did you mean to say:

% for T in T1_NUM - 1:

the text that follows the "%" sign is expected to be real Python code.
T1_NUM would be a variable declared in your template.
> --
> You received this message because you are subscribed to the Google Groups
> "Mako Templates for Python" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to mako-discuss...@googlegroups.com.
> To post to this group, send email to mako-d...@googlegroups.com.
> Visit this group at https://groups.google.com/group/mako-discuss.
> For more options, visit https://groups.google.com/d/optout.
Message has been deleted

Jonathan Vanasco

unread,
Nov 8, 2017, 4:44:19 PM11/8/17
to Mako Templates for Python
Extending on Mike's answer - which is the valid Mako - and looking at what you're trying to do

`T1_NUM - 1` would be a number, and `for` expects an iterable.

You might want to do:

    % for T in range(0, T1_NUM):

But this line is invalid:


     %{INTF} = '0/0/0', '0/0/1', '0/1/0','0/1/1'

You possibly intended:

    <% INTF = ['0/0/0', '0/0/1', '0/1/0','0/1/1'] %>


And looking at all this, I think what you want to do is...

    <% INTF = ['0/0/0', '0/0/1', '0/1/0','0/1/1'] %>
   % for T in INTF:
   % endfor

and if you needed the index of the looped element:

    <% INTF = ['0/0/0', '0/0/1', '0/1/0','0/1/1'] %>
   % for idx, T in enumerate(INTF):
   % endfor

Daniel Flick

unread,
Nov 8, 2017, 5:43:07 PM11/8/17
to Mako Templates for Python
Thanks for the help!  OK, If I am following you correctly, I think this is what you suggested;

<% INTF = ['0/0/0', '0/0/1', '0/1/0','0/1/1'] %>
<% T1_NUM = 4 %>  #just for testing. I plan to send this variable in the CSV file)  
% for T in range(0,T1_NUM):
    controller T1 ${INTF(T)}
     cablelength long 0db
     channel-group 0 timeslots 1-24
interface Serial ${INTF(T)}:0
     no ip address
     encapsulation ppp
     load-interval 30
     no fair-queue
     ppp multilink
     ppp multilink group 1
     no shut
% endfor

I get the following error:
Template Attribute error: 'list' object is not callable

To clarify a bit more, the list INTF will be a template on how we lay out T1s.  In production, we will likely cap out at 8 and I wanted all T1s to be predictable in the layout so that the 1st T1 in a bundle will always be 0/0/0 and the second T1 will be 0/1/0 and and the 3rd T1 would be 0/0/1 and so on.  Here is what the production array will likely look like:
<% INTF = ['0/0/0', '0/1/0', '0/0/1','0/1/1','0/2/0', '0/3/0', '0/02/1','0/3/1'] %>

T1_NUM is used in a BUNCH of places in the template.  So in my CSV, if T1_NUM= 4, then create 4 serial interfaces, set QoS bandwidth in kilobits to 4*1.544, set baseline for the bandwidth calculations to 4*1.544*1000 and set the headroom for QoS to 4*1.544*1000000*.004. I was thinking that I would use that same integer as a counter that would also allow me to reference a position in the array to pull the correct interface number (when the counter T = zero, INTF(0) would equal 0/0/0 and when T=2 then INTF(2) would equal 0/1/0 and so on.

I figured that using T1_NUM as a counter would be an easy way to create the interfaces but in looking at the info you sent, it seems that I could also have a CSV field set to something like ['0/0/0', '0/1/0', '0/0/1','0/1/1','0/2/0', '0/3/0', '0/02/1','0/3/1'] and use that to iterate.  I think that may be the smarter move as it would allow for a single port card with a dual port or a 4 port card, etc.  If I were to go that route instead, I tried this:
% for T in INTF:
    controller T1 ${INTF}
     cablelength long 0db
     channel-group 0 timeslots 1-24
interface Serial ${INTF}:0
     no ip address
     encapsulation ppp
     load-interval 30
     no fair-queue
     ppp multilink
     ppp multilink group 1
     no shut
% endfor

But my output has 30 instances of this:
    controller T1 ['0/0/0', '0/0/1', '0/1/0','0/1/1']
     cablelength long 0db
     channel-group 0 timeslots 1-24
	interface Serial ['0/0/0', '0/0/1', '0/1/0','0/1/1']:0
     no ip address
     encapsulation ppp
     load-interval 30
     no fair-queue
     ppp multilink
     ppp multilink group 1
     no shut
30 times in a row.

Thoughts?

Jonathan Vanasco

unread,
Nov 8, 2017, 6:47:58 PM11/8/17
to Mako Templates for Python
It looks like you're getting confused with some of the core Python fundamentals, not Mako.

This error: Template Attribute error: 'list' object is not callable

Is because of the brackets you are using.

   INTF(T)

is trying to "call" INTF as a method/function with T as an argument.

but if you change to square brackets:

    INTF[T]

then you are addressing the T index of the INTF iterable.  (Which might be a numeric index on a list/tuple or a key index on a dict)

In the last example, you possibly intended one of the following::

    ${T}
    ${INTF[T]}

It depends on what INTF is

this line runs the loop once for every item in the INTF list

    % for T in INTF:

and this line prints the INTF list once on each loop.  the INTF list never changes, so it always prints the same list

        controller T1 ${INTF}

This line simply iterates over INTF;

    for T in INTF:

let's use INTF as an array:

    <% INTF = (9, 8, 7, 6) %>

And use this loop:

    % for T in INTF:
       ${T}
    % endfor

The output will be:
   9
   8
   7
   6

But we can change the loop to enumerate...
    % for (idx, T) in enumerate(INTF):
       ${idx}, ${T}
    % endfor

The output will be:
   0, 9
   1, 8
   2, 7
   3, 6

Daniel Flick

unread,
Nov 8, 2017, 7:11:40 PM11/8/17
to Mako Templates for Python
I am VERY new to Python so thanks so much for the clarification.  So I made these changes and it works but is there any way to pass INTF in a CSV instead of in the template itself?  If I don't include the INTF tuple in the template and try to send it in a CSV instead, I get an error:


<% INTF = ['0/0/0', '0/0/1', '0/1/0','0/1/1'] %> #Works with this line but "Template Attribute error: 'Undefined' object is not iterable" if removed
% for T in INTF:
    controller T1 ${T}
     cablelength long 0db
     channel-group 0 timeslots 1-24
interface Serial ${T}:0
     no ip address
     encapsulation ppp
     load-interval 30
     no fair-queue
     ppp multilink
     ppp multilink group 1
     no shut
% endfor

Jonathan Vanasco

unread,
Nov 8, 2017, 7:54:35 PM11/8/17
to Mako Templates for Python


On Wednesday, November 8, 2017 at 7:11:40 PM UTC-5, Daniel Flick wrote:
I am VERY new to Python so thanks so much for the clarification.  So I made these changes and it works but is there any way to pass INTF in a CSV instead of in the template itself?

So you'd do a few things...

1. Open/read/validate the csv file.   

   The CSV module is great: https://docs.python.org/3/library/csv.html
   You could also just read it manually
      csv_data = [i.strip() for i in open('/path/to/file).read().split(',')]  # works for one line

2. get that data into mako
    you could do that in a python block in mako, or you could pass in the data as a render variable.


Daniel Flick

unread,
Nov 8, 2017, 8:29:33 PM11/8/17
to Mako Templates for Python
Yes, I am already reading it in and that works but the template is failing validation.  Since the code you gave me works, I think it may be something on the tool side.  I submitted an issue with the developer:

I will see what happens.  Thanks for your help!

Daniel Flick

unread,
Nov 8, 2017, 8:42:54 PM11/8/17
to Mako Templates for Python
So I had an idea.  I changed the working template:

<% INTF = ['0/0/0', '0/0/1', '0/1/0','0/1/1'] %>
% for T in INTF:
controller T1 ${T}
cablelength long 0db
channel-group 0 timeslots 1-24
interface Serial ${T}:0
no ip address
encapsulation ppp
load-interval 30
no fair-queue
ppp multilink
ppp multilink group 1
no shut
% endfor

which give me output like:
controller T1 0/0/0
     cablelength long 0db
     channel-group 0 timeslots 1-24
	interface Serial 0/0/0:0

to this:

${ INTF }

% for T in INTF:
controller T1 ${T}
cablelength long 0db
channel-group 0 timeslots 1-24
interface Serial ${T}:0
no ip address
encapsulation ppp
load-interval 30
no fair-queue
ppp multilink
ppp multilink group 1
no shut
% endfor

I set the CSV like this:
hostname;INTF;T
testhost;0/0/0,0/0/1,0/1/0,0/1/1;

I also tried to set INTF with brackets
hostname;INTF;T
testhost;[0/0/0,0/0/1,0/1/0,0/1/1];

and with quotes
hostname;INTF;T
testhost;['0/0/0','0/0/1','0/1/0','0/1/1'];

Each of these CSVs give me output of each character instead of the tuple:

controller T1 [
     cablelength long 0db
     channel-group 0 timeslots 1-24
	interface Serial [:0
....
controller T1 '
     cablelength long 0db
     channel-group 0 timeslots 1-24
	interface Serial ':0
...
controller T1 0
     cablelength long 0db
     channel-group 0 timeslots 1-24
	interface Serial 0:0

Jonathan Vanasco

unread,
Nov 8, 2017, 9:53:03 PM11/8/17
to Mako Templates for Python
as far as python is concerned, a CSV is just a string of text.

in all of these, the elements in that line are just a string:

  testhost;0/0/0,0/0/1,0/1/0,0/1/1;
  testhost;[0/0/0,0/0/1,0/1/0,0/1/1];
  testhost;['0/0/0','0/0/1','0/1/0','0/1/1'];

python isn't doing anything to turn the second element into a list.  

as far as python is concerned, you have the string

    value = "['0/0/0','0/0/1','0/1/0','0/1/1']"

not the list

     list = ['0/0/0','0/0/1','0/1/0','0/1/1']

turning that string into a list has a lot of pitfalls and caveats.  i suggest going to StackOverflow and looking for answers.  I'm confident there will be handful of similar questions, and many responses will show you (and warn you) on how to parse that.

Daniel Flick

unread,
Nov 8, 2017, 10:31:49 PM11/8/17
to Mako Templates for Python
I have a lot to learn!

So searching through stackoverflow, I see ways to do this but it does look like I am barking up the wrong tree here.  I think I am going to have to stick to a static definition.
CSV with columns for each T1
T1-1;T1-2;T1-3;etc
0/0/0;0/1/0;0/1/1;etc

 % if T1-1!='':
  controller T1 ${T1-1}
     cablelength long 0db
     channel-group 0 timeslots 1-24
interface Serial ${T1-1}:0
     no ip address
     encapsulation ppp
     load-interval 30
     no fair-queue
     ppp multilink
     ppp multilink group 1
     no shut
% endif

 % if T1-2!='':
  controller T1 ${T1-2}
     cablelength long 0db
     channel-group 0 timeslots 1-24
interface Serial ${T1-2}:0
     no ip address
     encapsulation ppp
     load-interval 30
     no fair-queue
     ppp multilink
     ppp multilink group 1
     no shut
% endif

etc, etc....

I guess I am trying to work well beyond my skill level!  
Reply all
Reply to author
Forward
0 new messages