<commit_before># -*- coding: utf-8 -*-

import os
import sys
import xml.etree.ElementTree as eT
import time

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

class myListWidget(QListWidget):
    def doubleClickedSlot(self,item):
        #TODO: Add edit screen
        database = ex.getDatabase()
        Tool.editDialog(ex, database[item.text()][0])
        return True

class Tool(QMainWindow):
    def __init__(self):
        super().__init__()

        self.database = ""
        self.filename = ""
        self.initUI()

        if len(sys.argv) >= 2:
            self.importHandlerInit(self.frameLeft)

    def initUI(self):
        exitAction = QAction('&Schließen', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.triggered.connect(qApp.quit)

        openAction = QAction('&Öffnen', self)
        openAction.setShortcut('Ctrl+O')
        openAction.triggered.connect(lambda: self.importHandler(self.frameLeft))

        addAction = QAction('&Eintrag anlegen', self)
        addAction.setShortcut('Ctrl+N')
        addAction.triggered.connect(lambda: self.newDialog())

        saveAction = QAction('&Speichern', self)
        saveAction.setShortcut('Ctrl+S')
        saveAction.triggered.connect(lambda: self.saveProjectfile())

        saveasAction = QAction('&Speichern unter', self)
        saveasAction.setShortcut('Ctrl+Shift+S')
        saveasAction.triggered.connect(lambda: self.saveAsProjectfile())

        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&Datei')
        fileMenu.addAction(openAction)
        fileMenu.addAction(saveAction)
        fileMenu.addAction(saveasAction)
        fileMenu.addAction(exitAction)

        editMenu = menubar.addMenu('&Bearbeiten')
        editMenu.addAction(addAction)

        screenbox = QWidget()

        hbox = QHBoxLayout(self)
        screenbox.setLayout(hbox)

        self.frameLeft = myListWidget()
        self.frameLeft.setMaximumWidth(self.frameLeft.sizeHintForColumn(0))
        self.frameLeft.currentItemChanged.connect(lambda: self.displayInfo(self.frameLeft, self.frameRight))
        self.frameLeft.itemDoubleClicked[QListWidgetItem].connect(self.frameLeft.doubleClickedSlot)

        self.frameRight = QLabel()
        self.frameRight.setTextFormat(Qt.RichText)
        self.frameRight.setTextInteractionFlags(Qt.TextBrowserInteraction)
        self.frameRight.setOpenExternalLinks(True)

        screenSplitter = QSplitter(Qt.Horizontal)
        screenSplitter.addWidget(self.frameLeft)
        screenSplitter.addWidget(self.frameRight)

        hbox.addWidget(screenSplitter)

        self.setCentralWidget(screenbox)
        self.showMaximized()
        self.setWindowTitle('Promasu Manager')
        self.show()

    def importHandler(self, listView):
        fileDialog = QFileDialog()
        fileDialog.setFileMode(QFileDialog.ExistingFile)
        filename = fileDialog.getOpenFileName(self, 'Promasu Datenbank öffnen', '.', 'Promasu Projectdatabase (*.pmdx)')
        try:
            tree = eT.parse(filename[0])
            root = tree.getroot()
            listView.clear()
            for child in root:
                listView.addItem(child[0].text)
            listView.setMaximumWidth(listView.sizeHintForColumn(0) + 10)
            listView.setMinimumWidth(listView.sizeHintForColumn(0) + 10)
            self.database = self.listDictParser(root)
            self.filename = str(filename[0])
            self.setWindowTitle('Promasu Manager - ' + self.filename)
        except eT.ParseError:
            self.warnBox("Die Datei hat kein gültiges Format.")
            self.importHandler(listView)
            return False
        except FileNotFoundError:
            self.warnBox("Die Datei wurde nicht gefunden.")
            self.importHandler(listView)
            return False
        except IndexError:
            self.warnBox("Die Datei ist nicht im richtigen Format.")
            self.importHandler(listView)
            return False
        except PermissionError:
            self.warnBox("Sie besitzen nicht die Berechtigung um diese Datei zu öffnen.")
            self.importHandler(listView)
            return False
        return True

    def importHandlerInit(self, listView):
        filename = sys.argv[1]
        try:
            tree = eT.parse(filename)
            root = tree.getroot()
            listView.clear()
            for child in root:
                listView.addItem(child[0].text)
            listView.setMaximumWidth(listView.sizeHintForColumn(0) + 10)
            listView.setMinimumWidth(listView.sizeHintForColumn(0) + 10)
            self.database = self.listDictParser(root)
            self.filename = str(filename)
            self.setWindowTitle('Promasu Manager - ' + os.path.abspath(self.filename))
        except eT.ParseError:
            self.warnBox("Die Datei hat kein gültiges Format.")
            return False
        except FileNotFoundError:
            self.warnBox("Die Datei wurde nicht gefunden.")
            return False
        except IndexError:
            self.warnBox("Die Datei ist nicht im richtigen Format.")
            return False
        except PermissionError:
            self.warnBox("Sie besitzen nicht die Berechtigung um diese Datei zu öffnen.")
            return False
        return True

    def displayInfo(self, listView, labelView):
        tree = eT.parse(self.filename)
        root = tree.getroot()
        itemNumber = listView.currentRow()
        saveString = "Speicherort: " + root[itemNumber][1].text + "
" if root[itemNumber][1].text != "NONE" or None else ""
        buildString = "Build: " + root[itemNumber][2].text + "
" if root[itemNumber][2].text != "NONE" or None else ""
        gitString = "Git: " + root[itemNumber][3].text + "
" if root[itemNumber][3].text != "NONE" or None else ""
        docString = "Dokumentation: " + root[itemNumber][4].text + "
" if root[itemNumber][4].text != "NONE" or None else ""
        designString = "Designs: " + root[itemNumber][5].text + "
" if root[itemNumber][5].text != "NONE" or None else ""
        imageString = "Bilder: " + root[itemNumber][6].text + "
" if root[itemNumber][6].text != "NONE" or None else ""
        langString = "Sprache: " + root[itemNumber][8].text + "
" if root[itemNumber][8].text != "NONE" or None else ""
        versionString = "Version: " + root[itemNumber][9].text if root[itemNumber][9].text != "NONE" else ""
        labelView.setText(saveString+buildString+gitString+docString+designString+imageString+langString+versionString)
        return True

    def warnBox(self, msg):
        msgBox = QMessageBox()
        msgBox.setText(msg)
        msgBox.setWindowTitle("Warnung")
        msgBox.exec()

    def testBox(self, msg):
        msgBox = QMessageBox()
        msgBox.setText(msg)
        msgBox.setWindowTitle("Test")
        return True

    def newDialog(self):
        window = QDialog()
        window.setWindowTitle("Neu...")
        formLayout = QFormLayout()

        submit = QPushButton()
        submit.setText("Hinzufügen")
        submit.clicked.connect(lambda: self.applyChanges([name.text(),code.text(),build.text(),git.text(),documentation.text(),design.text(),image.text(),logo.text(),language.text(),version.text()],window))

        abord = QPushButton()
        abord.setText("Abbrechen")
        abord.clicked.connect(lambda: window.close())

        window.setLayout(formLayout)

        name = QLineEdit()
        code = QLineEdit()
        build = QLineEdit()
        git = QLineEdit()
        documentation = QLineEdit()
        design = QLineEdit()
        image = QLineEdit()
        logo = QLineEdit()
        language = QLineEdit()
        version = QLineEdit()

        formLayout.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter)

        formLayout.addRow("Name: ", name)
        formLayout.addRow("Code: ", code)
        formLayout.addRow("Build: ", build)
        formLayout.addRow("Git: ", git)
        formLayout.addRow("Dokumentation: ", documentation)
        formLayout.addRow("Design: ", design)
        formLayout.addRow("Bilder: ", image)
        formLayout.addRow("Logo: ", logo)
        formLayout.addRow("Sprache: ", language)
        formLayout.addRow("Version: ", version)

        formLayout.addRow(submit, abord)

        window.exec()

    def editDialog(self, dictKey):
        window = QDialog()
        window.setWindowTitle("Ändern...")
        formLayout = QFormLayout()

        submit = QPushButton()
        submit.setText("Ändern")
        submit.clicked.connect(lambda: self.applyChanges([name.text(),code.text(),build.text(),git.text(),documentation.text(),design.text(),image.text(),logo.text(),language.text(),version.text()],window,dictKey))

        abord = QPushButton()
        abord.setText("Abbrechen")
        abord.clicked.connect(lambda: window.close())

        window.setLayout(formLayout)

        name = QLineEdit()
        name.setText(self.database[dictKey][0])
        code = QLineEdit()
        code.setText(self.database[dictKey][1])
        build = QLineEdit()
        build.setText(self.database[dictKey][2])
        git = QLineEdit()
        git.setText(self.database[dictKey][3])
        documentation = QLineEdit()
        documentation.setText(self.database[dictKey][4])
        design = QLineEdit()
        design.setText(self.database[dictKey][5])
        image = QLineEdit()
        image.setText(self.database[dictKey][6])
        logo = QLineEdit()
        logo.setText(self.database[dictKey][7])
        language = QLineEdit()
        language.setText(self.database[dictKey][8])
        version = QLineEdit()
        version.setText(self.database[dictKey][9])

        formLayout.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter)

        formLayout.addRow("Name: ", name)
        formLayout.addRow("Code: ", code)
        formLayout.addRow("Build: ", build)
        formLayout.addRow("Git: ", git)
        formLayout.addRow("Dokumentation: ", documentation)
        formLayout.addRow("Design: ", design)
        formLayout.addRow("Bilder: ", image)
        formLayout.addRow("Logo: ", logo)
        formLayout.addRow("Sprache: ", language)
        formLayout.addRow("Version: ", version)

        formLayout.addRow(submit, abord)

        window.exec()

    def applyChanges(self, entryList, window, dictKey=None):
        if dictKey != None:
            if entryList[0] == self.database[dictKey][0]:
                self.database[entryList[0]] = entryList
            else:
                del self.database[dictKey]
                self.database[entryList[0]] = entryList
        else:
            if entryList[0] == "":
                self.warnBox("Der Projektname darf nicht leer sein!")
                return False
            else:
                self.database[entryList[0]] = []
                for i in range(len(entryList)):
                    appendString = entryList[i] if entryList[i] != "" else "NONE"
                    self.database[entryList[0]].append(appendString)
        self.saveProjectfile()
        self.refreshViews()
        window.close()

    def refreshViews(self):
        self.displayInfo(self.frameLeft, self.frameRight)
        self.frameLeft.clear()
        for key in self.database:
            self.frameLeft.addItem(key)

    def listDictParser(self, xmlList):
        dict = {}
        for child in xmlList:
            dict[child[0].text] = child[0:len(child)]
        for entry in dict:
            for i in range(10):
                dict[entry][i] = dict[entry][i].text
        return dict

    def onChanged(self, text):
        self.lbl.setText(text)
        self.lbl.adjustSize()

    def getDatabase(self):
        database = self.database
        return database

    def saveProjectfile(self):
        try:
            fileContent = self.xmlWriter()
            f = open(self.filename, 'w+')
            f.write(fileContent)
            f.close()
            return True
        except FileNotFoundError:
            return False
        except:
            self.saveAsProjectfile()
            return True

    def saveAsProjectfile(self):
        try:
            fileDialog = QFileDialog()
            fileDialog.setFileMode(QFileDialog.AnyFile)
            fileDialog.setDefaultSuffix(".pmdx")
            dialogFilename = fileDialog.getSaveFileName(self, 'Promasu Datenbank speichern', '.', 'Promasu Projectdatabase (*.pmdx)')
            fileContent = self.xmlWriter()
            if dialogFilename[0][-5:] != ".pmdx":
                filename = dialogFilename[0] + ".pmdx"
            else:
                filename = dialogFilename[0]
            f = open(filename, 'w+')
            f.write(fileContent)
            f.close()
            self.filename = filename
            self.setWindowTitle("Promasu Manager - " + self.filename)
            return True
        except FileNotFoundError:
            return False
        except Exception as e:
            print(e)
            self.saveAsProjectfile()
            return True

    def xmlWriter(self):
        dict = self.database
        fileContent = ""
        fileContent += "<database>\n"
        for entry in dict:
            fileContent += "    <project>\n"
            fileContent += "        <name>"+dict[entry][0]+"</name>\n" if dict[entry][0] != "NONE" or None else "        <name>NONE</name>\n"
            fileContent += "        "+dict[entry][1]+"\n" if dict[entry][1] != "NONE" or None else "        NONE\n"
            fileContent += "        <build>"+dict[entry][2]+"</build>\n" if dict[entry][2] != "NONE" or None else "        <build>NONE</build>\n"
            fileContent += "        <git>"+dict[entry][3]+"</git>\n" if dict[entry][3] != "NONE" or None else "        <git>NONE</git>\n"
            fileContent += "        <documentation>"+dict[entry][4]+"</documentation>\n" if dict[entry][4] != "NONE" or None else "        <documentation>NONE</documentation>\n"
            fileContent += "        <design>"+dict[entry][5]+"</design>\n" if dict[entry][5] != "NONE" or None else "        <design>NONE</design>\n"
            fileContent += "        <image>"+dict[entry][6]+"</image>\n" if dict[entry][6] != "NONE" or None else "        <image>NONE</image>\n"
            fileContent += "        <logo>"+dict[entry][7]+"</logo>\n" if dict[entry][7] != "NONE" or None else "        <logo>NONE</logo>\n"
            fileContent += "        <language>"+dict[entry][8]+"</language>\n" if dict[entry][8] != "NONE" or None else "        <language>NONE</language>\n"
            fileContent += "        <version>"+dict[entry][9]+"</version>\n" if dict[entry][9] != "NONE" or None else "        <version>NONE</version>\n"
            fileContent += "    </project>\n"
        fileContent += "</database>"
        return fileContent

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Tool()
    sys.exit(app.exec_())
