atomicshop 2.9.33__py3-none-any.whl → 2.10.1__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.

Potentially problematic release.


This version of atomicshop might be problematic. Click here for more details.

atomicshop/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  """Atomic Basic functions and classes to make developer life easier"""
2
2
 
3
3
  __author__ = "Den Kras"
4
- __version__ = '2.9.33'
4
+ __version__ = '2.10.1'
@@ -3,7 +3,7 @@ import zipfile
3
3
  from io import BytesIO
4
4
  from typing import Union
5
5
 
6
- from . import zip, sevenz
6
+ from . import zips, sevenzs
7
7
  from .. import file_types
8
8
 
9
9
  import py7zr
@@ -37,12 +37,12 @@ def _get_unique_filename(directory, filename):
37
37
 
38
38
  def _is_zip_file(file, zip_obj):
39
39
  with zip_obj.open(file) as file_data:
40
- return zip.is_zip_zipfile(file_data.read())
40
+ return zips.is_zip_zipfile(file_data.read())
41
41
 
42
42
 
43
43
  def _is_7z_file(file, sevenz_obj):
44
44
  with sevenz_obj.open(file) as file_data:
45
- return sevenz.is_7z(file_data.read())
45
+ return sevenzs.is_7z(file_data.read())
46
46
 
47
47
 
48
48
  def _match_file_name(target, current, case_sensitive):
@@ -158,9 +158,9 @@ def _get_archive_type(file_object) -> Union[str, None]:
158
158
  if file_mime not in SUPPORTED_ARCHIVE_MIMES:
159
159
  return None
160
160
 
161
- if zip.is_zip_zipfile(file_object):
161
+ if zips.is_zip_zipfile(file_object):
162
162
  return 'zip'
163
- elif sevenz.is_7z(file_object):
163
+ elif sevenzs.is_7z(file_object):
164
164
  return '7z'
165
165
  else:
166
166
  raise UnknownArchiveType(f"{file_object[:10]} is not a known archive type.")
