snippetpythonMinor
Python API for TreeStructInfo - new simple config format
Viewed 0 times
formatsimplenewconfigforpythontreestructinfoapi
Problem
I've created a simple (300 SLOC) API for a new configuration format (I'm not the author of it). Unfortunately, the specification is only in Polish at the moment, but you can see how it looks here.
The format has a few additional features besides simple data storage. For example, many files can be linked together to create a nice configuration system, and attributes can be referenced from outside of the main tree.
The code repository, and also a mini-tutorial for the API, is here.
It's my first more complex project, so the code is probably quite bad (especially that spaghetti code for the parsing method).
tsi_file.py:
```
from treestructinfo import tsi_elements
FORMAT_VERSION = 0.1
class TsiFile:
def __init__(self, path="tsi_file.tsinfo", flags=['text', 'write']):
self.path = path
# TsiFile is also a node
self.current_node = ""
self.elements = []
self.flags = get_flags(flags) # converts parameter list into string understandable for open() method
if not file_does_exist(self.path):
raise FileNotFoundError
parse_data(self, self.path, self.flags)
def save(self):
with open(self.path+'test', "w+") as f:
refs = [] # list of referenced elements is used to correctly place definitions and declarations
indent = 0 # number of spaces before line (+2 for each level of nesting)
f.write('tsinfo version "{}"\n'.format(FORMAT_VERSION))
def iterate(ele):
nonlocal indent
if type(ele) is tsi_elements.TsiAttr:
name = ele.name
if ele.ref:
refs.append(ele)
f.write('{}ref attr {}\n'.format(' ' * indent, name))
else:
value_list = ele.value.split('\n') # multiline values are separated by \n char
line = '{}attr {}'.format(' ' * indent, name)
The format has a few additional features besides simple data storage. For example, many files can be linked together to create a nice configuration system, and attributes can be referenced from outside of the main tree.
The code repository, and also a mini-tutorial for the API, is here.
It's my first more complex project, so the code is probably quite bad (especially that spaghetti code for the parsing method).
tsi_file.py:
```
from treestructinfo import tsi_elements
FORMAT_VERSION = 0.1
class TsiFile:
def __init__(self, path="tsi_file.tsinfo", flags=['text', 'write']):
self.path = path
# TsiFile is also a node
self.current_node = ""
self.elements = []
self.flags = get_flags(flags) # converts parameter list into string understandable for open() method
if not file_does_exist(self.path):
raise FileNotFoundError
parse_data(self, self.path, self.flags)
def save(self):
with open(self.path+'test', "w+") as f:
refs = [] # list of referenced elements is used to correctly place definitions and declarations
indent = 0 # number of spaces before line (+2 for each level of nesting)
f.write('tsinfo version "{}"\n'.format(FORMAT_VERSION))
def iterate(ele):
nonlocal indent
if type(ele) is tsi_elements.TsiAttr:
name = ele.name
if ele.ref:
refs.append(ele)
f.write('{}ref attr {}\n'.format(' ' * indent, name))
else:
value_list = ele.value.split('\n') # multiline values are separated by \n char
line = '{}attr {}'.format(' ' * indent, name)
Solution
Note: I realize this was asked in 2014, and many python style requirements may have been different.
In your code you had:
Instead of returning pure
Updated Code
``
:param ele: Element to be iterated over
"""
nonlocal indent
if isinstance(ele, tsi_elements.TsiAttr):
name = ele.name
if ele.ref:
refs.append(ele)
file.write('{}ref attr {}\n'.format(' ' * indent, name))
else:
value_list = ele.value.split('\n') # multiline values are separated by \n char
line = '{}attr {}'.format(' ' * indent, name)
if len(value_list) == 1: # value is a single line
value = value_list[0]
else: # joining multiline strings
value = '\n'.join([' ' * line.length() + '"' + split + '"' for split in value_list])
file.write('{} "{}"\n'.format(line, value))
else: # type is tsi_elements.TsiNode
if not ele.ref and ele.link is None:
file.write('{}node {}\n'.format(' ' * indent, ele.name))
indent += 2
for element in ele.elements:
iterate(element)
if element == ele.elements[-1]: # adding closing tag after all the content is saved
indent -= 2
file.write('{}end\n'.format(' ' * indent))
elif ele.link is not None:
file.write('{}link "{}"
- Docstrings: You should include a
docstringat the beginning of every method, class, and module you write. This will help any documentation identify what your program is supposed to do.
- Imports: You imported
pathfromosin your methodfile_does_exist. This does not follow proper PEP-8 import formatting. All your imports should be placed at the top of your code, regardless of when you use them. They should also be grouped (standard library imports, related third party imports, then local application imports) and separated by a blank line.
- Unused variables in loops: Many times you do
for i, x in enumerate(split):, yet you never use thex. You can instead use an_, which will make it clear that that variable is to be, essentially, ignored.
- Variable Naming: Using one letter names isn't recommended, as it's hard to know what they're used for without looking at the code around them. This practices can greatly improve readability of your code.
- Simplify boolean comparisons:
In your code you had:
if path.isfile(path.join(file_path)):
return True
return FalseInstead of returning pure
True/False, you can return the condition instead, since it returns a boolean anyway, like so:return path.isfile(path.join(file_path))- type vs isinstance: This StackOverflow answer provides a very good and in-depth explanation about
typeandisinstanceand when to use them.
- Variable and operator spacing: In your code you had:
with open(self.path+'test', "w+") as file:While this is the only place in the code (that I saw) that doesn't space out, you should be consistent. Spacing out code improves readability of your code.
- Indentation: Stay consistent with your indentation. A few times you indented four spaces too much. This can put variables/methods in an entirely different scope.
- Redeclaring from outside scope: You import
path, yet also have a parameter with the namepath. This can cause some naming issues. In your circumstance, changing the parameter tofile_pathclears this concern, and also provides clarity about what the path actually is.
Updated Code
``
"""
Module Docstring:
A description about your program goes here
"""
from os import path
from treestructinfo import tsi_elements
FORMAT_VERSION = 0.1
class TsiFile:
"""
Class for a TsiFile
"""
def __init__(self, file_path="tsi_file.tsinfo", flags=['text', 'write']):
self.path = file_path
# TsiFile is also a node
self.current_node = ""
self.elements = []
# converts parameter list into string understandable for open() method
self.flags = get_flags(flags)
if not file_does_exist(self.path):
raise FileNotFoundError
parse_data(self, self.path, self.flags)
def save(self):
"""
Saves content to a file at self.path
"""
with open(self.path + 'test', "w+") as file:
refs = [] # list of referenced elements is used to correctly place definitions and declarations
indent = 0 # number of spaces before line (+2 for each level of nesting)
file.write('tsinfo version "{}"\n'.format(FORMAT_VERSION))
def iterate(ele):
"""
Iterates over the current element, ele`:param ele: Element to be iterated over
"""
nonlocal indent
if isinstance(ele, tsi_elements.TsiAttr):
name = ele.name
if ele.ref:
refs.append(ele)
file.write('{}ref attr {}\n'.format(' ' * indent, name))
else:
value_list = ele.value.split('\n') # multiline values are separated by \n char
line = '{}attr {}'.format(' ' * indent, name)
if len(value_list) == 1: # value is a single line
value = value_list[0]
else: # joining multiline strings
value = '\n'.join([' ' * line.length() + '"' + split + '"' for split in value_list])
file.write('{} "{}"\n'.format(line, value))
else: # type is tsi_elements.TsiNode
if not ele.ref and ele.link is None:
file.write('{}node {}\n'.format(' ' * indent, ele.name))
indent += 2
for element in ele.elements:
iterate(element)
if element == ele.elements[-1]: # adding closing tag after all the content is saved
indent -= 2
file.write('{}end\n'.format(' ' * indent))
elif ele.link is not None:
file.write('{}link "{}"
Code Snippets
if path.isfile(path.join(file_path)):
return True
return Falsereturn path.isfile(path.join(file_path))"""
Module Docstring:
A description about your program goes here
"""
from os import path
from treestructinfo import tsi_elements
FORMAT_VERSION = 0.1
class TsiFile:
"""
Class for a TsiFile
"""
def __init__(self, file_path="tsi_file.tsinfo", flags=['text', 'write']):
self.path = file_path
# TsiFile is also a node
self.current_node = ""
self.elements = []
# converts parameter list into string understandable for open() method
self.flags = get_flags(flags)
if not file_does_exist(self.path):
raise FileNotFoundError
parse_data(self, self.path, self.flags)
def save(self):
"""
Saves content to a file at `self.path`
"""
with open(self.path + 'test', "w+") as file:
refs = [] # list of referenced elements is used to correctly place definitions and declarations
indent = 0 # number of spaces before line (+2 for each level of nesting)
file.write('tsinfo version "{}"\n'.format(FORMAT_VERSION))
def iterate(ele):
"""
Iterates over the current element, `ele`
:param ele: Element to be iterated over
"""
nonlocal indent
if isinstance(ele, tsi_elements.TsiAttr):
name = ele.name
if ele.ref:
refs.append(ele)
file.write('{}ref attr {}\n'.format(' ' * indent, name))
else:
value_list = ele.value.split('\n') # multiline values are separated by \n char
line = '{}attr {}'.format(' ' * indent, name)
if len(value_list) == 1: # value is a single line
value = value_list[0]
else: # joining multiline strings
value = '\n'.join([' ' * line.length() + '"' + split + '"' for split in value_list])
file.write('{} "{}"\n'.format(line, value))
else: # type is tsi_elements.TsiNode
if not ele.ref and ele.link is None:
file.write('{}node {}\n'.format(' ' * indent, ele.name))
indent += 2
for element in ele.elements:
iterate(element)
if element == ele.elements[-1]: # adding closing tag after all the content is saved
indent -= 2
file.write('{}end\n'.format(' ' * indent))
elif ele.link is not None:
file.write('{}link "{}" as "{}"\n'.format(' ' * indent, ele.link, ele.name))
else: # ele.ref
refs.append(ele)
file.write('{}ref node {}\n'.format(' ' * indent, ele.name))
def iterate_ref(elContext
StackExchange Code Review Q#63934, answer score: 3
Revisions (0)
No revisions yet.