<commit_msg>Fixed some bugs and never closing windows. Make ready for first alpha release.<commit_after># -*- coding: utf-8 -*-

import os
import sys
import xml.etree.ElementTree as eT
import time

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

class myListWidget(QListWidget):
    def doubleClickedSlot(self,item):
        #TODO: Add edit screen
        database = ex.getDatabase()
        Tool.editDialog(ex, database[item.text()][0])
        return True

class Tool(QMainWindow):
    def __init__(self):
        super().__init__()

        self.database = {}
        self.filename = ""
        self.initUI()

        if len(sys.argv) >= 2:
            self.importHandlerInit(self.frameLeft, sys.argv[1])

    def initUI(self):
        exitAction = QAction('&Schließen', self)
        exitAction.setShortcut('Ctrl+Q')
        exitAction.triggered.connect(qApp.quit)

        openAction = QAction('&Öffnen', self)
        openAction.setShortcut('Ctrl+O')
        openAction.triggered.connect(lambda: self.importHandler(self.frameLeft))

        addAction = QAction('&Eintrag anlegen', self)
        addAction.setShortcut('Ctrl+N')
        addAction.triggered.connect(lambda: self.newDialog())

        saveAction = QAction('&Speichern', self)
        saveAction.setShortcut('Ctrl+S')
        saveAction.triggered.connect(lambda: self.saveProjectfile())

        saveasAction = QAction('&Speichern unter', self)
        saveasAction.setShortcut('Ctrl+Shift+S')
        saveasAction.triggered.connect(lambda: self.saveAsProjectfile())

        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&Datei')
        fileMenu.addAction(openAction)
        fileMenu.addAction(saveAction)
        fileMenu.addAction(saveasAction)
        fileMenu.addAction(exitAction)

        editMenu = menubar.addMenu('&Bearbeiten')
        editMenu.addAction(addAction)

        screenbox = QWidget()

        hbox = QHBoxLayout(self)
        screenbox.setLayout(hbox)

        self.frameLeft = myListWidget()
        self.frameLeft.setMaximumWidth(self.frameLeft.sizeHintForColumn(0))
        self.frameLeft.currentItemChanged.connect(lambda: self.displayInfo(self.frameLeft, self.frameRight))
        self.frameLeft.itemDoubleClicked[QListWidgetItem].connect(self.frameLeft.doubleClickedSlot)

        self.frameRight = QLabel()
        self.frameRight.setTextFormat(Qt.RichText)
        self.frameRight.setTextInteractionFlags(Qt.TextBrowserInteraction)
        self.frameRight.setOpenExternalLinks(True)

        screenSplitter = QSplitter(Qt.Horizontal)
        screenSplitter.addWidget(self.frameLeft)
        screenSplitter.addWidget(self.frameRight)

        hbox.addWidget(screenSplitter)

        self.setCentralWidget(screenbox)
        self.showMaximized()
        self.setWindowTitle('Promasu Manager')
        self.show()

    def importHandler(self, listView):
        fileDialog = QFileDialog()
        fileDialog.setFileMode(QFileDialog.ExistingFile)
        filename = fileDialog.getOpenFileName(self, 'Promasu Datenbank öffnen', '.', 'Promasu Projectdatabase (*.pmdx)')
        try:
            tree = eT.parse(filename[0])
            root = tree.getroot()
            listView.clear()
            for child in root:
                listView.addItem(child[0].text)
            listView.setMaximumWidth(listView.sizeHintForColumn(0) + 10)
            listView.setMinimumWidth(listView.sizeHintForColumn(0) + 10)
            self.database = self.listDictParser(root)
            self.filename = str(filename[0])
            self.setWindowTitle('Promasu Manager - ' + self.filename)
        except eT.ParseError:
            self.warnBox("Die Datei hat kein gültiges Format.")
            self.importHandler(listView)
            return False
        except FileNotFoundError:
            if not filename[0] == "":
                self.importHandler(listView)
            self.warnBox("Die Datei wurde nicht gefunden.")
            return False
        except IndexError:
            self.warnBox("Die Datei ist nicht im richtigen Format.")
            self.importHandler(listView)
            return False
        except PermissionError:
            self.warnBox("Sie besitzen nicht die Berechtigung um diese Datei zu öffnen.")
            self.importHandler(listView)
            return False
        return True

    def importHandlerInit(self, listView, filename):
        try:
            tree = eT.parse(filename)
            root = tree.getroot()
            listView.clear()
            for child in root:
                listView.addItem(child[0].text)
            listView.setMaximumWidth(listView.sizeHintForColumn(0) + 10)
            listView.setMinimumWidth(listView.sizeHintForColumn(0) + 10)
            self.database = self.listDictParser(root)
            self.filename = str(filename)
            self.setWindowTitle('Promasu Manager - ' + os.path.abspath(self.filename))
        except eT.ParseError:
            self.warnBox("Die Datei hat kein gültiges Format.")
            return False
        except FileNotFoundError:
            self.warnBox("Die Datei wurde nicht gefunden.")
            return False
        except IndexError:
            self.warnBox("Die Datei ist nicht im richtigen Format.")
            return False
        except PermissionError:
            self.warnBox("Sie besitzen nicht die Berechtigung um diese Datei zu öffnen.")
            return False
        return True

    def displayInfo(self, listView, labelView):
        tree = eT.parse(self.filename)
        root = tree.getroot()
        itemNumber = listView.currentRow()
        saveString = "Speicherort: " + root[itemNumber][1].text + "
" if root[itemNumber][1].text != "NONE" or None else ""
        buildString = "Build: " + root[itemNumber][2].text + "
" if root[itemNumber][2].text != "NONE" or None else ""
        gitString = "Git: " + root[itemNumber][3].text + "
" if root[itemNumber][3].text != "NONE" or None else ""
        docString = "Dokumentation: " + root[itemNumber][4].text + "
" if root[itemNumber][4].text != "NONE" or None else ""
        designString = "Designs: " + root[itemNumber][5].text + "
" if root[itemNumber][5].text != "NONE" or None else ""
        imageString = "Bilder: " + root[itemNumber][6].text + "
" if root[itemNumber][6].text != "NONE" or None else ""
        langString = "Sprache: " + root[itemNumber][8].text + "
