bear-utils 0.7.11__tar.gz → 0.7.12__tar.gz

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.
Files changed (85) hide show
  1. {bear_utils-0.7.11 → bear_utils-0.7.12}/PKG-INFO +3 -3
  2. {bear_utils-0.7.11 → bear_utils-0.7.12}/README.md +1 -1
  3. {bear_utils-0.7.11 → bear_utils-0.7.12}/pyproject.toml +3 -7
  4. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/__init__.py +3 -3
  5. bear_utils-0.7.12/src/bear_utils/constants/date_related.py +1 -0
  6. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/extras/__init__.py +3 -0
  7. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/_styles.py +3 -1
  8. bear_utils-0.7.12/src/bear_utils/time/__init__.py +13 -0
  9. bear_utils-0.7.11/src/bear_utils/constants/date_related.py +0 -36
  10. bear_utils-0.7.11/src/bear_utils/time/__init__.py +0 -16
  11. bear_utils-0.7.11/src/bear_utils/time/_helpers.py +0 -91
  12. bear_utils-0.7.11/src/bear_utils/time/_time_class.py +0 -316
  13. bear_utils-0.7.11/src/bear_utils/time/_timer.py +0 -80
  14. bear_utils-0.7.11/src/bear_utils/time/_tools.py +0 -17
  15. bear_utils-0.7.11/src/bear_utils/time/time_manager.py +0 -218
  16. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/ai/__init__.py +0 -0
  17. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/ai/ai_helpers/__init__.py +0 -0
  18. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/ai/ai_helpers/_common.py +0 -0
  19. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/ai/ai_helpers/_config.py +0 -0
  20. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/ai/ai_helpers/_parsers.py +0 -0
  21. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/ai/ai_helpers/_types.py +0 -0
  22. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/cache/__init__.py +0 -0
  23. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/cli/__init__.py +0 -0
  24. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/cli/commands.py +0 -0
  25. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/cli/prompt_helpers.py +0 -0
  26. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/cli/shell/__init__.py +0 -0
  27. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/cli/shell/_base_command.py +0 -0
  28. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/cli/shell/_base_shell.py +0 -0
  29. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/cli/shell/_common.py +0 -0
  30. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/config/__init__.py +0 -0
  31. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/config/config_manager.py +0 -0
  32. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/config/dir_manager.py +0 -0
  33. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/config/settings_manager.py +0 -0
  34. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/constants/__init__.py +0 -0
  35. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/constants/_exceptions.py +0 -0
  36. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/constants/_lazy_typing.py +0 -0
  37. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/constants/time_related.py +0 -0
  38. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/database/__init__.py +0 -0
  39. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/database/_db_manager.py +0 -0
  40. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/events/__init__.py +0 -0
  41. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/events/events_class.py +0 -0
  42. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/events/events_module.py +0 -0
  43. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/extras/_async_helpers.py +0 -0
  44. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/extras/_tools.py +0 -0
  45. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/extras/platform_utils.py +0 -0
  46. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/extras/wrappers/__init__.py +0 -0
  47. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/extras/wrappers/add_methods.py +0 -0
  48. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/files/__init__.py +0 -0
  49. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/files/file_handlers/__init__.py +0 -0
  50. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/files/file_handlers/_base_file_handler.py +0 -0
  51. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/files/file_handlers/file_handler_factory.py +0 -0
  52. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/files/file_handlers/json_file_handler.py +0 -0
  53. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/files/file_handlers/log_file_handler.py +0 -0
  54. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/files/file_handlers/txt_file_handler.py +0 -0
  55. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/files/file_handlers/yaml_file_handler.py +0 -0
  56. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/files/ignore_parser.py +0 -0
  57. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/graphics/__init__.py +0 -0
  58. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/graphics/bear_gradient.py +0 -0
  59. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/graphics/image_helpers.py +0 -0
  60. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/gui/__init__.py +0 -0
  61. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/gui/gui_tools/__init__.py +0 -0
  62. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/gui/gui_tools/_settings.py +0 -0
  63. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/gui/gui_tools/_types.py +0 -0
  64. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/gui/gui_tools/qt_app.py +0 -0
  65. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/gui/gui_tools/qt_color_picker.py +0 -0
  66. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/gui/gui_tools/qt_file_handler.py +0 -0
  67. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/gui/gui_tools/qt_input_dialog.py +0 -0
  68. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/__init__.py +0 -0
  69. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/__init__.py +0 -0
  70. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/_common.py +0 -0
  71. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/_console_junk.py +0 -0
  72. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/loggers/__init__.py +0 -0
  73. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/loggers/_base_logger.py +0 -0
  74. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/loggers/_base_logger.pyi +0 -0
  75. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/loggers/_buffer_logger.py +0 -0
  76. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/loggers/_console_logger.py +0 -0
  77. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/loggers/_console_logger.pyi +0 -0
  78. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/loggers/_file_logger.py +0 -0
  79. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/loggers/_level_sin.py +0 -0
  80. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/loggers/_logger.py +0 -0
  81. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/loggers/_sub_logger.py +0 -0
  82. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/logger_manager/loggers/_sub_logger.pyi +0 -0
  83. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/logging/loggers.py +0 -0
  84. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/monitoring/__init__.py +0 -0
  85. {bear_utils-0.7.11 → bear_utils-0.7.12}/src/bear_utils/monitoring/host_monitor.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: bear-utils
