commit 5e383dd4f99e91aaddc599a5dc6d5679a8efa705
Author: Stavros Sachtouris <saxtouri@admin.grnet.gr>
Date:   Fri Feb 21 15:42:44 2014 +0200

    Add a limit in number of history lines
    
    Refs: #4479

diff --git a/kamaki/cli/commands/history.py b/kamaki/cli/commands/history.py
index 3b735b5..1142113 100644
--- a/kamaki/cli/commands/history.py
+++ b/kamaki/cli/commands/history.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 
-# Copyright 2012-2013 GRNET S.A. All rights reserved.
+# Copyright 2012-2014 GRNET S.A. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or
 # without modification, are permitted provided that the following
@@ -68,8 +68,9 @@ class history_show(_init_history):
 
     @errors.generic.all
     def _run(self, cmd_slice):
-        lines = ['%s.\t%s' % (i, l) for i, l in enumerate(self.history[:])][
-            cmd_slice]
+        c = self.history.counter
+        lines = ['%s.\t%s' % (i + c, l) for i, l in enumerate(
+            self.history[:])][cmd_slice]
         if not isinstance(cmd_slice, slice):
             lines = [lines, ]
         if self['match']:
diff --git a/kamaki/cli/history.py b/kamaki/cli/history.py
index 9eb6564..fdc1f50 100644
--- a/kamaki/cli/history.py
+++ b/kamaki/cli/history.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 
-# Copyright 2012-2013 GRNET S.A. All rights reserved.
+# Copyright 2012-2014 GRNET S.A. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or
 # without modification, are permitted provided that the following
@@ -40,15 +40,37 @@ class History(object):
     def __init__(self, filepath, token=None):
         self.filepath = filepath
         self.token = token
+        self._limit = 0
+        self.counter = 0
 
     def __getitem__(self, cmd_ids):
         with codecs.open(self.filepath, mode='r', encoding='utf-8') as f:
             try:
-                cmd_list = f.readlines()
+                cmd_list = f.readlines()[1:]
                 return cmd_list[cmd_ids]
             except IndexError:
                 return None
 
+    @property
+    def limit(self):
+        return self._limit
+
+    @limit.setter
+    def limit(self, new_limit):
+        new_limit = int(new_limit)
+        if new_limit < 0:
+            raise ValueError('Invalid history limit (%s)' % new_limit)
+        old_limit, self._limit = self._limit, new_limit
+        if self._limit and self._limit < old_limit:
+            with codecs.open(self.filepath, mode='r', encoding='utf-8') as f:
+                lines = f.readlines()
+                old_len = len(lines[1:])
+                if old_len > new_limit:
+                    self.counter += old_len - new_limit
+                    f.write('%s\n' % self.counter)
+                    f.write(''.join(lines[(self.counter + 1):]))
+                    f.flush()
+
     @classmethod
     def _match(self, line, match_terms):
         if match_terms:
@@ -67,10 +89,13 @@ class History(object):
         with codecs.open(self.filepath, mode='a+', encoding='utf-8') as f:
             f.write(line + '\n')
             f.flush()
+        self.limit = self.limit
 
     def empty(self):
         with open(self.filepath, 'w') as f:
+            f.write('0\n')
             f.flush()
+        self.counter = 0
 
     def clean(self):
         """DEPRECATED since version 0.14"""
diff --git a/kamaki/cli/test.py b/kamaki/cli/test.py
index 7f6abcb..af52b84 100644
--- a/kamaki/cli/test.py
+++ b/kamaki/cli/test.py
@@ -37,15 +37,6 @@ from tempfile import NamedTemporaryFile
 from mock import patch, call
 from itertools import product
 
-from kamaki.cli.command_tree.test import Command, CommandTree
-from kamaki.cli.config.test import Config
-from kamaki.cli.argument.test import (
-    Argument, ConfigArgument, RuntimeConfigArgument, FlagArgument,
-    ValueArgument, IntArgument, DateArgument, VersionArgument,
-    RepeatableArgument, KeyValueArgument, ProgressBarArgument,
-    ArgumentParseManager)
-from kamaki.cli.utils.test import UtilsMethods
-
 
 class History(TestCase):
 
@@ -88,7 +79,7 @@ class History(TestCase):
         history = self.HCLASS(self.file.name)
         history.empty()
         self.file.seek(0)
-        self.assertEqual(self.file.read(), '')
+        self.assertEqual(self.file.read(), '0\n')
 
     def test_retrieve(self):
         sample_history = (
@@ -107,9 +98,40 @@ class History(TestCase):
         for i in (0, len(sample_history) + 1, - len(sample_history) - 1):
             self.assertEqual(history.retrieve(i), None)
         for i in range(1, len(sample_history)):
-            self.assertEqual(history.retrieve(i), sample_history[i - 1])
+            self.assertEqual(history.retrieve(i), sample_history[i])
             self.assertEqual(history.retrieve(- i), sample_history[- i])
 
+    def test_limit(self):
+        sample_history = (
+            'kamaki history show\n',
+            'kamaki file list\n',
+            'kamaki file create /pithos/f1\n',
+            'kamaki file info /pithos/f1\n',
+            'last command is always excluded')
+        sample_len = len(sample_history)
+        self.file.write(''.join(sample_history))
+        self.file.flush()
+        history = self.HCLASS(self.file.name)
+
+        for value, exp_e in (
+                    (-2, ValueError),
+                    ('non int', ValueError),
+                    (None, TypeError)):
+            try:
+                history.limit = value
+            except Exception as e:
+                self.assertTrue(isinstance(e, exp_e))
+
+        history.limit = 10
+        self.assertEqual(history.limit, 10)
+        self.file.seek(0)
+        self.assertEqual(len(self.file.readlines()), sample_len)
+
+        history.limit = sample_len - 1
+        self.assertEqual(history.limit, sample_len - 1)
+        self.file.seek(0)
+        self.assertEqual(len(self.file.readlines()), sample_len)
+
 
 class LoggerMethods(TestCase):
 
