filertk 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
filertk-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dylan Alves Santos
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
filertk-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.4
2
+ Name: filertk
3
+ Version: 1.0.0
4
+ Summary: package used for rewriting certain aspects of text in files
5
+ Author-email: Dylan Santos <dasantos726@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/dylanasantos/filertk
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.14
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: colorama>=0.4.6
14
+ Dynamic: license-file
15
+
16
+ # filertk 1.0.0
17
+ Filertk is a simple terminal-based system that allows for quick basic file rewrite instructions that would take excessive work by hand, such as renaming variables.\
18
+ \
19
+ **To activate:** simply write "filertk" within your terminal and paste in the file directory you want to modify. Afterwards input your commands when requested.\
20
+ - All file modifications are located in a directory inside of <ins>filertk\results</ins>
21
+
22
+ ## Commands
23
+ filertk has 17 different commands displayed at each launch of the program. These commands are used to specify the desired modifications to the file or to the results directory.
24
+
25
+ > [!NOTE]
26
+ > The general order of commands used does not matter. However, the identifiers you provide are a first-come-first-serve basis.
27
+
28
+ ### Identifiers
29
+ Identifiers are used to inform filertk what exactly you are referencing in your commands, such as the text you want to find or replace.
30
+
31
+ - **Name Identifiers "":** Used for specifying the text to match, rewrite, or exclude
32
+ - **Range Identifiers (x-y):** Used for specifying the lines from start-end you want to either exclude or include
33
+ <br><br>
34
+
35
+ ### Special Commands
36
+ - **exit:** closes filertk program
37
+ - **clear_res:** clears the entire file results directory, first displays all files being deleted and requests for permission
38
+ - **clear "filename.ext":** clears the specified file from results directory, first displays file direcotry and requests for permission
39
+ <br><br>
40
+
41
+ ### Reader Commands
42
+ Commands used to inform filertk how the text to match should be interpereted.
43
+
44
+ > [!NOTE]
45
+ > Reader Commands do not handle casing sensitivity, only character matching, refer to Casing Commands for more info.
46
+
47
+ - **match "text":** finds any words that contain the text
48
+ - **exact "text":** only finds words that contain the exact text
49
+ - **excluding "text" or (start-end):** excludes any matches of the word found (only used in match type) or ignores lines specified
50
+ - **from (start-end):** will only read/modify the lines specified (start and end are inclusive)
51
+ <br><br>
52
+
53
+ ### Writer Commands
54
+ Commands used to inform filertk how the matches found should be rewritten.
55
+
56
+ - **with "text":** removes and replaces the new text only with what matched (ex: demo_1 -> new_1)
57
+ - **replace "text":** removes the entire text with the match found and inserts the new text (ex: demo_1 -> new)
58
+ - **r_append "text":** appends the new text to the end of the match (ex: demo_1 -> demo_1new)
59
+ - **f_append "text":** appends the new text to the front of the match (ex: demo_1 -> newdemo_1)
60
+ <br><br>
61
+
62
+ ### Casing Commands
63
+ Commands used to inform filertk how the text casing should be handled.
64
+
65
+ - **!ignore_case:** ignores the casing of matches, any and all capitalizations are treated the same via the reader but are replaced by the exact casing of the writer identifier input.
66
+ - **!keep_case:** will attempt to copy the exact casing via index-by-index (this is only recommended for matches and writes of the same size), will automatically lowercase and uppercase all chars regardless if reader and writer identifier sizes match.<br>
67
+ - **!camel_case:** given the exact casing of the user's writer identifier, will replace all words with said input UNLESS the match if full capitalized or lowercased. If so, it will match the casing.
68
+
69
+ ### File Copy Command
70
+ Commands used on how the file modifications should be handled.
71
+
72
+ - **~h_copy:** creates a copy of the exact file name inside of the results directory. (filertk defaults to this, but will display a warning if defaulted)
73
+ - **~modify:** modifies the original file specified, creates an emergency backup inside of the results directory if the output is unwanted.
74
+ - **~s_copy "filename.ext":** creates a copy of the file with a custom name provided inside of the results directory.
@@ -0,0 +1,59 @@
1
+ # filertk 1.0.0
2
+ Filertk is a simple terminal-based system that allows for quick basic file rewrite instructions that would take excessive work by hand, such as renaming variables.\
3
+ \
4
+ **To activate:** simply write "filertk" within your terminal and paste in the file directory you want to modify. Afterwards input your commands when requested.\
5
+ - All file modifications are located in a directory inside of <ins>filertk\results</ins>
6
+
7
+ ## Commands
8
+ filertk has 17 different commands displayed at each launch of the program. These commands are used to specify the desired modifications to the file or to the results directory.
9
+
10
+ > [!NOTE]
11
+ > The general order of commands used does not matter. However, the identifiers you provide are a first-come-first-serve basis.
12
+
13
+ ### Identifiers
14
+ Identifiers are used to inform filertk what exactly you are referencing in your commands, such as the text you want to find or replace.
15
+
16
+ - **Name Identifiers "":** Used for specifying the text to match, rewrite, or exclude
17
+ - **Range Identifiers (x-y):** Used for specifying the lines from start-end you want to either exclude or include
18
+ <br><br>
19
+
20
+ ### Special Commands
21
+ - **exit:** closes filertk program
22
+ - **clear_res:** clears the entire file results directory, first displays all files being deleted and requests for permission
23
+ - **clear "filename.ext":** clears the specified file from results directory, first displays file direcotry and requests for permission
24
+ <br><br>
25
+
26
+ ### Reader Commands
27
+ Commands used to inform filertk how the text to match should be interpereted.
28
+
29
+ > [!NOTE]
30
+ > Reader Commands do not handle casing sensitivity, only character matching, refer to Casing Commands for more info.
31
+
32
+ - **match "text":** finds any words that contain the text
33
+ - **exact "text":** only finds words that contain the exact text
34
+ - **excluding "text" or (start-end):** excludes any matches of the word found (only used in match type) or ignores lines specified
35
+ - **from (start-end):** will only read/modify the lines specified (start and end are inclusive)
36
+ <br><br>
37
+
38
+ ### Writer Commands
39
+ Commands used to inform filertk how the matches found should be rewritten.
40
+
41
+ - **with "text":** removes and replaces the new text only with what matched (ex: demo_1 -> new_1)
42
+ - **replace "text":** removes the entire text with the match found and inserts the new text (ex: demo_1 -> new)
43
+ - **r_append "text":** appends the new text to the end of the match (ex: demo_1 -> demo_1new)
44
+ - **f_append "text":** appends the new text to the front of the match (ex: demo_1 -> newdemo_1)
45
+ <br><br>
46
+
47
+ ### Casing Commands
48
+ Commands used to inform filertk how the text casing should be handled.
49
+
50
+ - **!ignore_case:** ignores the casing of matches, any and all capitalizations are treated the same via the reader but are replaced by the exact casing of the writer identifier input.
51
+ - **!keep_case:** will attempt to copy the exact casing via index-by-index (this is only recommended for matches and writes of the same size), will automatically lowercase and uppercase all chars regardless if reader and writer identifier sizes match.<br>
52
+ - **!camel_case:** given the exact casing of the user's writer identifier, will replace all words with said input UNLESS the match if full capitalized or lowercased. If so, it will match the casing.
53
+
54
+ ### File Copy Command
55
+ Commands used on how the file modifications should be handled.
56
+
57
+ - **~h_copy:** creates a copy of the exact file name inside of the results directory. (filertk defaults to this, but will display a warning if defaulted)
58
+ - **~modify:** modifies the original file specified, creates an emergency backup inside of the results directory if the output is unwanted.
59
+ - **~s_copy "filename.ext":** creates a copy of the file with a custom name provided inside of the results directory.
@@ -0,0 +1,24 @@
1
+ [project]
2
+ name = "filertk"
3
+ version = "1.0.0"
4
+ dependencies = [
5
+ "colorama>=0.4.6"
6
+ ]
7
+ authors = [
8
+ { name="Dylan Santos", email="dasantos726@gmail.com" },
9
+ ]
10
+ description = "package used for rewriting certain aspects of text in files"
11
+ readme = "README.md"
12
+ requires-python = ">=3.14"
13
+ classifiers = [
14
+ "Programming Language :: Python :: 3",
15
+ "Operating System :: OS Independent",
16
+ ]
17
+ license = "MIT"
18
+ license-files = ["LICEN[CS]E*"]
19
+
20
+ [project.scripts]
21
+ filertk = "filertk.main:main"
22
+
23
+ [project.urls]
24
+ Homepage = "https://github.com/dylanasantos/filertk"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,187 @@
1
+ import re
2
+ from enum import Enum
3
+
4
+ class ReadType(Enum):
5
+ """Enum of all reader OrderTypes"""
6
+ MATCH = 1
7
+ EXACT = 2
8
+
9
+ class WriteType(Enum):
10
+ """Enum of all Writer OrderTypes"""
11
+ WITH = 1
12
+ REPLACE = 2
13
+ R_APPEND = 3
14
+ F_APPEND = 4
15
+
16
+ class CopyType(Enum):
17
+ """Enum of all Copy OrderTypes"""
18
+ HARD = 1
19
+ SOFT = 2
20
+ MODIFY = 3
21
+
22
+ class IdentifierType(Enum):
23
+ """Enum of all identifier OrderTypes"""
24
+ READER = 1
25
+ WRITER = 2
26
+ FROM = 3
27
+ EXCLUDING = 4
28
+
29
+ class CaseType(Enum):
30
+ """Enum of all Casing OrderTypes"""
31
+ KEEP = 1
32
+ IGNORE = 2
33
+ CAMEL = 3
34
+
35
+ class OrderType(Enum):
36
+ READER = 1
37
+ WRITER = 2
38
+ COPY = 3
39
+ IDN = 4
40
+ CASE = 5
41
+
42
+ OrderType.Read = ReadType
43
+ OrderType.Write = WriteType
44
+ OrderType.Copy = CopyType
45
+ OrderType.Idn = IdentifierType
46
+ OrderType.Case = CaseType
47
+
48
+ class CommandReader:
49
+ TOKEN_PATTERN = r'"[^"]*"|\S+'
50
+
51
+ EXIT_CMD = "exit"
52
+ FULL_CLEAR_CMD = "clear_res"
53
+ CLEAR_CMD = "clear"
54
+
55
+ ARG_TYPES = {
56
+ "match": ReadType.MATCH,
57
+ "exact": ReadType.EXACT,
58
+
59
+ "excluding": IdentifierType.EXCLUDING,
60
+ "from": IdentifierType.FROM,
61
+
62
+ "with": WriteType.WITH,
63
+ "replace": WriteType.REPLACE,
64
+ "r_append": WriteType.R_APPEND,
65
+ "f_append": WriteType.F_APPEND,
66
+
67
+ "!keep_case": CaseType.KEEP,
68
+ "!ignore_case": CaseType.IGNORE,
69
+ "!camel_case": CaseType.CAMEL,
70
+
71
+ "~h_copy": CopyType.HARD,
72
+ "~modify": CopyType.MODIFY,
73
+ "~s_copy": CopyType.SOFT
74
+ }
75
+
76
+ def __init__(self, commands: list[str]):
77
+ """Tokenizes & verifies commands given to terminal"""
78
+ self.__args = commands
79
+ self.__instructions = { OrderType.IDN: {}}
80
+ self.__identifier_queue: list[IdentifierType] = []
81
+
82
+ self.set_instructions()
83
+
84
+
85
+ @staticmethod
86
+ def is_name(potential_name: str) -> bool:
87
+ """Used for checking if a name identifier token is provided"""
88
+ if(len(potential_name) <= 2): return False
89
+
90
+ if(potential_name[0] == '"' and potential_name[-1] == '"'): return True
91
+
92
+ return False
93
+
94
+
95
+ @staticmethod
96
+ def is_range(potential_range: str) -> bool:
97
+ """Used for checking if a range identifier token is provided"""
98
+
99
+ if(potential_range[0] == "(" and potential_range[-1] == ")"): return True
100
+
101
+ return False
102
+
103
+
104
+ @staticmethod
105
+ def get_range_nums(range_token: str) -> list[int, int]:
106
+ """grabs ranges for FROM identifier, returns [start, end]"""
107
+
108
+ numbers = re.findall(r'\d+', range_token)
109
+ numbers = [int(num) for num in numbers]
110
+
111
+ if(len(numbers) != 2):
112
+ raise ValueError(
113
+ "CommandReader get_rane_nums() was given an invalid amount of range values provided for FROM, received: "
114
+ + len(numbers))
115
+
116
+ return numbers
117
+
118
+
119
+ def handle_identifier(self, idn: str) -> None:
120
+ """handles identifier queue status & allocation of identifiers with corresponding type"""
121
+
122
+ if(len(idn) < 3):
123
+ raise ValueError("CommandReader handle_identifier() found an identifier with not enough characters.\n"
124
+ + "[identifier: " + idn + "]\n"
125
+ + "make sure identifiers have a starting and ending quotation, with all characters inbetween")
126
+
127
+ if(self.__identifier_queue == 0):
128
+ raise ValueError("CommandReader handle_identifier() found an identifier but did not know where to place it\n"
129
+ + "\t[identifier: " + idn + "]\n"
130
+ + "make sure your identifiers have an identifier type (readers, writers, from, excluding) before writing them")
131
+
132
+ if(idn[0] != '"' or idn[-1] != '"'):
133
+ raise ValueError("CommandReader handle_identifier found an identifier lacking proper quotations.\n"
134
+ + "\t[identifier: " + idn + "]\n"
135
+ + "make sure all identifiers have leading and trailing quotations, with all characters inbetween")
136
+
137
+ self.__instructions[OrderType.IDN][self.__identifier_queue[0]] = idn[1 : -1] #to remove quotations from checks
138
+ self.__identifier_queue.pop(0)
139
+
140
+
141
+ def set_instructions(self):
142
+ """Reads through the args list and updates the instructions dictionary"""
143
+
144
+ for arg in self.__args:
145
+ arg_type = CommandReader.ARG_TYPES.get(arg)
146
+
147
+ if(arg_type):
148
+ match arg_type:
149
+ case ReadType():
150
+ self.__instructions[OrderType.READER] = arg_type
151
+ self.__identifier_queue.append(OrderType.READER)
152
+
153
+ case WriteType():
154
+ self.__instructions[OrderType.WRITER] = arg_type
155
+ self.__identifier_queue.append(OrderType.WRITER)
156
+
157
+ case CopyType():
158
+ self.__instructions[OrderType.COPY] = arg_type
159
+
160
+ if(arg_type == CopyType.SOFT):
161
+ self.__identifier_queue.append(OrderType.COPY)
162
+
163
+ case IdentifierType():
164
+ self.__identifier_queue.append(arg_type)
165
+
166
+ case CaseType():
167
+ self.__instructions[OrderType.CASE] = arg_type
168
+
169
+ elif(CommandReader.is_name(arg)):
170
+ self.handle_identifier(arg)
171
+
172
+ elif(CommandReader.is_range(arg)):
173
+ if(self.__identifier_queue[0] == IdentifierType.EXCLUDING):
174
+ self.__instructions[OrderType.IDN][IdentifierType.EXCLUDING] = CommandReader.get_range_nums(arg)
175
+ self.__identifier_queue.pop(0)
176
+ else:
177
+ self.__instructions[OrderType.IDN][IdentifierType.FROM] = CommandReader.get_range_nums(arg)
178
+ self.__identifier_queue.pop(0)
179
+
180
+ else:
181
+ raise ValueError("CommandReader set_instructions() detected an unexpected token, only use the token types specified"
182
+ + " or ensure text specifications use \" before and after (ex: \"text\")\n"
183
+ + "\t[token interpereted: " + arg + "]")
184
+
185
+
186
+ def get_instructions(self):
187
+ return self.__instructions
@@ -0,0 +1,335 @@
1
+ import shutil
2
+ import re
3
+
4
+ from colorama import Fore, Style
5
+
6
+ from pathlib import Path
7
+
8
+ from .command_reader import CommandReader, OrderType
9
+
10
+ class DataFormatter:
11
+ """Used for cleaner and easier access of individual lines and words inside said lines"""
12
+
13
+ def __init__(self, line_number=0, words=""):
14
+ self.line_number = line_number
15
+ self.words: list[str] = [text for text in re.split(r'(\s+)', words) if text]
16
+
17
+
18
+
19
+ class WordMatches:
20
+ """Used for cleaner and easier access of individual word matches"""
21
+
22
+ def __init__(self, word: str, line_number=0, cpos=0):
23
+ self.word = word
24
+ self.line_number = line_number
25
+ self.cpos = cpos
26
+
27
+
28
+
29
+ class FileManager:
30
+ """Reviews tokenized instructions and documents changes to file"""
31
+
32
+ RESULTS_DIR = Path(__file__).resolve().parent / "results"
33
+ ACCEPT_ANY_CASE = (OrderType.Case.KEEP, OrderType.Case.IGNORE, OrderType.Case.CAMEL)
34
+
35
+ def __init__(self, original: Path, commands: CommandReader):
36
+ self.__path = original
37
+ self.__commands = commands.get_instructions()
38
+
39
+ self.__original_data: list[DataFormatter] = []
40
+ self.read_original()
41
+
42
+ self.__matches: list[WordMatches] = []
43
+ self.__instance_changes_ctr = 0
44
+ self.__camel_ref = self.__commands[OrderType.IDN][OrderType.WRITER] if self.__commands.get(OrderType.CASE) == OrderType.Case.CAMEL else None
45
+
46
+ self.interperit_matcher(self.__commands.get(OrderType.READER))
47
+ self.interperit_writer(self.__commands.get(OrderType.WRITER))
48
+ self.write_to_file()
49
+
50
+
51
+
52
+ def __search_match(self, word: str) -> bool:
53
+ """searches for a match based off of casing condition issued and whether it matches the text"""
54
+
55
+ case_rule = self.__commands.get(OrderType.CASE)
56
+
57
+ match(self.__commands[OrderType.READER]):
58
+ case(OrderType.Read.MATCH):
59
+ if(case_rule in FileManager.ACCEPT_ANY_CASE):
60
+ return self.__commands[OrderType.IDN][OrderType.READER].lower() in word.lower()
61
+
62
+ else:
63
+ return self.__commands[OrderType.IDN][OrderType.READER] in word
64
+
65
+ case(OrderType.Read.EXACT):
66
+ return self.__commands[OrderType.IDN][OrderType.READER] == word
67
+
68
+ raise ValueError("file_manager __search_match() was called with an invalid READER type")
69
+
70
+
71
+
72
+ def __get_word_index(self, word: str) -> int:
73
+ """returns index of match if found inside of word"""
74
+
75
+ search: str = self.__commands[OrderType.IDN][OrderType.READER]
76
+
77
+ case_rule = self.__commands.get(OrderType.CASE)
78
+ if(case_rule in FileManager.ACCEPT_ANY_CASE):
79
+ return word.lower().find(search.lower())
80
+
81
+ return word.find(search)
82
+
83
+
84
+
85
+ def __set_match_casing(self, word: str) -> str:
86
+ """checks if the match inside a word provided is either fully capitalized or lowercased, and returns a string according to said capitalization"""
87
+
88
+ match_index = self.__get_word_index(word)
89
+ word_match = word[match_index : match_index + len(self.__commands[OrderType.IDN][OrderType.READER])]
90
+
91
+ if(word_match.isupper()):
92
+ return self.__commands[OrderType.IDN][OrderType.WRITER].upper()
93
+
94
+ if(word_match.islower()):
95
+ return self.__commands[OrderType.IDN][OrderType.WRITER].lower()
96
+
97
+ return self.__commands[OrderType.IDN][OrderType.WRITER]
98
+
99
+
100
+
101
+ def __with_formatter(self, word: str) -> str:
102
+ """returns the modified word under the with formatting context"""
103
+
104
+ replacement = ""
105
+ index = self.__get_word_index(word)
106
+ to_insert = self.__commands[OrderType.IDN][OrderType.WRITER]
107
+
108
+ if(index > -1):
109
+ replacement = word[0 : index] + to_insert + word[index + len(self.__commands[OrderType.IDN][OrderType.READER]) : ]
110
+ self.__instance_changes_ctr += 1
111
+
112
+ return replacement
113
+
114
+ raise ValueError("file_manager __with_formatter() was ordered to modify a word that does not match reader [unexpected behavior]"
115
+ + "\tpossibly an error with match detection?\n" +
116
+ "[reader idn: " + self.__commands[OrderType.IDN][OrderType.READER] + " word: " + word)
117
+
118
+
119
+
120
+ def __replace_formatter(self, word: str) -> str:
121
+ """returns the modified word under the replace formatting context"""
122
+
123
+ index = self.__get_word_index(word)
124
+ to_insert = self.__commands[OrderType.IDN][OrderType.WRITER]
125
+
126
+ if(index > -1):
127
+ self.__instance_changes_ctr += 1
128
+ return to_insert
129
+
130
+ raise ValueError("file_manager __replace_formatter() was ordered to modify a word that does not match reader [unexpected behavior]"
131
+ + "\tpossibly an error with match detection?\n" +
132
+ "[reader idn: " + self.__commands[OrderType.IDN][OrderType.READER] + " word: " + word)
133
+
134
+
135
+
136
+ def __append_formatter(self, word: str) -> str:
137
+ """returns the modified word under the append formatting context"""
138
+
139
+ replacement = ""
140
+ index = self.__get_word_index(word)
141
+ to_insert = self.__commands[OrderType.IDN][OrderType.WRITER]
142
+
143
+ if(index > -1):
144
+ word_end = index + len(word)
145
+
146
+ if(self.__commands.get(OrderType.Write.R_APPEND)):
147
+ replacement = word[0 : word_end] + to_insert + word[(word_end + len(to_insert)) : ]
148
+ else:
149
+ replacement = word[0 : index] + to_insert + word[index + len(to_insert) : ]
150
+
151
+ self.__instance_changes_ctr += 1
152
+ return replacement
153
+
154
+ raise ValueError("file_manager __append_formatter() was ordered to modify a word that does not match reader [unexpected behavior]"
155
+ + "\tpossibly an error with match detection?\n" +
156
+ "[reader idn: " + self.__commands[OrderType.IDN][OrderType.READER] + " word: " + word)
157
+
158
+
159
+
160
+ def __update_keep_casing(self, match: WordMatches) -> WordMatches:
161
+ """Updates the casing of writer to properly replace text off of KEEP request, returns match inserted (no changes)"""
162
+
163
+ idn: dict[OrderType] = self.__commands[OrderType.IDN]
164
+ original_word: str = self.__original_data[match.line_number - 1].words[match.cpos]
165
+
166
+ new_writer_word = idn[OrderType.WRITER]
167
+
168
+ if(original_word.isupper()): #picks up on capitalizations automatically
169
+ idn[OrderType.WRITER] = new_writer_word.upper()
170
+ return match
171
+
172
+ for i in range(min(len(original_word), len(idn[OrderType.WRITER]))):
173
+ writer_char = new_writer_word[i]
174
+ original_char = original_word[i]
175
+
176
+ if(original_char.isalpha() and writer_char.isalpha()):
177
+ if(original_char.isupper()):
178
+ new_writer_word = new_writer_word[ : i] + writer_char.upper() + new_writer_word[i + 1: ]
179
+ else:
180
+ new_writer_word = new_writer_word[ : i] + writer_char.lower() + new_writer_word[i + 1: ]
181
+
182
+ idn[OrderType.WRITER] = new_writer_word
183
+ return match
184
+
185
+
186
+
187
+ def __update_camel_casing(self, match: WordMatches) -> WordMatches:
188
+ """Updates the casing of the new writing text to match if it is any sort of camel type, including Titles, StanDard, lowercase, and FULL_CAPS"""
189
+
190
+ capital_ctr = 0
191
+ idn: str = self.__commands[OrderType.IDN]
192
+
193
+ idn[OrderType.WRITER] = self.__camel_ref
194
+ original_word: str = self.__original_data[match.line_number - 1].words[match.cpos]
195
+
196
+ casing_change = self.__set_match_casing(match.word)
197
+
198
+ if(casing_change != idn[OrderType.WRITER]):
199
+ idn[OrderType.WRITER] = casing_change
200
+ return match
201
+
202
+ for i in range(len(original_word)):
203
+ original_char = original_word[i]
204
+
205
+ if(original_char.isupper()):
206
+ capital_ctr += 1
207
+
208
+ if(capital_ctr > 1): #any text with > 1 capitalized chars is presumed to be a CamelCase of some sort
209
+ break
210
+ return match
211
+
212
+
213
+
214
+ def __create_backup(self) -> Path:
215
+ """used for creating backup files of original file"""
216
+
217
+ backup_path = self.RESULTS_DIR / (self.__path.stem + "_backup" + self.__path.suffix)
218
+ shutil.copy2(self.__path, backup_path)
219
+
220
+ return backup_path
221
+
222
+
223
+
224
+ def read_original(self):
225
+ """reads thorugh the original file provided"""
226
+ line_num = 0
227
+
228
+ with open(self.__path, 'r') as file:
229
+ for line in file:
230
+ line_num += 1
231
+ self.__original_data.append(DataFormatter(line_num, line))
232
+
233
+
234
+
235
+ def interperit_matcher(self, match_type: OrderType.Read):
236
+ """scans file for matches based off of match type specifier to be modified later"""
237
+
238
+ validator = lambda arg: False
239
+
240
+ range_based = self.__commands[OrderType.IDN].get(OrderType.Idn.FROM)
241
+ excluding = self.__commands[OrderType.IDN].get(OrderType.Idn.EXCLUDING)
242
+
243
+ excluding_handler = lambda _, __: False
244
+ if(isinstance(excluding, str)):
245
+ excluding_handler = lambda arg, _: excluding == arg #range arg in unwated in word exclude type
246
+
247
+ elif(isinstance(excluding, list)):
248
+ excluding_handler = lambda _, arg: arg < excluding[0] or arg > excluding[1] #word arg is unwanted in range exclude type
249
+
250
+
251
+ for data in self.__original_data:
252
+ for word_pos in range(len(data.words)):
253
+ word = data.words[word_pos]
254
+
255
+ if(range_based and (data.line_number < range_based[0] or data.line_number > range_based[1])):
256
+ break
257
+
258
+ if(excluding_handler(word, data.line_number)):
259
+ continue
260
+
261
+ if(self.__search_match(word)):
262
+ self.__matches.append(WordMatches(word, data.line_number, word_pos))
263
+
264
+
265
+
266
+ def interperit_writer(self, write_type: OrderType.WRITER):
267
+ """modifies matches found in file to specified write type"""
268
+
269
+ formatter: str = None
270
+ match(write_type):
271
+ case OrderType.Write.WITH:
272
+ formatter = lambda match: self.__with_formatter(match.word)
273
+
274
+ case OrderType.Write.REPLACE:
275
+ formatter = lambda match: self.__replace_formatter(match.word)
276
+
277
+ case OrderType.Write.APPEND:
278
+ formatter = lambda match: self.__append_formatter(match.word)
279
+
280
+ handler: str = formatter
281
+ if(self.__commands.get(OrderType.CASE) == OrderType.Case.KEEP):
282
+ if(len(self.__commands[OrderType.IDN][OrderType.WRITER]) != len(self.__commands[OrderType.IDN][OrderType.READER])):
283
+ print(Fore.YELLOW + "WARNING: !casing_kp [keep casing to match] request was made, but reader and writer identifiers differ in size.\n" \
284
+ + "\tResults may not be as expected due to size mismatch." + Style.RESET_ALL)
285
+
286
+ handler = lambda match: formatter(self.__update_keep_casing(match))
287
+
288
+ if(self.__commands.get(OrderType.CASE) == OrderType.Case.CAMEL):
289
+ handler = lambda match: formatter(self.__update_camel_casing(match))
290
+
291
+ for match in self.__matches:
292
+ match_result: WordMatches = WordMatches(word=handler(match))
293
+
294
+ while(self.__search_match(match_result.word)):
295
+ match_result.word = handler(match_result)
296
+
297
+ self.__original_data[match.line_number - 1].words[match.cpos] = match_result.word
298
+
299
+ print(f"{Fore.GREEN}{self.__instance_changes_ctr} instance changes made in \033]8;;{self.__path}\033\\'{self.__path}\033]8;;\033\\'")
300
+
301
+
302
+ def write_to_file(self):
303
+ """takes all data collected & modified from original file & writes it into instructed file"""
304
+
305
+ file_mode = self.__commands.get(OrderType.COPY)
306
+ file_path: Path
307
+
308
+ if(not file_mode):
309
+ print(Fore.YELLOW + "WARNING: FileManager write_to_file() detected no copy command from command list.\n"
310
+ + "\tFileManager write_to_file() is defaulting to hard copy [~h_copy]" + Style.RESET_ALL)
311
+
312
+ file_mode = OrderType.Copy.HARD
313
+
314
+ match(file_mode):
315
+ case OrderType.Copy.HARD:
316
+ file_path = FileManager.RESULTS_DIR / self.__path.name
317
+
318
+ case OrderType.Copy.SOFT:
319
+ requested_name = self.__commands[OrderType.IDN].get(OrderType.COPY)
320
+
321
+ if(not requested_name):
322
+ raise ValueError("FileManager write_to_file() was provided with ~s_copy() but recieved no identifier for file name; did you specify one?\n"
323
+ + "\t~s_copy requires a name for the file, make sure you wrote one!")
324
+ file_path = FileManager.RESULTS_DIR / requested_name
325
+
326
+ case OrderType.Copy.MODIFY:
327
+ file_path = self.__path
328
+ backup = self.__create_backup()
329
+
330
+ print(Fore.GREEN + "~modify copy type created a backup file at:", backup)
331
+
332
+ with open(file_path, 'w') as file:
333
+ for data in self.__original_data:
334
+ for word in data.words:
335
+ file.write(word)
@@ -0,0 +1,143 @@
1
+ import re
2
+ import sys
3
+
4
+ from colorama import Fore, Style
5
+
6
+ from pathlib import Path
7
+
8
+ from .file_manager import FileManager
9
+ from .command_reader import CommandReader
10
+
11
+ INSTRUCTIONS = """fileformatter cmdlet:
12
+ SPECIAL:
13
+ exit: exit program early
14
+ clear_res: clears results file
15
+ clear "file.ext": deletes file from results/
16
+
17
+ READ TYPES:
18
+ match "text": find words that contain the text
19
+ exact "text": words that only meet the exact input
20
+ excluding "text" or (num): do not include items of the exact syntax or line number (use for match or from)
21
+ from (start-end): only modify lines specified
22
+
23
+ WRITE TYPES:
24
+ with "text": removes only the text that matches with new text
25
+ replace "text": replaces matches with new text
26
+ r_append "text": appends text to the end of the matches
27
+ f_append "text": appends text to the front of the matches
28
+
29
+ CASING TYPES:
30
+ !keep_case: finds read matches ignoring casing and writes to the same casing style as the replacing text
31
+ !ignore_case: finds read matches ignoring casing, writes each match the same as write casing specified
32
+ !camel_case: with the exact casing of the write provided, it is utilized on all camel cased matches, otherwise fully lowercased/uppercased
33
+
34
+ FILE COPY TYPES:
35
+ ~h_copy: creates a copy of the same file name in results folder [defaulted to this]
36
+ ~modify: modified the original file (a backup is creates in results/)
37
+ ~s_copy "name": creates a copy of the same file with a custom name
38
+
39
+ Writing Commands:
40
+ simply combine the commands you want in no necessary order, for example:
41
+ from (1-10) excluding "var_name2" match "var_name" with "new_name" ~h_copy
42
+ """
43
+
44
+ def full_res_clear() -> None:
45
+ """Clears every file inside of the results folder"""
46
+
47
+ to_delete = []
48
+
49
+ for file_path in FileManager.RESULTS_DIR.iterdir():
50
+ print("\trequested delete: ", file_path)
51
+ to_delete.append(file_path)
52
+
53
+ validate = input("Are you sure you want to delete all these files? [Y/N]: ").strip()
54
+
55
+ if(validate.lower() == "y"):
56
+ for file in to_delete:
57
+ file.unlink(missing_ok=True)
58
+ print('\t' + str(file) + " deleted successfuly")
59
+ print(f"{Fore.GREEN}All files in results deleted!{Style.RESET_ALL}")
60
+ else:
61
+ print(f"{Fore.GREEN}clear_res was canceled{Style.RESET_ALL}")
62
+
63
+
64
+ def specify_res_clear(args: list[str]) -> None:
65
+ """Analyzes the tokenized input provided to allocate the file name specified"""
66
+
67
+ if(len(args) < 2):
68
+ print(f"{Fore.YELLOW}filertk clear command was used, but could not find a file identifier{Style.RESET_ALL}")
69
+ return
70
+
71
+ if(len(args) > 2):
72
+ print(f"{Fore.YELLOW}filertk clear command was used with too many identifiers; only provide the clear command, and the file name inside the reuslts folder{Style.RESET_ALL}")
73
+ return
74
+
75
+ file_idn = args[0] if args[0] != CommandReader.CLEAR_CMD else args[1]
76
+ if(not CommandReader.is_name(file_idn)):
77
+ print(f"{Fore.YELLOW}file identifier requires quotations around to ensure it's properly retrieved, make sure there is double quotes both at the front and end of your file's name{Style.RESET_ALL}")
78
+
79
+ else:
80
+ to_clear: Path = FileManager.RESULTS_DIR / file_idn[1 : -1]
81
+
82
+ if(to_clear.exists):
83
+ to_clear.unlink(missing_ok=True)
84
+ print(f"{Fore.GREEN}{to_clear} successfully deleted{Style.RESET_ALL}")
85
+
86
+ else:
87
+ print(f"{Fore.YELLOW}{to_clear} file was not found; are you sure you typed it correctly?{Style.RESET_ALL}")
88
+ return
89
+
90
+
91
+ def read_input(instructions: str, expect_file_dir=False) -> str:
92
+ user_input = input(instructions)
93
+ result: list[str] = re.findall(CommandReader.TOKEN_PATTERN, user_input)
94
+
95
+ if(CommandReader.EXIT_CMD in result):
96
+ print("exit called, closing filertk...")
97
+ sys.exit()
98
+
99
+ if(CommandReader.FULL_CLEAR_CMD in result):
100
+ full_res_clear()
101
+ return
102
+
103
+ if(CommandReader.CLEAR_CMD in result):
104
+ specify_res_clear(result)
105
+ return
106
+
107
+ if(expect_file_dir):
108
+ if(CommandReader.is_name(user_input)):
109
+ return user_input[1 : -1]
110
+ return user_input
111
+
112
+ return result
113
+
114
+
115
+ def main():
116
+ print(INSTRUCTIONS)
117
+
118
+ user_input: str
119
+ file_path: Path
120
+
121
+ while(True):
122
+ user_input = read_input("Enter a file directory: ", expect_file_dir=True)
123
+
124
+ if(user_input):
125
+ file_path = Path(user_input)
126
+ if(file_path.exists()): break
127
+ else:
128
+ continue
129
+
130
+ print(f"{Fore.YELLOW}\nInvalid file path provided! Make sure your file path is correct\n"
131
+ + f"\t[provided path: {file_path}{Style.RESET_ALL}]")
132
+
133
+ print("path", file_path, "accepted")
134
+
135
+ args: list[str] | bool = None
136
+ while(not args):
137
+ args = read_input("Enter command instructions:$// ")
138
+
139
+ reader = CommandReader(args)
140
+ manager = FileManager(file_path, reader)
141
+
142
+ if __name__ == "__main__":
143
+ main()
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.4
2
+ Name: filertk
3
+ Version: 1.0.0
4
+ Summary: package used for rewriting certain aspects of text in files
5
+ Author-email: Dylan Santos <dasantos726@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/dylanasantos/filertk
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.14
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: colorama>=0.4.6
14
+ Dynamic: license-file
15
+
16
+ # filertk 1.0.0
17
+ Filertk is a simple terminal-based system that allows for quick basic file rewrite instructions that would take excessive work by hand, such as renaming variables.\
18
+ \
19
+ **To activate:** simply write "filertk" within your terminal and paste in the file directory you want to modify. Afterwards input your commands when requested.\
20
+ - All file modifications are located in a directory inside of <ins>filertk\results</ins>
21
+
22
+ ## Commands
23
+ filertk has 17 different commands displayed at each launch of the program. These commands are used to specify the desired modifications to the file or to the results directory.
24
+
25
+ > [!NOTE]
26
+ > The general order of commands used does not matter. However, the identifiers you provide are a first-come-first-serve basis.
27
+
28
+ ### Identifiers
29
+ Identifiers are used to inform filertk what exactly you are referencing in your commands, such as the text you want to find or replace.
30
+
31
+ - **Name Identifiers "":** Used for specifying the text to match, rewrite, or exclude
32
+ - **Range Identifiers (x-y):** Used for specifying the lines from start-end you want to either exclude or include
33
+ <br><br>
34
+
35
+ ### Special Commands
36
+ - **exit:** closes filertk program
37
+ - **clear_res:** clears the entire file results directory, first displays all files being deleted and requests for permission
38
+ - **clear "filename.ext":** clears the specified file from results directory, first displays file direcotry and requests for permission
39
+ <br><br>
40
+
41
+ ### Reader Commands
42
+ Commands used to inform filertk how the text to match should be interpereted.
43
+
44
+ > [!NOTE]
45
+ > Reader Commands do not handle casing sensitivity, only character matching, refer to Casing Commands for more info.
46
+
47
+ - **match "text":** finds any words that contain the text
48
+ - **exact "text":** only finds words that contain the exact text
49
+ - **excluding "text" or (start-end):** excludes any matches of the word found (only used in match type) or ignores lines specified
50
+ - **from (start-end):** will only read/modify the lines specified (start and end are inclusive)
51
+ <br><br>
52
+
53
+ ### Writer Commands
54
+ Commands used to inform filertk how the matches found should be rewritten.
55
+
56
+ - **with "text":** removes and replaces the new text only with what matched (ex: demo_1 -> new_1)
57
+ - **replace "text":** removes the entire text with the match found and inserts the new text (ex: demo_1 -> new)
58
+ - **r_append "text":** appends the new text to the end of the match (ex: demo_1 -> demo_1new)
59
+ - **f_append "text":** appends the new text to the front of the match (ex: demo_1 -> newdemo_1)
60
+ <br><br>
61
+
62
+ ### Casing Commands
63
+ Commands used to inform filertk how the text casing should be handled.
64
+
65
+ - **!ignore_case:** ignores the casing of matches, any and all capitalizations are treated the same via the reader but are replaced by the exact casing of the writer identifier input.
66
+ - **!keep_case:** will attempt to copy the exact casing via index-by-index (this is only recommended for matches and writes of the same size), will automatically lowercase and uppercase all chars regardless if reader and writer identifier sizes match.<br>
67
+ - **!camel_case:** given the exact casing of the user's writer identifier, will replace all words with said input UNLESS the match if full capitalized or lowercased. If so, it will match the casing.
68
+
69
+ ### File Copy Command
70
+ Commands used on how the file modifications should be handled.
71
+
72
+ - **~h_copy:** creates a copy of the exact file name inside of the results directory. (filertk defaults to this, but will display a warning if defaulted)
73
+ - **~modify:** modifies the original file specified, creates an emergency backup inside of the results directory if the output is unwanted.
74
+ - **~s_copy "filename.ext":** creates a copy of the file with a custom name provided inside of the results directory.
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/filertk/__init__.py
5
+ src/filertk/command_reader.py
6
+ src/filertk/file_manager.py
7
+ src/filertk/main.py
8
+ src/filertk.egg-info/PKG-INFO
9
+ src/filertk.egg-info/SOURCES.txt
10
+ src/filertk.egg-info/dependency_links.txt
11
+ src/filertk.egg-info/entry_points.txt
12
+ src/filertk.egg-info/requires.txt
13
+ src/filertk.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ filertk = filertk.main:main
@@ -0,0 +1 @@
1
+ colorama>=0.4.6
@@ -0,0 +1 @@
1
+ filertk