Programming, electronics, lifestyle

06 Dec 2022

Python: Stack implementation by text file

Some time ago I realized that there is no a library to keep a stack of strings in a text file efficiently in Python. So I coded own one. I commented the code, and I suppose that any additional words will be redundant.

#! /usr/bin/env python3

#####################################################################
# The library allows to work with stack of strings in a text file   #
# To use import this file as library (ex. for same dir)             #
# from stack import Stack                                           #
# Author: Artem Smirnov <[email protected]>                         #
#####################################################################

import threading

class Stack():
    """
    The class allows to work with stack of strings in a text file
    """

    def __init__(self, filepath: str):
        """
        Provide path to file where stack is located.
        """

        self.path = filepath
        self.lock = threading.Lock()


    def is_empty(self):
        """
        Method allows to get state of the stack
        """

        with self.lock:
            with open(self.path, 'r') as fd:
                fd.seek(0, 2)
                return fd.tell() < 1


    def pop(self) -> str:
        """
        Method allow to pop one string from the stack
        """

        with self.lock:
            with open(self.path, 'r+') as fd:
                # go to last character in the file
                fd.seek(0, 2)
                end = fd.tell()

                pos = end
                buf = ""
                nlpos = -1
                # for better performance it should be a little bit more than medium line size
                chunk_size = 1

                # while not found
                while nlpos == -1 and pos != 0:
                    pos -= chunk_size

                    # if it is a last string in the file
                    if pos < 0: pos = 0

                    fd.seek(pos)
                    buf = fd.read(chunk_size) + buf

                    # try to find nl (newline) symbol, except last
                    nlpos = buf.rfind("\n", 0, -1)

                pos = pos + nlpos + 1
                fd.seek(pos)
                fd.truncate()

                if buf == "":
                    return None
                else:
                    buf = buf[nlpos+1:-1]
                    return buf


    def push(self, line: str):
        """
        Method allow to push one string to the stack
        """

        with self.lock:
            with open(self.path, 'a') as fd:
                fd.write(line + "\n")


def main():
    """
    Example of usage. It will create 10 items in stack,
    and will pop it
    """

    FILENAME = "./testfile.sql"
    stack = Stack(FILENAME)

    for i in range(1,10):
        stack.push(f"push-{i}")

    while not stack.is_empty():
        print(stack.pop())


if __name__ == "__main__":
    main()