reny 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.
Files changed (46) hide show
  1. reny-1.0.0/LICENSE +11 -0
  2. reny-1.0.0/PKG-INFO +131 -0
  3. reny-1.0.0/README.md +114 -0
  4. reny-1.0.0/pyproject.toml +3 -0
  5. reny-1.0.0/reny/__init__.py +0 -0
  6. reny-1.0.0/reny/cli/__init__.py +0 -0
  7. reny-1.0.0/reny/cli/base/__init__.py +0 -0
  8. reny-1.0.0/reny/cli/base/bmp_dispatch.py +60 -0
  9. reny-1.0.0/reny/cli/base/bmp_options.py +342 -0
  10. reny-1.0.0/reny/cli/base/vchk.py +47 -0
  11. reny-1.0.0/reny/cli/renamer/__init__.py +0 -0
  12. reny-1.0.0/reny/cli/renamer/renamer_dispatch.py +144 -0
  13. reny-1.0.0/reny/cli/renamer/renamer_options.py +364 -0
  14. reny-1.0.0/reny/commons/__init__.py +0 -0
  15. reny-1.0.0/reny/commons/chainedhandler.py +102 -0
  16. reny-1.0.0/reny/commons/descriptors.py +173 -0
  17. reny-1.0.0/reny/commons/progressbar.py +154 -0
  18. reny-1.0.0/reny/commons/taskprocessor.py +149 -0
  19. reny-1.0.0/reny/commons/utils.py +204 -0
  20. reny-1.0.0/reny/fstools/__init__.py +0 -0
  21. reny-1.0.0/reny/fstools/builders/__init__.py +0 -0
  22. reny-1.0.0/reny/fstools/builders/fsb.py +221 -0
  23. reny-1.0.0/reny/fstools/builders/fsentry.py +60 -0
  24. reny-1.0.0/reny/fstools/builders/fsprms.py +372 -0
  25. reny-1.0.0/reny/fstools/dirtools.py +389 -0
  26. reny-1.0.0/reny/fstools/fsutils.py +272 -0
  27. reny-1.0.0/reny/fstools/rename.py +403 -0
  28. reny-1.0.0/reny/fstools/virtual_organizer.py +301 -0
  29. reny-1.0.0/reny/fstools/walker.py +79 -0
  30. reny-1.0.0/reny.egg-info/PKG-INFO +131 -0
  31. reny-1.0.0/reny.egg-info/SOURCES.txt +44 -0
  32. reny-1.0.0/reny.egg-info/dependency_links.txt +1 -0
  33. reny-1.0.0/reny.egg-info/entry_points.txt +2 -0
  34. reny-1.0.0/reny.egg-info/requires.txt +5 -0
  35. reny-1.0.0/reny.egg-info/top_level.txt +2 -0
  36. reny-1.0.0/setup.cfg +4 -0
  37. reny-1.0.0/setup.py +23 -0
  38. reny-1.0.0/tests/__init__.py +0 -0
  39. reny-1.0.0/tests/base/__init__.py +0 -0
  40. reny-1.0.0/tests/base/test_base.py +92 -0
  41. reny-1.0.0/tests/commons/__init__.py +0 -0
  42. reny-1.0.0/tests/commons/test_commons.py +141 -0
  43. reny-1.0.0/tests/fs/__init__.py +0 -0
  44. reny-1.0.0/tests/fs/test_fs_base.py +32 -0
  45. reny-1.0.0/tests/fs/test_fs_organize.py +763 -0
  46. reny-1.0.0/tests/fs/test_fsutils.py +251 -0
