Skip to content

Commit d573e1a

Browse files
committed
Raising warnings for scandir instead of erroring
1 parent 7cdcd84 commit d573e1a

File tree

13 files changed

+100
-82
lines changed

13 files changed

+100
-82
lines changed

.github/workflows/black.yaml

Lines changed: 0 additions & 19 deletions
This file was deleted.

.github/workflows/tests.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ on:
99
jobs:
1010
test:
1111

12+
runs-on: ubuntu-latest
13+
1214
strategy:
1315
matrix:
1416
python-version: [3.5, 3.6, 3.7, 3.8]

reusables/cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727

2828
def cmd(command, ignore_stderr=False, raise_on_return=False, timeout=None, encoding="utf-8"):
29-
""" Run a shell command and have it automatically decoded and printed
29+
"""Run a shell command and have it automatically decoded and printed
3030
3131
:param command: Command to run as str
3232
:param ignore_stderr: To not print stderr
@@ -99,7 +99,7 @@ def ls(params="", directory=".", printed=True):
9999

100100

101101
def find(name=None, ext=None, directory=".", match_case=False, disable_glob=False, depth=None):
102-
""" Designed for the interactive interpreter by making default order
102+
"""Designed for the interactive interpreter by making default order
103103
of find_files faster.
104104
105105
:param name: Part of the file name

reusables/file_operations.py

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import shutil
1616
from collections import defaultdict
1717
from pathlib import Path
18+
import warnings
1819

1920
try:
2021
import ConfigParser as ConfigParser
@@ -53,6 +54,17 @@
5354

5455
logger = logging.getLogger("reusables")
5556

57+
scandir_warning_given = False
58+
59+
60+
def scandir_warning():
61+
global scandir_warning_given
62+
scandir_warning_given = True
63+
warnings.warn(
64+
'"enable_scandir" option will be removed in the next release of Reusables.'
65+
' Please remove all references to "enable_scandir" or version lock to Reusables < 1.0.0'
66+
)
67+
5668

