tquota.Quota

==========================================================

Quota Monitoring for Cloud Environments

Created on: Tue May 11, 2021, 13:00:30 Last updated: Wed Oct 16, 2024, 13:00:30

Author: Abdussalam Aljbri Emails: mr.aljbri@gmail.com, mr.aljbri@qq.com Project GitHub: https://github.com/aljbri/tquota Version: 0.0.2 License: MIT License

Description:

This script implements the Quota class, designed to monitor and manage session time limits in cloud environments like Kaggle or Colab with quota restrictions. It helps in tracking session time and takes action based on predefined quota and buffer times (gap times). The class provides automatic adjustment of gap times based on average execution time during operation.

Change Log:

v0.0.3 - Updated on Wed Oct 16, 2024

  • Introduced dynamic gap time (auto option) based on average execution times.
  • Added logging functionality to log remaining time.
  • Improved time validation to handle improper inputs.
  • Enhanced time format support, now allowing conversions from strings like '1h', '30m', '5s'.
  • Refactored internal code structure for readability.
  • Introduced automatic performance monitoring to calculate execution time per loop iteration.

v0.0.2 - Initial implementation (Tue May 12, 2021)

  • Fix major error in previous versions.

v0.0.1 - Initial implementation (Tue May 11, 2021)

  • Basic Quota class to track remaining session time.
  • Static gap time support.
  • Basic time formatting and conversion.

Usage:

