atomicshop 2.14.7__py3-none-any.whl → 2.14.9__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.14.7'
4
+ __version__ = '2.14.9'
@@ -0,0 +1,79 @@
1
+ import logging
2
+ import os
3
+
4
+
5
+ class HeaderFilter(logging.Filter):
6
+ """
7
+ A logging.Filter that writes a header to a log file if the file is empty (
8
+ i.e., no log records have been written, i.e.2, on file rotation).
9
+ """
10
+ def __init__(self, header, baseFilename):
11
+ super().__init__()
12
+ self.header = header
13
+ self.baseFilename = baseFilename
14
+ self._write_header_if_needed()
15
+
16
+ def _write_header_if_needed(self):
17
+ if not os.path.exists(self.baseFilename) or os.path.getsize(self.baseFilename) == 0:
18
+ self._write_header()
19
+
20
+ def _write_header(self):
21
+ if self.header:
22
+ with open(self.baseFilename, 'a') as f:
23
+ f.write(self.header + '\n')
24
+
25
+ def filter(self, record):
26
+ self._write_header_if_needed()
27
+ return True
28
+
29
+
30
+ """
31
+ A logging.Filter in Python's logging module is an object that provides a way to perform fine-grained
32
+ filtering of log records.
33
+ It allows you to control which log records are passed through and which are filtered out,
34
+ based on specific criteria you define.
35
+
36
+ Basic Concepts of logging.Filter
37
+ Purpose: Filters are used to allow or deny log records from being processed further.
38
+ This can be based on various criteria, such as the level of the log record, the source logger, or custom attributes.
39
+ Implementation: Filters are typically subclasses of logging.Filter,
40
+ but they can also be any callable that accepts a log record and returns a boolean value.
41
+
42
+ How logging.Filter Works
43
+ When a log record is emitted, it is passed through any filters attached to the logger or the handler.
44
+ If the filter returns True, the log record is processed. If the filter returns False, the log record is ignored.
45
+
46
+ Example of logging.Filter
47
+ Here’s a simple example to demonstrate the use of logging.Filter:
48
+
49
+ Create a Filter: Subclass logging.Filter and override the filter method.
50
+ import logging
51
+
52
+ class MyFilter(logging.Filter):
53
+ def filter(self, record):
54
+ # Example: Allow only log records with a level of WARNING or higher
55
+ return record.levelno >= logging.WARNING
56
+
57
+ Attach the Filter to a Handler or Logger:
58
+ # Create a logger
59
+ logger = logging.getLogger('my_logger')
60
+ logger.setLevel(logging.DEBUG)
61
+
62
+ # Create a console handler
63
+ console_handler = logging.StreamHandler()
64
+
65
+ # Create an instance of the custom filter
66
+ my_filter = MyFilter()
67
+
68
+ # Add the filter to the handler
69
+ console_handler.addFilter(my_filter)
70
+
71
+ # Add the handler to the logger
72
+ logger.addHandler(console_handler)
73
+
74
+ # Log some messages
75
+ logger.debug('This is a debug message') # Will be filtered out
76
+ logger.info('This is an info message') # Will be filtered out
77
+ logger.warning('This is a warning message') # Will be displayed
78
+ logger.error('This is an error message') # Will be displayed
79
+ """
@@ -1,6 +1,5 @@
1
1
  import logging
2
2
  from logging.handlers import TimedRotatingFileHandler, QueueListener, QueueHandler
3
- from logging import FileHandler
4
3
  import time
5
4
  import re
6
5
  import os
@@ -10,65 +9,15 @@ from typing import Literal, Union
10
9
  import threading
11
10
  from datetime import datetime
12
11
 
13
- from . import loggers, formatters
12
+ from . import loggers, formatters, filters
14
13
  from ... import datetimes, filesystem
15
14
 
16
15
 