" if root[itemNumber][8].text != "NONE" or None else ""
        versionString = "Version: " + root[itemNumber][9].text if root[itemNumber][9].text != "NONE" else ""
        labelView.setText(saveString+buildString+gitString+docString+designString+imageString+langString+versionString)
        return True

    def warnBox(self, msg):
        msgBox = QMessageBox()
        msgBox.setText(msg)
        msgBox.setWindowTitle("Warnung")
        msgBox.exec()

    def testBox(self, msg):
        msgBox = QMessageBox()
        msgBox.setText(msg)
        msgBox.setWindowTitle("Test")
        return True

    def newDialog(self):
        window = QDialog()
        window.setWindowTitle("Neu...")
        formLayout = QFormLayout()

        submit = QPushButton()
        submit.setText("Hinzufügen")
        submit.clicked.connect(lambda: self.applyChanges([name.text(),code.text(),build.text(),git.text(),documentation.text(),design.text(),image.text(),logo.text(),language.text(),version.text()],window))

        abord = QPushButton()
        abord.setText("Abbrechen")
        abord.clicked.connect(lambda: window.close())

        window.setLayout(formLayout)

        name = QLineEdit()
        code = QLineEdit()
        build = QLineEdit()
        git = QLineEdit()
        documentation = QLineEdit()
        design = QLineEdit()
        image = QLineEdit()
        logo = QLineEdit()
        language = QLineEdit()
        version = QLineEdit()

        formLayout.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter)

        formLayout.addRow("Name: ", name)
        formLayout.addRow("Code: ", code)
        formLayout.addRow("Build: ", build)
        formLayout.addRow("Git: ", git)
        formLayout.addRow("Dokumentation: ", documentation)
        formLayout.addRow("Design: ", design)
        formLayout.addRow("Bilder: ", image)
        formLayout.addRow("Logo: ", logo)
        formLayout.addRow("Sprache: ", language)
        formLayout.addRow("Version: ", version)

        formLayout.addRow(submit, abord)

        window.exec()

    def editDialog(self, dictKey):
        window = QDialog()
        window.setWindowTitle("Ändern...")
        formLayout = QFormLayout()

        submit = QPushButton()
        submit.setText("Ändern")
        submit.clicked.connect(lambda: self.applyChanges([name.text(),code.text(),build.text(),git.text(),documentation.text(),design.text(),image.text(),logo.text(),language.text(),version.text()],window,dictKey))

        abord = QPushButton()
        abord.setText("Abbrechen")
        abord.clicked.connect(lambda: window.close())

        window.setLayout(formLayout)

        name = QLineEdit()
        name.setText(self.database[dictKey][0])
        code = QLineEdit()
        code.setText(self.database[dictKey][1])
        build = QLineEdit()
        build.setText(self.database[dictKey][2])
        git = QLineEdit()
        git.setText(self.database[dictKey][3])
        documentation = QLineEdit()
        documentation.setText(self.database[dictKey][4])
        design = QLineEdit()
        design.setText(self.database[dictKey][5])
        image = QLineEdit()
        image.setText(self.database[dictKey][6])
        logo = QLineEdit()
        logo.setText(self.database[dictKey][7])
        language = QLineEdit()
        language.setText(self.database[dictKey][8])
        version = QLineEdit()
        version.setText(self.database[dictKey][9])

        formLayout.setLabelAlignment(Qt.AlignRight | Qt.AlignVCenter)

        formLayout.addRow("Name: ", name)
        formLayout.addRow("Code: ", code)
        formLayout.addRow("Build: ", build)
        formLayout.addRow("Git: ", git)
        formLayout.addRow("Dokumentation: ", documentation)
        formLayout.addRow("Design: ", design)
        formLayout.addRow("Bilder: ", image)
        formLayout.addRow("Logo: ", logo)
        formLayout.addRow("Sprache: ", language)
        formLayout.addRow("Version: ", version)

        formLayout.addRow(submit, abord)

        window.exec()

    def applyChanges(self, entryList, window, dictKey=None):
        if dictKey != None:
            if entryList[0] == self.database[dictKey][0]:
                self.database[entryList[0]] = entryList
            else:
                del self.database[dictKey]
                self.database[entryList[0]] = entryList
        else:
            if entryList[0] == "":
                self.warnBox("Der Projektname darf nicht leer sein!")
                return False
            else:
                self.database[entryList[0]] = []
                for i in range(len(entryList)):
                    appendString = entryList[i] if entryList[i] != "" else "NONE"
                    self.database[entryList[0]].append(appendString)
        if not self.filename == "":
            self.saveProjectfile()
            self.refreshViews()
        else:
            self.saveAsProjectfile()
            self.importHandlerInit(self.frameLeft,self.filename)
        window.close()

    def refreshViews(self):
        self.displayInfo(self.frameLeft, self.frameRight)
        self.frameLeft.clear()
        for key in self.database:
            self.frameLeft.addItem(key)

    def listDictParser(self, xmlList):
        dict = {}
        for child in xmlList:
            dict[child[0].text] = child[0:len(child)]
        for entry in dict:
            for i in range(10):
                dict[entry][i] = dict[entry][i].text
        return dict

    def onChanged(self, text):
        self.lbl.setText(text)
        self.lbl.adjustSize()

    def getDatabase(self):
        database = self.database
        return database

    def saveProjectfile(self):
        try:
            fileContent = self.xmlWriter()
            f = open(self.filename, 'w+')
            f.write(fileContent)
            f.close()
            return True
        except FileNotFoundError:
            return False
        except:
            self.saveAsProjectfile()
            return True

    def saveAsProjectfile(self):
        try:
            fileDialog = QFileDialog()
            fileDialog.setFileMode(QFileDialog.AnyFile)
            fileDialog.setDefaultSuffix(".pmdx")
            dialogFilename = fileDialog.getSaveFileName(self, 'Promasu Datenbank speichern', '.', 'Promasu Projectdatabase (*.pmdx)')
            fileContent = self.xmlWriter()
            if dialogFilename[0][-5:] != ".pmdx":
                filename = dialogFilename[0] + ".pmdx"
            else:
                filename = dialogFilename[0]
            f = open(filename, 'w+')
            f.write(fileContent)
            f.close()
            self.filename = filename
            self.setWindowTitle("Promasu Manager - " + self.filename)
            return True
        except FileNotFoundError:
            return False
        except Exception as e:
            print(e)
            self.saveAsProjectfile()
            return True

    def xmlWriter(self):
        dict = self.database
        fileContent = ""
        fileContent += "<database>\n"
        for entry in dict:
            fileContent += "    <project>\n"
            fileContent += "        <name>"+dict[entry][0]+"</name>\n" if dict[entry][0] != "NONE" or None else "        <name>NONE</name>\n"
            fileContent += "        "+dict[entry][1]+"\n" if dict[entry][1] != "NONE" or None else "        NONE\n"
            fileContent += "        <build>"+dict[entry][2]+"</build>\n" if dict[entry][2] != "NONE" or None else "        <build>NONE</build>\n"
            fileContent += "        <git>"+dict[entry][3]+"</git>\n" if dict[entry][3] != "NONE" or None else "        <git>NONE</git>\n"
            fileContent += "        <documentation>"+dict[entry][4]+"</documentation>\n" if dict[entry][4] != "NONE" or None else "        <documentation>NONE</documentation>\n"
            fileContent += "        <design>"+dict[entry][5]+"</design>\n" if dict[entry][5] != "NONE" or None else "        <design>NONE</design>\n"
            fileContent += "        <image>"+dict[entry][6]+"</image>\n" if dict[entry][6] != "NONE" or None else "        <image>NONE</image>\n"
            fileContent += "        <logo>"+dict[entry][7]+"</logo>\n" if dict[entry][7] != "NONE" or None else "        <logo>NONE</logo>\n"
            fileContent += "        <language>"+dict[entry][8]+"</language>\n" if dict[entry][8] != "NONE" or None else "        <language>NONE</language>\n"
            fileContent += "        <version>"+dict[entry][9]+"</version>\n" if dict[entry][9] != "NONE" or None else "        <version>NONE</version>\n"
            fileContent += "    </project>\n"
        fileContent += "</database>"
        return fileContent

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Tool()
    sys.exit(app.exec_())
<|endoftext|>


<commit_before>#! /usr/bin/env python
#
# Copyright (c) 2015 Autodesk Inc.
# All rights reserved
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import cmd
import json
import os
import sys

from os.path import isfile
from subprocess import Popen, PIPE
from sys import exit

def cli():
    """
        Minimalistic self-contained wrapper performing the curl calls to the ochopod proxy. The input
        is turned into a POST -H X-Shell:<> to the proxy at port TCP 9000. Any token from that input that
        matches a local file (wherever the script is running from) will force an upload for the said file.
        This mechanism is used for instance to upload the container definition YAML files when deploying a
        new cluster.

        The proxy IP is either passed as the first command-line argument or vi a $OCHOPOD_PROXY.
    """

    if len(sys.argv) > 1:
        ip = sys.argv[1]

    elif 'OCHOPOD_PROXY' in os.environ:
        ip = os.environ['OCHOPOD_PROXY']

    else:
        print 'either set $OCHOPOD_PROXY or pass the proxy IP as an argument'
        exit(1)

    class Shell(cmd.Cmd):

        prompt = '%s > ' % ip
        ruler = '-'

        def precmd(self, line):
            return 'shell %s' % line

        def emptyline(self):
            pass

        def do_exit(self, _):
            raise KeyboardInterrupt

        def do_shell(self, line):
            tokens = line.split(' ')
            files = ['-F %s=@%s' % (token, token) for token in tokens if isfile(token)]
            snippet = 'curl -X POST -H "X-Shell:%s" %s %s:9000/shell' % (line, ' '.join(files), ip)
            code, out = self._exec(snippet)
            print json.loads(out)['out'] if code is 0 else 'i/o failure (is the proxy down ?)'

        def _exec(self, snippet):
            pid = Popen(snippet, shell=True, stdout=PIPE, stderr=PIPE)
            pid.wait()
            return pid.returncode, pid.stdout.read()

    print('welcome to the ocho CLI ! (CTRL-C to exit)')
    try:

        Shell().cmdloop()

    except KeyboardInterrupt:
        exit(0)

    except Exception as failure:
        print('internal failure <- %s' % str(failure))
        exit(1)