For usage examples, refer to the class-level docstring of Quota.

  1# -*- coding: utf-8 -*-
  2"""
  3==========================================================
  4        Quota Monitoring for Cloud Environments
  5==========================================================
  6
  7Created on:    Tue May 11, 2021, 13:00:30
  8Last updated:  Wed Oct 16, 2024, 13:00:30
  9
 10Author:        Abdussalam Aljbri
 11Emails:        mr.aljbri@gmail.com, mr.aljbri@qq.com
 12Project GitHub: https://github.com/aljbri/tquota
 13Version:       0.0.2
 14License:       MIT License
 15
 16Description:
 17-------------
 18This script implements the `Quota` class, designed to monitor and manage session time limits in cloud environments
 19like Kaggle or Colab with quota restrictions. It helps in tracking session time and takes action based on predefined
 20quota and buffer times (gap times). The class provides automatic adjustment of gap times based on average execution
 21time during operation.
 22
 23Change Log:
 24-----------
 25v0.0.3 - Updated on Wed Oct 16, 2024
 26  - Introduced dynamic gap time (`auto` option) based on average execution times.
 27  - Added logging functionality to log remaining time.
 28  - Improved time validation to handle improper inputs.
 29  - Enhanced time format support, now allowing conversions from strings like '1h', '30m', '5s'.
 30  - Refactored internal code structure for readability.
 31  - Introduced automatic performance monitoring to calculate execution time per loop iteration.
 32
 33v0.0.2 - Initial implementation (Tue May 12, 2021)
 34  - Fix major error in previous versions.
 35
 36v0.0.1 - Initial implementation (Tue May 11, 2021)
 37  - Basic `Quota` class to track remaining session time.
 38  - Static gap time support.
 39  - Basic time formatting and conversion.
 40
 41Usage:
 42------
 43For usage examples, refer to the class-level docstring of `Quota`.
 44"""
 45
 46import re
 47import logging
 48from timeit import default_timer
 49
 50
 51class Quota:
 52    """
 53    Monitors session time limits in cloud environments with quota limitations (e.g., Kaggle, Colab).
 54
 55    This class allows users to set a quota time and a gap time, with options for automatic gap time adjustment
 56    based on the average execution time of operations within a loop. It can log remaining time and check if
 57    the time limits are reached.
 58
 59    Time Format:
 60        The time format must be passed as a string combining digits and a single character representing the time unit:
 61        - 's' for seconds
 62        - 'm' for minutes
 63        - 'h' for hours
 64        - 'd' for days
 65
 66        Examples:
 67        - '30s' for 30 seconds
 68        - '5m' for 5 minutes
 69        - '2h' for 2 hours
 70        - '1d' for 1 day
 71
 72        If an invalid format is provided, a ValueError will be raised.
 73
 74    Parameters:
 75    -----------
 76    - quota_time (str):
 77            Time quota for the session (e.g., '1h' for 1 hour, '30m' for 30 minutes).
 78            This is initially provided as a string and is internally converted to seconds.
 79            Default is '6h'.
 80    - gap_time (str):
 81            Time buffer before the quota ends to trigger actions.
 82            Set to 'auto' for automatic detection based on average execution time.
 83            Internally converted to seconds.
 84            Default is 'auto'.
 85    - enable_logging (bool, optional):
 86            Whether to enable logging or not. Default is False.
 87
 88    Raises:
 89    -----------
 90    - ValueError:
 91            - If the `quota_time` or `gap_time` format is invalid. This occurs when the time strings are not formatted correctly
 92              (e.g., not containing valid time units like 's', 'm', 'h', 'd').
 93            - If the `quota_time` is less than or equal to zero. A quota time must be a positive value in seconds.
 94            - If the `gap_time` is greater than or equal to the `quota_time`. The gap time should be smaller than the quota time to make sense.
 95    - TypeError:
 96            - If a non-string value is provided for `quota_time` or `gap_time`. Both `quota_time` and `gap_time` must be strings to be processed.
 97    - AttributeError:
 98            - If an attempt is made to access properties or methods without proper initialization of internal values (such as accessing `execution_times` before an operation).
 99
100    Properties:
101    -----------
102    - start_time: The timestamp when the `Quota` instance is initialized (in seconds from `default_timer`).
103    - quota_time: Total session quota time in seconds, converted from the input string (e.g., '1h' → 3600).
104    - auto_gap_time: Flag to indicate whether gap time is set to 'auto', allowing dynamic adjustment.
105    - gap_time: The buffer time before the quota ends. Can be statically set (manual time in seconds) or dynamically adjusted if 'auto'.
106    - enable_logging: Whether to log the remaining time to the console.
107    - execution_times: List of execution times (in seconds) for each loop iteration, used to calculate average execution time.
108    - total_execution_time: The sum of all execution times, used for calculating the dynamic gap time.
109    - last_execution_time: Timestamp of the last completed loop iteration (in seconds).
110    """
111
112    def __init__(self, quota_time='6h', gap_time='auto', enable_logging=False):
113        self.start_time = default_timer()
114        self.quota_time = self._to_seconds(quota_time)
115
116        # Initialize gap time based on input, either as seconds or 0.0001 if 'auto'.
117        self.auto_gap_time = gap_time in ['a', 'auto']
118        self.gap_time = 0.0001 if self.auto_gap_time else self._to_seconds(gap_time)
119
120        # Validate times
121        self._validate_times()
122
123        # Initialize logging if enabled
124        self.enable_logging = enable_logging
125        if self.enable_logging:
126            logging.basicConfig(level=logging.INFO)
127
128        # Initialize execution times tracker.
129        self.execution_times = []  # To track execution times
130        self.total_execution_time = 0.0  # To accumulate execution times
131        self.last_execution_time = default_timer()  # Initialize last execution time
132
133    def hastime(self):
134        """
135        Checks if the session still has time before the gap time is reached.
136
137        Returns:
138        -------
139            bool: True if there is enough time left in the session, False otherwise.
140        """
141        self._update_gap_time()  # Update gap time based on execution history
142        elapsed_time = self._remaining_time()
143        return elapsed_time >= self.gap_time
144
145    def time_up(self):
146        """
147        Checks if the session has reached or exceeded the quota time.
148
149        Returns:
150        -------
151            bool: True if the quota time has been exceeded, False otherwise.
152        """
153        self._update_gap_time()  # Update gap time based on execution history
154        remained_time = self._remaining_time()
155        return remained_time < self.gap_time
156
157    def remaining_time(self):
158        """
159        Returns the remaining time in a human-readable format.
160
161        Returns:
162        -------
163            str: Remaining time formatted as "xh:xm:xs".
164        """
165        return self._format_time(self._remaining_time())
166
167    def log_remaining_time(self):
168        """
169        Logs the remaining time to the console if logging is enabled.
170
171        Logs:
172        ----
173            str: Remaining time message if logging is enabled.
174        """
175        if self.enable_logging:
176            logging.info("Remaining time before quota is reached: {}".format(self.remaining_time()))
177
178    def _remaining_time(self):
179        """
180        Calculates the remaining time in seconds.
181
182        Returns:
183            int: Remaining time in seconds.
184        """
185        elapsed_time = self.quota_time - (default_timer() - self.start_time)
186        return max(elapsed_time, 0)
187
188    def _to_seconds(self, wTime):
189        """
190        Converts a time string into seconds (e.g., '1h' -> 3600).
191
192        Args:
193            wTime: A string representing the time in the format '1h', '30m', '5s', etc.
194
195        Returns:
196            float: Time in seconds.
197
198        Raises:
199            ValueError: If the time format is invalid.
200        """
201        pattern = r'^(\d+)([smhd])$'
202        match = re.match(pattern, wTime.lower())
203        if not match:
204            raise ValueError("Invalid time format: '{}'. Use format like '1h', '30m', '5s'.".format(wTime))
205
206        time_value, unit = match.groups()
207        time_value = int(time_value)
208        unit_seconds = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}
209
210        if time_value <= 0:
211            raise ValueError("Time value must be greater than zero: '{}'".format(wTime))
212
213        return time_value * unit_seconds[unit]
214
215    def _format_time(self, seconds):
216        """
217        Converts seconds into a human-readable time format.
218
219        Args:
220            seconds: Time in seconds.
221
222        Returns:
223            str: A human-readable time string in the format "Xh Xm Xs".
224        """
225        h, rem = divmod(seconds, 3600)
226        m, s = divmod(rem, 60)
227        return "{}h:{}m:{}s".format(int(h), int(m), int(s))
228
229    def _validate_times(self):
230        """
231        Validates that the quota time and gap time are logically consistent.
232
233        Raises:
234            ValueError: If gap time >= quota time or if either time is <= 0.
235        """
236        if self.gap_time >= self.quota_time:
237            raise ValueError("Gap time must be smaller than the quota time.")
238        if self.quota_time <= 0:
239            raise ValueError("Quota time must be greater than 0.")
240        if self.gap_time <= 0:
241            raise ValueError("Gap time must be greater than 0.")
242
243    def _update_gap_time(self):
244        """
245        Updates the gap time dynamically based on the average execution time, if `auto` mode is enabled.
246
247        It calculates the time taken since the last operation and updates the gap time accordingly.
248        """
249        current_time = default_timer()  # Get the current time
250        execution_time = current_time - self.last_execution_time  # Calculate time since last update
251        self.execution_times.append(execution_time)  # Record the execution time
252        self.total_execution_time += execution_time  # Update total execution time
253
254        # Update gap_time to average execution time if set to 'auto' and there are recorded times
255        if self.auto_gap_time and self.execution_times:
256            execution_time_average = self.total_execution_time / len(self.execution_times)
257            self.gap_time = execution_time_average  # Set gap_time to average in seconds
258
259        self.last_execution_time = current_time  # Update the last execution time for the next call
260
261    def _default_action(self):
262        """
263        Default action to take when the quota time is up.
264
265        Currently, this just logs a message if logging is enabled, but can be extended for future functionality.
266        """
267        if self.enable_logging:
268            logging.info("Quota time reached. Executing default action.")
class Quota:
 52class Quota:
 53    """
 54    Monitors session time limits in cloud environments with quota limitations (e.g., Kaggle, Colab).
 55
 56    This class allows users to set a quota time and a gap time, with options for automatic gap time adjustment
 57    based on the average execution time of operations within a loop. It can log remaining time and check if
 58    the time limits are reached.
 59
 60    Time Format:
 61        The time format must be passed as a string combining digits and a single character representing the time unit:
 62        - 's' for seconds
 63        - 'm' for minutes
 64        - 'h' for hours
 65        - 'd' for days
 66
 67        Examples:
 68        - '30s' for 30 seconds
 69        - '5m' for 5 minutes
 70        - '2h' for 2 hours
 71        - '1d' for 1 day
 72
 73        If an invalid format is provided, a ValueError will be raised.
 74
 75    Parameters:
 76    -----------
 77    - quota_time (str):
 78            Time quota for the session (e.g., '1h' for 1 hour, '30m' for 30 minutes).
 79            This is initially provided as a string and is internally converted to seconds.
 80            Default is '6h'.
 81    - gap_time (str):
 82            Time buffer before the quota ends to trigger actions.
 83            Set to 'auto' for automatic detection based on average execution time.
 84            Internally converted to seconds.
 85            Default is 'auto'.
 86    - enable_logging (bool, optional):
 87            Whether to enable logging or not. Default is False.
 88
 89    Raises:
 90    -----------
 91    - ValueError:
 92            - If the `quota_time` or `gap_time` format is invalid. This occurs when the time strings are not formatted correctly
 93              (e.g., not containing valid time units like 's', 'm', 'h', 'd').
 94            - If the `quota_time` is less than or equal to zero. A quota time must be a positive value in seconds.
 95            - If the `gap_time` is greater than or equal to the `quota_time`. The gap time should be smaller than the quota time to make sense.
 96    - TypeError:
 97            - If a non-string value is provided for `quota_time` or `gap_time`. Both `quota_time` and `gap_time` must be strings to be processed.
 98    - AttributeError:
 99            - If an attempt is made to access properties or methods without proper initialization of internal values (such as accessing `execution_times` before an operation).
100
101    Properties:
102    -----------
103    - start_time: The timestamp when the `Quota` instance is initialized (in seconds from `default_timer`).
104    - quota_time: Total session quota time in seconds, converted from the input string (e.g., '1h' → 3600).
105    - auto_gap_time: Flag to indicate whether gap time is set to 'auto', allowing dynamic adjustment.
106    - gap_time: The buffer time before the quota ends. Can be statically set (manual time in seconds) or dynamically adjusted if 'auto'.
107    - enable_logging: Whether to log the remaining time to the console.
108    - execution_times: List of execution times (in seconds) for each loop iteration, used to calculate average execution time.
109    - total_execution_time: The sum of all execution times, used for calculating the dynamic gap time.
110    - last_execution_time: Timestamp of the last completed loop iteration (in seconds).
111    """
112
113    def __init__(self, quota_time='6h', gap_time='auto', enable_logging=False):
114        self.start_time = default_timer()
115        self.quota_time = self._to_seconds(quota_time)
116
117        # Initialize gap time based on input, either as seconds or 0.0001 if 'auto'.
118        self.auto_gap_time = gap_time in ['a', 'auto']
119        self.gap_time = 0.0001 if self.auto_gap_time else self._to_seconds(gap_time)
120
121        # Validate times
122        self._validate_times()
123
124        # Initialize logging if enabled
125        self.enable_logging = enable_logging
126        if self.enable_logging:
127            logging.basicConfig(level=logging.INFO)
128
129        # Initialize execution times tracker.
130        self.execution_times = []  # To track execution times
131        self.total_execution_time = 0.0  # To accumulate execution times
132        self.last_execution_time = default_timer()  # Initialize last execution time
133
134    def hastime(self):
135        """
136        Checks if the session still has time before the gap time is reached.
137
138        Returns:
139        -------
140            bool: True if there is enough time left in the session, False otherwise.
141        """
142        self._update_gap_time()  # Update gap time based on execution history
143        elapsed_time = self._remaining_time()
144        return elapsed_time >= self.gap_time
145
146    def time_up(self):
147        """
148        Checks if the session has reached or exceeded the quota time.
149
150        Returns:
151        -------
152            bool: True if the quota time has been exceeded, False otherwise.
153        """
154        self._update_gap_time()  # Update gap time based on execution history
155        remained_time = self._remaining_time()
156        return remained_time < self.gap_time
157
158    def remaining_time(self):
159        """
160        Returns the remaining time in a human-readable format.
161
162        Returns:
163        -------
164            str: Remaining time formatted as "xh:xm:xs".
165        """
166        return self._format_time(self._remaining_time())
167
168    def log_remaining_time(self):
169        """
170        Logs the remaining time to the console if logging is enabled.
171
172        Logs:
173        ----
174            str: Remaining time message if logging is enabled.
175        """
176        if self.enable_logging:
177            logging.info("Remaining time before quota is reached: {}".format(self.remaining_time()))
178
179    def _remaining_time(self):
180        """
181        Calculates the remaining time in seconds.
182
183        Returns:
184            int: Remaining time in seconds.
185        """
186        elapsed_time = self.quota_time - (default_timer() - self.start_time)
187        return max(elapsed_time, 0)
188
189    def _to_seconds(self, wTime):
190        """
191        Converts a time string into seconds (e.g., '1h' -> 3600).
192
193        Args:
194            wTime: A string representing the time in the format '1h', '30m', '5s', etc.
195
196        Returns:
197            float: Time in seconds.
198
199        Raises:
200            ValueError: If the time format is invalid.
201        """
202        pattern = r'^(\d+)([smhd])$'
203        match = re.match(pattern, wTime.lower())
204        if not match:
205            raise ValueError("Invalid time format: '{}'. Use format like '1h', '30m', '5s'.".format(wTime))
206
207        time_value, unit = match.groups()
208        time_value = int(time_value)
209        unit_seconds = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}
210
211        if time_value <= 0:
212            raise ValueError("Time value must be greater than zero: '{}'".format(wTime))
213
214        return time_value * unit_seconds[unit]
215
216    def _format_time(self, seconds):
217        """
218        Converts seconds into a human-readable time format.
219
220        Args:
221            seconds: Time in seconds.
222
223        Returns:
224            str: A human-readable time string in the format "Xh Xm Xs".
225        """
226        h, rem = divmod(seconds, 3600)
227        m, s = divmod(rem, 60)
228        return "{}h:{}m:{}s".format(int(h), int(m), int(s))
229
230    def _validate_times(self):
231        """
232        Validates that the quota time and gap time are logically consistent.
233
234        Raises:
235            ValueError: If gap time >= quota time or if either time is <= 0.
236        """
237        if self.gap_time >= self.quota_time:
238            raise ValueError("Gap time must be smaller than the quota time.")
239        if self.quota_time <= 0:
240            raise ValueError("Quota time must be greater than 0.")
241        if self.gap_time <= 0:
242            raise ValueError("Gap time must be greater than 0.")
243
244    def _update_gap_time(self):
245        """
246        Updates the gap time dynamically based on the average execution time, if `auto` mode is enabled.
247
248        It calculates the time taken since the last operation and updates the gap time accordingly.
249        """
250        current_time = default_timer()  # Get the current time
251        execution_time = current_time - self.last_execution_time  # Calculate time since last update
252        self.execution_times.append(execution_time)  # Record the execution time
253        self.total_execution_time += execution_time  # Update total execution time
254
255        # Update gap_time to average execution time if set to 'auto' and there are recorded times
256        if self.auto_gap_time and self.execution_times:
257            execution_time_average = self.total_execution_time / len(self.execution_times)
258            self.gap_time = execution_time_average  # Set gap_time to average in seconds
259
260        self.last_execution_time = current_time  # Update the last execution time for the next call
261
262    def _default_action(self):
263        """
264        Default action to take when the quota time is up.
265
266        Currently, this just logs a message if logging is enabled, but can be extended for future functionality.
267        """
268        if self.enable_logging:
269            logging.info("Quota time reached. Executing default action.")

