ScriptCollection 3.5.13__py3-none-any.whl → 3.5.14__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.
- ScriptCollection/GeneralUtilities.py +1 -1
- ScriptCollection/ProgramRunnerPopen.py +10 -10
- ScriptCollection/ScriptCollectionCore.py +98 -196
- {ScriptCollection-3.5.13.dist-info → ScriptCollection-3.5.14.dist-info}/METADATA +1 -1
- {ScriptCollection-3.5.13.dist-info → ScriptCollection-3.5.14.dist-info}/RECORD +8 -8
- {ScriptCollection-3.5.13.dist-info → ScriptCollection-3.5.14.dist-info}/WHEEL +1 -1
- {ScriptCollection-3.5.13.dist-info → ScriptCollection-3.5.14.dist-info}/entry_points.txt +0 -0
- {ScriptCollection-3.5.13.dist-info → ScriptCollection-3.5.14.dist-info}/top_level.txt +0 -0
|
@@ -67,7 +67,7 @@ class GeneralUtilities:
|
|
|
67
67
|
@functools.wraps(func)
|
|
68
68
|
def new_func(*args, **kwargs):
|
|
69
69
|
warnings.simplefilter('always', DeprecationWarning)
|
|
70
|
-
warnings.warn(f"Call to deprecated function {func.__name__}",
|
|
70
|
+
warnings.warn(f"Call to deprecated function {func.__name__}", category=DeprecationWarning, stacklevel=2)
|
|
71
71
|
warnings.simplefilter('default', DeprecationWarning)
|
|
72
72
|
return func(*args, **kwargs)
|
|
73
73
|
return new_func
|
|
@@ -7,7 +7,7 @@ from .ProgramRunnerBase import ProgramRunnerBase
|
|
|
7
7
|
class ProgramRunnerPopen(ProgramRunnerBase):
|
|
8
8
|
|
|
9
9
|
@GeneralUtilities.check_arguments
|
|
10
|
-
def run_program_argsasarray_async_helper(self, program: str, arguments_as_array: list[str] = [], working_directory: str = None, custom_argument: object = None, interactive:bool=False) -> Popen:
|
|
10
|
+
def run_program_argsasarray_async_helper(self, program: str, arguments_as_array: list[str] = [], working_directory: str = None, custom_argument: object = None, interactive: bool = False) -> Popen:
|
|
11
11
|
arguments_for_process = [program]
|
|
12
12
|
arguments_for_process.extend(arguments_as_array)
|
|
13
13
|
# "shell=True" is not allowed because it is not recommended and also something like
|
|
@@ -15,9 +15,9 @@ class ProgramRunnerPopen(ProgramRunnerBase):
|
|
|
15
15
|
# would not be possible anymore because the ampersand will be treated as shell-command.
|
|
16
16
|
try:
|
|
17
17
|
if interactive:
|
|
18
|
-
result = Popen(arguments_for_process, cwd=working_directory
|
|
18
|
+
result = Popen(arguments_for_process, cwd=working_directory, stdout=PIPE, stderr=PIPE, shell=False, text=True, stdin=sys.stdin) # pylint: disable=consider-using-with
|
|
19
19
|
else:
|
|
20
|
-
result = Popen(arguments_for_process, cwd=working_directory, stdout=PIPE, stderr=PIPE, shell=False) # pylint: disable=consider-using-with
|
|
20
|
+
result = Popen(arguments_for_process, cwd=working_directory, stdout=PIPE, stderr=PIPE, shell=False, text=True) # pylint: disable=consider-using-with
|
|
21
21
|
except FileNotFoundError as fileNotFoundError:
|
|
22
22
|
raise FileNotFoundError(f"Starting '{program}' in '{working_directory}' resulted in a FileNotFoundError: '{fileNotFoundError.filename}'")
|
|
23
23
|
return result
|
|
@@ -34,18 +34,18 @@ class ProgramRunnerPopen(ProgramRunnerBase):
|
|
|
34
34
|
return result
|
|
35
35
|
|
|
36
36
|
@GeneralUtilities.check_arguments
|
|
37
|
-
def run_program_argsasarray(self, program: str, arguments_as_array: list[str] = [], working_directory: str = None, custom_argument: object = None, interactive:bool=False) -> tuple[int, str, str, int]:
|
|
38
|
-
process: Popen = self.run_program_argsasarray_async_helper(program, arguments_as_array, working_directory, custom_argument,interactive)
|
|
37
|
+
def run_program_argsasarray(self, program: str, arguments_as_array: list[str] = [], working_directory: str = None, custom_argument: object = None, interactive: bool = False) -> tuple[int, str, str, int]:
|
|
38
|
+
process: Popen = self.run_program_argsasarray_async_helper(program, arguments_as_array, working_directory, custom_argument, interactive)
|
|
39
39
|
return self.wait(process, custom_argument)
|
|
40
40
|
|
|
41
41
|
@GeneralUtilities.check_arguments
|
|
42
|
-
def run_program(self, program: str, arguments: str = "", working_directory: str = None, custom_argument: object = None, interactive:bool=False) -> tuple[int, str, str, int]:
|
|
42
|
+
def run_program(self, program: str, arguments: str = "", working_directory: str = None, custom_argument: object = None, interactive: bool = False) -> tuple[int, str, str, int]:
|
|
43
43
|
return self.run_program_argsasarray(program, GeneralUtilities.arguments_to_array(arguments), working_directory, custom_argument)
|
|
44
44
|
|
|
45
45
|
@GeneralUtilities.check_arguments
|
|
46
|
-
def run_program_argsasarray_async(self, program: str, arguments_as_array: list[str] = [], working_directory: str = None, custom_argument: object = None, interactive:bool=False) -> int:
|
|
47
|
-
return self.run_program_argsasarray_async_helper(program, arguments_as_array, working_directory, custom_argument,interactive).pid
|
|
46
|
+
def run_program_argsasarray_async(self, program: str, arguments_as_array: list[str] = [], working_directory: str = None, custom_argument: object = None, interactive: bool = False) -> int:
|
|
47
|
+
return self.run_program_argsasarray_async_helper(program, arguments_as_array, working_directory, custom_argument, interactive).pid
|
|
48
48
|
|
|
49
49
|
@GeneralUtilities.check_arguments
|
|
50
|
-
def run_program_async(self, program: str, arguments: str = "", working_directory: str = None, custom_argument: object = None, interactive:bool=False) -> int:
|
|
51
|
-
return self.run_program_argsasarray_async(program, GeneralUtilities.arguments_to_array(arguments), working_directory, custom_argument,interactive)
|
|
50
|
+
def run_program_async(self, program: str, arguments: str = "", working_directory: str = None, custom_argument: object = None, interactive: bool = False) -> int:
|
|
51
|
+
return self.run_program_argsasarray_async(program, GeneralUtilities.arguments_to_array(arguments), working_directory, custom_argument, interactive)
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import time
|
|
2
1
|
from datetime import timedelta, datetime
|
|
3
2
|
import json
|
|
4
3
|
import binascii
|
|
5
4
|
import filecmp
|
|
5
|
+
import time
|
|
6
6
|
import hashlib
|
|
7
|
-
from io import
|
|
7
|
+
from io import BytesIO
|
|
8
8
|
import itertools
|
|
9
9
|
import math
|
|
10
10
|
import os
|
|
11
|
+
from queue import Queue, Empty
|
|
12
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
11
13
|
from pathlib import Path
|
|
12
|
-
from random import randrange
|
|
13
14
|
from subprocess import Popen
|
|
14
15
|
import re
|
|
15
16
|
import shutil
|
|
16
|
-
import traceback
|
|
17
17
|
import uuid
|
|
18
18
|
import tempfile
|
|
19
19
|
import io
|
|
@@ -29,7 +29,7 @@ from .ProgramRunnerBase import ProgramRunnerBase
|
|
|
29
29
|
from .ProgramRunnerPopen import ProgramRunnerPopen
|
|
30
30
|
from .ProgramRunnerEpew import ProgramRunnerEpew, CustomEpewArgument
|
|
31
31
|
|
|
32
|
-
version = "3.5.
|
|
32
|
+
version = "3.5.14"
|
|
33
33
|
__version__ = version
|
|
34
34
|
|
|
35
35
|
|
|
@@ -59,8 +59,7 @@ class ScriptCollectionCore:
|
|
|
59
59
|
errorsonly_argument = ""
|
|
60
60
|
else:
|
|
61
61
|
errorsonly_argument = " --errors-only"
|
|
62
|
-
(exit_code, stdout, stderr, _) = self.run_program("pylint", filename +
|
|
63
|
-
errorsonly_argument, working_directory, throw_exception_if_exitcode_is_not_zero=False)
|
|
62
|
+
(exit_code, stdout, stderr, _) = self.run_program("pylint", filename + errorsonly_argument, working_directory, throw_exception_if_exitcode_is_not_zero=False)
|
|
64
63
|
if (exit_code != 0):
|
|
65
64
|
errors.append(f"Linting-issues of {file}:")
|
|
66
65
|
errors.append(f"Pylint-exitcode: {exit_code}")
|
|
@@ -74,18 +73,15 @@ class ScriptCollectionCore:
|
|
|
74
73
|
|
|
75
74
|
@GeneralUtilities.check_arguments
|
|
76
75
|
def replace_version_in_dockerfile_file(self, dockerfile: str, new_version_value: str) -> None:
|
|
77
|
-
GeneralUtilities.write_text_to_file(dockerfile, re.sub(
|
|
78
|
-
"ARG Version=\"\\d+\\.\\d+\\.\\d+\"", f"ARG Version=\"{new_version_value}\"", GeneralUtilities.read_text_from_file(dockerfile)))
|
|
76
|
+
GeneralUtilities.write_text_to_file(dockerfile, re.sub("ARG Version=\"\\d+\\.\\d+\\.\\d+\"", f"ARG Version=\"{new_version_value}\"", GeneralUtilities.read_text_from_file(dockerfile)))
|
|
79
77
|
|
|
80
78
|
@GeneralUtilities.check_arguments
|
|
81
79
|
def replace_version_in_python_file(self, file: str, new_version_value: str):
|
|
82
|
-
GeneralUtilities.write_text_to_file(file, re.sub(
|
|
83
|
-
"version = \"\\d+\\.\\d+\\.\\d+\"", f"version = \"{new_version_value}\"", GeneralUtilities.read_text_from_file(file)))
|
|
80
|
+
GeneralUtilities.write_text_to_file(file, re.sub("version = \"\\d+\\.\\d+\\.\\d+\"", f"version = \"{new_version_value}\"", GeneralUtilities.read_text_from_file(file)))
|
|
84
81
|
|
|
85
82
|
@GeneralUtilities.check_arguments
|
|
86
83
|
def replace_version_in_ini_file(self, file: str, new_version_value: str):
|
|
87
|
-
GeneralUtilities.write_text_to_file(file, re.sub(
|
|
88
|
-
"version = \\d+\\.\\d+\\.\\d+", f"version = {new_version_value}", GeneralUtilities.read_text_from_file(file)))
|
|
84
|
+
GeneralUtilities.write_text_to_file(file, re.sub("version = \\d+\\.\\d+\\.\\d+", f"version = {new_version_value}", GeneralUtilities.read_text_from_file(file)))
|
|
89
85
|
|
|
90
86
|
@GeneralUtilities.check_arguments
|
|
91
87
|
def replace_version_in_nuspec_file(self, nuspec_file: str, new_version: str) -> None:
|
|
@@ -96,8 +92,7 @@ class ScriptCollectionCore:
|
|
|
96
92
|
if pattern.match(new_version):
|
|
97
93
|
GeneralUtilities.write_text_to_file(nuspec_file, re.sub(f"<version>{versionregex}<\\/version>", f"<version>{new_version}</version>", GeneralUtilities.read_text_from_file(nuspec_file)))
|
|
98
94
|
else:
|
|
99
|
-
raise ValueError(
|
|
100
|
-
f"Version '{new_version}' does not match version-regex '{versiononlyregex}'")
|
|
95
|
+
raise ValueError(f"Version '{new_version}' does not match version-regex '{versiononlyregex}'")
|
|
101
96
|
|
|
102
97
|
@GeneralUtilities.check_arguments
|
|
103
98
|
def replace_version_in_csproj_file(self, csproj_file: str, current_version: str):
|
|
@@ -106,25 +101,20 @@ class ScriptCollectionCore:
|
|
|
106
101
|
pattern = re.compile(versiononlyregex)
|
|
107
102
|
if pattern.match(current_version):
|
|
108
103
|
for tag in ["Version", "AssemblyVersion", "FileVersion"]:
|
|
109
|
-
GeneralUtilities.write_text_to_file(csproj_file, re.sub(
|
|
110
|
-
f"<{tag}>{versionregex}(.\\d+)?<\\/{tag}>", f"<{tag}>{current_version}</{tag}>", GeneralUtilities.read_text_from_file(csproj_file)))
|
|
104
|
+
GeneralUtilities.write_text_to_file(csproj_file, re.sub(f"<{tag}>{versionregex}(.\\d+)?<\\/{tag}>", f"<{tag}>{current_version}</{tag}>", GeneralUtilities.read_text_from_file(csproj_file)))
|
|
111
105
|
else:
|
|
112
|
-
raise ValueError(
|
|
113
|
-
f"Version '{current_version}' does not match version-regex '{versiononlyregex}'")
|
|
106
|
+
raise ValueError(f"Version '{current_version}' does not match version-regex '{versiononlyregex}'")
|
|
114
107
|
|
|
115
108
|
@GeneralUtilities.check_arguments
|
|
116
109
|
def push_nuget_build_artifact(self, nupkg_file: str, registry_address: str, api_key: str, verbosity: int = 1):
|
|
117
110
|
nupkg_file_name = os.path.basename(nupkg_file)
|
|
118
111
|
nupkg_file_folder = os.path.dirname(nupkg_file)
|
|
119
|
-
self.run_program(
|
|
120
|
-
"dotnet", f"nuget push {nupkg_file_name} --force-english-output --source {registry_address} --api-key {api_key}", nupkg_file_folder, verbosity)
|
|
112
|
+
self.run_program("dotnet", f"nuget push {nupkg_file_name} --force-english-output --source {registry_address} --api-key {api_key}", nupkg_file_folder, verbosity)
|
|
121
113
|
|
|
122
114
|
@GeneralUtilities.check_arguments
|
|
123
115
|
def dotnet_build(self, repository_folder: str, projectname: str, configuration: str):
|
|
124
|
-
self.run_program(
|
|
125
|
-
|
|
126
|
-
self.run_program(
|
|
127
|
-
"dotnet", f"build {projectname}/{projectname}.csproj -c {configuration}", repository_folder)
|
|
116
|
+
self.run_program("dotnet", f"clean -c {configuration}", repository_folder)
|
|
117
|
+
self.run_program("dotnet", f"build {projectname}/{projectname}.csproj -c {configuration}", repository_folder)
|
|
128
118
|
|
|
129
119
|
@GeneralUtilities.check_arguments
|
|
130
120
|
def find_file_by_extension(self, folder: str, extension: str):
|
|
@@ -162,8 +152,7 @@ class ScriptCollectionCore:
|
|
|
162
152
|
subfolder_argument = ""
|
|
163
153
|
else:
|
|
164
154
|
subfolder_argument = f" -- {subfolder}"
|
|
165
|
-
log_result = self.run_program(
|
|
166
|
-
"git", f'log --pretty=%aN{space_character}%aE%n%cN{space_character}%cE HEAD{subfolder_argument}', repository_folder, verbosity=0)
|
|
155
|
+
log_result = self.run_program("git", f'log --pretty=%aN{space_character}%aE%n%cN{space_character}%cE HEAD{subfolder_argument}', repository_folder, verbosity=0)
|
|
167
156
|
plain_content: list[str] = list(
|
|
168
157
|
set([line for line in log_result[1].split("\n") if len(line) > 0]))
|
|
169
158
|
result: list[tuple[str, str]] = []
|
|
@@ -179,8 +168,7 @@ class ScriptCollectionCore:
|
|
|
179
168
|
def get_commit_ids_between_dates(self, repository_folder: str, since: datetime, until: datetime, ignore_commits_which_are_not_in_history_of_head: bool = True) -> None:
|
|
180
169
|
since_as_string = self.__datetime_to_string_for_git(since)
|
|
181
170
|
until_as_string = self.__datetime_to_string_for_git(until)
|
|
182
|
-
result = filter(lambda line: not GeneralUtilities.string_is_none_or_whitespace(line), self.run_program(
|
|
183
|
-
"git", f'log --since "{since_as_string}" --until "{until_as_string}" --pretty=format:"%H" --no-patch', repository_folder, throw_exception_if_exitcode_is_not_zero=True)[1].split("\n").replace("\r", ""))
|
|
171
|
+
result = filter(lambda line: not GeneralUtilities.string_is_none_or_whitespace(line), self.run_program("git", f'log --since "{since_as_string}" --until "{until_as_string}" --pretty=format:"%H" --no-patch', repository_folder, throw_exception_if_exitcode_is_not_zero=True)[1].split("\n").replace("\r", ""))
|
|
184
172
|
if ignore_commits_which_are_not_in_history_of_head:
|
|
185
173
|
result = [commit_id for commit_id in result if self.git_commit_is_ancestor(
|
|
186
174
|
repository_folder, commit_id)]
|
|
@@ -192,8 +180,7 @@ class ScriptCollectionCore:
|
|
|
192
180
|
|
|
193
181
|
@GeneralUtilities.check_arguments
|
|
194
182
|
def git_commit_is_ancestor(self, repository_folder: str, ancestor: str, descendant: str = "HEAD") -> bool:
|
|
195
|
-
exit_code = self.run_program_argsasarray(
|
|
196
|
-
"git", ["merge-base", "--is-ancestor", ancestor, descendant], repository_folder, throw_exception_if_exitcode_is_not_zero=False)[0]
|
|
183
|
+
exit_code = self.run_program_argsasarray("git", ["merge-base", "--is-ancestor", ancestor, descendant], repository_folder, throw_exception_if_exitcode_is_not_zero=False)[0]
|
|
197
184
|
if exit_code == 0:
|
|
198
185
|
return True
|
|
199
186
|
elif exit_code == 1:
|
|
@@ -203,8 +190,7 @@ class ScriptCollectionCore:
|
|
|
203
190
|
|
|
204
191
|
@GeneralUtilities.check_arguments
|
|
205
192
|
def __git_changes_helper(self, repository_folder: str, arguments_as_array: list[str]) -> bool:
|
|
206
|
-
lines = GeneralUtilities.string_to_lines(self.run_program_argsasarray(
|
|
207
|
-
"git", arguments_as_array, repository_folder, throw_exception_if_exitcode_is_not_zero=True, verbosity=0)[1], False)
|
|
193
|
+
lines = GeneralUtilities.string_to_lines(self.run_program_argsasarray("git", arguments_as_array, repository_folder, throw_exception_if_exitcode_is_not_zero=True, verbosity=0)[1], False)
|
|
208
194
|
for line in lines:
|
|
209
195
|
if GeneralUtilities.string_has_content(line):
|
|
210
196
|
return True
|
|
@@ -245,8 +231,7 @@ class ScriptCollectionCore:
|
|
|
245
231
|
|
|
246
232
|
@GeneralUtilities.check_arguments
|
|
247
233
|
def git_get_commit_date(self, repository_folder: str, commit: str = "HEAD") -> datetime:
|
|
248
|
-
result: tuple[int, str, str, int] = self.run_program_argsasarray(
|
|
249
|
-
"git", ["show", "-s", "--format=%ci", commit], repository_folder, throw_exception_if_exitcode_is_not_zero=True, verbosity=0)
|
|
234
|
+
result: tuple[int, str, str, int] = self.run_program_argsasarray("git", ["show", "-s", "--format=%ci", commit], repository_folder, throw_exception_if_exitcode_is_not_zero=True, verbosity=0)
|
|
250
235
|
date_as_string = result[1].replace('\n', '')
|
|
251
236
|
result = datetime.strptime(date_as_string, '%Y-%m-%d %H:%M:%S %z')
|
|
252
237
|
return result
|
|
@@ -359,8 +344,7 @@ class ScriptCollectionCore:
|
|
|
359
344
|
self.run_program_argsasarray("git", ['checkout', '.'], directory, throw_exception_if_exitcode_is_not_zero=True, verbosity=0)
|
|
360
345
|
|
|
361
346
|
@GeneralUtilities.check_arguments
|
|
362
|
-
def git_commit(self, directory: str, message: str, author_name: str = None, author_email: str = None, stage_all_changes: bool = True,
|
|
363
|
-
no_changes_behavior: int = 0) -> str:
|
|
347
|
+
def git_commit(self, directory: str, message: str, author_name: str = None, author_email: str = None, stage_all_changes: bool = True, no_changes_behavior: int = 0) -> str:
|
|
364
348
|
# no_changes_behavior=0 => No commit
|
|
365
349
|
# no_changes_behavior=1 => Commit anyway
|
|
366
350
|
# no_changes_behavior=2 => Exception
|
|
@@ -410,7 +394,7 @@ class ScriptCollectionCore:
|
|
|
410
394
|
@GeneralUtilities.check_arguments
|
|
411
395
|
def git_checkout(self, directory: str, branch: str) -> None:
|
|
412
396
|
self.run_program_argsasarray("git", ["checkout", branch], directory, throw_exception_if_exitcode_is_not_zero=True, verbosity=0)
|
|
413
|
-
self.run_program_argsasarray("git", ["submodule", "update", "--recursive"],
|
|
397
|
+
self.run_program_argsasarray("git", ["submodule", "update", "--recursive"], directory, throw_exception_if_exitcode_is_not_zero=True, verbosity=0)
|
|
414
398
|
|
|
415
399
|
@GeneralUtilities.check_arguments
|
|
416
400
|
def git_merge_abort(self, directory: str) -> None:
|
|
@@ -450,7 +434,7 @@ class ScriptCollectionCore:
|
|
|
450
434
|
self.git_fetch(target_directory)
|
|
451
435
|
else:
|
|
452
436
|
# clone
|
|
453
|
-
self.git_clone(target_repository, source_repository,
|
|
437
|
+
self.git_clone(target_repository, source_repository, include_submodules=True, mirror=True)
|
|
454
438
|
|
|
455
439
|
def get_git_submodules(self, folder: str) -> list[str]:
|
|
456
440
|
e = self.run_program("git", "submodule status", folder)
|
|
@@ -472,8 +456,7 @@ class ScriptCollectionCore:
|
|
|
472
456
|
return True
|
|
473
457
|
if (exit_code == 1):
|
|
474
458
|
return False
|
|
475
|
-
raise ValueError(
|
|
476
|
-
f"Unable to calculate whether '{file_in_repository}' in repository '{repositorybasefolder}' is ignored due to git-exitcode {exit_code}.")
|
|
459
|
+
raise ValueError(f"Unable to calculate whether '{file_in_repository}' in repository '{repositorybasefolder}' is ignored due to git-exitcode {exit_code}.")
|
|
477
460
|
|
|
478
461
|
@GeneralUtilities.check_arguments
|
|
479
462
|
def git_discard_all_changes(self, repository: str) -> None:
|
|
@@ -921,29 +904,6 @@ class ScriptCollectionCore:
|
|
|
921
904
|
line[0], line[1], line[2], line[3], line[4])
|
|
922
905
|
GeneralUtilities.write_message_to_stdout(separator_line)
|
|
923
906
|
|
|
924
|
-
@GeneralUtilities.check_arguments
|
|
925
|
-
def SCUploadFileToFileHost(self, file: str, host: str) -> int:
|
|
926
|
-
try:
|
|
927
|
-
GeneralUtilities.write_message_to_stdout(
|
|
928
|
-
self.upload_file_to_file_host(file, host))
|
|
929
|
-
return 0
|
|
930
|
-
except Exception as exception:
|
|
931
|
-
GeneralUtilities.write_exception_to_stderr_with_traceback(exception, traceback)
|
|
932
|
-
return 1
|
|
933
|
-
|
|
934
|
-
@GeneralUtilities.check_arguments
|
|
935
|
-
def SCFileIsAvailableOnFileHost(self, file: str) -> int:
|
|
936
|
-
try:
|
|
937
|
-
if self.file_is_available_on_file_host(file):
|
|
938
|
-
GeneralUtilities.write_message_to_stdout(f"'{file}' is available")
|
|
939
|
-
return 0
|
|
940
|
-
else:
|
|
941
|
-
GeneralUtilities.write_message_to_stdout(f"'{file}' is not available")
|
|
942
|
-
return 1
|
|
943
|
-
except Exception as exception:
|
|
944
|
-
GeneralUtilities.write_exception_to_stderr_with_traceback(exception, traceback)
|
|
945
|
-
return 2
|
|
946
|
-
|
|
947
907
|
@GeneralUtilities.check_arguments
|
|
948
908
|
def SCCalculateBitcoinBlockHash(self, block_version_number: str, previousblockhash: str, transactionsmerkleroot: str, timestamp: str, target: str, nonce: str) -> str:
|
|
949
909
|
# Example-values:
|
|
@@ -1132,44 +1092,6 @@ class ScriptCollectionCore:
|
|
|
1132
1092
|
tor_version = version_with_overhead.split("~")[0]
|
|
1133
1093
|
return tor_version
|
|
1134
1094
|
|
|
1135
|
-
@GeneralUtilities.check_arguments
|
|
1136
|
-
def upload_file_to_file_host(self, file: str, host: str) -> int:
|
|
1137
|
-
if (host is None):
|
|
1138
|
-
return self.upload_file_to_random_filesharing_service(file)
|
|
1139
|
-
elif host == "anonfiles.com":
|
|
1140
|
-
return self.upload_file_to_anonfiles(file)
|
|
1141
|
-
elif host == "bayfiles.com":
|
|
1142
|
-
return self.upload_file_to_bayfiles(file)
|
|
1143
|
-
GeneralUtilities.write_message_to_stderr("Unknown host: "+host)
|
|
1144
|
-
return 1
|
|
1145
|
-
|
|
1146
|
-
@GeneralUtilities.check_arguments
|
|
1147
|
-
def upload_file_to_random_filesharing_service(self, file: str) -> int:
|
|
1148
|
-
host = randrange(2)
|
|
1149
|
-
if host == 0:
|
|
1150
|
-
return self.upload_file_to_anonfiles(file)
|
|
1151
|
-
if host == 1:
|
|
1152
|
-
return self.upload_file_to_bayfiles(file)
|
|
1153
|
-
return 1
|
|
1154
|
-
|
|
1155
|
-
@GeneralUtilities.check_arguments
|
|
1156
|
-
def upload_file_to_anonfiles(self, file) -> int:
|
|
1157
|
-
return self.upload_file_by_using_simple_curl_request("https://api.anonfiles.com/upload", file)
|
|
1158
|
-
|
|
1159
|
-
@GeneralUtilities.check_arguments
|
|
1160
|
-
def upload_file_to_bayfiles(self, file) -> int:
|
|
1161
|
-
return self.upload_file_by_using_simple_curl_request("https://api.bayfiles.com/upload", file)
|
|
1162
|
-
|
|
1163
|
-
@GeneralUtilities.check_arguments
|
|
1164
|
-
def upload_file_by_using_simple_curl_request(self, api_url: str, file: str) -> int:
|
|
1165
|
-
# TODO implement
|
|
1166
|
-
return 1
|
|
1167
|
-
|
|
1168
|
-
@GeneralUtilities.check_arguments
|
|
1169
|
-
def file_is_available_on_file_host(self, file) -> int:
|
|
1170
|
-
# TODO implement
|
|
1171
|
-
return 1
|
|
1172
|
-
|
|
1173
1095
|
def run_testcases_for_python_project(self, repository_folder: str):
|
|
1174
1096
|
self.run_program("coverage", "run -m pytest", repository_folder)
|
|
1175
1097
|
self.run_program("coverage", "xml", repository_folder)
|
|
@@ -1180,7 +1102,7 @@ class ScriptCollectionCore:
|
|
|
1180
1102
|
|
|
1181
1103
|
@GeneralUtilities.check_arguments
|
|
1182
1104
|
def get_file_permission(self, file: str) -> str:
|
|
1183
|
-
"""This function returns an usual octet-triple, for example "
|
|
1105
|
+
"""This function returns an usual octet-triple, for example "700"."""
|
|
1184
1106
|
ls_output = self.__ls(file)
|
|
1185
1107
|
return self.__get_file_permission_helper(ls_output)
|
|
1186
1108
|
|
|
@@ -1223,12 +1145,10 @@ class ScriptCollectionCore:
|
|
|
1223
1145
|
@GeneralUtilities.check_arguments
|
|
1224
1146
|
def __ls(self, file: str) -> str:
|
|
1225
1147
|
file = file.replace("\\", "/")
|
|
1226
|
-
GeneralUtilities.assert_condition(os.path.isfile(file) or os.path.isdir(
|
|
1227
|
-
file), f"Can not execute 'ls' because '{file}' does not exist")
|
|
1148
|
+
GeneralUtilities.assert_condition(os.path.isfile(file) or os.path.isdir(file), f"Can not execute 'ls' because '{file}' does not exist.")
|
|
1228
1149
|
result = self.run_program_argsasarray("ls", ["-ld", file])
|
|
1229
1150
|
GeneralUtilities.assert_condition(result[0] == 0, f"'ls -ld {file}' resulted in exitcode {str(result[0])}. StdErr: {result[2]}")
|
|
1230
|
-
GeneralUtilities.assert_condition(not GeneralUtilities.string_is_none_or_whitespace(
|
|
1231
|
-
result[1]), f"'ls' of '{file}' had an empty output. StdErr: '{result[2]}'")
|
|
1151
|
+
GeneralUtilities.assert_condition(not GeneralUtilities.string_is_none_or_whitespace(result[1]), f"'ls' of '{file}' had an empty output. StdErr: '{result[2]}'")
|
|
1232
1152
|
return result[1]
|
|
1233
1153
|
|
|
1234
1154
|
@GeneralUtilities.check_arguments
|
|
@@ -1268,8 +1188,39 @@ class ScriptCollectionCore:
|
|
|
1268
1188
|
popen: Popen = self.program_runner.run_program_argsasarray_async_helper(program, arguments_as_array, working_directory, custom_argument, interactive)
|
|
1269
1189
|
return popen
|
|
1270
1190
|
|
|
1271
|
-
|
|
1191
|
+
@staticmethod
|
|
1192
|
+
@GeneralUtilities.check_arguments
|
|
1193
|
+
def __enqueue_output(file, queue):
|
|
1194
|
+
for line in iter(file.readline, ''):
|
|
1195
|
+
queue.put(line)
|
|
1196
|
+
file.close()
|
|
1272
1197
|
|
|
1198
|
+
@staticmethod
|
|
1199
|
+
@GeneralUtilities.check_arguments
|
|
1200
|
+
def __read_popen_pipes(p: Popen):
|
|
1201
|
+
|
|
1202
|
+
with ThreadPoolExecutor(2) as pool:
|
|
1203
|
+
q_stdout, q_stderr = Queue(), Queue()
|
|
1204
|
+
|
|
1205
|
+
pool.submit(ScriptCollectionCore.__enqueue_output, p.stdout, q_stdout)
|
|
1206
|
+
pool.submit(ScriptCollectionCore.__enqueue_output, p.stderr, q_stderr)
|
|
1207
|
+
while True:
|
|
1208
|
+
time.sleep(0.2)
|
|
1209
|
+
if p.poll() is not None and q_stdout.empty() and q_stderr.empty():
|
|
1210
|
+
break
|
|
1211
|
+
|
|
1212
|
+
out_line = ''
|
|
1213
|
+
err_line = ''
|
|
1214
|
+
|
|
1215
|
+
try:
|
|
1216
|
+
out_line = q_stdout.get_nowait()
|
|
1217
|
+
err_line = q_stderr.get_nowait()
|
|
1218
|
+
except Empty:
|
|
1219
|
+
pass
|
|
1220
|
+
|
|
1221
|
+
yield (out_line, err_line)
|
|
1222
|
+
|
|
1223
|
+
# Return-values program_runner: Exitcode, StdOut, StdErr, Pid
|
|
1273
1224
|
@GeneralUtilities.check_arguments
|
|
1274
1225
|
def run_program_argsasarray(self, program: str, arguments_as_array: list[str] = [], working_directory: str = None, verbosity: int = 1, print_errors_as_information: bool = False, log_file: str = None, timeoutInSeconds: int = 600, addLogOverhead: bool = False, title: str = None, log_namespace: str = "", arguments_for_log: list[str] = None, throw_exception_if_exitcode_is_not_zero: bool = True, custom_argument: object = None, interactive: bool = False) -> tuple[int, str, str, int]:
|
|
1275
1226
|
# verbosity 1: No output will be logged.
|
|
@@ -1297,82 +1248,37 @@ class ScriptCollectionCore:
|
|
|
1297
1248
|
|
|
1298
1249
|
if verbosity >= 3:
|
|
1299
1250
|
GeneralUtilities.write_message_to_stdout(f"Run '{info_for_log}'.")
|
|
1251
|
+
|
|
1252
|
+
print_live_output = 1 < verbosity
|
|
1253
|
+
|
|
1254
|
+
exit_code: int = None
|
|
1255
|
+
stdout: str = ""
|
|
1256
|
+
stderr: str = ""
|
|
1257
|
+
pid: int = None
|
|
1258
|
+
|
|
1300
1259
|
with self.__run_program_argsasarray_async_helper(program, arguments_as_array, working_directory, verbosity, print_errors_as_information, log_file, timeoutInSeconds, addLogOverhead, title, log_namespace, arguments_for_log, custom_argument, interactive) as process:
|
|
1260
|
+
|
|
1301
1261
|
pid = process.pid
|
|
1262
|
+
for out_line, err_line in ScriptCollectionCore.__read_popen_pipes(process):
|
|
1263
|
+
if print_live_output:
|
|
1264
|
+
#print(out_line, end='')
|
|
1265
|
+
GeneralUtilities.write_message_to_stdout(out_line)
|
|
1266
|
+
#print(err_line, end='')
|
|
1267
|
+
GeneralUtilities.write_message_to_stderr(err_line)
|
|
1302
1268
|
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
log_to_file = log_file is not None
|
|
1309
|
-
if log_to_file:
|
|
1310
|
-
GeneralUtilities.ensure_file_exists(log_file)
|
|
1311
|
-
|
|
1312
|
-
if interactive:
|
|
1313
|
-
# there are 2 issues in this part:
|
|
1314
|
-
# 1.: ctrl+c
|
|
1315
|
-
# 2.: sometimes this function does not terminate even if the started process exited
|
|
1316
|
-
def stream_process(process) -> bool:
|
|
1317
|
-
try:
|
|
1318
|
-
go: bool = process.poll() is None
|
|
1319
|
-
|
|
1320
|
-
stdoutreader: BufferedReader = process.stdout
|
|
1321
|
-
if stdoutreader.readable():
|
|
1322
|
-
stdoutresultb: bytes = stdoutreader.read()
|
|
1323
|
-
stdoutresult = GeneralUtilities.bytes_to_string(
|
|
1324
|
-
stdoutresultb)
|
|
1325
|
-
stdoutlines = GeneralUtilities.string_to_lines(stdoutresult)
|
|
1326
|
-
for line in stdoutlines:
|
|
1327
|
-
line_stripped = line.replace("\r", "").strip()
|
|
1328
|
-
if len(line_stripped) > 0:
|
|
1329
|
-
line_str = line_stripped
|
|
1330
|
-
stdout_lines.append(line_str)
|
|
1331
|
-
if live_console_output_printing:
|
|
1332
|
-
GeneralUtilities.write_message_to_stdout(line_str)
|
|
1333
|
-
if log_to_file:
|
|
1334
|
-
GeneralUtilities.append_line_to_file(
|
|
1335
|
-
log_file, line_str)
|
|
1336
|
-
|
|
1337
|
-
stderrreader: BufferedReader = process.stderr
|
|
1338
|
-
if stderrreader.readable():
|
|
1339
|
-
stderrresultb: bytes = stderrreader.read()
|
|
1340
|
-
stderrresult = GeneralUtilities.bytes_to_string(stderrresultb)
|
|
1341
|
-
stderrlines = GeneralUtilities.string_to_lines(stderrresult)
|
|
1342
|
-
for line in stderrlines:
|
|
1343
|
-
line_stripped = line.replace("\r", "").strip()
|
|
1344
|
-
if len(line_stripped) > 0:
|
|
1345
|
-
line_str = line_stripped
|
|
1346
|
-
stderr_lines.append(line_str)
|
|
1347
|
-
if live_console_output_printing:
|
|
1348
|
-
if print_errors_as_information:
|
|
1349
|
-
GeneralUtilities.write_message_to_stdout(line_str)
|
|
1350
|
-
else:
|
|
1351
|
-
GeneralUtilities.write_message_to_stderr(line_str)
|
|
1352
|
-
if log_to_file:
|
|
1353
|
-
GeneralUtilities.append_line_to_file(log_file, line_str)
|
|
1354
|
-
|
|
1355
|
-
return go
|
|
1356
|
-
except Exception:
|
|
1357
|
-
return False
|
|
1358
|
-
|
|
1359
|
-
while stream_process(process):
|
|
1360
|
-
time.sleep(0.1)
|
|
1361
|
-
|
|
1362
|
-
exit_code = process.poll()
|
|
1363
|
-
stdout = '\n'.join(stdout_lines)
|
|
1364
|
-
stderr = '\n'.join(stderr_lines)
|
|
1365
|
-
else:
|
|
1366
|
-
stdout, stderr = process.communicate()
|
|
1367
|
-
exit_code = process.wait()
|
|
1368
|
-
stdout = GeneralUtilities.bytes_to_string(stdout).replace('\r', '')
|
|
1369
|
-
stderr = GeneralUtilities.bytes_to_string(stderr).replace('\r', '')
|
|
1269
|
+
if out_line is not None and GeneralUtilities.string_has_content(out_line):
|
|
1270
|
+
stdout = stdout+"\n"+out_line
|
|
1271
|
+
if err_line is not None and GeneralUtilities.string_has_content(err_line):
|
|
1272
|
+
stderr = stderr+"\n"+err_line
|
|
1370
1273
|
|
|
1371
|
-
|
|
1372
|
-
|
|
1274
|
+
process.poll()
|
|
1275
|
+
exit_code = process.returncode
|
|
1373
1276
|
|
|
1374
|
-
|
|
1375
|
-
|
|
1277
|
+
if throw_exception_if_exitcode_is_not_zero and exit_code != 0:
|
|
1278
|
+
raise ValueError(f"Program '{working_directory}>{program} {arguments_for_log_as_string}' resulted in exitcode {exit_code}. (StdOut: '{stdout}', StdErr: '{stderr}')")
|
|
1279
|
+
|
|
1280
|
+
result = (exit_code, stdout, stderr, pid)
|
|
1281
|
+
return result
|
|
1376
1282
|
except Exception as e:
|
|
1377
1283
|
raise e
|
|
1378
1284
|
|
|
@@ -1529,8 +1435,7 @@ class ScriptCollectionCore:
|
|
|
1529
1435
|
result = self.get_version_from_gitversion(repository_folder, "MajorMinorPatch")
|
|
1530
1436
|
if self.git_repository_has_uncommitted_changes(repository_folder):
|
|
1531
1437
|
if self.get_current_git_branch_has_tag(repository_folder):
|
|
1532
|
-
id_of_latest_tag = self.git_get_commitid_of_tag(
|
|
1533
|
-
repository_folder, self.get_latest_git_tag(repository_folder))
|
|
1438
|
+
id_of_latest_tag = self.git_get_commitid_of_tag(repository_folder, self.get_latest_git_tag(repository_folder))
|
|
1534
1439
|
current_commit = self.git_get_commit_id(repository_folder)
|
|
1535
1440
|
current_commit_is_on_latest_tag = id_of_latest_tag == current_commit
|
|
1536
1441
|
if current_commit_is_on_latest_tag:
|
|
@@ -1573,8 +1478,7 @@ class ScriptCollectionCore:
|
|
|
1573
1478
|
self.run_program("openssl", f'genrsa -out {filename}.key {rsa_key_length}', folder)
|
|
1574
1479
|
self.run_program("openssl", f'req -new -subj /C={subj_c}/ST={subj_st}/L={subj_l}/O={subj_o}/CN={domain}/OU={subj_ou} -x509 -key {filename}.key -out {filename}.unsigned.crt -days {days_until_expire}', folder)
|
|
1575
1480
|
self.run_program("openssl", f'pkcs12 -export -out {filename}.selfsigned.pfx -password pass:{password} -inkey {filename}.key -in {filename}.unsigned.crt', folder)
|
|
1576
|
-
GeneralUtilities.write_text_to_file(
|
|
1577
|
-
os.path.join(folder, f"{filename}.password"), password)
|
|
1481
|
+
GeneralUtilities.write_text_to_file(os.path.join(folder, f"{filename}.password"), password)
|
|
1578
1482
|
GeneralUtilities.write_text_to_file(os.path.join(folder, f"{filename}.san.conf"), f"""[ req ]
|
|
1579
1483
|
default_bits = {rsa_key_length}
|
|
1580
1484
|
distinguished_name = req_distinguished_name
|
|
@@ -1600,8 +1504,7 @@ DNS = {domain}
|
|
|
1600
1504
|
|
|
1601
1505
|
@GeneralUtilities.check_arguments
|
|
1602
1506
|
def generate_certificate_sign_request(self, folder: str, domain: str, filename: str, subj_c: str, subj_st: str, subj_l: str, subj_o: str, subj_ou: str) -> None:
|
|
1603
|
-
self.run_program(
|
|
1604
|
-
"openssl", f'req -new -subj /C={subj_c}/ST={subj_st}/L={subj_l}/O={subj_o}/CN={domain}/OU={subj_ou} -key {filename}.key -out {filename}.csr -config {filename}.san.conf', folder)
|
|
1507
|
+
self.run_program("openssl", f'req -new -subj /C={subj_c}/ST={subj_st}/L={subj_l}/O={subj_o}/CN={domain}/OU={subj_ou} -key {filename}.key -out {filename}.csr -config {filename}.san.conf', folder)
|
|
1605
1508
|
|
|
1606
1509
|
@GeneralUtilities.check_arguments
|
|
1607
1510
|
def sign_certificate(self, folder: str, ca_folder: str, ca_name: str, domain: str, filename: str, days_until_expire: int = None) -> None:
|
|
@@ -1629,11 +1532,13 @@ DNS = {domain}
|
|
|
1629
1532
|
elif ">" in line:
|
|
1630
1533
|
try:
|
|
1631
1534
|
# line is something like "cyclonedx-bom>=2.0.2" and the function must return with the updated version
|
|
1632
|
-
# (something like "cyclonedx-bom>=
|
|
1535
|
+
# (something like "cyclonedx-bom>=2.11.0" for example)
|
|
1633
1536
|
package = line.split(">")[0]
|
|
1634
1537
|
operator = ">=" if ">=" in line else ">"
|
|
1635
1538
|
response = requests.get(f'https://pypi.org/pypi/{package}/json', timeout=5)
|
|
1636
1539
|
latest_version = response.json()['info']['version']
|
|
1540
|
+
# TODO update only minor- and patch-version
|
|
1541
|
+
# TODO print info if there is a new major-version
|
|
1637
1542
|
return package+operator+latest_version
|
|
1638
1543
|
except:
|
|
1639
1544
|
return line
|
|
@@ -1696,8 +1601,7 @@ DNS = {domain}
|
|
|
1696
1601
|
GeneralUtilities.ensure_directory_exists(packagecontent_entireresult_folder)
|
|
1697
1602
|
|
|
1698
1603
|
# create "debian-binary"-file
|
|
1699
|
-
debianbinary_file = os.path.join(
|
|
1700
|
-
packagecontent_entireresult_folder, "debian-binary")
|
|
1604
|
+
debianbinary_file = os.path.join(packagecontent_entireresult_folder, "debian-binary")
|
|
1701
1605
|
GeneralUtilities.ensure_file_exists(debianbinary_file)
|
|
1702
1606
|
GeneralUtilities.write_text_to_file(debianbinary_file, "2.0\n")
|
|
1703
1607
|
|
|
@@ -1714,10 +1618,10 @@ DNS = {domain}
|
|
|
1714
1618
|
link_file = f"/usr/bin/{toolname.lower()}"
|
|
1715
1619
|
permission = str(permission_of_executable_file_as_octet_triple)
|
|
1716
1620
|
GeneralUtilities.write_text_to_file(postinst_file, f"""#!/bin/sh
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1621
|
+
ln -s {exe_file} {link_file}
|
|
1622
|
+
chmod {permission} {exe_file}
|
|
1623
|
+
chmod {permission} {link_file}
|
|
1624
|
+
""")
|
|
1721
1625
|
|
|
1722
1626
|
# control
|
|
1723
1627
|
control_file = os.path.join(packagecontent_control_folder, "control")
|
|
@@ -1788,8 +1692,7 @@ DNS = {domain}
|
|
|
1788
1692
|
proxies = None
|
|
1789
1693
|
if GeneralUtilities.string_has_content(proxy):
|
|
1790
1694
|
proxies = {"http": proxy}
|
|
1791
|
-
response = requests.get('https://ipinfo.io',
|
|
1792
|
-
proxies=proxies, timeout=5)
|
|
1695
|
+
response = requests.get('https://ipinfo.io', proxies=proxies, timeout=5)
|
|
1793
1696
|
network_information_as_json_string = GeneralUtilities.bytes_to_string(
|
|
1794
1697
|
response.content)
|
|
1795
1698
|
return network_information_as_json_string
|
|
@@ -1802,8 +1705,7 @@ DNS = {domain}
|
|
|
1802
1705
|
else:
|
|
1803
1706
|
extension_to_compare = from_extension
|
|
1804
1707
|
for file in GeneralUtilities.get_direct_files_of_folder(folder):
|
|
1805
|
-
if (ignore_case and file.lower().endswith(f".{extension_to_compare}")
|
|
1806
|
-
or not ignore_case and file.endswith(f".{extension_to_compare}")):
|
|
1708
|
+
if (ignore_case and file.lower().endswith(f".{extension_to_compare}") or not ignore_case and file.endswith(f".{extension_to_compare}")):
|
|
1807
1709
|
p = Path(file)
|
|
1808
1710
|
p.rename(p.with_suffix('.'+to_extension))
|
|
1809
1711
|
if recursive:
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
ScriptCollection/Executables.py,sha256=msAlVLjgrxCxW-5Uu7Gk8NI1CNQLK2i6TjNR1fppaCY,19535
|
|
2
|
-
ScriptCollection/GeneralUtilities.py,sha256=
|
|
2
|
+
ScriptCollection/GeneralUtilities.py,sha256=AElXv2NO30cTw-Qs3qVmO-YCOQ5FvBQM3RZMywuKQ_Y,35121
|
|
3
3
|
ScriptCollection/ProcessesRunner.py,sha256=3mu4ZxzZleQo0Op6o9EYTCFiJfb6kx5ov2YfZfT89mU,1395
|
|
4
4
|
ScriptCollection/ProgramRunnerBase.py,sha256=7QAjoqOz6XPmJH19F2k-Z1fFQB_uZnPFvn-T54IJcHQ,2324
|
|
5
5
|
ScriptCollection/ProgramRunnerEpew.py,sha256=C2Rs3YWOWWWJct7XmKphp5CF1tf0j4Fp-ljV2drLTfs,6349
|
|
6
|
-
ScriptCollection/ProgramRunnerPopen.py,sha256=
|
|
6
|
+
ScriptCollection/ProgramRunnerPopen.py,sha256=G3LgQUVCfaq7XjBsGzalElH31Hbr0etttGR2_H87YzA,3512
|
|
7
7
|
ScriptCollection/RPStream.py,sha256=NRRHL3YSP3D9MuAV2jB_--0KUKCsvJGxeKnxgrRZ9kY,1545
|
|
8
|
-
ScriptCollection/ScriptCollectionCore.py,sha256=
|
|
8
|
+
ScriptCollection/ScriptCollectionCore.py,sha256=na1wwDDpUOZAyIqPREH5MeCn1IJfYvN0NEm-YeViCdE,92720
|
|
9
9
|
ScriptCollection/TasksForCommonProjectStructure.py,sha256=-QxOB01yFHrcPpTNnDA0GY0rv9McvQ95DZ3PNHRCrFE,184090
|
|
10
10
|
ScriptCollection/UpdateCertificates.py,sha256=Eynbgu7k9jLxApP2D_8Il77B6BFjJap6K7oTeEAZYbk,7790
|
|
11
11
|
ScriptCollection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
ScriptCollection-3.5.
|
|
13
|
-
ScriptCollection-3.5.
|
|
14
|
-
ScriptCollection-3.5.
|
|
15
|
-
ScriptCollection-3.5.
|
|
16
|
-
ScriptCollection-3.5.
|
|
12
|
+
ScriptCollection-3.5.14.dist-info/METADATA,sha256=e_vy24P8qOOWX0xn2ds4jO7IrEojsfAR4c5pCa1FcgM,7680
|
|
13
|
+
ScriptCollection-3.5.14.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
14
|
+
ScriptCollection-3.5.14.dist-info/entry_points.txt,sha256=Jz1pyS3Q6lqpuOWVHogt00jGwrpcHYtPrHGY0_Ltjbo,2227
|
|
15
|
+
ScriptCollection-3.5.14.dist-info/top_level.txt,sha256=hY2hOVH0V0Ce51WB76zKkIWTUNwMUdHo4XDkR2vYVwg,17
|
|
16
|
+
ScriptCollection-3.5.14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|