HiveBrain v1.2.0
Get Started
← Back to all entries
snippetpythonMinor

Python API for TreeStructInfo - new simple config format

Submitted by: @import:stackexchange-codereview··
0
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)

Solution

Note: I realize this was asked in 2014, and many python style requirements may have been different.

  • Docstrings: You should include a docstring at 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 path from os in your method file_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 the x. 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 False


Instead 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 type and isinstance and 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 name path. This can cause some naming issues. In your circumstance, changing the parameter to file_path clears 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 False
return 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(el

Context

StackExchange Code Review Q#63934, answer score: 3

Revisions (0)

No revisions yet.