3
- Version: 0.7.11
3
+ Version: 0.7.12
4
4
  Summary: Various utilities for Bear programmers, including a rich logging utility, a disk cache, and a SQLite database wrapper amongst other things.
5
5
  Author: chaz
6
6
  Author-email: bright.lid5647@fastmail.com
@@ -8,6 +8,7 @@ Requires-Python: >=3.12
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.12
10
10
  Classifier: Programming Language :: Python :: 3.13
11
+ Requires-Dist: bear-epoch-time (>=1.0.1)
11
12
  Requires-Dist: diskcache (>=5.6.3,<6.0.0)
12
13
  Requires-Dist: httpx (>=0.28.1)
13
14
  Requires-Dist: pathspec (>=0.12.1)
@@ -16,14 +17,13 @@ Requires-Dist: prompt-toolkit (>=3.0.51,<4.0.0)
16
17
  Requires-Dist: pydantic (>=2.11.5)
17
18
  Requires-Dist: pyglm (>=2.8.2,<3.0.0)
18
19
  Requires-Dist: pyqt6 (>=6.9.0)
19
- Requires-Dist: pytz (>=2025.2)
20
20
  Requires-Dist: pyyaml (>=6.0.2)
21
21
  Requires-Dist: rich (>=14.0.0,<15.0.0)
22
22
  Requires-Dist: singleton-base (>=1.0.5)
23
23
  Requires-Dist: sqlalchemy (>=2.0.40,<3.0.0)
24
24
  Description-Content-Type: text/markdown
25
25
 
26
- # Bear Utils v# Bear Utils v0.7.11
26
+ # Bear Utils v# Bear Utils v0.7.12
27
27
 
28
28
  Personal set of tools and utilities for Python projects, focusing on modularity and ease of use. This library includes components for caching, database management, logging, time handling, file operations, CLI prompts, image processing, clipboard interaction, gradient utilities, event systems, and async helpers.
29
29
 
@@ -1,4 +1,4 @@
1
- # Bear Utils v# Bear Utils v0.7.11
1
+ # Bear Utils v# Bear Utils v0.7.12
2
2
 
3
3
  Personal set of tools and utilities for Python projects, focusing on modularity and ease of use. This library includes components for caching, database management, logging, time handling, file operations, CLI prompts, image processing, clipboard interaction, gradient utilities, event systems, and async helpers.
4
4
 
@@ -1,10 +1,8 @@
1
1
  [project]
2
2
  name = "bear-utils"
3
- version = "0.7.11"
3
+ version = "0.7.12"
4
4
  description = "Various utilities for Bear programmers, including a rich logging utility, a disk cache, and a SQLite database wrapper amongst other things."
5
- authors = [
6
- { name = "chaz", email = "bright.lid5647@fastmail.com" }
7
- ]
5
+ authors = [{ name = "chaz", email = "bright.lid5647@fastmail.com" }]
8
6
  readme = "README.md"
9
7
  requires-python = ">=3.12"
