I have already written a simple python cmd program using the cmd module. However, I would like it to be modular. For Example, there would be a folder named script and any .py that contains a command would add the command to the application. How would I go about that?
NOTE:
I have figured out how to find and load modules that are within a folder using importlib.
First, you must understand how the cmd module works. I would not go into it here but the gist is command is entered, then split up into the actual command itself and its arguments. The command is then searched in the implementation of the cmd module using getattr(). The resulting function returned is then executed. Raises an error if the function(attribute) is not found.
Add the list of modules added to a list.
lst.append(importlib.importlib.import_module(<module path here>))
When cmd is finding a command, modify the code to get it to run through the list of imported modules and see if the function/command exists in that module. If so, execute it.
rough code to find func
def findfunc(funcname):
for module in lst:
if hasattr(<module class where func is stored>, "<funcname>"):
return getattr(<module class where func is stored>,"<funcname>")
However, I will leave my implementation here. It also includes the capability to reload modules(limited to a certain extent) and private commands that do not show up in help or autocomplete(I am using py prompt toolkit). A fair bit of warning, no docs or help in understanding this code as it is only meant for my personal use only. Meaning don't directly copy it and also It was a modification to the python cmd module.
import importlib
import os
import string
import sys
import time
from prompt_toolkit import PromptSession
from prompt_toolkit.completion import WordCompleter
from prompt_toolkit.styles import Style
from internal.logger import logger # my logger Implementation, ignore it
__all__ = ["cli2"]
PROMPT = '>>> '
IDENTCHARS = string.ascii_letters + string.digits + '_'
style = Style.from_dict(
{
"completion-menu.completion": "bg:#008888 #ffffff",
"completion-menu.completion.current": "bg:#00aaaa #000000",
"scrollbar.background": "bg:#88aaaa",
"scrollbar.button": "bg:#222222",
}
)
def parse(arg):
return arg.split()
class Interpreter:
identchars = IDENTCHARS
ruler = '='
lastcmd = ''
doc_leader = ""
doc_header = "Available Commands (type help <topic> to get documentation for <topic> ):"
misc_header = "Miscellaneous help topics:"
nohelp = "*** No help on %s"
use_rawinput = 1
modulelst = []
internalcmdlst = ["help", "exit", "reload", "listmodules", "listvar"]
def __init__(self, intro=None, prompt=PROMPT, stdin=None, stdout=None):
"""Instantiate a line-oriented interpreter framework.
The optional arguments stdin and stdout
specify alternate input and output file objects; if not specified,
sys.stdin and sys.stdout are used.
"""
self.prompt = prompt
self.intro = intro
if stdin is not None:
self.stdin = stdin
else:
self.stdin = sys.stdin
if stdout is not None:
self.stdout = stdout
else:
self.stdout = sys.stdout
self.cmdqueue = []
def cmdloop(self):
self.preloop()
try:
if self.intro:
self.stdout.write(str(self.intro) + "\n")
stop = None
while not stop:
if self.cmdqueue:
line = self.cmdqueue.pop(0)
else:
line = self.session.prompt(self.prompt)
line = self.precmd(line)
stop = self.onecmd(line)
stop = self.postcmd(stop, line)
self.postloop()
finally:
pass
def precmd(self, line):
"""Hook method executed just before the command line is
interpreted, but after the input prompt is generated and issued.
"""
# Parse command to argument
return self.parseline(line)
def postcmd(self, stop, line):
"""Hook method executed just after a command dispatch is finished."""
return stop
def preloop(self):
"""Hook method executed once when the cmdloop() method is called."""
self.autocommands = [item[3:] for item in self.get_names() if (item[-5:] != '__pvt') and (item[:3] == 'do_')]
self.session = PromptSession(completer=WordCompleter(self.autocommands, ignore_case=False))
def postloop(self):
"""Hook method executed once when the cmdloop() method is about to
return.
"""
self.autocommands, self.session = None, None
def parseline(self, line):
"""Parse the line into a command name and a string containing
the arguments. Returns a tuple containing (command, args, line).
'command' and 'args' may be None if the line couldn't be parsed.
"""
line = line.strip()
if not line:
return None, None, line
elif line[0] == '?':
line = 'help ' + line[1:]
elif line[0] == '!':
if hasattr(self, 'do_shell'):
line = 'shell ' + line[1:]
else:
return None, None, line
i, n = 0, len(line)
while i < n and line[i] in self.identchars: i = i + 1
cmd, arg = line[:i], line[i:].strip()
return cmd, arg, line
def onecmd(self, linecommand):
"""Interpret the argument as though it had been typed in response
to the prompt.
This may be overridden, but should not normally need to be;
see the precmd() and postcmd() methods for useful execution hooks.
The return value is a flag indicating whether interpretation of
commands by the interpreter should stop.
"""
cmd, arg, line = linecommand
if not line:
return self.emptyline()
if cmd is None:
return self.default(line)
self.lastcmd = line
if line == 'EOF':
self.lastcmd = ''
if cmd == '':
return self.default(line)
if cmd in self.internalcmdlst:
return self.getfunc(cmd)(arg)
else:
try:
terminalclass, func = self.getfunc(cmd)
except AttributeError:
return self.default(line)
return func(terminalclass, arg)
def emptyline(self):
"""Called when an empty line is entered in response to the prompt.
If this method is not overridden, it repeats the last nonempty
command entered.
"""
if self.lastcmd:
return self.onecmd(self.lastcmd)
def default(self, line):
"""Called on an input line when the command prefix is not recognized.
If this method is not overridden, it prints an error message and
returns.
"""
self.stdout.write('*** Unknown syntax: %s\n' % line.split(" ")[0])
def get_names(self):
# This method used to pull in base class attributes
# at a time dir() didn't do it yet.
lst = [*dir(self.__class__)]
for modulejs in self.modulelst:
terminalclass = getattr(getattr(modulejs["module"], 'cli_helper'), "Terminal")
lst += dir(terminalclass)
return lst
def print_topics(self, header, cmds, maxcol):
if cmds:
self.stdout.write("%s\n" % str(header))
if self.ruler:
self.stdout.write("%s\n" % str(self.ruler * len(header)))
self.columnize(cmds, maxcol - 1)
self.stdout.write("\n")
def columnize(self, lst, displaywidth=80):
"""Display a list of strings as a compact set of columns.
Each column is only as wide as necessary.
Columns are separated by two spaces (one was not legible enough).
"""
if not lst:
self.stdout.write("<empty>\n")
return
nonstrings = [i for i in range(len(lst))
if not isinstance(lst[i], str)]
if nonstrings:
raise TypeError("list[i] not a string for i in %s"
% ", ".join(map(str, nonstrings)))
size = len(lst)
if size == 1:
self.stdout.write('%s\n' % str(lst[0]))
return
# Try every row count from 1 upwards
for nrows in range(1, len(lst)):
ncols = (size + nrows - 1) // nrows
colwidths = []
totwidth = -2
for col in range(ncols):
colwidth = 0
for row in range(nrows):
i = row + nrows * col
if i >= size:
break
x = lst[i]
colwidth = max(colwidth, len(x))
colwidths.append(colwidth)
totwidth += colwidth + 2
if totwidth > displaywidth:
break
if totwidth <= displaywidth:
break
else:
nrows = len(lst)
ncols = 1
colwidths = [0]
for row in range(nrows):
texts = []
for col in range(ncols):
i = row + nrows * col
if i >= size:
x = ""
else:
x = lst[i]
texts.append(x)
while texts and not texts[-1]:
del texts[-1]
for col in range(len(texts)):
texts[col] = texts[col].ljust(colwidths[col])
self.stdout.write("%s\n" % str(" ".join(texts)))
def addmodulesfromdirectory(self, directory):
try:
directory_contents = os.listdir(directory)
for item in directory_contents:
self.addmodule(os.path.join(directory, item), "".join([directory[2:], ".", item]))
except OSError as e:
print("Something Happened\nPlease try again :)")
logger.error("OSErrot, {}".format(e))
except ImportError as e:
print("Something Went Wrong while importing modules\nPlease try again :)")
logger.error("ImportError, {}".format(e))
except Exception as e:
print("Oops, my fault\nPlease try again :)")
logger.error("Exception, {}".format(e))
finally:
logger.info("Done importing cli modules")
def addmodule(self, pathdir, importdir):
if os.path.isdir(pathdir):
importedmodule = importlib.import_module(importdir)
modulejs = {
"module": importedmodule,
"name": importedmodule.__name__,
"importdir": importdir,
"pathdir": pathdir
}
self.modulelst.append(modulejs)
def removemodule(self, modulename):
if modulename in [x["name"] for x in self.modulelst]:
self.modulelst.remove(self.getmodulejs(modulename))
del sys.modules[modulename]
else:
raise ModuleNotFoundError
def replacemodule(self, modulename):
module = self.getmodulejs(modulename)
self.removemodule(modulename)
self.addmodule(module["pathdir"], module["importdir"])
def getmodulejs(self, modulename):
for i in self.modulelst:
if i['name'] == modulename:
return i
#staticmethod
def getmoduledep(modulename):
unsortedscriptsysmodules = [module for module in sys.modules if (("script" in module) and ("script" != module))]
sortedlst = []
for scriptmodules in unsortedscriptsysmodules:
mod = sys.modules[scriptmodules]
if not (hasattr(mod, "__path__") and getattr(mod, '__file__', None) is None) and (
scriptmodules != "script.{}".format(modulename)):
sortedlst.append(sys.modules[scriptmodules])
return sortedlst
def reloadmoduledep(self, modulename):
for dep in self.getmoduledep(modulename):
try:
importlib.reload(dep)
except ModuleNotFoundError as e:
print(e)
def getfunc(self, command):
if "do_" in command:
command = command[3:]
if command in self.internalcmdlst:
return getattr(self, "_".join(['do', command]))
else:
for modulejs in self.modulelst:
terminalclass = getattr(getattr(modulejs["module"], 'cli_helper'), "Terminal")
if hasattr(terminalclass, "_".join(['do', command])):
return terminalclass, getattr(terminalclass, "_".join(['do', command]))
raise AttributeError
def do_help(self, arg):
"""
List available commands with "help" or detailed help with "help cmd".
"""
if arg:
# XXX check arg syntax
try:
func = self.getfunc('help_' + arg)
except AttributeError:
try:
if arg[-5:] == '__pvt':
raise AttributeError
doc = self.getfunc('do_' + arg).__doc__
if doc:
self.stdout.write("%s\n" % str(doc))
return
except AttributeError:
pass
self.stdout.write("%s\n" % str(self.nohelp % (arg,)))
return
func()
else:
names = self.get_names()
cmds_doc = []
funchelp = {}
for name in names:
if name[:5] == 'help_':
funchelp[name[5:]] = 1
names.sort()
# There can be duplicates if routines overridden
prevname = ''
for name in names:
if name[:3] == 'do_' and name[-5:] != '__pvt':
if name == prevname:
continue
prevname = name
cmd = name[3:]
if cmd in funchelp:
cmds_doc.append(cmd)
del funchelp[cmd]
elif self.getfunc(name).__doc__:
cmds_doc.append(cmd)
self.stdout.write("%s\n" % str(self.doc_leader))
self.print_topics(self.doc_header, cmds_doc, 80)
self.print_topics(self.misc_header, list(funchelp.keys()), 80)
def do_exit(self, arg):
"""
Exit
"""
if arg:
print(self.prompt + "No arguments please")
print("Exiting")
time.sleep(1)
return True
def do_reload(self, arg):
if arg == "":
print("No Arguments found")
return
arg = parse(arg)
if arg[0] == "all":
localmodulelst = self.modulelst.copy()
self.modulelst = []
for i in localmodulelst:
print("Reloading", ".".join(i["name"].split(".")[1:]))
importlib.invalidate_caches()
self.reloadmoduledep(i["name"])
self.addmodule(i["pathdir"], i["importdir"])
self.autocommands = [item[3:] for item in self.get_names() if
(item[-5:] != '__pvt') and (item[:3] == 'do_')]
self.session = PromptSession(completer=WordCompleter(self.autocommands, ignore_case=False))
else:
print("Only argument \'all\' is accepted")
def do_listmodules(self, arg):
arg = parse(arg)
if arg[0] == "sys":
for i in sys.modules:
print(i)
elif len(arg) != 0:
print("No Argument Please")
else:
print("Listing all imported Modules")
for i in self.modulelst:
print(".".join(i["module"].__name__.split(".")[1:]))
Related
So I'm trying to make a CLI but I'm having some errors with the 'cd' command. I can't access the 'target_path' variable from my cd command. Here's the code for the CLI:
import os, colorama
ERROR_COLOR = colorama.Back.RED + colorama.Fore.BLACK
user = os.getlogin()
target_path = f'C:\\Users\\{user}'
class Commands:
def __init__(self):
raise RuntimeError("Cannot instantiate class 'Commands'")
#staticmethod
def help(cmd=None):
cmd_help_msgs = {
'help': \
"""
HELP:
USAGE:
help [command: str]
DESCRIPTION:
Prints out the description and usage of a command.
If command is not passed, it prints out the help messages of all commands
""",
'cls': \
"""
CLS:
USAGE:
cls
DESCRIPTION:
Clears the terminal
""",
'exit': \
"""
EXIT:
USAGE:
exit
DESCRITPION:
Quits the program
""",
'echo': \
"""
ECHO:
USAGE:
echo <msg: str>
DESCRIPTION:
Prints out msg
"""
}
help_msg = f"""
IMPORTANT: [] = optional parameter, <> = mandatory parameter
COMMANDS:
"""
if cmd is None:
print(help_msg, end='')
for val in cmd_help_msgs.values():
print(val, end='')
else:
try:
print(help_msg, end='')
print(cmd_help_msgs[cmd])
except:
print(f"{ERROR_COLOR}'{cmd}' is not recognized as a valid command! Do 'help' to see all valid commands{colorama.Style.RESET_ALL}")
return
print('\n')
#staticmethod
def cls():
os.system('cls')
#staticmethod
def exit():
exit(0)
#staticmethod
def echo(msg=''):
if msg == '':
print(ERROR_COLOR + "Missing required argument 'msg'" + colorama.Style.RESET_ALL)
return
print(msg)
#staticmethod
def cd(directory=''):
if directory == '':
print(target_path)
return
if directory[1] != ':':
if os.path.exists(target_path + directory) and os.path.isdir(target_path + directory):
target_path += directory
else:
print(f"{directory} is not a valid directory")
else:
if os.path.exists(directory) and os.path.isdir(directory):
target_path = directory
else:
print(f"{directory} is not a valid directory")
command_list = [func for func in dir(Commands) if callable(getattr(Commands, func)) and not func.startswith('__')]
os.system('cls')
while True:
command_has_kws = False
command = input(f'[{target_path}]{colorama.Fore.CYAN} # {colorama.Fore.LIGHTBLUE_EX}').strip().split(' ')
print(colorama.Style.RESET_ALL, end='')
for i in range(len(command)):
command[i] = command[i].strip()
if command[i].startswith('-'):
command_has_kws = True
if command[0] in command_list:
try:
if not command_has_kws:
getattr(Commands, command[0])(*command[1:])
else:
# REMINDER: Type 'getattr(Commands, command[0]).__code__.co_varnames[:getattr(Commands, command[0]).__code__.co_argcount]' to get the parameters of a command
pass # TODO: Implement keywords
except TypeError:
print(f"{ERROR_COLOR}Too many or too little arguments were passed to '{command[0]}'{colorama.Style.RESET_ALL}")
else:
print(f"{ERROR_COLOR}'{command[0]}' is not recognized as a valid command! Do 'help' to see all valid commands{colorama.Style.RESET_ALL}\n")
Here's 2 things you should focus on:
target_path = f'C:\\Users\\{user}'
And:
#staticmethod
def cd(directory=''):
if directory == '':
print(target_path)
return
if directory[1] != ':':
if os.path.exists(target_path + directory) and os.path.isdir(target_path + directory):
target_path += directory
else:
print(f"{directory} is not a valid directory")
else:
if os.path.exists(directory) and os.path.isdir(directory):
target_path = directory
else:
print(f"{directory} is not a valid directory")
Also here's the error message:
Traceback (most recent call last):
File "C:\Users\[REDACTED]\Desktop\Crabby_CLI\main.py", line 132, in <module>
getattr(Commands, command[0])(*command[1:])
File "C:\Users\[REDACTED]\Desktop\Crabby_CLI\main.py", line 102, in cd
if os.path.exists(target_path + directory) and os.path.isdir(target_path + directory):
UnboundLocalError: local variable 'target_path' referenced before assignment
INFORMATION:
Interpreter - Python 3.8
OS - Windows 10
Editor - PyCharm Community Edition
local variable 'target_path' referenced before assignment this error suggests that you are trying to access target_path in one of your functions before assigning any value and if my understanding is correct, you want to use the global target_path, that you have set in top, within those functions.
If this is correct, then you need to tell your functions to use global variables.
Solution :
Within each function that uses the 'target_path' variable, in the beginning, put this line
def abc():
# add this line
global target_path
...
...
# now use `target_path` here
Create graph:-
def loadGraphFile(file):
graph = []
for line in file:
contents = line.split()
movieName = contents[0]
actorNames = [contents[i]+ " " + contents[i+1] for i in range(1, len(contents), 2)]
movieNode = findNode(graph, movieName)
if movieNode == None:
movieNode = mkNode(movieName)
graph.append(movieNode)
for actorName in actorNames:
actorNode = findNode(graph,actorName)
if actorNode == None:
actorNode = mkNode(actorName)
graph.append(actorNode)
actorNode.neighbor.append(movieNode)
movieNode.neighbor.append(actorNode)
return graph
def loadGraphFileName('file.text'):
return loadGraphFile(Open('file.text'))
You declared your function wrong:
def loadGraphFileName('file.text'): # change this
return loadGraphFile(Open('file.text'))
To this:
def loadGraphFileName(): # You don't use it anyway
return loadGraphFile(Open('file.text'))
Or:
def loadGraphFileName(filename='file.text'): # file.text will be the default. if you give an parameter with it, filename will change to that parameter
return loadGraphFile(Open(filename)) # And use it here
You cannot have literals as function params
You can instead do
def loadGraphFileName(f = 'file.txt'):
return loadGraphFile(Open(f))
Imagine a class Interpreter that takes a Parser and evaluates the data yielded by the Parser into a Document. The Interpreter must be instantiated and is supposed to be executed immediately, with no reuse. The reason to not make it just a function is to be able to use sub-procedures during the interpretation phase (methods).
Below is a simple example of such an Interpreter class. I personally consider it bad to call self.run() from the constructor, but at the same time, I can not think of a better design. Can you advise?
class Interpreter():
def __init__(self, workspace, document, parser):
super().__init__()
self.workspace = workspace
self.document = document
self.parser = parser
self.run()
def run(self):
while True:
data = self.parser.next()
if not data: break
self.interpret(data)
def interpret(self, data):
if data['type'] == 'statement':
handler_name = 'statement_{0}'.format(data['name'])
method = getattr(self, handler_name, None)
if not method or not callable(method):
raise SyntaxError('unknown statement {0}'.format(data['name']))
method(data)
elif data['type'] == 'assignment':
self.document.define(data['left'], data['right'])
else:
raise RuntimeError('unexpected data from parser')
def statement_print(self, data):
print(*data['args'])
def statement_load(self, data):
if len(data['args']) not in (1, 3):
raise SyntaxError('load statement requires 1 or 3 arguments')
name = data['args'][0]
dest = name
if len(data) == 3:
if data['args'][1] != 'as':
raise SyntaxError('load statement second argument must be "as"')
dest = data['args'][2]
doc = self.workspace.load(name)
self.document.alias(doc, dest)
def statement_if(self, data):
# todo: check if if-statement applies
# ...
next = self.parser.next()
if next['indent'] <= data['indent']:
raise SyntaxError('need at least one line after if-statement')
indent = next['indent']
while next['indent'] == indent:
self.interpret(next)
next = self.parser.next()
if next['indent'] > indent:
raise SyntaxError('unexpected indent')
self.parser.push_back(next)
IndentationError: unexpected unindent WHY???
#!/usr/bin/python
import sys
class Seq:
def __init__(self, id, adnseq, colen):
self.id = id
self.dna = adnseq
self.cdnlen = colen
self.prot = ""
def __str__(self):
return ">%s\n%s\n" % (self.id, self.prot)
def translate(self, transtable):
self.prot = ""
for i in range(0,len(self.dna),self.cdnlen):
codon = self.dna[i:i+self.cdnlen]
aa = transtable[codon]
self.prot += aa
def parseCommandOptions(cmdargs):
tfname = cmdargs[1]
sfname = cmdargs[2]
return (tfname, sfname)
def readTTable(fname):
try:
ttable = {}
cdnlen = -1
tfile = open(fname, "r")
for line in tfile:
linearr = line.split()
codon = linearr[0]
cdnlen = len(codon)
aa = linearr[1]
ttable[codon] = aa
tfile.close()
return (ttable, cdnlen)
def translateSData(sfname, cdnlen, ttable):
try:
sequences = []
seqf = open(seq_fname, "r")
line = seqf.readline()
while line:
if line[0] == ">":
id = line[1:len(line)].strip()
seq = ""
line = seqf.readline()
while line and line[0] != '>':
seq += line.strip()
line = seqf.readline()
sequence = Seq(id, seq, cdnlen)
sequence.translate(ttable)
sequences.append(sequence)
seqf.close()
return sequences
if __name__ == "__main__":
(trans_table_fname, seq_fname) = parseCommandOptions(sys.argv)
(transtable, colen) = readTTable(trans_table_fname)
seqs = translateSData(seq_fname, colen, transtable)
for s in seqs:
print s
It says:
def translateSeqData(sfname, cdnlen, ttable):
^
IndentationError: unexpected unindent
WHY? I have checked a thousands times and I can't find the problem. I have only used Tabs and no spaces. Plus, sometimes it asks to define the class. Is that Ok?
It's because you have:
def readTTable(fname):
try:
without a matching except block after the try: block. Every try must have at least one matching except.
See the Errors and Exceptions section of the Python tutorial.
you didn't complete your try statement. You need and except in there too.
This error could actually be in the code preceding where the error is reported. See the For example, if you have a syntax error as below, you'll get the indentation error. The syntax error is actually next to the "except" because it should contain a ":" right after it.
try:
#do something
except
print 'error/exception'
def printError(e):
print e
If you change "except" above to "except:", the error will go away.
Good luck.
#MaxPython The answer below is missing syntax ":"
try:
#do something
except:
# print 'error/exception'
def printError(e):
print e
Appologies for the really long drawn out question.
I am trying to read in a config file and get a list of rules out.
I have tried to use ConfigParser to do this but it is not a standard config file.
The file contains no section header and no token.
i.e.
config section a
set something to something else
config subsection a
set this to that
next
end
config firewall policy
edit 76
set srcintf "There"
set dstintf "Here"
set srcaddr "all"
set dstaddr "all"
set action accept
set schedule "always"
set service "TCP_5600"
next
edit 77
set srcintf "here"
set dstintf "there"
set srcaddr "all"
set dstaddr "all"
set action accept
set schedule "always"
set service "PING"
next
end
As I couldn't work out how to get ConfigParser to work I thought I would try to iterate through the file, unfortunately I don't have much programming skill so I have got stuck.
I really think I am making this more complicated than it should be.
Here's the code I have written;
class Parser(object):
def __init__(self):
self.config_section = ""
self.config_header = ""
self.section_list = []
self.header_list = []
def parse_config(self, fields): # Create a new section
new_list = []
self.config_section = " ".join(fields)
new_list.append(self.config_section)
if self.section_list: # Create a sub section
self.section_list[-1].append(new_list)
else: self.section_list.append(new_list)
def parse_edit(self, line): # Create a new header
self.config_header = line[0]
self.header_list.append(self.config_header)
self.section_list[-1].append(self.header_list)
def parse_set(self, line): # Key and values
key_value = {}
key = line[0]
values = line[1:]
key_value[key] = values
if self.header_list:
self.header_list.append(key_value)
else: self.section_list[-1].append(key_value)
def parse_next(self, line): # Close the header
self.config_header = []
def parse_end(self, line): # Close the section
self.config_section = []
def parse_file(self, path):
with open(path) as f:
for line in f:
# Clean up the fields and remove unused lines.
fields = line.replace('"', '').strip().split(" ")
if fields[0] == "set":
pass
elif fields[0] == "end":
pass
elif fields[0] == "edit":
pass
elif fields[0] == "config":
pass
elif fields[0] == "next":
pass
else: continue
# fetch and call method.
method = fields[0]
parse_method = "parse_" + method
getattr(Parser, parse_method)(self, fields[1:])
return self.section_list
config = Parser().parse_file('test_config.txt')
print config
The output I am looking for is something like the following;
[['section a', {'something': 'to something else'}, ['subsection a', {'this': 'to that'}]],['firewall policy',['76',{'srcintf':'There'}, {'dstintf':'Here'}{etc.}{etc.}]]]
and this is what I get
[['section a']]
EDIT
I have changed the above to reflect where I am currently at.
I am still having issues getting the output I expect. I just can't seem to get the list right.
class Parser(object):
def __init__(self):
self.my_section = 0
self.flag_section = False
# ...
def parse_config(self, fields):
self.my_section += 1
# go on with fields
# ...
self.flag_section = True
def parse_edit(self, line):
...
def parse_set(self, line):
...
def parse_end(self, line):
...
def parse_file(self, path):
with open(path) as f:
for line in f:
fields = f.strip().split(" ")
method = fields[0]
# fetch and call method
getattr(Parser, "parse_" + method)(self, fields[1:])
I post my answer for people who first come here from Google when trying to parse Fortigate configuration file !
I rewrote what I found here based on my own needs and it works great.
from collections import defaultdict
from pprint import pprint
import sys
f = lambda: defaultdict(f)
def getFromDict(dataDict, mapList):
return reduce(lambda d, k: d[k], mapList, dataDict)
def setInDict(dataDict, mapList, value):
getFromDict(dataDict, mapList[:-1])[mapList[-1]] = value
class Parser(object):
def __init__(self):
self.config_header = []
self.section_dict = defaultdict(f)
def parse_config(self, fields): # Create a new section
self.config_header.append(" ".join(fields))
def parse_edit(self, line): # Create a new header
self.config_header.append(line[0])
def parse_set(self, line): # Key and values
key = line[0]
values = " ".join(line[1:])
headers= self.config_header+[key]
setInDict(self.section_dict,headers,values)
def parse_next(self, line): # Close the header
self.config_header.pop()
def parse_end(self, line): # Close the section
self.config_header.pop()
def parse_file(self, path):
with open(path) as f:
gen_lines = (line.rstrip() for line in f if line.strip())
for line in gen_lines:
# pprint(dict(self.section_dict))
# Clean up the fields and remove unused lines.
fields = line.replace('"', '').strip().split(" ")
valid_fields= ["set","end","edit","config","next"]
if fields[0] in valid_fields:
method = fields[0]
# fetch and call method
getattr(Parser, "parse_" + method)(self, fields[1:])
return self.section_dict
config = Parser().parse_file('FGT02_20130308.conf')
print config["system admin"]["admin"]["dashboard-tabs"]["1"]["name"]
print config["firewall address"]["ftp.fr.debian.org"]["type"]
I do not know if this can help you too, but it did for me : http://wiki.python.org/moin/ConfigParserExamples
Have fun !
I would do it in a simpler way:
flagSection = False
flagSub = False
mySection = 0
mySubsection = 0
myItem = 0
with open('d:/config.txt', 'r') as f:
gen_lines = (line.rstrip() for line in f if line.strip())
for line in gen_lines:
if line[0:7]=='config ':
mySection = mySection + 1
newLine = line[7:]
# Create a new section
# Mark section as open
flagSection == True
elif line[0:5]=='edit '):
mySubsection = mySubsection + 1
newLine = line[5:]
# Create a new sub-section
# Mark subsection as open
flagSub == true
elif line[0:4]=='set '):
myItem = myItem + 1
name, value = x.split(' ',2)[1:]
# Add to whatever is open
elif line=='end':
# If subsection = open then close and goto end
if flagSub:
# Or if section = open then close and goto end
elif flagSection:
# :End
continue
The instruction gen_lines = (line.rstrip() for line in f if line.strip())
creates a generator of not empty lines (thanks to the test if line.strip()) without newline and without blanks at the right (thanks to line.rstrip())
.
If I would know more about the operations you want to perform with name,value and in the section opened with if line=='end' , I could propose a code using regexes.
Edit
from time import clock
n = 1000000
print 'Measuring times with clock()'
te = clock()
for i in xrange(n):
x = ('abcdfafdf'[:3] == 'end')
print clock()-te,
print "\tx = ('abcdfafdf'[:3] == 'end')"
te = clock()
for i in xrange(n):
x = 'abcdfafdf'.startswith('end')
print clock()-te,
print "\tx = 'abcdfafdf'.startswith('end')"
print '\nMeasuring times with timeit module'
import timeit
ti = timeit.repeat("x = ('abcdfafdf'[:3] == 'end')",repeat=10,number = n)
print min(ti),
print "\tx = ('abcdfafdf'[:3] == 'end')"
to = timeit.repeat("x = 'abcdfafdf'.startswith('end')",repeat=10,number = n)
print min(to),
print "\tx = 'abcdfafdf'.startswith('end')"
result:
Measuring times with clock()
0.543445605517 x = ('abcdfafdf'[:3] == 'end')
1.08590449345 x = 'abcdfafdf'.startswith('end')
Measuring times with timeit module
0.294152748464 x = ('abcdfafdf'[:3] == 'end')
0.901923289133 x = 'abcdfafdf'.startswith('end')
Is the fact the times are smaller with timieit than with clock() due to the fact that the GC is unplugged when the program is run ? Anyway, with either clock() or timeit module , executing startswith() takes more time than slicing.