Monitors session time limits in cloud environments with quota limitations (e.g., Kaggle, Colab).

This class allows users to set a quota time and a gap time, with options for automatic gap time adjustment based on the average execution time of operations within a loop. It can log remaining time and check if the time limits are reached.

Time Format: The time format must be passed as a string combining digits and a single character representing the time unit: - 's' for seconds - 'm' for minutes - 'h' for hours - 'd' for days

Examples:
- '30s' for 30 seconds
- '5m' for 5 minutes
- '2h' for 2 hours
- '1d' for 1 day

If an invalid format is provided, a ValueError will be raised.

Parameters:

  • quota_time (str): Time quota for the session (e.g., '1h' for 1 hour, '30m' for 30 minutes). This is initially provided as a string and is internally converted to seconds. Default is '6h'.
  • gap_time (str): Time buffer before the quota ends to trigger actions. Set to 'auto' for automatic detection based on average execution time. Internally converted to seconds. Default is 'auto'.
  • enable_logging (bool, optional): Whether to enable logging or not. Default is False.

Raises:

  • ValueError:
    • If the quota_time or gap_time format is invalid. This occurs when the time strings are not formatted correctly (e.g., not containing valid time units like 's', 'm', 'h', 'd').
    • If the quota_time is less than or equal to zero. A quota time must be a positive value in seconds.
    • If the gap_time is greater than or equal to the quota_time. The gap time should be smaller than the quota time to make sense.
  • TypeError:
    • If a non-string value is provided for quota_time or gap_time. Both quota_time and gap_time must be strings to be processed.
  • AttributeError:
    • If an attempt is made to access properties or methods without proper initialization of internal values (such as accessing execution_times before an operation).

