python-backpack 1.0.4__py3-none-any.whl → 1.1.4__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.
- backpack/cache.py +68 -0
- backpack/custom_errors.py +9 -8
- backpack/file_utils.py +30 -21
- backpack/folder_utils.py +15 -14
- backpack/json_metadata.py +54 -77
- backpack/json_user_settings.py +30 -27
- backpack/json_utils.py +19 -12
- backpack/logger.py +2 -2
- backpack/patterns.py +5 -3
- backpack/strings.py +15 -16
- backpack/test_utils.py +9 -6
- backpack/version.py +7 -1
- python_backpack-1.1.4.dist-info/METADATA +12 -0
- python_backpack-1.1.4.dist-info/RECORD +17 -0
- {python_backpack-1.0.4.dist-info → python_backpack-1.1.4.dist-info}/WHEEL +1 -2
- python_backpack-1.0.4.dist-info/METADATA +0 -70
- python_backpack-1.0.4.dist-info/RECORD +0 -27
- python_backpack-1.0.4.dist-info/top_level.txt +0 -2
- tests/__init__.py +0 -0
- tests/test_errors.py +0 -50
- tests/test_file_utils.py +0 -49
- tests/test_folder_utils.py +0 -96
- tests/test_json_user_settings.py +0 -76
- tests/test_json_utils.py +0 -46
- tests/test_jsonmd.py +0 -90
- tests/test_misc.py +0 -54
- tests/test_strings.py +0 -94
- tests/test_test_utils.py +0 -45
- {python_backpack-1.0.4.dist-info → python_backpack-1.1.4.dist-info}/LICENSE +0 -0
backpack/cache.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# ----------------------------------------------------------------------------------------
|
|
2
|
+
# Python-Backpack - Pattern Utilities
|
|
3
|
+
# Maximiliano Rocamora / maxirocamora@gmail.com
|
|
4
|
+
# https://github.com/MaxRocamora/python-backpack
|
|
5
|
+
# ----------------------------------------------------------------------------------------
|
|
6
|
+
from datetime import datetime, timedelta, timezone
|
|
7
|
+
from functools import lru_cache, wraps
|
|
8
|
+
|
|
9
|
+
from backpack.logger import get_logger
|
|
10
|
+
|
|
11
|
+
log = get_logger('Python Backpack - Cache')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def timed_lru_cache(seconds: int, maxsize: int = 128):
|
|
15
|
+
"""Lru_cache with expiration time.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
seconds (int): expiration time in seconds
|
|
19
|
+
maxsize (int): maxsize for lru_cache
|
|
20
|
+
Note:
|
|
21
|
+
Wrapped function can be forced to clear cache with: force_clear=True
|
|
22
|
+
Wrapped function can show log on clear with: show_log=True
|
|
23
|
+
Returns:
|
|
24
|
+
function result
|
|
25
|
+
|
|
26
|
+
# * Usage:
|
|
27
|
+
|
|
28
|
+
# * Add the decorator to your function
|
|
29
|
+
|
|
30
|
+
@timed_lru_cache(seconds=60)
|
|
31
|
+
def my_function():
|
|
32
|
+
return 'Hello World'
|
|
33
|
+
|
|
34
|
+
# * to clear the cache, use force_clear=True on the function call
|
|
35
|
+
my_function(force_clear=True)
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def wrapper_cache(func):
|
|
40
|
+
func = lru_cache(maxsize=maxsize)(func)
|
|
41
|
+
func.lifetime = timedelta(seconds=seconds)
|
|
42
|
+
func.expiration = datetime.now(timezone.utc) + func.lifetime
|
|
43
|
+
|
|
44
|
+
@wraps(func)
|
|
45
|
+
def wrapped_func(*args, force_clear: bool = False, show_log: bool = False, **kwargs):
|
|
46
|
+
"""Wrapper function for lru_cache with expiration time.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
*args: function arguments
|
|
50
|
+
force_clear (bool): forces a clear cache
|
|
51
|
+
show_log (bool): show log on clear
|
|
52
|
+
**kwargs: function keyword arguments
|
|
53
|
+
Returns:
|
|
54
|
+
function result
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
if force_clear or datetime.now(timezone.utc) >= func.expiration:
|
|
58
|
+
if show_log:
|
|
59
|
+
log.debug(f'Cache cleared for {func.__name__}')
|
|
60
|
+
|
|
61
|
+
func.cache_clear()
|
|
62
|
+
func.expiration = datetime.now(timezone.utc) + func.lifetime
|
|
63
|
+
|
|
64
|
+
return func(*args, **kwargs)
|
|
65
|
+
|
|
66
|
+
return wrapped_func
|
|
67
|
+
|
|
68
|
+
return wrapper_cache
|
backpack/custom_errors.py
CHANGED
|
@@ -4,33 +4,34 @@
|
|
|
4
4
|
# https://github.com/MaxRocamora/python-backpack
|
|
5
5
|
# ----------------------------------------------------------------------------------------
|
|
6
6
|
|
|
7
|
-
class EnvironmentVariableNotFound(Exception):
|
|
8
7
|
|
|
8
|
+
class EnvironmentVariableNotFoundError(Exception):
|
|
9
9
|
def __init__(self, var_name: str) -> None:
|
|
10
|
-
|
|
10
|
+
"""Error Raised when a required environment variable is missing from os.
|
|
11
11
|
|
|
12
12
|
Args:
|
|
13
13
|
var_name (str): name of the required variable missing
|
|
14
|
-
|
|
14
|
+
"""
|
|
15
15
|
self.var_name = var_name
|
|
16
|
-
self.message = f'
|
|
16
|
+
self.message = f'Required Environment Variable [{var_name}] not found.'
|
|
17
17
|
super().__init__(self.message)
|
|
18
18
|
|
|
19
19
|
def __str__(self):
|
|
20
|
+
"""Return the error message."""
|
|
20
21
|
return self.message
|
|
21
22
|
|
|
22
23
|
|
|
23
|
-
class
|
|
24
|
-
|
|
24
|
+
class ApplicationNotFoundError(Exception):
|
|
25
25
|
def __init__(self, app_name: str) -> None:
|
|
26
|
-
|
|
26
|
+
"""Error Raised when an Application Name required is not found.
|
|
27
27
|
|
|
28
28
|
Args:
|
|
29
29
|
app_name (str): name of the required application missing
|
|
30
|
-
|
|
30
|
+
"""
|
|
31
31
|
self.app_name = app_name
|
|
32
32
|
self.message = f'Application ({app_name}) not found.'
|
|
33
33
|
super().__init__(self.message)
|
|
34
34
|
|
|
35
35
|
def __str__(self):
|
|
36
|
+
"""Return the error message."""
|
|
36
37
|
return self.message
|
backpack/file_utils.py
CHANGED
|
@@ -8,23 +8,25 @@ import contextlib
|
|
|
8
8
|
|
|
9
9
|
from backpack.logger import get_logger
|
|
10
10
|
|
|
11
|
-
log = get_logger('FileUtils')
|
|
11
|
+
log = get_logger('Python Backpack - FileUtils')
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def replace_strings_in_file(ascii_file: str, strings: list, new_string: str):
|
|
15
|
-
|
|
14
|
+
def replace_strings_in_file(ascii_file: str, strings: list, new_string: str) -> None:
|
|
15
|
+
"""Opens ascii file and replaces all occurrences from strings into new_string.
|
|
16
|
+
|
|
16
17
|
In this class we use a full path to avoid use of os.dirname, which
|
|
17
18
|
causes string encode problems.
|
|
19
|
+
|
|
18
20
|
Args:
|
|
19
|
-
ascii_file (fullpath) file to open
|
|
20
|
-
strings (list) strings to replace
|
|
21
|
-
new_string (string) new string or path to set
|
|
22
|
-
|
|
21
|
+
ascii_file: (fullpath string) file to open
|
|
22
|
+
strings: (list) strings to replace
|
|
23
|
+
new_string: (string) new string or path to set
|
|
24
|
+
"""
|
|
23
25
|
|
|
24
26
|
# force forward slashes
|
|
25
|
-
new_string = new_string.replace(
|
|
27
|
+
new_string = new_string.replace('\\', '/')
|
|
26
28
|
|
|
27
|
-
log.info(f
|
|
29
|
+
log.info(f'Replacing Strings, Opening File: {ascii_file}')
|
|
28
30
|
|
|
29
31
|
with open(ascii_file) as f:
|
|
30
32
|
file_data = f.read()
|
|
@@ -37,41 +39,48 @@ def replace_strings_in_file(ascii_file: str, strings: list, new_string: str):
|
|
|
37
39
|
with open(ascii_file, 'w') as f:
|
|
38
40
|
f.write(file_data)
|
|
39
41
|
f.close()
|
|
40
|
-
log.info(f
|
|
42
|
+
log.info(f'Closing File: {ascii_file}')
|
|
43
|
+
|
|
41
44
|
|
|
45
|
+
def remove_line_from_file(ascii_file: str, strings: list, verbose: bool = False) -> None:
|
|
46
|
+
"""Removes given lines from ascii file.
|
|
42
47
|
|
|
43
|
-
def remove_line_from_file(ascii_file: str, strings: str):
|
|
44
|
-
''' removes given lines from ascii file.
|
|
45
48
|
Args:
|
|
46
|
-
ascii_file (path) ASCII file to process
|
|
47
|
-
strings (list) match lines to remove
|
|
48
|
-
|
|
49
|
+
ascii_file: (string path) ASCII file to process
|
|
50
|
+
strings: (list) match lines to remove
|
|
51
|
+
verbose: (bool) if true, prints removed lines
|
|
52
|
+
"""
|
|
49
53
|
|
|
50
54
|
with open(ascii_file) as f:
|
|
51
55
|
file_content = f.read().splitlines()
|
|
52
56
|
|
|
53
57
|
for line in file_content:
|
|
54
|
-
|
|
58
|
+
if verbose:
|
|
59
|
+
log.info(f'Checking line: {line}')
|
|
55
60
|
with contextlib.suppress(ValueError):
|
|
56
61
|
if line in strings:
|
|
62
|
+
if verbose:
|
|
63
|
+
log.info(f'Removing line: {line}')
|
|
57
64
|
file_content.pop(file_content.index(line))
|
|
58
65
|
|
|
59
|
-
with open(ascii_file,
|
|
60
|
-
contents =
|
|
66
|
+
with open(ascii_file, 'w') as f:
|
|
67
|
+
contents = '\n'.join(file_content)
|
|
61
68
|
f.write(contents)
|
|
62
69
|
|
|
63
70
|
|
|
64
71
|
def file_is_writeable(filepath: str) -> bool:
|
|
65
|
-
|
|
72
|
+
"""Checks if a file is locked and can be overwritten.
|
|
73
|
+
|
|
66
74
|
Args:
|
|
67
|
-
filepath (str)
|
|
75
|
+
filepath: (str) file to check
|
|
68
76
|
Returns:
|
|
69
77
|
bool : true if file is writeable
|
|
70
|
-
|
|
78
|
+
"""
|
|
71
79
|
try:
|
|
72
80
|
with open(filepath, 'r+') as _:
|
|
73
81
|
return True
|
|
74
82
|
except OSError as x:
|
|
75
83
|
log.warning(f'{filepath} is locked ')
|
|
76
84
|
log.info(x.strerror)
|
|
85
|
+
|
|
77
86
|
return False
|
backpack/folder_utils.py
CHANGED
|
@@ -10,42 +10,43 @@ import subprocess
|
|
|
10
10
|
|
|
11
11
|
from backpack.logger import get_logger
|
|
12
12
|
|
|
13
|
-
log = get_logger('FolderUtils')
|
|
13
|
+
log = get_logger('Python Backpack - FolderUtils')
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def browse_folder(folder: str) -> bool:
|
|
17
|
-
|
|
17
|
+
"""Open windows explorer on folder.
|
|
18
|
+
|
|
18
19
|
Args:
|
|
19
|
-
folder (path) folder to open
|
|
20
|
-
|
|
20
|
+
folder: (string path) folder to open
|
|
21
|
+
"""
|
|
21
22
|
if folder and os.path.isdir(folder):
|
|
22
|
-
subprocess.Popen(f
|
|
23
|
+
subprocess.Popen(f'explorer {os.path.abspath(folder)}')
|
|
23
24
|
return True
|
|
24
25
|
|
|
25
|
-
log.warning(f
|
|
26
|
+
log.warning(f'Unable to open folder {folder}')
|
|
26
27
|
return False
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def create_folders(folders: list, force_empty: bool = False, verbose: bool = False):
|
|
30
|
-
|
|
31
|
+
"""Creates multiple folders on disc.
|
|
31
32
|
|
|
32
33
|
Args:
|
|
33
34
|
folders (list): folder list
|
|
34
35
|
force_empty (bool, optional): forces clearing folder content. Defaults to False.
|
|
35
36
|
verbose (bool, optional):shows log. Defaults to False.
|
|
36
|
-
|
|
37
|
+
"""
|
|
37
38
|
for folder in folders:
|
|
38
39
|
create_folder(folder, force_empty=force_empty, verbose=verbose)
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
def create_folder(path: str, force_empty: bool = False, verbose: bool = True):
|
|
42
|
-
|
|
43
|
+
"""Creates a folder.
|
|
43
44
|
|
|
44
45
|
Args:
|
|
45
46
|
path (str): folder path
|
|
46
47
|
force_empty (bool, optional): forces clearing folder content. Defaults to False.
|
|
47
48
|
verbose (bool, optional): show log. Defaults to True.
|
|
48
|
-
|
|
49
|
+
"""
|
|
49
50
|
abspath = os.path.abspath(path)
|
|
50
51
|
if verbose:
|
|
51
52
|
log.info(f'Creating Folder {abspath}')
|
|
@@ -56,14 +57,14 @@ def create_folder(path: str, force_empty: bool = False, verbose: bool = True):
|
|
|
56
57
|
elif force_empty:
|
|
57
58
|
remove_files_in_dir(abspath)
|
|
58
59
|
except OSError as e:
|
|
59
|
-
log.warning(f
|
|
60
|
+
log.warning(f'Unable to create folder: {path}')
|
|
60
61
|
log.error(str(e))
|
|
61
62
|
|
|
62
63
|
return True
|
|
63
64
|
|
|
64
65
|
|
|
65
66
|
def remove_files_in_dir(path: str):
|
|
66
|
-
|
|
67
|
+
"""Clears all content in given directory."""
|
|
67
68
|
for root, dirs, files in os.walk(path):
|
|
68
69
|
for f in files:
|
|
69
70
|
os.unlink(os.path.join(root, f))
|
|
@@ -72,12 +73,12 @@ def remove_files_in_dir(path: str):
|
|
|
72
73
|
|
|
73
74
|
|
|
74
75
|
def recursive_dir_copy(source_path: str, target_path: str):
|
|
75
|
-
|
|
76
|
+
"""Copy all files src dir to dest dir, including sub-directories.
|
|
76
77
|
|
|
77
78
|
Args:
|
|
78
79
|
source_path (str): source path
|
|
79
80
|
target_path (str): destination path
|
|
80
|
-
|
|
81
|
+
"""
|
|
81
82
|
|
|
82
83
|
create_folder(source_path)
|
|
83
84
|
create_folder(target_path)
|
backpack/json_metadata.py
CHANGED
|
@@ -3,153 +3,130 @@
|
|
|
3
3
|
# Maximiliano Rocamora / maxirocamora@gmail.com
|
|
4
4
|
# https://github.com/MaxRocamora/python-backpack
|
|
5
5
|
# ----------------------------------------------------------------------------------------
|
|
6
|
-
import
|
|
6
|
+
import inspect
|
|
7
7
|
import os
|
|
8
|
+
import platform
|
|
9
|
+
import sys
|
|
8
10
|
import time
|
|
9
|
-
import inspect
|
|
10
11
|
from datetime import datetime
|
|
11
|
-
import
|
|
12
|
+
from typing import Any
|
|
12
13
|
|
|
13
14
|
from backpack.json_utils import json_load, json_save
|
|
14
15
|
from backpack.version import version
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
class JsonMetaFile
|
|
18
|
+
class JsonMetaFile:
|
|
19
|
+
PREFIX = 'MD_'
|
|
18
20
|
|
|
19
|
-
def __init__(self, name: str, path: str):
|
|
20
|
-
|
|
21
|
+
def __init__(self, name: str, path: str) -> None:
|
|
22
|
+
"""saves/load a class/dict as a json metadata file.
|
|
21
23
|
|
|
22
24
|
Args:
|
|
23
25
|
name (str): name of the file/class
|
|
24
26
|
path (str, optional): filepath. Defaults to None.
|
|
25
|
-
|
|
27
|
+
"""
|
|
26
28
|
self._name = name
|
|
27
29
|
self._path = path
|
|
28
|
-
self._data = {}
|
|
29
|
-
self._data["_about"] = {'package': 'python-backpack',
|
|
30
|
-
'version': self.version}
|
|
31
|
-
|
|
32
|
-
# ------------------------------------------------------------------------------------
|
|
33
|
-
# PROPERTIES
|
|
34
|
-
# ------------------------------------------------------------------------------------
|
|
30
|
+
self._data = {'_about': {'package': 'python-backpack', 'version': self.version}}
|
|
35
31
|
|
|
36
32
|
@property
|
|
37
|
-
def name(self):
|
|
38
|
-
|
|
33
|
+
def name(self) -> str:
|
|
34
|
+
"""Name of this metadata class."""
|
|
39
35
|
return self._name
|
|
40
36
|
|
|
41
37
|
@property
|
|
42
|
-
def version(self):
|
|
43
|
-
|
|
38
|
+
def version(self) -> str:
|
|
39
|
+
"""Version of this metadata class."""
|
|
44
40
|
return version
|
|
45
41
|
|
|
46
42
|
@property
|
|
47
|
-
def
|
|
48
|
-
|
|
49
|
-
return self.
|
|
50
|
-
|
|
51
|
-
@data.setter
|
|
52
|
-
def data(self, val):
|
|
53
|
-
self._data = val
|
|
54
|
-
|
|
55
|
-
@property
|
|
56
|
-
def prefix(self):
|
|
57
|
-
''' file prefix, is auto-included on the filename '''
|
|
58
|
-
return "MD_"
|
|
59
|
-
|
|
60
|
-
@property
|
|
61
|
-
def filename(self):
|
|
62
|
-
''' Returns default filename with prefix and extension '''
|
|
63
|
-
return self.prefix + self.name + '.json'
|
|
43
|
+
def filename(self) -> str:
|
|
44
|
+
"""Returns default filename with prefix and extension."""
|
|
45
|
+
return self.PREFIX + self.name + '.json'
|
|
64
46
|
|
|
65
47
|
@property
|
|
66
|
-
def filepath(self):
|
|
67
|
-
|
|
48
|
+
def filepath(self) -> str:
|
|
49
|
+
"""Full json metadata filepath."""
|
|
68
50
|
return os.path.join(self.path, self.filename)
|
|
69
51
|
|
|
70
52
|
@property
|
|
71
|
-
def path(self):
|
|
72
|
-
|
|
53
|
+
def path(self) -> str:
|
|
54
|
+
"""Base path location of metadata json file."""
|
|
73
55
|
return self._path
|
|
74
56
|
|
|
75
|
-
def has_file(self):
|
|
57
|
+
def has_file(self) -> bool:
|
|
58
|
+
"""Returns true if file exists."""
|
|
76
59
|
return os.path.exists(self.filepath)
|
|
77
60
|
|
|
78
61
|
# ------------------------------------------------------------------------------------
|
|
79
62
|
# LOAD/INSERT/REMOVE/SAVE
|
|
80
63
|
# ------------------------------------------------------------------------------------
|
|
81
64
|
|
|
82
|
-
def load(self):
|
|
83
|
-
|
|
65
|
+
def load(self) -> None:
|
|
66
|
+
"""Loads metadata from disk."""
|
|
84
67
|
self._data = json_load(self.filepath) if self.has_file() else {}
|
|
85
68
|
|
|
86
|
-
def insert(self, key, value):
|
|
87
|
-
|
|
69
|
+
def insert(self, key: str, value: Any) -> None:
|
|
70
|
+
"""Inserts value into metadata."""
|
|
88
71
|
self._data[key] = value
|
|
89
72
|
|
|
90
|
-
def remove(self, key):
|
|
91
|
-
|
|
73
|
+
def remove(self, key: str) -> None:
|
|
74
|
+
"""Remove key from metadata."""
|
|
92
75
|
if key in self._data.keys():
|
|
93
76
|
del self._data[key]
|
|
94
77
|
|
|
95
|
-
def save(self
|
|
96
|
-
|
|
97
|
-
Args:
|
|
98
|
-
path (str) sets target path for json file. Optional. Defaults to None
|
|
99
|
-
'''
|
|
78
|
+
def save(self) -> None:
|
|
79
|
+
"""Save current metadata into json file."""
|
|
100
80
|
if not os.path.exists(self.path):
|
|
101
81
|
os.makedirs(self.path)
|
|
102
82
|
|
|
103
|
-
self.
|
|
104
|
-
json_save(self.
|
|
83
|
+
self._data['system'] = self._system_data()
|
|
84
|
+
json_save(self._data, self.filepath)
|
|
105
85
|
|
|
106
86
|
# ------------------------------------------------------------------------------------
|
|
107
87
|
# CLASS MODE METHODS
|
|
108
88
|
# ------------------------------------------------------------------------------------
|
|
109
89
|
|
|
110
|
-
def load_as_class(self):
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return
|
|
114
|
-
|
|
115
|
-
def insert_class(self, _class):
|
|
116
|
-
''' set class dict to data, data is cleared '''
|
|
117
|
-
self.data = self._attributes_from_class(_class)
|
|
90
|
+
def load_as_class(self) -> type:
|
|
91
|
+
"""Returns the metadata dict as a class obj."""
|
|
92
|
+
metadata_class = type(self.name, (), self._data)
|
|
93
|
+
return metadata_class
|
|
118
94
|
|
|
119
|
-
def
|
|
95
|
+
def insert_class(self, _class: type) -> None:
|
|
96
|
+
"""Load all attributes from a given class into this class metadata."""
|
|
120
97
|
attributes = {}
|
|
121
98
|
for name in dir(_class):
|
|
122
99
|
value = getattr(_class, name)
|
|
123
100
|
if not name.startswith('__') and not inspect.ismethod(value):
|
|
124
101
|
attributes[name] = value
|
|
125
|
-
|
|
102
|
+
|
|
103
|
+
self._data = attributes
|
|
126
104
|
|
|
127
105
|
# ------------------------------------------------------------------------------------
|
|
128
106
|
# SYSTEM METADATA OS/USER/TIME
|
|
129
107
|
# ------------------------------------------------------------------------------------
|
|
130
108
|
|
|
131
|
-
def _system_data(self):
|
|
132
|
-
|
|
109
|
+
def _system_data(self) -> dict:
|
|
110
|
+
"""Add system metadata to the default data before save."""
|
|
133
111
|
return {
|
|
134
112
|
'name': self.name,
|
|
135
113
|
'app': os.path.basename(sys.executable),
|
|
136
114
|
'PC': str(platform.node()),
|
|
137
115
|
'python_version': sys.version,
|
|
138
116
|
'User': str(os.getenv('username')),
|
|
139
|
-
'time': self.
|
|
117
|
+
'time': self._current_time_metadata(),
|
|
140
118
|
}
|
|
141
119
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
times = ftime.split(",")
|
|
120
|
+
def _current_time_metadata(self) -> dict:
|
|
121
|
+
"""Get export time info."""
|
|
122
|
+
ftime = time.strftime('%Y,%b,%d,%j,%H:%M', time.localtime())
|
|
123
|
+
times = ftime.split(',')
|
|
147
124
|
td = {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
'save_time': datetime.now().ctime()
|
|
125
|
+
'year': times[0],
|
|
126
|
+
'month': times[1],
|
|
127
|
+
'day': times[2],
|
|
128
|
+
'year_day': times[3],
|
|
129
|
+
'time': times[4],
|
|
130
|
+
'save_time': datetime.now().ctime(),
|
|
154
131
|
}
|
|
155
132
|
return td
|
backpack/json_user_settings.py
CHANGED
|
@@ -1,68 +1,71 @@
|
|
|
1
|
-
#
|
|
2
|
-
# --------------------------------------------------------------------------------------------
|
|
1
|
+
# ----------------------------------------------------------------------------------------
|
|
3
2
|
# Json Settings Class
|
|
4
3
|
# This class handle load/save json files on win/linux local user folder
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
"""Usage.
|
|
6
|
+
|
|
6
7
|
us = JsonUserSettings('my_app')
|
|
7
8
|
us.save(someDict)
|
|
8
9
|
data = us.load()
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
#
|
|
11
|
+
"""
|
|
12
|
+
# ----------------------------------------------------------------------------------------
|
|
12
13
|
import os
|
|
13
14
|
|
|
15
|
+
from backpack.json_utils import json_load, json_save
|
|
14
16
|
from backpack.logger import get_logger
|
|
15
|
-
from backpack.json_utils import json_save, json_load
|
|
16
17
|
|
|
17
|
-
log = get_logger('UserSettings')
|
|
18
|
+
log = get_logger('Python Backpack - UserSettings')
|
|
18
19
|
|
|
19
20
|
|
|
20
|
-
class JsonUserSettings
|
|
21
|
-
def __init__(self, folder: str, name: str):
|
|
22
|
-
|
|
21
|
+
class JsonUserSettings:
|
|
22
|
+
def __init__(self, folder: str, name: str) -> None:
|
|
23
|
+
"""Manages saving/loading json file on local user folder.
|
|
23
24
|
|
|
24
25
|
Args:
|
|
25
26
|
folder (str): name of sub folder inside user path. Defaults to 'json_settings'.
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
name (str): name used for the json file. Defaults to 'user_data'.
|
|
28
|
+
"""
|
|
28
29
|
self.name = name
|
|
29
30
|
self.folder = folder
|
|
30
31
|
self._user_data = {}
|
|
31
32
|
self._verify_path()
|
|
32
33
|
|
|
33
34
|
@property
|
|
34
|
-
def filepath(self):
|
|
35
|
-
|
|
36
|
-
path = os.path.join(self.os_user_folder, self.folder, self.name
|
|
35
|
+
def filepath(self) -> str:
|
|
36
|
+
"""Returns user filepath."""
|
|
37
|
+
path = os.path.join(self.os_user_folder, self.folder, f'{self.name}.json')
|
|
37
38
|
return os.path.abspath(path)
|
|
38
39
|
|
|
39
40
|
@property
|
|
40
|
-
def os_user_folder(self):
|
|
41
|
-
|
|
41
|
+
def os_user_folder(self) -> str:
|
|
42
|
+
"""Returns os users home directory."""
|
|
42
43
|
return os.path.expanduser('~')
|
|
43
44
|
|
|
44
45
|
@property
|
|
45
|
-
def user_data(self):
|
|
46
|
-
|
|
46
|
+
def user_data(self) -> dict:
|
|
47
|
+
"""Override this property to modify saving dict."""
|
|
47
48
|
return self._user_data
|
|
48
49
|
|
|
49
50
|
@user_data.setter
|
|
50
|
-
def user_data(self, v):
|
|
51
|
+
def user_data(self, v: dict) -> None:
|
|
51
52
|
self._user_data = v
|
|
52
53
|
|
|
53
|
-
def _verify_path(self):
|
|
54
|
-
|
|
54
|
+
def _verify_path(self) -> bool:
|
|
55
|
+
"""Checks for target directory or make it."""
|
|
55
56
|
path = os.path.dirname(self.filepath)
|
|
56
57
|
if not os.path.exists(path):
|
|
57
58
|
os.makedirs(path)
|
|
59
|
+
|
|
58
60
|
return True
|
|
59
61
|
|
|
60
|
-
def save_settings(self, data=False):
|
|
61
|
-
|
|
62
|
+
def save_settings(self, data=False) -> bool:
|
|
63
|
+
"""Saves a dictionary into a json file (os user path).
|
|
64
|
+
|
|
62
65
|
Args:
|
|
63
66
|
data (dictionary) : info dictionary to save, if set to False,
|
|
64
67
|
saves instead local self.user_data property
|
|
65
|
-
|
|
68
|
+
"""
|
|
66
69
|
if not data:
|
|
67
70
|
data = self.user_data
|
|
68
71
|
|
|
@@ -71,8 +74,8 @@ class JsonUserSettings():
|
|
|
71
74
|
log.info('json settings file saved! [%s]', self.filepath)
|
|
72
75
|
return r
|
|
73
76
|
|
|
74
|
-
def load_settings(self):
|
|
75
|
-
|
|
77
|
+
def load_settings(self) -> dict:
|
|
78
|
+
"""Load json file from path and returns its contents."""
|
|
76
79
|
try:
|
|
77
80
|
return json_load(self.filepath)
|
|
78
81
|
except OSError:
|
backpack/json_utils.py
CHANGED
|
@@ -1,36 +1,43 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import json
|
|
2
|
+
import os
|
|
3
3
|
|
|
4
4
|
from backpack.logger import get_logger
|
|
5
5
|
|
|
6
|
-
log = get_logger('JsonUtils')
|
|
6
|
+
log = get_logger('Python Backpack - JsonUtils')
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def json_load(json_file: str) -> dict:
|
|
10
|
-
|
|
10
|
+
"""Reads a json file.
|
|
11
|
+
|
|
11
12
|
Args:
|
|
12
|
-
json_file (filepath) json file to read data.
|
|
13
|
+
json_file: (string filepath) json file to read data.
|
|
14
|
+
|
|
13
15
|
Returns:
|
|
14
16
|
dict (file content)
|
|
15
|
-
|
|
17
|
+
"""
|
|
16
18
|
if not os.path.exists(json_file):
|
|
17
|
-
raise OSError(f
|
|
19
|
+
raise OSError(f'json_load: File not found: {json_file}.')
|
|
18
20
|
|
|
19
21
|
with open(json_file) as json_file_opened:
|
|
20
22
|
try:
|
|
21
23
|
value = json.load(json_file_opened)
|
|
22
24
|
except ValueError as e:
|
|
23
25
|
json_file_opened.close()
|
|
24
|
-
raise OSError(f
|
|
26
|
+
raise OSError(f'{json_file} \n JSON File issue: {str(e)}') from e
|
|
27
|
+
|
|
25
28
|
return value
|
|
26
29
|
|
|
27
30
|
|
|
28
|
-
def json_save(data: dict, json_file: str):
|
|
29
|
-
|
|
31
|
+
def json_save(data: dict, json_file: str) -> bool:
|
|
32
|
+
"""Saves a dictionary into a json file.
|
|
33
|
+
|
|
30
34
|
Args:
|
|
31
|
-
data (dict)
|
|
32
|
-
json_file (filepath) json file to save data.
|
|
33
|
-
|
|
35
|
+
data: (dict) dictionary to save
|
|
36
|
+
json_file: (string filepath) json file to save data.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
bool (True if success)
|
|
40
|
+
"""
|
|
34
41
|
|
|
35
42
|
if not os.path.exists(os.path.dirname(json_file)):
|
|
36
43
|
os.makedirs(os.path.dirname(json_file))
|