if __name__ == "__main__":
    cli()<commit_msg>Only calling /shell if there is input<commit_after>#! /usr/bin/env python
#
# Copyright (c) 2015 Autodesk Inc.
# All rights reserved
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import cmd
import json
import os
import sys

from os.path import isfile
from subprocess import Popen, PIPE
from sys import exit

def cli():
    """
        Minimalistic self-contained wrapper performing the curl calls to the ochopod proxy. The input
        is turned into a POST -H X-Shell:<> to the proxy at port TCP 9000. Any token from that input that
        matches a local file (wherever the script is running from) will force an upload for the said file.
        This mechanism is used for instance to upload the container definition YAML files when deploying a
        new cluster.

        The proxy IP is either passed as the first command-line argument or vi a $OCHOPOD_PROXY.
    """

    if len(sys.argv) > 1:
        ip = sys.argv[1]

    elif 'OCHOPOD_PROXY' in os.environ:
        ip = os.environ['OCHOPOD_PROXY']

    else:
        print 'either set $OCHOPOD_PROXY or pass the proxy IP as an argument'
        exit(1)

    class Shell(cmd.Cmd):

        prompt = '%s > ' % ip
        ruler = '-'

        def precmd(self, line):
            return 'shell %s' % line

        def emptyline(self):
            pass

        def do_exit(self, _):
            raise KeyboardInterrupt

        def do_shell(self, line):
            if line:
                tokens = line.split(' ')
                files = ['-F %s=@%s' % (token, token) for token in tokens if isfile(token)]
                snippet = 'curl -X POST -H "X-Shell:%s" %s %s:9000/shell' % (line, ' '.join(files), ip)
                code, out = self._exec(snippet)
                print json.loads(out)['out'] if code is 0 else 'i/o failure (is the proxy down ?)'

        def _exec(self, snippet):
            pid = Popen(snippet, shell=True, stdout=PIPE, stderr=PIPE)
            pid.wait()
            return pid.returncode, pid.stdout.read()

    print('welcome to the ocho CLI ! (CTRL-C to exit)')
    try:

        Shell().cmdloop()

    except KeyboardInterrupt:
        exit(0)

    except Exception as failure:
        print('internal failure <- %s' % str(failure))
        exit(1)

if __name__ == "__main__":
    cli()<|endoftext|>


<commit_before>"""
A number of argparse helpers, generally using custom argument typing for
additional input value transformations.
"""
import argparse
import re
import sys
from typing import List, Tuple, Union

def add_standard_params(parser: argparse.ArgumentParser) -> None:
    """
    Adds -v, -p and -t.
    """
    parser.add_argument("-v", "--verbose",
                        dest="verbose",
                        help="Print debug info to stdout",
                        action='store_true')
    parser.add_argument("-p", "--post-to-github",
                        help="Post all issues to GitHub instead of stdout",
                        action="store_true")
    parser.add_argument("-t", "--time-log-dir",
                        help="If set, time logs are written to that directory",
                        action="store", default=None)

def add_event_param(
        parser: argparse.ArgumentParser,
        dest: str = "event",
        required: bool = False,
        template: str = "{}y visit",
        accepted_regex: str = r'^\d+$',
        coerce: bool = True) -> None:
    """
    Handles and transforms arbitrarily entered events.
    """

    nargs = '+' if required else '*'

    def __event_handler(arg: str) -> Union[str, None]:
        if re.match(accepted_regex, arg):
            return template.format(arg)
        elif coerce:
            return arg
        else:
            return None

    parser.add_argument("-e", "--event",
                        help=("Event name matching regex {}, before "
                              "transformation to {}").format(accepted_regex,
                                                             template),
                        nargs=nargs,
                        required=required,
                        type=__event_handler)

def add_subject_param(parser: argparse.ArgumentParser,
                      dest="subject",
                      required: bool = False,
                      choices: List[str] = None) -> None:
    """
    Add parameter for subject ID entry.
    """
    nargs = '+' if required else '*'

    parser.add_argument("-s", "--study-id", "--subject",
                        help="Subject IDs that the script should affect",
                        nargs=nargs,
                        dest=dest,
                        choices=choices,
                        # metavar="STUDY_ID"
                        required=required,
                        action="store")

def add_form_param(parser: argparse.ArgumentParser,
                   dest='forms',
                   required: bool = False,
                   eligible_forms: List[Tuple[str]] = [],
                   raise_missing: bool = True,
                   short_switch="-i") -> None:
    """
    Add the forms parameter, with checking for form aliases.

    eligible_forms: a list of tuples, where the first element in the tuple is
        the canonical name of the form, and the remaining are allowable names
        for the form
    short_switch: the "short" option - `-i` by default (short for instruments),
        but some CLIs use -f to mean --forms instead of --force.
    """
    # _eligible_forms = []
    # for x in eligible_forms:
    #     if isinstance(x, string_types):
    #         _eligible_forms.append((x))
    #     else:
    #         _eligible_forms.append(x)

    def __form_handler(arg):
        forms_found = [x for x in eligible_forms if arg in x]
        if len(forms_found) == 1:
            return forms_found[0][0]
        elif not raise_missing:
            return None
        else:
            if len(forms_found) == 0:
                raise ValueError("{} not found in eligible forms {}"
                                 .format(arg, eligible_forms))
            elif len(forms_found) > 1:
                raise ValueError("Ambiguous {}, found in following forms: {}"
                                 .format(arg, forms_found))

    nargs = '+' if required else '*'
    parser.add_argument(short_switch, '--forms', '--form', '--instrument',
                        help="Forms that the script should affect",
                        dest=dest,
                        nargs=nargs,
                        required=required,
                        type=__form_handler)

def transform_commas_in_args(arg_list: List[str] = sys.argv[1:]) -> List[str]:
    """
    Essentially, turn comma splits into spaces.

    Limited, in that it splices *any* commas, which might be actually valid
    filename parts etc. Ideally, this would be part of an argparse.Action.
    """
    _arg_list = []
    for x in arg_list:
        if "," in x:
            _arg_list.extend(x.split(","))
        else:
            _arg_list.append(x)
    return _arg_list

# if __name__ == '__main__':
#     args = transform_commas_in_args(sys.argv[1:])
#     parser = argparse.ArgumentParser()
#     add_event_param(parser, template="visit_{}")
#     add_subject_param(parser)
#     add_form_param(parser, eligible_forms=[
#         ('limesurvey_ssaga_youth', 'lssaga_youth', 'lssaga1_youth')
#     ])
#     add_standard_params(parser)
#     print(parser.parse_args(args))
<commit_msg>Improve handling of CLI edge cases<commit_after>"""
A number of argparse helpers, generally using custom argument typing for
additional input value transformations.
"""
import argparse
import re
import sys
from typing import List, Tuple, Union

def add_standard_params(parser: argparse.ArgumentParser) -> None:
    """
    Adds -v, -p and -t.
    """
    parser.add_argument("-v", "--verbose",
                        dest="verbose",
                        help="Print debug info to stdout",
                        action='store_true')
    parser.add_argument("-p", "--post-to-github",
                        help="Post all issues to GitHub instead of stdout",
                        action="store_true")
    parser.add_argument("-t", "--time-log-dir",
                        help="If set, time logs are written to that directory",
                        action="store", default=None)

def add_event_param(
        parser: argparse.ArgumentParser,
        dest: str = "event",
        required: bool = False,
        template: str = "{}y visit",
        accepted_regex: str = r'^\d+$',
        keep_nonmatch: bool = False) -> None:
    """
    Handles and transforms arbitrarily entered events.
    """

    nargs = '+' if required else '*'

    def __event_handler(arg: str) -> Union[str, None]:
        if re.match(accepted_regex, arg):
            return template.format(arg)
        elif keep_nonmatch:
            return arg
        else:
            return None

    parser.add_argument("-e", "--event",
                        help=("Event name matching regex {}, before "
                              "transformation to {}").format(accepted_regex,
                                                             template),
                        nargs=nargs,
                        required=required,
                        type=__event_handler)

def add_subject_param(parser: argparse.ArgumentParser,
                      dest="subject",
                      required: bool = False,
                      choices: List[str] = None) -> None:
    """
    Add parameter for subject ID entry.
    """
    nargs = '+' if required else '*'

    parser.add_argument("-s", "--study-id", "--subject",
                        help="Subject IDs that the script should affect",
                        nargs=nargs,
                        dest=dest,
                        choices=choices,
                        # metavar="STUDY_ID"
                        required=required,
                        action="store")

def add_form_param(parser: argparse.ArgumentParser,
                   dest='forms',
                   required: bool = False,
                   eligible_forms: List[Tuple[str]] = [],
                   raise_missing: bool = True,
                   short_switch="-i") -> None:
    """
    Add the forms parameter, with checking for form aliases.

    eligible_forms: a list of tuples, where the first element in the tuple is
        the canonical name of the form, and the remaining are allowable names
        for the form
    short_switch: the "short" option - `-i` by default (short for instruments),
        but some CLIs use -f to mean --forms instead of --force.
    """
    # _eligible_forms = []
    # for x in eligible_forms:
    #     if isinstance(x, string_types):
    #         _eligible_forms.append((x))
    #     else:
    #         _eligible_forms.append(x)

    def __form_handler(arg):
        if len(eligible_forms) == 0:
            return arg

        forms_found = [x for x in eligible_forms if arg in x]
        if len(forms_found) == 1:
            return forms_found[0][0]
        elif not raise_missing:
            return None
        else:
            if len(forms_found) == 0:
                raise ValueError("{} not found in eligible forms {}"
                                 .format(arg, eligible_forms))
            elif len(forms_found) > 1:
                raise ValueError("Ambiguous {}, found in following forms: {}"
                                 .format(arg, forms_found))

    nargs = '+' if required else '*'
    parser.add_argument(short_switch, '--forms', '--form', '--instrument',
                        help="Forms that the script should affect",
                        dest=dest,
                        nargs=nargs,
                        required=required,
                        type=__form_handler)