17
- DEFAULT_DATE_STRING_FORMAT: str = "%Y_%m_%d"
16
+ """
18
17
  # Not used, only for the reference:
19
- # _DEFAULT_DATE_REGEX_PATTERN: str = r"^\d{4}_\d{2}_\d{2}$"
20
-
21
-
22
- class ForceAtTimeRotationTimedRotatingFileHandler(TimedRotatingFileHandler):
23
- def __init__(self, *args, **kwargs):
24
- super().__init__(*args, **kwargs)
25
- self._last_rotated_date = None
26
- self._start_rotation_check()
27
-
28
- def _start_rotation_check(self):
29
- self._rotation_thread = threading.Thread(target=self._check_for_rotation)
30
- self._rotation_thread.daemon = True
31
- self._rotation_thread.start()
32
-
33
- def _check_for_rotation(self):
34
- while True:
35
- now = datetime.now()
36
- current_date = now.date()
37
- # Check if it's midnight and the logs haven't been rotated today
38
- if now.hour == 0 and now.minute == 0 and current_date != self._last_rotated_date:
39
- self._last_rotated_date = current_date
40
- self.doRollover()
41
- time.sleep(0.1)
42
-
43
- def doRollover(self):
44
- super().doRollover()
45
-
46
-
47
- class TimedRotatingFileHandlerWithHeader(ForceAtTimeRotationTimedRotatingFileHandler):
48
- """
49
- Custom TimedRotatingFileHandler that writes a header to the log file each time there is a file rotation.
50
- Useful for writing CSV files.
51
-
52
- :param header: string, Header to write to the log file.
53
- Example: "time,host,error"
54
- """
55
- def __init__(self, *args, **kwargs):
56
- self.header = kwargs.pop('header', None)
57
- super().__init__(*args, **kwargs)
58
-
59
- def doRollover(self):
60
- super().doRollover()
61
- self._write_header()
62
-
63
- def _write_header(self):
64
- if self.header:
65
- with open(self.baseFilename, 'a') as f:
66
- f.write(self.header + '\n')
67
-
68
- def emit(self, record):
69
- if not os.path.exists(self.baseFilename) or os.path.getsize(self.baseFilename) == 0:
70
- self._write_header()
71
- super().emit(record)
18
+ DEFAULT_DATE_STRING_FORMAT: str = "%Y_%m_%d"
19
+ DEFAULT_DATE_REGEX_PATTERN: str = r"^\d{4}_\d{2}_\d{2}$"
20
+ """
72
21
 
73
22
 
