#!/usr/bin/env python

"""
This is a direct copy of code in cmssw/FWCore/PythonUtilities/python/LumiList.py
It can be removed if we ever have a core set of code shared between DMWM and CMSSW

Handle lists of lumi sections. Constuct in several different formats and filter
(mask) a secondary list of lumis.
This class can also handle ranges of events as the structure is identical
or could be subclassed renaming a function or two.

This code began life in COMP/CRAB/python/LumiList.py
"""


from builtins import zip, range, next, str, object

from future.utils import viewitems

from future import standard_library
standard_library.install_aliases()

import copy
import itertools
import json
import re
import urllib.request
from contextlib import closing

class LumiList(object):
    """
    Deal with lists of lumis in several different forms:
    Compact list:
        {
        '1': [[1, 33], [35, 35], [37, 47], [49, 75], [77, 130], [133, 136]],
        '2':[[1,45],[50,80]]
        }
        where the first key is the run number, subsequent pairs are
        ranges of lumis within that run that are desired
    Runs and lumis:
        {
        '1': [1,2,3,4,6,7,8,9,10],
        '2': [1,4,5,20]
        }
        where the first key is the run number and the list is a list of
        individual lumi sections. This form also takes a list of these objects
        which can be much faster than LumiList += LumiList
    Run  lumi pairs:
        [[1,1], [1,2],[1,4], [2,1], [2,5], [1,10]]
        where each pair in the list is an individual run&lumi
    CMSSW representation:
        '1:1-1:33,1:35,1:37-1:47,2:1-2:45,2:50-2:80'
        The string used by CMSSW in lumisToProcess or lumisToSkip
        is a subset of the compactList example above
    """

    def __init__(self, filename=None, lumis=None, runsAndLumis=None, runs=None, compactList=None, url=None,
                 wmagentFormat=None):
        """
        Constructor takes filename (JSON), a list of run/lumi pairs,
        or a dict with run #'s as the keys and a list of lumis as the values, or just a list of runs
        """
        self.compactList = {}
        self.duplicates = {}
        if filename:
            self.filename = filename
            with open(self.filename,'r') as jsonFile:
                self.compactList = json.load(jsonFile)
        elif url:
            self.url = url
            with closing(urllib.request.urlopen(url)) as jsonFile:
                self.compactList = json.load(jsonFile)
        elif lumis:
            runsAndLumis = {}
            for (run, lumi) in lumis:
                run = str(run)
                if run not in runsAndLumis:
                    runsAndLumis[run] = []
                runsAndLumis[run].append(lumi)

        if isinstance(runsAndLumis, list):
            queued = {}
            for runLumiList in runsAndLumis:
                for run, lumis in viewitems(runLumiList):
                    queued.setdefault(run, []).extend(lumis)
            runsAndLumis = queued

        if runsAndLumis:
            for run in runsAndLumis:
                runString = str(run)
                lastLumi = -1000
                lumiList = runsAndLumis[run]
                if lumiList:
                    self.compactList[runString] = []
                    self.duplicates[runString] = []
                    for lumi in sorted(int(l) for l in lumiList):
                        if lumi == lastLumi:
                            self.duplicates[runString].append(lumi)
                        elif lumi != lastLumi + 1: # Break in lumi sequence
                            self.compactList[runString].append([lumi, lumi])
                        else:
                            nRange =  len(self.compactList[runString])
                            self.compactList[runString][nRange-1][1] = lumi
                        lastLumi = lumi
        if runs:
            for run in runs:
                runString = str(run)
                self.compactList[runString] = [[1, 0xFFFFFFF]]

        if compactList:
            for run in compactList:
                runString = str(run)
                if compactList[run]:
                    self.compactList[runString] = compactList[run]

        if wmagentFormat:
            """
            Expecting a list or tuple of two elements. First is a list of run #s, the second is a list of strings.
            Each string is a comma separated list of lumi numbers. Even numbers are the start of a lumi range,
            odd numbers are the end of that lumi range.
            """

            if not isinstance(wmagentFormat, (list, tuple)) or len(wmagentFormat) != 2:
                raise RuntimeError('Improper format for wmagentFormat. Must be list or tuple of lists')

            runs, lumis = wmagentFormat

            if len(runs) <= 0 or len(lumis) != len(runs):
                raise RuntimeError('Improper format for wmagentFormat. # of lumi lists must match # of runs')

            for run, lumiString in zip(runs, lumis):
                runLumis = lumiString.split(',')
                if str(run) not in self.compactList:
                    self.compactList[str(run)] = []
                if len(runLumis) % 2:
                    raise RuntimeError('Improper format for wmagentFormat. Lumis must be in pairs')

                # Walk through the list two at a time, get both values
                it = iter(runLumis)
                for beginLumi in it:
                    endLumi = next(it)
                    self.compactList[str(run)].append([int(beginLumi), int(endLumi)])

        # Compact each run and make it unique

        for run in self.compactList:
            newLumis = []
            for lumi in sorted(self.compactList[run]):
                # If the next lumi starts inside or just after the last just change the endpoint of the first
                if newLumis and newLumis[-1][0] <= lumi[0] <= newLumis[-1][1] + 1:
                    newLumis[-1][1] = max(newLumis[-1][1], lumi[1])
                else:
                    newLumis.append(lumi)
            self.compactList[run] = newLumis

    def __sub__(self, other): # Things from self not in other
        result = {}
        for run in sorted(self.compactList.keys()):
            alumis = sorted(self.compactList[run])
            blumis = sorted(other.compactList.get(run, []))
            alist = []                    # verified part
            for alumi in alumis:
                tmplist = [alumi[0], alumi[1]] # may be part
                for blumi in blumis:
                    if blumi[0] <= tmplist[0] and blumi[1] >= tmplist[1]: # blumi has all of alumi
                        tmplist = []
                        break # blumi is has all of alumi
                    if blumi[0] > tmplist[0] and blumi[1] < tmplist[1]: # blumi is part of alumi
                        alist.append([tmplist[0], blumi[0]-1])
                        tmplist = [blumi[1]+1, tmplist[1]]
                    elif blumi[0] <= tmplist[0] and blumi[1] < tmplist[1] and blumi[1]>=tmplist[0]: # overlaps start
                        tmplist = [blumi[1]+1, tmplist[1]]
                    elif blumi[0] > tmplist[0] and blumi[1] >= tmplist[1] and blumi[0]<=tmplist[1]: # overlaps end
                        alist.append([tmplist[0], blumi[0]-1])
                        tmplist = []
                        break
                if tmplist:
                    alist.append(tmplist)
            result[run] = alist

        return LumiList(compactList = result)


    def __and__(self, other): # Things in both
        result = {}
        aruns = set(self.compactList.keys())
        bruns = set(other.compactList.keys())
        for run in aruns & bruns:
            lumiList = []                    # List for this run
            unique = []                    # List for this run
            for alumi in self.compactList[run]:
                for blumi in other.compactList[run]:
                    if blumi[0] <= alumi[0] and blumi[1] >= alumi[1]: # blumi has all of alumi
                        lumiList.append(alumi)
                    if blumi[0] > alumi[0] and blumi[1] < alumi[1]: # blumi is part of alumi
                        lumiList.append(blumi)
                    elif blumi[0] <= alumi[0] and blumi[1] < alumi[1] and blumi[1] >= alumi[0]: # overlaps start
                        lumiList.append([alumi[0], blumi[1]])
                    elif blumi[0] > alumi[0] and blumi[1] >= alumi[1] and blumi[0] <= alumi[1]: # overlaps end
                        lumiList.append([blumi[0], alumi[1]])


            if lumiList:
                unique = [lumiList[0]]
            for pair in lumiList[1:]:
                if pair[0] == unique[-1][1]+1:
                    unique[-1][1] = copy.deepcopy(pair[1])
                else:
                    unique.append(copy.deepcopy(pair))

            result[run] = unique
        return LumiList(compactList = result)


    def __or__(self, other):
        result = {}
        aruns = list(self.compactList)
        bruns = list(other.compactList)
        runs = set(aruns + bruns)
        for run in runs:
            overlap = sorted(self.compactList.get(run, []) + other.compactList.get(run, []))
            unique = [overlap[0]]
            for pair in overlap[1:]:
                if pair[0] >= unique[-1][0] and pair[0] <= unique[-1][1]+1 and pair[1] > unique[-1][1]:
                    unique[-1][1] = copy.deepcopy(pair[1])
                elif pair[0] > unique[-1][1]:
                    unique.append(copy.deepcopy(pair))
            result[run] = unique
        return LumiList(compactList = result)


    def __add__(self, other):
        # + is the same as |
        return self.__or__(other)

    def __len__(self):
        '''Returns number of runs in list'''
        return len(self.compactList)

    def filterLumis(self, lumiList):
        """
        Return a list of lumis that are in compactList.
        lumilist is of the simple form
        [(run1,lumi1),(run1,lumi2),(run2,lumi1)]
        """
        filteredList = []
        for (run, lumi) in lumiList:
            runsInLumi = self.compactList.get(str(run), [[0, -1]])
            for (first, last) in runsInLumi:
                if lumi >= first and lumi <= last:
                    filteredList.append((run, lumi))
                    break
        return filteredList


    def __str__ (self):
        doubleBracketRE = re.compile (r']],')
        return doubleBracketRE.sub (']],\n',
                                    json.dumps (self.compactList,
                                                sort_keys=True))

    def getCompactList(self):
        """
        Return the compact list representation
        """
        return self.compactList


    def getDuplicates(self):
        """
        Return the list of duplicates found during construction as a LumiList
        """
        return LumiList(runsAndLumis = self.duplicates)


    def getLumis(self):
        """
        Return the list of pairs representation
        """
        theList = []
        runs = list(self.compactList)
        runs.sort(key=int)
        for run in runs:
            lumis = self.compactList[run]
            for lumiPair in sorted(lumis):
                for lumi in range(lumiPair[0], lumiPair[1]+1):
                    theList.append((int(run), lumi))

        return theList


    def getRuns(self):
        '''
        return the sorted list of runs contained
        '''
        return sorted (self.compactList.keys())


    def _getLumiParts(self):
        """
        Turn compactList into a list of the format
        [ 'R1:L1', 'R2:L2-R2:L3' ] which is used by getCMSSWString and getVLuminosityBlockRange
        """

        parts = []
        runs = list(self.compactList)
        runs.sort(key=int)
        for run in runs:
            lumis = self.compactList[run]
            for lumiPair in sorted(lumis):
                if lumiPair[0] == lumiPair[1]:
                    parts.append("%s:%s" % (run, lumiPair[0]))
                else:
                    parts.append("%s:%s-%s:%s" %
                                 (run, lumiPair[0], run, lumiPair[1]))
        return parts


    def getCMSSWString(self):
        """
        Turn compactList into a list of the format
        R1:L1,R2:L2-R2:L3 which is acceptable to CMSSW LumiBlockRange variable
        """

        parts = self._getLumiParts()
        output = ','.join(parts)
        return str(output)


    def getVLuminosityBlockRange(self, tracked = False):
        """
        Turn compactList into an (optionally tracked) VLuminosityBlockRange
        """

        import FWCore.ParameterSet.Config as cms
        parts = self._getLumiParts()
        if tracked:
            return cms.VLuminosityBlockRange(parts)
        else:
            return cms.untracked.VLuminosityBlockRange(parts)


    def writeJSON(self, fileName):
        """
        Write out a JSON file representation of the object
        """
        with open(fileName,'w') as jsonFile:
            jsonFile.write("%s\n" % self)


    def removeRuns (self, runList):
        '''
        removes runs from runList from collection
        '''
        for run in runList:
            run = str(run)
            if run in self.compactList:
                del self.compactList[run]

        return


    def selectRuns (self, runList):
        '''
        Selects only runs from runList in collection
        '''
        runsToDelete = []
        for run in list(self.compactList):
            if int(run) not in runList and run not in runList:
                runsToDelete.append(run)

        for run in runsToDelete:
            del self.compactList[run]

        return

    def contains (self, run, lumiSection = None):
        '''
        returns true if the run, lumi section passed in is contained
        in this lumiList.  Input can be either:
        - a single tuple of (run, lumi),
        - separate run and lumi numbers
        - a single run number (returns true if any lumi sections exist)
        '''
        if lumiSection is None:
            # if this is an integer or a string, see if the run exists
            if isinstance (run, int) or isinstance (run, str):
                return str(run) in self.compactList
            # if we're here, then run better be a tuple or list
            try:
                lumiSection = run[1]
                run         = run[0]
            except:
                raise RuntimeError("Improper format for run '%s'" % run)
        lumiRangeList = self.compactList.get( str(run) )
        if not lumiRangeList:
            # the run isn't there, so no need to look any further
            return False
        for lumiRange in lumiRangeList:
            # we want to make this as found if either the lumiSection
            # is inside the range OR if the lumi section is greater
            # than or equal to the lower bound of the lumi range and
            # the upper bound is 0 (which means extends to the end of
            # the run)
            if lumiRange[0] <= lumiSection and \
               (0 == lumiRange[1] or lumiSection <= lumiRange[1]):
                # got it
                return True
        return False


    def __contains__ (self, runTuple):
        return self.contains (runTuple)



