atomicshop 2.16.33__py3-none-any.whl → 2.16.35__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.16.33'
4
+ __version__ = '2.16.35'
@@ -0,0 +1,12 @@
1
+ #! /usr/bin/env python3
2
+ import sys
3
+
4
+ from atomicshop.wrappers.mongodbw import install_mongodb_ubuntu
5
+
6
+
7
+ def main():
8
+ install_mongodb_ubuntu.install_main(compass=True)
9
+
10
+
11
+ if __name__ == "__main__":
12
+ sys.exit(main())
@@ -1,3 +1,4 @@
1
+ #! /usr/bin/env python3
1
2
  import sys
2
3
 
3
4
  from atomicshop.wrappers.pycharmw import ubuntu
@@ -1,8 +1,8 @@
1
- from atomicshop.wrappers.mongodbw import install_mongodb
1
+ from atomicshop.wrappers.mongodbw import install_mongodb_win
2
2
 
3
3
 
4
4
  def main():
5
- install_mongodb.download_install_latest_main()
5
+ install_mongodb_win.download_install_latest_main()
6
6
 
7
7
 
8
8
  if __name__ == "__main__":
atomicshop/print_api.py CHANGED
@@ -128,7 +128,7 @@ def print_api(
128
128
  if print_end == '\n':
129
129
  if stdcolor and color is not None:
130
130
  # Use logger to output message.
131
- with loggingw.temporary_change_logger_stream_handler_emit_color(logger, color):
131
+ with loggingw.temporary_change_logger_stream_record_color(logger, color):
132
132
  getattr(logger, logger_method)(message)
133
133
  else:
134
134
  # Use logger to output message.
@@ -5,7 +5,7 @@ import datetime
5
5
  import contextlib
6
6
  import threading
7
7
 
8
- from . import loggers, handlers
8
+ from . import loggers, handlers, filters
9
9
  from ...file_io import csvs
10
10
  from ...basics import tracebacks, ansi_escape_codes
11
11
  from ... import print_api
@@ -308,10 +308,10 @@ def find_the_parent_logger_with_stream_handler(logger: logging.Logger) -> loggin
308
308
  @contextlib.contextmanager
309
309
  def _temporary_change_logger_stream_handler_color(logger: logging.Logger, color: str):
310
310
  """
311
- THIS IS ONLY FOR REFERENCE, for better result use the 'temporary_change_logger_stream_handler_emit_color' function.
311
+ THIS IS ONLY FOR REFERENCE.
312
+ Better use 'temporary_change_logger_stream_record_color', since it is thread safe.
312
313
  If there are several threads that use this logger, there could be a problem, since unwanted messages
313
- could be colored with the color of the other thread. 'temporary_change_logger_stream_handler_emit_color' is thread
314
- safe and will color only the messages from the current thread.
314
+ could be colored with the color of the other thread.
315
315
 
316
316
  Context manager to temporarily change the color of the logger's StreamHandler formatter.
317
317
 
@@ -351,13 +351,16 @@ def _temporary_change_logger_stream_handler_color(logger: logging.Logger, color:
351
351
  # found_stream_handler.removeFilter(color_filter)
352
352
 
353
353
 
354
- # Thread-local storage to store color codes per thread
355
- thread_local = threading.local()
356
-
357
-
358
354
  @contextlib.contextmanager
359
- def temporary_change_logger_stream_handler_emit_color(logger: logging.Logger, color: str):
360
- """Context manager to temporarily set the color code for log messages in the current thread."""
355
+ def temporary_change_logger_stream_record_color(logger: logging.Logger, color: str):
356
+ """
357
+ This function will temporarily change the color of the logger's StreamHandler record message.
358
+
359
+ Example:
360
+ with temporary_change_logger_stream_record_color(logger, "red"):
361
+ # Do something with the temporary color.
362
+ logger.error("This message will be colored with the 'red'.")
363
+ """
361
364
 
362
365
  # Find the current or the topmost logger's StreamHandler.
363
366
  # Could be that it is a child logger inherits its handlers from the parent.
@@ -369,32 +372,21 @@ def temporary_change_logger_stream_handler_emit_color(logger: logging.Logger, co
369
372
  found_stream_handler = handler
370
373
  break
371
374
 
372
- # Save the original emit method of the stream handler
373
- original_emit = found_stream_handler.emit
375
+ # Save the original state of the handler
376
+ # original_filters = found_stream_handler.filters.copy() # To restore the original filters
374
377
 
375
- def emit_with_color(record):
376
- original_msg = record.msg
377
- # Check if the current thread has a color code
378
- if getattr(thread_local, 'color', None):
379
- record.msg = (
380
- ansi_escape_codes.get_colors_basic_dict(color) + original_msg +
381
- ansi_escape_codes.ColorsBasic.END)
382
- original_emit(record) # Call the original emit method
383
- record.msg = original_msg # Restore the original message for other handlers
378
+ # Create a thread-specific color filter
379
+ thread_id = threading.get_ident()
380
+ color_filter = filters.ThreadColorLogFilter(color, thread_id)
384
381
 
385
- # Replace the emit method with our custom method
386
- found_stream_handler.emit = emit_with_color
387
-
388
- # Set the color code in thread-local storage for this thread
389
- thread_local.color = color
382
+ # Add the filter to the handler
383
+ found_stream_handler.addFilter(color_filter)
390
384
 
391
385
  try:
392
- yield
386
+ yield # Do the logging within the context
393
387
  finally:
394
- # Restore the original emit method after the context manager is exited
395
- found_stream_handler.emit = original_emit
396
- # Clear the color code from thread-local storage
397
- thread_local.color = None
388
+ # Restore the original filters, ensuring thread safety
389
+ found_stream_handler.removeFilter(color_filter)
398
390
 
399
391
 
400
392
  class ExceptionCsvLogger:
@@ -0,0 +1,100 @@
1
+ import subprocess
2
+ import sys
3
+ import os
4
+
5
+
6
+ from ... import print_api, web
7
+ from .. import ubuntu_terminal
8
+ from ...permissions import ubuntu_permissions
9
+
10
+
11
+ COMPASS_INSTALLATION_SCRIPT_URL: str = \
12
+ 'https://raw.githubusercontent.com/mongodb/mongo/master/src/mongo/installer/compass/install_compass'
13
+
14
+
15
+ def run_command(command):
16
+ """Run a system command and exit if the command fails."""
17
+ try:
18
+ subprocess.run(command, check=True, shell=True)
19
+ except subprocess.CalledProcessError as e:
20
+ print_api.print_api(f"Error: {e}", color='red')
21
+ sys.exit(1)
22
+
23
+
24
+ def install_mongodb(version):
25
+ """Install the specified major version of MongoDB on Ubuntu."""
26
+
27
+ if not version.endswith(".0"):
28
+ version = f"{version}.0"
29
+
30
+ print_api.print_api(f"Installing MongoDB {version} on Ubuntu...")
31
+ print_api.print_api(f"Installing Prerequisites...")
32
+ ubuntu_terminal.update_system_packages()
33
+ ubuntu_terminal.install_packages(["wget", "curl", "gnupg"])
34
+
35
+ # Step 1: Import the MongoDB public GPG key
36
+ print_api.print_api("Step 1: Importing the MongoDB public GPG key...")
37
+ run_command(f"curl -fsSL https://pgp.mongodb.com/server-{version}.asc | "
38
+ f"sudo gpg --dearmor -o /usr/share/keyrings/mongodb-server-{version}.gpg")
39
+
40
+ # Step 2: Create the MongoDB list file for APT
41
+ print_api.print_api("Step 2: Creating MongoDB APT list file...")
42
+ distro_version = subprocess.check_output("lsb_release -sc", shell=True).decode('utf-8').strip()
43
+ run_command(
44
+ f"echo 'deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-{version}.gpg ] "
45
+ f"https://repo.mongodb.org/apt/ubuntu {distro_version}/mongodb-org/{version} multiverse' | "
46
+ f"sudo tee /etc/apt/sources.list.d/mongodb-org-{version}.list")
47
+
48
+ # Step 3: Update the APT package index
49
+ print_api.print_api("Step 3: Updating the APT package index...")
50
+ ubuntu_terminal.update_system_packages()
51
+
52
+ # Step 4: Install the latest version of MongoDB for the specified major version
53
+ print_api.print_api(f"Step 4: Installing MongoDB version {version}...")
54
+ ubuntu_terminal.install_packages(["mongodb-org"])
55
+
56
+ # Step 5: Start MongoDB service and enable it on startup
57
+ print_api.print_api("Step 5: Starting MongoDB service and enabling it on startup...")
58
+ ubuntu_terminal.start_enable_service_check_availability("mongod")
59
+
60
+ print_api.print_api(f"MongoDB {version} installation complete!", color='green')
61
+
62
+
63
+ def install_main(
64
+ compass: bool = False,
65
+ ):
66
+ """
67
+ Install the latest minor version of MongoDB Community Server on Ubuntu by providing the major version.
68
+ :param compass: bool, if True, MongoDB Compass will be installed.
69
+ :return:
70
+ """
71
+ # Ensure the user provides a MongoDB major version as an argument.
72
+ if len(sys.argv) != 2:
73
+ message: str = ("Usage: python install_mongodb.py <mongo_major_version>\n"
74
+ "Example: python install_mongodb.py 7")
75
+ print_api.print_api(message, color='red')
76
+ return 1
77
+
78
+ mongo_version = sys.argv[1]
79
+
80
+ # Call the 'install' function with the major version.
81
+ install_mongodb(mongo_version)
82
+
83
+ if not compass:
84
+ return 0
85
+
86
+ # It doesn't matter what you do with the MSI it will not install Compass, only if you run it manually.
87
+ # So we will use installation script from their GitHub.
88
+ print_api.print_api("Downloading MongoDB Compass installation script...")
89
+ compass_script_path: str = web.download(COMPASS_INSTALLATION_SCRIPT_URL)
90
+
91
+ print_api.print_api("Installing MongoDB Compass from script...")
92
+ ubuntu_permissions.set_executable(compass_script_path)
93
+ run_command(f'sudo -E python3 {compass_script_path}')
94
+
95
+ # Clean up the installer file
96
+ if os.path.exists(compass_script_path):
97
+ os.remove(compass_script_path)
98
+ print_api.print_api("Cleaned up the Compass installer file.")
99
+
100
+ return 0
@@ -18,6 +18,18 @@ These are good examples to get you started, but can't really cover all the use c
18
18
  """
19
19
 
20
20
 
21
+ class MongoDBReplaceOneError(Exception):
22
+ pass
23
+
24
+
25
+ class MongoDBUpdateOneError(Exception):
26
+ pass
27
+
28
+
29
+ class MongoDBUpdateManyError(Exception):
30
+ pass
31
+
32
+
21
33
  class MongoDBWrapper:
22
34
  def __init__(
23
35
  self,
@@ -80,13 +92,15 @@ class MongoDBWrapper:
80
92
 
81
93
  def delete(
82
94
  self,
83
- object_instance: Union[list[dict], dict],
95
+ query_instance: Union[list[dict], dict],
84
96
  collection_name: str
85
97
  ):
86
98
  """
87
- Remove a list of dictionaries or a dictionary from a MongoDB collection.
99
+ Remove a dict or list of dictionaries or a dictionary from a MongoDB collection.
100
+ For pure mongo, this is the list of queries to remove.
101
+ Each query for a single item.
88
102
 
89
- :param object_instance: list of dictionaries, the list of dictionaries to remove from the collection.
103
+ :param query_instance: dict or list of dictionaries, the list of queries to remove from the collection.
90
104
  :param collection_name: str, the name of the collection.
91
105
 
92
106
  :return: None
@@ -95,7 +109,30 @@ class MongoDBWrapper:
95
109
  self.connect()
96
110
 
97
111
  delete(
98
- object_instance=object_instance,
112
+ query_instance=query_instance,
113
+ database=self.db, collection_name=collection_name,
114
+ mongo_client=self.client, close_client=False)
115
+
116
+ def delete_many(
117
+ self,
118
+ query: dict,
119
+ collection_name: str
120
+ ):
121
+ """
122
+ Remove all entries that match the query from a MongoDB collection.
123
+
124
+ :param query: dict, the query to search for.
125
+ Example, search for all entries with column name 'name' equal to 'John':
126
+ query = {'name': 'John'}
127
+ :param collection_name: str, the name of the collection.
128
+
129
+ :return: result of the operation.
130
+ """
131
+
132
+ self.connect()
133
+
134
+ return delete_many(
135
+ query=query,
99
136
  database=self.db, collection_name=collection_name,
100
137
  mongo_client=self.client, close_client=False)
101
138
 
@@ -203,6 +240,69 @@ class MongoDBWrapper:
203
240
 
204
241
  return distinct_values
205
242
 
243
+ def update(
244
+ self,
245
+ collection_name: str,
246
+ query: dict,
247
+ update_instance: Union[dict, list[dict]],
248
+ add_timestamp: bool = False,
249
+ convert_mixed_lists_to_strings: bool = False
250
+ ):
251
+ """
252
+ Update one entry in a MongoDB collection by query.
253
+ :param collection_name: str, the name of the collection.
254
+ :param query: dict, the query to search for.
255
+ Example, search for all entries with column name 'name' equal to 'John':
256
+ query = {'name': 'John'}
257
+ Find by Object id:
258
+ query = {'_id': ObjectId('5f3e3b3b4b9f3b3b4b9f3b3b')}
259
+ :param update_instance: dict or list of dicts, the update to apply.
260
+ Get examples for operators for each dict in the docstring of the function 'update' below.
261
+ :param add_timestamp: bool, if True, a current time timestamp will be added to the object.
262
+ :param convert_mixed_lists_to_strings: bool, if True, mixed lists or tuples when entries are
263
+ strings and integers, the integers will be converted to strings.
264
+ :return: result of the operation.
265
+ """
266
+
267
+ self.connect()
268
+
269
+ return update(
270
+ database=self.db, collection_name=collection_name,
271
+ query=query, update_instance=update_instance, add_timestamp=add_timestamp,
272
+ convert_mixed_lists_to_strings=convert_mixed_lists_to_strings,
273
+ mongo_client=self.client, close_client=False)
274
+
275
+ def replace(
276
+ self,
277
+ collection_name: str,
278
+ query: dict,
279
+ replacement: dict,
280
+ add_timestamp: bool = False,
281
+ convert_mixed_lists_to_strings: bool = False
282
+ ):
283
+ """
284
+ Replace one entry in a MongoDB collection by query.
285
+ :param collection_name: str, the name of the collection.
286
+ :param query: dict, the query to search for.
287
+ Example, search for all entries with column name 'name' equal to 'John':
288
+ query = {'name': 'John'}
289
+ Find by Object id:
290
+ query = {'_id': ObjectId('5f3e3b3b4b9f3b3b4b9f3b3b')}
291
+ :param replacement: dict, the replacement to apply.
292
+ :param add_timestamp: bool, if True, a current time timestamp will be added to the object.
293
+ :param convert_mixed_lists_to_strings: bool, if True, mixed lists or tuples when entries are
294
+
295
+ :return: result of the operation.
296
+ """
297
+
298
+ self.connect()
299
+
300
+ return replace(
301
+ database=self.db, collection_name=collection_name,
302
+ query=query, replacement=replacement,
303
+ add_timestamp=add_timestamp, convert_mixed_lists_to_strings=convert_mixed_lists_to_strings,
304
+ mongo_client=self.client, close_client=False)
305
+
206
306
  def count_entries_in_collection(
207
307
  self,
208
308
  collection_name: str,
@@ -328,7 +428,11 @@ def index(
328
428
  collection = db[collection_name]
329
429
 
330
430
  if convert_mixed_lists_to_strings:
331
- object_instance = dicts.convert_int_to_str_in_mixed_lists(object_instance)
431
+ if isinstance(object_instance, dict):
432
+ object_instance = dicts.convert_int_to_str_in_mixed_lists(object_instance)
433
+ elif isinstance(object_instance, list):
434
+ for doc_index, doc in enumerate(object_instance):
435
+ object_instance[doc_index] = dicts.convert_int_to_str_in_mixed_lists(doc)
332
436
 
333
437
  if add_timestamp:
334
438
  timestamp = datetime.datetime.now()
@@ -348,16 +452,18 @@ def index(
348
452
 
349
453
 
350
454
  def delete(
351
- object_instance: Union[list[dict], dict],
455
+ query_instance: Union[list[dict], dict],
352
456
  database: Union[str, pymongo.database.Database],
353
457
  collection_name: str,
354
458
  mongo_client: pymongo.MongoClient = None,
355
459
  close_client: bool = False
356
460
  ):
357
461
  """
358
- Remove a list of dictionaries or a dictionary from a MongoDB collection.
462
+ Remove a dict or list of dictionaries or a dictionary from a MongoDB collection.
359
463
 
360
- :param object_instance: list of dictionaries, the list of dictionaries to remove from the collection.
464
+ :param query_instance: list of dictionaries, the list of dictionaries to remove from the collection.
465
+ For pure mongo, this is the list of queries to remove.
466
+ Each query for a single item.
361
467
  :param database: String or the database object.
362
468
  str - the name of the database. In this case the database object will be created.
363
469
  pymongo.database.Database - the database object that will be used instead of creating a new one.
@@ -369,7 +475,7 @@ def delete(
369
475
  :return: None
370
476
  """
371
477
 
372
- _is_object_list_of_dicts_or_dict(object_instance)
478
+ _is_object_list_of_dicts_or_dict(query_instance)
373
479
 
374
480
  if not mongo_client:
375
481
  mongo_client = connect()
@@ -378,16 +484,55 @@ def delete(
378
484
  db = _get_pymongo_db_from_string_or_pymongo_db(database, mongo_client)
379
485
  collection = db[collection_name]
380
486
 
381
- if isinstance(object_instance, dict):
382
- collection.delete_one(object_instance)
383
- elif isinstance(object_instance, list):
384
- for doc in object_instance:
487
+ if isinstance(query_instance, dict):
488
+ collection.delete_one(query_instance)
489
+ elif isinstance(query_instance, list):
490
+ for doc in query_instance:
385
491
  collection.delete_one(doc)
386
492
 
387
493
  if close_client:
388
494
  mongo_client.close()
389
495
 
390
496
 
497
+ def delete_many(
498
+ query: dict,
499
+ database: Union[str, pymongo.database.Database],
500
+ collection_name: str,
501
+ mongo_client: pymongo.MongoClient = None,
502
+ close_client: bool = False
503
+ ):
504
+ """
505
+ Remove all entries that match the query from a MongoDB collection.
506
+
507
+ :param query: dict, the query to search for.
508
+ Example, search for all entries with column name 'name' equal to 'John':
509
+ query = {'name': 'John'}
510
+ :param database: String or the database object.
511
+ str - the name of the database. In this case the database object will be created.
512
+ pymongo.database.Database - the database object that will be used instead of creating a new one.
513
+ :param collection_name: str, the name of the collection.
514
+ :param mongo_client: pymongo.MongoClient, the connection object.
515
+ If None, a new connection will be created to default URI.
516
+ :param close_client: bool, if True, the connection will be closed after the operation.
517
+
518
+ :return: result of the operation.
519
+ """
520
+
521
+ if not mongo_client:
522
+ mongo_client = connect()
523
+ close_client = True
524
+
525
+ db = _get_pymongo_db_from_string_or_pymongo_db(database, mongo_client)
526
+ collection = db[collection_name]
527
+
528
+ result = collection.delete_many(query)
529
+
530
+ if close_client:
531
+ mongo_client.close()
532
+
533
+ return result
534
+
535
+
391
536
  def find(
392
537
  database: Union[str, pymongo.database.Database],
393
538
  collection_name: str,
@@ -614,6 +759,145 @@ def distinct(
614
759
  return distinct_values
615
760
 
616
761
 
762
+ def update(
763
+ database: Union[str, pymongo.database.Database],
764
+ collection_name: str,
765
+ query: dict,
766
+ update_instance: Union[dict, list[dict]],
767
+ add_timestamp: bool = False,
768
+ convert_mixed_lists_to_strings: bool = False,
769
+ mongo_client: pymongo.MongoClient = None,
770
+ close_client: bool = False
771
+ ):
772
+ """
773
+ Update one entry in a MongoDB collection by query.
774
+ :param database: String or the database object.
775
+ str - the name of the database. In this case the database object will be created.
776
+ pymongo.database.Database - the database object that will be used instead of creating a new one.
777
+ :param collection_name: str, the name of the collection.
778
+ :param query: dict, the query to search for.
779
+ Example, search for all entries with column name 'name' equal to 'John':
780
+ query = {'name': 'John'}
781
+ Find by Object id:
782
+ query = {'_id': ObjectId('5f3e3b3b4b9f3b3b4b9f3b3b')}
783
+ :param update_instance: dict or list of dicts, the update to apply.
784
+ If dict, the update will be applied to one entry using 'update_one'.
785
+ If list of dicts, the update will be applied to multiple entries using 'update_many'.
786
+
787
+ Examples for operators for each dict:
788
+ $set: update the column 'name' to 'Alice':
789
+ update_instance = {'$set': {'name': 'Alice'}}
790
+ $inc: increment the column 'age' by 1:
791
+ update_instance = {'$inc': {'age': 1}}
792
+ $unset: remove the column 'name':
793
+ update_instance = {'$unset': {'name': ''}}
794
+ $push: add a value to the list 'hobbies':
795
+ update_instance = {'$push': {'hobbies': 'swimming'}}
796
+ $pull: remove a value from the list 'hobbies':
797
+ update_instance = {'$pull': {'hobbies': 'swimming'}}
798
+ :param add_timestamp: bool, if True, a current time timestamp will be added to the object.
799
+ :param convert_mixed_lists_to_strings: bool, if True, mixed lists or tuples when entries are
800
+ strings and integers, the integers will be converted to strings.
801
+ :param mongo_client: pymongo.MongoClient, the connection object.
802
+ If None, a new connection will be created to default URI.
803
+ :param close_client: bool, if True, the connection will be closed after the operation.
804
+
805
+ :return: None
806
+ """
807
+
808
+ if not mongo_client:
809
+ mongo_client = connect()
810
+ close_client = True
811
+
812
+ db = _get_pymongo_db_from_string_or_pymongo_db(database, mongo_client)
813
+ collection = db[collection_name]
814
+
815
+ if convert_mixed_lists_to_strings:
816
+ if isinstance(update_instance, dict):
817
+ object_instance = dicts.convert_int_to_str_in_mixed_lists(update_instance)
818
+ elif isinstance(update_instance, list):
819
+ for doc_index, doc in enumerate(update_instance):
820
+ update_instance[doc_index] = dicts.convert_int_to_str_in_mixed_lists(doc)
821
+
822
+ if add_timestamp:
823
+ timestamp = datetime.datetime.now()
824
+ if isinstance(update_instance, dict):
825
+ update_instance['timestamp'] = timestamp
826
+ elif isinstance(update_instance, list):
827
+ for doc in update_instance:
828
+ doc['timestamp'] = timestamp
829
+
830
+ result = None
831
+ if isinstance(update_instance, dict):
832
+ result = collection.update_one(query, update_instance)
833
+ elif isinstance(update_instance, list):
834
+ result = collection.update_many(query, update_instance)
835
+
836
+ if result.matched_count == 0:
837
+ raise MongoDBUpdateOneError("No document found to update.")
838
+
839
+ if close_client:
840
+ mongo_client.close()
841
+
842
+ return result
843
+
844
+
845
+ def replace(
846
+ database: Union[str, pymongo.database.Database],
847
+ collection_name: str,
848
+ query: dict,
849
+ replacement: dict,
850
+ add_timestamp: bool = False,
851
+ convert_mixed_lists_to_strings: bool = False,
852
+ mongo_client: pymongo.MongoClient = None,
853
+ close_client: bool = False
854
+ ):
855
+ """
856
+ Replace one entry in a MongoDB collection by query.
857
+ :param database: String or the database object.
858
+ str - the name of the database. In this case the database object will be created.
859
+ pymongo.database.Database - the database object that will be used instead of creating a new one.
860
+ :param collection_name: str, the name of the collection.
861
+ :param query: dict, the query to search for.
862
+ Example, search for all entries with column name 'name' equal to 'John':
863
+ query = {'name': 'John'}
864
+ Find by Object id:
865
+ query = {'_id': ObjectId('5f3e3b3b4b9f3b3b4b9f3b3b')}
866
+ :param replacement: dict, the replacement to apply.
867
+ :param add_timestamp: bool, if True, a current time timestamp will be added to the object.
868
+ :param convert_mixed_lists_to_strings: bool, if True, mixed lists or tuples when entries are strings and integers,
869
+ the integers will be converted to strings.
870
+ :param mongo_client: pymongo.MongoClient, the connection object.
871
+ If None, a new connection will be created to default URI.
872
+ :param close_client: bool, if True, the connection will be closed after the operation.
873
+
874
+ :return: None
875
+ """
876
+
877
+ if not mongo_client:
878
+ mongo_client = connect()
879
+ close_client = True
880
+
881
+ db = _get_pymongo_db_from_string_or_pymongo_db(database, mongo_client)
882
+ collection = db[collection_name]
883
+
884
+ if convert_mixed_lists_to_strings:
885
+ replacement = dicts.convert_int_to_str_in_mixed_lists(replacement)
886
+
887
+ if add_timestamp:
888
+ timestamp = datetime.datetime.now()
889
+ replacement['timestamp'] = timestamp
890
+
891
+ result = collection.replace_one(query, replacement)
892
+ if result.matched_count == 0:
893
+ raise MongoDBReplaceOneError("No document found to replace.")
894
+
895
+ if close_client:
896
+ mongo_client.close()
897
+
898
+ return result
899
+
900
+
617
901
  def count_entries_in_collection(
618
902
  database: Union[str, pymongo.database.Database],
619
903
  collection_name: str,
@@ -73,20 +73,12 @@ class GetCommandLine:
73
73
  print_api(
74
74
  execution_error, error_type=True, logger_method="error", traceback_string=True,
75
75
  **print_kwargs)
76
- pass
77
76
  except psutil.AccessDenied:
78
77
  execution_error = f"Access Denied for 'psutil' to read system process command line. " \
79
78
  f"Run script with Admin Rights."
80
79
  print_api(
81
80
  execution_error, error_type=True, logger_method="error", traceback_string=True,
82
81
  **print_kwargs)
83
- pass
84
- except Exception:
85
- execution_error = "There was undocumented exception in localhost script execution."
86
- print_api(
87
- execution_error, error_type=True, logger_method="error", traceback_string=True,
88
- **print_kwargs)
89
- pass
90
82
 
91
83
  if not execution_error:
92
84
  # Reading the buffer.
@@ -97,8 +89,8 @@ class GetCommandLine:
97
89
 
98
90
  return process_name
99
91
 
92
+ @staticmethod
100
93
  def get_commandline_and_error(
101
- self,
102
94
  execution_output,
103
95
  execution_error,
104
96
  print_kwargs: dict = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.16.33
3
+ Version: 2.16.35
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=Q8UxOCNLGPrZ4zmxZygiIYY6-S1LBodk2crxUcbd0aQ,124
1
+ atomicshop/__init__.py,sha256=UtKTcWqPrVHpu6j_BPCWMmIdo2RCZbCRac7Es1CoDwI,124
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
@@ -25,7 +25,7 @@ atomicshop/ip_addresses.py,sha256=Hvi4TumEFoTEpKWaq5WNF-YzcRzt24IxmNgv-Mgax1s,11
25
25
  atomicshop/keyboard_press.py,sha256=1W5kRtOB75fulVx-uF2yarBhW0_IzdI1k73AnvXstk0,452
26
26
  atomicshop/on_exit.py,sha256=Rpg2SaF0aginuO7JYwA49YJYnS8F6K2jUqhjH65WzuU,6889
27
27
  atomicshop/pbtkmultifile_argparse.py,sha256=aEk8nhvoQVu-xyfZosK3ma17CwIgOjzO1erXXdjwtS4,4574
28
- atomicshop/print_api.py,sha256=REkd1W3--g1Av4_nH3M2CvQCIEDecfsT7spMXgIRUJE,11158
28
+ atomicshop/print_api.py,sha256=q9dAQCASk3pHp_PtYIpr6iZmRcW_lvrV_gPPNwTMRsw,11152
29
29
  atomicshop/process.py,sha256=PeLvyixXaCfftdUF3oMbohI1L4MdLtvQVDx2V1Tz_Rk,16662
30
30
  atomicshop/python_file_patcher.py,sha256=-uhbUX-um5k-If_XXuOfCr8wMzZ3QE6h9N8xGWw6W_o,5486
31
31
  atomicshop/python_functions.py,sha256=zJg4ogUwECxrDD7xdDN5JikIUctITM5lsyabr_ZNsRw,4435
@@ -48,9 +48,10 @@ atomicshop/web.py,sha256=DkpdUYiwRu_Du5J8LlUyxLj-NAEnDLfEwfVlSI22trM,11642
48
48
  atomicshop/a_installs/ubuntu/docker_rootless.py,sha256=9IPNtGZYjfy1_n6ZRt7gWz9KZgR6XCgevjqq02xk-o0,281
49
49
  atomicshop/a_installs/ubuntu/docker_sudo.py,sha256=JzayxeyKDtiuT4Icp2L2LyFRbx4wvpyN_bHLfZ-yX5E,281
50
50
  atomicshop/a_installs/ubuntu/elastic_search_and_kibana.py,sha256=yRB-l1zBxdiN6av-FwNkhcBlaeu4zrDPjQ0uPGgpK2I,244
51
- atomicshop/a_installs/ubuntu/pycharm.py,sha256=eFk_JDIzJJhxCuYFkjxOYHYMnZBC62Q9n5ZaFt4z9cs,132
51
+ atomicshop/a_installs/ubuntu/mongodb.py,sha256=xuRJS1qqOZ0EZp7of5R3tTjSu6CwBIxYg8-NaM7othE,230
52
+ atomicshop/a_installs/ubuntu/pycharm.py,sha256=Ld7YQBwPxrjuZcTG1K4kZqjbBdt8aooCVRa15u5FOmE,157
52
53
  atomicshop/a_installs/win/fibratus.py,sha256=TU4e9gdZ_zI73C40uueJ59pD3qmN-UFGdX5GFoVf6cM,179
53
- atomicshop/a_installs/win/mongodb.py,sha256=5k9sFWM_9Zh5ShutH2IhGvAo7nrLkOIeUPzhoKvEsx8,171
54
+ atomicshop/a_installs/win/mongodb.py,sha256=AqyItXu19aaoe49pppDxtEkXey6PMy0PoT2Y_RmPpPE,179
54
55
  atomicshop/a_installs/win/pycharm.py,sha256=j_RSd7aDOyC3yDd-_GUTMLlQTmDrqtVFG--oUfGLiZk,140
55
56
  atomicshop/a_installs/win/wsl_ubuntu_lts.py,sha256=dZbPRLNKFeMd6MotjkE6UDY9cOiIaaclIdR1kGYWI50,139
56
57
  atomicshop/a_mains/dns_gateway_setting.py,sha256=ncc2rFQCChxlNP59UshwmTonLqC6MWblrVAzbbz-13M,149
@@ -250,12 +251,13 @@ atomicshop/wrappers/loggingw/filters.py,sha256=48UVhJHemCS0agXmQP8dHvAHM8r9DFphJ
250
251
  atomicshop/wrappers/loggingw/formatters.py,sha256=ZY12IokVY1G_Wzn2Zlv9qjK-e8CtIK6yUgUfPHvH2BU,5802
251
252
  atomicshop/wrappers/loggingw/handlers.py,sha256=vxaSSnlJGs9NKJvYROKtNjaFTqePdHy0sz-GwN5aNPw,19035
252
253
  atomicshop/wrappers/loggingw/loggers.py,sha256=mmM__XR3W4QC82wbsDRG_M4_0JYGGEP0Qn0WCOSp-go,2910
253
- atomicshop/wrappers/loggingw/loggingw.py,sha256=64r5XZSAwJ5GfkN7JqAvuLFlJRdf79n0jr_FriaaaCw,21330
254
+ atomicshop/wrappers/loggingw/loggingw.py,sha256=uLY7DJS-3xIYQBRvI--9eFvdcnvsWSXmtJKS-gTRfjM,20863
254
255
  atomicshop/wrappers/loggingw/reading.py,sha256=sCNlgqLNH5XdKqOOjjEox7CvViMHzs6h7-hwCnx4NKk,17566
255
256
  atomicshop/wrappers/mongodbw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
256
- atomicshop/wrappers/mongodbw/install_mongodb.py,sha256=3ZPqrXxj3lC-PnAKGXclylLuOqsbyXYeUpb5iGjdeUU,6626
257
+ atomicshop/wrappers/mongodbw/install_mongodb_ubuntu.py,sha256=pmI9AwWJ2cv5h8GionSpSJkllg6kfp0M381pk6y4Y5U,4015
258
+ atomicshop/wrappers/mongodbw/install_mongodb_win.py,sha256=3ZPqrXxj3lC-PnAKGXclylLuOqsbyXYeUpb5iGjdeUU,6626
257
259
  atomicshop/wrappers/mongodbw/mongo_infra.py,sha256=IjEF0jPzQz866MpTm7rnksnyyWQeUT_B2h2DA9ryAio,2034
258
- atomicshop/wrappers/mongodbw/mongodbw.py,sha256=oM2pS-M0EI7HhewWY0ri_Ri9U5GOBo0CehPmo4Yas3o,32736
260
+ atomicshop/wrappers/mongodbw/mongodbw.py,sha256=DlKiZaB__OIkWNQgQxKD569qrzm1l7m1TWKH2NTe8qs,44278
259
261
  atomicshop/wrappers/nodejsw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
260
262
  atomicshop/wrappers/nodejsw/install_nodejs.py,sha256=TKGa3jSlSqZTL2NA0nMkWDFtlkz7rxGGn44ywCg7MN8,5228
261
263
  atomicshop/wrappers/playwrightw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -299,7 +301,7 @@ atomicshop/wrappers/socketw/certificator.py,sha256=3CpQKtcW68FSbH6LVSEZTqWBS6Yg_
299
301
  atomicshop/wrappers/socketw/creator.py,sha256=3_OraDkw2DAWZfoSdY3svCGMOIxpjLEEY7NxWd7M5P4,9873
300
302
  atomicshop/wrappers/socketw/dns_server.py,sha256=VHV6s7vd0zqqW3dhE6li-260YRzmEB5ZUXqYJ9p0vVA,49069
301
303
  atomicshop/wrappers/socketw/exception_wrapper.py,sha256=B-X5SHLSUIWToihH2MKnOB1F4A81_X0DpLLfnYKYbEc,7067
302
- atomicshop/wrappers/socketw/get_process.py,sha256=zKEqh98cB9UDLFhtxVpperfXsCjyIMNANHilDD06p0U,6094
304
+ atomicshop/wrappers/socketw/get_process.py,sha256=aJC-_qFUv3NgWCSUzDI72E4z8_-VTZE9NVZ0CwUoNlM,5698
303
305
  atomicshop/wrappers/socketw/receiver.py,sha256=XVvWOoeCo3vA0O5p19ryi-hcDIyx382WNG7WzMNVeYk,9322
304
306
  atomicshop/wrappers/socketw/sender.py,sha256=5HPrgTS2pA1g-jbG1TUtR7drHT1Z_8UevlRCTwW7dgY,5007
305
307
  atomicshop/wrappers/socketw/sni.py,sha256=J1kPnQ77XwKN1pO5aOI1c_VfhuivCm95OOaQxMpPuZ0,17627
@@ -310,8 +312,8 @@ atomicshop/wrappers/socketw/ssl_base.py,sha256=kmiif84kMhBr5yjQW17p935sfjR5JKG0L
310
312
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=w1AH-zf4mBuT4euf28UKij9ihM-b1BRU9Qfby0QDdqI,2957
311
313
  atomicshop/wrappers/winregw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
312
314
  atomicshop/wrappers/winregw/winreg_network.py,sha256=bQ8Jql8bVGBJ0dt3VQ56lga_1LBOMLI3Km_otvvbU6c,7138
313
- atomicshop-2.16.33.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
314
- atomicshop-2.16.33.dist-info/METADATA,sha256=snw-nClHcOxtJnor7m7ldSl-0Lu3-R5jfEZ9l53m3WU,10473
315
- atomicshop-2.16.33.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
316
- atomicshop-2.16.33.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
317
- atomicshop-2.16.33.dist-info/RECORD,,
315
+ atomicshop-2.16.35.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
316
+ atomicshop-2.16.35.dist-info/METADATA,sha256=Dbczjg5jtonq5G-BaD0Hqj93hiHQhsodw5FN3Ytv6UM,10473
317
+ atomicshop-2.16.35.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
318
+ atomicshop-2.16.35.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
319
+ atomicshop-2.16.35.dist-info/RECORD,,