Skip to content

Commit 6d978ed

Browse files
committed
Added FileIO
1 parent a957191 commit 6d978ed

File tree

1 file changed

+174
-0
lines changed

1 file changed

+174
-0
lines changed

filexdb/fileio.py

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import json
2+
import pickle
3+
import os
4+
import io
5+
from abc import ABC, abstractmethod
6+
7+
__all__ = ("FileIO", "JsonFileIO", "BinaryFileIO")
8+
9+
10+
def create_db(db_name: str, data_dir: str | None):
11+
"""
12+
Create a file if it doesn't exist yet.
13+
14+
:param db_name: Database-file to create.
15+
:param data_dir: Where the Database-file will be stored.
16+
"""
17+
18+
# Default Database-file path.
19+
_db_path = db_name
20+
21+
if data_dir is not None:
22+
_db_path = os.path.join(data_dir, db_name)
23+
24+
# Check if path is already exist or not
25+
if not os.path.exists(_db_path):
26+
27+
# Check if we need to create data directories
28+
if not os.path.exists(data_dir):
29+
os.makedirs(data_dir)
30+
31+
# Create the file by opening it in "a" mode which creates the file if it
32+
# does not exist yet but does not modify its contents
33+
with open(_db_path, 'ab'):
34+
pass
35+
36+
37+
class FileIO(ABC):
38+
"""
39+
The abstract base class for all FileIO Classes.
40+
41+
A FileIO (de)serializes the current state of the database and stores it in
42+
some place.
43+
"""
44+
45+
# Using ABCMeta as metaclass allows instantiating only storages that have
46+
# implemented read and write
47+
48+
@abstractmethod
49+
def read(self) -> dict:
50+
"""
51+
Read the current state of the database from the Database-file.
52+
53+
Any kind of deserialization should go here.
54+
55+
Return ``None`` if the Database-file is empty.
56+
"""
57+
58+
raise NotImplementedError('To be overridden!')
59+
60+
@abstractmethod
61+
def write(self, data: dict):
62+
"""
63+
Write the current state of the database to the Database-file.
64+
65+
Any kind of serialization should go here.
66+
67+
:param data: The current state of the database.
68+
"""
69+
70+
raise NotImplementedError('To be overridden!')
71+
72+
73+
class BinaryFileIO(FileIO):
74+
def __init__(self, db_name: str, data_dir: str | None):
75+
"""
76+
Create a new instance.
77+
78+
Also creates the Database-file, if it doesn't exist.
79+
80+
[Recommended] Don't add any file extension
81+
82+
:param db_name: Name of Database
83+
:param data_dir: Where to store the data
84+
"""
85+
86+
super().__init__()
87+
88+
self._data_dir = data_dir
89+
90+
# Adding ``FileXdb`` specific file extention to the Database-file.
91+
self._db_name = f"{db_name}.fxdb"
92+
93+
# Setting default Database-file path.
94+
self._db_file_path = self._db_name
95+
96+
# Checking if Data Directory is on root or not.
97+
if self._data_dir is not None:
98+
99+
# Creating Database-file full path by joining data_dir & db_name.
100+
self._db_file_path = os.path.join(self._data_dir, self._db_name)
101+
102+
# Create the Database/File if it doesn't exist
103+
create_db(self._db_name, self._data_dir)
104+
105+
def read(self) -> dict:
106+
"""
107+
Reads existing Database-file, either it is empty or non-empty.
108+
109+
If empty returns an empty dict, else returns saved Data.
110+
111+
:return: Database as a python Dictionary.
112+
"""
113+
database = None
114+
115+
with open(self._db_file_path, "rb") as file:
116+
# Get the file size by moving the cursor to the file end and reading its location.
117+
file.seek(0, os.SEEK_END)
118+
size = file.tell()
119+
120+
# check if size of file is 0
121+
if size is not 0:
122+
try:
123+
# Load whole Database form Database-file
124+
database = pickle.load(file)
125+
126+
except io.UnsupportedOperation:
127+
# Through an Unsupported Operation Error.
128+
raise IOError(f"Cannot read file.\n\t`{self._db_name}` is not a database")
129+
130+
else:
131+
# Returns an empty dict as
132+
database = {}
133+
134+
return database
135+
136+
def write(self, data: dict) -> None:
137+
"""
138+
Write the current state of entire Database to the Database-file.
139+
140+
:param data: Dictionary object to write on Database.
141+
:return: None.
142+
"""
143+
with open(self._db_file_path, "wb") as file:
144+
145+
# Move the cursor to the beginning of the file just in case.
146+
file.seek(0)
147+
148+
# Serialize the database state using the user-provided arguments
149+
serialized = pickle.dumps(data)
150+
151+
# Write the serialized data to the file
152+
try:
153+
file.write(serialized)
154+
except io.UnsupportedOperation:
155+
raise IOError(f"Cannot write to the file.\n\t`{self._db_name}` is not a database")
156+
157+
# Ensure the file has been written
158+
file.flush()
159+
os.fsync(file.fileno())
160+
161+
# Remove data that is behind the new cursor if the file has gotten shorter.
162+
file.truncate()
163+
164+
165+
class JsonFileIO(FileIO):
166+
def __init__(self, db_name):
167+
super().__init__()
168+
self.db_name = db_name
169+
170+
def read(self) -> dict:
171+
pass
172+
173+
def write(self, data: dict):
174+
pass

0 commit comments

Comments
 (0)