74
23
  def _process_formatter_attribute(
@@ -133,6 +82,48 @@ def add_stream_handler(
133
82
  loggers.set_propagation(logger)
134
83
 
135
84
 
85
+ # Function to start the interval-based rotation check
86
+ def _start_interval_rotation(handler):
87
+ def check_rotation():
88
+ while True:
89
+ next_rollover = _calculate_next_rollover()
90
+ while datetime.now() < next_rollover:
91
+ time.sleep(0.1)
92
+
93
+ # Check if the next_rollover has changed (indicating a rollover by an event)
94
+ if _calculate_next_rollover() != next_rollover:
95
+ next_rollover = _calculate_next_rollover()
96
+ break
97
+
98
+ # Perform manual rollover if needed
99
+ if datetime.now() >= next_rollover:
100
+ _rotate_log()
101
+
102
+ def _calculate_next_rollover():
103
+ return datetime.fromtimestamp(handler.rolloverAt)
104
+
105
+ # Function to rotate logs
106
+ def _rotate_log():
107
+ handler.doRollover()
108
+
109
+ rotation_thread = threading.Thread(target=check_rotation)
110
+ rotation_thread.daemon = True
111
+ rotation_thread.start()
112
+
113
+
114
+ def _wrap_do_rollover(handler, header):
115
+ original_do_rollover = handler.doRollover
116
+
117
+ def new_do_rollover():
118
+ original_do_rollover()
119
+ # After rollover, write the header
120
+ if header:
121
+ with open(handler.baseFilename, 'a') as f:
122
+ f.write(header + '\n')
123
+
124
+ handler.doRollover = new_do_rollover
125
+
126
+
136
127
  def add_timedfilehandler_with_queuehandler(
137
128
  logger: logging.Logger,
138
129
  file_path: str,
@@ -146,6 +137,10 @@ def add_timedfilehandler_with_queuehandler(
146
137
  str,
147
138
  None] = None,
148
139
  formatter_use_nanoseconds: bool = False,
140
+ rotate_at_rollover_time: bool = True,
141
+ rotation_date_format: str = None,
142
+ rotation_callback_namer_function: callable = None,
143
+ rotation_use_default_callback_namer_function: bool = True,
149
144
  when: str = 'midnight',
150
145
  interval: int = 1,
151
146
  delay: bool = True,
@@ -158,10 +153,6 @@ def add_timedfilehandler_with_queuehandler(
158
153
  This is needed, since TimedRotatingFileHandler is not thread-safe, though official docs say it is.
159
154
  """
160
155
 
161
- # If file name wasn't provided we will use the logger name instead.
162
- # if not file_name_no_extension:
163
- # file_name_no_extension = logger.name
164
-
165
156
  # Setting the TimedRotatingFileHandler, without adding it to the logger.
166
157
  # It will be added to the QueueListener, which will use the TimedRotatingFileHandler to write logs.
167
158
  # This is needed since there's a bug in TimedRotatingFileHandler, which won't let it be used with
@@ -172,15 +163,8 @@ def add_timedfilehandler_with_queuehandler(
172
163
 
173
164
  filesystem.create_directory(os.path.dirname(file_path))
174
165
 
175
- if file_type == "csv":
176
- # If file extension is CSV, we'll set the header to the file.
177
- # This is needed since the CSV file will be rotated, and we'll need to set the header each time.
178
- # We'll use the custom TimedRotatingFileHandlerWithHeader class.
179
- file_handler = get_timed_rotating_file_handler_with_header(
180
- file_path, when=when, interval=interval, delay=delay, encoding=encoding, header=header)
181
- else:
182
- file_handler = get_timed_rotating_file_handler(
183
- file_path, when=when, interval=interval, delay=delay, encoding=encoding)
166
+ file_handler = get_timed_rotating_file_handler(
167
+ file_path, when=when, interval=interval, delay=delay, encoding=encoding)
184
168
 
185
169
  loggers.set_logging_level(file_handler, logging_level)
186
170
 
@@ -195,7 +179,23 @@ def add_timedfilehandler_with_queuehandler(
195
179
  set_formatter(file_handler, logging_formatter)
196
180
 
197
181
  # This function will change the suffix behavior of the rotated file name.
198
- change_rotated_filename(file_handler)
182
+ change_rotated_filename(
183
+ file_handler=file_handler, date_format_string=rotation_date_format,
184
+ callback_namer_function=rotation_callback_namer_function,
185
+ use_default_callback_namer_function=rotation_use_default_callback_namer_function
186
+ )
187
+
188
+ # If header is set, we'll add the filter to the handler that will create the header on file rotation.
189
+ if header:
190
+ # Filter is added to write header on logger startup.
191
+ add_filter_to_handler(file_handler, filters.HeaderFilter(header, file_handler.baseFilename))
192
+ # Wrap the doRollover method to write the header after each rotation, since adding the filter
193
+ # will only write the header on log file creation.
194
+ _wrap_do_rollover(file_handler, header)
195
+
196
+ # Start the interval-based rotation forcing.
197
+ if rotate_at_rollover_time:
198
+ _start_interval_rotation(file_handler)
199
199
 
200
200
  queue_handler = start_queue_listener_for_file_handler_and_get_queue_handler(file_handler)
201
201
  loggers.set_logging_level(queue_handler, logging_level)
@@ -242,7 +242,7 @@ def get_stream_handler() -> logging.StreamHandler:
242
242
 
243
243
  def get_timed_rotating_file_handler(
244
244
  log_file_path: str, when: str = "midnight", interval: int = 1, delay: bool = False, encoding=None
245
- ) -> ForceAtTimeRotationTimedRotatingFileHandler:
245
+ ) -> TimedRotatingFileHandler:
246
246
  """
247
247
  Function to get a TimedRotatingFileHandler.
248
248
  This handler will output messages to a file, rotating the log file at certain timed intervals.
@@ -260,32 +260,10 @@ def get_timed_rotating_file_handler(
260
260
  :return: TimedRotatingFileHandler.
261
261
  """
262
262
 
263
- return ForceAtTimeRotationTimedRotatingFileHandler(
263
+ return TimedRotatingFileHandler(
264
264
  filename=log_file_path, when=when, interval=interval, delay=delay, encoding=encoding)
265
265
 
266
266
 
267
- def get_timed_rotating_file_handler_with_header(
268
- log_file_path: str, when: str = "midnight", interval: int = 1, delay: bool = False, encoding=None,
269
- header: str = None) -> TimedRotatingFileHandlerWithHeader:
270
- """
271
- Function to get a TimedRotatingFileHandler with header.
272
- This handler will output messages to a file, rotating the log file at certain timed intervals.
273
- It will write a header to the log file each time there is a file rotation.
274
-
275
- :param log_file_path: Path to the log file.
276
- :param when: When to rotate the log file. Possible
277
- :param interval: Interval to rotate the log file.
278
- :param delay: bool, If set to True, the log file will be created only if there's something to write.
279
- :param encoding: Encoding to use for the log file. Same as for the TimeRotatingFileHandler, which uses Default None.
280
- :param header: Header to write to the log file.
281
- Example: "time,host,error"
282
- :return: TimedRotatingFileHandlerWithHeader.
283
- """
284
-
285
- return TimedRotatingFileHandlerWithHeader(
286
- filename=log_file_path, when=when, interval=interval, delay=delay, encoding=encoding, header=header)
287
-
288
-
289
267
  def start_queue_listener_for_file_handler(
290
268
  file_handler: logging.FileHandler, queue_object) -> logging.handlers.QueueListener:
291
269
  """
@@ -350,72 +328,74 @@ def get_handler_name(handler: logging.Handler) -> str:
350
328
 
351
329
  def change_rotated_filename(
352
330
  file_handler: logging.Handler,
353
- date_format_string: str = None
331
+ date_format_string: str = None,
332
+ callback_namer_function: callable = None,
333
+ use_default_callback_namer_function: bool = True
354
334
  ):
355
335
  """
356
336
  Function to change the way TimedRotatingFileHandler managing the rotating filename.
357
337
 
358
338
  :param file_handler: FileHandler to change the rotating filename for.
359
- :param date_format_string: Date format string to use for the rotated log filename.
360
- If None, the default 'DEFAULT_DATE_STRING_FORMAT' will be used.
339
+ :param date_format_string: Date format string to set to the handler's suffix.
340
+ :param callback_namer_function: Callback function to change the filename on rotation.
341
+ :param use_default_callback_namer_function: If set to True, the default callback namer function will be used
342
+ and the filename will be changed on rotation instead of using the default like this:
343
+ 'file.log.2021-12-24' -> 'file_2021-12-24.log'.
344
+
345
+ ---------------------
346
+
347
+ At this point, 'file_handler.suffix' is already '%Y-%m-%d' if 'when' is set to 'midnight'.
348
+ You can change it if you wish (example: '%Y_%m_%d'), the method is described below.
361
349
  """
362
- # Changing the way TimedRotatingFileHandler managing the rotating filename
363
- # Default file suffix is only "Year_Month_Day" with addition of the dot (".") character to the
364
- # "file name + extension" that you provide it. Example: log file name:
365
- # test.log
366
- # After file is rotated at midnight, by default the old filename will be:
367
- # test.log.2021_12_24
368
- # And the log file of 25th, now will be "test.log".
369
- # So, Changing the file suffix to include the extension to the suffix, so it will be:
370
- # test.log.2021_12_24.log
371
- # file_handler.suffix = logfile_suffix
372
- # file_handler.suffix = "_%Y_%m_%d.txt"
373
- # This step will remove the created ".log." above before the suffix and the filename will look like:
374
- # test.2021_12_24.log
375
- # file_handler.namer = lambda name: name.replace(log_file_extension + ".", "") + log_file_extension
376
- # file_handler.namer = lambda name: name.replace(".txt.", "") + log_file_extension
377
- # This will recompile the string to tell the handler the length of the suffix parts
378
- # file_handler.extMatch = re.compile(r"^\d{4}_\d{2}_\d{2}" + re.escape(log_file_extension) + r"$")
379
- # file_handler.extMatch = re.compile(r"^\d{4}_\d{2}_\d{2}.txt$")
380
350
 
381
351
  def callback_namer(name):
382
352
  """
383
353
  Callback function to change the filename of the rotated log file on file rotation.
384
354
  """
385
355
  # Currently the 'name' is full file path + '.' + logfile_suffix.
386
- # Example: 'C:\\path\\to\\file.log._2021_12_24'
356
+ # Example: 'C:\\path\\to\\file.log.2021-12-24'
387
357
  # Get the parent directory of the file: C:\path\to
388
358
  parent_dir: str = str(Path(name).parent)
389
359
  # Get the base filename without the extension: file.log
390
360
  filename: str = Path(name).stem
391
- # Get the date part of the filename: _2021_12_24
361
+ # Get the date part of the filename: 2021-12-24
392
362
  date_part: str = str(Path(name).suffix).replace(".", "")
393
363
  # Get the file extension: log
394
364
  file_extension: str = Path(filename).suffix
395
365
  # Get the file name without the extension: file
396
366
  file_stem: str = Path(filename).stem
397
367
 
398
- return f"{parent_dir}{os.sep}{file_stem}{date_part}{file_extension}"
368
+ return f"{parent_dir}{os.sep}{file_stem}_{date_part}{file_extension}"
369
+
370
+ def change_file_handler_suffix():
371
+ # Get regex pattern from string format.
372
+ # Example: '%Y_%m_%d' -> r'\d{4}_\d{2}_\d{2}'
373
+ date_regex_pattern = datetimes.datetime_format_to_regex(date_format_string)
399
374
 
400
- if date_format_string is None:
401
- date_format_string = DEFAULT_DATE_STRING_FORMAT
375
+ # Regex pattern to match the rotated log filenames
376
+ logfile_regex_suffix = re.compile(date_regex_pattern)
402
377
 
403
- # Construct the new suffix without the file extension
404
- logfile_suffix = f"_{date_format_string}"
378
+ # Update the handler's suffix to include the date format
379
+ file_handler.suffix = date_format_string
405
380
 
406
- # Get regex pattern from string format.
407
- # Example: '%Y_%m_%d' -> r'\d{4}_\d{2}_\d{2}'
408
- date_regex_pattern = datetimes.datetime_format_to_regex(date_format_string)
381
+ # Update the handler's extMatch regex to match the new filename format
382
+ file_handler.extMatch = logfile_regex_suffix
409
383
 
410
- # Regex pattern to match the rotated log filenames
411
- logfile_regex_suffix = re.compile(date_regex_pattern)
384
+ if use_default_callback_namer_function and callback_namer_function:
385
+ raise ValueError("You can't use both default and custom callback namer function.")
386
+ elif not use_default_callback_namer_function and not callback_namer_function:
387
+ raise ValueError(
388
+ "You need to provide a 'callback_namer_function' or our 'use_default_callback_namer_function'.")
412
389
 
413
- # Update the handler's suffix to include the date format
414
- file_handler.suffix = logfile_suffix
390
+ if date_format_string:
391
+ change_file_handler_suffix()
415
392
 
416
- file_handler.namer = callback_namer
417
- # Update the handler's extMatch regex to match the new filename format
418
- file_handler.extMatch = logfile_regex_suffix
393
+ # Set the callback function to change the filename on rotation.
394
+ if use_default_callback_namer_function:
395
+ file_handler.namer = callback_namer
396
+
397
+ if callback_namer_function:
398
+ file_handler.namer = callback_namer_function
419
399
 
420
400
 
421
401
  def has_handlers(logger: logging.Logger) -> bool:
@@ -437,7 +417,7 @@ def has_handlers(logger: logging.Logger) -> bool:
437
417
  return True
438
418
 
439
419
 
440
- def extract_datetime_format_from_file_handler(file_handler: FileHandler) -> Union[str, None]:
420
+ def extract_datetime_format_from_file_handler(file_handler: logging.FileHandler) -> Union[str, None]:
441
421
  """
442
422
  Extract the datetime string formats from all TimedRotatingFileHandlers in the logger.
443
423
 
@@ -455,3 +435,13 @@ def extract_datetime_format_from_file_handler(file_handler: FileHandler) -> Unio
455
435
  return datetime_format
456
436
 
457
437
  return None
438
+
439
+
440
+ def add_filter_to_handler(handler: logging.Handler, filter_object: logging.Filter):
441
+ """
442
+ Function to add a filter to the handler.
443
+ :param handler: Handler to add the filter to.
444
+ :param filter_object: Filter object to add to the handler.
445
+ """
446
+
447
+ handler.addFilter(filter_object)
@@ -2,7 +2,7 @@ import logging
2
2
  import os
3
3
  from typing import Literal, Union
4
4
 
5
- from . import loggers, handlers, formatters
5
+ from . import loggers, handlers
6
6
 
7
7
 
8
8
  def create_logger(
@@ -26,6 +26,10 @@ def create_logger(
26
26
  None] = None,
27
27
  formatter_streamhandler_use_nanoseconds: bool = True,
28
28
  formatter_filehandler_use_nanoseconds: bool = True,
29
+ filehandler_rotate_at_rollover_time: bool = True,
30
+ filehandler_rotation_date_format: str = None,
31
+ filehandler_rotation_callback_namer_function: callable = None,
32
+ filehandler_rotation_use_default_namer_function: bool = True,
29
33
  when: str = "midnight",
30
34
  interval: int = 1,
31
35
  delay: bool = False,
@@ -67,6 +71,20 @@ def create_logger(
67
71
  in the formatter in case you provide 'asctime' element.
68
72
  :param formatter_filehandler_use_nanoseconds: bool, If set to True, the nanoseconds will be used
69
73
  in the formatter in case you provide 'asctime' element.
74
+ :param filehandler_rotate_at_rollover_time: bool,
75
+ If set to True, the log file will be rotated at the rollover time, even if there's nothing to write.
76
+ If set to False, the log file will be rotated after 'when' time, but only when event occurs.
77
+ :param filehandler_rotation_date_format: string, Date format to use for the log file rotation.
78
+ Example for 'when="midnight"': the default date format is '%Y-%m-%d', resulting in filename on rotation like:
79
+ "test.log.2021-11-25"
80
+ If you want to change the date format to '%Y_%m_%d', the filename will be:
81
+ "test.log.2021_11_25"
82
+ :param filehandler_rotation_callback_namer_function: callable, Callback function to use for the log file naming
83
+ on rotation. If set to None, logging module default function will be used. With "when='midnight'",
84
+ and filename: "test.log" this will name the file on rotation similar to: "test.log.2021-11-25".
85
+ :param filehandler_rotation_use_default_namer_function: bool, If set to True, the default namer function will be
86
+ used for the log file naming on rotation. With "when='midnight'" and filename: "test.log",
87
+ this will name the file on rotation similar to: "test_2021-11-25.log".
70
88
  :param when: string, When to rotate the log file. Default is 'midnight'.
71
89
  [when="midnight"] is set to rotate the filename at midnight. This means that the current file name will be
72
90
  added Yesterday's date to the end of the file and today's file will continue to write at the same
@@ -159,6 +177,10 @@ def create_logger(
159
177
  handlers.add_timedfilehandler_with_queuehandler(
160
178
  logger=logger, file_path=file_path, logging_level=logging_level, formatter=formatter_filehandler,
161
179
  formatter_use_nanoseconds=formatter_filehandler_use_nanoseconds, file_type=file_type,
180
+ rotate_at_rollover_time=filehandler_rotate_at_rollover_time,
181
+ rotation_date_format=filehandler_rotation_date_format,
182
+ rotation_callback_namer_function=filehandler_rotation_callback_namer_function,
183
+ rotation_use_default_callback_namer_function=filehandler_rotation_use_default_namer_function,
162
184
  when=when, interval=interval, delay=delay, encoding=encoding, header=header)
163
185
 
164
186
  return logger
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: atomicshop
3
- Version: 2.14.7
3
+ Version: 2.14.9
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=YZ57rvsVxMZXfB8JSAelbztciak4YqXL7GUpZSNcHRc,123
1
+ atomicshop/__init__.py,sha256=DBF3vx08oq-VNJ9rHPgRbBVXDm9QZq_mn99gKX4ERy0,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
@@ -234,10 +234,11 @@ atomicshop/wrappers/factw/rest/statistics.py,sha256=vznwzKP1gEF7uXz3HsuV66BU9wrp
234
234
  atomicshop/wrappers/factw/rest/status.py,sha256=4O3xS1poafwyUiLDkhyx4oMMe4PBwABuRPpOMnMKgIU,641
235
235
  atomicshop/wrappers/fibratusw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
236
236
  atomicshop/wrappers/fibratusw/install.py,sha256=PLVymDe0HuOvU0r2lje8BkQAgtiOWEeRO7n-1zKuL7A,3287
237
+ atomicshop/wrappers/loggingw/filters.py,sha256=CMs5PAMb68zxJgBcQobaOFDG5kLJBOVYnoBHjDgksO8,2859
237
238
  atomicshop/wrappers/loggingw/formatters.py,sha256=808R7K3e3ZJD2BXfqI6UMOyXGrCgt9SYh2Uv7sL_1KQ,7432
238
- atomicshop/wrappers/loggingw/handlers.py,sha256=0n4Bqr9yQ7GiQOBYtSVFtCFBeEUfcvkaAFYgWY1E3Tg,18014
239
+ atomicshop/wrappers/loggingw/handlers.py,sha256=bv3oCm_P0JdXaJKYjhyfFNMNong6Nc9LE4JGFxLN2As,16940
239
240
  atomicshop/wrappers/loggingw/loggers.py,sha256=DHOOTAtqkwn1xgvLHSkOiBm6yFGNuQy1kvbhG-TDog8,2374
240
- atomicshop/wrappers/loggingw/loggingw.py,sha256=irzlIYXTcCpSVjmb6NU6GJGCIy3V48ZVKNvKOlH70CU,9417
241
+ atomicshop/wrappers/loggingw/loggingw.py,sha256=lo4OZPXCbYZi3GqpaaJSs9SOGFfqD2EgHzzTK7f5IR4,11275
241
242
  atomicshop/wrappers/loggingw/reading.py,sha256=yh7uNPxEdn6KsxSKrYny2C57XdI25F5gaByz77CO_pw,17038
242
243
  atomicshop/wrappers/nodejsw/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
243
244
  atomicshop/wrappers/nodejsw/install_nodejs.py,sha256=QZg-R2iTQt7kFb8wNtnTmwraSGwvUs34JIasdbNa7ZU,5154
@@ -283,8 +284,8 @@ atomicshop/wrappers/socketw/socket_server_tester.py,sha256=AhpurHJmP2kgzHaUbq5ey
283
284
  atomicshop/wrappers/socketw/socket_wrapper.py,sha256=aXBwlEIJhFT0-c4i8iNlFx2It9VpCEpsv--5Oqcpxao,11624
284
285
  atomicshop/wrappers/socketw/ssl_base.py,sha256=k4V3gwkbq10MvOH4btU4onLX2GNOsSfUAdcHmL1rpVE,2274
285
286
  atomicshop/wrappers/socketw/statistics_csv.py,sha256=t3dtDEfN47CfYVi0CW6Kc2QHTEeZVyYhc57IYYh5nmA,826
286
- atomicshop-2.14.7.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
287
- atomicshop-2.14.7.dist-info/METADATA,sha256=2H8W_LfNKy-rfKr9G9xXvs85xp4fS0vDM3Ctze7Rvko,10478
288
- atomicshop-2.14.7.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
289
- atomicshop-2.14.7.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
290
- atomicshop-2.14.7.dist-info/RECORD,,
287
+ atomicshop-2.14.9.dist-info/LICENSE.txt,sha256=lLU7EYycfYcK2NR_1gfnhnRC8b8ccOTElACYplgZN88,1094
288
+ atomicshop-2.14.9.dist-info/METADATA,sha256=TL4CY4-kaxRLWK4vD8N1GxSUsFCHwBesZSZB3nAz7OU,10478
289
+ atomicshop-2.14.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
290
+ atomicshop-2.14.9.dist-info/top_level.txt,sha256=EgKJB-7xcrAPeqTRF2laD_Np2gNGYkJkd4OyXqpJphA,11
291
+ atomicshop-2.14.9.dist-info/RECORD,,