FileHiker 0.3.9.2__py3-none-any.whl

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.
Files changed (30) hide show
  1. filehiker/__init__.py +4 -0
  2. filehiker/__main__.py +249 -0
  3. filehiker/cases/20260806o1211.sorted.txt +49 -0
  4. filehiker/cases/20260806o1213.invert.py +30 -0
  5. filehiker/cases/20260806o1215.inverted.txt +49 -0
  6. filehiker/cases/README.md +24 -0
  7. filehiker/cases/^^file-to-ignore^^.txt +3 -0
  8. filehiker/cases/^^folder-to-ignore^^/more123.txt +3 -0
  9. filehiker/cases/dir_2/file_01_abc.txt +1 -0
  10. filehiker/cases/dir_2b.txt +1 -0
  11. filehiker/cases/dir_3/dir_31/dir_311/^^dir_3111^^/file_03_more.txt +3 -0
  12. filehiker/cases/dir_3/dir_31/dir_311/dir_3113/file_04_bcd.txt +1 -0
  13. filehiker/cases/dir_3/dir_31/file_02_abc.txt +1 -0
  14. filehiker/cases/dir_4.txt +1 -0
  15. filehiker/cases/dir_5/dir_51/dir_511/dir_5111/file_08_k.txt +1 -0
  16. filehiker/cases/dir_5/dir_51/dir_511/dir_5111/file_10_m.txt +1 -0
  17. filehiker/cases/dir_5/dir_51/dir_511/dir_5111/file_11_n.txt +1 -0
  18. filehiker/cases/dir_5/dir_52/dir_521/dir_5212/file_08_g.txt +1 -0
  19. filehiker/cases/dir_5/dir_52/file_07_c.txt +1 -0
  20. filehiker/cases/dir_5/dir_53/dir_531/dir_5311/dir_53111/dir_531111/file_12_x.txt +1 -0
  21. filehiker/cases/dir_5/dir_53/dir_531/dir_5311/dir_53111/dir_531111/file_13_y.txt +1 -0
  22. filehiker/cases/dir_5/dir_53/dir_531/dir_5311/dir_53111/dir_531111/file_14_z.txt +1 -0
  23. filehiker/cases/dir_5/file_05_a.txt +1 -0
  24. filehiker/cases/dir_5/file_06_h.txt +1 -0
  25. filehiker/cases/dir_5/file_07_p.txt +1 -0
  26. filehiker/filehiker.py +833 -0
  27. filehiker-0.3.9.2.dist-info/METADATA +226 -0
  28. filehiker-0.3.9.2.dist-info/RECORD +30 -0
  29. filehiker-0.3.9.2.dist-info/WHEEL +5 -0
  30. filehiker-0.3.9.2.dist-info/top_level.txt +1 -0