def transform_commas_in_args(arg_list: List[str] = sys.argv[1:]) -> List[str]:
    """
    Essentially, turn comma splits into spaces.

    Limited, in that it splices *any* commas, which might be actually valid
    filename parts etc. Ideally, this would be part of an argparse.Action.
    """
    _arg_list = []
    for x in arg_list:
        if "," in x:
            _arg_list.extend(x.split(","))
        else:
            _arg_list.append(x)
    return _arg_list

# if __name__ == '__main__':
#     args = transform_commas_in_args(sys.argv[1:])
#     parser = argparse.ArgumentParser()
#     add_event_param(parser, template="visit_{}")
#     add_subject_param(parser)
#     add_form_param(parser, eligible_forms=[
#         ('limesurvey_ssaga_youth', 'lssaga_youth', 'lssaga1_youth')
#     ])
#     add_standard_params(parser)
#     print(parser.parse_args(args))
<|endoftext|>


<commit_before>#!/usr/bin/env python3

import atexit
import readline
import signal
import sys
import os
#import plot
import kraken
import btce
import exsimu
#import cryptsy
import time
import threading
from depth import depth

version = "0.1 beta"
prompt = "bot > "
histfile = "/tmp/history"
usage = {'exit': 'exit terminal',
         'help': 'show this message',
         'version': 'show version',
         'echo': 'echo messsage',
         'showmods': 'show modules',
         'load': 'load module',
         'plot': 'plot data',
         'kraken.get_balance': 'get balance from kraken',
         'kraken.get_depth': 'get depth from kraken',
         'kraken.print_depth': 'get depth from kraken',
         'kraken.add_order': 'add an order in kraken',
         'spread': 'get spread e.g: bot> spread api1=btce api2=kraken',
         'btce.add_order': 'add an order in btc-e',
         'btce.get_balance': 'get balance from btc-e',
         'btce.get_depth': 'get depth from btc-e',
         'btce.print_depth': 'get depth from btc-e',
         'exsimu1.add_order': 'add an order in btc-e (price, vol, order)',
         'exsimu1.fees': 'get exchange fees',
         'exsimu1.balance': 'get balance from simulator',
         'exsimu1.depth': 'get depth from simulator',
         'exsimu1.active_orders': 'get active orders',
         'exsimu1.cancel_orders': 'cancel active order',
         'exsimu1.trade_history': 'get trade history',
         'exsimu1.modify_balance': 'set currency balance in sim (cur, amount)'
         }

def get_depth(callback, callback_args, interval):
    while True:
        callback(**callback_args)
        time.sleep(interval)

def start_depth_thread(api):
    t = threading.Thread(target=get_depth,
                         args=[api.get_depth, {}, 1])
    t.setDaemon(True)
    t.start()

markets = {
    'kraken': kraken.kraken('foobox'),
    'btce': btce.btce(),
    'exsimu1': exsimu.exsimu('data1', 'exsimu1'),
    'exsimu2': exsimu.exsimu('data2', 'exsimu2'),
}

#   'cryptsy': cryptsy_api,

markets['kraken'].decipher_key('kraken.enc')

class Cli:
    matches = []
    completions = {}
    values = []

    def __init__(self, histfile, values=None):
        readline.clear_history()
        try:
            readline.read_history_file(histfile)
        except:
            pass
        atexit.register(readline.write_history_file, histfile)
        readline.set_completer(self.completer)
        readline.parse_and_bind('tab: menu-complete')
        self.append_values(values)

    def append_values(self, appendix):
        self.values += appendix

    def completer(self, text, state):
        try:
            self.matches = self.completions[text]
        except KeyError:
            self.matches = [value for value in self.values
                            if text.upper() in value.upper()]
            self.completions[text] = self.matches
        try:
            return self.matches[state]
        except IndexError:
            return None

class MethodDispather():
    #plot = plot.Plot()

#    def depth(self):
#        print('Kraken')
#        print('------')
#        print(kraken.kraken.current_depth[-1])
#
#        print('btce')
#        print('------')
#        print(btce.btce.current_depth[-1])

    def spread(self, **kwargs):
        kwargs.setdefault('pair', 'btc_eur')
        params = {}
        try:
            params['api1'] = markets[kwargs['api1']]
            params['api2'] = markets[kwargs['api2']]
            params['pair'] = kwargs['pair']
        except Exception as e:
            raise Exception('unknown key', e)
        try:
            r = depth.spread(**params)
            for k, v in r.items():
                print('%s %s' % (k, v))
        except Exception as e:
            print (e)
            pass

    def exit(self, params=None):
        print ('goodbye.')
        sys.exit(1)

    def version(self, params=None):
        print (version)

    def help(self, params=None):
        i = max(len(x) for x in usage.keys())
        for val in sorted(usage.keys()):
            s = '   %-' + str(i + 3) + 's:     %s'
            print (s % (val, usage[val]))

    def echo(self, params=None):
        print (params)

    def plot(self, params=None):
        print ('plotting %s' % params)

    def load(self, params=None):
        name = "package." + params[0]
        print ('trying to import', params[0])
        mod = __import__(name, fromlist=[])
        if mod:
            print ("worked!")

    def showmods(self, params=None):
        lst = os.listdir("modules")
        dir = []
        for d in lst:
            s = os.path.abspath("modules") + os.sep + d
            if os.path.isdir(s) and os.path.exists(s + os.sep + "__init__.py"):
                dir.append(d)
        for d in dir:
            print ('modules.' + d)
            #res[d] = __import__("modules." + d, fromlist=["*"])

    def call(self, params=None):
        try:
            modulefunc = params[0].split('.')
            if len(modulefunc) > 1:
                params[0] = params[0].split('.', 1)[1]
                methodToCall = getattr(markets[modulefunc[0]], params[0])
            else:
                methodToCall = getattr(self, params[0])
        except IndexError as e:
            print(e)
            print ('unknown command')
            return None
        except Exception as e:
            print(e)
            print (params[0], 'unknown command')
            return None

        param_dict = dict(map(lambda t: tuple(t.split('=')), params[1:]))
        try:
            r = methodToCall(**param_dict)
            if r:
                print(r)
        except Exception as e:
            print(e)

    def __init__(self, values):
        self.values = values

def handler(signum, frame):
    print ('Signal handler called with signal', signum)
    print ('Hit Ctrl-D to exit')

def main():
#    start_depth_thread(markets['kraken'])
#    start_depth_thread(markets['btce'])

    cli = Cli(histfile, usage.keys())
    methods = MethodDispather(cli.values)

    signal.signal(signal.SIGINT, handler)

    while 1:
        a = input(prompt)
        try:
            params = a.split(' ')
            while '' in params:
                params.remove('')

            methods.call(params)
        except Exception as e:
            print(e)
            continue

if __name__ == "__main__":
    main()
<commit_msg>updated help<commit_after>#!/usr/bin/env python3

import atexit
import readline
import signal
import sys
import os
#import plot
import kraken
import btce
import exsimu
#import cryptsy
import time
import threading
from depth import depth

version = "0.1 beta"
prompt = "bot > "
histfile = "/tmp/history"
usage = {'exit': 'exit terminal',
         'help': 'show this message',
         'version': 'show version',
         'echo': 'echo messsage',
         'showmods': 'show modules',
         'load': 'load module',
         'plot': 'plot data',
         'kraken.balance': 'get balance from kraken',
         'kraken.depth': 'get depth from kraken',
         'kraken.add_order': 'add an order in kraken',
         'spread': 'get spread e.g: bot> spread api1=btce api2=kraken',
         'btce.add_order': 'add an order in btc-e',
         'btce.balance': 'get balance from btc-e',
         'btce.depth': 'get depth from btc-e',
         'exsimu1.add_order': 'add an order in btc-e (price, vol, order)',
         'exsimu1.fees': 'get exchange fees',
         'exsimu1.balance': 'get balance from simulator',
         'exsimu1.depth': 'get depth from simulator',
         'exsimu1.active_orders': 'get active orders',
         'exsimu1.cancel_orders': 'cancel active order',
         'exsimu1.trade_history': 'get trade history',
         'exsimu1.modify_balance': 'set currency balance in sim (cur, amount)'
         }

def get_depth(callback, callback_args, interval):
    while True:
        callback(**callback_args)
        time.sleep(interval)

def start_depth_thread(api):
    t = threading.Thread(target=get_depth,
                         args=[api.get_depth, {}, 1])
    t.setDaemon(True)
    t.start()

markets = {
    'kraken': kraken.kraken('foobox'),
    'btce': btce.btce(),
    'exsimu1': exsimu.exsimu('data1', 'exsimu1'),
    'exsimu2': exsimu.exsimu('data2', 'exsimu2'),
}

#   'cryptsy': cryptsy_api,

markets['kraken'].decipher_key('kraken.enc')

class Cli:
    matches = []
    completions = {}
    values = []

    def __init__(self, histfile, values=None):
        readline.clear_history()
        try:
            readline.read_history_file(histfile)
        except:
            pass
        atexit.register(readline.write_history_file, histfile)
        readline.set_completer(self.completer)
        readline.parse_and_bind('tab: menu-complete')
        self.append_values(values)

    def append_values(self, appendix):
        self.values += appendix

    def completer(self, text, state):
        try:
            self.matches = self.completions[text]
        except KeyError:
            self.matches = [value for value in self.values
                            if text.upper() in value.upper()]
            self.completions[text] = self.matches
        try:
            return self.matches[state]
        except IndexError:
            return None

class MethodDispather():
    #plot = plot.Plot()