'''
# Unit test code
import unittest
import FWCore.ParameterSet.Config as cms

class LumiListTest(unittest.TestCase):
    """
    _LumiListTest_

    """

    def testRead(self):
        """
        Test reading from JSON
        """
        exString = "1:1-1:33,1:35,1:37-1:47,2:49-2:75,2:77-2:130,2:133-2:136"
        exDict   = {'1': [[1, 33], [35, 35], [37, 47]],
                    '2': [[49, 75], [77, 130], [133, 136]]}
        exVLBR   = cms.VLuminosityBlockRange('1:1-1:33', '1:35', '1:37-1:47', '2:49-2:75', '2:77-2:130', '2:133-2:136')

        jsonList = LumiList(filename = 'lumiTest.json')
        lumiString = jsonList.getCMSSWString()
        lumiList = jsonList.getCompactList()
        lumiVLBR = jsonList.getVLuminosityBlockRange(True)

        self.assertTrue(lumiString == exString)
        self.assertTrue(lumiList   == exDict)
        self.assertTrue(lumiVLBR   == exVLBR)

    def testList(self):
        """
        Test constucting from list of pairs
        """

        listLs1 = range(1, 34) + [35] + range(37, 48)
        listLs2 = range(49, 76) + range(77, 131) + range(133, 137)
        lumis = zip([1]*100, listLs1) + zip([2]*100, listLs2)

        jsonLister = LumiList(filename = 'lumiTest.json')
        jsonString = jsonLister.getCMSSWString()
        jsonList = jsonLister.getCompactList()

        pairLister = LumiList(lumis = lumis)
        pairString = pairLister.getCMSSWString()
        pairList = pairLister.getCompactList()

        self.assertTrue(jsonString == pairString)
        self.assertTrue(jsonList   == pairList)


    def testRuns(self):
        """
        Test constucting from run and list of lumis
        """
        runsAndLumis = {
            1: range(1, 34) + [35] + range(37, 48),
            2: range(49, 76) + range(77, 131) + range(133, 137)
        }
        runsAndLumis2 = {
            '1': range(1, 34) + [35] + range(37, 48),
            '2': range(49, 76) + range(77, 131) + range(133, 137)
        }
        blank = {
            '1': [],
            '2': []
        }

        jsonLister = LumiList(filename = 'lumiTest.json')
        jsonString = jsonLister.getCMSSWString()
        jsonList   = jsonLister.getCompactList()

        runLister = LumiList(runsAndLumis = runsAndLumis)
        runString = runLister.getCMSSWString()
        runList   = runLister.getCompactList()

        runLister2 = LumiList(runsAndLumis = runsAndLumis2)
        runList2 = runLister2.getCompactList()

        runLister3 = LumiList(runsAndLumis = blank)


        self.assertTrue(jsonString == runString)
        self.assertTrue(jsonList   == runList)
        self.assertTrue(runList2   == runList)
        self.assertTrue(len(runLister3) == 0)

    def testFilter(self):
        """
        Test filtering of a list of lumis
        """
        runsAndLumis = {
            1: range(1, 34) + [35] + range(37, 48),
            2: range(49, 76) + range(77, 131) + range(133, 137)
        }

        completeList = zip([1]*150, range(1, 150)) + \
                       zip([2]*150, range(1, 150)) + \
                       zip([3]*150, range(1, 150))

        smallList    = zip([1]*50,  range(1, 10)) + zip([2]*50, range(50, 70))
        overlapList  = zip([1]*150, range(30, 40)) + \
                       zip([2]*150, range(60, 80))
        overlapRes   = zip([1]*9,   range(30, 34)) + [(1, 35)] + \
                       zip([1]*9,   range(37, 40)) + \
                       zip([2]*30,  range(60, 76)) + \
                       zip([2]*9,   range(77, 80))

        runLister = LumiList(runsAndLumis = runsAndLumis)

        # Test a list to be filtered which is a superset of constructed list
        filterComplete = runLister.filterLumis(completeList)
        # Test a list to be filtered which is a subset of constructed list
        filterSmall    = runLister.filterLumis(smallList)
        # Test a list to be filtered which is neither
        filterOverlap  = runLister.filterLumis(overlapList)

        self.assertTrue(filterComplete == runLister.getLumis())
        self.assertTrue(filterSmall    == smallList)
        self.assertTrue(filterOverlap  == overlapRes)

    def testDuplicates(self):
        """
        Test a list with lots of duplicates
        """
        result = zip([1]*100, range(1, 34) + range(37, 48))
        lumis  = zip([1]*100, range(1, 34) + range(37, 48) + range(5, 25))

        lister = LumiList(lumis = lumis)
        self.assertTrue(lister.getLumis() == result)

    def testNull(self):
        """
        Test a null list
        """

        runLister = LumiList(lumis = None)

        self.assertTrue(runLister.getCMSSWString() == '')
        self.assertTrue(runLister.getLumis() == [])
        self.assertTrue(runLister.getCompactList() == {})

    def testSubtract(self):
        """
        a-b for lots of cases
        """

        alumis = {'1' : range(2,20) + range(31,39) + range(45,49),
                  '2' : range(6,20) + range (30,40),
                  '3' : range(10,20) + range (30,40) + range(50,60),
                 }
        blumis = {'1' : range(1,6) + range(12,13) + range(16,30) + range(40,50) + range(33,36),
                  '2' : range(10,35),
                  '3' : range(10,15) + range(35,40) + range(45,51) + range(59,70),
                 }
        clumis = {'1' : range(1,6) + range(12,13) + range(16,30) + range(40,50) + range(33,36),
                  '2' : range(10,35),
                 }
        result = {'1' : range(6,12) + range(13,16) + range(31,33) + range(36,39),
                  '2' : range(6,10) + range(35,40),
                  '3' : range(15,20) + range(30,35) + range(51,59),
                 }
        result2 = {'1' : range(6,12) + range(13,16) + range(31,33) + range(36,39),
                   '2' : range(6,10) + range(35,40),
                   '3' : range(10,20) + range (30,40) + range(50,60),
                 }
        a = LumiList(runsAndLumis = alumis)
        b = LumiList(runsAndLumis = blumis)
        c = LumiList(runsAndLumis = clumis)
        r = LumiList(runsAndLumis = result)
        r2 = LumiList(runsAndLumis = result2)

        self.assertTrue((a-b).getCMSSWString() == r.getCMSSWString())
        self.assertTrue((a-b).getCMSSWString() != (b-a).getCMSSWString())
        # Test where c is missing runs from a
        self.assertTrue((a-c).getCMSSWString() == r2.getCMSSWString())
        self.assertTrue((a-c).getCMSSWString() != (c-a).getCMSSWString())
        # Test empty lists
        self.assertTrue(str(a-a) == '{}')
        self.assertTrue(len(a-a) == 0)

    def testOr(self):
        """
        a|b for lots of cases
        """

        alumis = {'1' : range(2,20) + range(31,39) + range(45,49),
                  '2' : range(6,20) + range (30,40),
                  '3' : range(10,20) + range (30,40) + range(50,60),
                 }
        blumis = {'1' : range(1,6) + range(12,13) + range(16,30) + range(40,50) + range(39,80),
                  '2' : range(10,35),
                  '3' : range(10,15) + range(35,40) + range(45,51) + range(59,70),
                 }
        clumis = {'1' : range(1,6) + range(12,13) + range(16,30) + range(40,50) + range(39,80),
                  '2' : range(10,35),
                 }
        result = {'1' : range(2,20) + range(31,39) + range(45,49) + range(1,6) + range(12,13) + range(16,30) + range(40,50) + range(39,80),
                  '2' : range(6,20) + range (30,40) + range(10,35),
                  '3' : range(10,20) + range (30,40) + range(50,60) + range(10,15) + range(35,40) + range(45,51) + range(59,70),
                 }
        a = LumiList(runsAndLumis = alumis)
        b = LumiList(runsAndLumis = blumis)
        c = LumiList(runsAndLumis = blumis)
        r = LumiList(runsAndLumis = result)
        self.assertTrue((a|b).getCMSSWString() == r.getCMSSWString())
        self.assertTrue((a|b).getCMSSWString() == (b|a).getCMSSWString())
        self.assertTrue((a|b).getCMSSWString() == (a+b).getCMSSWString())

        # Test list constuction (faster)

        multiple = [alumis, blumis, clumis]
        easy = LumiList(runsAndLumis = multiple)
        hard = a + b
        hard += c
        self.assertTrue(hard.getCMSSWString() == easy.getCMSSWString())

    def testAnd(self):
        """
        a&b for lots of cases
        """

        alumis = {'1' : range(2,20) + range(31,39) + range(45,49),
                  '2' : range(6,20) + range (30,40),
                  '3' : range(10,20) + range (30,40) + range(50,60),
                  '4' : range(1,100),
                 }
        blumis = {'1' : range(1,6) + range(12,13) + range(16,25) + range(25,40) + range(40,50) + range(33,36),
                  '2' : range(10,35),
                  '3' : range(10,15) + range(35,40) + range(45,51) + range(59,70),
                  '5' : range(1,100),
                 }
        result = {'1' : range(2,6) + range(12,13) + range(16,20) + range(31,39) + range(45,49),
                  '2' : range(10,20) + range(30,35),
                  '3' : range(10,15) + range(35,40) + range(50,51)+ range(59,60),
                 }
        a = LumiList(runsAndLumis = alumis)
        b = LumiList(runsAndLumis = blumis)
        r = LumiList(runsAndLumis = result)
        self.assertTrue((a&b).getCMSSWString() == r.getCMSSWString())
        self.assertTrue((a&b).getCMSSWString() == (b&a).getCMSSWString())
        self.assertTrue((a|b).getCMSSWString() != r.getCMSSWString())

    def testRemoveSelect(self):
        """
        a-b for lots of cases
        """

        alumis = {'1' : range(2,20) + range(31,39) + range(45,49),
                  '2' : range(6,20) + range (30,40),
                  '3' : range(10,20) + range (30,40) + range(50,60),
                  '4' : range(10,20) + range (30,80),
                 }

        result = {'2' : range(6,20) + range (30,40),
                  '4' : range(10,20) + range (30,80),
                 }

        rem = LumiList(runsAndLumis = alumis)
        sel = LumiList(runsAndLumis = alumis)
        res = LumiList(runsAndLumis = result)

        rem.removeRuns([1,3])
        sel.selectRuns([2,4])

        self.assertTrue(rem.getCMSSWString() == res.getCMSSWString())
        self.assertTrue(sel.getCMSSWString() == res.getCMSSWString())
        self.assertTrue(sel.getCMSSWString() == rem.getCMSSWString())

    def testURL(self):
        URL = 'https://cms-service-dqm.web.cern.ch/cms-service-dqm/CAF/certification/Collisions12/8TeV/Reprocessing/Cert_190456-195530_8TeV_08Jun2012ReReco_Collisions12_JSON.txt'
        ll = LumiList(url=URL)
        self.assertTrue(len(ll) > 0)


    def testWrite(self):
        alumis = {'1' : range(2,20) + range(31,39) + range(45,49),
                  '2' : range(6,20) + range (30,40),
                  '3' : range(10,20) + range (30,40) + range(50,60),
                  '4' : range(1,100),
                 }
        a = LumiList(runsAndLumis = alumis)
        a.writeJSON('newFile.json')


if __name__ == '__main__':
    with open('lumiTest.json','w') as jsonFile:
        jsonFile.write('{"1": [[1, 33], [35, 35], [37, 47]], "2": [[49, 75], [77, 130], [133, 136]]}')
    unittest.main()
'''
# Test JSON file

#{"1": [[1, 33], [35, 35], [37, 47]], "2": [[49, 75], [77, 130], [133, 136]]}

if __name__ == '__main__':
    #############################################
    ## Load and save command line history when ##
    ## running interactively.                  ##
    #############################################
    import os, readline
    import atexit
    historyPath = os.path.expanduser("~/.pyhistory")


    def save_history(historyPath=historyPath):
        import readline
        readline.write_history_file(historyPath)
        if os.path.exists(historyPath):
            readline.read_history_file(historyPath)


    atexit.register(save_history)
    readline.parse_and_bind("set show-all-if-ambiguous on")
    readline.parse_and_bind("tab: complete")
    if os.path.exists (historyPath) :
        readline.read_history_file(historyPath)
        readline.set_history_length(-1)