filehiker/__init__.py ADDED
@@ -0,0 +1,4 @@
1
+ # file 20250406°0715 filehiker/__init__.py
2
+
3
+ from .filehiker import FileHiker # … [line 20250729°0845]
4
+ __all__ = ['main'] # Optional but recommended
filehiker/__main__.py ADDED
@@ -0,0 +1,249 @@
1
+ ## file 20260811°0711 filehikerdev/src/filehiker/__main__.py
2
+ # summary : This module allows the package to be executed via `python.exe -m filehiker`
3
+ # license : BSD 3-Clause | © 2026 – 2026 Norbert C. Maier
4
+ # status : Proof-of-concept
5
+
6
+
7
+ import os, sys
8
+ import pathlib
9
+ from typing import Optional
10
+
11
+
12
+ ## \entry Function 20260816°1211 retrieve_package_version
13
+ # \brief Retrieve the package version number from the package metadata
14
+ def retrieve_package_version(package_name: str) -> Optional[str] :
15
+
16
+ """
17
+ Retrieve the version of a Python package.
18
+ Works for both installed and local (uninstalled) packages.
19
+
20
+ Args:
21
+ package_name: Name of the package (e.g., "mypackage").
22
+
23
+ Returns:
24
+ Version string if found, otherwise None.
25
+ """
26
+
27
+ # (1) Try importlib.metadata (installed packages, Python 3.8+)
28
+ try:
29
+ from importlib.metadata import version
30
+
31
+ x = version(package_name)
32
+ return x # 🚀
33
+
34
+ except (ImportError, Exception):
35
+ pass
36
+
37
+
38
+ # (2) Try pkg_resources (installed packages, legacy)
39
+ try:
40
+ import pkg_resources
41
+
42
+ x = pkg_resources.get_distribution(package_name).version
43
+ return x # 🚀
44
+
45
+ except (ImportError, Exception):
46
+ pass
47
+
48
+
49
+ # (3) Try pyproject.toml (local development)
50
+ try:
51
+ import tomli
52
+ pyproject_path = Path("pyproject.toml")
53
+ if pyproject_path.exists():
54
+ with open(pyproject_path, "rb") as f:
55
+ pyproject_data = tomli.load(f)
56
+ # Try Poetry format
57
+ if "tool" in pyproject_data and "poetry" in pyproject_data["tool"]:
58
+ x = pyproject_data["tool"]["poetry"]["version"]
59
+ return x # 🚀
60
+ # Try PEP 621 format
61
+ elif "project" in pyproject_data:
62
+ x = pyproject_data["project"]["version"]
63
+ return x # 🚀
64
+ except (ImportError, Exception):
65
+ pass
66
+
67
+
68
+ # (4) Try PKG-INFO (local development, legacy)
69
+ try:
70
+ from email.parser import Parser
71
+ pkg_info_path = Path("PKG-INFO")
72
+ if pkg_info_path.exists():
73
+ with open(pkg_info_path, "r") as f:
74
+ pkg_info = f.read()
75
+ parser = Parser()
76
+ metadata = parser.parsestr(pkg_info)
77
+ x = metadata["Version"]
78
+ return x # 🚀
79
+ except (ImportError, Exception):
80
+ pass
81
+
82
+
83
+ # (5) Try __version__ attribute (common in many packages)
84
+ try:
85
+ module = __import__(package_name)
86
+ x = getattr(module, "__version__", None)
87
+ return x # 🚀
88
+ except (ImportError, AttributeError, Exception):
89
+ pass
90
+
91
+
92
+ # () None of the above tries was successful
93
+ return None
94
+
95
+
96
+ ## \entry Method 20260806°1423 main
97
+ # \brief Perform a selftest…
98
+ def selftest_1_default(b_Use_Builtin_Folder) :
99
+
100
+ import filehiker
101
+
102
+ # () Prepare test constants
103
+ i_Steps_Total = 110 # 30 40 45 60 110
104
+ i_Steps_Per_Direction = 52 # 11 17 45 50 52
105
+
106
+ # () Determine basepath [seq 20260816°0741]
107
+ sBpth = ''
108
+ if not b_Use_Builtin_Folder :
109
+ sBpth = filehiker.FileHiker.normaleis(os.getcwd()) # CWD
110
+
111
+ # () The `selftest = True` parameter is ugly but sorrily necessary, but the easiest way to tell the class about
112
+ fhk = filehiker.FileHiker(basepath = sBpth, verbose = True, selftest = True) # Flag 'selftest = True' is mandatory here
113
+
114
+ # () …
115
+ if True : # Toggle
116
+ fhk.set_IgnoreList([])
117
+
118
+ # () Feedback basepath
119
+ sBasePath = fhk.get_BasePath()
120
+ print(f'[Msg_4151] BasePath = "{sBasePath}"')
121
+
122
+ # () Test variations
123
+ caselist = ['', 'dir_1/', 'dir_2/file_01_abc.txt', 'dir_2b.txt'] # Blank is basefolder, others are inside basefolder
124
+ sTarget = sBasePath + caselist[3] # Select case
125
+
126
+ # () Execute total steps
127
+ iDirecton = 1
128
+ for iTotal in range(1, i_Steps_Total + 1):
129
+
130
+ # () Notification part 1 of 2
131
+ sMsg = f' • {iTotal:<3} {iDirecton:2} {fhk.strip_prefix(sBasePath, sTarget):44} →'
132
+ print(sMsg, end = '', flush = True)
133
+
134
+ # () Prepare next loop
135
+ sNeighbour = fhk.find_neighbour(sTarget) # The payload function
136
+
137
+ ## () Notification part 2 of 2
138
+ print(f' {fhk.strip_prefix(sBasePath, sNeighbour)}')
139
+
140
+
141
+ # () Toggle direction flag?
142
+ if iTotal % i_Steps_Per_Direction == 0:
143
+
144
+ # (.) Yes, toggle direction flag!
145
+ # (.1) Toggle the flag
146
+ bDirection = not fhk.get_Direction()
147
+ fhk.set_Direction(bDirection)
148
+ print(f'[Msg_4153] Toggle direction to "{'Forward' if bDirection else 'Backward'}"')
149
+
150
+ # (.2) Set phase counter
151
+ iDirecton = 0 if bDirection else i_Steps_Per_Direction + 1
152
+
153
+ # (.3) Repeat last target
154
+ sNeighbour = sTarget
155
+
156
+ # () Maintain phase counter
157
+ iDirecton = iDirecton + 1 if fhk.get_Direction() else iDirecton - 1
158
+
159
+ # () Prepare next loop
160
+ sTarget = sNeighbour
161
+
162
+ return 0
163
+
164
+
165
+ # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
166
+ # note 20260816°0821 'Test proceedings'
167
+ # Try the following sequence
168
+ # python.exe -m filehiker — Prints just the help screen
169
+ # python.exe -m filehiker s — Regular run from the base folder
170
+ # python.exe -m filehiker s dir_2 — Regular run from inside base folder
171
+ # python.exe -m filehiker s dir_2/file_01_abc.txt — Regular run from inside base folder ✅
172
+ # python.exe -m filehiker s dir_2/file_02_xxx/ — Run with invalide target folder
173
+ # python.exe -m filehiker s dir_2/file_02_xxx/y.txt — Run with invalide target file
174
+ # python.exe -m filehiker t — Looks different depending on the start folder
175
+ #
176
+ # Te part counter´s output shall forth and back be the same — Does not work yet as wanted
177
+ # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
178
+
179
+
180
+ ## \register Function 20260811°0721 run
181
+ # \brief Dispatch package call …
182
+ # \remark Remember issue 20260811°1021 'Command `python.exe -m filehiker`' …
183
+ def main():
184
+
185
+ sVersion = retrieve_package_version('filehiker')
186
+ sMsg = f'*** Welcome to package FileHiker v{sVersion} ***'
187
+ print(sMsg)
188
+
189
+ # (A) Evaluate arguments [seq 20260816°0711]
190
+ sArg0 = None
191
+ sArg1 = None
192
+ sArg2 = None
193
+ sArg3 = None
194
+ for iCount in range(0, len(sys.argv)):
195
+ match iCount:
196
+ case 0: sArg0 = sys.argv[0] # Is always like e.g. "G:\…\filehikerdev\src\filehiker\__main__.py"
197
+ case 1: sArg1 = sys.argv[1]
198
+ case 2: sArg2 = sys.argv[2]
199
+ case 3: sArg3 = sys.argv[3]
200
+ case _: pass # Actually a program flow error!
201
+ print(f'[Dbg_4155] Arguments: • "{sArg0}"\n • "{sArg1}" • "{sArg2}" • "{sArg3}"')
202
+
203
+ # (B) Dispatch calls [seq 20260816°0721]
204
+ # (B.1) Help [seq 20260816°0723]
205
+ if sArg1 == None or sArg1 == 'h' or sArg1 == 'help' :
206
+ sMsg = '[Msg_4157] FileHiker Help Screen' \
207
+ + '\n 1.1 python.exe -m filehiker — Show this help screen' \
208
+ + '\n 1.2 python.exe -m filehiker help — Show this help screen' \
209
+ + '\n 2 python.exe -m filehiker info — Show environment infos' \
210
+ + '\n 3.1 python.exe -m filehiker self — Selftest on the built-in sample folder' \
211
+ + '\n 3.2 python.exe -m filehiker self x… — Selftest on sample folder with offset x…' \
212
+ + '\n 4.1 python.exe -m filehiker test — Selftest on CWD (where Phyton was called from)' \
213
+ + '\n 4.2 python.exe -m filehiker test x… — Selftest on CWD with offset x…' \
214
+ + '\n For help, info, self and test also the first letter suffices.'
215
+
216
+ print(sMsg)
217
+
218
+ # (B.2) Selftest [seq 20260816°0725]
219
+ elif sArg1 == 'i' or sArg1 == 'info' :
220
+ print('[Msg_4159] FileHiker info feature is yet to be implemented')
221
+
222
+ # (B.3) Selftest [seq 20260816°0727]
223
+ elif sArg1 == 's' or sArg1 == 'self' :
224
+ selftest_1_default(True)
225
+
226
+ # (B.3) Foldertest [seq 20260816°0729]
227
+ elif sArg1 == 't' or sArg1 == 'test' :
228
+ selftest_1_default(False)
229
+
230
+ # (B.4) Invalid argument [seq 20260816°0731]
231
+ else :
232
+ print('[Msg_4163] Invalid argument(s)')
233
+
234
+ # (C) Finish [seq 20260816°0733]
235
+ print('*** Bye FileHiker***')
236
+ return 0
237
+
238
+
239
+ ## \register Sequence 20260811°0713
240
+ # \brief Main idiom
241
+ if __name__ == "__main__":
242
+
243
+ if False : # True False # Toggle for debugging
244
+ if len(sys.argv) < 2 : # Bad workaround, just quicker than fiddling with the VSCodium *.json [seq 20260816°0731]
245
+ sys.argv.append('')
246
+ sys.argv[1] = 's'
247
+ #sys.argv[1] = 't'
248
+
249
+ main()
@@ -0,0 +1,49 @@
1
+ 1 cases
2
+ 2 cases\20260806o1211.sorted.txt
3
+ 3 cases\20260806o1213.invert.py
4
+ 4 cases\20260806o1215.inverted.txt
5
+ 5 cases\^^file-to-ignore^^.txt
6
+ 6 cases\^^folder-to-ignore^^
7
+ 7 cases\^^folder-to-ignore^^\more123.txt
8
+ 8 cases\dir_1
9
+ 9 cases\dir_2
10
+ 10 cases\dir_2\file_01_abc.txt
11
+ 11 cases\dir_2b.txt
12
+ 12 cases\dir_3
13
+ 13 cases\dir_3\dir_31
14
+ 14 cases\dir_3\dir_31\dir_311
15
+ 15 cases\dir_3\dir_31\dir_311\^^dir_3111^^
16
+ 16 cases\dir_3\dir_31\dir_311\^^dir_3111^^\file_03_more.txt
17
+ 17 cases\dir_3\dir_31\dir_311\dir_3112
18
+ 18 cases\dir_3\dir_31\dir_311\dir_3113
19
+ 19 cases\dir_3\dir_31\dir_311\dir_3113\file_04_bcd.txt
20
+ 20 cases\dir_3\dir_31\dir_311\dir_3114
21
+ 21 cases\dir_3\dir_31\file_02_abc.txt
22
+ 22 cases\dir_4.txt
23
+ 23 cases\dir_5
24
+ 24 cases\dir_5\dir_51
25
+ 25 cases\dir_5\dir_51\dir_511
26
+ 26 cases\dir_5\dir_51\dir_511\dir_5111
27
+ 27 cases\dir_5\dir_51\dir_511\dir_5111\dir_51111
28
+ 28 cases\dir_5\dir_51\dir_511\dir_5111\file_08_k.txt
29
+ 29 cases\dir_5\dir_51\dir_511\dir_5111\file_10_m.txt
30
+ 30 cases\dir_5\dir_51\dir_511\dir_5111\file_11_n.txt
31
+ 31 cases\dir_5\dir_52
32
+ 32 cases\dir_5\dir_52\dir_521
33
+ 33 cases\dir_5\dir_52\dir_521\dir_5211
34
+ 34 cases\dir_5\dir_52\dir_521\dir_5212
35
+ 35 cases\dir_5\dir_52\dir_521\dir_5212\file_08_g.txt
36
+ 36 cases\dir_5\dir_52\file_07_c.txt
37
+ 37 cases\dir_5\dir_53
38
+ 38 cases\dir_5\dir_53\dir_531
39
+ 39 cases\dir_5\dir_53\dir_531\dir_5311
40
+ 40 cases\dir_5\dir_53\dir_531\dir_5311\dir_53111
41
+ 41 cases\dir_5\dir_53\dir_531\dir_5311\dir_53111\dir_531111
42
+ 42 cases\dir_5\dir_53\dir_531\dir_5311\dir_53111\dir_531111\dir_5311111
43
+ 43 cases\dir_5\dir_53\dir_531\dir_5311\dir_53111\dir_531111\file_12_x.txt
44
+ 44 cases\dir_5\dir_53\dir_531\dir_5311\dir_53111\dir_531111\file_13_y.txt
45
+ 45 cases\dir_5\dir_53\dir_531\dir_5311\dir_53111\dir_531111\file_14_z.txt
46
+ 46 cases\dir_5\file_05_a.txt
47
+ 47 cases\dir_5\file_06_h.txt
48
+ 48 cases\dir_5\file_07_p.txt
49
+ 49 cases\README.md
@@ -0,0 +1,30 @@
1
+ # file : 20260806o1213.invert.py
2
+ # summary : Invert the order of the lines of a textfile
3
+ # license : BSD 3-Clause | © 2026 – 2026 Norbert C. Maier
4
+ # status : It works
5
+
6
+
7
+ sFILE_INPUT = './20260806o1211.sorted.txt'
8
+ sFILE_OUTPUT = './20260806o1215.inverted.txt'
9
+
10
+
11
+ def invert_file_lines(input_file_path, output_file_path) : # [func 20260806°1321]
12
+
13
+ with open(input_file_path, 'r') as file:
14
+ lines = file.readlines()
15
+
16
+ inverted_lines = lines[::-1]
17
+
18
+ with open(output_file_path, 'w') as file:
19
+ file.writelines(inverted_lines)
20
+
21
+
22
+ def main() : # [func 20260806°1331]
23
+
24
+ invert_file_lines(sFILE_INPUT, sFILE_OUTPUT)
25
+
26
+
27
+ if __name__ == '__main__': # Main idiom [seq 20260806°1311]
28
+
29
+ #i = main(sys.argv)
30
+ i = main()
@@ -0,0 +1,49 @@
1
+ cases\README.md
2
+ cases\dir_5\file_07_p.txt
3
+ cases\dir_5\file_06_h.txt
4
+ cases\dir_5\file_05_a.txt
5
+ cases\dir_5\dir_53\dir_531\dir_5311\dir_53111\dir_531111\file_14_z.txt
6
+ cases\dir_5\dir_53\dir_531\dir_5311\dir_53111\dir_531111\file_13_y.txt
7
+ cases\dir_5\dir_53\dir_531\dir_5311\dir_53111\dir_531111\file_12_x.txt
8
+ cases\dir_5\dir_53\dir_531\dir_5311\dir_53111\dir_531111\dir_5311111
9
+ cases\dir_5\dir_53\dir_531\dir_5311\dir_53111\dir_531111
10
+ cases\dir_5\dir_53\dir_531\dir_5311\dir_53111
11
+ cases\dir_5\dir_53\dir_531\dir_5311
12
+ cases\dir_5\dir_53\dir_531
13
+ cases\dir_5\dir_53
14
+ cases\dir_5\dir_52\file_07_c.txt
15
+ cases\dir_5\dir_52\dir_521\dir_5212\file_08_g.txt
16
+ cases\dir_5\dir_52\dir_521\dir_5212
17
+ cases\dir_5\dir_52\dir_521\dir_5211
18
+ cases\dir_5\dir_52\dir_521
19
+ cases\dir_5\dir_52
20
+ cases\dir_5\dir_51\dir_511\dir_5111\file_11_n.txt
21
+ cases\dir_5\dir_51\dir_511\dir_5111\file_10_m.txt
22
+ cases\dir_5\dir_51\dir_511\dir_5111\file_08_k.txt
23
+ cases\dir_5\dir_51\dir_511\dir_5111\dir_51111
24
+ cases\dir_5\dir_51\dir_511\dir_5111
25
+ cases\dir_5\dir_51\dir_511
26
+ cases\dir_5\dir_51
27
+ cases\dir_5
28
+ cases\dir_4.txt
29
+ cases\dir_3\dir_31\file_02_abc.txt
30
+ cases\dir_3\dir_31\dir_311\dir_3114
31
+ cases\dir_3\dir_31\dir_311\dir_3113\file_04_bcd.txt
32
+ cases\dir_3\dir_31\dir_311\dir_3113
33
+ cases\dir_3\dir_31\dir_311\dir_3112
34
+ cases\dir_3\dir_31\dir_311\^^dir_3111^^\file_03_more.txt
35
+ cases\dir_3\dir_31\dir_311\^^dir_3111^^
36
+ cases\dir_3\dir_31\dir_311
37
+ cases\dir_3\dir_31
38
+ cases\dir_3
39
+ cases\dir_2b.txt
40
+ cases\dir_2\file_01_abc.txt
41
+ cases\dir_2
42
+ cases\dir_1
43
+ cases\^^folder-to-ignore^^\more123.txt
44
+ cases\^^folder-to-ignore^^
45
+ cases\^^file-to-ignore^^.txt
46
+ cases\20260806o1215.inverted.txt
47
+ cases\20260806o1213.invert.py
48
+ cases\20260806o1211.sorted.txt
49
+ cases
@@ -0,0 +1,24 @@
1
+ [Home](./../README.md)
2
+
3
+ # FileHiker → Cases <span class="lmp-title-type">Demo-Data</span>
4
+
5
+ This are some files and folders for the FileHiker **selftest** …
6
+
7
+ The demo folder shall cover edge cases like
8
+ - Empty folder
9
+ - Foldername with blank *`Still missing`*
10
+ - Filename with blank *`Still missing`*
11
+ - Ignored file
12
+ - Ignored folder
13
+ - Invalid file path — Must be done by the caller
14
+ - Invalid folder path — Must be done by the caller
15
+ - Symlinks — *`Still missing`*
16
+ - …
17
+
18
+ About the ignore cases.
19
+ For ignored files and folders the **pattern `^^*^^`** was choosen, because the
20
+ real-world pattern `__*__` is already used by the repositories ignore feature,
21
+ so the real-world pattern files would not even be committed.
22
+
23
+
24
+ <sup>Page 20260725°1311 ⬞Ω</sup>
@@ -0,0 +1,3 @@
1
+ This file shall be ignored by FileHiker
2
+
3
+ [file 20260725°1231]
@@ -0,0 +1,3 @@
1
+ This file shall be ignored because it resides in an ignored folder
2
+
3
+ [file 20260725°1233]
@@ -0,0 +1 @@
1
+ abc.txt
@@ -0,0 +1 @@
1
+ This is dir_2b.txt
@@ -0,0 +1,3 @@
1
+ This file shall be ignored because it resides in an ignored folder
2
+
3
+ [file 20260725°1235]
@@ -0,0 +1 @@
1
+ abc
@@ -0,0 +1 @@
1
+ This is dir_4.txt
@@ -0,0 +1 @@
1
+ File 'file_08_k.txt'
@@ -0,0 +1 @@
1
+ c.txt
@@ -0,0 +1 @@
1
+ a.txt
@@ -0,0 +1 @@
1
+ h.txt
@@ -0,0 +1 @@
1
+ p.txt