#    def depth(self):
#        print('Kraken')
#        print('------')
#        print(kraken.kraken.current_depth[-1])
#
#        print('btce')
#        print('------')
#        print(btce.btce.current_depth[-1])

    def spread(self, **kwargs):
        kwargs.setdefault('pair', 'btc_eur')
        params = {}
        try:
            params['api1'] = markets[kwargs['api1']]
            params['api2'] = markets[kwargs['api2']]
            params['pair'] = kwargs['pair']
        except Exception as e:
            raise Exception('unknown key', e)
        try:
            r = depth.spread(**params)
            for k, v in r.items():
                print('%s %s' % (k, v))
        except Exception as e:
            print (e)
            pass

    def exit(self, params=None):
        print ('goodbye.')
        sys.exit(1)

    def version(self, params=None):
        print (version)

    def help(self, params=None):
        i = max(len(x) for x in usage.keys())
        for val in sorted(usage.keys()):
            s = '   %-' + str(i + 3) + 's:     %s'
            print (s % (val, usage[val]))

    def echo(self, params=None):
        print (params)

    def plot(self, params=None):
        print ('plotting %s' % params)

    def load(self, params=None):
        name = "package." + params[0]
        print ('trying to import', params[0])
        mod = __import__(name, fromlist=[])
        if mod:
            print ("worked!")

    def showmods(self, params=None):
        lst = os.listdir("modules")
        dir = []
        for d in lst:
            s = os.path.abspath("modules") + os.sep + d
            if os.path.isdir(s) and os.path.exists(s + os.sep + "__init__.py"):
                dir.append(d)
        for d in dir:
            print ('modules.' + d)
            #res[d] = __import__("modules." + d, fromlist=["*"])

    def call(self, params=None):
        try:
            modulefunc = params[0].split('.')
            if len(modulefunc) > 1:
                params[0] = params[0].split('.', 1)[1]
                methodToCall = getattr(markets[modulefunc[0]], params[0])
            else:
                methodToCall = getattr(self, params[0])
        except IndexError as e:
            print(e)
            print ('unknown command')
            return None
        except Exception as e:
            print(e)
            print (params[0], 'unknown command')
            return None

        param_dict = dict(map(lambda t: tuple(t.split('=')), params[1:]))
        try:
            r = methodToCall(**param_dict)
            if r:
                print(r)
        except Exception as e:
            print(e)

    def __init__(self, values):
        self.values = values

def handler(signum, frame):
    print ('Signal handler called with signal', signum)
    print ('Hit Ctrl-D to exit')

def main():
#    start_depth_thread(markets['kraken'])
#    start_depth_thread(markets['btce'])

    cli = Cli(histfile, usage.keys())
    methods = MethodDispather(cli.values)

    signal.signal(signal.SIGINT, handler)

    while 1:
        a = input(prompt)
        try:
            params = a.split(' ')
            while '' in params:
                params.remove('')

            methods.call(params)
        except Exception as e:
            print(e)
            continue

if __name__ == "__main__":
    main()
<|endoftext|>


<commit_before>#!/usr/bin/env python3
#
# Copyright (c) 2018, Nicola Coretti
# All rights reserved.

import sys
import abc
import enum
import numbers
import functools
import argparse

MAJOR_VERSION = 0
MINOR_VERSION = 9
PATCH_VERSION = 3

VERSION_TEMPLATE = '{major}.{minor}.{patch}'
LIBRARY_VERSION = VERSION_TEMPLATE.format(major=MAJOR_VERSION, minor=MINOR_VERSION, patch=PATCH_VERSION)

__author__ = 'Nicola Coretti'
__email__ = 'nico.coretti@gmail.com'
__version__ = LIBRARY_VERSION

class AbstractCrcRegister(metaclass=abc.ABCMeta):
    """
    Abstract base class / Interface a crc register needs to implement.

    Workflow:
        1. The Crc-Register needs to be initialized.    1 time     (init)
        2. Data is feed into the crc register.          1..n times (update)
        3. Final result is calculated.                  1 time     (digest)
    """

    @abc.abstractmethod
    def init(self):
        """
        Initializes the crc register.
        """
        pass

    @abc.abstractmethod
    def update(self, data):
        """
        Feeds the provided data into the crc register.

        :param bytes data: a bytes like object or ann object which can be converted to a bytes
                     like object using the built in bytes() function.
        :return: the current value of the crc register.
        """
        pass

    @abc.abstractmethod
    def digest(self):
        """
        Final crc checksum will be calculated.

        :return: the final crc checksum.
        :rtype: int.
        """
        pass

    @abc.abstractmethod
    def reverse(self):
        """
        Calculates the reversed value of the crc register.

        :return: the the reversed value of the crc register.
        """
        pass

class Configuration(object):
    """
    A Configuration provides all settings necessary to determine the concrete
    implementation of a specific crc algorithm/register.
    """

    def __init__(self, width, polynomial, init_value=0, final_xor_value=0, reverse_input=False, reverse_output=False):
        self._width = width
        self._polynomial = polynomial
        self._init_value = init_value
        self._final_xor_value = final_xor_value
        self._reverse_input = reverse_input
        self._reverse_output = reverse_output

    @property
    def width(self):
        return self._width

    @property
    def polynomial(self):
        return self._polynomial

    @property
    def init_value(self):
        return self._init_value

    @property
    def final_xor_value(self):
        return self._final_xor_value

    @property
    def reverse_input(self):
        return self._reverse_input

    @property
    def reverse_output(self):
        return self._reverse_output

class CrcRegisterBase(AbstractCrcRegister):
    """
    Implements the common crc algorithm, assuming a user of this base
    class will provide an overwrite for the _proces_byte method.
    """

    def __init__(self, configuration):
        """
        Create a new CrcRegisterBase.

        :param configuration: used for the crc algorithm.
        """
        if isinstance(configuration, enum.Enum):
            configuration = configuration.value
        self._topbit = 1 << (configuration.width - 1)
        self._bitmask = 2 ** configuration.width - 1
        self._config = configuration
        self._register = configuration.init_value & self._bitmask

    def __len__(self):
        """
        Returns the length (width) of the register.

        :return: the register size/width in bytes.
        """
        return self._config.width // 8

    def __getitem__(self, index):
        """
        Gets a single byte of the register.

        :param index: byte which shall be returned.
        :return: the byte at the specified index.
        :raises IndexError: if the index is out of bounce.
        """
        if index >= (self._config.width / 8) or index < 0:
            raise IndexError
        shift_offset = index * 8
        return (self.register & (0xFF << shift_offset)) >> shift_offset

    def init(self):
        """
        See AbstractCrcRegister.init
        """
        self.register = self._config.init_value

    def update(self, data):
        """
        See AbstractCrcRegister.update
        """
        for byte in data:
            byte = Byte(byte)
            if self._config.reverse_input:
                byte = byte.reversed()
            self._register = self._process_byte(byte)
        return self.register

    @abc.abstractmethod
    def _process_byte(self, byte):
        """
        Processes an entire byte feed to the crc register.

        :param byte: the byte which shall be processed by the crc register.
        :return: the new value of the crc register will have after the byte have been processed.
        """
        pass

    def digest(self):
        """
        See AbstractCrcRegister.digest
        """
        if self._config.reverse_output:
            self.register = self.reverse()
        return self.register ^ self._config.final_xor_value

    def reverse(self):
        """
        See AbstractCrcRegister.digest
        """
        index = 0
        reversed_value = 0
        for byte in reversed(self):
            reversed_value += int(Byte(byte).reversed()) << index
            index += 8
        return reversed_value

    def _is_division_possible(self):
        return (self.register & self._topbit) > 0

    @property
    def register(self):
        return self._register & self._bitmask

    @register.setter
    def register(self, value):
        self._register = value & self._bitmask

class CrcRegister(CrcRegisterBase):
    """
    Simple crc register, which will process one bit at the time.

    .. note:

        If performance is an important issue for the crc calcualation use table
        based register.
    """

    def __init__(self, configuration):
        super().__init__(configuration)

    def _process_byte(self, byte):
        """
        See CrcRegisterBase._process_byte
        """
        self.register ^= int(byte) << (self._config.width - 8)
        for bit in byte:
            if self._is_division_possible():
                self.register = (self.register << 1) ^ self._config.polynomial
            else:
                self.register <<= 1
        return self.register

class TableBasedCrcRegister(CrcRegisterBase):
    """
    Lookup table based crc register.

    .. note::

        this register type will be much faster than a simple bit by bit based crc register.
        (e.g. CrcRegister)
    """

    def __init__(self, configuration):
        """
        Creates a new table based crc register.

        :param configuration: used for the crc algorithm.

        :attention: creating a table based register initaliy might take some extra time, due to the
                    fact that some lookup tables need to be calculated/initialized .
        """
        super().__init__(configuration)
        if isinstance(configuration, enum.Enum):
            configuration = configuration.value
        self._lookup_table = create_lookup_table(configuration.width, configuration.polynomial)

    def _process_byte(self, byte):
        """
        See CrcRegisterBase._process_byte
        """
        index = int(byte) ^ (self.register >> (self._config.width - 8))
        self.register = self._lookup_table[index] ^ (self.register << 8)
        return self.register

class Byte(numbers.Number):
    BIT_LENGTH = 8
    BIT_MASK = 0xFF

    def __init__(self, value=0x00):
        self._value = value & Byte.BIT_MASK

    def __add__(self, other):
        if not isinstance(other, Byte):
            other = Byte(other)
        return Byte(self.value + other.value)

    def __radd__(self, other):
        return self + other

    def __iadd__(self, other):
        result = self + other
        self.value = result.value
        return self

    def __eq__(self, other):
        if not isinstance(other, Byte):
            raise TypeError('unsupported operand')
        return self.value == other.value

    def __hash__(self):
        return hash(self.value)

    def __len__(self):
        return Byte.BIT_LENGTH

    def __getitem__(self, index):
        if index >= Byte.BIT_LENGTH or index < 0:
            raise IndexError
        return (self.value & (1 << index)) >> index

    def __int__(self):
        return self.value

    @property
    def value(self):
        return self._value & Byte.BIT_MASK

    @value.setter
    def value(self, value):
        self._value = value & Byte.BIT_MASK

    def reversed(self):
        value = 0
        index = 0
        for bit in reversed(self):
            value += bit << index
            index += 1
        return Byte(value)

