clock-pattern 0.3.0__tar.gz → 0.5.0__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.
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.4
2
+ Name: clock-pattern
3
+ Version: 0.5.0
4
+ Summary: The Clock Pattern is a Python package that turns time into an injectable dependency.
5
+ Project-URL: Homepage, https://github.com/adriamontoto/clock-pattern
6
+ Project-URL: Repository, https://github.com/adriamontoto/clock-pattern
7
+ Project-URL: Issues, https://github.com/adriamontoto/clock-pattern/issues
8
+ Author: Adria Montoto
9
+ License-Expression: MIT
10
+ License-File: LICENSE.md
11
+ Keywords: clock,dependency-injection,development,domain-driven-design,pattern,python,utilities
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: value-object-pattern>=0.6.0
23
+ Description-Content-Type: text/markdown
24
+
25
+ <a name="readme-top"></a>
26
+
27
+ # 🕰️ Clock Pattern
28
+
29
+ <p align="center">
30
+ <a href="https://github.com/adriamontoto/clock-pattern/actions/workflows/ci.yaml?event=push&branch=master" target="_blank">
31
+ <img src="https://github.com/adriamontoto/clock-pattern/actions/workflows/ci.yaml/badge.svg?event=push&branch=master" alt="CI Pipeline">
32
+ </a>
33
+ <a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/adriamontoto/clock-pattern" target="_blank">
34
+ <img src="https://coverage-badge.samuelcolvin.workers.dev/adriamontoto/clock-pattern.svg" alt="Coverage Pipeline">
35
+ </a>
36
+ <a href="https://pypi.org/project/clock-pattern" target="_blank">
37
+ <img src="https://img.shields.io/pypi/v/clock-pattern?color=%2334D058&label=pypi%20package" alt="Package Version">
38
+ </a>
39
+ <a href="https://pypi.org/project/clock-pattern/" target="_blank">
40
+ <img src="https://img.shields.io/pypi/pyversions/clock-pattern.svg?color=%2334D058" alt="Supported Python Versions">
41
+ </a>
42
+ <a href="https://pepy.tech/projects/clock-pattern" target="_blank">
43
+ <img src="https://static.pepy.tech/badge/clock-pattern/month" alt="Package Downloads">
44
+ </a>
45
+ </p>
46
+
47
+ The **Clock Pattern** is a Python 🐍 package that turns time into an injectable dependency 🧩. By replacing ad-hoc datetime.now() calls with a swappable Clock interface 🕰️ you unlock deterministic tests 🧪, decouple business logic from the OS clock, and gain the freedom to swap in high-precision or logical clocks without touching domain code.
48
+ <br><br>
49
+
50
+ ## Table of Contents
51
+
52
+ - [📥 Installation](#installation)
53
+ - [💻 Utilization](#utilization)
54
+ - [📚 Available Clocks](#available-clocks)
55
+ - [🎄 Real-Life Case: Christmas Detector Service](#real-life-case-christmas-detector-service)
56
+ - [🤝 Contributing](#contributing)
57
+ - [🔑 License](#license)
58
+
59
+ <p align="right">
60
+ <a href="#readme-top">🔼 Back to top</a>
61
+ </p><br><br>
62
+
63
+ <a name="installation"></a>
64
+
65
+ ## 📥 Installation
66
+
67
+ You can install **Clock Pattern** using `pip`:
68
+
69
+ ```bash
70
+ pip install clock-pattern
71
+ ```
72
+
73
+ <p align="right">
74
+ <a href="#readme-top">🔼 Back to top</a>
75
+ </p><br><br>
76
+
77
+ <a name="utilization"></a>
78
+
79
+ ## 💻 Utilization
80
+
81
+ The **Clock Pattern** library is designed to be straightforward. Simply import the desired clock and use its `now()` or `today()` methods to get the current datetime/date. This approach allows for easy dependency injection and testing.
82
+
83
+ Here is a basic example of how to use the [`SystemClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/system_clock.py) clock:
84
+
85
+ ```python
86
+ from datetime import timezone
87
+
88
+ from clock_pattern import SystemClock
89
+
90
+ clock = SystemClock(timezone=timezone.utc)
91
+ print(clock.now())
92
+ # >>> 2025-06-16 13:57:26.210964+00:00
93
+ ```
94
+
95
+ <p align="right">
96
+ <a href="#readme-top">🔼 Back to top</a>
97
+ </p><br><br>
98
+
99
+ <a name="available-clocks"></a>
100
+
101
+ ## 📚 Available Clocks
102
+
103
+ The package offers several clock implementations to suit different needs:
104
+
105
+ - [`clock_pattern.SystemClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/system_clock.py): The standard clock implementation that returns the system's current datetime/date with the provided timezone.
106
+ - [`clock_pattern.UtcClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/utc_clock.py): A clock implementation that returns the system's current datetime/date in UTC. Ideal for production environments.
107
+ - [`clock_pattern.clocks.testing.FixedClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/testing/fixed_clock.py): A clock that always returns a fixed, preset datetime/date. It is perfect for basic testing as it allows you to control the datetime/date within your test environment, ensuring deterministic results.
108
+ - [`clock_pattern.clocks.testing.MockClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/testing/mock_clock.py): A clock that allows you to mock the system clock. It is perfect for more complex testing as it allows you to control the datetime/date within your test environment and if or not the methods are called or not.
109
+ <p align="right">
110
+ <a href="#readme-top">🔼 Back to top</a>
111
+ </p><br><br>
112
+
113
+ <a name="real-life-case-christmas-detector-service"></a>
114
+
115
+ ## 🎄 Real-Life Case: Christmas Detector Service
116
+
117
+ Below is an example of a real-life scenario where Clock Pattern can create clean and testable code. We have a `ChristmasDetectorService` that checks if the curren date falls within a specific Christmas holiday range. Using the Clock Pattern, in this case [`UtcClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/utc_clock.py) and [`MockClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/testing/mock_clock.py), we can decouple the service from the python `datetime.now()` and `datetime.today()` functions, making it easy to test for different dates without changing the system's time.
118
+
119
+ ```python
120
+ from datetime import date
121
+
122
+ from clock_pattern import Clock, UtcClock
123
+ from clock_pattern.clocks.testing import MockClock
124
+
125
+
126
+ class ChristmasDetectorService:
127
+ def __init__(self, clock: Clock) -> None:
128
+ self.clock = clock
129
+ self.christmas_start = date(year=2024, month=12, day=24)
130
+ self.christmas_end = date(year=2025, month=1, day=6)
131
+
132
+ def is_christmas(self) -> bool:
133
+ return self.christmas_start <= self.clock.today() <= self.christmas_end
134
+
135
+
136
+ clock = UtcClock()
137
+ christmas_detector_service = ChristmasDetectorService(clock=clock)
138
+
139
+ print(christmas_detector_service.is_christmas())
140
+ # >>> False
141
+
142
+
143
+ def test_christmas_detector_is_christmas() -> None:
144
+ clock = MockClock()
145
+ christmas_detector_service = ChristmasDetectorService(clock=clock)
146
+
147
+ today = date(year=2024, month=12, day=25)
148
+ clock.prepare_today_method_return_value(today=today)
149
+
150
+ assert christmas_detector_service.is_christmas() is True
151
+ clock.assert_today_method_was_called_once()
152
+
153
+
154
+ def test_christmas_detector_is_not_christmas() -> None:
155
+ clock = MockClock()
156
+ christmas_detector_service = ChristmasDetectorService(clock=clock)
157
+
158
+ today = date(year=2025, month=1, day=7)
159
+ clock.prepare_today_method_return_value(today=today)
160
+
161
+ assert christmas_detector_service.is_christmas() is False
162
+ clock.assert_today_method_was_called_once()
163
+ ```
164
+
165
+ <p align="right">
166
+ <a href="#readme-top">🔼 Back to top</a>
167
+ </p><br><br>
168
+
169
+ <a name="contributing"></a>
170
+
171
+ ## 🤝 Contributing
172
+
173
+ We love community help! Before you open an issue or pull request, please read:
174
+
175
+ - [`🤝 How to Contribute`](https://github.com/adriamontoto/clock-pattern/blob/master/.github/CONTRIBUTING.md)
176
+ - [`🧭 Code of Conduct`](https://github.com/adriamontoto/clock-pattern/blob/master/.github/CODE_OF_CONDUCT.md)
177
+ - [`🔐 Security Policy`](https://github.com/adriamontoto/clock-pattern/blob/master/.github/SECURITY.md)
178
+
179
+ _Thank you for helping make **🕰️ Clock Pattern** package awesome! 🌟_
180
+
181
+ <p align="right">
182
+ <a href="#readme-top">🔼 Back to top</a>
183
+ </p><br><br>
184
+
185
+ <a name="license"></a>
186
+
187
+ ## 🔑 License
188
+
189
+ This project is licensed under the terms of the [`MIT license`](https://github.com/adriamontoto/clock-pattern/blob/master/LICENSE.md).
190
+
191
+ <p align="right">
192
+ <a href="#readme-top">🔼 Back to top</a>
193
+ </p>
@@ -0,0 +1,169 @@
1
+ <a name="readme-top"></a>
2
+
3
+ # 🕰️ Clock Pattern
4
+
5
+ <p align="center">
6
+ <a href="https://github.com/adriamontoto/clock-pattern/actions/workflows/ci.yaml?event=push&branch=master" target="_blank">
7
+ <img src="https://github.com/adriamontoto/clock-pattern/actions/workflows/ci.yaml/badge.svg?event=push&branch=master" alt="CI Pipeline">
8
+ </a>
9
+ <a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/adriamontoto/clock-pattern" target="_blank">
10
+ <img src="https://coverage-badge.samuelcolvin.workers.dev/adriamontoto/clock-pattern.svg" alt="Coverage Pipeline">
11
+ </a>
12
+ <a href="https://pypi.org/project/clock-pattern" target="_blank">
13
+ <img src="https://img.shields.io/pypi/v/clock-pattern?color=%2334D058&label=pypi%20package" alt="Package Version">
14
+ </a>
15
+ <a href="https://pypi.org/project/clock-pattern/" target="_blank">
16
+ <img src="https://img.shields.io/pypi/pyversions/clock-pattern.svg?color=%2334D058" alt="Supported Python Versions">
17
+ </a>
18
+ <a href="https://pepy.tech/projects/clock-pattern" target="_blank">
19
+ <img src="https://static.pepy.tech/badge/clock-pattern/month" alt="Package Downloads">
20
+ </a>
21
+ </p>
22
+
23
+ The **Clock Pattern** is a Python 🐍 package that turns time into an injectable dependency 🧩. By replacing ad-hoc datetime.now() calls with a swappable Clock interface 🕰️ you unlock deterministic tests 🧪, decouple business logic from the OS clock, and gain the freedom to swap in high-precision or logical clocks without touching domain code.
24
+ <br><br>
25
+
26
+ ## Table of Contents
27
+
28
+ - [📥 Installation](#installation)
29
+ - [💻 Utilization](#utilization)
30
+ - [📚 Available Clocks](#available-clocks)
31
+ - [🎄 Real-Life Case: Christmas Detector Service](#real-life-case-christmas-detector-service)
32
+ - [🤝 Contributing](#contributing)
33
+ - [🔑 License](#license)
34
+
35
+ <p align="right">
36
+ <a href="#readme-top">🔼 Back to top</a>
37
+ </p><br><br>
38
+
39
+ <a name="installation"></a>
40
+
41
+ ## 📥 Installation
42
+
43
+ You can install **Clock Pattern** using `pip`:
44
+
45
+ ```bash
46
+ pip install clock-pattern
47
+ ```
48
+
49
+ <p align="right">
50
+ <a href="#readme-top">🔼 Back to top</a>
51
+ </p><br><br>
52
+
53
+ <a name="utilization"></a>
54
+
55
+ ## 💻 Utilization
56
+
57
+ The **Clock Pattern** library is designed to be straightforward. Simply import the desired clock and use its `now()` or `today()` methods to get the current datetime/date. This approach allows for easy dependency injection and testing.
58
+
59
+ Here is a basic example of how to use the [`SystemClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/system_clock.py) clock:
60
+
61
+ ```python
62
+ from datetime import timezone
63
+
64
+ from clock_pattern import SystemClock
65
+
66
+ clock = SystemClock(timezone=timezone.utc)
67
+ print(clock.now())
68
+ # >>> 2025-06-16 13:57:26.210964+00:00
69
+ ```
70
+
71
+ <p align="right">
72
+ <a href="#readme-top">🔼 Back to top</a>
73
+ </p><br><br>
74
+
75
+ <a name="available-clocks"></a>
76
+
77
+ ## 📚 Available Clocks
78
+
79
+ The package offers several clock implementations to suit different needs:
80
+
81
+ - [`clock_pattern.SystemClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/system_clock.py): The standard clock implementation that returns the system's current datetime/date with the provided timezone.
82
+ - [`clock_pattern.UtcClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/utc_clock.py): A clock implementation that returns the system's current datetime/date in UTC. Ideal for production environments.
83
+ - [`clock_pattern.clocks.testing.FixedClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/testing/fixed_clock.py): A clock that always returns a fixed, preset datetime/date. It is perfect for basic testing as it allows you to control the datetime/date within your test environment, ensuring deterministic results.
84
+ - [`clock_pattern.clocks.testing.MockClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/testing/mock_clock.py): A clock that allows you to mock the system clock. It is perfect for more complex testing as it allows you to control the datetime/date within your test environment and if or not the methods are called or not.
85
+ <p align="right">
86
+ <a href="#readme-top">🔼 Back to top</a>
87
+ </p><br><br>
88
+
89
+ <a name="real-life-case-christmas-detector-service"></a>
90
+
91
+ ## 🎄 Real-Life Case: Christmas Detector Service
92
+
93
+ Below is an example of a real-life scenario where Clock Pattern can create clean and testable code. We have a `ChristmasDetectorService` that checks if the curren date falls within a specific Christmas holiday range. Using the Clock Pattern, in this case [`UtcClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/utc_clock.py) and [`MockClock`](https://github.com/adriamontoto/clock-pattern/blob/master/clock_pattern/clocks/testing/mock_clock.py), we can decouple the service from the python `datetime.now()` and `datetime.today()` functions, making it easy to test for different dates without changing the system's time.
94
+
95
+ ```python
96
+ from datetime import date
97
+
98
+ from clock_pattern import Clock, UtcClock
99
+ from clock_pattern.clocks.testing import MockClock
100
+
101
+
102
+ class ChristmasDetectorService:
103
+ def __init__(self, clock: Clock) -> None:
104
+ self.clock = clock
105
+ self.christmas_start = date(year=2024, month=12, day=24)
106
+ self.christmas_end = date(year=2025, month=1, day=6)
107
+
108
+ def is_christmas(self) -> bool:
109
+ return self.christmas_start <= self.clock.today() <= self.christmas_end
110
+
111
+
112
+ clock = UtcClock()
113
+ christmas_detector_service = ChristmasDetectorService(clock=clock)
114
+
115
+ print(christmas_detector_service.is_christmas())
116
+ # >>> False
117
+
118
+
119
+ def test_christmas_detector_is_christmas() -> None:
120
+ clock = MockClock()
121
+ christmas_detector_service = ChristmasDetectorService(clock=clock)
122
+
123
+ today = date(year=2024, month=12, day=25)
124
+ clock.prepare_today_method_return_value(today=today)
125
+
126
+ assert christmas_detector_service.is_christmas() is True
127
+ clock.assert_today_method_was_called_once()
128
+
129
+
130
+ def test_christmas_detector_is_not_christmas() -> None:
131
+ clock = MockClock()
132
+ christmas_detector_service = ChristmasDetectorService(clock=clock)
133
+
134
+ today = date(year=2025, month=1, day=7)
135
+ clock.prepare_today_method_return_value(today=today)
136
+
137
+ assert christmas_detector_service.is_christmas() is False
138
+ clock.assert_today_method_was_called_once()
139
+ ```
140
+
141
+ <p align="right">
142
+ <a href="#readme-top">🔼 Back to top</a>
143
+ </p><br><br>
144
+
145
+ <a name="contributing"></a>
146
+
147
+ ## 🤝 Contributing
148
+
149
+ We love community help! Before you open an issue or pull request, please read:
150
+
151
+ - [`🤝 How to Contribute`](https://github.com/adriamontoto/clock-pattern/blob/master/.github/CONTRIBUTING.md)
152
+ - [`🧭 Code of Conduct`](https://github.com/adriamontoto/clock-pattern/blob/master/.github/CODE_OF_CONDUCT.md)
153
+ - [`🔐 Security Policy`](https://github.com/adriamontoto/clock-pattern/blob/master/.github/SECURITY.md)
154
+
155
+ _Thank you for helping make **🕰️ Clock Pattern** package awesome! 🌟_
156
+
157
+ <p align="right">
158
+ <a href="#readme-top">🔼 Back to top</a>
159
+ </p><br><br>
160
+
161
+ <a name="license"></a>
162
+
163
+ ## 🔑 License
164
+
165
+ This project is licensed under the terms of the [`MIT license`](https://github.com/adriamontoto/clock-pattern/blob/master/LICENSE.md).
166
+
167
+ <p align="right">
168
+ <a href="#readme-top">🔼 Back to top</a>
169
+ </p>
@@ -0,0 +1,10 @@
1
+ __version__ = '0.5.0'
2
+
3
+ from .clocks import SystemClock, UtcClock
4
+ from .models import Clock
5
+
6
+ __all__ = (
7
+ 'Clock',
8
+ 'SystemClock',
9
+ 'UtcClock',
10
+ )
@@ -23,7 +23,7 @@ class SystemClock(Clock):
23
23
 
24
24
  Example:
25
25
  ```python
26
- from clock_pattern.clocks import SystemClock
26
+ from clock_pattern import SystemClock
27
27
 
28
28
  clock = SystemClock()
29
29
  print(clock.now())
@@ -48,7 +48,7 @@ class SystemClock(Clock):
48
48
 
49
49
  Example:
50
50
  ```python
51
- from clock_pattern.clocks import SystemClock
51
+ from clock_pattern import SystemClock
52
52
 
53
53
  clock = SystemClock()
54
54
  print(clock.now())
@@ -72,7 +72,7 @@ class SystemClock(Clock):
72
72
 
73
73
  Example:
74
74
  ```python
75
- from clock_pattern.clocks import SystemClock
75
+ from clock_pattern import SystemClock
76
76
 
77
77
  clock = SystemClock()
78
78
  print(clock.now())
@@ -91,7 +91,7 @@ class SystemClock(Clock):
91
91
 
92
92
  Example:
93
93
  ```python
94
- from clock_pattern.clocks import SystemClock
94
+ from clock_pattern import SystemClock
95
95
 
96
96
  clock = SystemClock()
97
97
  print(clock.now())
@@ -110,7 +110,7 @@ class SystemClock(Clock):
110
110
 
111
111
  Example:
112
112
  ```python
113
- from clock_pattern.clocks import SystemClock
113
+ from clock_pattern import SystemClock
114
114
 
115
115
  clock = SystemClock()
116
116
  print(clock.timezone)
@@ -0,0 +1,7 @@
1
+ from .fixed_clock import FixedClock
2
+ from .mock_clock import MockClock
3
+
4
+ __all__ = (
5
+ 'FixedClock',
6
+ 'MockClock',
7
+ )
@@ -0,0 +1,289 @@
1
+ """
2
+ MockClock module.
3
+ """
4
+
5
+ from sys import version_info
6
+
7
+ if version_info >= (3, 12):
8
+ from typing import override # pragma: no cover
9
+ else:
10
+ from typing_extensions import override # pragma: no cover
11
+
12
+ from datetime import UTC, date, datetime
13
+ from unittest.mock import Mock
14
+
15
+ from value_object_pattern.usables import NotNoneValueObject
16
+ from value_object_pattern.usables.dates import DateValueObject, DatetimeValueObject
17
+
18
+ from clock_pattern.models.clock import Clock
19
+
20
+
21
+ class MockClock(Clock):
22
+ """
23
+ MockModel is responsible of mocking Clock class implementation for testing purposes.
24
+
25
+ Example:
26
+ ```python
27
+ from datetime import datetime
28
+
29
+ from clock_pattern.clocks.testing import MockClock
30
+
31
+ return_datetime = datetime(year=1999, month=1, day=1)
32
+ clock = MockClock()
33
+
34
+ clock.prepare_now_method_return_value(now=return_datetime)
35
+ print(clock.now())
36
+ # >>> 1999-01-01 00:00:00+00:00
37
+
38
+ clock.assert_now_method_was_called_once()
39
+ ```
40
+ """
41
+
42
+ _now_mock: Mock
43
+ _today_mock: Mock
44
+ _now_datetime: datetime | None
45
+ _today_date: date | None
46
+
47
+ def __init__(self) -> None:
48
+ """
49
+ MockClock constructor.
50
+
51
+ Example:
52
+ ```python
53
+ from datetime import datetime
54
+
55
+ from clock_pattern.clocks.testing import MockClock
56
+
57
+ return_datetime = datetime(year=1999, month=1, day=1)
58
+ clock = MockClock()
59
+
60
+ clock.prepare_now_method_return_value(now=return_datetime)
61
+ print(clock.now())
62
+ # >>> 1999-01-01 00:00:00+00:00
63
+
64
+ clock.assert_now_method_was_called_once()
65
+ ```
66
+ """
67
+ self._now_mock = Mock()
68
+ self._today_mock = Mock()
69
+ self._now_datetime = None
70
+ self._today_date = None
71
+
72
+ @override
73
+ def now(self) -> datetime:
74
+ """
75
+ Retrieve the current datetime (now). Use `MockClock.prepare_now_method_return_value` to prepare the return value
76
+ for this method.
77
+
78
+ Raises:
79
+ TypeError: If `now` method return value is not configured.
80
+
81
+ Returns:
82
+ datetime: The current datetime.
83
+
84
+ Example:
85
+ ```python
86
+ from datetime import datetime
87
+
88
+ from clock_pattern.clocks.testing import MockClock
89
+
90
+ return_datetime = datetime(year=1999, month=1, day=1)
91
+ clock = MockClock()
92
+
93
+ clock.prepare_now_method_return_value(now=return_datetime)
94
+ print(clock.now())
95
+ # >>> 1999-01-01 00:00:00+00:00
96
+
97
+ clock.assert_now_method_was_called_once()
98
+ ```
99
+ """
100
+ NotNoneValueObject(value=self._now_datetime, title='MockClock', parameter='now')
101
+
102
+ self._now_mock()
103
+
104
+ return self._now_datetime # type: ignore[return-value]
105
+
106
+ def prepare_now_method_return_value(self, *, now: datetime) -> None:
107
+ """
108
+ Prepare now method to return the provided datetime `now`. If the provided datetime `now` has not timezone UTC
109
+ will be set.
110
+
111
+ Args:
112
+ now (datetime): Datetime to return.
113
+
114
+ Raises:
115
+ TypeError: If `now` is not of type datetime.
116
+
117
+ Example:
118
+ ```python
119
+ from datetime import datetime
120
+
121
+ from clock_pattern.clocks.testing import MockClock
122
+
123
+ return_datetime = datetime(year=1999, month=1, day=1)
124
+ clock = MockClock()
125
+
126
+ clock.prepare_now_method_return_value(now=return_datetime)
127
+ print(clock.now())
128
+ # >>> 1999-01-01 00:00:00+00:00
129
+
130
+ clock.assert_now_method_was_called_once()
131
+ ```
132
+ """
133
+ DatetimeValueObject(value=now, title='MockClock', parameter='now')
134
+
135
+ if now.tzinfo is None:
136
+ now = now.replace(tzinfo=UTC)
137
+
138
+ self._now_datetime = now
139
+
140
+ def assert_now_method_was_called_once(self) -> None:
141
+ """
142
+ Assert that the now method was called once.
143
+
144
+ Example:
145
+ ```python
146
+ from datetime import datetime
147
+
148
+ from clock_pattern.clocks.testing import MockClock
149
+
150
+ return_datetime = datetime(year=1999, month=1, day=1)
151
+ clock = MockClock()
152
+
153
+ clock.prepare_now_method_return_value(now=return_datetime)
154
+ print(clock.now())
155
+ # >>> 1999-01-01 00:00:00+00:00
156
+
157
+ clock.assert_now_method_was_called_once()
158
+ ```
159
+ """
160
+ self._now_mock.assert_called_once_with()
161
+
162
+ def assert_now_method_was_not_called(self) -> None:
163
+ """
164
+ Assert that the now method was not called.
165
+
166
+ Example:
167
+ ```python
168
+ from datetime import date
169
+
170
+ from clock_pattern.clocks.testing import MockClock
171
+
172
+ return_date = date(year=1999, month=1, day=1)
173
+ clock = MockClock()
174
+
175
+ clock.prepare_today_method_return_value(today=return_date)
176
+ print(clock.today())
177
+ # >>> 2025-06-16
178
+
179
+ clock.assert_now_method_was_not_called()
180
+ ```
181
+ """
182
+ self._now_mock.assert_not_called()
183
+
184
+ @override
185
+ def today(self) -> date:
186
+ """
187
+ Retrieve the current date (today). Use `MockClock.prepare_today_method_return_value` to prepare the return value
188
+ for this method.
189
+
190
+ Raises:
191
+ ValueError: If `today` method return value is not configured.
192
+
193
+ Returns:
194
+ date: The current date.
195
+
196
+ Example:
197
+ ```python
198
+ from datetime import date
199
+
200
+ from clock_pattern.clocks.testing import MockClock
201
+
202
+ return_date = date(year=1999, month=1, day=1)
203
+ clock = MockClock()
204
+
205
+ clock.prepare_today_method_return_value(today=return_date)
206
+ print(clock.today())
207
+ # >>> 2025-06-16
208
+
209
+ clock.assert_today_method_was_called_once()
210
+ ```
211
+ """
212
+ NotNoneValueObject(value=self._today_date, title='MockClock', parameter='today')
213
+
214
+ self._today_mock()
215
+
216
+ return self._today_date # type: ignore[return-value]
217
+
218
+ def prepare_today_method_return_value(self, *, today: date) -> None:
219
+ """
220
+ Prepare today method to return the provided date `today`.
221
+
222
+ Args:
223
+ today (date): Date to return.
224
+
225
+ Raises:
226
+ TypeError: If `today` is not of type date.
227
+
228
+ Example:
229
+ ```python
230
+ from datetime import date
231
+
232
+ from clock_pattern.clocks.testing import MockClock
233
+
234
+ return_date = date(year=1999, month=1, day=1)
235
+ clock = MockClock()
236
+
237
+ clock.prepare_today_method_return_value(today=return_date)
238
+ print(clock.today())
239
+ # >>> 2025-06-16
240
+
241
+ clock.assert_today_method_was_called_once()
242
+ ```
243
+ """
244
+ DateValueObject(value=today, title='MockClock', parameter='today')
245
+ self._today_date = today
246
+
247
+ def assert_today_method_was_called_once(self) -> None:
248
+ """
249
+ Assert that the today method was called once.
250
+
251
+ Example:
252
+ ```python
253
+ from datetime import date
254
+
255
+ from clock_pattern.clocks.testing import MockClock
256
+
257
+ return_date = date(year=1999, month=1, day=1)
258
+ clock = MockClock()
259
+
260
+ clock.prepare_today_method_return_value(today=return_date)
261
+ print(clock.today())
262
+ # >>> 2025-06-16
263
+
264
+ clock.assert_today_method_was_called_once()
265
+ ```
266
+ """
267
+ self._today_mock.assert_called_once_with()
268
+
269
+ def assert_today_method_was_not_called(self) -> None:
270
+ """
271
+ Assert that the today method was not called.
272
+
273
+ Example:
274
+ ```python
275
+ from datetime import datetime
276
+
277
+ from clock_pattern.clocks.testing import MockClock
278
+
279
+ return_datetime = datetime(year=1999, month=1, day=1)
280
+ clock = MockClock()
281
+
282
+ clock.prepare_now_method_return_value(now=return_datetime)
283
+ print(clock.now())
284
+ # >>> 1999-01-01 00:00:00+00:00
285
+
286
+ clock.assert_today_method_was_not_called()
287
+ ```
288
+ """
289
+ self._today_mock.assert_not_called()
@@ -13,7 +13,7 @@ class UtcClock(SystemClock):
13
13
 
14
14
  Example:
15
15
  ```python
16
- from clock_pattern.clocks import UtcClock
16
+ from clock_pattern import UtcClock
17
17
 
18
18
  clock = UtcClock()
19
19
  print(clock.now())
@@ -27,7 +27,7 @@ class UtcClock(SystemClock):
27
27
 
28
28
  Example:
29
29
  ```python
30
- from clock_pattern.clocks import UtcClock
30
+ from clock_pattern import UtcClock
31
31
 
32
32
  clock = UtcClock()
33
33
  print(clock.now())
@@ -14,7 +14,7 @@ class Clock(ABC):
14
14
 
15
15
  Example:
16
16
  ```python
17
- from clock_pattern.clocks import SystemClock
17
+ from clock_pattern import SystemClock
18
18
 
19
19
  clock = SystemClock()
20
20
  print(clock.now())
@@ -32,7 +32,7 @@ class Clock(ABC):
32
32
 
33
33
  Example:
34
34
  ```python
35
- from clock_pattern.clocks import SystemClock
35
+ from clock_pattern import SystemClock
36
36
 
37
37
  clock = SystemClock()
38
38
  print(clock.now())
@@ -50,7 +50,7 @@ class Clock(ABC):
50
50
 
51
51
  Example:
52
52
  ```python
53
- from clock_pattern.clocks import SystemClock
53
+ from clock_pattern import SystemClock
54
54
 
55
55
  clock = SystemClock()
56
56
  print(clock.today())
@@ -37,7 +37,7 @@ keywords = [
37
37
  'domain-driven-design',
38
38
  ]
39
39
  requires-python = '>=3.11'
40
- dependencies = ['value-object-pattern>=0.5.0']
40
+ dependencies = ['value-object-pattern>=0.6.0']
41
41
  dynamic = ['version']
42
42
 
43
43
  [project.urls]
@@ -1,95 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: clock-pattern
3
- Version: 0.3.0
4
- Summary: The Clock Pattern is a Python package that turns time into an injectable dependency.
5
- Project-URL: Homepage, https://github.com/adriamontoto/clock-pattern
6
- Project-URL: Repository, https://github.com/adriamontoto/clock-pattern
7
- Project-URL: Issues, https://github.com/adriamontoto/clock-pattern/issues
8
- Author: Adria Montoto
9
- License-Expression: MIT
10
- License-File: LICENSE.md
11
- Keywords: clock,dependency-injection,development,domain-driven-design,pattern,python,utilities
12
- Classifier: Intended Audience :: Developers
13
- Classifier: License :: OSI Approved :: MIT License
14
- Classifier: Operating System :: OS Independent
15
- Classifier: Programming Language :: Python
16
- Classifier: Programming Language :: Python :: 3
17
- Classifier: Programming Language :: Python :: 3.11
18
- Classifier: Programming Language :: Python :: 3.12
19
- Classifier: Programming Language :: Python :: 3.13
20
- Classifier: Typing :: Typed
21
- Requires-Python: >=3.11
22
- Requires-Dist: value-object-pattern>=0.5.0
23
- Description-Content-Type: text/markdown
24
-
25
- <a name="readme-top"></a>
26
-
27
- # 🕰️ Clock Pattern
28
-
29
- <p align="center">
30
- <a href="https://github.com/adriamontoto/clock-pattern/actions/workflows/ci.yaml?event=push&branch=master" target="_blank">
31
- <img src="https://github.com/adriamontoto/clock-pattern/actions/workflows/ci.yaml/badge.svg?event=push&branch=master" alt="CI Pipeline">
32
- </a>
33
- <a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/adriamontoto/clock-pattern" target="_blank">
34
- <img src="https://coverage-badge.samuelcolvin.workers.dev/adriamontoto/clock-pattern.svg" alt="Coverage Pipeline">
35
- </a>
36
- <a href="https://pypi.org/project/clock-pattern" target="_blank">
37
- <img src="https://img.shields.io/pypi/v/clock-pattern?color=%2334D058&label=pypi%20package" alt="Package Version">
38
- </a>
39
- <a href="https://pypi.org/project/clock-pattern/" target="_blank">
40
- <img src="https://img.shields.io/pypi/pyversions/clock-pattern.svg?color=%2334D058" alt="Supported Python Versions">
41
- </a>
42
- </p>
43
-
44
- The **Clock Pattern** is a Python 🐍 package that turns time into an injectable dependency 🧩. By replacing ad-hoc datetime.now() calls with a swappable Clock interface 🕰️ you unlock deterministic tests 🧪, decouple business logic from the OS clock, and gain the freedom to swap in high-precision or logical clocks without touching domain code.
45
- <br><br>
46
-
47
- ## Table of Contents
48
-
49
- - [📥 Installation](#installation)
50
- - [🤝 Contributing](#contributing)
51
- - [🔑 License](#license)
52
-
53
- <p align="right">
54
- <a href="#readme-top">🔼 Back to top</a>
55
- </p><br><br>
56
-
57
- <a name="installation"></a>
58
-
59
- ## 📥 Installation
60
-
61
- You can install **Clock Pattern** using `pip`:
62
-
63
- ```bash
64
- pip install clock-pattern
65
- ```
66
-
67
- <p align="right">
68
- <a href="#readme-top">🔼 Back to top</a>
69
- </p><br><br>
70
-
71
- <a name="contributing"></a>
72
-
73
- ## 🤝 Contributing
74
-
75
- We love community help! Before you open an issue or pull request, please read:
76
-
77
- - [`🤝 How to Contribute`](https://github.com/adriamontoto/clock-pattern/blob/master/.github/CONTRIBUTING.md)
78
- - [`🧭 Code of Conduct`](https://github.com/adriamontoto/clock-pattern/blob/master/.github/CODE_OF_CONDUCT.md)
79
- - [`🔐 Security Policy`](https://github.com/adriamontoto/clock-pattern/blob/master/.github/SECURITY.md)
80
-
81
- _Thank you for helping make **🕰️ Clock Pattern** package awesome! 🌟_
82
-
83
- <p align="right">
84
- <a href="#readme-top">🔼 Back to top</a>
85
- </p><br><br>
86
-
87
- <a name="license"></a>
88
-
89
- ## 🔑 License
90
-
91
- This project is licensed under the terms of the [`MIT license`](https://github.com/adriamontoto/clock-pattern/blob/master/LICENSE.md).
92
-
93
- <p align="right">
94
- <a href="#readme-top">🔼 Back to top</a>
95
- </p>
@@ -1,71 +0,0 @@
1
- <a name="readme-top"></a>
2
-
3
- # 🕰️ Clock Pattern
4
-
5
- <p align="center">
6
- <a href="https://github.com/adriamontoto/clock-pattern/actions/workflows/ci.yaml?event=push&branch=master" target="_blank">
7
- <img src="https://github.com/adriamontoto/clock-pattern/actions/workflows/ci.yaml/badge.svg?event=push&branch=master" alt="CI Pipeline">
8
- </a>
9
- <a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/adriamontoto/clock-pattern" target="_blank">
10
- <img src="https://coverage-badge.samuelcolvin.workers.dev/adriamontoto/clock-pattern.svg" alt="Coverage Pipeline">
11
- </a>
12
- <a href="https://pypi.org/project/clock-pattern" target="_blank">
13
- <img src="https://img.shields.io/pypi/v/clock-pattern?color=%2334D058&label=pypi%20package" alt="Package Version">
14
- </a>
15
- <a href="https://pypi.org/project/clock-pattern/" target="_blank">
16
- <img src="https://img.shields.io/pypi/pyversions/clock-pattern.svg?color=%2334D058" alt="Supported Python Versions">
17
- </a>
18
- </p>
19
-
20
- The **Clock Pattern** is a Python 🐍 package that turns time into an injectable dependency 🧩. By replacing ad-hoc datetime.now() calls with a swappable Clock interface 🕰️ you unlock deterministic tests 🧪, decouple business logic from the OS clock, and gain the freedom to swap in high-precision or logical clocks without touching domain code.
21
- <br><br>
22
-
23
- ## Table of Contents
24
-
25
- - [📥 Installation](#installation)
26
- - [🤝 Contributing](#contributing)
27
- - [🔑 License](#license)
28
-
29
- <p align="right">
30
- <a href="#readme-top">🔼 Back to top</a>
31
- </p><br><br>
32
-
33
- <a name="installation"></a>
34
-
35
- ## 📥 Installation
36
-
37
- You can install **Clock Pattern** using `pip`:
38
-
39
- ```bash
40
- pip install clock-pattern
41
- ```
42
-
43
- <p align="right">
44
- <a href="#readme-top">🔼 Back to top</a>
45
- </p><br><br>
46
-
47
- <a name="contributing"></a>
48
-
49
- ## 🤝 Contributing
50
-
51
- We love community help! Before you open an issue or pull request, please read:
52
-
53
- - [`🤝 How to Contribute`](https://github.com/adriamontoto/clock-pattern/blob/master/.github/CONTRIBUTING.md)
54
- - [`🧭 Code of Conduct`](https://github.com/adriamontoto/clock-pattern/blob/master/.github/CODE_OF_CONDUCT.md)
55
- - [`🔐 Security Policy`](https://github.com/adriamontoto/clock-pattern/blob/master/.github/SECURITY.md)
56
-
57
- _Thank you for helping make **🕰️ Clock Pattern** package awesome! 🌟_
58
-
59
- <p align="right">
60
- <a href="#readme-top">🔼 Back to top</a>
61
- </p><br><br>
62
-
63
- <a name="license"></a>
64
-
65
- ## 🔑 License
66
-
67
- This project is licensed under the terms of the [`MIT license`](https://github.com/adriamontoto/clock-pattern/blob/master/LICENSE.md).
68
-
69
- <p align="right">
70
- <a href="#readme-top">🔼 Back to top</a>
71
- </p>
@@ -1 +0,0 @@
1
- __version__ = '0.3.0'
@@ -1,3 +0,0 @@
1
- from .fixed_clock import FixedClock
2
-
3
- __all__ = ('FixedClock',)
File without changes
File without changes