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 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))