10
8
  dependencies = [
@@ -18,12 +16,11 @@ dependencies = [
18
16
  "pydantic>=2.11.5",
19
17
  "pathspec>=0.12.1",
20
18
  "pyyaml>=6.0.2",
21
- "pytz>=2025.2",
22
19
  "singleton-base>=1.0.5",
23
20
  "httpx>=0.28.1",
21
+ "bear-epoch-time>=1.0.1",
24
22
  ]
25
23
 
26
-
27
24
  [build-system]
28
25
  requires = ["poetry-core>=2.0.0,<3.0.0"]
29
26
  build-backend = "poetry.core.masonry.api"
@@ -36,7 +33,6 @@ dev = [
36
33
  "twine>=6.1.0",
37
34
  ]
38
35
 
39
-
40
36
  [tool.pytest.ini_options]
41
37
  markers = [
42
38
  "visual: marks tests as visual verification tests (deselect with '-m \"not visual\"')"
@@ -1,3 +1,5 @@
1
+ from bear_epoch_time import EpochTimestamp, TimeTools
2
+
1
3
  from .cache import CacheWrapper, cache, cache_factory
2
4
  from .config.settings_manager import SettingsManager, get_settings_manager
3
5
  from .constants.date_related import DATE_FORMAT, DATE_TIME_FORMAT
@@ -7,7 +9,5 @@ from .files.file_handlers.file_handler_factory import FileHandlerFactory
7
9
  from .logging.logger_manager._common import VERBOSE_CONSOLE_FORMAT
8
10
  from .logging.logger_manager._styles import VERBOSE
9
11
  from .logging.loggers import BaseLogger, BufferLogger, ConsoleLogger, FileLogger
10
- from .time._time_class import EpochTimestamp
11
- from .time.time_manager import TimeTools
12
12
 
13
- __version__ = "__version__ = "0.7.11""
13
+ __version__ = "0.7.12"
@@ -0,0 +1 @@
1
+ from bear_epoch_time.constants.date_related import DATE_FORMAT, DATE_TIME_FORMAT
@@ -1,3 +1,5 @@
1
+ from singleton_base import SingletonBase
2
+
1
3
  from ._tools import ClipboardManager, clear_clipboard, copy_to_clipboard, fmt_header, paste_from_clipboard
2
4
  from .platform_utils import OS, get_platform, is_linux, is_macos, is_windows
3
5
  from .wrappers.add_methods import add_comparison_methods
@@ -14,4 +16,5 @@ __all__ = [
14
16
  "clear_clipboard",
15
17
  "fmt_header",
16
18
  "add_comparison_methods",
19
+ "SingletonBase",
17
20
  ]
@@ -85,7 +85,9 @@ def get_method(name: str) -> LoggerExtraInfo:
85
85
  return LOGGER_METHODS[name]
86
86
 
87
87
 
88
- DEFAULT_STYLES: dict[str, str] = {**{method: info["style"].upper() for method, info in LOGGER_METHODS.items()}}
88
+ DEFAULT_STYLES: dict[str, str] = {**{method: info["style"] for method, info in LOGGER_METHODS.items()}}
89
89
  """Just the styles of the logger methods, used to create the theme."""
90
90
 
91
91
  DEFAULT_THEME = Theme(styles=DEFAULT_STYLES)
92
+
93
+ print(DEFAULT_STYLES)
@@ -0,0 +1,13 @@
1
+ from bear_epoch_time import EpochTimestamp, TimerData, TimeTools, add_ord_suffix, create_timer, timer
2
+ from bear_epoch_time.constants.date_related import DATE_FORMAT, DATE_TIME_FORMAT
3
+
4
+ __all__ = [
5
+ "EpochTimestamp",
6
+ "TimerData",
7
+ "create_timer",
8
+ "timer",
9
+ "TimeTools",
10
+ "add_ord_suffix",
11
+ "DATE_FORMAT",
12
+ "DATE_TIME_FORMAT",
13
+ ]
@@ -1,36 +0,0 @@
1
- from typing import LiteralString
2
-
3
- from pytz import UTC, timezone
4
- from pytz.tzinfo import BaseTzInfo, DstTzInfo, StaticTzInfo
5
-
6
- TimeZoneType = BaseTzInfo | DstTzInfo | StaticTzInfo
7
-
8
- DATE_FORMAT = "%m-%d-%Y"
9
- """Date format"""
10
-
11
- TIME_FORMAT = "%I:%M %p"
12
- """Time format with 12 hour format"""
13
-
14
- TIME_FORMAT_WITH_SECONDS: LiteralString = "%I:%M:%S %p"
15
- """Time format with 12 hour format and seconds"""
16
-
17
- DATE_TIME_FORMAT: LiteralString = f"{DATE_FORMAT} {TIME_FORMAT}"
18
- """Datetime format with 12 hour format"""
19
-
20
- DT_FORMAT_WITH_SECONDS: LiteralString = f"{DATE_FORMAT} {TIME_FORMAT_WITH_SECONDS}"
21
- """Datetime format with 12 hour format and seconds"""
22
-
23
- DT_FORMAT_WITH_TZ: LiteralString = f"{DATE_TIME_FORMAT} %Z"
24
- """Datetime format with 12 hour format and timezone"""
25
-
26
- DT_FORMAT_WITH_TZ_AND_SECONDS: LiteralString = f"{DT_FORMAT_WITH_SECONDS} %Z"
27
- """Datetime format with 12 hour format, seconds, and timezone"""
28
-
29
- PT_TIME_ZONE: TimeZoneType = timezone("America/Los_Angeles")
30
- """Default timezone, a Pacific Time Zone using a pytz timezone object"""
31
-
32
- UTC_TIME_ZONE: TimeZoneType = UTC
33
- """UTC timezone, a UTC timezone using a pytz timezone object"""
34
-
35
- CEST_TIME_ZONE: TimeZoneType = timezone("Europe/Berlin") # Central European Summer Time, I GUESS!
36
- """Central European Summer Time timezone, a CEST timezone using a pytz timezone object"""
@@ -1,16 +0,0 @@
1
- from ..constants.date_related import DATE_FORMAT, DATE_TIME_FORMAT
2
- from ._time_class import EpochTimestamp
3
- from ._timer import TimerData, create_timer, timer
4
- from ._tools import add_ord_suffix
5
- from .time_manager import TimeTools
6
-
7
- __all__ = [
8
- "EpochTimestamp",
9
- "TimerData",
10
- "create_timer",
11
- "timer",
12
- "TimeTools",
13
- "add_ord_suffix",
14
- "DATE_FORMAT",
15
- "DATE_TIME_FORMAT",
16
- ]
@@ -1,91 +0,0 @@
1
- import re
2
- from datetime import timedelta
3
-
4
- from ..constants.time_related import SECONDS_IN_DAY, SECONDS_IN_HOUR, SECONDS_IN_MINUTE, SECONDS_IN_MONTH
5
-
6
-
7
- def convert_to_seconds(time_str: str) -> int:
8
- """Convert a time string to seconds.
9
-
10
- Examples
11
- --------
12
- >>> convert_to_seconds("1M 30m")
13
- 2610000
14
-
15
- Notes
16
- -----
17
- * ``M`` or ``mo`` denotes **months**.
18
- * ``m`` denotes **minutes**.
19
- """
20
-
21
- time_parts: list[tuple[str, str]] = re.findall(r"(\d+)\s*(M|mo|[dhms])", time_str)
22
- total_seconds = 0
23
- for value, unit in time_parts:
24
- if not value.isdigit():
25
- raise ValueError(f"Invalid time value: {value}")
26
- value = int(value)
27
-
28
- if unit == "M" or unit.lower() == "mo":
29
- total_seconds += value * SECONDS_IN_MONTH
30
- elif unit == "d":
31
- total_seconds += value * SECONDS_IN_DAY
32
- elif unit == "h":
33
- total_seconds += value * SECONDS_IN_HOUR
34
- elif unit == "m":
35
- total_seconds += value * SECONDS_IN_MINUTE
36
- elif unit == "s":
37
- total_seconds += value
38
- else:
39
- raise ValueError(f"Invalid time unit: {unit}")
40
- return total_seconds
41
-
42
-
43
- def timedelta_to_seconds(td: timedelta) -> int:
44
- """Convert a timedelta object to seconds."""
45
- if not isinstance(td, timedelta):
46
- raise ValueError("Input must be a timedelta object")
47
- return int(td.total_seconds())
48
-
49
-
50
- def convert_to_milliseconds(time_str: str) -> int:
51
- return convert_to_seconds(time_str) * 1000
52
-
53
-
54
- def milliseconds_to_time(milliseconds: int) -> str:
55
- """Convert milliseconds to a human-readable time string."""
56
- if milliseconds < 0:
57
- raise ValueError("Milliseconds cannot be negative")
58
- seconds = milliseconds // 1000
59
- return seconds_to_time(seconds)
60
-
61
-
62
- def seconds_to_timedelta(seconds: int) -> timedelta:
63
- """Convert seconds to a timedelta object."""
64
- if seconds < 0:
65
- raise ValueError("Seconds cannot be negative")
66
- return timedelta(seconds=seconds)
67
-
68
-
69
- def seconds_to_time(seconds: int) -> str:
70
- """Convert seconds to a human-readable time string.
71
-
72
- Months are represented with ``M`` while minutes use ``m``.
73
- """
74
- if seconds < 0:
75
- raise ValueError("Seconds cannot be negative")
76
- months, reminder = divmod(seconds, SECONDS_IN_MONTH)
77
- days, reminder = divmod(reminder, SECONDS_IN_DAY)
78
- hours, reminder = divmod(reminder, SECONDS_IN_HOUR)
79
- minutes, seconds = divmod(reminder, SECONDS_IN_MINUTE)
80
- time_parts = []
81
- if months > 0:
82
- time_parts.append(f"{months}M")
83
- if days > 0:
84
- time_parts.append(f"{days}d")
85
- if hours > 0:
86
- time_parts.append(f"{hours}h")
87
- if minutes > 0:
88
- time_parts.append(f"{minutes}m")
89
- if seconds > 0:
90
- time_parts.append(f"{seconds}s")
91
- return " ".join(time_parts)
@@ -1,316 +0,0 @@
1
- from datetime import datetime
2
- from typing import ClassVar, Literal, Self
3
-
4
- from pytz import UTC
5
-
6
- from ..constants.date_related import DATE_FORMAT, DT_FORMAT_WITH_TZ, PT_TIME_ZONE, TIME_FORMAT, TimeZoneType
7
-
8
- REPR_CHOICES = Literal["int", "object", "datetime"]
9
- MULT = Literal[1000, 1]
10
-
11
-
12
- class EpochTimestamp(int):
13
- """
14
- Custom class to represent epoch timestamps.
15
- Inherits from int to allow direct arithmetic operations.
16
- This class is used to represent time in seconds or milliseconds since the epoch (1970-01-01 00:00:00 UTC)
17
- with it defaulting to ms since that is the most common use case.
18
-
19
- Default value is the current epoch time in milliseconds. It is suggested to set the value to 0 if using it as
20
- a placeholder value or call `now()` to get the current epoch time.
21
- """
22
-
23
- _repr_style: ClassVar[REPR_CHOICES] = "int"
24
- """Three choices: int, object, or datetime.
25
- Int is the default and is the most common use case.
26
- Object will return the object representation of the class.
27
- Datetime will return a human readable timestamp like "10-01-2025." """
28
- _datefmt: ClassVar[str] = DATE_FORMAT
29
- """The format of the default date string. Default is "%m-%d-%Y"."""
30
- _timefmt: ClassVar[str] = TIME_FORMAT
31
- """The format of the default time string. Default is "%I:%M %p"."""
32
- _fullfmt: ClassVar[str] = DT_FORMAT_WITH_TZ
33
- """The format of the default datetime string. Default is "%m-%d-%Y %I:%M %p %Z"."""
34
- _tz: ClassVar[TimeZoneType] = PT_TIME_ZONE
35
- """The default timezone for the class. Default is "America/Los_Angeles"."""
36
-
37
- # region Class Methods
38
-
39
- @classmethod
40
- def set_repr_style(cls, repr_style: REPR_CHOICES) -> None:
41
- """
42
- Set the plain representation of the class.
43
-
44
- Args:
45
- repr_style (str): The representation style ("int", "object", or "datetime")
46
- """
47
- cls._repr_style = repr_style
48
-
49
- @classmethod
50
- def set_date_format(cls, datefmt: str) -> None:
51
- """
52
- Set the default date format for the class.
53
-
54
- Args:
55
- datefmt (str): The format of the date string. Default is "%m-%d-%Y".
56
- """
57
- cls._datefmt = datefmt
58
-
59
- @classmethod
60
- def set_time_format(cls, timefmt: str) -> None:
61
- """
62
- Set the default time format for the class.
63
-
64
- Args:
65
- timefmt (str): The format of the time string. Default is "%I:%M %p".
66
- """
67
- cls._timefmt = timefmt
68
-
69
- @classmethod
70
- def set_full_format(cls, fullfmt: str) -> None:
71
- """
72
- Set the default datetime format for the class.
73
-
74
- Args:
75
- fullfmt (str): The format of the datetime string. Default is "%m-%d-%Y %I:%M %p %Z".
76
- """
77
- cls._fullfmt = fullfmt
78
-
79
- @classmethod
80
- def set_timezone(cls, tz: TimeZoneType) -> None:
81
- """
82
- Set the default timezone for the class.
83
-
84
- Args:
85
- tz (TimeZoneType): The timezone to set. Default is PT_TIME_ZONE.
86
- """
87
- cls._tz = tz
88
-
89
- @classmethod
90
- def now(cls, milliseconds: bool = True) -> Self:
91
- """
92
- Get the current epoch time in milliseconds or seconds in UTC.
93
-
94
- Args:
95
- milliseconds (bool): If True, return milliseconds. If False, return seconds. Default is True for milliseconds.
96
-
97
- Returns:
98
- EpochTimestamp: The current epoch time.
99
- """
100
- multiplier: MULT = 1000 if milliseconds else 1
101
- t: int = int(datetime.now(UTC).timestamp()) * multiplier
102
- return cls(t, milliseconds=milliseconds)
103
-
104
- @classmethod
105
- def from_datetime(cls, dt: datetime, milliseconds: bool = True) -> Self:
106
- """
107
- Convert a datetime object to an epoch timestamp.
108
-
109
- Args:
110
- dt (datetime): The datetime object to convert.
111
- milliseconds (bool): If True, return milliseconds. If False, return seconds.
112
-
113
- Returns:
114
- EpochTimestamp: The epoch timestamp in milliseconds or seconds based on the milliseconds argument.
115
- """
116
- multiplier: MULT = 1000 if milliseconds else 1
117
- return cls(int(dt.astimezone(UTC).timestamp() * multiplier), milliseconds=milliseconds)
118
-
119
- @classmethod
120
- def from_dt_string(cls, dt_string: str, milliseconds: bool = True, fmt: str | None = None) -> Self:
121
- """
122
- Convert a datetime string to an epoch timestamp.
123
-
124
- Args:
125
- dt_string (str): The datetime string to convert.
126
- milliseconds (bool): If True, return milliseconds. If False, return seconds.
127
- fmt (str): The format of the datetime string. Default is "%m-%d-%Y %I:%M %p".
128
-
129
- Returns:
130
- EpochTimestamp: The epoch timestamp in milliseconds or seconds based on the milliseconds argument.
131
- """
132
- multiplier: MULT = 1000 if milliseconds else 1
133
- dt: datetime = datetime.strptime(dt_string, fmt if fmt else cls._fullfmt)
134
- return cls(int(dt.astimezone(UTC).timestamp() * multiplier))
135
-
136
- def __new__(cls, value: int | None = None, milliseconds: bool = True) -> Self:
137
- value = value if value is not None else cls.now(milliseconds)
138
- return super().__new__(cls, value)
139
-
140
- # endregion
141
-
142
- # region Instance Methods
143
-
144
- def __init__(self, value: int | None = None, milliseconds: bool = True):
145
- self.milliseconds = milliseconds
146
- self.multiplier: MULT = 1000 if self.milliseconds else 1
147
-
148
- def __str__(self):
149
- return int.__str__(self)
150
-
151
- def __repr__(self) -> str:
152
- if self.is_default:
153
- return "EpochTimestamp(0) (Default Value)"
154
- match self._repr_style:
155
- case "int":
156
- return f"{int(self)}"
157
- case "object":
158
- return f"EpochTimestamp({int(self)})"
159
- case "datetime":
160
- return f"{self.to_datetime.strftime(self._datefmt)}"
161
- case _:
162
- raise ValueError(f"Invalid repr style: {self._repr_style}")
163
-
164
- def to_string(self, fmt: str | None = None, tz: TimeZoneType | None = None) -> str:
165
- """
166
- Convert the epoch timestamp to a formatted string, taking into account the timezone and format.
167
-
168
- Args:
169
- fmt (str): The format of the datetime string. Default is "%m-%d-%Y %I:%M %p".
170
- tz (TimeZoneType | None): The timezone to convert to. Default is PT_TIME_ZONE.
171
-
172
- Returns:
173
- str: The formatted date string.
174
- """
175
- if self.is_default:
176
- raise ValueError("Cannot convert default value to string.")
177
- fmt = fmt if fmt else self._fullfmt
178
- tz = tz if tz else self._tz
179
- return self.to_datetime.astimezone(tz).strftime(fmt)
180
-
181
- def date_str(self, tz: TimeZoneType | None = None) -> str:
182
- """
183
- Convert the epoch timestamp to a date string in the format "%m-%d-%Y".
184
-
185
- Args:
186
- tz (TimeZoneType | None): The timezone to convert to. Default is PT_TIME_ZONE.
187
-
188
- Returns:
189
- str: The formatted date string.
190
- """
191
- return self.to_string(fmt=self._datefmt, tz=tz if tz else self._tz)
192
-
193
- def time_str(self, tz: TimeZoneType | None = None) -> str:
194
- """
195
- Convert the epoch timestamp to a time string in the format "%I:%M %p".
196
-
197
- Args:
198
- tz (TimeZoneType | None): The timezone to convert to. Default is PT_TIME_ZONE.
199
-
200
- Returns:
201
- str: The formatted time string.
202
- """
203
- return self.to_string(fmt=self._timefmt, tz=tz if tz else self._tz)
204
-
205
- def add_timedelta(self, seconds: int = 0, milliseconds: int = 0) -> Self:
206
- """
207
- Add a timedelta to the epoch timestamp.
208
-
209
- Args:
210
- seconds (int): The number of seconds to add. Default is 0.
211
- milliseconds (int): The number of milliseconds to add. Default is 0.
212
-
213
- Returns:
214
- EpochTimestamp: The new epoch timestamp after adding the timedelta.
215
- """
216
- total_seconds = seconds + (milliseconds / 1000)
217
- new_timestamp = int((self.to_seconds + total_seconds) * self.multiplier)
218
- new_ts: Self = type(self)(new_timestamp)
219
- return new_ts
220
-
221
- def start_of_day(self, tz: TimeZoneType | None = None) -> Self:
222
- """
223
- Get the start of the day for the epoch timestamp, defaults to midnight of the day in UTC.
224
-
225
- Args:
226
- tz (TimeZoneType | None): The timezone to convert to. Will default to UTC if not provided.
227
-
228
- Returns:
229
- EpochTimestamp: The epoch timestamp at the start of the day.
230
- """
231
- dt: datetime = self.to_datetime.astimezone(tz) if tz else self.to_datetime
232
- dt = dt.replace(hour=0, minute=0, second=0, microsecond=0)
233
- return type(self).from_datetime(dt, milliseconds=self.milliseconds)
234
-
235
- def end_of_day(self, tz: TimeZoneType | None = None) -> Self:
236
- """
237
- Get the end of the day for the epoch timestamp, defaults to 23:59:59.999999 of the day in UTC.
238
-
239
- Args:
240
- tz (TimeZoneType | None): The timezone to convert to. Will default to UTC if not provided.
241
-
242
- Returns:
243
- EpochTimestamp: The epoch timestamp at the end of the day.
244
- """
245
- dt: datetime = self.to_datetime.astimezone(tz) if tz else self.to_datetime
246
- dt = dt.replace(hour=23, minute=59, second=59, microsecond=999999)
247
- return type(self).from_datetime(dt, milliseconds=self.milliseconds)
248
-
249
- # endregion
250
-
251
- # region Properties
252
-
253
- @property
254
- def to_datetime(self) -> datetime:
255
- """
256
- Convert the epoch timestamp to a datetime object in UTC.
257
-
258
- Returns:
259
- datetime: The datetime representation of the epoch timestamp.
260
- """
261
- return datetime.fromtimestamp(self.to_seconds if self.milliseconds else self, tz=UTC)
262
-
263
- @property
264
- def to_seconds(self) -> int:
265
- """
266
- Get the total seconds from the epoch timestamp, if the timestamp is in milliseconds, it converts it to seconds else
267
- just returns the integer value.
268
-
269
- Returns:
270
- int: The total seconds since the epoch.
271
- """
272
- return int(self // 1000) if self.milliseconds else int(self)
273
-
274
- @property
275
- def to_milliseconds(self) -> int:
276
- """
277
- Get the total milliseconds from the epoch timestamp, if the timestamp is in seconds, it converts it to milliseconds else
278
- just returns the integer value.
279
-
280
- Returns:
281
- int: The total milliseconds since the epoch.
282
- """
283
- return int(self * 1000) if not self.milliseconds else int(self)
284
-
285
- @property
286
- def to_int(self) -> int:
287
- """
288
- Converts the epoch timestamp to an integer value. Mostly used for type hinting since this *IS* an int.
289
-
290
- Returns:
291
- int: The total milliseconds since the epoch.
292
- """
293
- return int(self)
294
-
295
- @property
296
- def is_default(self) -> bool:
297
- """
298
- Check if the timestamp is zero, this is useful since zero is the default value.
299
-
300
- Returns:
301
- bool: True if the timestamp is zero, False otherwise.
302
- """
303
- return self == 0
304
-
305
- # endregion
306
-
307
-
308
- # if __name__ == "__main__":
309
- # # Example usage
310
- # default_value = EpochTimestamp(0)
311
- # ts = EpochTimestamp.now()
312
- # t2 = EpochTimestamp.now(milliseconds=False)
313
-
314
- # print(f"Default Zero Value = {default_value}")
315
- # print(f"Current timestamp in milliseconds: {ts} {ts.milliseconds=}")
316
- # print(f"Current timestamp in seconds: {t2} {t2.milliseconds=}")
@@ -1,80 +0,0 @@
1
- from collections.abc import Callable
2
- from contextlib import contextmanager
3
- from functools import wraps
4
-
5
- from ..logging import ConsoleLogger
6
- from ._time_class import EpochTimestamp
7
-
8
-
9
- def create_timer(**defaults):
10
- """A way to set defaults for a frequently used timer decorator."""
11
-
12
- def timer_decorator(func):
13
- @wraps(func)
14
- def wrapper(*args, **kwargs):
15
- defaults["name"] = func.__name__
16
- with timer(**defaults):
17
- return func(*args, **kwargs)
18
-
19
- return wrapper
20
-
21
- return timer_decorator
22
-
23
-
24
- @contextmanager
25
- def timer(**kwargs):
26
- data: TimerData = kwargs.get("data", None) or TimerData(kwargs=kwargs)
27
- data.start()
28
- try:
29
- yield data
30
- finally:
31
- data.stop()
32
-
33
-
34
- class TimerData:
35
- def __init__(self, **kwargs):
36
- self.name: str = kwargs.get("name", "Default Timer")
37
- self.start_time: EpochTimestamp = EpochTimestamp(0)
38
- self.end_time: EpochTimestamp = EpochTimestamp(0)
39
- self._raw_elapsed_time: EpochTimestamp = EpochTimestamp(0)
40
- self.console: ConsoleLogger | None = self.get_console(kwargs.get("output", False))
41
- self.callback: Callable | None = kwargs.get("callback", None)
42
- self._style: str = kwargs.get("style", "bold green")
43
-
44
- def get_console(self, to_console: bool) -> ConsoleLogger | None:
45
- if to_console:
46
- try:
47
- console = ConsoleLogger.get_instance() # will crash if nothing else has initialized ConsoleLogger
48
- except RuntimeError:
49
- console = ConsoleLogger()
50
- return console
51
-
52
- def start(self):
53
- self.start_time = EpochTimestamp.now()
54
-
55
- def send_callback(self):
56
- if self.callback is not None:
57
- self.callback(self)
58
-
59
- def stop(self):
60
- self.end_time = EpochTimestamp.now()
61
- self._raw_elapsed_time = EpochTimestamp(self.end_time - self.start_time)
62
- if self.callback:
63
- self.send_callback()
64
- if self.console:
65
- self.console.print(f"[{self.name}] Elapsed time: {self.elapsed_seconds:.2f} seconds", style=self._style)
66
-
67
- @property
68
- def elapsed_milliseconds(self) -> int:
69
- if self._raw_elapsed_time:
70
- return self._raw_elapsed_time.to_milliseconds
71
- return -1
72
-
73
- @property
74
- def elapsed_seconds(self) -> int:
75
- if self._raw_elapsed_time:
76
- return self._raw_elapsed_time.to_seconds
77
- return -1
78
-
79
-
80
- __all__ = ["TimerData"]
@@ -1,17 +0,0 @@
1
- def add_ord_suffix(number: int) -> str:
2
- """
3
- Add an ordinal suffix to a given number, usually used for dates or rankings.
4
-
5
- Parameters:
6
- number: int - The number to add an ordinal suffix to.
7
-
8
- Returns:
9
- str - The number with its ordinal suffix.
10
- """
11
- suffix = ["th", "st", "nd", "rd", "th"][min(number % 10, 4)]
12
- if 11 <= (number % 100) <= 13:
13
- suffix = "th"
14
- return f"{number}{suffix}"
15
-
16
-
17
- __all__ = ["add_ord_suffix"]
@@ -1,218 +0,0 @@
1
- from datetime import UTC, datetime, timedelta
2
-
3
- from ..constants.date_related import DATE_FORMAT, DT_FORMAT_WITH_TZ, PT_TIME_ZONE, TIME_FORMAT, TimeZoneType
4
- from ._time_class import EpochTimestamp
5
-
6
-
7
- class TimeTools:
8
- """
9
- A utility class for managing time-related operations, including epoch timestamps, date formatting,
10
- and timezone handling.
11
-
12
- Attributes:
13
- timezone (TimeZoneType): The timezone to use for date and time operations. Defaults to PT_TIME_ZONE.
14
- datefmt (str): The format string for date and time formatting. Defaults to DT_FORMAT_WITH_TZ.
15
- """
16
-
17
- def __init__(self, timezone=PT_TIME_ZONE, datefmt=DT_FORMAT_WITH_TZ):
18
- self.timezone: TimeZoneType = timezone
19
- self.datefmt: str = datefmt
20
-
21
- def now(self, as_str: bool = False) -> EpochTimestamp | str:
22
- """
23
- Get a UTC epoch timestamp in milliseconds or a formatted string of the current date and time.
24
-
25
- Args:
26
- as_str (bool): If True, return formatted string instead of timestamp.
27
-
28
- Returns:
29
- EpochTimestamp | str: The current epoch timestamp in milliseconds or formatted string.
30
- """
31
- if as_str:
32
- return self.format_now
33
- return EpochTimestamp.now()
34
-
35
- @property
36
- def format_now(self) -> str:
37
- """
38
- Get the current date and time as a formatted string.
39
-
40
- Returns:
41
- str: The current date and time as a formatted string.
42
- """
43
- return EpochTimestamp.now().to_string(fmt=self.datefmt, tz=self.timezone)
44
-
45
- def dt_to_ts(self, dt: datetime) -> EpochTimestamp:
46
- """
47
- Convert a datetime object to an epoch timestamp in milliseconds.
48
-
49
- Args:
50
- dt (datetime): The datetime object to convert.
51
-
52
- Returns:
53
- EpochTimestamp: The epoch timestamp in milliseconds.
54
- """
55
- dt_str = dt.strftime(self.datefmt)
56
- return EpochTimestamp.from_dt_string(dt_str, milliseconds=True, fmt=self.datefmt)
57
-
58
- def str_to_ts(self, date_str: str, fmt: str | None = None) -> EpochTimestamp:
59
- """
60
- Convert a date string to an epoch timestamp in milliseconds.
61
-
62
- Args:
63
- date_str (str): The date string to convert.
64
- fmt (str | None): Format string. Uses instance default if not provided.
65
-
66
- Returns:
67
- EpochTimestamp: The epoch timestamp in milliseconds.
68
- """
69
- format_str = fmt or self.datefmt
70
- return EpochTimestamp.from_dt_string(date_str, milliseconds=True, fmt=format_str)
71
-
72
- def ts_to_str(self, ts: EpochTimestamp) -> str:
73
- """
74
- Convert an epoch timestamp to a formatted date string.
75
-
76
- Args:
77
- ts (EpochTimestamp): The epoch timestamp to convert.
78
-
79
- Returns:
80
- str: The formatted date string.
81
- """
82
- return ts.to_string(fmt=self.datefmt, tz=self.timezone)
83
-
84
- def ts_to_dt(self, ts: EpochTimestamp) -> datetime:
85
- """
86
- Convert an epoch timestamp to a datetime object.
87
-
88
- Args:
89
- ts (EpochTimestamp): The epoch timestamp to convert.
90
-
91
- Returns:
92
- datetime: The datetime object.
93
- """
94
- return ts.to_datetime.astimezone(self.timezone)
95
-
96
- def add_delta(
97
- self,
98
- ts: EpochTimestamp,
99
- days: int = 0,
100
- hours: int = 0,
101
- minutes: int = 0,
102
- seconds: int = 0,
103
- dt_obj: timedelta | None = None,
104
- ) -> EpochTimestamp:
105
- """
106
- Add a timedelta to an epoch timestamp.
107
-
108
- Args:
109
- ts (EpochTimestamp): The epoch timestamp to modify.
110
- days (int): Number of days to add.
111
- hours (int): Number of hours to add.
112
- minutes (int): Number of minutes to add.
113
- seconds (int): Number of seconds to add.
114
- dt_obj (timedelta | None): A timedelta object to add (combined with other params).
115
-
116
- Returns:
117
- EpochTimestamp: The modified epoch timestamp.
118
- """
119
- if dt_obj:
120
- if not isinstance(dt_obj, timedelta):
121
- raise TypeError("timedelta_obj must be an instance of datetime.timedelta")
122
- days += dt_obj.days
123
- seconds += int(dt_obj.seconds + (dt_obj.microseconds / 1_000_000))
124
- else:
125
- seconds = seconds + (minutes * 60) + (hours * 3600) + (days * 86400)
126
- return ts.add_timedelta(seconds=seconds)
127
-
128
- def get_day_range(self, ts: EpochTimestamp | None = None) -> tuple[EpochTimestamp, EpochTimestamp]:
129
- """
130
- Get the start and end of a day from an epoch timestamp in the instance timezone, defaulting to now.
131
-
132
- Args:
133
- ts (EpochTimestamp): The epoch timestamp to convert (optional, defaults to now).
134
-
135
- Returns:
136
- tuple[EpochTimestamp, EpochTimestamp]: A tuple containing the start and end of the day as epoch timestamps.
137
- """
138
- if ts is None:
139
- ts = EpochTimestamp.now()
140
-
141
- timestamp_start: EpochTimestamp = ts.start_of_day(tz=self.timezone)
142
- timestamp_end: EpochTimestamp = ts.end_of_day(tz=self.timezone)
143
- return timestamp_start, timestamp_end
144
-
145
- def is_same_day(self, start_date: EpochTimestamp, end_date: EpochTimestamp) -> bool:
146
- """
147
- Check if two epoch timestamps are on the same day.
148
-
149
- Args:
150
- start_date (EpochTimestamp): The first epoch timestamp.
151
- end_date (EpochTimestamp): The second epoch timestamp.
152
-
153
- Returns:
154
- bool: True if both timestamps are on the same day, False otherwise.
155
- """
156
- dt1: datetime = datetime.fromtimestamp(start_date.to_seconds, tz=UTC)
157
- dt2: datetime = datetime.fromtimestamp(end_date.to_seconds, tz=UTC)
158
- return dt1.date() == dt2.date()
159
-
160
- def is_multi_day(self, start_date: str | datetime, end_date: str | datetime) -> bool:
161
- """Determines if the span between two dates covers multiple days.
162
-
163
- Args:
164
- start_date (str | datetime): The start date.
165
- end_date (str | datetime): The end date.
166
-
167
- Returns:
168
- bool: If the span covers multiple days, return True. Else, return False.
169
- """
170
- if isinstance(start_date, str):
171
- start_date = datetime.strptime(start_date, self.datefmt)
172
- if isinstance(end_date, str):
173
- end_date = datetime.strptime(end_date, self.datefmt)
174
- return start_date.date() != end_date.date()
175
-
176
- def time_only(self, ts: EpochTimestamp) -> str:
177
- """
178
- Get the time part of an epoch timestamp as a formatted string.
179
-
180
- Args:
181
- ts (EpochTimestamp): The epoch timestamp to convert.
182
-
183
- Returns:
184
- str: The time part of the timestamp.
185
- """
186
- return ts.to_string(fmt=TIME_FORMAT, tz=self.timezone)
187
-
188
- def date_only(self, ts: EpochTimestamp) -> str:
189
- """
190
- Get the date part of an epoch timestamp as a formatted string.
191
-
192
- Args:
193
- ts (EpochTimestamp): The epoch timestamp to convert.
194
-
195
- Returns:
196
- str: The date part of the timestamp.
197
- """
198
- return ts.to_string(fmt=DATE_FORMAT, tz=self.timezone)
199
-
200
-
201
- # if __name__ == "__main__":
202
-
203
- # # def example_callback(timer_data: TimerData):
204
- # # print(f"Timer '{timer_data.name}' finished in {timer_data._raw_elapsed_time} ms")
205
-
206
- # # # Example usage
207
- # # with timer(
208
- # # name="Tester Timer",
209
- # # console=True,
210
- # # ) as time:
211
- # # sleep(2) # Simulate some work
212
- # # data: TimerData = time
213
-
214
- # # get start and end of day
215
- # time_tools = TimeTools()
216
- # start_of_day, end_of_day = time_tools.get_day_range()
217
- # print(f"Start of day: {start_of_day.to_string()}")
218
- # print(f"End of day: {end_of_day.to_string()}")