Adding variables to dataclass from json config file - python

After watching ArjanCodes video on dataclasses,
I've been trying to add variables to a python dataclass from a json config file to format the font style of a print function printT in Jupyterlab.
I use ANSI escapes for the formatting which doesn't work anymore if I import the variables to the dataclass. Instead of formatting the text, the ANSI code get's printed out.
# config.json
{
"lb" : "\n",
"solid_line" : "'___'*20 + config.lb",
"dotted_line" : "'---'*20 + config.lb",
"BOLD" : "\\033[1m",
"END" : "\\033[0m"
}
# config.py
from dataclasses import dataclass
import json
#dataclass
class PrintConfig:
lb : str
solid_line : str
dotted_line : str
BOLD : str
END : str
def read_config(config_file : str) -> PrintConfig:
with open(config_file, 'r') as file:
data = json.load(file)
return(PrintConfig(**data))
# helper.py
from config import read_config
config = read_config('config.json')
def printT(title,linebreak= True,addLine = True, lineType = config.solid_line,toDisplay = None):
'''
Prints a line break, the input text and a solid line.
Inputs:
title = as string
linebreak = True(default) or False; Adds a line break before printing the title
addLine = True(default) or False; Adds a line after printing the title
lineType = solid_line(default) or dotted_line; Defines line type
toDisplay = displays input, doesnt work with df.info(),because info executes during input
'''
if linebreak:
print(config.lb)
print(config.BOLD + title + config.END)
if addLine:
print(lineType)
if toDisplay is not None:
display(toDisplay)
# test.ipynb
from helper import printT
printT('Hello World')
Output
\033[1mHello World\033[0m
'___'*20 + config.lb
Desired result
Hello World
It works if I use eval if addLine: print(eval(lineType)) but I'd like to get deeper insights into the mechanics here. Is there a way of getting it to work without eval?
Also this part "solid_line" : "'___'*20 + config.lb" feels wrong.
Markdown as alternative to ANSI

Here's a basic configuration system. I won't add the output since it would need a screenshot but it works on bash/macos. Inspired by and [tip_colors_and_formatting]
And from (https://misc.flogisoft.com/bash/tip_colors_and_formatting):
In Bash, the character can be obtained with the following syntaxes:
\e
\033
\x1B
\e didn't work, so I went on to use to \x1B since that worked in the linked SE answer. \033 works too, I checked.
from dataclasses import dataclass
PREFIX = "\x1B["
#these aren't configurable, they are ANSI constants so probably
#not useful to put them in a config json
CODES = dict(
prefix = PREFIX,
bold = f"1",
reset = f"{PREFIX}0m",
red = "31",
green = "32",
)
#dataclass
class PrintConfig:
bold : bool = False
color : str = ""
def __post_init__(self):
# these are calculated variables, none of client code's
# business:
self.start = self.end = ""
start = ""
if self.bold:
start += CODES["bold"] + ";"
if self.color:
start += CODES[self.color.lower()] + ";"
if start:
self.end = CODES["reset"]
#add the escape prefix, then the codes and close with m
self.start = f"{CODES['prefix']}{start}".rstrip(";") + "m"
def print(self,v):
print(f"{self.start}{v}{self.end}")
normal = PrintConfig()
normal.print("Hello World")
bold = PrintConfig(bold=1)
print(f"{bold=}:")
bold.print(" Hello World")
boldred = PrintConfig(bold=1,color="red")
print(f"{boldred=}:")
boldred.print(" Hello bold red")
#this is how you would do it from json
green = PrintConfig(**dict(color="green"))
green.print(" Little Greenie")
#inspired from https://stackoverflow.com/a/287934
print("\n\ninspired by...")
CSI = "\x1B["
print(CSI+"31;40m" + "Colored Text" + CSI + "0m")
print(CSI+"1m" + "Colored Text" + CSI + "0m")

This string consists of an actual backslash followed by the digits 033, etc.
"BOLD" : "\\033[1m",
To turn on bold on an ansi terminal, you need an escape character (octal 33) followed by [1m. In Python, you can write those escape codes with a single backslash: "\033[1m". In a json file, you must provide the unicode codepoint of the escape character, \u001b. If the rest is in order, you'll see boldface.
"BOLD" : "\u001b[1m",
"END" : "\u001b[0m"
As for the eval part, you have a string containing the expression you need to evaluate. I assume you wrote it this way because you first tried without the double quotes, e.g. ,
"dotted_line" : '---'*20 + config.lb,
and you got a json syntax error. That's not surprising: Json files are data, not code, and they cannot incorporate expressions or variable references. Either place your config in a python file that you include instead of loading json, or move the dependencies to the code. Or both.
In a python file, config.py:
config = {
"lb": "\n",
"solid_line" : '___'*20,
...
In helper.py:
...
if addLine:
print(lineType + config.lb)

Related

How to escape all HTML entities in show_popup() method and fix Parse Error in Sublime Text 3 plugin?

I am making a plugin for Sublime Text 3. It contacts my server in Java and receives a response in the form of a list of strings, that contains C code.
To display this code in a popup window you need to pass a string in HTML format to the method show_popup. Accordingly, all C-code characters that can be recognized by the parser as HTML entities should be replaced with their names (&name;) or numbers (&#number;). At first, I just replaced the most common characters with replace(), but it didn't always work out - Parse Error was displayed in the console:
Parse Error: <br> printf ("Decimals: %d %ld\n", 1977, 650000L);
<br> printf ("Preceding with blanks:&nbs
...
y</a></li><p><b>____________________________________________________</b></p>
</ul>
</body>
code: Unexpected character
I've tried to escape html entities with Python's html library:
import html
...
html.escape(string)
But Sublime doesn't see import and print in console that I was using a function without defining it - I guess he didn't see that I connected this library(Whyyy?). cgi.escape - is depricated, so I can't use this. I decided to write the function myself.
Then I saw a very interesting way to replace all the characters whose code is >127 and some other characters (&, <,>) with their numbers:
def escape_html (s):
out = ""
i = 0
while i < len(s):
c = s[i]
number = ord(c)
if number > 127 or c == '"' or c == '\'' or c == '<' or c == '>' or c == '&':
out += "&#"
out += str(number)
out += ";"
else:
out += c
i += 1
out = out.replace(" ", " ")
out = out.replace("\n", "<br>")
return out
This code works perfectly for displaying characters in a browser, but unfortunately it is not supported by Sublime Text 3.
As a result, I came to the conclusion that these characters should be replaced with their equivalent names:
def dumb_escape_html(s):
entities = [["&", "&"], ["<", "<"], [">", ">"], ["\n", "<br>"],
[" ", " "]]
for entity in entities:
s = s.replace(entity[0], entity[1])
return s
But again I faced an obstacle: not all names are supported in Sublime. And again an error Parse Error.
I also attach a link to JSON file, which contains answer from my server, content of which should be displayed in pop-up window: Example of data from sever (codeshare.io)
I absolutely do not understand, in what I make a mistake - I hope, that great programmers know how to solve my problem.
Edit. Minimal, Reproducible Example:
import sublime
import sublime_plugin
import string
import sys
import json
def get_func_name(line, column):
return "printf"
def get_const_data(func_name):
input_file = open ("PATH_TO_JSON/data_printf.json")
results = json.load(input_file)
return results
def dumb_escape_html(s):
entities = [["&", "&"], ["<", "<"], [">", ">"], ["\n", "<br>"],
[" ", " "]]
for entity in entities:
s = s.replace(entity[0], entity[1])
return s
def dumb_unescape_html(s):
entities = [["<", "<"], [">", ">"], ["<br>", "\n"],
[" ", " "], ["&", "&"]]
for entity in entities:
s = s.replace(entity[0], entity[1])
return s
class CoderecsysCommand(sublime_plugin.TextCommand):
def run(self, edit):
v = self.view
cur_line = v.substr(v.line(v.sel()[0]))
for sel in v.sel():
line_begin = v.rowcol(sel.begin())[0]
line_end = v.rowcol(sel.end())[0]
pos = v.rowcol(v.sel()[0].begin()) # (row, column)
try:
func_name = get_func_name(cur_line, pos[1]-1)
li_tree = ""
final_data = get_const_data(func_name)
for i in range(len(final_data)):
source = "source: " + final_data[i]["source"]
escaped = dumb_escape_html(final_data[i]["code"])
divider = "<b>____________________________________________________</b>"
li_tree += "<li><p>%s</p>%s <a href='%s'>Copy</a></li><p>%s</p>" %(source, escaped, escaped, divider)
# The html to be shown.
html = """
<body id=copy-multiline>
Examples of using <b>%s</b> function.
<ul>
%s
</ul>
</body>
""" %(func_name, li_tree)
self.view.show_popup(html, max_width=700, on_navigate=lambda example: self.copy_example(example, func_name, source))
except Exception as ex:
self.view.show_popup("<b style=\"color:#1c87c9\">CodeRec Error:</b> " + str(ex), max_width=700)
def copy_example(self, example, func_name, source):
# Copies the code to the clipboard.
unescaped = dumb_unescape_html(example)
unescaped = "// " + source + unescaped
sublime.set_clipboard(unescaped)
self.view.hide_popup()
sublime.status_message('Example of using ' + func_name + ' copied to clipboard !')

Text in .write gets cut off

I am trying to make a program that will create a LaTex file (.tex) with a preamble in it, and sooner some sections. I have defined my function thepreamble(title,subject) such that the inputs will be created in the string, which is seen below in my code.
# -*- coding: utf-8 -*-
import io
def thepreamble(title, subject):
global preamble
preamble = r'''\documentclass[a4paper, 12pt]{extarticle}
\usepackage[T1]{fontenc}
\usepackage[utf8x]{inputenc}
\usepackage[english, danish]{babel}
\usepackage{fancyhdr}
\usepackage[dvipsnames]{xcolor}
\usepackage{mathtools}
\usepackage{graphicx}
\usepackage{amssymb}
\usepackage{titlesec}
\usepackage[left=0.5in, right=0.5in, top=0.8in, bottom=0.8in]{geometry}
\usepackage{lipsum}
\usepackage[breaklinks, colorlinks=true,linkcolor=NavyBlue, citecolor=blue, urlcolor=Blue, linktoc=all]{hyperref}
\usepackage[utf8x]{inputenc}
\usepackage{titlesec}
\usepackage{fix-cm}
\usepackage{titletoc}
\usepackage{tocloft}
\usepackage{setspace}
\usepackage[all]{hypcap}
\usepackage{tikz, pgfplots}
\usetikzlibrary{calc}
\usepackage{tkz-euclide}
\usetkzobj{all}
\usetikzlibrary{positioning}
\usepackage{tikzrput}
\usetikzlibrary{arrows.meta}
\usepackage[labelfont=bf]{caption}
\usepackage[hang, flushmargin]{footmisc}
\usepackage{footnotebackref}
\pagestyle{fancy}
\fancyhf{}
\fancyfoot[R]{\textbf \thepage}
\renewcommand{\headrulewidth}{0pt}
\renewcommand{\footrulewidth}{2pt}
\renewcommand{\footrule}{\hbox to\headwidth{\color{NavyBlue}\leaders\hrule height \footrulewidth\hfill}}
\newcommand{\dl}[1]{\underline{\underline{#1}}}
\setcounter{tocdepth}{2}
\setcounter{secnumdepth}{0}
\begin{document}
\begin{titlepage}
\begin{center}
\vspace*{30ex}
{\fontsize{38}{0}\selectfont \bfseries \fontfamily{put}\selectfont \color{NavyBlue} '''+ str(title)+'''} \\
[3ex]
{\fontsize{18}{0}\selectfont \bfseries \fontfamily{put}\selectfont \color{NavyBlue} ('''+str(subject)+ ''')}\\
[14ex]
{ \fontsize{15}{0}\selectfont Casper Juul Lorentzen} \\
[3ex]
{\large \scshape 1.z} \\
[2ex]
{\large \scshape 2018}\\
\vspace{\fill}
\includegraphics[scale=0.45]{C:/LaTeX/Next.png} \\
[4mm]
\small{\bfseries Albertslund Gymnasium \& HF} \\
\end{center}
\end{titlepage}
\renewcommand\contentsname{Indhold \vspace{3ex}}
\tableofcontents
\thispagestyle{empty}
\newpage
\setcounter{page}{1}
'''
return preamble
def sections(numsec, numsubsec):
numbers = []
numbers.extend(numsubsec)
global tasks
tasks = []
print("")
#Brug præfikset 'r' foran unicodes
print("")
for n,i in zip(range(1, numsec+1),range(0,numsec)):
print("")
opgaver = "\section{Opgave "+str(n)+"}"
print(opgaver)
print("")
tasks.append(opgaver)
for x in range(int(numsubsec[i])):
print("\subsection{}")
print("")
return tasks
def runprogram():
encoding ='utf8'
titlefile = input("Title (file): ")
title = input("Title of document: ")
subject = input("Subject: ")
numsec = int(input("How many sections? "))
filename = "C:\\Users\\Casper\\Documents\\LaTeX\\fire.tex"
while True:
numsubsec = input("How many subsections?")
while len(numsubsec) !=numsec:
print("")
numsubsec =input("Error; input must be of "+ str(numsec) + " digits ")
try:
with io.open(filename.replace('fire.tex',titlefile+".tex"), 'w', encoding=encoding) as f:
f.write(unicode_thepreamble(title, subject))
f.close()
#sections(numsec, numsubsec)
break
except:
print("Error")
runprogram()
Whenever I run the program, it creates a new .tex file with the name of
titlefile = input("Title (file): ")
As you can see, i have defined preamble as a text with unicode characters in it. And when I run the program, it writes almost everything of the preamble string in the tex document, but it cuts some of it off and creates weird symbol, like this:
tex document created
I named the title 'stackoverflow' and the subject 'python problem', and that works fine. But what ought to be '\renewcommand' is in the document ' enewcommand'. I do not know how to fix this. I just want precisely what my preamble strings says.
When you merge your title and subject into the string you have to make the second piece raw again
r''' bla bla '''+ str(title) + r''' bla bla'''
The second "r" is missing, twice in your example.
You should consider merging using str.format().

Python; How to replace escaped non-unicode characters with their respective 'real' utf-8

I am relatively new to programming, and I have a small problem writing a python equivalent of Snip for spotify for ubuntu(linux)
Somehow i can encode the title correctly, but am unable to encode the artist the same way
when i try to encode the artist in the same fashion i get this:
File "./songfinder.py", line 11, in currentplaying
artiststr = str((metadata['xesam:artist']).encode('utf-8'))
AttributeError: 'dbus.Array' object has no attribute 'encode'
however the title is done exactly the same and that is working.
Code so far IS working but has for example \xd8 instead of Ø, and similar:
import dbus
session_bus = dbus.SessionBus()
spotify_bus = session_bus.get_object("org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2")
spotify_properties = dbus.Interface(spotify_bus, "org.freedesktop.DBus.Properties")
def currentplaying():
metadata = spotify_properties.Get("org.mpris.MediaPlayer2.Player", "Metadata")
title = str((metadata['xesam:title']).encode('utf-8'))
artiststr = str((metadata['xesam:artist']))
if ("dbus.string" in artiststr.lower()):
artists = artiststr.split("(u")
artist = artists[1]
artists = artist.split(")],")
artist = artists[0]
artist = artist.replace("(u", "")
else:
artist = "'unknown'"
artist = (artist.replace("'",""))
playing = (artist + " - " + title + " ")
return playing
#save playing to file.txt
relevant qna's:
Replace non-ascii chars from a unicode string in Python
Why it does not resolve my problem: I would like to print/save the actual character, not replace it with similar ones
Looking at your question metadata contains at least something like this with Unicode strings. The artist field seems to be some sort of iterable the begins with the artist. Something like this (feel free to post actual metadata content):
metadata = {'xesam:title':u'title','xesam:artist':[u'artist']}
In the title assignment line, str is unnecessary since encoding a Unicode string returns a str anyway, but no need to encode it either. Unicode strings represent text, so leave it that way:
title = metadata['xesam:title']
Similar for artist assignment, but get the first element of the iterable:
artist = metadata['xesam:artist'][0]
Next, in your song-updating logic, use io.open to open the files with a UTF-8 encoding. This lets Unicode strings (text) be written directly and the file will handle the encoding. Also use a with statement to automatically close the file when the with ends.
Program with recommended changes:
import time
import dbus
import io
session_bus = dbus.SessionBus()
spotify_bus = session_bus.get_object("org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2")
spotify_properties = dbus.Interface(spotify_bus, "org.freedesktop.DBus.Properties")
def currentplaying():
metadata = spotify_properties.Get("org.mpris.MediaPlayer2.Player", "Metadata")
title = metadata['xesam:title']
artist = metadata['xesam:artist'][0]
playing = artist + " - " + title + " "
return playing
while True:
with io.open('currentsongspotify.txt', encoding='utf8') as filetxt:
oldtitle = filetxt.read()
newtitle = currentplaying()
if newtitle == oldtitle:
time.sleep(1)
else:
with io.open('currentsongspotify.txt','w',encoding='utf8') as filetxt: # save newtitle to file, overwriting existing data
filetxt.write(newtitle)
print 'new file saved:',newtitle
The error you getting is not about unicode, it is about wrong type. Python complains that you trying to call string method encode from the array object. Which does not have this method.
The first this I would try is to remove redundant brackets here it getting artiststr like this: artiststr = str(metadata['xesam:artist']).
But I'm not sure this would work. If it doesn't work, you need to find out what type has metadata['xesam:artist']. Looks like it is not string, but array. So you need to fix the code which fills metadata['xesam:artist'] with data. You can try to use debugger or just print() function to find out the content of metadata['xesam:artist']. Or provide the relevant code in you question too.
Final program, feel free to use if you like:
import time
import dbus
session_bus = dbus.SessionBus()
spotify_bus = session_bus.get_object("org.mpris.MediaPlayer2.spotify", "/org/mpris/MediaPlayer2")
spotify_properties = dbus.Interface(spotify_bus, "org.freedesktop.DBus.Properties")
def currentplaying():
metadata = spotify_properties.Get("org.mpris.MediaPlayer2.Player", "Metadata")
title = str((metadata['xesam:title']).encode('utf-8'))
artiststr = str((metadata['xesam:artist'])[0].encode('utf-8'))
artist = artiststr
playing = (artist + " - " + title + " ")
return playing
while True:
filetxt = open("/home/USER/Desktop/currentsongspotify.txt", "r")
oldtitle = filetxt.read()
filetxt.close()
newtitle = str(currentplaying())
if(newtitle == oldtitle):
time.sleep(1)
else:
filetxt = open("/home/USER/Desktop/currentsongspotify.txt", "w") #save newtitle to file, overwriting existing data
filetxt.write(str(newtitle))
print("new file saved: " + newtitle)

How to parse F5 bigip.conf using pyparsing

am trying to figure out how to use this nifty lib to parse BigIP config files...
the grammar should,be something like this:
stanza :: name { content }
name :: several words, might contain alphas nums dot dash underscore or slash
content:: stanza OR ZeroOrMore(printable characters)
To make things slightly more complicated, one exception:
If name starts with "rule ", then content cannot be "stanza"
I started with this:
from pyparsing import *
def parse(config):
def BNF():
"""
Example:
...
ltm virtual /Common/vdi.uis.test.com_80_vs {
destination /Common/1.2.3.4:80
http-class {
/Common/http2https
}
ip-protocol tcp
mask 255.255.255.255
profiles {
/Common/http { }
/Common/tcp { }
}
vlans-disabled
}
...
"""
lcb, rcb, slash, dot, underscore, dash = [c for c in '{}/._-']
name_word = Word(alphas + nums + dot + underscore + slash + dash)
name = OneOrMore(name_word).setResultsName("name")
stanza = Forward()
content = OneOrMore(stanza | ZeroOrMore(OneOrMore(Word(printables)))).setResultsName("content")
stanza << Group(name + lcb + content + rcb).setResultsName("stanza")
return stanza
return [x for x in BNF().scanString(config)]
The code above seems to lock up in some infinite loop. It is also missing my requirement for excluding looking for 'stanza" if "name" starts with "rule ".
OneOrMore(ZeroOrMore(OneOrMore(Word(printables))) will always match, thus leading to the infinite loop.
Also, printables includes a closing curly bracket, which gets consumed by the content term, and is no longer available for the stanza. (If your content can including a closing bracket, you need to define something to escape it, to distinguish a content bracket from a stanza bracket.)
To address the name rule, you need another content definition, one that doesn't include stanza, and a "rule rule".
def parse(config):
def BNF():
lcb, rcb, slash, dot, underscore, dash = [c for c in '{}/._-']
printables_no_rcb = Word(printables, excludeChars=rcb)
name_word = Word(alphas + nums + dot + underscore + slash + dash)
name = OneOrMore(name_word).setResultsName("name")
rule = Group(Literal('rule') + name).setResultsName("name")
rule_content = OneOrMore(printables_no_rcb).setResultsName("content")
stanza = Forward()
content = OneOrMore(stanza | OneOrMore(printables_no_rcb)).setResultsName("content")
stanza << Group(rule + lcb + rule_content + rcb | name + lcb + content + rcb).setResultsName("stanza")
return stanza
return [x for x in BNF().scanString(config)]

What's the best way(error proof / foolproof) to parse a file using python with following format?

########################################
# some comment
# other comment
########################################
block1 {
value=data
some_value=some other kind of data
othervalue=032423432
}
block2 {
value=data
some_value=some other kind of data
othervalue=032423432
}
The best way would be to use an existing format such as JSON.
Here's an example parser for your format:
from lepl import (AnyBut, Digit, Drop, Eos, Integer, Letter,
NON_GREEDY, Regexp, Space, Separator, Word)
# EBNF
# name = ( letter | "_" ) , { letter | "_" | digit } ;
name = Word(Letter() | '_',
Letter() | '_' | Digit())
# words = word , space+ , word , { space+ , word } ;
# two or more space-separated words (non-greedy to allow comment at the end)
words = Word()[2::NON_GREEDY, ~Space()[1:]] > list
# value = integer | word | words ;
value = (Integer() >> int) | Word() | words
# comment = "#" , { all characters - "\n" } , ( "\n" | EOF ) ;
comment = '#' & AnyBut('\n')[:] & ('\n' | Eos())
with Separator(~Regexp(r'\s*')):
# statement = name , "=" , value ;
statement = name & Drop('=') & value > tuple
# suite = "{" , { comment | statement } , "}" ;
suite = Drop('{') & (~comment | statement)[:] & Drop('}') > dict
# block = name , suite ;
block = name & suite > tuple
# config = { comment | block } ;
config = (~comment | block)[:] & Eos() > dict
from pprint import pprint
pprint(config.parse(open('input.cfg').read()))
Output:
[{'block1': {'othervalue': 32423432,
'some_value': ['some', 'other', 'kind', 'of', 'data'],
'value': 'data'},
'block2': {'othervalue': 32423432,
'some_value': ['some', 'other', 'kind', 'of', 'data'],
'value': 'data'}}]
Well, the data looks pretty regular. So you could do something like this (untested):
class Block(object):
def __init__(self, name):
self.name = name
infile = open(...) # insert filename here
current = None
blocks = []
for line in infile:
if line.lstrip().startswith('#'):
continue
elif line.rstrip().endswith('{'):
current = Block(line.split()[0])
elif '=' in line:
attr, value = line.strip().split('=')
try:
value = int(value)
except ValueError:
pass
setattr(current, attr, value)
elif line.rstrip().endswith('}'):
blocks.append(current)
The result will be a list of Block instances, where block.name will be the name ('block1', 'block2', etc.) and other attributes correspond to the keys in your data. So, blocks[0].value will be 'data', etc. Note that this only handles strings and integers as values.
(there is an obvious bug here if your keys can ever include 'name'. You might like to change self.name to self._name or something if this can happen)
HTH!
If you do not really mean parsing, but rather text processing and the input data is really that regular, then go with John's solution. If you really need some parsing (like there are some a little more complex rules to the data that you are getting), then depending on the amount of data that you need to parse, I'd go either with pyparsing or simpleparse. I've tried both of them, but actually pyparsing was too slow for me.
You might look into something like pyparsing.
Grako (for grammar compiler) allows to separate the input format specification (grammar) from its interpretation (semantics). Here's grammar for your input format in Grako's variety of EBNF:
(* a file contains zero or more blocks *)
file = {block} $;
(* a named block has at least one assignment statement *)
block = name '{' {assignment}+ '}';
assignment = name '=' value NEWLINE;
name = /[a-z][a-z0-9_]*/;
value = integer | string;
NEWLINE = /\n/;
integer = /[0-9]+/;
(* string value is everything until the next newline *)
string = /[^\n]+/;
To install grako, run pip install grako. To generate the PEG parser from the grammar:
$ grako -o config_parser.py Config.ebnf
To convert stdin into json using the generated config_parser module:
#!/usr/bin/env python
import json
import string
import sys
from config_parser import ConfigParser
class Semantics(object):
def file(self, ast):
# file = {block} $
# all blocks should have unique names within the file
return dict(ast)
def block(self, ast):
# block = name '{' {assignment}+ '}'
# all assignment statements should use unique names
return ast[0], dict(ast[2])
def assignment(self, ast):
# assignment = name '=' value NEWLINE
# value = integer | string
return ast[0], ast[2] # name, value
def integer(self, ast):
return int(ast)
def string(self, ast):
return ast.strip() # remove leading/trailing whitespace
parser = ConfigParser(whitespace='\t\n\v\f\r ', eol_comments_re="#.*?$")
ast = parser.parse(sys.stdin.read(), rule_name='file', semantics=Semantics())
json.dump(ast, sys.stdout, indent=2, sort_keys=True)
Output
{
"block1": {
"othervalue": 32423432,
"some_value": "some other kind of data",
"value": "data"
},
"block2": {
"othervalue": 32423432,
"some_value": "some other kind of data",
"value": "data"
}
}

Categories

Resources