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.
@@ -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__}", category=DeprecationWarning, stacklevel=2)
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 , stdout=PIPE, stderr=PIPE, shell=False, stdin = sys.stdin) # pylint: disable=consider-using-with
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 BufferedReader, BytesIO
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.13"
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
- "dotnet", f"clean -c {configuration}", repository_folder)
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"], directory, throw_exception_if_exitcode_is_not_zero=True, verbosity=0)
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, include_submodules=True, mirror=True)
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 "0700"."""
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
- # Return-values program_runner: Exitcode, StdOut, StdErr, Pid
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
- stdout_lines = list[str]()
1304
- stderr_lines = list[str]()
1305
-
1306
- live_console_output_printing = 2 < verbosity and interactive
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
- if throw_exception_if_exitcode_is_not_zero and exit_code != 0:
1372
- raise ValueError(f"Program '{working_directory}>{program} {arguments_for_log_as_string}' resulted in exitcode {exit_code}. (StdOut: '{stdout}', StdErr: '{stderr}')")
1274
+ process.poll()
1275
+ exit_code = process.returncode
1373
1276
 
1374
- result = (exit_code, stdout, stderr, pid)
1375
- return result
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>=3.11.0" for example)
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
- ln -s {exe_file} {link_file}
1718
- chmod {permission} {exe_file}
1719
- chmod {permission} {link_file}
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ScriptCollection
3
- Version: 3.5.13
3
+ Version: 3.5.14
4
4
  Summary: The ScriptCollection is the place for reusable scripts.
5
5
  Home-page: https://github.com/anionDev/ScriptCollection
6
6
  Author: Marius Göcke
@@ -1,16 +1,16 @@
1
1
  ScriptCollection/Executables.py,sha256=msAlVLjgrxCxW-5Uu7Gk8NI1CNQLK2i6TjNR1fppaCY,19535
2
- ScriptCollection/GeneralUtilities.py,sha256=1Q5ML9RwwS5e1mZPuJps3Z3ceVCGF5taFhh0GEG6MlY,35167
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=ECx35Yz-MKPeoRa_42Bsq8qmbHd13I40vXUJSpZPrI4,3475
6
+ ScriptCollection/ProgramRunnerPopen.py,sha256=G3LgQUVCfaq7XjBsGzalElH31Hbr0etttGR2_H87YzA,3512
7
7
  ScriptCollection/RPStream.py,sha256=NRRHL3YSP3D9MuAV2jB_--0KUKCsvJGxeKnxgrRZ9kY,1545
8
- ScriptCollection/ScriptCollectionCore.py,sha256=cWn10QGcBDP4C6u5TtzGYn6OiYV5OpY8BRkeNGQo00w,97421
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.dist-info/METADATA,sha256=LxH_9vYMJRCIrEukh8lc02T6npy78tCjQTrpaOtYB1A,7680
13
- ScriptCollection-3.5.13.dist-info/WHEEL,sha256=ixB2d4u7mugx_bCBycvM9OzZ5yD7NmPXFRtKlORZS2Y,91
14
- ScriptCollection-3.5.13.dist-info/entry_points.txt,sha256=Jz1pyS3Q6lqpuOWVHogt00jGwrpcHYtPrHGY0_Ltjbo,2227
15
- ScriptCollection-3.5.13.dist-info/top_level.txt,sha256=hY2hOVH0V0Ce51WB76zKkIWTUNwMUdHo4XDkR2vYVwg,17
16
- ScriptCollection-3.5.13.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (74.1.0)
2
+ Generator: setuptools (75.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5