@functools.lru_cache()
def create_lookup_table(width, polynom):
    """
    Creates a crc lookup table.

    :param int width: of the crc checksum.
    :parma int polynom: which is used for the crc calculation.
    """
    config = Configuration(width=width, polynomial=polynom)
    crc_register = CrcRegister(config)
    lookup_table = list()
    for index in range(0, 256):
        crc_register.init()
        data = bytes((index).to_bytes(1, byteorder='big'))
        crc_register.update(data)
        lookup_table.append(crc_register.digest())
    return lookup_table

class CrcCalculator(object):

    def __init__(self, configuration, table_based=False):
        """
        Creates a new CrcCalculator.

        :param configuration: for the crc algortihm.
        :param table_based: if true a tables based register will be used for the calculations.

        :attention: initalizing a table based calculator might take some extra time, due to the
                    fact that the lookup table need to be initialized.
        """
        if table_based:
            self._crc_register = TableBasedCrcRegister(configuration)
        else:
            self._crc_register = CrcRegister(configuration)

    def calculate_checksum(self, data):
        self._crc_register.init()
        self._crc_register.update(data)
        return self._crc_register.digest()

    def verify_checksum(self, data, expected_checksum):
        return self.calculate_checksum(data) == expected_checksum

@enum.unique
class Crc8(enum.Enum):
    CCITT = Configuration(
        width=8,
        polynomial=0x07,
        init_value=0x00,
        final_xor_value=0x00,
        reverse_input=False,
        reverse_output=False
    )

    SAEJ1850 = Configuration(
        width=8,
        polynomial=0x1D,
        init_value=0x00,
        final_xor_value=0x00,
        reverse_input=False,
        reverse_output=False
    )

    AUTOSAR = Configuration(
        width=8,
        polynomial=0x2F,
        init_value=0xFF,
        final_xor_value=0xFF,
        reverse_input=False,
        reverse_output=False
    )

    BLUETOOTH = Configuration(
        width=8,
        polynomial=0xA7,
        init_value=0x00,
        final_xor_value=0x00,
        reverse_input=True,
        reverse_output=True
    )

@enum.unique
class Crc16(enum.Enum):
    CCITT = Configuration(
        width=16,
        polynomial=0x1021,
        init_value=0x0000,
        final_xor_value=0x0000,
        reverse_input=False,
        reverse_output=False
    )

    GSM = Configuration(
        width=16,
        polynomial=0x1021,
        init_value=0x0000,
        final_xor_value=0xFFFF,
        reverse_input=False,
        reverse_output=False
    )

    PROFIBUS = Configuration(
        width=16,
        polynomial=0x1DCF,
        init_value=0xFFFF,
        final_xor_value=0xFFFF,
        reverse_input=False,
        reverse_output=False
    )

@enum.unique
class Crc32(enum.Enum):
    CRC32 = Configuration(
        width=32,
        polynomial=0x04C11DB7,
        init_value=0xFFFFFFFF,
        final_xor_value=0xFFFFFFFF,
        reverse_input=True,
        reverse_output=True
    )

    AUTOSAR = Configuration(
        width=32,
        polynomial=0xF4ACFB13,
        init_value=0xFFFFFFFF,
        final_xor_value=0xFFFFFFFF,
        reverse_input=True,
        reverse_output=True
    )

    BZIP2 = Configuration(
        width=32,
        polynomial=0x04C11DB7,
        init_value=0xFFFFFFFF,
        final_xor_value=0xFFFFFFFF,
        reverse_input=False,
        reverse_output=False
    )

    POSIX = Configuration(
        width=32,
        polynomial=0x04C11DB7,
        init_value=0x00000000,
        final_xor_value=0xFFFFFFFF,
        reverse_input=False,
        reverse_output=False
    )

@enum.unique
class Crc64(enum.Enum):
    CRC64 = Configuration(
        width=64,
        polynomial=0x42F0E1EBA9EA3693,
        init_value=0x0000000000000000,
        final_xor_value=0x0000000000000000,
        reverse_input=False,
        reverse_output=False
    )

CRC_TYPES = {
    Crc8.__name__: Crc8,
    Crc16.__name__: Crc16,
    Crc32.__name__: Crc32,
    Crc64.__name__: Crc64
}

def argument_parser():
    into_int = functools.partial(int, base=0)
    prog = 'crc'
    description = 'A set of crc checksum related command line tools.'
    parser = argparse.ArgumentParser(prog=prog, description=description,
                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter)

    subparsers = parser.add_subparsers()

    t = subparsers.add_parser('table', help='Generates lookup tables for various crc algorithm settings')
    t.add_argument('width', metavar='<width>', type=into_int,
                   help='width of the crc algorithm, common width\'s are 8, 16, 32, 64')
    t.add_argument('polynom', metavar='<polynom>', type=into_int,
                   help='hex value of the polynom used for calculating the crc table')
    t.set_defaults(func=table)

    c = subparsers.add_parser('checksum', help='Calculate checksum(s) for the specified input(s)')
    c.add_argument('inputs', nargs='*', type=argparse.FileType('r'), default=[sys.stdin],
                   help='who will be feed into the crc calculation')
    c.add_argument('-c', '--category', choices=[t for t in CRC_TYPES], default=Crc8.__name__,
                   help='of crc algorithms which shall be used for calculation')
    c.set_defaults(func=checksum)

    return parser

