Skip to content

Commit 716c231

Browse files
committed
Update
file: (Add param<is_auto_create> & param<is_occupy> to class<File> constructor, support setting whether to automatically create the file if it does not exist and enable file lock. Get file attribute information from property:dict<info> instead of each individual property. Add method<create> to class<File>), doc, __init__:Add var<__all__>.
1 parent 270d807 commit 716c231

File tree

5 files changed

+138
-64
lines changed

5 files changed

+138
-64
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![License](https://img.shields.io/pypi/l/easierfile.svg)](https://github.com/leoweyr/Python-Easierfile/blob/main/LICENSE)
66
[![Downloads](https://static.pepy.tech/personalized-badge/easierfile?period=total&units=international_system&left_color=grey&right_color=green&left_text=pypi%20downloads)](https://pepy.tech/project/easierfile)
77

8-
A simple Python package that object-oriented encapsulates Python traditional built-in file operations. It's also easier than `easygui`. o((>ω< ))o
8+
An easier-to-use Python package that object-oriented encapsulates Python traditional built-in file operations. It's also easier than `easygui`. o((>ω< ))o
99

1010
## ⚖️License
1111

doc/API reference.md

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,45 +3,44 @@
33
### Constructor
44

55
```python
6-
File(file_path)
6+
File(file_path, is_auto_create, is_occupy)
77
```
88

9-
| Parameter | Type | Default | Description |
10-
| --------- | ------ | ------- | ------------------------------------------------------------ |
11-
| file_path | string | | The absolute path or relative path of the file. If using a relative path, it must include `.` or `/`. |
9+
| Parameter | Type | Default | Description |
10+
| -------------- | ------ | ------- | ------------------------------------------------------------ |
11+
| file_path | string | | The absolute path or relative path of the file. |
12+
| is_auto_create | bool | True | Whether to automatically create the file if it does not exist. |
13+
| is_occupy | bool | True | Whether to enable file lock. |
1214

1315
### Attributes
1416

1517
<table>
1618
<tr>
17-
<td><code>File.content</code></td>
19+
<td><code>File().content</code></td>
1820
<td>Return the entire content of the file.</td>
1921
</tr>
2022
<tr>
21-
<td><code>File.full_name</code></td>
22-
<td>Return the name of the file.</td>
23-
</tr>
24-
<tr>
25-
<td><code>File.name</code></td>
26-
<td>Return the name of the file without the file extension.</td>
27-
</tr>
28-
<tr>
29-
<td><code>File.ext</code></td>
30-
<td>Return the file extension of the file.</td>
23+
<td><code>File().info</code></td>
24+
<td>Return a dict of file attribute information.</br>● path: the absolute path of the file</br>● dir_path: the absolute path of the directory where the file is located</br>● full_name: the name of the file</br>● name: the name of the file without file extension</br>● ext: the extension of the file</td>
3125
</tr>
3226
</table>
3327

3428

29+
3530
### Operation
3631

37-
#### File.rewrite(content)
32+
#### File().create()
3833

39-
Rewrite the entire content of the file.
34+
Create file.
4035

41-
#### File.append(content)
36+
#### File().delete()
4237

43-
Append content at the end of file content.
38+
Delete file.
39+
40+
#### File().rewrite(content)
4441

45-
#### File.delete()
42+
Rewrite the entire content of the file.
4643

47-
Delete file.
44+
#### File().append(content)
45+
46+
Append content at the end of file content.

easierfile/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
from .file import (
22
File
3-
)
3+
)
4+
5+
__all__ = [
6+
"File"
7+
]

easierfile/file.py

Lines changed: 109 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,121 @@
1+
import sys
2+
3+
# This module's built-in functions about file lock.
4+
if sys.platform.startswith("win"): # For Windows.
5+
import msvcrt
6+
7+
def _lock_file(file):
8+
msvcrt.locking(file.fileno(), msvcrt.LK_RLCK, 0)
9+
10+
def _unlock_file(file):
11+
msvcrt.locking(file.fileno(), msvcrt.LK_UNLCK, 0)
12+
13+
else: # For Linux, Unix, MacOS.
14+
import fcntl
15+
16+
def _lock_file(file):
17+
fcntl.flock(file.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
18+
19+
def _unlock_file(file):
20+
fcntl.flock(file.fileno(), fcntl.LOCK_UN)
21+
22+
123
import os
224

25+
326
class File:
4-
def __init__(self, file_path):
5-
self.__m_file_path = file_path
6-
self.__m_file_full_name = os.path.basename(self.__m_file_path)
7-
self.__m_file_name, self.__m_file_extension = os.path.splitext(self.__m_file_full_name)
8-
self.__m_dir_path = os.path.dirname(self.__m_file_path)
9-
10-
if os.path.exists(self.__m_dir_path) == False: # If the directory of the file path does not exist, it will be created.
11-
os.mkdir(self.__m_dir_path)
12-
try: # If the file does not exist, it will be created.
13-
self.__m_file = open(self.__m_file_path, "x")
14-
except FileExistsError:
15-
pass # Indicates that the file exists and no further exception handling is required.
16-
else:
17-
self.__m_file.close()
27+
def __init__(self, file_path, is_auto_create=True, is_occupy=True):
28+
# Get static file information.
29+
self.__m_info = {}
30+
self.__m_info["path"] = os.path.abspath(file_path)
31+
self.__m_info["dir_path"] = os.path.dirname(self.__m_info["path"])
32+
self.__m_info["full_name"] = os.path.basename(self.__m_info["path"])
33+
self.__m_info["name"], self.__m_info["ext"] = os.path.splitext(self.__m_info["full_name"])
34+
self.__m_info["ext"] = self.__m_info["ext"].split(".")[-1] # Format ext of info to remove str<.>.
1835

19-
@property
20-
def content(self):
21-
self.__m_file = open(self.__m_file_path, "r")
22-
self.__m_content = self.__m_file.read()
23-
self.__m_file.close()
24-
return self.__m_content
36+
self.__m_is_occupy = is_occupy
37+
self.__m_is_lock = False # Initialize file lock state.
2538

26-
@property
27-
def full_name(self):
28-
return self.__m_file_full_name
39+
if is_auto_create:
40+
try:
41+
self.create()
42+
except FileExistsError: # Indicates that the file exists and no further exception handling is required.
43+
pass
2944

30-
@property
31-
def name(self):
32-
return self.__m_file_name
45+
if is_occupy and (not self.__m_is_lock):
46+
self.__lock(True)
3347

34-
@property
35-
def ext(self):
36-
return self.__m_file_extension.split(".")[-1]
48+
def __del__(self):
49+
# Release file lock.
50+
if self.__m_is_lock:
51+
try:
52+
self.__lock(False)
53+
except FileNotFoundError: # Indicates that the file lock does not exist and no further exception handling is required.
54+
pass
55+
56+
def __lock(self, is_lock):
57+
if os.path.isfile(self.__m_info["path"]):
58+
if is_lock:
59+
self.__m_file = open(self.__m_info["path"], "w")
60+
_lock_file(self.__m_file)
61+
self.__m_is_lock = is_lock
62+
else:
63+
_unlock_file(self.__m_file)
64+
self.__m_file.close()
65+
self.__m_is_lock = not is_lock
66+
else:
67+
if is_lock:
68+
raise FileNotFoundError("File was about to be occupied, but not found: " + self.__m_info["path"])
69+
else:
70+
raise FileNotFoundError("File was about to be unoccupied, but not found: " + self.__m_info["path"])
71+
72+
def create(self):
73+
if not os.path.isfile(self.__m_info["path"]):
74+
if not os.path.exists(self.__m_info["dir_path"]): # Create the file directory if it doesn't exist, so that code<open()> doesn't throw the exception.
75+
os.mkdir(self.__m_info["dir_path"])
76+
try:
77+
file_temp = open(self.__m_info["path"], "x")
78+
except FileExistsError: # Avoid exception caused by creating corresponding file in other ways during program execution intervals.
79+
pass
80+
else:
81+
file_temp.close()
82+
if self.__m_is_occupy:
83+
self.__lock(True)
84+
else:
85+
raise FileExistsError("File exists: " + self.__m_info["path"])
86+
87+
def delete(self):
88+
if self.__m_is_lock:
89+
self.__lock(False)
90+
if os.path.isfile(self.__m_info["path"]):
91+
os.remove(self.__m_info["path"])
3792

3893
def rewrite(self,content):
39-
self.__m_file = open(self.__m_file_path, "w")
40-
self.__m_file.write(content)
41-
self.__m_file.close()
94+
if os.path.isfile(self.__m_info["path"]):
95+
file_temp = open(self.__m_info["path"], "w")
96+
file_temp.write(content)
97+
file_temp.close()
98+
else:
99+
raise FileNotFoundError("File not found: " + self.__m_info["path"])
42100

43101
def append(self,content):
44-
self.__m_file = open(self.__m_file_path, "a")
45-
self.__m_file.write(content)
46-
self.__m_file.close()
102+
if os.path.isfile(self.__m_info["path"]):
103+
file_temp = open(self.__m_info["path"], "a")
104+
file_temp.write(content)
105+
file_temp.close()
106+
else:
107+
raise FileNotFoundError("File not found: " + self.__m_info["path"])
47108

48-
def delete(self):
49-
if os.path.exists(self.__m_file_path):
50-
os.remove(self.__m_file_path)
109+
@property
110+
def content(self):
111+
if os.path.isfile(self.__m_info["path"]):
112+
file_temp = open(self.__m_info["path"], "r")
113+
content = file_temp.read()
114+
file_temp.close()
115+
return content
116+
else:
117+
raise FileNotFoundError("File not found: " + self.__m_info["path"])
118+
119+
@property
120+
def info(self):
121+
return self.__m_info

pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
[tool.poetry]
22
name = "easierfile"
3-
version = "1.1.0"
4-
description = "A simple Python package that object-oriented encapsulates Python traditional built-in file operations."
3+
version = "2.0.0"
4+
description = "An easier-to-use Python package that object-oriented encapsulates Python traditional built-in file operations."
55
license = "MIT"
66
authors = ["leoweyr <[email protected]>"]
77
readme = "README.md"
88
repository = "https://github.com/leoweyr/Python-Easierfile"
99
classifiers = [
10-
"Development Status :: 3 - Alpha"
10+
"Development Status :: 4 - Beta"
1111
]
1212
packages = [{include = "easierfile"}]
1313

0 commit comments

Comments
 (0)