5769
def extract(archive_file, path=".", delete_on_success=False, enable_rar=False):
5870
"""
@@ -124,7 +136,7 @@ def archive(
124136
allow_zip_64=True,
125137
**tarfile_kwargs,
126138
):
127-
""" Archive a list of files (or files inside a folder), can chose between
139+
"""Archive a list of files (or files inside a folder), can chose between
128140
129141
- zip
130142
- tar
@@ -166,8 +178,7 @@ def archive(
166178
raise ValueError(err_msg)
167179
logger.debug("{0} file detected for {1}".format(archive_type, name))
168180
elif archive_type not in ("tar", "gz", "bz2", "zip", "lzma"):
169-
err_msg = ("archive_type must be zip, gz, bz2,"
170-
" or gz, was {0}".format(archive_type))
181+
err_msg = "archive_type must be zip, gz, bz2, lzma, or gz, was {0}".format(archive_type)
171182
logger.error(err_msg)
172183
raise ValueError(err_msg)
173184

@@ -183,16 +194,14 @@ def archive(
183194
elif store:
184195
compression = zipfile.ZIP_STORED
185196

186-
arch = zipfile.ZipFile(name, 'w',
187-
compression,
188-
allowZip64=allow_zip_64)
197+
arch = zipfile.ZipFile(name, "w", compression, allowZip64=allow_zip_64)
189198
write = arch.write
190199
elif archive_type in ("tar", "gz", "bz2"):
191200
mode = archive_type if archive_type != "tar" else ""
192201
arch = tarfile.open(name, "w:{0}".format(mode), **tarfile_kwargs)
193202
write = arch.add
194203
else:
195-
raise ValueError("archive_type must be zip, gz, bz2, or gz")
204+
raise ValueError("archive_type must be zip, gz, bz2, lzma, or gz")
196205

197206
try:
198207
for file_path in files_to_archive:
@@ -396,7 +405,7 @@ def config_namespace(config_file=None, auto_find=False, verify=True, **cfg_optio
396405
return ConfigNamespace(**config_dict(config_file, auto_find, verify, **cfg_options))
397406

398407

399-
def os_tree(directory):
408+
def os_tree(directory, enable_scandir=False):
400409
"""
401410
Return a directories contents as a dictionary hierarchy.
402411
@@ -413,6 +422,9 @@ def os_tree(directory):
413422
:param directory: path to directory to created the tree of.
414423
:return: dictionary of the directory
415424
"""
425+
if enable_scandir and not scandir_warning_given:
426+
scandir_warning()
427+
416428
if not os.path.exists(directory):
417429
raise OSError("Directory does not exist")
418430
if not os.path.isdir(directory):
@@ -484,6 +496,7 @@ def find_files(
484496
disable_glob=False,
485497
depth=None,
486498
abspath=False,
499+
enable_scandir=False,
487500
disable_pathlib=False,
488501
):
489502
"""
@@ -525,6 +538,8 @@ def find_files(
525538
:param disable_pathlib: only return string, not path objects
526539
:return: generator of all files in the specified directory
527540
"""
541+
if enable_scandir and not scandir_warning_given:
542+
scandir_warning()
528543

529544
def pathed(path):
530545
if disable_pathlib:
@@ -571,7 +586,7 @@ def pathed(path):
571586
yield pathed(os.path.join(root, file_name))
572587

573588

574-
def remove_empty_directories(root_directory, dry_run=False, ignore_errors=True):
589+
def remove_empty_directories(root_directory, dry_run=False, ignore_errors=True, enable_scandir=False):
575590
"""
576591
Remove all empty folders from a path. Returns list of empty directories.
577592
@@ -580,6 +595,8 @@ def remove_empty_directories(root_directory, dry_run=False, ignore_errors=True):
580595
:param ignore_errors: Permissions are a pain, just ignore if you blocked
581596
:return: list of removed directories
582597
"""
598+
if enable_scandir and not scandir_warning_given:
599+
scandir_warning()
583600

584601
directory_list = []
585602
for root, directories, files in os.walk(root_directory, topdown=False):
@@ -609,7 +626,7 @@ def remove_empty_directories(root_directory, dry_run=False, ignore_errors=True):
609626
return directory_list
610627

611628

612-
def remove_empty_files(root_directory, dry_run=False, ignore_errors=True):
629+
def remove_empty_files(root_directory, dry_run=False, ignore_errors=True, enable_scandir=False):
613630
"""
614631
Remove all empty files from a path. Returns list of the empty files removed.
615632
@@ -618,6 +635,9 @@ def remove_empty_files(root_directory, dry_run=False, ignore_errors=True):
618635
:param ignore_errors: Permissions are a pain, just ignore if you blocked
619636
:return: list of removed files
620637
"""
638+
if enable_scandir and not scandir_warning_given:
639+
scandir_warning()
640+
621641
file_list = []
622642
for root, directories, files in os.walk(root_directory):
623643
for file_name in files:
@@ -641,7 +661,7 @@ def remove_empty_files(root_directory, dry_run=False, ignore_errors=True):
641661
return file_list
642662

643663

644-
def dup_finder(file_path, directory="."):
664+
def dup_finder(file_path, directory=".", enable_scandir=False):
645665
"""
646666
Check a directory for duplicates of the specified file. This is meant
647667
for a single file only, for checking a directory for dups, use
@@ -668,6 +688,9 @@ def dup_finder(file_path, directory="."):
668688
:param directory: Directory to dig recursively into to look for duplicates
669689
:return: generators
670690
"""
691+
if enable_scandir and not scandir_warning_given:
692+
scandir_warning()
693+
671694
size = os.path.getsize(file_path)
672695
if size == 0:
673696
for empty_file in remove_empty_files(directory, dry_run=True):

reusables/log.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ def add_rotating_file_handler(
177177
backup_count=5,
178178
**handler_kwargs,
179179
):
180-
""" Adds a rotating file handler to the specified logger.
180+
"""Adds a rotating file handler to the specified logger.
181181
182182
:param logger: logging name or object to modify, defaults to root logger
183183
:param file_path: path to file to log to
@@ -213,7 +213,7 @@ def add_timed_rotating_file_handler(
213213
backup_count=5,
214214
**handler_kwargs,
215215
):
216-
""" Adds a timed rotating file handler to the specified logger.
216+
"""Adds a timed rotating file handler to the specified logger.
217217
Defaults to weekly rotation, with 5 backups.
218218
219219
:param logger: logging name or object to modify, defaults to root logger

reusables/namespace.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ def __getattr__(self, item):
176176
return super(ConfigNamespace, self).__getattr__(item.lower())
177177

178178
def bool(self, item, default=None):
179-
""" Return value of key as a boolean
179+
"""Return value of key as a boolean
180180
181181
:param item: key of value to transform
182182
:param default: value to return if item does not exist
@@ -198,7 +198,7 @@ def bool(self, item, default=None):
198198
return True if item else False
199199

200200
def int(self, item, default=None):
201-
""" Return value of key as an int
201+
"""Return value of key as an int
202202
203203
:param item: key of value to transform
204204
:param default: value to return if item does not exist
@@ -213,7 +213,7 @@ def int(self, item, default=None):
213213
return int(item)
214214

215215
def float(self, item, default=None):
216-
""" Return value of key as a float
216+
"""Return value of key as a float
217217
218218
:param item: key of value to transform
219219
:param default: value to return if item does not exist
@@ -228,7 +228,7 @@ def float(self, item, default=None):
228228
return float(item)
229229

230230
def list(self, item, default=None, spliter=",", strip=True, mod=None):
231-
""" Return value of key as a list
231+
"""Return value of key as a list
232232
233233
:param item: key of value to transform
234234
:param mod: function to map against list

reusables/process_helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def check_returncode(self):
9494

9595

9696
def run_in_pool(target, iterable, threaded=True, processes=4, asynchronous=False, target_kwargs=None):
97-
""" Run a set of iterables to a function in a Threaded or MP Pool.
97+
"""Run a set of iterables to a function in a Threaded or MP Pool.
9898
9999
.. code: python
100100

reusables/sanitizers.py

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,40 +26,40 @@ def sanitized_input(
2626
message="", cast_as=None, number_of_retries=-1, error_message="", valid_input=(), raise_on_invalid=False
2727
):
2828
"""
29-
Clean up and cast user input.
30-
31-
:param message: string to show the user (default: "")
32-
:param cast_as: an object to cast the string into. Object must have a __new__
33-
method that can take a string as the first positional argument
34-
and be a subclass of type.
35-
The object should raise a ValueError exception if a
36-
string can't be cast into that object.
37-
cast_as can also be a tuple or a list, which will
38-
chain casts until the end of the list. Casts are chained in
39-
reverse order of the list (to mimic the syntax int(float(x))) (default: str)
40-
:param number_of_retries: number of retries. No limit if n_retries == -1 (default: -1)
41-
:param error_message: message to show the user before asking the input again in
42-
case an error occurs (default: repr of the exception).
43-
Can include {error}.
44-
:param valid_input: an iterable to check if the result is allowed.
45-
:param raise_on_invalid: boolean, whether this function will raise a
46-
reusables.InvalidInputError if the input doesn't match
47-
the valid_input argument.
48-
:return: string literal casted into the cast_as as per that object's rules.
49-
50-
:raises: RetryCountExceededError if the retry count has exceeded the n_retries limit.
51-
52-
53-
Examples:
54-
integer = sanitized_input("How many apples?", int,
55-
error_msg="Please enter a valid number")
56-
# returns an int, will prompt until the user enters an integer.
57-
58-
validated = sanitized_input(">>>", valid_input=["string"], raise_on_invalid=True)
59-
# returns the value "string", and will raise InvalidInputError otherwise.
60-
61-
chain_cast = sanitized_input(">>>", cast_as=[int, float])
62-
# returns an int, prompts like '2.3' won't raise a ValueError Exception.
29+
Clean up and cast user input.
30+
31+
:param message: string to show the user (default: "")
32+
:param cast_as: an object to cast the string into. Object must have a __new__
33+
method that can take a string as the first positional argument
34+
and be a subclass of type.
35+
The object should raise a ValueError exception if a
36+
string can't be cast into that object.
37+
cast_as can also be a tuple or a list, which will
38+
chain casts until the end of the list. Casts are chained in
39+
reverse order of the list (to mimic the syntax int(float(x))) (default: str)
40+
:param number_of_retries: number of retries. No limit if n_retries == -1 (default: -1)
41+
:param error_message: message to show the user before asking the input again in
42+
case an error occurs (default: repr of the exception).
43+
Can include {error}.
44+
:param valid_input: an iterable to check if the result is allowed.
45+
:param raise_on_invalid: boolean, whether this function will raise a
46+
reusables.InvalidInputError if the input doesn't match
47+
the valid_input argument.
48+
:return: string literal casted into the cast_as as per that object's rules.
49+
50+
:raises: RetryCountExceededError if the retry count has exceeded the n_retries limit.
51+
52+
53+
Examples:
54+
integer = sanitized_input("How many apples?", int,
55+
error_msg="Please enter a valid number")
56+
# returns an int, will prompt until the user enters an integer.
57+
58+
validated = sanitized_input(">>>", valid_input=["string"], raise_on_invalid=True)
59+
# returns the value "string", and will raise InvalidInputError otherwise.
60+
61+
chain_cast = sanitized_input(">>>", cast_as=[int, float])
62+
# returns an int, prompts like '2.3' won't raise a ValueError Exception.
6363
"""
6464
retry_count = 0
6565

reusables/tasker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ def hook_post_task(self):
239239
def main_loop(self, stop_at_empty=False):
240240
"""Blocking function that can be run directly, if so would probably
241241
want to specify 'stop_at_empty' to true, or have a separate process
242-
adding items to the queue. """
242+
adding items to the queue."""
243243
try:
244244
while True:
245245
self.hook_pre_command()

setup.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
include_package_data=True,
3636
platforms="any",
3737
setup_requires=["pytest-runner"],
38-
python_requires='>3.6',
38+
python_requires=">3.6",
3939
classifiers=[
4040
"Programming Language :: Python",
4141
"Programming Language :: Python :: 3",
@@ -60,5 +60,7 @@
6060
"Topic :: System :: Filesystems",
6161
"Topic :: System :: Logging",
6262
],
63-
extras_require={"testing": ["pytest", "coverage >= 3.6", "argparse", "rarfile", "tox", "scandir", "pytest-cov"],},
63+
extras_require={
64+
"testing": ["pytest", "coverage >= 3.6", "argparse", "rarfile", "tox", "scandir", "pytest-cov"],
65+
},
6466
)

0 commit comments

Comments
 (0)