def table(args):
    width = args.width
    polynom = args.polynom
    lookup_table = create_lookup_table(width, polynom)
    fmt_spec = '{{:0<0{}X}}'.format(width // 4)
    template = "0x{} ".format(fmt_spec)
    for id, entry in enumerate(lookup_table):
        if (id != 0) and (id % 8 == 0): print()
        print(template.format(entry), end='')
    print()
    return True

def checksum(args):
    data = bytearray()
    for input in args.inputs:
        data.extend(bytes(input.read(), 'utf-8'))
    category = CRC_TYPES[args.category]
    for algorithm in sorted(category, key=str):
        name = f'{algorithm}'.split('.')[1]
        calculator = CrcCalculator(algorithm, True)
        print(f'{name}: 0x{calculator.calculate_checksum(data):X}')
    return True

def main(argv=None):
    parser = argument_parser()
    args = parser.parse_args(argv)
    if 'func' in args:
        exit_code = 0 if args.func(args) else -1
        sys.exit(exit_code)
    else:
        parser.print_help()
        sys.exit(-1)

if __name__ == '__main__':
    main()
<commit_msg>Fix typos<commit_after>#!/usr/bin/env python3
#
# Copyright (c) 2018, Nicola Coretti
# All rights reserved.

import sys
import abc
import enum
import numbers
import functools
import argparse

MAJOR_VERSION = 0
MINOR_VERSION = 9
PATCH_VERSION = 3

VERSION_TEMPLATE = '{major}.{minor}.{patch}'
LIBRARY_VERSION = VERSION_TEMPLATE.format(major=MAJOR_VERSION, minor=MINOR_VERSION, patch=PATCH_VERSION)

__author__ = 'Nicola Coretti'
__email__ = 'nico.coretti@gmail.com'
__version__ = LIBRARY_VERSION

class AbstractCrcRegister(metaclass=abc.ABCMeta):
    """
    Abstract base class / Interface a crc register needs to implement.

    Workflow:
        1. The Crc-Register needs to be initialized.    1 time     (init)
        2. Data is feed into the crc register.          1..n times (update)
        3. Final result is calculated.                  1 time     (digest)
    """

    @abc.abstractmethod
    def init(self):
        """
        Initializes the crc register.
        """
        pass

    @abc.abstractmethod
    def update(self, data):
        """
        Feeds the provided data into the crc register.

        :param bytes data: a bytes like object or ann object which can be converted to a bytes
                     like object using the built in bytes() function.
        :return: the current value of the crc register.
        """
        pass

    @abc.abstractmethod
    def digest(self):
        """
        Final crc checksum will be calculated.

        :return: the final crc checksum.
        :rtype: int.
        """
        pass

    @abc.abstractmethod
    def reverse(self):
        """
        Calculates the reversed value of the crc register.

        :return: the the reversed value of the crc register.
        """
        pass

class Configuration(object):
    """
    A Configuration provides all settings necessary to determine the concrete
    implementation of a specific crc algorithm/register.
    """

    def __init__(self, width, polynomial, init_value=0, final_xor_value=0, reverse_input=False, reverse_output=False):
        self._width = width
        self._polynomial = polynomial
        self._init_value = init_value
        self._final_xor_value = final_xor_value
        self._reverse_input = reverse_input
        self._reverse_output = reverse_output

    @property
    def width(self):
        return self._width

    @property
    def polynomial(self):
        return self._polynomial

    @property
    def init_value(self):
        return self._init_value

    @property
    def final_xor_value(self):
        return self._final_xor_value

    @property
    def reverse_input(self):
        return self._reverse_input

    @property
    def reverse_output(self):
        return self._reverse_output

class CrcRegisterBase(AbstractCrcRegister):
    """
    Implements the common crc algorithm, assuming a user of this base
    class will provide an overwrite for the _process_byte method.
    """

    def __init__(self, configuration):
        """
        Create a new CrcRegisterBase.

        :param configuration: used for the crc algorithm.
        """
        if isinstance(configuration, enum.Enum):
            configuration = configuration.value
        self._topbit = 1 << (configuration.width - 1)
        self._bitmask = 2 ** configuration.width - 1
        self._config = configuration
        self._register = configuration.init_value & self._bitmask

    def __len__(self):
        """
        Returns the length (width) of the register.

        :return: the register size/width in bytes.
        """
        return self._config.width // 8

    def __getitem__(self, index):
        """
        Gets a single byte of the register.

        :param index: byte which shall be returned.
        :return: the byte at the specified index.
        :raises IndexError: if the index is out of bounce.
        """
        if index >= (self._config.width / 8) or index < 0:
            raise IndexError
        shift_offset = index * 8
        return (self.register & (0xFF << shift_offset)) >> shift_offset

    def init(self):
        """
        See AbstractCrcRegister.init
        """
        self.register = self._config.init_value

    def update(self, data):
        """
        See AbstractCrcRegister.update
        """
        for byte in data:
            byte = Byte(byte)
            if self._config.reverse_input:
                byte = byte.reversed()
            self._register = self._process_byte(byte)
        return self.register

    @abc.abstractmethod
    def _process_byte(self, byte):
        """
        Processes an entire byte feed to the crc register.

        :param byte: the byte which shall be processed by the crc register.
        :return: the new value of the crc register will have after the byte have been processed.
        """
        pass

    def digest(self):
        """
        See AbstractCrcRegister.digest
        """
        if self._config.reverse_output:
            self.register = self.reverse()
        return self.register ^ self._config.final_xor_value

    def reverse(self):
        """
        See AbstractCrcRegister.digest
        """
        index = 0
        reversed_value = 0
        for byte in reversed(self):
            reversed_value += int(Byte(byte).reversed()) << index
            index += 8
        return reversed_value

    def _is_division_possible(self):
        return (self.register & self._topbit) > 0

    @property
    def register(self):
        return self._register & self._bitmask

    @register.setter
    def register(self, value):
        self._register = value & self._bitmask

class CrcRegister(CrcRegisterBase):
    """
    Simple crc register, which will process one bit at the time.

    .. note:

        If performance is an important issue for the crc calculation use a table
        based register.
    """

    def __init__(self, configuration):
        super().__init__(configuration)

    def _process_byte(self, byte):
        """
        See CrcRegisterBase._process_byte
        """
        self.register ^= int(byte) << (self._config.width - 8)
        for bit in byte:
            if self._is_division_possible():
                self.register = (self.register << 1) ^ self._config.polynomial
            else:
                self.register <<= 1
        return self.register

class TableBasedCrcRegister(CrcRegisterBase):
    """
    Lookup table based crc register.

    .. note::

        this register type will be much faster than a simple bit by bit based crc register.
        (e.g. CrcRegister)
    """

    def __init__(self, configuration):
        """
        Creates a new table based crc register.

        :param configuration: used for the crc algorithm.

        :attention: creating a table based register initially might take some extra time, due to the
                    fact that some lookup tables need to be calculated/initialized .
        """
        super().__init__(configuration)
        if isinstance(configuration, enum.Enum):
            configuration = configuration.value
        self._lookup_table = create_lookup_table(configuration.width, configuration.polynomial)

    def _process_byte(self, byte):
        """
        See CrcRegisterBase._process_byte
        """
        index = int(byte) ^ (self.register >> (self._config.width - 8))
        self.register = self._lookup_table[index] ^ (self.register << 8)
        return self.register

class Byte(numbers.Number):
    BIT_LENGTH = 8
    BIT_MASK = 0xFF

    def __init__(self, value=0x00):
        self._value = value & Byte.BIT_MASK

    def __add__(self, other):
        if not isinstance(other, Byte):
            other = Byte(other)
        return Byte(self.value + other.value)

    def __radd__(self, other):
        return self + other

    def __iadd__(self, other):
        result = self + other
        self.value = result.value
        return self

    def __eq__(self, other):
        if not isinstance(other, Byte):
            raise TypeError('unsupported operand')
        return self.value == other.value

    def __hash__(self):
        return hash(self.value)

    def __len__(self):
        return Byte.BIT_LENGTH

    def __getitem__(self, index):
        if index >= Byte.BIT_LENGTH or index < 0:
            raise IndexError
        return (self.value & (1 << index)) >> index

    def __int__(self):
        return self.value

    @property
    def value(self):
        return self._value & Byte.BIT_MASK

    @value.setter
    def value(self, value):
        self._value = value & Byte.BIT_MASK

    def reversed(self):
        value = 0
        index = 0
        for bit in reversed(self):
            value += bit << index
            index += 1
        return Byte(value)

@functools.lru_cache()
def create_lookup_table(width, polynom):
    """
    Creates a crc lookup table.

    :param int width: of the crc checksum.
    :parma int polynom: which is used for the crc calculation.
    """
    config = Configuration(width=width, polynomial=polynom)
    crc_register = CrcRegister(config)
    lookup_table = list()
    for index in range(0, 256):
        crc_register.init()
        data = bytes((index).to_bytes(1, byteorder='big'))
        crc_register.update(data)
        lookup_table.append(crc_register.digest())
    return lookup_table

class CrcCalculator(object):

    def __init__(self, configuration, table_based=False):
        """
        Creates a new CrcCalculator.

        :param configuration: for the crc algorithm.
        :param table_based: if true a tables based register will be used for the calculations.

        :attention: initializing a table based calculator might take some extra time, due to the
                    fact that the lookup table need to be initialized.
        """
        if table_based:
            self._crc_register = TableBasedCrcRegister(configuration)
        else:
            self._crc_register = CrcRegister(configuration)

    def calculate_checksum(self, data):
        self._crc_register.init()
        self._crc_register.update(data)
        return self._crc_register.digest()

    def verify_checksum(self, data, expected_checksum):
        return self.calculate_checksum(data) == expected_checksum

@enum.unique
class Crc8(enum.Enum):
    CCITT = Configuration(
        width=8,
        polynomial=0x07,
        init_value=0x00,
        final_xor_value=0x00,
        reverse_input=False,
        reverse_output=False
    )

    SAEJ1850 = Configuration(
        width=8,
        polynomial=0x1D,
        init_value=0x00,
        final_xor_value=0x00,
        reverse_input=False,
        reverse_output=False
    )

    AUTOSAR = Configuration(
        width=8,
        polynomial=0x2F,
        init_value=0xFF,
        final_xor_value=0xFF,
        reverse_input=False,
        reverse_output=False
    )

    BLUETOOTH = Configuration(
        width=8,
        polynomial=0xA7,
        init_value=0x00,
        final_xor_value=0x00,
        reverse_input=True,
        reverse_output=True
    )

@enum.unique
class Crc16(enum.Enum):
    CCITT = Configuration(
        width=16,
        polynomial=0x1021,
        init_value=0x0000,
        final_xor_value=0x0000,
        reverse_input=False,
        reverse_output=False
    )

    GSM = Configuration(
        width=16,
        polynomial=0x1021,
        init_value=0x0000,
        final_xor_value=0xFFFF,
        reverse_input=False,
        reverse_output=False
    )

    PROFIBUS = Configuration(
        width=16,
        polynomial=0x1DCF,
        init_value=0xFFFF,
        final_xor_value=0xFFFF,
        reverse_input=False,
        reverse_output=False
    )

@enum.unique
class Crc32(enum.Enum):
    CRC32 = Configuration(
        width=32,
        polynomial=0x04C11DB7,
        init_value=0xFFFFFFFF,
        final_xor_value=0xFFFFFFFF,
        reverse_input=True,
        reverse_output=True
    )

    AUTOSAR = Configuration(
        width=32,
        polynomial=0xF4ACFB13,
        init_value=0xFFFFFFFF,
        final_xor_value=0xFFFFFFFF,
        reverse_input=True,
        reverse_output=True
    )

    BZIP2 = Configuration(
        width=32,
        polynomial=0x04C11DB7,
        init_value=0xFFFFFFFF,
        final_xor_value=0xFFFFFFFF,
        reverse_input=False,
        reverse_output=False
    )

    POSIX = Configuration(
        width=32,
        polynomial=0x04C11DB7,
        init_value=0x00000000,
        final_xor_value=0xFFFFFFFF,
        reverse_input=False,
        reverse_output=False
    )

@enum.unique
class Crc64(enum.Enum):
    CRC64 = Configuration(
        width=64,
        polynomial=0x42F0E1EBA9EA3693,
        init_value=0x0000000000000000,
        final_xor_value=0x0000000000000000,
        reverse_input=False,
        reverse_output=False
    )

CRC_TYPES = {
    Crc8.__name__: Crc8,
    Crc16.__name__: Crc16,
    Crc32.__name__: Crc32,
    Crc64.__name__: Crc64
}

def argument_parser():
    into_int = functools.partial(int, base=0)
    program = 'crc'
    description = 'A set of crc checksum related command line tools.'
    parser = argparse.ArgumentParser(prog=program, description=description,
                                     formatter_class=argparse.ArgumentDefaultsHelpFormatter)

    subparsers = parser.add_subparsers()

    t = subparsers.add_parser('table', help='Generates lookup tables for various crc algorithm settings')
    t.add_argument('width', metavar='<width>', type=into_int,
                   help='width of the crc algorithm, common width\'s are 8, 16, 32, 64')
    t.add_argument('polynom', metavar='<polynom>', type=into_int,
                   help='hex value of the polynom used for calculating the crc table')
    t.set_defaults(func=table)

    c = subparsers.add_parser('checksum', help='Calculate checksum(s) for the specified input(s)')
    c.add_argument('inputs', nargs='*', type=argparse.FileType('r'), default=[sys.stdin],
                   help='who will be feed into the crc calculation')
    c.add_argument('-c', '--category', choices=[t for t in CRC_TYPES], default=Crc8.__name__,
                   help='of crc algorithms which shall be used for calculation')
    c.set_defaults(func=checksum)

    return parser

def table(args):
    width = args.width
    polynom = args.polynom
    lookup_table = create_lookup_table(width, polynom)
    fmt_spec = '{{:0<0{}X}}'.format(width // 4)
    template = "0x{} ".format(fmt_spec)
    for id, entry in enumerate(lookup_table):
        if (id != 0) and (id % 8 == 0): print()
        print(template.format(entry), end='')
    print()
    return True

def checksum(args):
    data = bytearray()
    for input in args.inputs:
        data.extend(bytes(input.read(), 'utf-8'))
    category = CRC_TYPES[args.category]
    for algorithm in sorted(category, key=str):
        name = f'{algorithm}'.split('.')[1]
        calculator = CrcCalculator(algorithm, True)
        print(f'{name}: 0x{calculator.calculate_checksum(data):X}')
    return True

def main(argv=None):
    parser = argument_parser()
    args = parser.parse_args(argv)
    if 'func' in args:
        exit_code = 0 if args.func(args) else -1
        sys.exit(exit_code)
    else:
        parser.print_help()
        sys.exit(-1)

if __name__ == '__main__':
    main()
<|endoftext|>