@@ -244,6 +244,58 @@ def convert_object_with_attributes_to_dict(
244
244
  return obj_dict
245
245
 
246
246
 
247
+ def convert_complex_object_to_dict(data):
248
+ """ Function that converts complex objects to dict recursively """
249
+
250
+ # 1. Extracts only the first level of objects. No byte decoding.
251
+ # new_dict = dict()
252
+ # for key, value in vars(obj).items():
253
+ # new_dict.update({key: str(value)})
254
+ # return new_dict
255
+
256
+ # 2. Extracts only the first level of objects. Tries to decode bytes, if exception rises stores string as is.
257
+ # new_dict = dict()
258
+ # for key, value in vars(obj).items():
259
+ # try:
260
+ # new_dict.update({key: value.decode('utf-8')})
261
+ # except Exception:
262
+ # new_dict.update({key: str(value)})
263
+ # return new_dict
264
+
265
+ # 3. Decompress all the objects, save objects as is (no byte decoding).
266
+ if hasattr(data, "__dict__"):
267
+ # 'vars' return a dictionary of all the variables in a class / object.
268
+ # 'map' iterates through function 'dict_converter' all the values of the second argument and returns a list
269
+ # of all iterations of the function result.
270
+ function_return = dict(map(convert_complex_object_to_dict, vars(data).items()))
271
+ # If 'data' type is 'bytes'
272
+ elif isinstance(data, dict):
273
+ function_return = dict(map(convert_complex_object_to_dict, data.items()))
274
+ elif isinstance(data, tuple):
275
+ function_return = tuple(map(convert_complex_object_to_dict, data))
276
+ elif isinstance(data, list):
277
+ function_return = list(map(convert_complex_object_to_dict, data))
278
+ # One value objects.
279
+ elif isinstance(data, datetime.datetime):
280
+ function_return = data.strftime('%Y-%m-%d-%H:%M:%S')
281
+ elif isinstance(data, bytes) or isinstance(data, bytearray):
282
+ function_return = str(data)
283
+
284
+ # Don't want to use the next method, since there will be different formatted strings / messages. And we don't
285
+ # want that, since we can't manipulate it easily later.
286
+ # # Try to decode, if fails, return string.
287
+ # try:
288
+ # function_return = data.decode(encoding='utf-8')
289
+ # except Exception:
290
+ # function_return = str(data)
291
+ # pass
292
+ # Any other type will return as is (something that 'dict()' function can handle), like strings and integers.
293
+ else:
294
+ function_return = data
295
+
296
+ return function_return
297
+
298
+
247
299
  def convert_tuples_to_lists(obj):
248
300
  """
249
301
  Convert all tuples in object to lists. The first input 'obj' can be a dictionary, list or tuple.
@@ -1,11 +1,9 @@
1
- # v1.0.2 - 21.03.2023 13:50
2
1
  import json
3
- # Needed to convert datetime objects inside 'dict_converter' function.
4
- import datetime
5
2
  # Needed to get the function caller module.
6
3
  import inspect
7
4
 
8
5
  from ..wrappers.loggingw import loggingw
6
+ from ..basics import dicts
9
7
 
10
8
 
11
9
  # If the string has several dot characters (".") - return the most right string after the last dot.
@@ -40,59 +38,7 @@ def create_custom_logger():
40
38
  return loggingw.get_logger_with_level(logger_name)
41
39
 
42
40
 
43
- def dict_converter(data):
44
- """ Function that converts complex objects to dict recursively """
45
-
46
- # 1. Extracts only the first level of objects. No byte decoding.
47
- # new_dict = dict()
48
- # for key, value in vars(obj).items():
49
- # new_dict.update({key: str(value)})
50
- # return new_dict
51
-
52
- # 2. Extracts only the first level of objects. Tries to decode bytes, if exception rises stores string as is.
53
- # new_dict = dict()
54
- # for key, value in vars(obj).items():
55
- # try:
56
- # new_dict.update({key: value.decode('utf-8')})
57
- # except Exception:
58
- # new_dict.update({key: str(value)})
59
- # return new_dict
60
-
61
- # 3. Decompress all the objects, save objects as is (no byte decoding).
62
- if hasattr(data, "__dict__"):
63
- # 'vars' return a dictionary of all the variables in a class / object.
64
- # 'map' iterates through function 'dict_converter' all the values of the second argument and returns a list
65
- # of all iterations of the function result.
66
- function_return = dict(map(dict_converter, vars(data).items()))
67
- # If 'data' type is 'bytes'
68
- elif isinstance(data, dict):
69
- function_return = dict(map(dict_converter, data.items()))
70
- elif isinstance(data, tuple):
71
- function_return = tuple(map(dict_converter, data))
72
- elif isinstance(data, list):
73
- function_return = list(map(dict_converter, data))
74
- # One value objects.
75
- elif isinstance(data, datetime.datetime):
76
- function_return = data.strftime('%Y-%m-%d-%H:%M:%S')
77
- elif isinstance(data, bytes) or isinstance(data, bytearray):
78
- function_return = str(data)
79
-
80
- # Don't want to use the next method, since there will be different formatted strings / messages. And we don't
81
- # want that, since we can't manipulate it easily later.
82
- # # Try to decode, if fails, return string.
83
- # try:
84
- # function_return = data.decode(encoding='utf-8')
85
- # except Exception:
86
- # function_return = str(data)
87
- # pass
88
- # Any other type will return as is (something that 'dict()' function can handle), like strings and integers.
89
- else:
90
- function_return = data
91
-
92
- return function_return
93
-
94
-
95
41
  def get_json(obj):
96
42
  """ Convert any nested object to json / dict and values to string as is """
97
43
 
98
- return json.dumps(obj, default=dict_converter)
44
+ return json.dumps(obj, default=dicts.convert_complex_object_to_dict)
atomicshop/web.py CHANGED
@@ -2,7 +2,7 @@ import os
2
2
  import urllib.request
3
3
 
4
4
  from .print_api import print_api
5
- from .archiver import zip
5
+ from .archiver import zips
6
6
  from .urls import url_parser
7
7
  from .file_io import file_io
8
8
  from .wrappers.playwrightw import scenarios
@@ -239,7 +239,7 @@ def download_and_extract_file(
239
239
  file_url=file_url, target_directory=target_directory, file_name=file_name, **kwargs)
240
240
 
241
241
  # Extract the archive and remove the first directory.
242
- zip.extract_archive_with_zipfile(
242
+ zips.extract_archive_with_zipfile(
243
243
  archive_path=f'{file_path}', extract_directory=target_directory,
244
244
  remove_first_directory=archive_remove_first_directory, **kwargs)
245
245
 
@@ -6,10 +6,15 @@ DEFAULT_KIBANA_PORT: str = '5601'
6
6
  DEFAULT_KIBANA_HOST: str = 'localhost'
7
7
  DEFAULT_KIBANA_URL: str = f"http://{DEFAULT_KIBANA_HOST}:{DEFAULT_KIBANA_PORT}"
8
8
 
9
- ELASTIC_CONFIG_FILE: str = "/etc/elasticsearch/elasticsearch.yml"
10
- ELASTIC_JVM_OPTIONS_FILE: str = "/etc/elasticsearch/jvm.options"
9
+ ELASTIC_SEARCH_CONFIG_DIRECTORY: str = "/etc/elasticsearch"
10
+
11
+ ELASTIC_CONFIG_FILE: str = f"{ELASTIC_SEARCH_CONFIG_DIRECTORY}/elasticsearch.yml"
11
12
  XPACK_SECURITY_SETTING_NAME: str = "xpack.security.enabled"
12
13
 
14
+ ELASTIC_JVM_OPTIONS_DIRECTORY: str = f"{ELASTIC_SEARCH_CONFIG_DIRECTORY}/jvm.options.d"
15
+ ELASTIC_JVM_OPTIONS_CUSTOM_FILE: str = f"{ELASTIC_JVM_OPTIONS_DIRECTORY}/custom.options"
16
+ ELASTIC_JVM_OPTIONS_4GB_MEMORY_USAGE: list[str] = ['-Xms4g', '-Xmx4g']
17
+
13
18
  UBUNTU_DEPENDENCY_PACKAGES: list[str] = ['apt-transport-https', 'openjdk-11-jdk', 'wget']
14
19
  UBUNTU_ELASTIC_PACKAGE_NAME: str = 'elasticsearch'
15
20
  UBUNTU_ELASTIC_SERVICE_NAME: str = 'elasticsearch'
@@ -170,6 +170,29 @@ def modify_xpack_security_setting(
170
170
  print_api(f"The setting is already set to [{setting}].")
171
171
 
172
172
 
173
+ def create_jvm_options_custom_file(file_path: str = None, options: list = None):
174
+ """
175
+ The function creates a custom JVM options file for Elasticsearch.
176
+ The default file path is 'config_basic.ELASTIC_JVM_OPTIONS_CUSTOM_FILE'.
177
+ The default options are 'config_basic.ELASTIC_JVM_OPTIONS_4GB_MEMORY_USAGE'.
178
+ The 4GB memory usage options are needed for the Elasticsearch to work properly and not to crash.
179
+ :param file_path: str, the path to the custom JVM options file.
180
+ :param options: list, the list of JVM options.
181
+ :return:
182
+ """
183
+
184
+ if not file_path:
185
+ file_path = config_basic.ELASTIC_JVM_OPTIONS_CUSTOM_FILE
186
+
187
+ if not options:
188
+ options = config_basic.ELASTIC_JVM_OPTIONS_4GB_MEMORY_USAGE
189
+
190
+ # Write the options to the file.
191
+ with open(file_path, 'w') as file:
192
+ for option in options:
193
+ file.write(f"{option}\n")
194
+
195
+
173
196
  def is_server_available(
174
197
  max_attempts: int = 5,
175
198
  wait_between_attempts_seconds: float = 10,
@@ -215,6 +215,12 @@ def install_elastic_kibana_ubuntu(install_elastic: bool = True, install_kibana:
215
215
 
216
216
  infrastructure.start_elastic_and_check_service_availability()
217
217
 
218
+ print_api("Creating custom JVM options file with 4GB memory usage.")
219
+ infrastructure.create_jvm_options_custom_file(
220
+ file_path=config_basic.ELASTIC_JVM_OPTIONS_CUSTOM_FILE,
221
+ options=config_basic.ELASTIC_JVM_OPTIONS_4GB_MEMORY_USAGE
222
+ )
223
+
218
224
  if install_kibana:
219
225
  # Install Kibana.
220
226
  ubuntu_terminal.install_packages([config_basic.UBUNTU_KIBANA_PACKAGE_NAME])
@@ -3,13 +3,19 @@ from pathlib import Path
3
3
  import sys
4
4
 
5
5
  from .... import permissions, filesystem
6
+ from ....archiver import zips
6
7
  from ....print_api import print_api
7
- from ... import githubw, ubuntu_terminal
8
+ from ... import githubw
8
9
  from ...dockerw import install_docker
9
10
  from .. import config_install
10
11
 
11
12
 
12
- def install_before_restart(installation_directory: str, remove_existing_installation_directory: bool = True):
13
+ def install_before_restart(
14
+ installation_directory: str,
15
+ remove_existing_installation_directory: bool = True,
16
+ fact_source_archive_path: str = None,
17
+ print_kwargs: dict = None
18
+ ):
13
19
  """
14
20
  This function will install the FACT_core before the restart of the computer.
15
21
  :param installation_directory: string, the directory to install the FACT_core to.
@@ -17,6 +23,13 @@ def install_before_restart(installation_directory: str, remove_existing_installa
17
23
  if True, the existing installation directory will be removed.
18
24
  if False, the existing installation directory will not be removed and FACT installation scripts will do their
19
25
  best to install the FACT_core to the existing installation directory.
26
+ :param fact_source_archive_path: string, the path to the FACT_core source archive.
27
+ This is used when the FACT_core source archive is already downloaded, and you want to use the specific archive
28
+ instead of downloading it again. Or you want to use a specific version of the FACT_core.
29
+ This is optional, if not provided, the latest version of the FACT_core will be downloaded.
30
+ The archive should be an exact copy of the FACT_core repository of the master branch as you download it from
31
+ GitHub.
32
+ :param print_kwargs: dict, the print kwargs for the print_api function.
20
33
  :return:
21
34
  """
22
35
 
@@ -36,20 +49,27 @@ def install_before_restart(installation_directory: str, remove_existing_installa
36
49
  if remove_existing_installation_directory:
37
50
  filesystem.remove_directory(installation_directory)
38
51
 
52
+ # Since you run the script with sudo, we need to change the permissions to the current user.
39
53
  with permissions.temporary_regular_permissions():
40
54
  # Create the FACT_core directory.
41
55
  filesystem.create_directory(installation_directory)
42
56
 
43
- # Download the FACT_core repo.
44
- if not filesystem.get_file_paths_from_directory(installation_directory):
45
- git_wrapper = githubw.GitHubWrapper(repo_url=config_install.FACT_CORE_GITHUB_URL)
46
- git_wrapper.build_links_from_repo_url()
47
- git_wrapper.download_and_extract_branch(
48
- target_directory=installation_directory,
49
- archive_remove_first_directory=True)
57
+ if not fact_source_archive_path:
58
+ # Download the FACT_core repo.
59
+ if not filesystem.get_file_paths_from_directory(installation_directory):
60
+ git_wrapper = githubw.GitHubWrapper(repo_url=config_install.FACT_CORE_GITHUB_URL)
61
+ git_wrapper.build_links_from_repo_url()
62
+ git_wrapper.download_and_extract_branch(
63
+ target_directory=installation_directory,
64
+ archive_remove_first_directory=True)
65
+ else:
66
+ # Extract the archive and remove the first directory.
67
+ zips.extract_archive_with_zipfile(
68
+ archive_path=fact_source_archive_path, extract_directory=installation_directory,
69
+ remove_first_directory=True, **(print_kwargs or {}))
50
70
 
51
- # Set the executable permission on the pre-install file.
52
- permissions.set_executable_permission(fact_core_pre_install_file_path)
71
+ # Set the executable permission on the pre-install file.
72
+ permissions.set_executable_permission(fact_core_pre_install_file_path)
53
73
 
54
74
  # Run the shell script
55
75
  subprocess.run(['bash', fact_core_pre_install_file_path])
@@ -1,18 +1,79 @@
1
1
  import requests
2
2
  import fnmatch
3
3
 
4
- from ..web import download_and_extract_file
4
+ from .. import web, urls
5
5
  from ..print_api import print_api
6
- from ..urls import url_parser
7
6
 
8
7
 
9
8
  class GitHubWrapper:
10
9
  # You also can use '.tar.gz' as extension.
11
10
  def __init__(
12
- self, user_name: str = str(), repo_name: str = str(), repo_url: str = str(),
13
- branch: str = 'master', branch_file_extension: str = '.zip'):
11
+ self,
12
+ user_name: str = None,
13
+ repo_name: str = None,
14
+ repo_url: str = None,
15
+ branch: str = 'master',
16
+ branch_file_extension: str = '.zip'
17
+ ):
18
+ """
19
+ This class is a wrapper for GitHub repositories. It can download the branch file from the repository and extract
20
+ it to the target directory and more.
21
+
22
+ :param user_name: str, the user-name of the repository.
23
+ https://github.com/{user_name}/{repo_name}
24
+ :param repo_name: str, the repository name.
25
+ :param repo_url: str, the repository url.
26
+ You can provide the full url to the repository directly and then extract the user_name and repo_name from it
27
+ with the 'build_links_from_repo_url' function.
28
+ :param branch: str, the branch name. The default is 'master'.
29
+ :param branch_file_extension: str, the branch file extension. The default is '.zip'.
30
+
31
+ ================================================================================================================
32
+ Usage to download the 'master' branch file:
33
+ git_wrapper = GitHubWrapper(user_name='user_name', repo_name='repo_name')
34
+ git_wrapper.download_and_extract_branch(target_directory='target_directory')
35
+
36
+ Usage to download the 'main' branch file:
37
+ git_wrapper = GitHubWrapper(user_name='user_name', repo_name='repo_name', branch='main')
38
+ git_wrapper.download_and_extract_branch(target_directory='target_directory')
39
+
40
+ You can provide the user_name and repo_name after the initialization of the class:
41
+ git_wrapper = GitHubWrapper()
42
+ git_wrapper.user_name = 'user_name'
43
+ git_wrapper.repo_name = 'repo_name'
44
+ git_wrapper.build_links_from_user_and_repo()
45
+ git_wrapper.download_and_extract_branch(target_directory='target_directory')
46
+ ================================================================================================================
47
+ Usage to download the 'master' branch file from the repository url:
48
+ git_wrapper = GitHubWrapper(repo_url='http://github.com/user_name/repo_name')
49
+ git_wrapper.download_and_extract_branch(target_directory='target_directory')
50
+
51
+ Usage to download the 'main' branch file from the repository url:
52
+ git_wrapper = GitHubWrapper(repo_url='http://github.com/user_name/repo_name', branch='main')
53
+ git_wrapper.download_and_extract_branch(target_directory='target_directory')
54
+
55
+ You can provide the repo_url after the initialization of the class:
56
+ git_wrapper = GitHubWrapper()
57
+ git_wrapper.repo_url = 'http://github.com/user_name/repo_name'
58
+ git_wrapper.build_links_from_repo_url()
59
+ git_wrapper.download_and_extract_branch(target_directory='target_directory')
60
+ ================================================================================================================
61
+ Usage to download the latest release where the file name is 'test_file.zip':
62
+ git_wrapper = GitHubWrapper(user_name='user_name', repo_name='repo_name')
63
+ git_wrapper.download_and_extract_latest_release(
64
+ target_directory='target_directory', string_pattern='test_*.zip')
65
+ ================================================================================================================
66
+ Usage to get the latest release json:
67
+ git_wrapper = GitHubWrapper(user_name='user_name', repo_name='repo_name')
68
+ git_wrapper.get_the_latest_release_json()
69
+ ================================================================================================================
70
+ Usage to get the latest release version:
71
+ git_wrapper = GitHubWrapper(user_name='user_name', repo_name='repo_name')
72
+ git_wrapper.get_the_latest_release_version_number()
73
+ """
74
+
14
75
  self.user_name: str = user_name
15
- self.repo_name = repo_name
76
+ self.repo_name: str = repo_name
16
77
  self.repo_url: str = repo_url
17
78
  self.branch: str = branch
18
79
  self.branch_file_extension: str = branch_file_extension
@@ -27,6 +88,12 @@ class GitHubWrapper:
27
88
  self.branch_downloaded_folder_name: str = str()
28
89
  self.latest_release_json_url: str = str()
29
90
 
91
+ if self.user_name and self.repo_name and not self.repo_url:
92
+ self.build_links_from_user_and_repo()
93
+
94
+ if self.repo_url and not self.user_name and not self.repo_name:
95
+ self.build_links_from_repo_url()
96
+
30
97
  def build_links_from_user_and_repo(self, **kwargs):
31
98
  if not self.user_name or not self.repo_name:
32
99
  message = "'user_name' or 'repo_name' is empty."
@@ -43,7 +110,7 @@ class GitHubWrapper:
43
110
  message = "'repo_url' is empty."
44
111
  print_api(message, color="red", error_type=True, **kwargs)
45
112
 
46
- repo_url_parsed = url_parser(self.repo_url)
113
+ repo_url_parsed = urls.url_parser(self.repo_url)
47
114
  self.check_github_domain(repo_url_parsed['netloc'])
48
115
  self.user_name = repo_url_parsed['directories'][0]
49
116
  self.repo_name = repo_url_parsed['directories'][1]
@@ -73,20 +140,32 @@ class GitHubWrapper:
73
140
  """
74
141
 
75
142
  # Download the repo to current working directory, extract and remove the archive.
76
- download_and_extract_file(
143
+ web.download_and_extract_file(
77
144
  file_url=self.branch_download_link,
78
145
  target_directory=target_directory,
79
146
  archive_remove_first_directory=archive_remove_first_directory,
80
147
  **kwargs)
81
148
 
82
149
  def download_and_extract_latest_release(
83
- self, target_directory: str, string_pattern: str,
84
- archive_remove_first_directory: bool = False, **kwargs):
85
- # Download latest release url.
86
- response = requests.get(self.latest_release_json_url)
87
- # Response from the latest releases page is json. Convert response to json from downloaded format and get
88
- # 'assets' key.
89
- github_latest_releases_list = response.json()['assets']
150
+ self,
151
+ target_directory: str,
152
+ string_pattern: str,
153
+ archive_remove_first_directory: bool = False,
154
+ **kwargs):
155
+ """
156
+ This function will download the latest release from the GitHub repository, extract the file and remove the file,
157
+ leaving only the extracted folder.
158
+ :param target_directory: str, the target directory to download and extract the file.
159
+ :param string_pattern: str, the string pattern to search in the latest release. Wildcards can be used.
160
+ :param archive_remove_first_directory: bool, sets if archive extract function will extract the archive
161
+ without first directory in the archive. Check reference in the
162
+ 'archiver.zip.extract_archive_with_zipfile' function.
163
+ :param kwargs: dict, the print arguments for the 'print_api' function.
164
+ :return:
165
+ """
166
+
167
+ # Get the 'assets' key of the latest release json.
168
+ github_latest_releases_list = self.get_the_latest_release_json()['assets']
90
169
 
91
170
  # Get only download urls of the latest releases.
92
171
  download_urls: list = list()
@@ -103,8 +182,23 @@ class GitHubWrapper:
103
182
  f'{found_urls}'
104
183
  print_api(message, color="red", error_type=True, **kwargs)
105
184
 
106
- download_and_extract_file(
185
+ web.download_and_extract_file(
107
186
  file_url=found_urls[0],
108
187
  target_directory=target_directory,
109
188
  archive_remove_first_directory=archive_remove_first_directory,
110
189
  **kwargs)
190
+
191
+ def get_the_latest_release_json(self):
192
+ """
193
+ This function will get the latest releases json.
194
+ :return:
195
+ """
196
+ response = requests.get(self.latest_release_json_url)
197
+ return response.json()
198
+
199
+ def get_the_latest_release_version_number(self):
200
+ """
201
+ This function will get the latest release version number.
202
+ :return:
203
+ """
204
+ return self.get_the_latest_release_json()['tag_name']
@@ -1,7 +1,8 @@
1
1
  import subprocess
2
- import getpass
2
+ import requests
3
3
 
4
- from ... import process, filesystem, permissions
4
+ from ...basics import booleans
5
+ from .. import githubw, ubuntu_terminal
5
6
  from ...print_api import print_api
6
7
 
7
8
 
@@ -28,22 +29,105 @@ def is_nodejs_installed():
28
29
  return False
29
30
 
30
31
 
31
- def install_nodejs_ubuntu():
32
+ def get_nodejs_latest_version_number(
33
+ by_github_api: bool = True,
34
+ _by_nodejs_website: bool = False,
35
+ get_major: bool = False
36
+ ) -> str:
37
+ """
38
+ The function will get the latest version number of Node.js.
39
+ :param by_github_api: bool, if True, the function will get the version number using the GitHub API.
40
+ Limitations: rate limits apply.
41
+ :param _by_nodejs_website: bool, if True, the function will get the version number using the Node.js website.
42
+ Limitations: the website structure can change and the json file is relatively large.
43
+ This is only for reference, it is not tested.
44
+ :param get_major: bool, if True, the function will return only the major version number string.
45
+ :return: str.
46
+ """
47
+
48
+ if by_github_api and _by_nodejs_website:
49
+ raise ValueError("Only one of the arguments can be True.")
50
+ elif not by_github_api and not _by_nodejs_website:
51
+ raise ValueError("At least one of the arguments must be True.")
52
+
53
+ latest_version = ''
54
+ if by_github_api:
55
+ github_wrapper = githubw.GitHubWrapper('nodejs', 'node')
56
+ latest_version = github_wrapper.get_the_latest_release_version_number()
57
+ elif _by_nodejs_website:
58
+ url = "https://nodejs.org/dist/index.json"
59
+ response = requests.get(url)
60
+ versions = response.json()
61
+ latest_version = versions[0]['version'] # Assuming the first one is the latest.
62
+
63
+ if get_major:
64
+ latest_version = latest_version.replace('v', '')
65
+ latest_version = latest_version.split('.')[0]
66
+
67
+ return latest_version
68
+
69
+
70
+ def install_nodejs_ubuntu(
71
+ install_latest_version: bool = False,
72
+ install_lts: bool = True,
73
+ install_by_version_number: str = None,
74
+ force_install: bool = False
75
+ ):
32
76
  """
33
77
  The function will install Node.js on Ubuntu.
78
+
79
+ :param install_latest_version: bool, if True, the function will install the latest version of Node.js.
80
+ :param install_lts: bool, if True, the function will install the LTS version of Node.js.
81
+ :param install_by_version_number: str, the version number of Node.js to install.
82
+ :param force_install: bool, if True, the function will install Node.js even if it is already installed.
83
+
34
84
  :return:
35
85
  """
36
86
 
87
+ booleans.check_3_booleans_when_only_1_can_be_true(
88
+ (install_latest_version, 'install_latest_version'),
89
+ (install_lts, 'install_lts'),
90
+ (install_by_version_number, 'install_by_version_number')
91
+ )
92
+
37
93
  # Check if Node.js is already installed.
38
94
  if is_nodejs_installed():
39
- return
95
+ if not force_install:
96
+ return
40
97
 
41
- # Add the Node.js repository.
42
- process.run_command(['curl', '-sL', 'https://deb.nodesource.com/setup_14.x', '-o', '/tmp/nodesource_setup.sh'])
43
- process.run_command(['bash', '/tmp/nodesource_setup.sh'])
98
+ # NodeSource is listed as source under official Node.js GitHub repository:
99
+ # https://github.com/nodejs/node?tab=readme-ov-file#current-and-lts-releases
100
+ print_api("Adding NodeSource repository...")
44
101
 
45
- # Install Node.js
46
- process.run_command(['apt-get', 'install', '-y', 'nodejs'])
102
+ # Fetch and execute the NodeSource repository setup script.
103
+ if install_latest_version:
104
+ install_by_version_number: str = get_nodejs_latest_version_number(get_major=True)
105
+
106
+ command: str = ''
107
+ if install_latest_version or install_by_version_number:
108
+ command = f"curl -fsSL https://deb.nodesource.com/setup_{install_by_version_number}.x | sudo -E bash -"
109
+ elif install_lts:
110
+ command = "curl -fsSL https://deb.nodesource.com/setup_current.x | sudo -E bash -"
111
+
112
+ _ = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)
113
+
114
+ ubuntu_terminal.update_system_packages()
115
+ ubuntu_terminal.install_packages(['nodejs'])
116
+
117
+ # Check if Node.js is installed.
118
+ is_nodejs_installed()
119
+
120
+
121
+ def install_npm_package_ubuntu(package_name: str):
122
+ """
123
+ The function will install a npm package on Ubuntu.
124
+ :param package_name: str, the name of the package to install.
125
+ :return:
126
+ """
47
127
 
48
128
  # Check if Node.js is installed.
49
- is_nodejs_installed()
129
+ if not is_nodejs_installed():
130
+ return
131
+
132
+ command = f"npm install -g {package_name}"
133
+ _ = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.9.33
3
+ Version: 2.10.1
4
4
  Summary: Atomic functions and classes to make developer life easier
5
5
  Author: Denis Kras
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
- atomicshop/__init__.py,sha256=xZ_X7wkuj9RC8hITqUIPCnbV_tPMEP3Ct3FVSt2GWdM,123
1
+ atomicshop/__init__.py,sha256=ukl6aqshNmALeGqIPGLVnag3oMdx9rXTGHupAsgJhXk,123
2
2
  atomicshop/_basics_temp.py,sha256=6cu2dd6r2dLrd1BRNcVDKTHlsHs_26Gpw8QS6v32lQ0,3699
3
3
  atomicshop/_create_pdf_demo.py,sha256=Yi-PGZuMg0RKvQmLqVeLIZYadqEZwUm-4A9JxBl_vYA,3713
4
4
  atomicshop/_patch_import.py,sha256=ENp55sKVJ0e6-4lBvZnpz9PQCt3Otbur7F6aXDlyje4,6334
@@ -44,7 +44,7 @@ atomicshop/timer.py,sha256=KxBBgVM8po6pUJDW8TgY1UXj0iiDmRmL5XDCq0VHAfU,1670
44
44
  atomicshop/urls.py,sha256=CQl1j1kjEVDlAuYJqYD9XxPF1SUSgrmG8PjlcXNEKsQ,597
45
45
  atomicshop/uuids.py,sha256=JSQdm3ZTJiwPQ1gYe6kU0TKS_7suwVrHc8JZDGYlydM,2214
46
46
  atomicshop/virtualization.py,sha256=LPP4vjE0Vr10R6DA4lqhfX_WaNdDGRAZUW0Am6VeGco,494
47
- atomicshop/web.py,sha256=K3UndqJqHO9bTogZUWDz-IEZN776KNhpk28m3Ct_pbc,11069
47
+ atomicshop/web.py,sha256=7WPV6Q4xZX7bByEeCl2VAfPlyMY42BpR_byVX0Gu7Js,11071
48
48
  atomicshop/addons/PlayWrightCodegen.cmd,sha256=Z5cnllsyXD4F1W2h-WLEnyFkg5nZy0-hTGHRWXVOuW4,173
49
49
  atomicshop/addons/ScriptExecution.cmd,sha256=8iC-uHs9MX9qUD_C2M7n9Xw4MZvwOfxT8H5v3hluVps,93
50
50
  atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh,sha256=lM7LkXQ2AxfFzDGyzSOfIS_zpg9bAD1k3JJ-qu5CdH8,81
@@ -67,16 +67,16 @@ atomicshop/addons/process_list/compiled/Win10x64/process_list.lib,sha256=n9c2MVP
67
67
  atomicshop/archiver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
68
  atomicshop/archiver/_search_in_zip.py,sha256=dd8qFSvIhcKmtnPj_uYNJFPmMwZp4tZys0kKgTw_ACw,8385
69
69
  atomicshop/archiver/archiver.py,sha256=BomnK7zT-nQXA1z0i2R2aTv8eu88wPx7tf2HtOdbmEc,1280
70
- atomicshop/archiver/search_in_archive.py,sha256=mEngDfULXBd3Oio8a2SUtynfJASVLsH74XIYJOWVkH0,10467
71
- atomicshop/archiver/sevenz.py,sha256=5i_C50-deC1Cz_GQdMGofV2jeMPbbGWAvih-QnA72cg,1370
72
- atomicshop/archiver/zip.py,sha256=k742K1bEDtc_4N44j_Waebi-uOkxxavqltvV6q-BLW4,14402
70
+ atomicshop/archiver/search_in_archive.py,sha256=kui33oobL2F3VLgAE8L037rHR8Ud3HpXz_E0tbuiDD4,10473
71
+ atomicshop/archiver/sevenzs.py,sha256=5i_C50-deC1Cz_GQdMGofV2jeMPbbGWAvih-QnA72cg,1370
72
+ atomicshop/archiver/zips.py,sha256=k742K1bEDtc_4N44j_Waebi-uOkxxavqltvV6q-BLW4,14402
73
73
  atomicshop/basics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
74
  atomicshop/basics/ansi_escape_codes.py,sha256=WtIkm-BjSZS5J5irDUdAMBNvdX-qXFZcTX98jcBMpJE,3140
75
75
  atomicshop/basics/argparse_template.py,sha256=horwgSf3MX1ZgRnYxtmmQuz9OU_vKrKggF65gmjlmfg,5836
76
76
  atomicshop/basics/booleans.py,sha256=va3LYIaSOhjdifW4ZEesnIQxBICNHyQjUAkYelzchhE,2047
77
77
  atomicshop/basics/bytes_arrays.py,sha256=WvSRDhIGt1ywF95t-yNgpxLm1nlZUbM1Dz6QckcyE8Y,5915
78
78
  atomicshop/basics/classes.py,sha256=EijW_g4EhdNBnKPMG3nT3HjFspTchtM7to6zm9Ad_Mk,9771
79
- atomicshop/basics/dicts.py,sha256=N696f-vamrCcLpLOvtRpHrEmLfyOqkCyW8JDZnwYLpg,11295
79
+ atomicshop/basics/dicts.py,sha256=JevEkLsQdH4Tqn8rspSey40jW6h8pJ2SP5fcuFBR4dU,13651
80
80
  atomicshop/basics/dicts_nested.py,sha256=StYxYnYPa0SEJr1lmEwAv5zfERWWqoULeyG8e0zRAwE,4107
81
81
  atomicshop/basics/enumerations.py,sha256=41VVQYh_vnVapggxKg2IRU5e_EiMpZzX1n1mtxvoSzM,1364
82
82
  atomicshop/basics/enums.py,sha256=CeV8MfqWHihK7vvV6Mbzq7rD9JykeQfrJeFdLVzfHI4,3547
@@ -111,7 +111,7 @@ atomicshop/mitm/import_config.py,sha256=_V-IVJ7a1L6E-VOR4CDfZj-S1odbsIlBe13ij0Nl
111
111
  atomicshop/mitm/initialize_engines.py,sha256=UGdT5DKYNri3MNOxESP7oeSxYiUDrVilJ4jic_nwdew,8055
112
112
  atomicshop/mitm/initialize_mitm_server.py,sha256=aXNZlRu1_RGjC7lagvs2Q8rjQiygxYucy-U4C_SBnsk,13871
113
113
  atomicshop/mitm/message.py,sha256=u2U2f2SOHdBNU-6r1Ik2W14ai2EOwxUV4wVfGZA098k,1732
114
- atomicshop/mitm/shared_functions.py,sha256=NeHABBlY-tmQRooWGVl2jZQx1wSTKJtEqG7mMvF2Jqo,4268
114
+ atomicshop/mitm/shared_functions.py,sha256=PaK_sbnEA5zo9k2ktEOKLmvo-6wRUunxzSNRr41uXIQ,1924
115
115
  atomicshop/mitm/statistic_analyzer.py,sha256=1g5l6X-NbnHvh_TREJRumTDWgE4ixUNJ8pKGneKcf4Y,23524
116
116
  atomicshop/mitm/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
117
117
  atomicshop/mitm/engines/create_module_template.py,sha256=tRjVSm1sD6FzML71Qbuwvita0qsusdFGm8NZLsZ-XMs,4853
@@ -143,7 +143,7 @@ atomicshop/wrappers/astw.py,sha256=VkYfkfyc_PJLIOxByT6L7B8uUmKY6-I8XGZl4t_z828,4
143
143
  atomicshop/wrappers/configparserw.py,sha256=JwDTPjZoSrv44YKwIRcjyUnpN-FjgXVfMqMK_tJuSgU,22800
144
144
  atomicshop/wrappers/cryptographyw.py,sha256=H5NaHHDkr97QYhUrHFO9vY218u8k3N3Zgh6bQRnicUE,13140
145
145
  atomicshop/wrappers/ffmpegw.py,sha256=wcq0ZnAe0yajBOuTKZCCaKI7CDBjkq7FAgdW5IsKcVE,6031
146
- atomicshop/wrappers/githubw.py,sha256=AjD0VUlV2Kcddns2OaGmyX-FOAvdps-8SPSWS05E0QA,4809
146
+ atomicshop/wrappers/githubw.py,sha256=mQGtj6up1HIvjOD2t0bmOWjLooJLYvuIa7d7H-tknrw,9998
147
147
  atomicshop/wrappers/numpyw.py,sha256=sBV4gSKyr23kXTalqAb1oqttzE_2XxBooCui66jbAqc,1025
148
148
  atomicshop/wrappers/process_wrapper_pbtk.py,sha256=ycPmBRnv627RWks6N8OhxJQe8Gu3h3Vwj-4HswPOw0k,599
149
149
  atomicshop/wrappers/pyopensslw.py,sha256=OBWxA6EJ2vU_Qlf4M8m6ilcG3hyYB4yB0EsXUf7NhEU,6804
@@ -156,10 +156,10 @@ atomicshop/wrappers/dockerw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMp
156
156
  atomicshop/wrappers/dockerw/dockerw.py,sha256=w8zSJr5C7cbvbuG09ORCpAe0BOcibqqL_Z2EKEBHYK4,6266
157
157
  atomicshop/wrappers/dockerw/install_docker.py,sha256=eF0raR1EO9Xk1MVH7CvtkFI2Fgu9zL12MIYV_1vPTQk,4799
158
158
  atomicshop/wrappers/elasticsearchw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
159
- atomicshop/wrappers/elasticsearchw/config_basic.py,sha256=XJMKKfDrUq9ZKxYnQ-xFJxSA51z31Nn4eB8_n_hryVk,800
159
+ atomicshop/wrappers/elasticsearchw/config_basic.py,sha256=q9Ybr8TTeNPy6FxtiYCln8WNuwOQjdPazTRoGl7Lk8w,1066
160
160
  atomicshop/wrappers/elasticsearchw/elasticsearchw.py,sha256=7TqFdEFznO8NlligJhEKk1vm641ALpCYdaRl1uoXdzM,9768
161
- atomicshop/wrappers/elasticsearchw/infrastructure.py,sha256=J1hKIEQA8ImRUmkM25fbmwHbZPC9VnT9NK1_kOfXitA,8311
162
- atomicshop/wrappers/elasticsearchw/install_elastic.py,sha256=EeMZ2zSZIE3bvQ3HHJ4ed2TXLg1ufwKlDon79X5mhIU,8467
161
+ atomicshop/wrappers/elasticsearchw/infrastructure.py,sha256=Sy15gLVe_LAk9WpAoFxu5YApecFlYeJzzJ8lk5NYQEw,9217
162
+ atomicshop/wrappers/elasticsearchw/install_elastic.py,sha256=5CNgIoPIDnTCk5kU7Zcyb48TP4JScvUzeZmhcWt63sI,8754
163
163
  atomicshop/wrappers/elasticsearchw/queries/__init__.py,sha256=KBjT-bAt75CJsx1Apko9mpuFU4pfZV8DcGWQvpX65RU,78
164
164
  atomicshop/wrappers/elasticsearchw/queries/aggregation.py,sha256=N9a5yMMnb10sMa_x1qJBFQpgyJ49UWo8_vxuqmUtZ1A,1742
165
165
  atomicshop/wrappers/elasticsearchw/queries/info.py,sha256=P_VhhBo8MvRI4Shi-bh4RsYtlYNRKRBzScXPC64Up_Q,2900
@@ -177,7 +177,7 @@ atomicshop/wrappers/factw/fact_extractor/docker_image.py,sha256=jJAoJNQ4aoATjn3x
177
177
  atomicshop/wrappers/factw/fact_extractor/get_extractor.py,sha256=2mfOAftHIlCcGt1s7MWdq7DsDCuI6wX3MtvcEZ4SK-0,756
178
178
  atomicshop/wrappers/factw/install/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
179
179
  atomicshop/wrappers/factw/install/install_after_restart.py,sha256=roM1W2hkDynpEKda55xd7AsZxodsFj8i4wmFGt_HHzA,1558
180
- atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py,sha256=xPMYgXoyA7y1MHyG5paoqKTWuklhsSGym2ObPcex75M,3036
180
+ atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py,sha256=fAH53sJgWoXOOhjeAmME2SHYm-5YQAgEi1neaok82Lk,4246
181
181
  atomicshop/wrappers/factw/postgresql/__init__.py,sha256=xMBn2d3Exo23IPP2F_9-SXmOlhFbwWDgS9KwozSTjA0,162
182
182
  atomicshop/wrappers/factw/postgresql/analysis.py,sha256=2Rxzy2jyq3zEKIo53z8VkjuslKE_i5mq2ZpmJAvyd6U,716
183
183
  atomicshop/wrappers/factw/postgresql/file_object.py,sha256=VRiCXnsd6yDbnsE-TEKYPC-gkAgFVkE6rygRrJLQShI,713
@@ -199,7 +199,7 @@ atomicshop/wrappers/loggingw/loggers.py,sha256=DHOOTAtqkwn1xgvLHSkOiBm6yFGNuQy1k
199
199
  atomicshop/wrappers/loggingw/loggingw.py,sha256=v9WAseZXB50LluT9rIUcRvvevg2nLVKPgz3dbGejfV0,12151
200
200
  atomicshop/wrappers/loggingw/reading.py,sha256=xs7L6Jo-vedrhCVP7m-cJo0VhWmoSoK86avR4Tm0kG4,3675
201
201
  atomicshop/wrappers/nodejsw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
202
- atomicshop/wrappers/nodejsw/install_nodejs.py,sha256=an7zX_sEG4CF57tdeR3jldDSNnS8Z4vRfSY9y1OjF4g,1440
202
+ atomicshop/wrappers/nodejsw/install_nodejs.py,sha256=VZvefvdeSzoyetCx1y8zg_liX3_GtFtpOpjw7P5iMLk,4956
203
203
  atomicshop/wrappers/playwrightw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
204
204
  atomicshop/wrappers/playwrightw/_tryouts.py,sha256=l1BLkFsiIMNlgv7nfZd1XGEvXQkIQkIcg48__9OaC00,4920
205
205
  atomicshop/wrappers/playwrightw/base.py,sha256=WeRpx8otdXuKSr-BjY-uCJTze21kbPpfitoOjKQz5-g,9818
@@ -232,8 +232,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
232
232
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
233
233
  atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
234
234
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
235
- atomicshop-2.9.33.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
236
- atomicshop-2.9.33.dist-info/METADATA,sha256=xhGDHuJiZxOFiXby5r0kKvwrMjy6bJN59Vx0ynMYkFU,10423
237
- atomicshop-2.9.33.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
238
- atomicshop-2.9.33.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
239
- atomicshop-2.9.33.dist-info/RECORD,,
235
+ atomicshop-2.10.1.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
236
+ atomicshop-2.10.1.dist-info/METADATA,sha256=Bhbu_I_0laKqCOs51DFk4MH4r7BgeHumK5kATL_9cwM,10423
237
+ atomicshop-2.10.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
238
+ atomicshop-2.10.1.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
239
+ atomicshop-2.10.1.dist-info/RECORD,,
File without changes
File without changes