py3toolset 1.2.18__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.
- py3toolset/LICENSE.md +11 -0
- py3toolset/__init__.py +1 -0
- py3toolset/bash_autocomp.py +70 -0
- py3toolset/cmd_interact.py +153 -0
- py3toolset/dep.py +144 -0
- py3toolset/file_backup.py +113 -0
- py3toolset/fs.py +543 -0
- py3toolset/nmath.py +419 -0
- py3toolset/setup.py +32 -0
- py3toolset/tuple_file.py +104 -0
- py3toolset/txt_color.py +168 -0
- py3toolset-1.2.18.dist-info/METADATA +24 -0
- py3toolset-1.2.18.dist-info/RECORD +16 -0
- py3toolset-1.2.18.dist-info/WHEEL +5 -0
- py3toolset-1.2.18.dist-info/licenses/LICENSE.md +11 -0
- py3toolset-1.2.18.dist-info/top_level.txt +1 -0
py3toolset/LICENSE.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Copyright 2017-2023 Éric Beucler, Hakim Hadj-Djilani
|
2
|
+
|
3
|
+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
4
|
+
|
5
|
+
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
6
|
+
|
7
|
+
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
8
|
+
|
9
|
+
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
10
|
+
|
11
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
py3toolset/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
""" Utility package """
|
@@ -0,0 +1,70 @@
|
|
1
|
+
"""
|
2
|
+
Module for installing bash autocompletion scripts.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
from os.path import exists
|
7
|
+
import sys
|
8
|
+
|
9
|
+
from py3toolset.cmd_interact import ask_yes_or_no
|
10
|
+
from py3toolset.fs import get_prog_parent_dir
|
11
|
+
from py3toolset.txt_color import warn, col, Color
|
12
|
+
|
13
|
+
|
14
|
+
def propose_bashautocomp(autocomp_script):
|
15
|
+
"""
|
16
|
+
Prompts user for a Bash completion script install for the current running
|
17
|
+
program (``sys.argv[0]``).
|
18
|
+
|
19
|
+
User decides not to/to install Bash completion script in his home
|
20
|
+
directory.
|
21
|
+
User is asked whether to prompt her/him again the next time.
|
22
|
+
|
23
|
+
The script install is done as advised in `bash-completion doc
|
24
|
+
<https://github.com/scop/bash-completion/blob/master/README.md>`_.
|
25
|
+
"""
|
26
|
+
prog_path = get_prog_parent_dir()
|
27
|
+
# TODO: take care that the script dir might not be writable
|
28
|
+
autocomp_script_src = prog_path + os.sep + autocomp_script
|
29
|
+
dontask_again_file = prog_path + os.sep + ".dontask_autocomp_install"
|
30
|
+
src_command = "source " + autocomp_script_src + " 2>/dev/null\n"
|
31
|
+
# stderr ignored in case of a later script deletion
|
32
|
+
# otherwise user will be bothered with the error every time
|
33
|
+
# (s)he launches a Bash
|
34
|
+
if (not exists(dontask_again_file) and
|
35
|
+
(not exists(INSTALL_PATH) or
|
36
|
+
not _is_cmd_in_install_conf(src_command))):
|
37
|
+
warn("Bash command completion feature for " + sys.argv[0] +
|
38
|
+
" isn't installed on your system.")
|
39
|
+
print("Do you want to install it in ", INSTALL_PATH+"? ")
|
40
|
+
answer = ask_yes_or_no()
|
41
|
+
if answer[0] == "y":
|
42
|
+
f = open(INSTALL_PATH, "a")
|
43
|
+
f.write(src_command)
|
44
|
+
f.close()
|
45
|
+
print(col(Color.GREEN, "Bash completion feature installed! But"
|
46
|
+
" you need to start a new bash session to have"
|
47
|
+
" it applied."))
|
48
|
+
else:
|
49
|
+
print("Ask again the next time ?")
|
50
|
+
answer = ask_yes_or_no()
|
51
|
+
if answer[0] == "n":
|
52
|
+
print(dontask_again_file)
|
53
|
+
f = open(dontask_again_file, "w")
|
54
|
+
f.write("This file is a marker for avoiding the program "
|
55
|
+
+ sys.argv[0] +
|
56
|
+
" to ask again if you want bash completion"
|
57
|
+
" to be enabled. Delete it to be asked again!\n")
|
58
|
+
f.close()
|
59
|
+
|
60
|
+
|
61
|
+
def _is_cmd_in_install_conf(src_command):
|
62
|
+
"""
|
63
|
+
Test if a bash completion facility corresponding to src_command is already
|
64
|
+
installed in bash_completion file.
|
65
|
+
"""
|
66
|
+
with open(INSTALL_PATH) as f:
|
67
|
+
return [line for line in f.readlines()].count(src_command) > 0
|
68
|
+
|
69
|
+
|
70
|
+
INSTALL_PATH = os.getenv("HOME")+os.sep+".bash_completion"
|
@@ -0,0 +1,153 @@
|
|
1
|
+
"""
|
2
|
+
Functions to help writing user interactions with a script (parsing options,
|
3
|
+
yes/no question interactions, etc.).
|
4
|
+
"""
|
5
|
+
|
6
|
+
import re
|
7
|
+
|
8
|
+
|
9
|
+
def is_help_switch(string):
|
10
|
+
"""
|
11
|
+
Tests if string is a -h/--help switch.
|
12
|
+
|
13
|
+
Args:
|
14
|
+
string: str
|
15
|
+
String to test.
|
16
|
+
|
17
|
+
Return:
|
18
|
+
- True if the string is a help switch: -h or --help.
|
19
|
+
- False otherwise.
|
20
|
+
|
21
|
+
Example:
|
22
|
+
>>> is_help_switch('-h')
|
23
|
+
True
|
24
|
+
>>> is_help_switch('--help')
|
25
|
+
True
|
26
|
+
>>> is_help_switch('-a')
|
27
|
+
False
|
28
|
+
>>> is_help_switch('--anything')
|
29
|
+
False
|
30
|
+
>>> is_help_switch('anything')
|
31
|
+
False
|
32
|
+
|
33
|
+
"""
|
34
|
+
return is_switch("h", "help", string)
|
35
|
+
|
36
|
+
|
37
|
+
def contains_help_switch(strings):
|
38
|
+
"""
|
39
|
+
Tests if strings contain a -h/--help switch.
|
40
|
+
|
41
|
+
strings: list[str]
|
42
|
+
strings to tests.
|
43
|
+
|
44
|
+
Return:
|
45
|
+
- True if strings contain a help switch: -h/--help.
|
46
|
+
- False otherwise.
|
47
|
+
|
48
|
+
Examples:
|
49
|
+
>>> contains_help_switch(["-h", "--anything"])
|
50
|
+
True
|
51
|
+
>>> contains_help_switch(["--help", "anything"])
|
52
|
+
True
|
53
|
+
>>> contains_help_switch(["-a", "--anything"])
|
54
|
+
False
|
55
|
+
"""
|
56
|
+
return contains_switch("h", "help", strings)
|
57
|
+
|
58
|
+
|
59
|
+
def is_switch(short_sw, long_sw, string):
|
60
|
+
"""
|
61
|
+
Tests if string is the switch short_sw or long_sw.
|
62
|
+
|
63
|
+
Args:
|
64
|
+
short_sw: str
|
65
|
+
short switch (e.g. '-s').
|
66
|
+
long_sw:
|
67
|
+
long switch (e.g. '--switch').
|
68
|
+
string: str
|
69
|
+
string to test.
|
70
|
+
|
71
|
+
Return:
|
72
|
+
- True if string is the short or long switch defined in 2 first args.
|
73
|
+
- False otherwise.
|
74
|
+
|
75
|
+
Examples:
|
76
|
+
>>> is_switch("h", "help", "-h")
|
77
|
+
True
|
78
|
+
>>> is_switch("h", "help", "--help")
|
79
|
+
True
|
80
|
+
>>> is_switch("h", "help", "-z")
|
81
|
+
False
|
82
|
+
>>> is_switch("h", "help", "--zip")
|
83
|
+
False
|
84
|
+
|
85
|
+
"""
|
86
|
+
return (short_sw and re.match("^(-" + short_sw + ")$", string) is not None
|
87
|
+
or
|
88
|
+
long_sw and re.match("^(--" + long_sw + ")$", string) is not None)
|
89
|
+
|
90
|
+
|
91
|
+
def contains_switch(short_sw, long_sw, strings):
|
92
|
+
"""
|
93
|
+
Tests if a switch short_sw or long_sw belongs to one of strings.
|
94
|
+
|
95
|
+
Args:
|
96
|
+
short_sw: str
|
97
|
+
short switch (e.g. '-s').
|
98
|
+
long_sw:
|
99
|
+
long switch (e.g. '--switch').
|
100
|
+
strings: list[str]
|
101
|
+
strings to tests.
|
102
|
+
|
103
|
+
Return:
|
104
|
+
- True if any strings[i] verifies:
|
105
|
+
``is_switch(short_sw, long_sw, strings)``,
|
106
|
+
- False otherwise.
|
107
|
+
|
108
|
+
Examples:
|
109
|
+
>>> contains_switch("h", "help", ["-h","-a"])
|
110
|
+
True
|
111
|
+
>>> contains_switch("h", "help", ["--zip","-a"])
|
112
|
+
False
|
113
|
+
|
114
|
+
"""
|
115
|
+
for i in range(0, len(strings)):
|
116
|
+
if is_switch(short_sw, long_sw, strings[i]):
|
117
|
+
return True
|
118
|
+
else:
|
119
|
+
return False
|
120
|
+
|
121
|
+
|
122
|
+
def ask_yes_or_no(answer=""):
|
123
|
+
"""
|
124
|
+
Asks user 'yes' or 'no' and loops until a valid response is given.
|
125
|
+
|
126
|
+
The response can also be 'y' or 'n'.
|
127
|
+
|
128
|
+
Args:
|
129
|
+
answer: str
|
130
|
+
If set to a valid response, the user is not asked for an answer.
|
131
|
+
|
132
|
+
Return:
|
133
|
+
The answer.
|
134
|
+
|
135
|
+
Examples:
|
136
|
+
>>> ask_yes_or_no('y')
|
137
|
+
'y'
|
138
|
+
>>> ask_yes_or_no('yes')
|
139
|
+
'yes'
|
140
|
+
>>> ask_yes_or_no('no')
|
141
|
+
'no'
|
142
|
+
>>> ask_yes_or_no('n')
|
143
|
+
'n'
|
144
|
+
>>> # with a user input
|
145
|
+
>>> # ask_yes_or_no()
|
146
|
+
# [y/n or yes/no]: y
|
147
|
+
# 'y'
|
148
|
+
|
149
|
+
"""
|
150
|
+
while re.match("^(y|yes|n|no)$", answer, re.I) is None:
|
151
|
+
print("[y/n or yes/no]: ", end='')
|
152
|
+
answer = input()
|
153
|
+
return answer
|
py3toolset/dep.py
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
"""
|
2
|
+
Dependency checking module.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
from os.path import exists
|
7
|
+
import sys
|
8
|
+
|
9
|
+
|
10
|
+
checked_progs = []
|
11
|
+
|
12
|
+
|
13
|
+
def check_executable(progname, *extra_paths, info_msg=""):
|
14
|
+
"""
|
15
|
+
Checks if the program ``progname`` availability.
|
16
|
+
|
17
|
+
The program is search in PATH environment variable and optionally extra
|
18
|
+
paths.
|
19
|
+
If the program is not found an exception is raised.
|
20
|
+
|
21
|
+
If a program has already been checked before it is stored in module
|
22
|
+
variable ``checked_progs`` and next calls to ``check_executable`` will
|
23
|
+
always succeed.
|
24
|
+
|
25
|
+
Args:
|
26
|
+
progname: ``str``
|
27
|
+
program name to check the availability.
|
28
|
+
extra_paths: ``list[str]``
|
29
|
+
extra paths to find the program in.
|
30
|
+
info_msg: ``str``
|
31
|
+
a message to display if the function failed to find the program.
|
32
|
+
|
33
|
+
Return:
|
34
|
+
None in case of success (program found).
|
35
|
+
Exception raise otherwise.
|
36
|
+
|
37
|
+
|
38
|
+
Example:
|
39
|
+
>>> check_executable('more')
|
40
|
+
>>> info_msg = 'more not found'
|
41
|
+
>>> check_executable('more', None, info_msg)
|
42
|
+
>>> check_executable('more', '/', 'home', info_msg)
|
43
|
+
"""
|
44
|
+
if _prog_already_checked(progname): # work already done
|
45
|
+
return
|
46
|
+
if extra_paths is not None:
|
47
|
+
for p in extra_paths:
|
48
|
+
# don't add a path twice in PATH
|
49
|
+
if p not in os.environ["PATH"].split(os.pathsep):
|
50
|
+
print("Adding: "+repr(p)+" TO PATH ENV.")
|
51
|
+
os.environ["PATH"] = p+os.pathsep+os.environ["PATH"]
|
52
|
+
probed_pathes = [os.path.join(path.strip('"'), progname)
|
53
|
+
for path in os.environ["PATH"].split(os.pathsep)]
|
54
|
+
existing_pathes = [path for path in probed_pathes if exists(path)]
|
55
|
+
if len(existing_pathes) == 0:
|
56
|
+
raise Exception("Error: " + progname + " not found in PATH environment"
|
57
|
+
" variable directories.\n"+info_msg)
|
58
|
+
executable_pathes = [path for path in existing_pathes
|
59
|
+
if os.access(path, os.X_OK)]
|
60
|
+
if len(executable_pathes) == 0:
|
61
|
+
raise Exception("Error: " + progname + " found but not executable "
|
62
|
+
+ repr(existing_pathes))
|
63
|
+
# print("exec pathes: " + repr(executable_pathes))
|
64
|
+
checked_progs.append(progname)
|
65
|
+
|
66
|
+
|
67
|
+
def check_prog_deps(prog_list, *extra_paths, info_msgs=None):
|
68
|
+
"""
|
69
|
+
Checks the availability of programs in ``prog_list``.
|
70
|
+
|
71
|
+
It is just an helper function that uses :func:`.check_executable`.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
prog_list: ``list[str]``
|
75
|
+
programs to check availability.
|
76
|
+
extra_paths: ``list[str]``
|
77
|
+
paths to search the programs in addition to the paths in PATH
|
78
|
+
environment variable.
|
79
|
+
info_msgs: ``list[str]``
|
80
|
+
Messages to display when the function failed to find a program.
|
81
|
+
One message per program or none at all.
|
82
|
+
The order must match order of ``prog_list``.
|
83
|
+
|
84
|
+
|
85
|
+
Return:
|
86
|
+
None in case of success (program found).
|
87
|
+
Exception raise otherwise.
|
88
|
+
|
89
|
+
Example:
|
90
|
+
>>> check_prog_deps(['more', 'dir'])
|
91
|
+
>>> info_msgs = [['more not found'], ['dir not found']]
|
92
|
+
>>> check_prog_deps(['more', 'dir'], None, info_msgs)
|
93
|
+
>>> check_prog_deps(['more', 'dir'], '/', '/home', info_msgs)
|
94
|
+
"""
|
95
|
+
if info_msgs is not None:
|
96
|
+
for progname, info in zip(prog_list, info_msgs):
|
97
|
+
check_executable(progname, *extra_paths, info_msg=info)
|
98
|
+
else:
|
99
|
+
for progname in prog_list:
|
100
|
+
check_executable(progname, *extra_paths)
|
101
|
+
|
102
|
+
|
103
|
+
def _prog_already_checked(progname):
|
104
|
+
"""
|
105
|
+
Returns ``True`` if a program has already been found ``False`` otherwise.
|
106
|
+
|
107
|
+
"""
|
108
|
+
return checked_progs.count(progname) > 0
|
109
|
+
|
110
|
+
|
111
|
+
def check_mod_pkg(mod_pkg, message=None, stdout=False):
|
112
|
+
"""
|
113
|
+
Checks Python module/package availability.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
mod_pkg: ``str``
|
117
|
+
Python package or module name (including Cython extension module).
|
118
|
+
message: ``str``
|
119
|
+
Error message displayed the module/package has not been found.
|
120
|
+
stdout: ``bool``
|
121
|
+
If ``True`` display ``message`` on ``sys.stdout``,
|
122
|
+
otherwise on ``sys.stderr`` (default).
|
123
|
+
|
124
|
+
Return:
|
125
|
+
``True`` if Python ``mod_pkg`` (module or package) is found.
|
126
|
+
``False`` otherwise.
|
127
|
+
``None`` if ``mod_pkg`` wasn't found but not because of an
|
128
|
+
``ImportError``.
|
129
|
+
|
130
|
+
Example:
|
131
|
+
>>> check_mod_pkg("notexistingpkg", "pkg not found.", stdout=True)
|
132
|
+
pkg not found.
|
133
|
+
False
|
134
|
+
>>> check_mod_pkg("os.path", "it must be found")
|
135
|
+
True
|
136
|
+
|
137
|
+
"""
|
138
|
+
try:
|
139
|
+
exec('import '+mod_pkg)
|
140
|
+
return True
|
141
|
+
except ImportError:
|
142
|
+
if message is not None:
|
143
|
+
print(message, file=sys.stdout if stdout else sys.stderr)
|
144
|
+
return False
|
@@ -0,0 +1,113 @@
|
|
1
|
+
"""
|
2
|
+
This module manages the backup and restoration of a list of files.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import os
|
6
|
+
from os.path import dirname, isdir, basename, exists
|
7
|
+
import re
|
8
|
+
from shutil import copyfile
|
9
|
+
|
10
|
+
from py3toolset.cmd_interact import ask_yes_or_no
|
11
|
+
from py3toolset.txt_color import print_frame
|
12
|
+
|
13
|
+
# default backup folder
|
14
|
+
BACKUP_FOLDER = ".backup"
|
15
|
+
|
16
|
+
|
17
|
+
def work_on_copies(*files, answer="", backup_folder=BACKUP_FOLDER,
|
18
|
+
header_msg=None, hdr_frame=True):
|
19
|
+
"""
|
20
|
+
Saves/Restores original ``files`` in ``backup_folder``.
|
21
|
+
|
22
|
+
The first call makes a copy, the next one a restoration.
|
23
|
+
All ``files`` must be in the same parent directory.
|
24
|
+
The function quits the program if user refused to restore files.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
answer: ``str``
|
28
|
+
if "yes" then the function auto-restores files from backup
|
29
|
+
(without prompting user for it).
|
30
|
+
Defaultly, the question is asked to user.
|
31
|
+
backup_folder: ``str``
|
32
|
+
The folder into which the files are backed up.
|
33
|
+
It is located in the same parent directory as ``files``.
|
34
|
+
header_msg: ``str``
|
35
|
+
The header message to print before saving/restoring ``files``.
|
36
|
+
Defaultly (``None``) no message.
|
37
|
+
|
38
|
+
Example:
|
39
|
+
>>> from random import randint
|
40
|
+
>>> rand_suffix = str(randint(1, 2**10))
|
41
|
+
>>> # create a folder and dummy files
|
42
|
+
>>> parent_dir = "test_work_on_copies" + rand_suffix
|
43
|
+
>>> os.mkdir(parent_dir)
|
44
|
+
>>> f1 = os.path.join(parent_dir, "f1")
|
45
|
+
>>> f_out = open(f1, 'w')
|
46
|
+
>>> f_out.writelines(["Test test test"])
|
47
|
+
>>> f_out.close()
|
48
|
+
>>> f2 = os.path.join(parent_dir, "f2")
|
49
|
+
>>> f2_out = open(f2, 'w')
|
50
|
+
>>> f2_out.writelines(["Test2 test2 test2"])
|
51
|
+
>>> f2_out.close()
|
52
|
+
>>> # now try work_on_copies
|
53
|
+
>>> work_on_copies(f1, f2, answer="y")
|
54
|
+
Saving original files to .backup
|
55
|
+
>>> # do some modifications
|
56
|
+
>>> f_out = open(f1, 'w')
|
57
|
+
>>> f_out.writelines(["Not the same test"])
|
58
|
+
>>> f_out.close()
|
59
|
+
>>> # file f1 is modified
|
60
|
+
>>> f_out = open(f1, 'r')
|
61
|
+
>>> print(f_out.readlines()[0])
|
62
|
+
Not the same test
|
63
|
+
>>> # restore f1
|
64
|
+
>>> work_on_copies(f1, f2, answer="y") #doctest:+ELLIPSIS
|
65
|
+
Getting back original files from .backup
|
66
|
+
list of files to be restored: test_work_on_copies...
|
67
|
+
The previous file working copy will be overridden...
|
68
|
+
Do you want to proceed?
|
69
|
+
Copying test_work_on_copies...
|
70
|
+
Copying test_work_on_copies...
|
71
|
+
>>> f_out = open(f1, 'r')
|
72
|
+
>>> print(f_out.readlines()[0])
|
73
|
+
Test test test
|
74
|
+
>>> f_out.close()
|
75
|
+
>>> # file f1 has been restored
|
76
|
+
|
77
|
+
"""
|
78
|
+
if header_msg:
|
79
|
+
if hdr_frame:
|
80
|
+
print_frame(header_msg)
|
81
|
+
else:
|
82
|
+
print(header_msg)
|
83
|
+
pdir = dirname(files[0])
|
84
|
+
if re.match(pdir, r"\s") is not None:
|
85
|
+
pdir = "."
|
86
|
+
sav_dir_path = pdir + os.sep + backup_folder
|
87
|
+
# print("backup sac copy dir.: "+sav_dir_path)
|
88
|
+
if isdir(sav_dir_path):
|
89
|
+
print("Getting back original files from " + backup_folder)
|
90
|
+
print("list of files to be restored: "+", ".join(files))
|
91
|
+
print("The previous file working copy will be overridden"
|
92
|
+
" (if a corresponding backup file exists)!\n"
|
93
|
+
"Do you want to proceed?")
|
94
|
+
answer = ask_yes_or_no(answer)
|
95
|
+
if answer[0] == 'n':
|
96
|
+
print("Quitting, nothing done.")
|
97
|
+
exit()
|
98
|
+
for _file in files:
|
99
|
+
saved_sac = sav_dir_path + os.sep + basename(_file)
|
100
|
+
if exists(saved_sac):
|
101
|
+
print("Copying " + saved_sac + " to " + _file)
|
102
|
+
copyfile(saved_sac, _file)
|
103
|
+
else:
|
104
|
+
print("Backup doesn't exist in " +
|
105
|
+
sav_dir_path + "." +
|
106
|
+
" Keeping file: " + _file)
|
107
|
+
print("Saving the file above in " + sav_dir_path)
|
108
|
+
copyfile(_file, saved_sac)
|
109
|
+
else:
|
110
|
+
print("Saving original files to " + backup_folder)
|
111
|
+
os.mkdir(sav_dir_path)
|
112
|
+
for _file in files:
|
113
|
+
copyfile(_file, sav_dir_path + os.sep + basename(_file))
|