Properties:

  • start_time: The timestamp when the Quota instance is initialized (in seconds from default_timer).
  • quota_time: Total session quota time in seconds, converted from the input string (e.g., '1h' → 3600).
  • auto_gap_time: Flag to indicate whether gap time is set to 'auto', allowing dynamic adjustment.
  • gap_time: The buffer time before the quota ends. Can be statically set (manual time in seconds) or dynamically adjusted if 'auto'.
  • enable_logging: Whether to log the remaining time to the console.
  • execution_times: List of execution times (in seconds) for each loop iteration, used to calculate average execution time.
  • total_execution_time: The sum of all execution times, used for calculating the dynamic gap time.
  • last_execution_time: Timestamp of the last completed loop iteration (in seconds).
def hastime(self):
134    def hastime(self):
135        """
136        Checks if the session still has time before the gap time is reached.
137
138        Returns:
139        -------
140            bool: True if there is enough time left in the session, False otherwise.
141        """
142        self._update_gap_time()  # Update gap time based on execution history
143        elapsed_time = self._remaining_time()
144        return elapsed_time >= self.gap_time

Checks if the session still has time before the gap time is reached.

Returns:

bool: True if there is enough time left in the session, False otherwise.
def time_up(self):
146    def time_up(self):
147        """
148        Checks if the session has reached or exceeded the quota time.
149
150        Returns:
151        -------
152            bool: True if the quota time has been exceeded, False otherwise.
153        """
154        self._update_gap_time()  # Update gap time based on execution history
155        remained_time = self._remaining_time()
156        return remained_time < self.gap_time

Checks if the session has reached or exceeded the quota time.

Returns:

bool: True if the quota time has been exceeded, False otherwise.
def remaining_time(self):
158    def remaining_time(self):
159        """
160        Returns the remaining time in a human-readable format.
161
162        Returns:
163        -------
164            str: Remaining time formatted as "xh:xm:xs".
165        """
166        return self._format_time(self._remaining_time())

Returns the remaining time in a human-readable format.

Returns:

str: Remaining time formatted as "xh:xm:xs".
def log_remaining_time(self):
168    def log_remaining_time(self):
169        """
170        Logs the remaining time to the console if logging is enabled.
171
172        Logs:
173        ----
174            str: Remaining time message if logging is enabled.
175        """
176        if self.enable_logging:
177            logging.info("Remaining time before quota is reached: {}".format(self.remaining_time()))

Logs the remaining time to the console if logging is enabled.

Logs:

str: Remaining time message if logging is enabled.