forked from fareasthospitality/webapps
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.py
87 lines (74 loc) · 4.12 KB
/
utils.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import os
import re
import sys
import datetime as dt
import time
import logging
def dec_err_handler(retries=0):
"""
Decorator function to handle logging and retries.
Usage: Call without the retries parameter to have it log exceptions only.
Assumptions: 1) args[0] is "self", and "self.logger" has been instantiated.
Ref: https://stackoverflow.com/questions/11731136/python-class-method-decorator-with-self-arguments
:retries: Number of times to retry, in addition to original try.
"""
def wrap(f): # Doing the wrapping here. Called during the decoration of the function.
def wrapped_err_handler(*args, **kwargs): # This way, kwargs can be handled too.
logger = args[0].logger # args[0] is intended to be "self". Assumes that self.logger has already been created on __init__.
if not isinstance(logger, logging.Logger): # Ensures that a Logger object is provided.
print('[ERROR] Please provide an instance of class: logging.Logger')
sys.exit('[ERROR] Please provide an instance of class: logging.Logger')
for i in range(retries + 1): # First attempt 0 does not count as a retry.
try:
f(*args, **kwargs)
#print('No exception encountered')
break # So you don't run f() multiple times!
except Exception as ex:
# To only log exceptions.
#print('Exception: ' + str(ex))
if i > 0:
logger.info(f'[RETRYING] {f.__name__}: {i}/{retries}')
time.sleep(2 ** i) # Exponential backoff. Pause processing for an increasing number of seconds, with each error.
logger.error(ex)
wrapped_err_handler.__name__ = f.__name__ # Nicety. Rename the error handler function name to that of the wrapped function.
return wrapped_err_handler
return wrap
def get_latest_file(str_folder=None, pattern=None):
"""
Given a folder, return the last updated file in that folder.
If pattern (regex) is given, apply pattern as a filter first.
"""
_, _, l_files = next(os.walk(str_folder)) # First, always get all files in the dir.
# Apply pattern to filter l_files if pattern exists #
if pattern is not None:
l_files = [f for f in l_files if re.search(pattern, f)]
if len(l_files) == 0: raise Exception('No files found that match the given pattern.')
# Get last modified file, from the filtered list #
dt_prev = None # Initialize outside the loop.
for file in l_files:
str_fn_curr = os.path.join(str_folder, file)
dt_curr = dt.datetime.fromtimestamp(os.path.getmtime(str_fn_curr))
if dt_prev is None:
dt_prev = dt_curr
str_fn_prev = str_fn_curr
else:
if dt_curr > dt_prev: # Keep the most recent datetime value.
dt_prev = dt_curr
str_fn_prev = str_fn_curr
return (str_fn_prev, file)
def get_files(str_folder=None, pattern=None, latest_only=False):
""" Given a directory name, return all full filenames that exist there, and which match the pattern. Can search for latest filename only.
:param str_folder: Directory to search for files.
:param pattern: A regex expression, to filter the list of files.
:param latest_only: True, if you want to get the latest filename only.
:return: Returns a tuple of (<full filename>, <filename>) if latest_only=True. Otherwise, returns a list of tuples of (<full filename>, <filename>).
"""
_, _, l_files = next(os.walk(str_folder)) # First, always get all files in the dir.
# Simple case. Retrieve all files that match the pattern.
if (str_folder is not None) & ~latest_only:
if pattern is None: # Return all files in the directory
return l_files
else: # Return only files which match pattern.
return [(os.path.join(str_folder, fn), fn) for fn in l_files if re.search(pattern, fn)]
else:
return get_latest_file(str_folder=str_folder, pattern=pattern) # Note: The function will return a 2-values tuple!