reny-1.0.0/LICENSE ADDED
@@ -0,0 +1,11 @@
1
+ ## Copyright (c) 2026 Arseniy Kuznetsov
2
+ ##
3
+ ## This program is free software; you can redistribute it and/or
4
+ ## modify it under the terms of the GNU General Public License
5
+ ## as published by the Free Software Foundation; either version 2
6
+ ## of the License, or (at your option) any later version.
7
+ ##
8
+ ## This program is distributed in the hope that it will be useful,
9
+ ## but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ ## GNU General Public License for more details.
reny-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.4
2
+ Name: reny
3
+ Version: 1.0.0
4
+ Summary: A lightweight, powerful batch renaming and filesystem organizing CLI tool.
5
+ Description-Content-Type: text/markdown
6
+ License-File: LICENSE
7
+ Requires-Dist: pygtrie
8
+ Provides-Extra: test
9
+ Requires-Dist: pytest; extra == "test"
10
+ Requires-Dist: pytest-mock; extra == "test"
11
+ Dynamic: description
12
+ Dynamic: description-content-type
13
+ Dynamic: license-file
14
+ Dynamic: provides-extra
15
+ Dynamic: requires-dist
16
+ Dynamic: summary
17
+
18
+ # Reny
19
+ A lightweight, fast, and safe batch renaming and filesystem organization tool.
20
+
21
+ ## Background
22
+ `reny` was originally created as the `renamer` component inside the larger [`batchmp`](https://github.com/akpw/batch-mp-tools) (Batch Media Processing) suite. It was spun off into its own standalone package to provide for an ultra-lightweight, safe, pure-filesystem organizing tool without FFmpeg / Mutagen media dependencies.
23
+
24
+ If you need advanced media operations (like denoising, cover-art extraction, or format transcoding), check out the original [`batchmp`](https://github.com/akpw/batch-mp-tools) project. If you just need to safely organize your files with surgical precision, `reny` is all you need.
25
+
26
+ ## Installation
27
+ You can install `reny` globally using pipx:
28
+ ```bash
29
+ pipx install .
30
+ ```
31
+
32
+ ## Features
33
+ - **Virtual Views:** Preview how a directory structure would look when reorganised by type, size, or date without moving or changing anything
34
+ - **Indexing:** Multi-level indexing across nested directories, supporting multiple indexing schemes
35
+ - **Padding:** Automatically pad existing numbers in filenames with leading zeros to fix sorting orders
36
+ - **Flattening:** Safely collapse nested directory structures into a single folder
37
+ - **Regex Replacement:** Powerful batch renaming using standard regular expressions
38
+ - **Dry-Run by Default:** `reny` will always visualize targeted changes and ask for confirmation before it ever touches your files
39
+
40
+ ## Examples
41
+
42
+ ### Basic Operations
43
+ **Print current directory structure:**
44
+ ```bash
45
+ reny
46
+ ```
47
+ *(Without arguments, `reny` defaults to the `print` command)*
48
+
49
+ **Add a sequential index to all `.txt` files recursively:**
50
+ ```bash
51
+ reny -r -in '*.txt' index
52
+ ```
53
+
54
+ **Pad existing numbers with leading zeros (e.g., `2 kms.png` becomes `02 kms.png`):**
55
+ ```bash
56
+ reny pad -md 2
57
+ ```
58
+
59
+ **Regex Replace:**
60
+ Change spaces to underscores in all filenames:
61
+ ```bash
62
+ reny replace -fs ' ' -rs '_'
63
+ ```
64
+
65
+ ### Advanced Operations & Virtual Views
66
+ **Flattening nested directories:**
67
+ Collapse all sub-directories and bring their files up to the current folder level:
68
+ ```bash
69
+ reny flatten
70
+ ```
71
+
72
+ **Organize by File Type:**
73
+ Move files into sub-directories grouped by their file extension (e.g., `png/`, `pdf/`):
74
+ ```bash
75
+ reny organize -b type
76
+ ```
77
+
78
+ **Virtual Views (Dry-Run Preview):**
79
+ Preview how files would look if organized by year and month, *without actually moving any files on your drive*:
80
+ ```bash
81
+ reny print -b date --date-format "%Y/%m"
82
+ ```
83
+ ```text
84
+ Virtual view by date:
85
+ ~/Downloads
86
+ |- 2025/
87
+ |- 01/
88
+ |- document.pdf
89
+ |- 02/
90
+ |- image.png
91
+ ```
92
+
93
+ ## Documentation & Tutorials
94
+ Although `reny` is a standalone project, its core organizing logic is inherited directly from [`batchmp`](https://github.com/akpw/batch-mp-tools). You can find detailed tutorials and deep-dives on how to master its capabilities in the original blog posts:
95
+ - [Renamer Organize & Virtual Views](https://akpw.github.io/articles/2025/09/22/Print-and-Organize.html) – *Highly recommended reading for mastering virtual directory views*
96
+ - [BatchMP Tools Tutorial, Part II: renaming files with renamer](https://akpw.github.io/articles/2015/04/11/batchmp-tutorial-part-ii.html)
97
+ - [Practical BatchMP Series](https://akpw.github.io//tags.html#BatchMP+Tools)
98
+
99
+ ## Usage
100
+ Run `reny --help` to see all available filesystem operations!
101
+
102
+ ## Development
103
+ To set up the project for development:
104
+
105
+ 1. Clone the repository and navigate into it:
106
+ ```bash
107
+ git clone https://github.com/akpw/reny.git
108
+ cd reny
109
+ ```
110
+ 2. Create and activate a virtual environment:
111
+ ```bash
112
+ python3 -m venv .venv
113
+ source .venv/bin/activate
114
+ ```
115
+ 3. Install the project in editable mode along with testing dependencies:
116
+ ```bash
117
+ pip install -e ".[test]"
118
+ ```
119
+
120
+ ## Running Tests
121
+ The project uses `pytest` for its test suite. Because `reny` performs real filesystem operations, the tests are designed to dynamically create and clean up safe temporary sandbox folders during execution.
122
+
123
+ To run the full test suite:
124
+ ```bash
125
+ pytest -v --tb=short tests/
126
+ ```
127
+
128
+ To run a specific test file:
129
+ ```bash
130
+ pytest tests/fs/test_fs_organize.py
131
+ ```
reny-1.0.0/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # Reny
2
+ A lightweight, fast, and safe batch renaming and filesystem organization tool.
3
+
4
+ ## Background
5
+ `reny` was originally created as the `renamer` component inside the larger [`batchmp`](https://github.com/akpw/batch-mp-tools) (Batch Media Processing) suite. It was spun off into its own standalone package to provide for an ultra-lightweight, safe, pure-filesystem organizing tool without FFmpeg / Mutagen media dependencies.
6
+
7
+ If you need advanced media operations (like denoising, cover-art extraction, or format transcoding), check out the original [`batchmp`](https://github.com/akpw/batch-mp-tools) project. If you just need to safely organize your files with surgical precision, `reny` is all you need.
8
+
9
+ ## Installation
10
+ You can install `reny` globally using pipx:
11
+ ```bash
12
+ pipx install .
13
+ ```
14
+
15
+ ## Features
16
+ - **Virtual Views:** Preview how a directory structure would look when reorganised by type, size, or date without moving or changing anything
17
+ - **Indexing:** Multi-level indexing across nested directories, supporting multiple indexing schemes
18
+ - **Padding:** Automatically pad existing numbers in filenames with leading zeros to fix sorting orders
19
+ - **Flattening:** Safely collapse nested directory structures into a single folder
20
+ - **Regex Replacement:** Powerful batch renaming using standard regular expressions
21
+ - **Dry-Run by Default:** `reny` will always visualize targeted changes and ask for confirmation before it ever touches your files
22
+
23
+ ## Examples
24
+
25
+ ### Basic Operations
26
+ **Print current directory structure:**
27
+ ```bash
28
+ reny
29
+ ```
30
+ *(Without arguments, `reny` defaults to the `print` command)*
31
+
32
+ **Add a sequential index to all `.txt` files recursively:**
33
+ ```bash
34
+ reny -r -in '*.txt' index
35
+ ```
36
+
37
+ **Pad existing numbers with leading zeros (e.g., `2 kms.png` becomes `02 kms.png`):**
38
+ ```bash
39
+ reny pad -md 2
40
+ ```
41
+
42
+ **Regex Replace:**
43
+ Change spaces to underscores in all filenames:
44
+ ```bash
45
+ reny replace -fs ' ' -rs '_'
46
+ ```
47
+
48
+ ### Advanced Operations & Virtual Views
49
+ **Flattening nested directories:**
50
+ Collapse all sub-directories and bring their files up to the current folder level:
51
+ ```bash
52
+ reny flatten
53
+ ```
54
+
55
+ **Organize by File Type:**
56
+ Move files into sub-directories grouped by their file extension (e.g., `png/`, `pdf/`):
57
+ ```bash
58
+ reny organize -b type
59
+ ```
60
+
61
+ **Virtual Views (Dry-Run Preview):**
62
+ Preview how files would look if organized by year and month, *without actually moving any files on your drive*:
63
+ ```bash
64
+ reny print -b date --date-format "%Y/%m"
65
+ ```
66
+ ```text
67
+ Virtual view by date:
68
+ ~/Downloads
69
+ |- 2025/
70
+ |- 01/
71
+ |- document.pdf
72
+ |- 02/
73
+ |- image.png
74
+ ```
75
+
76
+ ## Documentation & Tutorials
77
+ Although `reny` is a standalone project, its core organizing logic is inherited directly from [`batchmp`](https://github.com/akpw/batch-mp-tools). You can find detailed tutorials and deep-dives on how to master its capabilities in the original blog posts:
78
+ - [Renamer Organize & Virtual Views](https://akpw.github.io/articles/2025/09/22/Print-and-Organize.html) – *Highly recommended reading for mastering virtual directory views*
79
+ - [BatchMP Tools Tutorial, Part II: renaming files with renamer](https://akpw.github.io/articles/2015/04/11/batchmp-tutorial-part-ii.html)
80
+ - [Practical BatchMP Series](https://akpw.github.io//tags.html#BatchMP+Tools)
81
+
82
+ ## Usage
83
+ Run `reny --help` to see all available filesystem operations!
84
+
85
+ ## Development
86
+ To set up the project for development:
87
+
88
+ 1. Clone the repository and navigate into it:
89
+ ```bash
90
+ git clone https://github.com/akpw/reny.git
91
+ cd reny
92
+ ```
93
+ 2. Create and activate a virtual environment:
94
+ ```bash
95
+ python3 -m venv .venv
96
+ source .venv/bin/activate
97
+ ```
98
+ 3. Install the project in editable mode along with testing dependencies:
99
+ ```bash
100
+ pip install -e ".[test]"
101
+ ```
102
+
103
+ ## Running Tests
104
+ The project uses `pytest` for its test suite. Because `reny` performs real filesystem operations, the tests are designed to dynamically create and clean up safe temporary sandbox folders during execution.
105
+
106
+ To run the full test suite:
107
+ ```bash
108
+ pytest -v --tb=short tests/
109
+ ```
110
+
111
+ To run a specific test file:
112
+ ```bash
113
+ pytest tests/fs/test_fs_organize.py
114
+ ```
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
File without changes
File without changes
File without changes
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env python
2
+ # coding=utf8
3
+ ## Copyright (c) 2014 Arseniy Kuznetsov
4
+ ##
5
+ ## This program is free software; you can redistribute it and/or
6
+ ## modify it under the terms of the GNU General Public License
7
+ ## as published by the Free Software Foundation; either version 2
8
+ ## of the License, or (at your option) any later version.
9
+ ##
10
+ ## This program is distributed in the hope that it will be useful,
11
+ ## but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ ## GNU General Public License for more details.
14
+
15
+ from importlib import metadata
16
+ import reny.cli.base.vchk
17
+ from reny.cli.base.bmp_options import BatchMPArgParser, BatchMPBaseCommands
18
+
19
+
20
+ class BatchMPDispatcher:
21
+ ''' Base BatchMP Commands Dispatcher
22
+ '''
23
+ def __init__(self):
24
+ self.option_parser = BatchMPArgParser()
25
+
26
+ # Dispatcher
27
+ def dispatch(self):
28
+ args = self.option_parser.parse_options()
29
+
30
+ if args['sub_cmd'] == BatchMPBaseCommands.VERSION:
31
+ self.print_version()
32
+
33
+ elif args['sub_cmd'] == BatchMPBaseCommands.INFO:
34
+ self.print_info()
35
+
36
+ else:
37
+ # nothing to dispatch
38
+ return False
39
+
40
+ return True
41
+
42
+ # Dispatched methods
43
+ def print_version(self):
44
+ ''' Prints BatchMP version info
45
+ '''
46
+ version = metadata.version("reny")
47
+ print('BatchMP tools version {}'.format(version))
48
+
49
+ def print_info(self):
50
+ print('\nBatch Media Processing Tools: {}'.format(self.option_parser.script_name))
51
+ print(self.option_parser.description)
52
+
53
+ def main():
54
+ ''' BatchMP entry point
55
+ '''
56
+ BatchMPDispatcher().dispatch()
57
+
58
+ if __name__ == '__main__':
59
+ main()
60
+
@@ -0,0 +1,342 @@
1
+ #!/usr/bin/env python
2
+ # coding=utf8
3
+ ## Copyright (c) 2014 Arseniy Kuznetsov
4
+ ##
5
+ ## This program is free software; you can redistribute it and/or
6
+ ## modify it under the terms of the GNU General Public License
7
+ ## as published by the Free Software Foundation; either version 2
8
+ ## of the License, or (at your option) any later version.
9
+ ##
10
+ ## This program is distributed in the hope that it will be useful,
11
+ ## but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ ## GNU General Public License for more details.
14
+
15
+
16
+ """ Global options parsing:
17
+ [-r, --recursive] Recurse into nested folders
18
+ [-el, --end-level] End level for recursion into nested folders
19
+
20
+ [-in, --include] Include names pattern (Unix style)
21
+ [-ex, --exclude] Exclude names pattern (Unix style)
22
+ (excludes hidden files by default)
23
+ [-ad, --all-dirs] Prevent using Include/Exclude patterns on directories
24
+ [-af, --all-files] Prevent using Include/Exclude patterns on files
25
+ (shows hidden files excluded by default)
26
+
27
+ [-s, --sort]{na|nd|sa|sd} Sort order for files / folders (name | date, asc | desc)
28
+ [-ni, nested-indent] Indent for printing nested directories
29
+ [-q, --quiet] Do not visualise changes / show messages during processing
30
+ """
31
+
32
+ import os, sys, string
33
+ from argparse import ArgumentParser, HelpFormatter
34
+ from reny.commons.utils import strtobool
35
+ from urllib.parse import urlparse
36
+ from reny.commons.utils import MiscHelpers
37
+ from reny.fstools.fsutils import FSH
38
+ from reny.fstools.builders.fsentry import FSEntry, FSEntryDefaults
39
+
40
+
41
+
42
+ class BatchMPBaseCommands:
43
+ VERSION = 'version'
44
+ INFO = 'info'
45
+ PRINT = 'print'
46
+
47
+ @classmethod
48
+ def commands_meta(cls):
49
+ return ''.join(('{',
50
+ '{}, '.format(cls.INFO),
51
+ '{}'.format(cls.VERSION),
52
+ '}'))
53
+
54
+ class BatchMPArgParser:
55
+ def __init__(self):
56
+ self._script_name = 'Reny'
57
+ self._description = '''
58
+ Reny provides management of files, directories, etc...
59
+
60
+ Reny tools consist of three main command-line utilities.
61
+ For more information, run:
62
+ $ renamer -h
63
+ $ tagger -h
64
+ $ bmfp -h
65
+ '''
66
+
67
+ @property
68
+ def description(self):
69
+ return self._description
70
+
71
+ @property
72
+ def script_name(self):
73
+ return self._script_name
74
+
75
+ # Args parsing
76
+ def parse_options(self):
77
+ ''' Common workflow for parsing options
78
+ '''
79
+ parser = ArgumentParser(prog = self._script_name, description = self._description,
80
+ formatter_class=BatchMPHelpFormatter)
81
+
82
+ self.parse_global_options(parser)
83
+
84
+ self.parse_commands(parser)
85
+
86
+ args = vars(parser.parse_args())
87
+
88
+ self.check_args(args, parser)
89
+
90
+ return args
91
+
92
+ def parse_global_options(self, parser):
93
+ ''' Parses global options
94
+ '''
95
+ source_mode_group = parser.add_argument_group('Input source mode')
96
+ source_mode_group.add_argument("-d", "--dir", dest = "dir",
97
+ type = lambda d: self._is_valid_dir_path(parser, d),
98
+ help = "Source directory (default is current directory)",
99
+ default = os.curdir)
100
+ source_mode_group.add_argument("-f", "--file", dest = "file",
101
+ type = lambda f: self._is_valid_file_path(parser, f),
102
+ help = "File to process")
103
+
104
+ recursive_mode_group = parser.add_argument_group('Recursion mode')
105
+ recursive_mode_group.add_argument("-r", "--recursive", dest = "recursive",
106
+ help = "Recurse into nested folders",
107
+ action = 'store_true')
108
+ recursive_mode_group.add_argument("-el", "--end-level", dest = "end_level",
109
+ help = "End level for recursion into nested folders",
110
+ type = int,
111
+ default = 0)
112
+
113
+ include_mode_group = parser.add_argument_group('Filter files or folders')
114
+ include_mode_group.add_argument("-in", "--include", dest = "include",
115
+ help = "Include: Unix-style name patterns separated by ';'",
116
+ type = str,
117
+ default = FSEntryDefaults.DEFAULT_INCLUDE)
118
+ include_mode_group.add_argument("-ex", "--exclude", dest = "exclude",
119
+ help = "Exclude: Unix-style name patterns separated by ';' (excludes hidden files by default)",
120
+ type = str,
121
+ default = FSEntryDefaults.DEFAULT_EXCLUDE)
122
+ include_mode_group.add_argument("-ad", "--all-dirs", dest = "all_dirs",
123
+ help = "Disable Include/Exclude patterns on directories",
124
+ action = 'store_true')
125
+ include_mode_group.add_argument("-af", "--all-files", dest = "all_files",
126
+ help = "Disable Include/Exclude patterns on files (shows hidden files excluded by default)",
127
+ action = 'store_true')
128
+
129
+ media_types_group = parser.add_argument_group('File media types')
130
+ media_types_group.add_argument("-ft", "--file-type", dest = "file_type",
131
+ help = "File Media Type",
132
+ type = str,
133
+ choices = ['image', 'video', 'audio', 'media', 'nonmedia', 'playable', 'nonplayable', 'any'],
134
+ default = FSEntryDefaults.DEFAULT_FILE_TYPE)
135
+ media_types_group.add_argument("-ms", "--media-scan", dest = "media_scan",
136
+ help = "Scan for media types, instead of using file extensions (can take a long time)",
137
+ action = 'store_true')
138
+
139
+
140
+ # Add Default Miscellaneous Group
141
+ self._add_arg_misc_group(parser)
142
+
143
+ def parse_commands(self, parser):
144
+ ''' Specific commands parsing
145
+ '''
146
+ subparsers = parser.add_subparsers(dest = 'sub_cmd',
147
+ title = 'BatchMP commands',
148
+ metavar = BatchMPBaseCommands.commands_meta())
149
+ self._add_version(subparsers)
150
+ self._add_info(subparsers)
151
+
152
+ # Args checking
153
+ def check_cmd_args(self, args, parser,
154
+ show_help = False,
155
+ exit = False):
156
+ if not args.get('sub_cmd'):
157
+ if show_help:
158
+ parser.print_help()
159
+ if exit:
160
+ sys.exit(1)
161
+
162
+ # if not exiting, need to default
163
+ self.default_command(args, parser)
164
+
165
+ def default_command(self, args, parser):
166
+ args['sub_cmd'] = BatchMPBaseCommands.INFO
167
+
168
+ def check_args(self, args, parser):
169
+ ''' Validation of supplied CLI arguments
170
+ '''
171
+ # check if there is a cmd to execute
172
+ self.check_cmd_args(args, parser)
173
+
174
+ # if input source is a file, need to adjust
175
+ if args['file']:
176
+ args['dir'] = os.path.dirname(args['file'])
177
+ args['include'] = os.path.basename(args['file'])
178
+ args['exclude'] = ''
179
+ args['end_level'] = 0
180
+ args['all_files'] = False
181
+ args['all_dirs'] = False
182
+
183
+ # check recursion
184
+ if args['recursive'] and args['end_level'] == 0:
185
+ args['end_level'] = sys.maxsize
186
+
187
+
188
+ if args['media_scan']:
189
+ pass
190
+
191
+ if args['sub_cmd'] == BatchMPBaseCommands.PRINT:
192
+ if args['start_level'] != 0:
193
+ if args['file']:
194
+ print ('Start Level parameter requires a source directory\n Ignoring requested Start Level...')
195
+ args['start_level'] = 0
196
+ elif args['end_level'] < args['start_level']:
197
+ ''' print ('Start Level should be greater than or equal to the Recursion End Level Global Option\n'
198
+ '... Adjusting End Level to: {}'.format(args['start_level']))
199
+ '''
200
+ args['end_level'] = args['start_level']
201
+
202
+ # Internal Helpers
203
+ @staticmethod
204
+ def _is_valid_dir_path(parser, path_arg):
205
+ """ Checks if path_arg is a valid dir path
206
+ """
207
+ path_arg = FSH.full_path(path_arg)
208
+ if not (os.path.exists(path_arg) and os.path.isdir(path_arg)):
209
+ parser.error('"{}" does not seem to be an existing directory path'.format(path_arg))
210
+ else:
211
+ return path_arg
212
+
213
+ @staticmethod
214
+ def _is_valid_file_path(parser, path_arg):
215
+ """ Checks if path_arg is a valid file path
216
+ """
217
+ path_arg = FSH.full_path(path_arg)
218
+ if not (os.path.exists(path_arg) and os.path.isfile(path_arg)):
219
+ parser.error('"{}" does not seem to be an existing file path'.format(path_arg))
220
+ else:
221
+ return path_arg
222
+
223
+ @staticmethod
224
+ def _is_boolean(parser, bool_arg):
225
+ """ Checks if bool_arg can be interpreted as a boolean value
226
+ """
227
+ try:
228
+ bool_arg = True if strtobool(bool_arg) else False
229
+ except ValueError:
230
+ parser.error('"{}": Please enter a boolean value'.format(bool_arg))
231
+ return False
232
+
233
+ @staticmethod
234
+ def _is_valid_url(parser, url_arg):
235
+ url_parts = urlparse(url_arg)
236
+
237
+ def _parser_error():
238
+ parser.error('"{}": Please enter a valid URL'.format(url_arg))
239
+
240
+ if url_parts.scheme in (None, '') and url_parts.netloc in (None, ''):
241
+ _parser_error()
242
+
243
+ if url_parts.scheme == 'file':
244
+ if url_parts.netloc == '~':
245
+ fpath = '~{}'.format(url_parts.path)
246
+ else:
247
+ fpath = url_parts.path
248
+ return BatchMPArgParser._is_valid_file_path(parser, fpath)
249
+
250
+ if not set(url_parts.netloc).issubset(set(string.ascii_letters + string.digits + '-.')):
251
+ _parser_error()
252
+
253
+ if not url_parts.scheme in ['http', 'https', 'ftp', 'file']:
254
+ _parser_error()
255
+
256
+ return url_arg
257
+
258
+ @staticmethod
259
+ def _is_valid_url_or_file_path(parser, url_or_file_path_arg):
260
+ url_parts = urlparse(url_or_file_path_arg)
261
+ if url_parts.scheme in (None, '') and url_parts.netloc in (None, ''):
262
+ return BatchMPArgParser._is_valid_file_path(parser, url_or_file_path_arg)
263
+ else:
264
+ return BatchMPArgParser._is_valid_url(parser, url_or_file_path_arg)
265
+
266
+ @staticmethod
267
+ def _is_timedelta(parser, td_arg):
268
+ try:
269
+ td = MiscHelpers.time_delta(td_arg)
270
+ except ValueError:
271
+ parser.error('"{}": Please enter a valid value, ' \
272
+ 'in seconds or in the "hh:mm:ss[.xxx]" format'.format(td_arg))
273
+ return td
274
+
275
+ # Processing mode for relevant commands
276
+ @staticmethod
277
+ def _add_arg_display_curent_state_mode(parser):
278
+ parser.add_argument('-dc', '--display-current', dest = 'display_current',
279
+ help ='Unless in quiet mode, display current (pre-processing) state in the confirmation propmt',
280
+ action = 'store_true')
281
+
282
+ @staticmethod
283
+ def _add_arg_misc_group(parser):
284
+ misc_group = parser.add_argument_group('Miscellaneous')
285
+ misc_group.add_argument('-s', '--sort', dest = 'sort',
286
+ help = "Sorting for files ('na', i.e. by name ascending by default)",
287
+ type = str,
288
+ choices = ['na', 'nd', 'sa', 'sd'],
289
+ default = FSEntryDefaults.DEFAULT_SORT)
290
+ misc_group.add_argument('-ni', '--nested_indent', dest = 'nested_indent',
291
+ help = "Indent for printing nested directories",
292
+ type = str,
293
+ default = ' ')
294
+ misc_group.add_argument("-q", "--quiet", dest = 'quiet',
295
+ help = "Disable visualising changes & displaying info messages during processing",
296
+ action = 'store_true')
297
+
298
+ @staticmethod
299
+ def _add_version(parser):
300
+ ''' Adds the version command
301
+ '''
302
+ parser.add_parser(BatchMPBaseCommands.VERSION,
303
+ description = 'Displays BatchMP version info',
304
+ formatter_class=BatchMPHelpFormatter)
305
+
306
+ @staticmethod
307
+ def _add_info(parser):
308
+ ''' Adds the info command
309
+ '''
310
+ parser.add_parser(BatchMPBaseCommands.INFO,
311
+ description = 'Displays BatchMP info',
312
+ formatter_class=BatchMPHelpFormatter)
313
+
314
+
315
+
316
+ class BatchMPHelpFormatter(HelpFormatter):
317
+ ''' Custom ArgumentParser formatter
318
+ Disables double metavar display, showing it only for long-named options
319
+ '''
320
+ def _format_action_invocation(self, action):
321
+ if not action.option_strings:
322
+ metavar, = self._metavar_formatter(action, action.dest)(1)
323
+ return metavar
324
+ else:
325
+ parts = []
326
+ # if the Optional doesn't take a value, format is:
327
+ # -s, --long
328
+ if action.nargs == 0:
329
+ parts.extend(action.option_strings)
330
+
331
+ # if the Optional takes a value, format is:
332
+ # -s ARGS, --long ARGS
333
+ # change to
334
+ # -s, --long ARGS
335
+ else:
336
+ default = action.dest.upper()
337
+ args_string = self._format_args(action, default)
338
+ for option_string in action.option_strings:
339
+ #parts.append('%s %s' % (option_string, args_string))
340
+ parts.append('%s' % option_string)
341
+ parts[-1] += ' %s'%args_string
342
+ return ', '.join(parts)