from __future__ import print_function

from collections import namedtuple
from itertools import count
import os

try:
    _range = xrange
except NameError:
    _range = range

LineProperties = namedtuple(
                   'LineProperties', 'column_widths, spacing, num_lines')

def columnize(items, spacing=2, max_line_width=80,
                     allow_exceeding=True, linesep=os.linesep):
    column_width_calculator = ColumnWidthCalculator(spacing, max_line_width)
    formatter = Formatter(column_width_calculator, allow_exceeding, linesep)
    return formatter.format(items)


class ColumnWidthCalculator(object):
    def __init__(self, spacing=2, max_line_width=80):
        self.spacing = spacing
        self.max_line_width = max_line_width

    def get_properties(self, items):
        self._check_values()
        item_widths = [len(item) for item in items]
        if not item_widths:
            column_widths, num_lines = [], 0
        elif any(width > self.max_line_width for width in item_widths):
            column_widths, num_lines = [self.max_line_width], len(item_widths)
        else:
            column_widths, num_lines = self.calculate_columns(item_widths)
        return LineProperties(column_widths, self.spacing, num_lines)

    def _check_values(self):
        for value in (self.spacing, self.max_line_width):
            if not isinstance(value, int) or value < 0:
                msg = 'spacing and max_line_width must be non-negative integers'
                raise ValueError(msg)

    def calculate_columns(self, item_widths):
        num_items = len(item_widths)
        for chunk_size in count(1):
            column_widths = []
            line_width = -self.spacing
            for i in _range(0, num_items, chunk_size):
                column_width = max(item_widths[i : i + chunk_size])
                line_width += column_width + self.spacing
                if line_width > self.max_line_width:
                    break
                column_widths.append(column_width)
            else:
                return column_widths, chunk_size


class Formatter(object):
    def __init__(self, column_width_calculator=ColumnWidthCalculator(),
                       allow_exceeding=True, linesep=os.linesep):
        self.column_width_calculator = column_width_calculator
        self.allow_exceeding = allow_exceeding
        self.linesep = linesep

    def format(self, items):
        line_strings = self.iter_line_strings(items)
        return self.linesep.join(line_strings)

    def iter_line_strings(self, items):
        properties = self.column_width_calculator.get_properties(items)
        column_widths = properties.column_widths
        chunk_size = properties.num_lines
        template = self.get_line_template(column_widths)
        for i in _range(chunk_size):
            line_items = tuple(items[i::chunk_size])
            try:
                yield template % line_items
            except TypeError:
                # number of specs != len(line_items)
                # -> re-generate template
                num_columns = len(line_items)
                template = self.get_line_template(column_widths[:num_columns])
                yield template % line_items

    def get_line_template(self, column_widths):
        if not column_widths:
            return ''
        if len(column_widths) == 1 and self.allow_exceeding:
            return '%s'
        specs = ['%%-%d.%ds' % (width, width) for width in column_widths[:-1]]
        specs.append('%%.%ds' % column_widths[-1])
        return (self.column_width_calculator.spacing * ' ').join(specs)


def test(items=None, spacing=2, max_line_width=80, sort_items=True):
    if items is None:
        items = globals().keys()
    if sort_items:
        items = sorted(items, key=str.lower)
    print(columnize(items, spacing, max_line_width))

@@ -2568,21 +2568,16 @@
     prop
-ertie
 s = self
@@ -2627,57 +2627,8 @@
 ms)%0A
-        column_widths = properties.column_widths%0A
     
@@ -2648,21 +2648,16 @@
 e = prop
-ertie
 s.num_li
@@ -2694,32 +2694,38 @@
 t_line_template(
+props.
 column_widths)%0A 
@@ -2717,24 +2717,39 @@
 olumn_widths
+, props.spacing
 )%0A        fo
@@ -3052,32 +3052,98 @@
 len(line_items)%0A
+                column_widths = props.column_widths%5B:num_columns%5D%0A
                 
@@ -3189,30 +3189,31 @@
 n_widths
-%5B:num_columns%5D
+, props.spacing
 )%0A      
@@ -3292,24 +3292,33 @@
 olumn_widths
+, spacing
 ):%0A        i
@@ -3595,37 +3595,8 @@
 rn (
-self.column_width_calculator.
 spac

Add `spacing` argument to `.get_line_template()`.