├── .coveragerc ├── .github └── workflows │ ├── ci.yaml │ └── release.yml ├── .gitignore ├── AUTHORS.rst ├── CHANGELOG ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── SECURITY.md ├── create_tag.py ├── freezegun ├── __init__.py ├── _async.py ├── api.py ├── config.py └── py.typed ├── pyproject.toml ├── requirements.txt ├── setup.cfg ├── setup.py ├── tests ├── __init__.py ├── another_module.py ├── fake_module.py ├── test_asyncio.py ├── test_class_import.py ├── test_configure.py ├── test_datetimes.py ├── test_errors.py ├── test_import_alias.py ├── test_operations.py ├── test_pickle.py ├── test_sqlite3.py ├── test_ticking.py ├── test_utils.py ├── test_uuid.py ├── test_warnings.py └── utils.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | include = freezegun/* 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | - pull_request 5 | - push 6 | 7 | jobs: 8 | mypy: 9 | name: mypy 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | python-version: 14 | - '3.8' 15 | - '3.9' 16 | - '3.10' 17 | - '3.11' 18 | - '3.12' 19 | - '3.13' 20 | 21 | steps: 22 | - uses: actions/checkout@master 23 | 24 | - name: Set up Python 25 | uses: actions/setup-python@v4 26 | with: 27 | python-version: ${{ matrix.python-version }} 28 | 29 | - name: Install dependencies 30 | run: | 31 | pip install mypy 32 | pip install -r requirements.txt 33 | 34 | - name: Run MyPy 35 | run: mypy --install-types --non-interactive 36 | 37 | tests: 38 | name: Python ${{ matrix.implementation }}${{ matrix.python-version }} 39 | runs-on: ubuntu-latest 40 | strategy: 41 | matrix: 42 | python-version: 43 | - '3.8' 44 | - '3.9' 45 | - '3.10' 46 | - '3.11' 47 | - '3.12' 48 | - '3.13' 49 | implementation: 50 | - '' # CPython 51 | - 'pypy' # PyPy 52 | exclude: # unreleased; 53 | - implementation: 'pypy' 54 | python-version: '3.11' 55 | - implementation: 'pypy' 56 | python-version: '3.12' 57 | - implementation: 'pypy' 58 | python-version: '3.13' 59 | 60 | steps: 61 | - uses: actions/checkout@master 62 | 63 | - name: Set up Python ${{ matrix.implementation }}${{ matrix.python-version }} 64 | uses: actions/setup-python@v4 65 | with: 66 | python-version: ${{ matrix.implementation }}${{ matrix.python-version }} 67 | 68 | - name: Install tox 69 | run: pip install tox 70 | 71 | - name: Run tests 72 | run: pip install python-dateutil && tox -e py 73 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: 'Version' 8 | required: true 9 | 10 | jobs: 11 | release-freezegun: 12 | runs-on: ubuntu-latest 13 | name: Release FreezeGun 14 | permissions: 15 | contents: write 16 | id-token: write 17 | env: 18 | VERSION: 0.0.0 19 | steps: 20 | - name: Set Env 21 | run: | 22 | echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV 23 | - uses: actions/checkout@v4 24 | with: 25 | fetch-depth: 0 26 | - name: Set up Python 3.10 27 | uses: actions/setup-python@v4 28 | with: 29 | python-version: "3.10" 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install --upgrade pip 33 | pip install wheel setuptools packaging twine build --upgrade 34 | - name: Verify Tag does not exist 35 | run: | 36 | ! git rev-parse ${{ env.VERSION }} || { echo "Ensure that no tag exists for ${{ env.VERSION }}" ; exit 1; } 37 | - name: Set version number 38 | run: sed -i "s/__version__ =.*/__version__ = '${{ env.VERSION }}'/g" freezegun/__init__.py 39 | - name: Increase patch version number 40 | run: | 41 | git config --local user.email "6058517+bblommers@users.noreply.github.com" 42 | git config --local user.name "FreezeGun Admin" 43 | git add freezegun/__init__.py 44 | git commit -m "Increase version number" || echo "Nothing to commit" 45 | git push || echo "Nothing to push" 46 | - name: Build Python 47 | run: python -m build 48 | - name: Publish to PyPI 49 | uses: pypa/gh-action-pypi-publish@release/v1 50 | - name: Tag version on Github 51 | run: | 52 | git tag ${{ env.VERSION }} 53 | git push origin ${{ env.VERSION }} 54 | - name: Create GitHub release 55 | uses: softprops/action-gh-release@v1 56 | with: 57 | name: ${{ env.VERSION }} 58 | tag_name: ${{ env.VERSION }} 59 | files: dist/* 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .coverage 3 | *.egg-info 4 | *.tox 5 | dist 6 | *.pyc 7 | venv/ 8 | .idea 9 | htmlcov/ 10 | .DS_Store 11 | -------------------------------------------------------------------------------- /AUTHORS.rst: -------------------------------------------------------------------------------- 1 | Patches and Suggestions 2 | ``````````````````````` 3 | 4 | - `Dan Miller `_ 5 | - `Matthew Schinckel `_ 6 | - `JJ Geewax `_ 7 | - `Roman Imankulov `_ 8 | - `Martin Geisler `_ 9 | - `Richard Eames `_ 10 | - `Tye Wang `_ 11 | - `Andreas Pelme `_ 12 | - `Jesse London `_ 13 | - `Zach Smith `_ 14 | - `Adam Johnson `_ 15 | - `Alex Ehlke `_ 16 | - `James Lu `_ 17 | - `Dan Elkis `_ 18 | - `Bastien Vallet `_ 19 | - `Julian Mehnle `_ 20 | - `Lukasz Balcerzak `_ 21 | - `Hannes Ljungberg `_ 22 | - `staticdev `_ 23 | - `Marcin Sulikowski `_ 24 | - `Ashish Patil `_ 25 | - `Victor Ferrer `_ 26 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Freezegun Changelog 2 | =================== 3 | 4 | 1.5.2 5 | ----- 6 | * Remove support for Python 3.7 7 | * Explicitly marks support for Python 3.13 8 | * Improved project documentation 9 | 10 | 1.5.1 11 | ----- 12 | * Fix the typing of the `tick()` method, and improve it's behaviour. 13 | 14 | 1.5.0 15 | ---- 16 | * The default ignore list now contains the `queue`-package 17 | * Added a missing `move_to`-function when calling `freeze_time(tick=True)` 18 | * Fixes a rounding error in `time.time_ns()` 19 | * Fixed a bug where the default ignore list could not be empty (`configure(default_ignore_list=[])`) 20 | * All `tick()` methods now return the new datetime (instead of None) 21 | * Type improvements 22 | 23 | 1.4.0 24 | ----- 25 | * `asyncio`-support from 1.3.x introduced quite a few bugs, so that functionality is now hidden behind a flag: 26 | `with freeze_time('1970-01-02', real_asyncio=True):` 27 | * Added documentation for the `real_asyncio` parameter in the `README.rst` file. 28 | 29 | 1.3.1 30 | ----- 31 | * Fixed the release number in the build 32 | 33 | 1.3.0 34 | ----- 35 | 36 | * Fixed `asyncio` support to avoid `await asyncio.sleep(1)` hanging forever. 37 | 38 | * Added support for Python 3.12 39 | 40 | 41 | 1.2.2 42 | ----- 43 | 44 | * Removes Python 3.6 support, which reached its EOL on 2021-12-23 (https://devguide.python.org/versions/?highlight=End-of-life#unsupported-versions). 45 | 46 | * Improved static typing definitions 47 | 48 | * Don't freeze pytest timings. This avoids class-level decorator usage messing with pytest timings. 49 | 50 | * Pass through all setUp and tearDown arguments 51 | 52 | 53 | 1.2.1 54 | ----- 55 | 56 | * Added missing typeshed types from distribution 57 | 58 | * Pass all arguments on recursive freeze_time calls 59 | 60 | 61 | 1.2.0 62 | ----- 63 | 64 | * Add support for `time.perf_counter` (and `…_ns`) 65 | 66 | * Added typeshed types 67 | 68 | * Dropped support for python 3.5 69 | 70 | 1.1.0 71 | ----- 72 | 73 | * Add support for `time.monotonic` (and `…_ns`) 74 | 75 | * Allow to configure default ignore list, and also to just extend the default 76 | 77 | * Fixed when accessing from thread after stop() 78 | 79 | 80 | 1.0.0 81 | ------ 82 | 83 | * Dropped Py2 support 84 | * Added as_kwarg argument in order to have the frozen time object passed with the name provided in as_kwarg argument 85 | 86 | 0.3.15 87 | ------ 88 | 89 | * Fix locale timestamp bug. CC #328 90 | 91 | 0.3.14 92 | ------ 93 | 94 | * Fix calendar.timegm behavior 95 | 96 | 0.3.13 97 | ------ 98 | 99 | * Fix for Py3.8 100 | * Reset time.time_ns on stop 101 | 102 | 0.3.12 103 | ------ 104 | 105 | * Refactor classes to functions 106 | * Ignore Selenium 107 | * Move to pytest 108 | * Conditionally patch time.clock 109 | * Patch time.time_ns added in Python 3.7 110 | 111 | 0.3.11 112 | ------ 113 | 114 | * Performance improvements 115 | * Fix nesting time.time 116 | * Add nanosecond property 117 | 118 | 0.3.10 119 | ------ 120 | 121 | * Performance improvements 122 | * Coroutine support 123 | 124 | 0.3.9 125 | ----- 126 | 127 | * If no time to be frozen, use current time 128 | * Fix uuid1 issues 129 | * Add support for python 3.6 130 | 131 | 0.3.8 132 | ----- 133 | 134 | * Bugfix for old-style classes 135 | * Ignore warnings when patching 136 | * Add `move_to` method to change time 137 | 138 | 0.3.7 139 | ----- 140 | 141 | * Fix CPython detection 142 | 143 | 0.3.6 144 | ----- 145 | 146 | * Catch TypeError when fetching attribute values 147 | * Speed improvements 148 | * Add manual tick increment 149 | 150 | 151 | 0.3.5 152 | ----- 153 | 154 | * Add `tick` argument to allow time to move forward 155 | * Performance improvements 156 | * Fix timezone example in README 157 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2012 Steve Pulec 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst LICENSE AUTHORS.rst CHANGELOG pyproject.toml 2 | recursive-include tests * 3 | include requirements.txt tox.ini 4 | include freezegun/py.typed 5 | recursive-include freezegun *.pyi 6 | global-exclude __pycache__ 7 | global-exclude *.py[co] 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := /bin/bash 2 | 3 | init: 4 | python setup.py develop 5 | pip install -r requirements.txt 6 | 7 | test: 8 | rm -f .coverage 9 | pytest 10 | 11 | tag: 12 | python create_tag.py 13 | 14 | publish: 15 | rm -rf dist 16 | python -m build 17 | twine upload dist/* 18 | 19 | venv: 20 | virtualenv venv 21 | venv/bin/pip install -r requirements.txt 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | FreezeGun: Let your Python tests travel through time 2 | ==================================================== 3 | 4 | .. image:: https://img.shields.io/pypi/v/freezegun.svg 5 | :target: https://pypi.python.org/pypi/freezegun/ 6 | .. image:: https://github.com/spulec/freezegun/workflows/CI/badge.svg 7 | :target: https://github.com/spulec/freezegun/actions 8 | .. image:: https://coveralls.io/repos/spulec/freezegun/badge.svg?branch=master 9 | :target: https://coveralls.io/r/spulec/freezegun 10 | 11 | FreezeGun is a library that allows your Python tests to travel through time by mocking the datetime module. 12 | 13 | Usage 14 | ----- 15 | 16 | Once the decorator or context manager have been invoked, all calls to datetime.datetime.now(), datetime.datetime.utcnow(), datetime.date.today(), time.time(), time.localtime(), time.gmtime(), and time.strftime() will return the time that has been frozen. time.monotonic() and time.perf_counter() will also be frozen, but as usual it makes no guarantees about their absolute value, only their changes over time. 17 | 18 | Decorator 19 | ~~~~~~~~~ 20 | 21 | .. code-block:: python 22 | 23 | from freezegun import freeze_time 24 | import datetime 25 | import unittest 26 | 27 | # Freeze time for a pytest style test: 28 | 29 | @freeze_time("2012-01-14") 30 | def test(): 31 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 32 | 33 | # Or a unittest TestCase - freezes for every test, and set up and tear down code 34 | 35 | @freeze_time("1955-11-12") 36 | class MyTests(unittest.TestCase): 37 | def test_the_class(self): 38 | assert datetime.datetime.now() == datetime.datetime(1955, 11, 12) 39 | 40 | # Or any other class - freezes around each callable (may not work in every case) 41 | 42 | @freeze_time("2012-01-14") 43 | class Tester(object): 44 | def test_the_class(self): 45 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 46 | 47 | # Or method decorator, might also pass frozen time object as kwarg 48 | 49 | class TestUnitTestMethodDecorator(unittest.TestCase): 50 | @freeze_time('2013-04-09') 51 | def test_method_decorator_works_on_unittest(self): 52 | self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today()) 53 | 54 | @freeze_time('2013-04-09', as_kwarg='frozen_time') 55 | def test_method_decorator_works_on_unittest(self, frozen_time): 56 | self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today()) 57 | self.assertEqual(datetime.date(2013, 4, 9), frozen_time.time_to_freeze.date()) 58 | 59 | @freeze_time('2013-04-09', as_kwarg='hello') 60 | def test_method_decorator_works_on_unittest(self, **kwargs): 61 | self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today()) 62 | self.assertEqual(datetime.date(2013, 4, 9), kwargs.get('hello').time_to_freeze.date()) 63 | 64 | Context manager 65 | ~~~~~~~~~~~~~~~ 66 | 67 | .. code-block:: python 68 | 69 | from freezegun import freeze_time 70 | 71 | def test(): 72 | assert datetime.datetime.now() != datetime.datetime(2012, 1, 14) 73 | with freeze_time("2012-01-14"): 74 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 75 | assert datetime.datetime.now() != datetime.datetime(2012, 1, 14) 76 | 77 | Raw use 78 | ~~~~~~~ 79 | 80 | .. code-block:: python 81 | 82 | from freezegun import freeze_time 83 | 84 | freezer = freeze_time("2012-01-14 12:00:01") 85 | freezer.start() 86 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14, 12, 0, 1) 87 | freezer.stop() 88 | 89 | Timezones 90 | ~~~~~~~~~ 91 | 92 | .. code-block:: python 93 | 94 | from freezegun import freeze_time 95 | 96 | @freeze_time("2012-01-14 03:21:34", tz_offset=-4) 97 | def test(): 98 | assert datetime.datetime.utcnow() == datetime.datetime(2012, 1, 14, 3, 21, 34) 99 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 13, 23, 21, 34) 100 | 101 | # datetime.date.today() uses local time 102 | assert datetime.date.today() == datetime.date(2012, 1, 13) 103 | 104 | @freeze_time("2012-01-14 03:21:34", tz_offset=-datetime.timedelta(hours=3, minutes=30)) 105 | def test_timedelta_offset(): 106 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 13, 23, 51, 34) 107 | 108 | Nice inputs 109 | ~~~~~~~~~~~ 110 | 111 | FreezeGun uses dateutil behind the scenes so you can have nice-looking datetimes. 112 | 113 | .. code-block:: python 114 | 115 | @freeze_time("Jan 14th, 2012") 116 | def test_nice_datetime(): 117 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 118 | 119 | Function and generator objects 120 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 121 | 122 | FreezeGun is able to handle function and generator objects. 123 | 124 | .. code-block:: python 125 | 126 | def test_lambda(): 127 | with freeze_time(lambda: datetime.datetime(2012, 1, 14)): 128 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 129 | 130 | def test_generator(): 131 | datetimes = (datetime.datetime(year, 1, 1) for year in range(2010, 2012)) 132 | 133 | with freeze_time(datetimes): 134 | assert datetime.datetime.now() == datetime.datetime(2010, 1, 1) 135 | 136 | with freeze_time(datetimes): 137 | assert datetime.datetime.now() == datetime.datetime(2011, 1, 1) 138 | 139 | # The next call to freeze_time(datetimes) would raise a StopIteration exception. 140 | 141 | ``tick`` argument 142 | ~~~~~~~~~~~~~~~~~ 143 | 144 | FreezeGun has an additional ``tick`` argument which will restart time at the given 145 | value, but then time will keep ticking. This is an alternative to the default 146 | parameters which will keep time stopped. 147 | 148 | .. code-block:: python 149 | 150 | @freeze_time("Jan 14th, 2020", tick=True) 151 | def test_nice_datetime(): 152 | assert datetime.datetime.now() > datetime.datetime(2020, 1, 14) 153 | 154 | ``auto_tick_seconds`` argument 155 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 156 | 157 | FreezeGun has an additional ``auto_tick_seconds`` argument which will autoincrement the 158 | value every time by the given amount from the start value. This is an alternative to the default 159 | parameters which will keep time stopped. Note that given ``auto_tick_seconds`` the ``tick`` parameter will be ignored. 160 | 161 | .. code-block:: python 162 | 163 | @freeze_time("Jan 14th, 2020", auto_tick_seconds=15) 164 | def test_nice_datetime(): 165 | first_time = datetime.datetime.now() 166 | auto_incremented_time = datetime.datetime.now() 167 | assert first_time + datetime.timedelta(seconds=15) == auto_incremented_time 168 | 169 | 170 | Manual ticks 171 | ~~~~~~~~~~~~ 172 | 173 | FreezeGun allows for the time to be manually forwarded as well. 174 | 175 | .. code-block:: python 176 | 177 | def test_manual_tick(): 178 | initial_datetime = datetime.datetime(year=1, month=7, day=12, 179 | hour=15, minute=6, second=3) 180 | with freeze_time(initial_datetime) as frozen_datetime: 181 | assert frozen_datetime() == initial_datetime 182 | 183 | frozen_datetime.tick() 184 | initial_datetime += datetime.timedelta(seconds=1) 185 | assert frozen_datetime() == initial_datetime 186 | 187 | frozen_datetime.tick(delta=datetime.timedelta(seconds=10)) 188 | initial_datetime += datetime.timedelta(seconds=10) 189 | assert frozen_datetime() == initial_datetime 190 | 191 | .. code-block:: python 192 | 193 | def test_monotonic_manual_tick(): 194 | initial_datetime = datetime.datetime(year=1, month=7, day=12, 195 | hour=15, minute=6, second=3) 196 | with freeze_time(initial_datetime) as frozen_datetime: 197 | monotonic_t0 = time.monotonic() 198 | frozen_datetime.tick(1.0) 199 | monotonic_t1 = time.monotonic() 200 | assert monotonic_t1 == monotonic_t0 + 1.0 201 | 202 | 203 | Moving time to specify datetime 204 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 205 | 206 | FreezeGun allows moving time to specific dates. 207 | 208 | .. code-block:: python 209 | 210 | def test_move_to(): 211 | initial_datetime = datetime.datetime(year=1, month=7, day=12, 212 | hour=15, minute=6, second=3) 213 | 214 | other_datetime = datetime.datetime(year=2, month=8, day=13, 215 | hour=14, minute=5, second=0) 216 | with freeze_time(initial_datetime) as frozen_datetime: 217 | assert frozen_datetime() == initial_datetime 218 | 219 | frozen_datetime.move_to(other_datetime) 220 | assert frozen_datetime() == other_datetime 221 | 222 | frozen_datetime.move_to(initial_datetime) 223 | assert frozen_datetime() == initial_datetime 224 | 225 | 226 | @freeze_time("2012-01-14", as_arg=True) 227 | def test(frozen_time): 228 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 229 | frozen_time.move_to("2014-02-12") 230 | assert datetime.datetime.now() == datetime.datetime(2014, 2, 12) 231 | 232 | Parameter for ``move_to`` can be any valid ``freeze_time`` date (string, date, datetime). 233 | 234 | ``real_asyncio`` parameter 235 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 236 | 237 | FreezeGun has an additional ``real_asyncio`` parameter which allows asyncio event loops to see real monotonic time even though time.monotonic() is frozen. This is useful to avoid breaking asyncio.sleep() and other asyncio functions that rely on monotonic time. 238 | 239 | .. code-block:: python 240 | 241 | @freeze_time("2012-01-14", real_asyncio=True) 242 | async def test_asyncio(): 243 | await asyncio.sleep(1) 244 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 245 | 246 | API Documentation 247 | ~~~~~~~~~~~~~~~~~ 248 | 249 | Here is a succinct API documentation with all options listed: 250 | 251 | .. code-block:: python 252 | 253 | freeze_time(time_to_freeze: Optional[_Freezable]=None, tz_offset: Union[int, datetime.timedelta]=0, ignore: Optional[List[str]]=None, tick: bool=False, as_arg: bool=False, as_kwarg: str='', auto_tick_seconds: float=0, real_asyncio: bool=False) -> _freeze_time 254 | 255 | _freeze_time(time_to_freeze_str: Optional[_Freezable], tz_offset: Union[int, datetime.timedelta], ignore: List[str], tick: bool, as_arg: bool, as_kwarg: str, auto_tick_seconds: float, real_asyncio: Optional[bool]) 256 | 257 | _freeze_time.start() -> Union[StepTickTimeFactory, TickingDateTimeFactory, FrozenDateTimeFactory] 258 | 259 | _freeze_time.stop() -> None 260 | 261 | _freeze_time.move_to(target_datetime: _Freezable) -> None 262 | 263 | _freeze_time.tick(delta: Union[datetime.timedelta, float]=datetime.timedelta(seconds=1)) -> datetime.datetime 264 | 265 | _freeze_time.decorate_class(klass: Type[T2]) -> Type[T2] 266 | 267 | _freeze_time.decorate_coroutine(coroutine: Callable[P, Awaitable[T]]) -> Callable[P, Awaitable[T]] 268 | 269 | _freeze_time.decorate_callable(func: Callable[P, T]) -> Callable[P, T] 270 | 271 | _freeze_time.__enter__() -> Union[StepTickTimeFactory, TickingDateTimeFactory, FrozenDateTimeFactory] 272 | 273 | _freeze_time.__exit__(*args: Any) -> None 274 | 275 | Default arguments 276 | ~~~~~~~~~~~~~~~~~ 277 | 278 | Note that FreezeGun will not modify default arguments. The following code will 279 | print the current date. See `here `_ for why. 280 | 281 | .. code-block:: python 282 | 283 | from freezegun import freeze_time 284 | import datetime as dt 285 | 286 | def test(default=dt.date.today()): 287 | print(default) 288 | 289 | with freeze_time('2000-1-1'): 290 | test() 291 | 292 | 293 | Installation 294 | ------------ 295 | 296 | To install FreezeGun, simply: 297 | 298 | .. code-block:: bash 299 | 300 | $ pip install freezegun 301 | 302 | On Debian systems: 303 | 304 | .. code-block:: bash 305 | 306 | $ sudo apt-get install python-freezegun 307 | 308 | 309 | Ignore packages 310 | --------------- 311 | 312 | Sometimes it's desired to ignore FreezeGun behaviour for particular packages (i.e. libraries). 313 | It's possible to ignore them for a single invocation: 314 | 315 | 316 | .. code-block:: python 317 | 318 | from freezegun import freeze_time 319 | 320 | with freeze_time('2020-10-06', ignore=['threading']): 321 | # ... 322 | 323 | 324 | By default FreezeGun ignores following packages: 325 | 326 | .. code-block:: python 327 | 328 | [ 329 | 'nose.plugins', 330 | 'six.moves', 331 | 'django.utils.six.moves', 332 | 'google.gax', 333 | 'threading', 334 | 'Queue', 335 | 'selenium', 336 | '_pytest.terminal.', 337 | '_pytest.runner.', 338 | 'gi', 339 | ] 340 | 341 | 342 | It's possible to set your own default ignore list: 343 | 344 | .. code-block:: python 345 | 346 | import freezegun 347 | 348 | freezegun.configure(default_ignore_list=['threading', 'tensorflow']) 349 | 350 | 351 | Please note this will override default ignore list. If you want to extend existing defaults 352 | please use: 353 | 354 | .. code-block:: python 355 | 356 | import freezegun 357 | 358 | freezegun.configure(extend_ignore_list=['tensorflow']) 359 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security contact information 2 | 3 | To report a security vulnerability, please use the 4 | [Tidelift security contact](https://tidelift.com/security). 5 | Tidelift will coordinate the fix and disclosure. 6 | -------------------------------------------------------------------------------- /create_tag.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | 5 | def read_version(): 6 | with open(os.path.join('freezegun', '__init__.py')) as f: 7 | m = re.search(r'''__version__\s*=\s*['"]([^'"]*)['"]''', f.read()) 8 | if m: 9 | return m.group(1) 10 | raise ValueError("couldn't find version") 11 | 12 | 13 | def create_tag(): 14 | from subprocess import call 15 | version = read_version() 16 | errno = call(['git', 'tag', '--annotate', version, '--message', 'Version %s' % version]) 17 | if errno == 0: 18 | print("Added tag for version %s" % version) 19 | 20 | 21 | if __name__ == '__main__': 22 | create_tag() 23 | -------------------------------------------------------------------------------- /freezegun/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | freezegun 3 | ~~~~~~~~ 4 | 5 | :copyright: (c) 2012 by Steve Pulec. 6 | 7 | """ 8 | from .api import freeze_time 9 | from .config import configure 10 | 11 | __title__ = 'freezegun' 12 | __version__ = '1.5.2' 13 | __author__ = 'Steve Pulec' 14 | __license__ = 'Apache License 2.0' 15 | __copyright__ = 'Copyright 2012 Steve Pulec' 16 | 17 | 18 | __all__ = ["freeze_time", "configure"] 19 | -------------------------------------------------------------------------------- /freezegun/_async.py: -------------------------------------------------------------------------------- 1 | import functools 2 | from typing import Any, Callable, TypeVar, cast 3 | 4 | 5 | _CallableT = TypeVar("_CallableT", bound=Callable[..., Any]) 6 | 7 | 8 | def wrap_coroutine(api: Any, coroutine: _CallableT) -> _CallableT: 9 | @functools.wraps(coroutine) 10 | async def wrapper(*args: Any, **kwargs: Any) -> Any: 11 | with api as time_factory: 12 | if api.as_arg: 13 | result = await coroutine(time_factory, *args, **kwargs) 14 | else: 15 | result = await coroutine(*args, **kwargs) 16 | return result 17 | 18 | return cast(_CallableT, wrapper) 19 | -------------------------------------------------------------------------------- /freezegun/api.py: -------------------------------------------------------------------------------- 1 | from . import config 2 | from ._async import wrap_coroutine 3 | import asyncio 4 | import copyreg 5 | import dateutil 6 | import datetime 7 | import functools 8 | import sys 9 | import time 10 | import uuid 11 | import calendar 12 | import unittest 13 | import platform 14 | import warnings 15 | import types 16 | import numbers 17 | import inspect 18 | from typing import TYPE_CHECKING, overload 19 | from typing import Any, Awaitable, Callable, Dict, Iterator, List, Optional, Set, Type, TypeVar, Tuple, Union 20 | 21 | from dateutil import parser 22 | from dateutil.tz import tzlocal 23 | 24 | try: 25 | from maya import MayaDT # type: ignore 26 | except ImportError: 27 | MayaDT = None 28 | 29 | if TYPE_CHECKING: 30 | from typing_extensions import ParamSpec 31 | 32 | P = ParamSpec("P") 33 | 34 | T = TypeVar("T") 35 | 36 | _TIME_NS_PRESENT = hasattr(time, 'time_ns') 37 | _MONOTONIC_NS_PRESENT = hasattr(time, 'monotonic_ns') 38 | _PERF_COUNTER_NS_PRESENT = hasattr(time, 'perf_counter_ns') 39 | _EPOCH = datetime.datetime(1970, 1, 1) 40 | _EPOCHTZ = datetime.datetime(1970, 1, 1, tzinfo=dateutil.tz.UTC) 41 | 42 | T2 = TypeVar("T2") 43 | _Freezable = Union[str, datetime.datetime, datetime.date, datetime.timedelta, types.FunctionType, Callable[[], Union[str, datetime.datetime, datetime.date, datetime.timedelta]], Iterator[datetime.datetime]] 44 | 45 | real_time = time.time 46 | real_localtime = time.localtime 47 | real_gmtime = time.gmtime 48 | real_monotonic = time.monotonic 49 | real_perf_counter = time.perf_counter 50 | real_strftime = time.strftime 51 | real_date = datetime.date 52 | real_datetime = datetime.datetime 53 | real_date_objects = [real_time, real_localtime, real_gmtime, real_monotonic, real_perf_counter, real_strftime, real_date, real_datetime] 54 | 55 | if _TIME_NS_PRESENT: 56 | real_time_ns = time.time_ns 57 | real_date_objects.append(real_time_ns) 58 | 59 | if _MONOTONIC_NS_PRESENT: 60 | real_monotonic_ns = time.monotonic_ns 61 | real_date_objects.append(real_monotonic_ns) 62 | 63 | if _PERF_COUNTER_NS_PRESENT: 64 | real_perf_counter_ns = time.perf_counter_ns 65 | real_date_objects.append(real_perf_counter_ns) 66 | 67 | _real_time_object_ids = {id(obj) for obj in real_date_objects} 68 | 69 | # time.clock is deprecated and was removed in Python 3.8 70 | real_clock = getattr(time, 'clock', None) 71 | 72 | freeze_factories: List[Union["StepTickTimeFactory", "TickingDateTimeFactory", "FrozenDateTimeFactory"]] = [] 73 | tz_offsets: List[datetime.timedelta] = [] 74 | ignore_lists: List[Tuple[str, ...]] = [] 75 | tick_flags: List[bool] = [] 76 | 77 | try: 78 | # noinspection PyUnresolvedReferences 79 | real_uuid_generate_time = uuid._uuid_generate_time # type: ignore 80 | uuid_generate_time_attr = '_uuid_generate_time' 81 | except AttributeError: 82 | # noinspection PyUnresolvedReferences 83 | if hasattr(uuid, '_load_system_functions'): 84 | # A no-op after Python ~3.9, being removed in 3.13. 85 | uuid._load_system_functions() 86 | # noinspection PyUnresolvedReferences 87 | real_uuid_generate_time = uuid._generate_time_safe # type: ignore 88 | uuid_generate_time_attr = '_generate_time_safe' 89 | except ImportError: 90 | real_uuid_generate_time = None 91 | uuid_generate_time_attr = None # type: ignore 92 | 93 | try: 94 | # noinspection PyUnresolvedReferences 95 | real_uuid_create = uuid._UuidCreate # type: ignore 96 | except (AttributeError, ImportError): 97 | real_uuid_create = None 98 | 99 | 100 | # keep a cache of module attributes otherwise freezegun will need to analyze too many modules all the time 101 | _GLOBAL_MODULES_CACHE: Dict[str, Tuple[str, List[Tuple[str, Any]]]] = {} 102 | 103 | 104 | def _get_module_attributes(module: types.ModuleType) -> List[Tuple[str, Any]]: 105 | result: List[Tuple[str, Any]] = [] 106 | try: 107 | module_attributes = dir(module) 108 | except (ImportError, TypeError): 109 | return result 110 | for attribute_name in module_attributes: 111 | try: 112 | attribute_value = getattr(module, attribute_name) 113 | except (ImportError, AttributeError, TypeError): 114 | # For certain libraries, this can result in ImportError(_winreg) or AttributeError (celery) 115 | continue 116 | else: 117 | result.append((attribute_name, attribute_value)) 118 | return result 119 | 120 | 121 | def _setup_module_cache(module: types.ModuleType) -> None: 122 | date_attrs = [] 123 | all_module_attributes = _get_module_attributes(module) 124 | for attribute_name, attribute_value in all_module_attributes: 125 | if id(attribute_value) in _real_time_object_ids: 126 | date_attrs.append((attribute_name, attribute_value)) 127 | _GLOBAL_MODULES_CACHE[module.__name__] = (_get_module_attributes_hash(module), date_attrs) 128 | 129 | 130 | def _get_module_attributes_hash(module: types.ModuleType) -> str: 131 | try: 132 | module_dir = dir(module) 133 | except (ImportError, TypeError): 134 | module_dir = [] 135 | return f'{id(module)}-{hash(frozenset(module_dir))}' 136 | 137 | 138 | def _get_cached_module_attributes(module: types.ModuleType) -> List[Tuple[str, Any]]: 139 | module_hash, cached_attrs = _GLOBAL_MODULES_CACHE.get(module.__name__, ('0', [])) 140 | if _get_module_attributes_hash(module) == module_hash: 141 | return cached_attrs 142 | 143 | # cache miss: update the cache and return the refreshed value 144 | _setup_module_cache(module) 145 | # return the newly cached value 146 | module_hash, cached_attrs = _GLOBAL_MODULES_CACHE[module.__name__] 147 | return cached_attrs 148 | 149 | 150 | _is_cpython = ( 151 | hasattr(platform, 'python_implementation') and 152 | platform.python_implementation().lower() == "cpython" 153 | ) 154 | 155 | 156 | call_stack_inspection_limit = 5 157 | 158 | 159 | def _should_use_real_time() -> bool: 160 | if not call_stack_inspection_limit: 161 | return False 162 | 163 | # Means stop() has already been called, so we can now return the real time 164 | if not ignore_lists: 165 | return True 166 | 167 | if not ignore_lists[-1]: 168 | return False 169 | 170 | frame = inspect.currentframe().f_back.f_back # type: ignore 171 | 172 | for _ in range(call_stack_inspection_limit): 173 | module_name = frame.f_globals.get('__name__') # type: ignore 174 | if module_name and module_name.startswith(ignore_lists[-1]): 175 | return True 176 | 177 | frame = frame.f_back # type: ignore 178 | if frame is None: 179 | break 180 | 181 | return False 182 | 183 | 184 | def get_current_time() -> datetime.datetime: 185 | return freeze_factories[-1]() 186 | 187 | 188 | def fake_time() -> float: 189 | if _should_use_real_time(): 190 | return real_time() 191 | current_time = get_current_time() 192 | return calendar.timegm(current_time.timetuple()) + current_time.microsecond / 1000000.0 193 | 194 | if _TIME_NS_PRESENT: 195 | def fake_time_ns() -> int: 196 | if _should_use_real_time(): 197 | return real_time_ns() 198 | return int(fake_time() * 1e9) 199 | 200 | 201 | def fake_localtime(t: Optional[float]=None) -> time.struct_time: 202 | if t is not None: 203 | return real_localtime(t) 204 | if _should_use_real_time(): 205 | return real_localtime() 206 | shifted_time = get_current_time() - datetime.timedelta(seconds=time.timezone) 207 | return shifted_time.timetuple() 208 | 209 | 210 | def fake_gmtime(t: Optional[float]=None) -> time.struct_time: 211 | if t is not None: 212 | return real_gmtime(t) 213 | if _should_use_real_time(): 214 | return real_gmtime() 215 | return get_current_time().timetuple() 216 | 217 | 218 | def _get_fake_monotonic() -> float: 219 | # For monotonic timers like .monotonic(), .perf_counter(), etc 220 | current_time = get_current_time() 221 | return ( 222 | calendar.timegm(current_time.timetuple()) + 223 | current_time.microsecond / 1e6 224 | ) 225 | 226 | 227 | def _get_fake_monotonic_ns() -> int: 228 | # For monotonic timers like .monotonic(), .perf_counter(), etc 229 | current_time = get_current_time() 230 | return ( 231 | calendar.timegm(current_time.timetuple()) * 1000000 + 232 | current_time.microsecond 233 | ) * 1000 234 | 235 | 236 | def fake_monotonic() -> float: 237 | if _should_use_real_time(): 238 | return real_monotonic() 239 | 240 | return _get_fake_monotonic() 241 | 242 | 243 | def fake_perf_counter() -> float: 244 | if _should_use_real_time(): 245 | return real_perf_counter() 246 | 247 | return _get_fake_monotonic() 248 | 249 | 250 | if _MONOTONIC_NS_PRESENT: 251 | def fake_monotonic_ns() -> int: 252 | if _should_use_real_time(): 253 | return real_monotonic_ns() 254 | 255 | return _get_fake_monotonic_ns() 256 | 257 | 258 | if _PERF_COUNTER_NS_PRESENT: 259 | def fake_perf_counter_ns() -> int: 260 | if _should_use_real_time(): 261 | return real_perf_counter_ns() 262 | return _get_fake_monotonic_ns() 263 | 264 | 265 | def fake_strftime(format: Any, time_to_format: Any=None) -> str: 266 | if time_to_format is None: 267 | if not _should_use_real_time(): 268 | time_to_format = fake_localtime() 269 | 270 | if time_to_format is None: 271 | return real_strftime(format) 272 | else: 273 | return real_strftime(format, time_to_format) 274 | 275 | if real_clock is not None: 276 | def fake_clock() -> Any: 277 | if _should_use_real_time(): 278 | return real_clock() # type: ignore 279 | 280 | if len(freeze_factories) == 1: 281 | return 0.0 if not tick_flags[-1] else real_clock() # type: ignore 282 | 283 | first_frozen_time = freeze_factories[0]() 284 | last_frozen_time = get_current_time() 285 | 286 | timedelta = (last_frozen_time - first_frozen_time) 287 | total_seconds = timedelta.total_seconds() 288 | 289 | if tick_flags[-1]: 290 | total_seconds += real_clock() # type: ignore 291 | 292 | return total_seconds 293 | 294 | 295 | class FakeDateMeta(type): 296 | @classmethod 297 | def __instancecheck__(self, obj: Any) -> bool: 298 | return isinstance(obj, real_date) 299 | 300 | @classmethod 301 | def __subclasscheck__(cls, subclass: Any) -> bool: 302 | return issubclass(subclass, real_date) 303 | 304 | 305 | def datetime_to_fakedatetime(datetime: datetime.datetime) -> "FakeDatetime": 306 | return FakeDatetime(datetime.year, 307 | datetime.month, 308 | datetime.day, 309 | datetime.hour, 310 | datetime.minute, 311 | datetime.second, 312 | datetime.microsecond, 313 | datetime.tzinfo) 314 | 315 | 316 | def date_to_fakedate(date: datetime.date) -> "FakeDate": 317 | return FakeDate(date.year, 318 | date.month, 319 | date.day) 320 | 321 | 322 | class FakeDate(real_date, metaclass=FakeDateMeta): 323 | def __add__(self, other: Any) -> "FakeDate": 324 | result = real_date.__add__(self, other) 325 | if result is NotImplemented: 326 | return result 327 | return date_to_fakedate(result) 328 | 329 | def __sub__(self, other: Any) -> "FakeDate": # type: ignore 330 | result = real_date.__sub__(self, other) 331 | if result is NotImplemented: 332 | return result # type: ignore 333 | if isinstance(result, real_date): 334 | return date_to_fakedate(result) 335 | else: 336 | return result # type: ignore 337 | 338 | @classmethod 339 | def today(cls: Type["FakeDate"]) -> "FakeDate": 340 | result = cls._date_to_freeze() + cls._tz_offset() 341 | return date_to_fakedate(result) 342 | 343 | @staticmethod 344 | def _date_to_freeze() -> datetime.datetime: 345 | return get_current_time() 346 | 347 | @classmethod 348 | def _tz_offset(cls) -> datetime.timedelta: 349 | return tz_offsets[-1] 350 | 351 | FakeDate.min = date_to_fakedate(real_date.min) 352 | FakeDate.max = date_to_fakedate(real_date.max) 353 | 354 | 355 | class FakeDatetimeMeta(FakeDateMeta): 356 | @classmethod 357 | def __instancecheck__(self, obj: Any) -> bool: 358 | return isinstance(obj, real_datetime) 359 | 360 | @classmethod 361 | def __subclasscheck__(cls, subclass: Any) -> bool: 362 | return issubclass(subclass, real_datetime) 363 | 364 | 365 | class FakeDatetime(real_datetime, FakeDate, metaclass=FakeDatetimeMeta): 366 | def __add__(self, other: Any) -> "FakeDatetime": # type: ignore 367 | result = real_datetime.__add__(self, other) 368 | if result is NotImplemented: 369 | return result 370 | return datetime_to_fakedatetime(result) 371 | 372 | def __sub__(self, other: Any) -> "FakeDatetime": # type: ignore 373 | result = real_datetime.__sub__(self, other) 374 | if result is NotImplemented: 375 | return result # type: ignore 376 | if isinstance(result, real_datetime): 377 | return datetime_to_fakedatetime(result) 378 | else: 379 | return result # type: ignore 380 | 381 | def astimezone(self, tz: Optional[datetime.tzinfo]=None) -> "FakeDatetime": 382 | if tz is None: 383 | tz = tzlocal() 384 | return datetime_to_fakedatetime(real_datetime.astimezone(self, tz)) 385 | 386 | @classmethod 387 | def fromtimestamp(cls, t: float, tz: Optional[datetime.tzinfo]=None) -> "FakeDatetime": 388 | if tz is None: 389 | tz = dateutil.tz.tzoffset("freezegun", cls._tz_offset()) 390 | result = real_datetime.fromtimestamp(t, tz=tz).replace(tzinfo=None) 391 | else: 392 | result = real_datetime.fromtimestamp(t, tz) 393 | return datetime_to_fakedatetime(result) 394 | 395 | def timestamp(self) -> float: 396 | if self.tzinfo is None: 397 | return (self - _EPOCH - self._tz_offset()).total_seconds() # type: ignore 398 | return (self - _EPOCHTZ).total_seconds() # type: ignore 399 | 400 | @classmethod 401 | def now(cls, tz: Optional[datetime.tzinfo] = None) -> "FakeDatetime": 402 | now = cls._time_to_freeze() or real_datetime.now() 403 | if tz: 404 | result = tz.fromutc(now.replace(tzinfo=tz)) + cls._tz_offset() 405 | else: 406 | result = now + cls._tz_offset() 407 | return datetime_to_fakedatetime(result) 408 | 409 | def date(self) -> "FakeDate": 410 | return date_to_fakedate(self) 411 | 412 | @property 413 | def nanosecond(self) -> int: 414 | try: 415 | # noinspection PyUnresolvedReferences 416 | return real_datetime.nanosecond # type: ignore 417 | except AttributeError: 418 | return 0 419 | 420 | @classmethod 421 | def today(cls) -> "FakeDatetime": 422 | return cls.now(tz=None) 423 | 424 | @classmethod 425 | def utcnow(cls) -> "FakeDatetime": 426 | result = cls._time_to_freeze() or real_datetime.now(datetime.timezone.utc) 427 | return datetime_to_fakedatetime(result) 428 | 429 | @staticmethod 430 | def _time_to_freeze() -> Optional[datetime.datetime]: 431 | if freeze_factories: 432 | return get_current_time() 433 | return None 434 | 435 | @classmethod 436 | def _tz_offset(cls) -> datetime.timedelta: 437 | return tz_offsets[-1] 438 | 439 | 440 | FakeDatetime.min = datetime_to_fakedatetime(real_datetime.min) 441 | FakeDatetime.max = datetime_to_fakedatetime(real_datetime.max) 442 | 443 | 444 | def convert_to_timezone_naive(time_to_freeze: datetime.datetime) -> datetime.datetime: 445 | """ 446 | Converts a potentially timezone-aware datetime to be a naive UTC datetime 447 | """ 448 | if time_to_freeze.tzinfo: 449 | time_to_freeze -= time_to_freeze.utcoffset() # type: ignore 450 | time_to_freeze = time_to_freeze.replace(tzinfo=None) 451 | return time_to_freeze 452 | 453 | 454 | def pickle_fake_date(datetime_: datetime.date) -> Tuple[Type[FakeDate], Tuple[int, int, int]]: 455 | # A pickle function for FakeDate 456 | return FakeDate, ( 457 | datetime_.year, 458 | datetime_.month, 459 | datetime_.day, 460 | ) 461 | 462 | 463 | def pickle_fake_datetime(datetime_: datetime.datetime) -> Tuple[Type[FakeDatetime], Tuple[int, int, int, int, int, int, int, Optional[datetime.tzinfo]]]: 464 | # A pickle function for FakeDatetime 465 | return FakeDatetime, ( 466 | datetime_.year, 467 | datetime_.month, 468 | datetime_.day, 469 | datetime_.hour, 470 | datetime_.minute, 471 | datetime_.second, 472 | datetime_.microsecond, 473 | datetime_.tzinfo, 474 | ) 475 | 476 | 477 | def _parse_time_to_freeze(time_to_freeze_str: Optional[_Freezable]) -> datetime.datetime: 478 | """Parses all the possible inputs for freeze_time 479 | :returns: a naive ``datetime.datetime`` object 480 | """ 481 | if time_to_freeze_str is None: 482 | time_to_freeze_str = datetime.datetime.now(datetime.timezone.utc) 483 | 484 | if isinstance(time_to_freeze_str, datetime.datetime): 485 | time_to_freeze = time_to_freeze_str 486 | elif isinstance(time_to_freeze_str, datetime.date): 487 | time_to_freeze = datetime.datetime.combine(time_to_freeze_str, datetime.time()) 488 | elif isinstance(time_to_freeze_str, datetime.timedelta): 489 | time_to_freeze = datetime.datetime.now(datetime.timezone.utc) + time_to_freeze_str 490 | else: 491 | time_to_freeze = parser.parse(time_to_freeze_str) # type: ignore 492 | 493 | return convert_to_timezone_naive(time_to_freeze) 494 | 495 | 496 | def _parse_tz_offset(tz_offset: Union[datetime.timedelta, float]) -> datetime.timedelta: 497 | if isinstance(tz_offset, datetime.timedelta): 498 | return tz_offset 499 | else: 500 | return datetime.timedelta(hours=tz_offset) 501 | 502 | 503 | class TickingDateTimeFactory: 504 | 505 | def __init__(self, time_to_freeze: datetime.datetime, start: datetime.datetime): 506 | self.time_to_freeze = time_to_freeze 507 | self.start = start 508 | 509 | def __call__(self) -> datetime.datetime: 510 | return self.time_to_freeze + (real_datetime.now() - self.start) 511 | 512 | def tick(self, delta: Union[datetime.timedelta, float]=datetime.timedelta(seconds=1)) -> datetime.datetime: 513 | if isinstance(delta, numbers.Integral): 514 | self.move_to(self.time_to_freeze + datetime.timedelta(seconds=int(delta))) 515 | elif isinstance(delta, numbers.Real): 516 | self.move_to(self.time_to_freeze + datetime.timedelta(seconds=float(delta))) 517 | else: 518 | self.move_to(self.time_to_freeze + delta) # type: ignore 519 | return self.time_to_freeze 520 | 521 | def move_to(self, target_datetime: _Freezable) -> None: 522 | """Moves frozen date to the given ``target_datetime``""" 523 | self.start = real_datetime.now() 524 | self.time_to_freeze = _parse_time_to_freeze(target_datetime) 525 | 526 | 527 | class FrozenDateTimeFactory: 528 | 529 | def __init__(self, time_to_freeze: datetime.datetime): 530 | self.time_to_freeze = time_to_freeze 531 | 532 | def __call__(self) -> datetime.datetime: 533 | return self.time_to_freeze 534 | 535 | def tick(self, delta: Union[datetime.timedelta, float]=datetime.timedelta(seconds=1)) -> datetime.datetime: 536 | if isinstance(delta, numbers.Integral): 537 | self.move_to(self.time_to_freeze + datetime.timedelta(seconds=int(delta))) 538 | elif isinstance(delta, numbers.Real): 539 | self.move_to(self.time_to_freeze + datetime.timedelta(seconds=float(delta))) 540 | else: 541 | self.time_to_freeze += delta # type: ignore 542 | return self.time_to_freeze 543 | 544 | def move_to(self, target_datetime: _Freezable) -> None: 545 | """Moves frozen date to the given ``target_datetime``""" 546 | target_datetime = _parse_time_to_freeze(target_datetime) 547 | delta = target_datetime - self.time_to_freeze 548 | self.tick(delta=delta) 549 | 550 | 551 | class StepTickTimeFactory: 552 | 553 | def __init__(self, time_to_freeze: datetime.datetime, step_width: float): 554 | self.time_to_freeze = time_to_freeze 555 | self.step_width = step_width 556 | 557 | def __call__(self) -> datetime.datetime: 558 | return_time = self.time_to_freeze 559 | self.tick() 560 | return return_time 561 | 562 | def tick(self, delta: Union[datetime.timedelta, float, None]=None) -> datetime.datetime: 563 | if not delta: 564 | delta = datetime.timedelta(seconds=self.step_width) 565 | elif isinstance(delta, numbers.Integral): 566 | delta = datetime.timedelta(seconds=int(delta)) 567 | elif isinstance(delta, numbers.Real): 568 | delta = datetime.timedelta(seconds=float(delta)) 569 | self.time_to_freeze += delta # type: ignore 570 | return self.time_to_freeze 571 | 572 | def update_step_width(self, step_width: float) -> None: 573 | self.step_width = step_width 574 | 575 | def move_to(self, target_datetime: _Freezable) -> None: 576 | """Moves frozen date to the given ``target_datetime``""" 577 | target_datetime = _parse_time_to_freeze(target_datetime) 578 | delta = target_datetime - self.time_to_freeze 579 | self.tick(delta=delta) 580 | 581 | 582 | class _freeze_time: 583 | """ 584 | A class to freeze time for testing purposes. 585 | 586 | This class can be used as a context manager or a decorator to freeze time 587 | during the execution of a block of code or a function. It provides various 588 | options to customize the behavior of the frozen time. 589 | 590 | Attributes: 591 | time_to_freeze (datetime.datetime): The datetime to freeze time at. 592 | tz_offset (datetime.timedelta): The timezone offset to apply to the frozen time. 593 | ignore (List[str]): A list of module names to ignore when freezing time. 594 | tick (bool): Whether to allow time to tick forward. 595 | auto_tick_seconds (float): The number of seconds to auto-tick the frozen time. 596 | undo_changes (List[Tuple[types.ModuleType, str, Any]]): A list of changes to undo when stopping the frozen time. 597 | modules_at_start (Set[str]): A set of module names that were loaded at the start of freezing time. 598 | as_arg (bool): Whether to pass the frozen time as an argument to the decorated function. 599 | as_kwarg (str): The name of the keyword argument to pass the frozen time to the decorated function. 600 | real_asyncio (Optional[bool]): Whether to allow asyncio event loops to see real monotonic time. 601 | 602 | Methods: 603 | __call__(func): Decorates a function or class to freeze time during its execution. 604 | decorate_class(klass): Decorates a class to freeze time during its execution. 605 | __enter__(): Starts freezing time and returns the time factory. 606 | __exit__(*args): Stops freezing time. 607 | start(): Starts freezing time and returns the time factory. 608 | stop(): Stops freezing time and restores the original time functions. 609 | decorate_coroutine(coroutine): Decorates a coroutine to freeze time during its execution. 610 | decorate_callable(func): Decorates a callable to freeze time during its execution. 611 | """ 612 | 613 | def __init__( 614 | self, 615 | time_to_freeze_str: Optional[_Freezable], 616 | tz_offset: Union[int, datetime.timedelta], 617 | ignore: List[str], 618 | tick: bool, 619 | as_arg: bool, 620 | as_kwarg: str, 621 | auto_tick_seconds: float, 622 | real_asyncio: Optional[bool], 623 | ): 624 | self.time_to_freeze = _parse_time_to_freeze(time_to_freeze_str) 625 | self.tz_offset = _parse_tz_offset(tz_offset) 626 | self.ignore = tuple(ignore) 627 | self.tick = tick 628 | self.auto_tick_seconds = auto_tick_seconds 629 | self.undo_changes: List[Tuple[types.ModuleType, str, Any]] = [] 630 | self.modules_at_start: Set[str] = set() 631 | self.as_arg = as_arg 632 | self.as_kwarg = as_kwarg 633 | self.real_asyncio = real_asyncio 634 | 635 | @overload 636 | def __call__(self, func: "Callable[P, Awaitable[Any]]") -> "Callable[P, Awaitable[Any]]": 637 | ... 638 | 639 | @overload 640 | def __call__(self, func: "Callable[P, T]") -> "Callable[P, T]": 641 | ... 642 | 643 | def __call__(self, func: Union[Type[T2], "Callable[P, Awaitable[Any]]", "Callable[P, T]"]) -> Union[Type[T2], "Callable[P, Awaitable[Any]]", "Callable[P, T]"]: # type: ignore 644 | if inspect.isclass(func): 645 | return self.decorate_class(func) 646 | elif inspect.iscoroutinefunction(func): 647 | return self.decorate_coroutine(func) 648 | return self.decorate_callable(func) # type: ignore 649 | 650 | def decorate_class(self, klass: Type[T2]) -> Type[T2]: 651 | if issubclass(klass, unittest.TestCase): 652 | # If it's a TestCase, we freeze time around setup and teardown, as well 653 | # as for every test case. This requires some care to avoid freezing 654 | # the time pytest sees, as otherwise this would distort the reported 655 | # timings. 656 | 657 | orig_setUpClass = klass.setUpClass 658 | orig_tearDownClass = klass.tearDownClass 659 | 660 | # noinspection PyDecorator 661 | @classmethod # type: ignore 662 | def setUpClass(cls: type) -> None: 663 | self.start() 664 | if orig_setUpClass is not None: 665 | orig_setUpClass() 666 | self.stop() 667 | 668 | # noinspection PyDecorator 669 | @classmethod # type: ignore 670 | def tearDownClass(cls: type) -> None: 671 | self.start() 672 | if orig_tearDownClass is not None: 673 | orig_tearDownClass() 674 | self.stop() 675 | 676 | klass.setUpClass = setUpClass # type: ignore 677 | klass.tearDownClass = tearDownClass # type: ignore 678 | 679 | orig_setUp = klass.setUp 680 | orig_tearDown = klass.tearDown 681 | 682 | def setUp(*args: Any, **kwargs: Any) -> None: 683 | self.start() 684 | if orig_setUp is not None: 685 | orig_setUp(*args, **kwargs) 686 | 687 | def tearDown(*args: Any, **kwargs: Any) -> None: 688 | if orig_tearDown is not None: 689 | orig_tearDown(*args, **kwargs) 690 | self.stop() 691 | 692 | klass.setUp = setUp # type: ignore[method-assign] 693 | klass.tearDown = tearDown # type: ignore[method-assign] 694 | 695 | else: 696 | 697 | seen = set() 698 | 699 | klasses = klass.mro() 700 | for base_klass in klasses: 701 | for (attr, attr_value) in base_klass.__dict__.items(): 702 | if attr.startswith('_') or attr in seen: 703 | continue 704 | seen.add(attr) 705 | 706 | if not callable(attr_value) or inspect.isclass(attr_value) or isinstance(attr_value, staticmethod): 707 | continue 708 | 709 | try: 710 | setattr(klass, attr, self(attr_value)) 711 | except (AttributeError, TypeError): 712 | # Sometimes we can't set this for built-in types and custom callables 713 | continue 714 | return klass 715 | 716 | def __enter__(self) -> Union[StepTickTimeFactory, TickingDateTimeFactory, FrozenDateTimeFactory]: 717 | return self.start() 718 | 719 | def __exit__(self, *args: Any) -> None: 720 | self.stop() 721 | 722 | def start(self) -> Union[StepTickTimeFactory, TickingDateTimeFactory, FrozenDateTimeFactory]: 723 | 724 | if self.auto_tick_seconds: 725 | freeze_factory: Union[StepTickTimeFactory, TickingDateTimeFactory, FrozenDateTimeFactory] = StepTickTimeFactory(self.time_to_freeze, self.auto_tick_seconds) 726 | elif self.tick: 727 | freeze_factory = TickingDateTimeFactory(self.time_to_freeze, real_datetime.now()) 728 | else: 729 | freeze_factory = FrozenDateTimeFactory(self.time_to_freeze) 730 | 731 | is_already_started = len(freeze_factories) > 0 732 | freeze_factories.append(freeze_factory) 733 | tz_offsets.append(self.tz_offset) 734 | ignore_lists.append(self.ignore) 735 | tick_flags.append(self.tick) 736 | 737 | if is_already_started: 738 | return freeze_factory 739 | 740 | # Change the modules 741 | datetime.datetime = FakeDatetime # type: ignore[misc] 742 | datetime.date = FakeDate # type: ignore[misc] 743 | 744 | time.time = fake_time 745 | time.monotonic = fake_monotonic 746 | time.perf_counter = fake_perf_counter 747 | time.localtime = fake_localtime # type: ignore 748 | time.gmtime = fake_gmtime # type: ignore 749 | time.strftime = fake_strftime # type: ignore 750 | if uuid_generate_time_attr: 751 | setattr(uuid, uuid_generate_time_attr, None) 752 | uuid._UuidCreate = None # type: ignore[attr-defined] 753 | uuid._last_timestamp = None # type: ignore[attr-defined] 754 | 755 | copyreg.dispatch_table[real_datetime] = pickle_fake_datetime 756 | copyreg.dispatch_table[real_date] = pickle_fake_date 757 | 758 | # Change any place where the module had already been imported 759 | to_patch = [ 760 | ('real_date', real_date, FakeDate), 761 | ('real_datetime', real_datetime, FakeDatetime), 762 | ('real_gmtime', real_gmtime, fake_gmtime), 763 | ('real_localtime', real_localtime, fake_localtime), 764 | ('real_monotonic', real_monotonic, fake_monotonic), 765 | ('real_perf_counter', real_perf_counter, fake_perf_counter), 766 | ('real_strftime', real_strftime, fake_strftime), 767 | ('real_time', real_time, fake_time), 768 | ] 769 | 770 | if _TIME_NS_PRESENT: 771 | time.time_ns = fake_time_ns 772 | to_patch.append(('real_time_ns', real_time_ns, fake_time_ns)) 773 | 774 | if _MONOTONIC_NS_PRESENT: 775 | time.monotonic_ns = fake_monotonic_ns 776 | to_patch.append(('real_monotonic_ns', real_monotonic_ns, fake_monotonic_ns)) 777 | 778 | if _PERF_COUNTER_NS_PRESENT: 779 | time.perf_counter_ns = fake_perf_counter_ns 780 | to_patch.append(('real_perf_counter_ns', real_perf_counter_ns, fake_perf_counter_ns)) 781 | 782 | if real_clock is not None: 783 | # time.clock is deprecated and was removed in Python 3.8 784 | time.clock = fake_clock # type: ignore[attr-defined] 785 | to_patch.append(('real_clock', real_clock, fake_clock)) 786 | 787 | self.fake_names = tuple(fake.__name__ for real_name, real, fake in to_patch) # type: ignore 788 | self.reals = {id(fake): real for real_name, real, fake in to_patch} 789 | fakes = {id(real): fake for real_name, real, fake in to_patch} 790 | add_change = self.undo_changes.append 791 | 792 | # Save the current loaded modules 793 | self.modules_at_start = set(sys.modules.keys()) 794 | 795 | with warnings.catch_warnings(): 796 | warnings.filterwarnings('ignore') 797 | 798 | for mod_name, module in list(sys.modules.items()): 799 | if mod_name is None or module is None or mod_name == __name__: 800 | continue 801 | elif mod_name.startswith(self.ignore) or mod_name.endswith('.six.moves'): 802 | continue 803 | elif (not hasattr(module, "__name__") or module.__name__ in ('datetime', 'time')): 804 | continue 805 | 806 | module_attrs = _get_cached_module_attributes(module) 807 | for attribute_name, attribute_value in module_attrs: 808 | fake = fakes.get(id(attribute_value)) 809 | if fake: 810 | setattr(module, attribute_name, fake) 811 | add_change((module, attribute_name, attribute_value)) 812 | 813 | if self.real_asyncio: 814 | # To avoid breaking `asyncio.sleep()`, let asyncio event loops see real 815 | # monotonic time even though we've just frozen `time.monotonic()` which 816 | # is normally used there. If we didn't do this, `await asyncio.sleep()` 817 | # would be hanging forever breaking many tests that use `freeze_time`. 818 | # 819 | # Note that we cannot statically tell the class of asyncio event loops 820 | # because it is not officially documented and can actually be changed 821 | # at run time using `asyncio.set_event_loop_policy`. That's why we check 822 | # the type by creating a loop here and destroying it immediately. 823 | event_loop = asyncio.new_event_loop() 824 | event_loop.close() 825 | EventLoopClass = type(event_loop) 826 | add_change((EventLoopClass, "time", EventLoopClass.time)) # type: ignore 827 | EventLoopClass.time = lambda self: real_monotonic() # type: ignore[method-assign] 828 | 829 | return freeze_factory 830 | 831 | def stop(self) -> None: 832 | freeze_factories.pop() 833 | ignore_lists.pop() 834 | tick_flags.pop() 835 | tz_offsets.pop() 836 | 837 | if not freeze_factories: 838 | datetime.datetime = real_datetime # type: ignore[misc] 839 | datetime.date = real_date # type: ignore[misc] 840 | copyreg.dispatch_table.pop(real_datetime) 841 | copyreg.dispatch_table.pop(real_date) 842 | for module_or_object, attribute, original_value in self.undo_changes: 843 | setattr(module_or_object, attribute, original_value) 844 | self.undo_changes = [] 845 | 846 | # Restore modules loaded after start() 847 | modules_to_restore = set(sys.modules.keys()) - self.modules_at_start 848 | self.modules_at_start = set() 849 | with warnings.catch_warnings(): 850 | warnings.simplefilter('ignore') 851 | for mod_name in modules_to_restore: 852 | module = sys.modules.get(mod_name, None) 853 | if mod_name is None or module is None: 854 | continue 855 | elif mod_name.startswith(self.ignore) or mod_name.endswith('.six.moves'): 856 | continue 857 | elif not hasattr(module, "__name__") or module.__name__ in ('datetime', 'time'): 858 | continue 859 | for module_attribute in dir(module): 860 | 861 | if module_attribute in self.fake_names: 862 | continue 863 | try: 864 | attribute_value = getattr(module, module_attribute) 865 | except (ImportError, AttributeError, TypeError): 866 | # For certain libraries, this can result in ImportError(_winreg) or AttributeError (celery) 867 | continue 868 | 869 | real = self.reals.get(id(attribute_value)) 870 | if real: 871 | setattr(module, module_attribute, real) 872 | 873 | time.time = real_time 874 | time.monotonic = real_monotonic 875 | time.perf_counter = real_perf_counter 876 | time.gmtime = real_gmtime 877 | time.localtime = real_localtime 878 | time.strftime = real_strftime 879 | time.clock = real_clock # type: ignore[attr-defined] 880 | 881 | if _TIME_NS_PRESENT: 882 | time.time_ns = real_time_ns 883 | 884 | if _MONOTONIC_NS_PRESENT: 885 | time.monotonic_ns = real_monotonic_ns 886 | 887 | if _PERF_COUNTER_NS_PRESENT: 888 | time.perf_counter_ns = real_perf_counter_ns 889 | 890 | if uuid_generate_time_attr: 891 | setattr(uuid, uuid_generate_time_attr, real_uuid_generate_time) 892 | uuid._UuidCreate = real_uuid_create # type: ignore[attr-defined] 893 | uuid._last_timestamp = None # type: ignore[attr-defined] 894 | 895 | def decorate_coroutine(self, coroutine: "Callable[P, Awaitable[T]]") -> "Callable[P, Awaitable[T]]": 896 | return wrap_coroutine(self, coroutine) 897 | 898 | def decorate_callable(self, func: "Callable[P, T]") -> "Callable[P, T]": 899 | @functools.wraps(func) 900 | def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> T: 901 | with self as time_factory: 902 | if self.as_arg and self.as_kwarg: 903 | assert False, "You can't specify both as_arg and as_kwarg at the same time. Pick one." 904 | elif self.as_arg: 905 | result = func(time_factory, *args, **kwargs) # type: ignore 906 | elif self.as_kwarg: 907 | kwargs[self.as_kwarg] = time_factory 908 | result = func(*args, **kwargs) 909 | else: 910 | result = func(*args, **kwargs) 911 | return result 912 | 913 | return wrapper 914 | 915 | 916 | def freeze_time(time_to_freeze: Optional[_Freezable]=None, tz_offset: Union[int, datetime.timedelta]=0, ignore: Optional[List[str]]=None, tick: bool=False, as_arg: bool=False, as_kwarg: str='', 917 | auto_tick_seconds: float=0, real_asyncio: bool=False) -> _freeze_time: 918 | """ 919 | Freezes time for testing purposes. 920 | 921 | This function can be used as a decorator or a context manager to freeze time 922 | during the execution of a block of code or a function. It provides various 923 | options to customize the behavior of the frozen time. 924 | 925 | Args: 926 | time_to_freeze (Optional[_Freezable]): The datetime to freeze time at. 927 | tz_offset (Union[int, datetime.timedelta]): The timezone offset to apply to the frozen time. 928 | ignore (Optional[List[str]]): A list of module names to ignore when freezing time. 929 | tick (bool): Whether to allow time to tick forward. 930 | as_arg (bool): Whether to pass the frozen time as an argument to the decorated function. 931 | as_kwarg (str): The name of the keyword argument to pass the frozen time to the decorated function. 932 | auto_tick_seconds (float): The number of seconds to auto-tick the frozen time. 933 | real_asyncio (bool): Whether to allow asyncio event loops to see real monotonic time. 934 | 935 | Returns: 936 | _freeze_time: An instance of the _freeze_time class. 937 | """ 938 | acceptable_times: Any = (type(None), str, datetime.date, datetime.timedelta, 939 | types.FunctionType, types.GeneratorType) 940 | 941 | if MayaDT is not None: 942 | acceptable_times += MayaDT, 943 | 944 | if not isinstance(time_to_freeze, acceptable_times): 945 | raise TypeError(('freeze_time() expected None, a string, date instance, datetime ' 946 | 'instance, MayaDT, timedelta instance, function or a generator, but got ' 947 | 'type {}.').format(type(time_to_freeze))) 948 | if tick and not _is_cpython: 949 | raise SystemError('Calling freeze_time with tick=True is only compatible with CPython') 950 | 951 | if isinstance(time_to_freeze, types.FunctionType): 952 | return freeze_time(time_to_freeze(), tz_offset, ignore, tick, as_arg, as_kwarg, auto_tick_seconds, real_asyncio=real_asyncio) 953 | 954 | if isinstance(time_to_freeze, types.GeneratorType): 955 | return freeze_time(next(time_to_freeze), tz_offset, ignore, tick, as_arg, as_kwarg, auto_tick_seconds, real_asyncio=real_asyncio) 956 | 957 | if MayaDT is not None and isinstance(time_to_freeze, MayaDT): 958 | return freeze_time(time_to_freeze.datetime(), tz_offset, ignore, 959 | tick, as_arg, as_kwarg, auto_tick_seconds, real_asyncio=real_asyncio) 960 | 961 | if ignore is None: 962 | ignore = [] 963 | ignore = ignore[:] 964 | if config.settings.default_ignore_list: 965 | ignore.extend(config.settings.default_ignore_list) 966 | 967 | return _freeze_time( 968 | time_to_freeze_str=time_to_freeze, 969 | tz_offset=tz_offset, 970 | ignore=ignore, 971 | tick=tick, 972 | as_arg=as_arg, 973 | as_kwarg=as_kwarg, 974 | auto_tick_seconds=auto_tick_seconds, 975 | real_asyncio=real_asyncio, 976 | ) 977 | 978 | 979 | # Setup adapters for sqlite 980 | try: 981 | # noinspection PyUnresolvedReferences 982 | import sqlite3 983 | except ImportError: 984 | # Some systems have trouble with this 985 | pass 986 | else: 987 | # These are copied from Python sqlite3.dbapi2 988 | def adapt_date(val: datetime.date) -> str: 989 | return val.isoformat() 990 | 991 | def adapt_datetime(val: datetime.datetime) -> str: 992 | return val.isoformat(" ") 993 | 994 | sqlite3.register_adapter(FakeDate, adapt_date) 995 | sqlite3.register_adapter(FakeDatetime, adapt_datetime) 996 | 997 | 998 | # Setup converters for pymysql 999 | try: 1000 | import pymysql.converters 1001 | except ImportError: 1002 | pass 1003 | else: 1004 | pymysql.converters.encoders[FakeDate] = pymysql.converters.encoders[real_date] 1005 | pymysql.converters.conversions[FakeDate] = pymysql.converters.encoders[real_date] 1006 | pymysql.converters.encoders[FakeDatetime] = pymysql.converters.encoders[real_datetime] 1007 | pymysql.converters.conversions[FakeDatetime] = pymysql.converters.encoders[real_datetime] 1008 | -------------------------------------------------------------------------------- /freezegun/config.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional 2 | 3 | 4 | DEFAULT_IGNORE_LIST = [ 5 | 'nose.plugins', 6 | 'six.moves', 7 | 'django.utils.six.moves', 8 | 'google.gax', 9 | 'threading', 10 | 'multiprocessing', 11 | 'queue', 12 | 'selenium', 13 | '_pytest.terminal.', 14 | '_pytest.runner.', 15 | 'gi', 16 | 'prompt_toolkit', 17 | ] 18 | 19 | 20 | class Settings: 21 | def __init__(self, default_ignore_list: Optional[List[str]]=None) -> None: 22 | self.default_ignore_list = default_ignore_list or DEFAULT_IGNORE_LIST[:] 23 | 24 | 25 | settings = Settings() 26 | 27 | 28 | class ConfigurationError(Exception): 29 | pass 30 | 31 | 32 | def configure(default_ignore_list: Optional[List[str]]=None, extend_ignore_list: Optional[List[str]]=None) -> None: 33 | if default_ignore_list is not None and extend_ignore_list is not None: 34 | raise ConfigurationError("Either default_ignore_list or extend_ignore_list might be given, not both") 35 | if default_ignore_list is not None: 36 | settings.default_ignore_list = default_ignore_list 37 | if extend_ignore_list: 38 | settings.default_ignore_list = list(dict.fromkeys([*settings.default_ignore_list, *extend_ignore_list])) 39 | 40 | 41 | def reset_config() -> None: 42 | global settings 43 | settings = Settings() 44 | -------------------------------------------------------------------------------- /freezegun/py.typed: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spulec/freezegun/ba06fa43d18c24e6bd4b3bacb7edcb547e7b30ae/freezegun/py.typed -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.mypy] 2 | files = "freezegun,tests" 3 | strict = true 4 | pretty = true 5 | show_column_numbers = true 6 | show_error_codes = true 7 | show_error_context = true 8 | warn_unused_ignores = false 9 | 10 | [build-system] 11 | requires = ["setuptools", "wheel"] 12 | build-backend = "setuptools.build_meta" 13 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | pytest-cov 3 | coveralls 4 | python-dateutil >= 2.7 5 | maya; python_version < '3.12' 6 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = freezegun 3 | version = attr: freezegun.__version__ 4 | description = Let your Python tests travel through time 5 | long_description = file: README.rst 6 | author = Steve Pulec 7 | author_email = spulec@gmail.com 8 | url = https://github.com/spulec/freezegun 9 | project_urls = 10 | Bug Tracker = https://github.com/spulec/freezegun/issues 11 | Changes = https://github.com/spulec/freezegun/blob/master/CHANGELOG 12 | Documentation = https://github.com/spulec/freezegun/blob/master/README.rst 13 | Source Code = https://github.com/spulec/freezegun 14 | license = Apache-2.0 15 | classifiers = 16 | Programming Language :: Python :: 3 17 | Programming Language :: Python :: 3.8 18 | Programming Language :: Python :: 3.9 19 | Programming Language :: Python :: 3.10 20 | Programming Language :: Python :: 3.11 21 | Programming Language :: Python :: 3.12 22 | Programming Language :: Python :: 3.13 23 | Programming Language :: Python :: Implementation :: CPython 24 | Programming Language :: Python :: Implementation :: PyPy 25 | 26 | [options] 27 | packages = freezegun 28 | python_requires = >=3.8 29 | install_requires = 30 | python-dateutil >= 2.7 31 | include_package_data = true 32 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | 4 | setup() 5 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/spulec/freezegun/ba06fa43d18c24e6bd4b3bacb7edcb547e7b30ae/tests/__init__.py -------------------------------------------------------------------------------- /tests/another_module.py: -------------------------------------------------------------------------------- 1 | from datetime import date, datetime 2 | from time import time, localtime, gmtime, strftime 3 | from typing import Any 4 | 5 | from freezegun.api import ( 6 | FakeDatetime, 7 | FakeDate, 8 | fake_time, 9 | fake_localtime, 10 | fake_gmtime, 11 | fake_strftime, 12 | ) 13 | 14 | 15 | # Reals 16 | 17 | def get_datetime() -> Any: 18 | return datetime 19 | 20 | 21 | def get_date() -> Any: 22 | return date 23 | 24 | 25 | def get_time() -> Any: 26 | return time 27 | 28 | 29 | def get_localtime() -> Any: 30 | return localtime 31 | 32 | 33 | def get_gmtime() -> Any: 34 | return gmtime 35 | 36 | 37 | def get_strftime() -> Any: 38 | return strftime 39 | 40 | 41 | # Fakes 42 | 43 | def get_fake_datetime() -> Any: 44 | return FakeDatetime 45 | 46 | 47 | def get_fake_date() -> Any: 48 | return FakeDate 49 | 50 | 51 | def get_fake_time() -> Any: 52 | return fake_time 53 | 54 | 55 | def get_fake_localtime() -> Any: 56 | return fake_localtime 57 | 58 | 59 | def get_fake_gmtime() -> Any: 60 | return fake_gmtime 61 | 62 | 63 | def get_fake_strftime() -> Any: 64 | return fake_strftime 65 | -------------------------------------------------------------------------------- /tests/fake_module.py: -------------------------------------------------------------------------------- 1 | from datetime import date, datetime 2 | from time import time, localtime, gmtime, strftime, struct_time 3 | from typing import Any 4 | 5 | 6 | def fake_datetime_function() -> datetime: 7 | return datetime.now() 8 | 9 | 10 | def fake_date_function() -> date: 11 | return date.today() 12 | 13 | 14 | def fake_time_function() -> float: 15 | return time() 16 | 17 | 18 | def fake_localtime_function() -> struct_time: 19 | return localtime() 20 | 21 | 22 | def fake_gmtime_function() -> struct_time: 23 | return gmtime() 24 | 25 | 26 | def fake_strftime_function() -> str: 27 | return strftime("%Y") 28 | 29 | 30 | class EqualToAnything: 31 | description = 'This is the equal_to_anything object' 32 | 33 | def __eq__(self, other: Any) -> bool: 34 | return True 35 | 36 | def __neq__(self, other: Any) -> bool: 37 | return False 38 | 39 | 40 | equal_to_anything = EqualToAnything() 41 | -------------------------------------------------------------------------------- /tests/test_asyncio.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import datetime 3 | import time 4 | from typing import Any 5 | 6 | from freezegun import freeze_time 7 | 8 | 9 | def test_datetime_in_coroutine() -> None: 10 | @freeze_time('1970-01-01') 11 | async def frozen_coroutine() -> Any: 12 | assert datetime.date.today() == datetime.date(1970, 1, 1) 13 | 14 | asyncio.run(frozen_coroutine()) # type: ignore 15 | 16 | 17 | def test_freezing_time_in_coroutine() -> None: 18 | """Test calling freeze_time while executing asyncio loop.""" 19 | async def coroutine() -> None: 20 | with freeze_time('1970-01-02'): 21 | assert time.time() == 86400 22 | with freeze_time('1970-01-03'): 23 | assert time.time() == 86400 * 2 24 | 25 | asyncio.run(coroutine()) 26 | 27 | 28 | def test_freezing_time_before_running_coroutine() -> None: 29 | """Test calling freeze_time before executing asyncio loop.""" 30 | async def coroutine() -> None: 31 | assert time.time() == 86400 32 | with freeze_time('1970-01-02'): 33 | asyncio.run(coroutine()) 34 | 35 | 36 | def test_asyncio_sleeping_not_affected_by_freeze_time() -> None: 37 | """Test that asyncio.sleep() is not affected by `freeze_time`. 38 | 39 | This test ensures that despite freezing time using `freeze_time`, 40 | the asyncio event loop can see real monotonic time, which is required 41 | to make things like `asyncio.sleep()` work. 42 | """ 43 | 44 | async def coroutine() -> None: 45 | # Sleeping with time frozen should sleep the expected duration. 46 | before_sleep = time.time() 47 | with freeze_time('1970-01-02', real_asyncio=True): 48 | await asyncio.sleep(0.05) 49 | assert 0.02 <= time.time() - before_sleep < 0.3 50 | 51 | # Exiting `freeze_time` the time should not break asyncio sleeping. 52 | before_sleep = time.time() 53 | await asyncio.sleep(0.05) 54 | assert 0.02 <= time.time() - before_sleep < 0.3 55 | 56 | asyncio.run(coroutine()) 57 | 58 | 59 | def test_asyncio_to_call_later_with_frozen_time() -> None: 60 | """Test that asyncio `loop.call_later` works with frozen time.""" 61 | # `to_call_later` will be called by asyncio event loop and should add 62 | # the Unix timestamp of 1970-01-02 00:00 to the `timestamps` list. 63 | timestamps = [] 64 | def to_call_later() -> None: 65 | timestamps.append(time.time()) 66 | 67 | async def coroutine() -> None: 68 | # Schedule calling `to_call_later` in 100 ms. 69 | asyncio.get_running_loop().call_later(0.1, to_call_later) 70 | 71 | # Sleeping for 10 ms should not result in calling `to_call_later`. 72 | await asyncio.sleep(0.01) 73 | assert timestamps == [] 74 | 75 | # But sleeping more (150 ms in this case) should call `to_call_later` 76 | # and we should see `timestamps` updated. 77 | await asyncio.sleep(0.15) 78 | assert timestamps == [86400] 79 | 80 | with freeze_time('1970-01-02', real_asyncio=True): 81 | asyncio.run(coroutine()) 82 | -------------------------------------------------------------------------------- /tests/test_class_import.py: -------------------------------------------------------------------------------- 1 | import time 2 | import sys 3 | from .fake_module import ( 4 | fake_date_function, 5 | fake_datetime_function, 6 | fake_gmtime_function, 7 | fake_localtime_function, 8 | fake_strftime_function, 9 | fake_time_function, 10 | ) 11 | from . import fake_module 12 | from freezegun import freeze_time 13 | from freezegun.api import ( 14 | FakeDatetime, 15 | FakeDate, 16 | fake_time, 17 | fake_localtime, 18 | fake_gmtime, 19 | fake_strftime, 20 | ) 21 | import datetime 22 | 23 | 24 | @freeze_time("2012-01-14") 25 | def test_import_datetime_works() -> None: 26 | assert fake_datetime_function().day == 14 27 | 28 | 29 | @freeze_time("2012-01-14") 30 | def test_import_date_works() -> None: 31 | assert fake_date_function().day == 14 32 | 33 | 34 | @freeze_time("2012-01-14") 35 | def test_import_time() -> None: 36 | local_time = datetime.datetime(2012, 1, 14) 37 | utc_time = local_time - datetime.timedelta(seconds=time.timezone) 38 | expected_timestamp = time.mktime(utc_time.timetuple()) 39 | assert fake_time_function() == expected_timestamp 40 | 41 | 42 | def test_start_and_stop_works() -> None: 43 | freezer = freeze_time("2012-01-14") 44 | 45 | result = fake_datetime_function() 46 | assert result.__class__ == datetime.datetime 47 | assert result.__class__ != FakeDatetime 48 | 49 | freezer.start() 50 | assert fake_datetime_function().day == 14 51 | assert isinstance(fake_datetime_function(), datetime.datetime) 52 | assert isinstance(fake_datetime_function(), FakeDatetime) 53 | 54 | freezer.stop() 55 | result = fake_datetime_function() 56 | assert result.__class__ == datetime.datetime 57 | assert result.__class__ != FakeDatetime 58 | 59 | 60 | def test_isinstance_works() -> None: 61 | date = datetime.date.today() 62 | now = datetime.datetime.now() 63 | 64 | freezer = freeze_time('2011-01-01') 65 | freezer.start() 66 | assert isinstance(date, datetime.date) 67 | assert not isinstance(date, datetime.datetime) 68 | assert isinstance(now, datetime.datetime) 69 | assert isinstance(now, datetime.date) 70 | freezer.stop() 71 | 72 | 73 | def test_issubclass_works() -> None: 74 | real_date = datetime.date 75 | real_datetime = datetime.datetime 76 | 77 | freezer = freeze_time('2011-01-01') 78 | freezer.start() 79 | assert issubclass(real_date, datetime.date) 80 | assert issubclass(real_datetime, datetime.datetime) 81 | freezer.stop() 82 | 83 | 84 | def test_fake_uses_real_when_ignored() -> None: 85 | real_time_before = time.time() 86 | with freeze_time('2012-01-14', ignore=['tests.fake_module']): 87 | real_time = fake_time_function() 88 | real_time_after = time.time() 89 | assert real_time_before <= real_time <= real_time_after 90 | 91 | 92 | def test_can_ignore_email_module() -> None: 93 | from email.utils import formatdate 94 | with freeze_time('2012-01-14'): 95 | faked_date_str = formatdate() 96 | 97 | before_date_str = formatdate() 98 | with freeze_time('2012-01-14', ignore=['email']): 99 | date_str = formatdate() 100 | 101 | after_date_str = formatdate() 102 | assert date_str != faked_date_str 103 | assert before_date_str <= date_str <= after_date_str 104 | 105 | 106 | @freeze_time('2011-01-01') 107 | def test_avoid_replacing_equal_to_anything() -> None: 108 | assert fake_module.equal_to_anything.description == 'This is the equal_to_anything object' 109 | 110 | 111 | @freeze_time("2012-01-14 12:00:00") 112 | def test_import_localtime() -> None: 113 | struct = fake_localtime_function() 114 | assert struct.tm_year == 2012 115 | assert struct.tm_mon == 1 116 | assert struct.tm_mday >= 13 # eg. GMT+14 117 | assert struct.tm_mday <= 15 # eg. GMT-14 118 | 119 | 120 | @freeze_time("2012-01-14 12:00:00") 121 | def test_fake_gmtime_function() -> None: 122 | struct = fake_gmtime_function() 123 | assert struct.tm_year == 2012 124 | assert struct.tm_mon == 1 125 | assert struct.tm_mday == 14 126 | 127 | 128 | @freeze_time("2012-01-14") 129 | def test_fake_strftime_function() -> None: 130 | assert fake_strftime_function() == '2012' 131 | 132 | 133 | def test_import_after_start() -> None: 134 | with freeze_time('2012-01-14'): 135 | assert 'tests.another_module' not in sys.modules.keys() 136 | from tests import another_module 137 | 138 | # Reals 139 | assert another_module.get_datetime() is datetime.datetime 140 | assert another_module.get_datetime() is FakeDatetime 141 | assert another_module.get_date() is datetime.date 142 | assert another_module.get_date() is FakeDate 143 | assert another_module.get_time() is time.time 144 | assert another_module.get_time() is fake_time 145 | assert another_module.get_localtime() is time.localtime 146 | assert another_module.get_localtime() is fake_localtime 147 | assert another_module.get_gmtime() is time.gmtime 148 | assert another_module.get_gmtime() is fake_gmtime 149 | assert another_module.get_strftime() is time.strftime 150 | assert another_module.get_strftime() is fake_strftime 151 | 152 | # Fakes 153 | assert another_module.get_fake_datetime() is FakeDatetime 154 | assert another_module.get_fake_date() is FakeDate 155 | assert another_module.get_fake_time() is fake_time 156 | assert another_module.get_fake_localtime() is fake_localtime 157 | assert another_module.get_fake_gmtime() is fake_gmtime 158 | assert another_module.get_fake_strftime() is fake_strftime 159 | 160 | # Reals 161 | assert another_module.get_datetime() is datetime.datetime 162 | assert not another_module.get_datetime() is FakeDatetime 163 | assert another_module.get_date() is datetime.date 164 | assert not another_module.get_date() is FakeDate 165 | assert another_module.get_time() is time.time 166 | assert not another_module.get_time() is fake_time 167 | assert another_module.get_localtime() is time.localtime 168 | assert not another_module.get_localtime() is fake_localtime 169 | assert another_module.get_gmtime() is time.gmtime 170 | assert not another_module.get_gmtime() is fake_gmtime 171 | assert another_module.get_strftime() is time.strftime 172 | assert not another_module.get_strftime() is fake_strftime 173 | 174 | # Fakes 175 | assert another_module.get_fake_datetime() is FakeDatetime 176 | assert another_module.get_fake_date() is FakeDate 177 | assert another_module.get_fake_time() is fake_time 178 | assert another_module.get_fake_localtime() is fake_localtime 179 | assert another_module.get_fake_gmtime() is fake_gmtime 180 | assert another_module.get_fake_strftime() is fake_strftime 181 | del sys.modules['tests.another_module'] 182 | 183 | def test_none_as_initial() -> None: 184 | with freeze_time() as ft: 185 | ft.move_to('2012-01-14') 186 | assert fake_strftime_function() == '2012' 187 | -------------------------------------------------------------------------------- /tests/test_configure.py: -------------------------------------------------------------------------------- 1 | from unittest import mock 2 | import pytest 3 | import freezegun 4 | import freezegun.config 5 | 6 | from typing import List 7 | 8 | 9 | def setup_function() -> None: 10 | freezegun.config.reset_config() 11 | 12 | 13 | def teardown_function() -> None: 14 | freezegun.config.reset_config() 15 | 16 | 17 | @pytest.mark.parametrize('ignorelist', (['threading', 'tensorflow'], [])) 18 | def test_default_ignore_list_is_overridden(ignorelist: List[str]) -> None: 19 | freezegun.configure(default_ignore_list=list(ignorelist)) 20 | 21 | with mock.patch("freezegun.api._freeze_time.__init__", return_value=None) as _freeze_time_init_mock: 22 | 23 | freezegun.freeze_time("2020-10-06") 24 | 25 | _freeze_time_init_mock.assert_called_once_with( 26 | time_to_freeze_str="2020-10-06", 27 | tz_offset=0, 28 | ignore=ignorelist, 29 | tick=False, 30 | as_arg=False, 31 | as_kwarg='', 32 | auto_tick_seconds=0, 33 | real_asyncio=False, 34 | ) 35 | 36 | 37 | @pytest.mark.parametrize('ignorelist', (['tensorflow'], [])) 38 | def test_extend_default_ignore_list(ignorelist: List[str]) -> None: 39 | freezegun.configure(extend_ignore_list=list(ignorelist)) 40 | 41 | with mock.patch("freezegun.api._freeze_time.__init__", return_value=None) as _freeze_time_init_mock: 42 | 43 | freezegun.freeze_time("2020-10-06") 44 | 45 | expected_ignore_list = [ 46 | 'nose.plugins', 47 | 'six.moves', 48 | 'django.utils.six.moves', 49 | 'google.gax', 50 | 'threading', 51 | 'multiprocessing', 52 | 'queue', 53 | 'selenium', 54 | '_pytest.terminal.', 55 | '_pytest.runner.', 56 | 'gi', 57 | 'prompt_toolkit', 58 | ] + ignorelist 59 | 60 | _freeze_time_init_mock.assert_called_once_with( 61 | time_to_freeze_str="2020-10-06", 62 | tz_offset=0, 63 | ignore=expected_ignore_list, 64 | tick=False, 65 | as_arg=False, 66 | as_kwarg='', 67 | auto_tick_seconds=0, 68 | real_asyncio=False, 69 | ) 70 | 71 | def test_extend_default_ignore_list_duplicate_items() -> None: 72 | freezegun.configure(extend_ignore_list=['tensorflow', 'pymongo', 'tensorflow','rabbitmq']) 73 | freezegun.configure(extend_ignore_list=['tensorflow']) 74 | 75 | with mock.patch("freezegun.api._freeze_time.__init__", return_value=None) as _freeze_time_init_mock: 76 | 77 | freezegun.freeze_time("2020-10-06") 78 | 79 | expected_ignore_list = [ 80 | 'nose.plugins', 81 | 'six.moves', 82 | 'django.utils.six.moves', 83 | 'google.gax', 84 | 'threading', 85 | 'multiprocessing', 86 | 'queue', 87 | 'selenium', 88 | '_pytest.terminal.', 89 | '_pytest.runner.', 90 | 'gi', 91 | 'prompt_toolkit', 92 | 'tensorflow', 93 | 'pymongo', 94 | 'rabbitmq', 95 | ] 96 | 97 | _freeze_time_init_mock.assert_called_once_with( 98 | time_to_freeze_str="2020-10-06", 99 | tz_offset=0, 100 | ignore=expected_ignore_list, 101 | tick=False, 102 | as_arg=False, 103 | as_kwarg='', 104 | auto_tick_seconds=0, 105 | real_asyncio=False, 106 | ) 107 | -------------------------------------------------------------------------------- /tests/test_datetimes.py: -------------------------------------------------------------------------------- 1 | import time 2 | import calendar 3 | import datetime 4 | import fractions 5 | import unittest 6 | import locale 7 | import sys 8 | from typing import Any, Callable 9 | from unittest import SkipTest 10 | from dateutil.tz import UTC 11 | 12 | import pytest 13 | from tests import utils 14 | 15 | from freezegun import freeze_time 16 | from freezegun.api import FakeDatetime, FakeDate 17 | 18 | try: 19 | import maya # type: ignore 20 | except ImportError: 21 | maya = None 22 | 23 | # time.clock was removed in Python 3.8 24 | HAS_CLOCK = hasattr(time, 'clock') 25 | HAS_TIME_NS = hasattr(time, 'time_ns') 26 | HAS_MONOTONIC_NS = hasattr(time, 'monotonic_ns') 27 | HAS_PERF_COUNTER_NS = hasattr(time, 'perf_counter_ns') 28 | 29 | class temp_locale: 30 | """Temporarily change the locale.""" 31 | 32 | def __init__(self, *targets: str): 33 | self.targets = targets 34 | 35 | def __enter__(self) -> None: 36 | self.old = locale.setlocale(locale.LC_ALL) 37 | for target in self.targets: 38 | try: 39 | locale.setlocale(locale.LC_ALL, target) 40 | return 41 | except locale.Error: 42 | pass 43 | msg = 'could not set locale to any of: %s' % ', '.join(self.targets) 44 | raise SkipTest(msg) 45 | 46 | def __exit__(self, *args: Any) -> None: 47 | locale.setlocale(locale.LC_ALL, self.old) 48 | 49 | # Small sample of locales where '%x' expands to a dd/mm/yyyy string, 50 | # which can cause trouble when parsed with dateutil. 51 | _dd_mm_yyyy_locales = ['da_DK.UTF-8', 'de_DE.UTF-8', 'fr_FR.UTF-8'] 52 | 53 | 54 | def test_simple_api() -> None: 55 | # time to freeze is always provided in UTC 56 | freezer = freeze_time("2012-01-14") 57 | # expected timestamp must be a timestamp, corresponding to 2012-01-14 UTC 58 | local_time = datetime.datetime(2012, 1, 14) 59 | utc_time = local_time - datetime.timedelta(seconds=time.timezone) 60 | expected_timestamp = time.mktime(utc_time.timetuple()) 61 | 62 | freezer.start() 63 | assert time.time() == expected_timestamp 64 | assert time.monotonic() >= 0.0 65 | assert time.perf_counter() >= 0.0 66 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 67 | assert datetime.datetime.utcnow() == datetime.datetime(2012, 1, 14) 68 | assert datetime.date.today() == datetime.date(2012, 1, 14) 69 | assert datetime.datetime.now().today() == datetime.datetime(2012, 1, 14) 70 | freezer.stop() 71 | assert time.time() != expected_timestamp 72 | assert time.monotonic() >= 0.0 73 | assert time.perf_counter() >= 0.0 74 | assert datetime.datetime.now() != datetime.datetime(2012, 1, 14) 75 | assert datetime.datetime.utcnow() != datetime.datetime(2012, 1, 14) 76 | freezer = freeze_time("2012-01-10 13:52:01") 77 | freezer.start() 78 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 10, 13, 52, 1) 79 | freezer.stop() 80 | 81 | 82 | def test_tz_offset() -> None: 83 | freezer = freeze_time("2012-01-14 03:21:34", tz_offset=-4) 84 | # expected timestamp must be a timestamp, 85 | # corresponding to 2012-01-14 03:21:34 UTC 86 | # and it doesn't depend on tz_offset 87 | local_time = datetime.datetime(2012, 1, 14, 3, 21, 34) 88 | utc_time = local_time - datetime.timedelta(seconds=time.timezone) 89 | expected_timestamp = time.mktime(utc_time.timetuple()) 90 | 91 | freezer.start() 92 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 13, 23, 21, 34) 93 | assert datetime.datetime.utcnow() == datetime.datetime(2012, 1, 14, 3, 21, 34) 94 | assert time.time() == expected_timestamp 95 | freezer.stop() 96 | 97 | 98 | def test_timestamp_tz_offset() -> None: 99 | freezer = freeze_time(datetime.datetime.fromtimestamp(1), tz_offset=-1) 100 | freezer.start() 101 | t = datetime.datetime.now().timestamp() 102 | 103 | assert datetime.datetime.fromtimestamp(t).timestamp() == t 104 | freezer.stop() 105 | 106 | 107 | def test_timedelta_tz_offset() -> None: 108 | freezer = freeze_time("2012-01-14 03:21:34", 109 | tz_offset=-datetime.timedelta(hours=3, minutes=30)) 110 | freezer.start() 111 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 13, 23, 51, 34) 112 | assert datetime.datetime.utcnow() == datetime.datetime(2012, 1, 14, 3, 21, 34) 113 | freezer.stop() 114 | 115 | 116 | def test_tz_offset_with_today() -> None: 117 | freezer = freeze_time("2012-01-14", tz_offset=-4) 118 | freezer.start() 119 | assert datetime.date.today() == datetime.date(2012, 1, 13) 120 | freezer.stop() 121 | assert datetime.date.today() != datetime.date(2012, 1, 13) 122 | 123 | 124 | def test_zero_tz_offset_with_time() -> None: 125 | # we expect the system to behave like a system with UTC timezone 126 | # at the beginning of the Epoch 127 | freezer = freeze_time('1970-01-01') 128 | freezer.start() 129 | assert datetime.date.today() == datetime.date(1970, 1, 1) 130 | assert datetime.datetime.now() == datetime.datetime(1970, 1, 1) 131 | assert datetime.datetime.utcnow() == datetime.datetime(1970, 1, 1) 132 | assert time.time() == 0.0 133 | assert time.monotonic() >= 0.0 134 | assert time.perf_counter() >= 0.0 135 | freezer.stop() 136 | 137 | 138 | def test_tz_offset_with_time() -> None: 139 | # we expect the system to behave like a system with UTC-4 timezone 140 | # at the beginning of the Epoch (wall clock should be 4 hrs late) 141 | freezer = freeze_time('1970-01-01', tz_offset=-4) 142 | freezer.start() 143 | assert datetime.date.today() == datetime.date(1969, 12, 31) 144 | assert datetime.datetime.now() == datetime.datetime(1969, 12, 31, 20) 145 | assert datetime.datetime.utcnow() == datetime.datetime(1970, 1, 1) 146 | assert time.time() == 0.0 147 | assert time.monotonic() >= 0 148 | assert time.perf_counter() >= 0 149 | freezer.stop() 150 | 151 | 152 | def test_time_with_microseconds() -> None: 153 | freezer = freeze_time(datetime.datetime(1970, 1, 1, 0, 0, 1, 123456)) 154 | freezer.start() 155 | assert time.time() == 1.123456 156 | freezer.stop() 157 | 158 | 159 | def test_time_with_dst() -> None: 160 | freezer = freeze_time(datetime.datetime(1970, 6, 1, 0, 0, 1, 123456)) 161 | freezer.start() 162 | assert time.time() == 13046401.123456 163 | freezer.stop() 164 | 165 | 166 | def test_manual_increment() -> None: 167 | initial_datetime = datetime.datetime(year=1, month=7, day=12, 168 | hour=15, minute=6, second=3) 169 | with freeze_time(initial_datetime) as frozen_datetime: 170 | assert frozen_datetime() == initial_datetime 171 | 172 | expected = initial_datetime + datetime.timedelta(seconds=1) 173 | assert frozen_datetime.tick() == expected 174 | assert frozen_datetime() == expected 175 | 176 | expected = initial_datetime + datetime.timedelta(seconds=11) 177 | assert frozen_datetime.tick(10) == expected 178 | assert frozen_datetime() == expected 179 | 180 | expected = initial_datetime + datetime.timedelta(seconds=21) 181 | assert frozen_datetime.tick(delta=datetime.timedelta(seconds=10)) == expected 182 | assert frozen_datetime() == expected 183 | 184 | expected = initial_datetime + datetime.timedelta(seconds=22.5) 185 | ticked_time = frozen_datetime.tick( 186 | delta=fractions.Fraction(3, 2) # type: ignore 187 | # type hints follow the recommendation of 188 | # https://peps.python.org/pep-0484/#the-numeric-tower 189 | # which means for instance `Fraction`s work at runtime, but not 190 | # during static type analysis 191 | ) 192 | assert ticked_time == expected 193 | assert frozen_datetime() == expected 194 | 195 | 196 | def test_move_to() -> None: 197 | initial_datetime = datetime.datetime(year=1, month=7, day=12, 198 | hour=15, minute=6, second=3) 199 | 200 | other_datetime = datetime.datetime(year=2, month=8, day=13, 201 | hour=14, minute=5, second=0) 202 | with freeze_time(initial_datetime) as frozen_datetime: 203 | assert frozen_datetime() == initial_datetime 204 | 205 | frozen_datetime.move_to(other_datetime) 206 | assert frozen_datetime() == other_datetime 207 | 208 | frozen_datetime.move_to(initial_datetime) 209 | assert frozen_datetime() == initial_datetime 210 | 211 | 212 | def test_bad_time_argument() -> None: 213 | try: 214 | freeze_time("2012-13-14", tz_offset=-4) 215 | except ValueError: 216 | pass 217 | else: 218 | assert False, "Bad values should raise a ValueError" 219 | 220 | 221 | @pytest.mark.parametrize("func_name, has_func, tick_size", ( 222 | ("monotonic", True, 1.0), 223 | ("monotonic_ns", HAS_MONOTONIC_NS, int(1e9)), 224 | ("perf_counter", True, 1.0), 225 | ("perf_counter_ns", HAS_PERF_COUNTER_NS, int(1e9)),) 226 | ) 227 | def test_time_monotonic(func_name: str, has_func: bool, tick_size: int) -> None: 228 | initial_datetime = datetime.datetime(year=1, month=7, day=12, 229 | hour=15, minute=6, second=3) 230 | if not has_func: 231 | pytest.skip("%s does not exist in current version" % func_name) 232 | 233 | with freeze_time(initial_datetime) as frozen_datetime: 234 | func = getattr(time, func_name) 235 | t0 = func() 236 | 237 | frozen_datetime.tick() 238 | 239 | t1 = func() 240 | 241 | assert t1 == t0 + tick_size 242 | 243 | frozen_datetime.tick(10) 244 | 245 | t11 = func() 246 | assert t11 == t1 + 10 * tick_size 247 | 248 | 249 | def test_time_gmtime() -> None: 250 | with freeze_time('2012-01-14 03:21:34'): 251 | time_struct = time.gmtime() 252 | assert time_struct.tm_year == 2012 253 | assert time_struct.tm_mon == 1 254 | assert time_struct.tm_mday == 14 255 | assert time_struct.tm_hour == 3 256 | assert time_struct.tm_min == 21 257 | assert time_struct.tm_sec == 34 258 | assert time_struct.tm_wday == 5 259 | assert time_struct.tm_yday == 14 260 | assert time_struct.tm_isdst == -1 261 | 262 | 263 | @pytest.mark.skipif(not HAS_CLOCK, 264 | reason="time.clock was removed in Python 3.8") 265 | def test_time_clock() -> None: 266 | with freeze_time('2012-01-14 03:21:34'): 267 | assert time.clock() == 0 # type: ignore[attr-defined] 268 | 269 | with freeze_time('2012-01-14 03:21:35'): 270 | assert time.clock() == 1 # type: ignore[attr-defined] 271 | 272 | with freeze_time('2012-01-14 03:21:36'): 273 | assert time.clock() == 2 # type: ignore[attr-defined] 274 | 275 | 276 | class modify_timezone: 277 | 278 | def __init__(self, new_timezone: int): 279 | self.new_timezone = new_timezone 280 | self.original_timezone = time.timezone 281 | 282 | def __enter__(self) -> None: 283 | time.timezone = self.new_timezone 284 | 285 | def __exit__(self, *args: Any) -> None: 286 | time.timezone = self.original_timezone 287 | 288 | 289 | def test_time_localtime() -> None: 290 | with modify_timezone(-3600): # Set this for UTC-1 291 | with freeze_time('2012-01-14 03:21:34'): 292 | time_struct = time.localtime() 293 | assert time_struct.tm_year == 2012 294 | assert time_struct.tm_mon == 1 295 | assert time_struct.tm_mday == 14 296 | assert time_struct.tm_hour == 4 # offset of 1 hour due to time zone 297 | assert time_struct.tm_min == 21 298 | assert time_struct.tm_sec == 34 299 | assert time_struct.tm_wday == 5 300 | assert time_struct.tm_yday == 14 301 | assert time_struct.tm_isdst == -1 302 | assert time.localtime().tm_year != 2012 303 | 304 | 305 | def test_strftime() -> None: 306 | with modify_timezone(0): 307 | with freeze_time('1970-01-01'): 308 | assert time.strftime("%Y") == "1970" 309 | 310 | 311 | def test_real_strftime_fall_through() -> None: 312 | this_real_year = datetime.datetime.now().year 313 | with freeze_time(): 314 | assert time.strftime('%Y') == str(this_real_year) 315 | assert time.strftime('%Y', (2001, 1, 1, 1, 1, 1, 1, 1, 1)) == '2001' 316 | 317 | 318 | def test_date_object() -> None: 319 | frozen_date = datetime.date(year=2012, month=11, day=10) 320 | date_freezer = freeze_time(frozen_date) 321 | regular_freezer = freeze_time('2012-11-10') 322 | assert date_freezer.time_to_freeze == regular_freezer.time_to_freeze 323 | 324 | 325 | def test_old_date_object() -> None: 326 | frozen_date = datetime.date(year=1, month=1, day=1) 327 | with freeze_time(frozen_date): 328 | assert datetime.date.today() == frozen_date 329 | 330 | 331 | def test_date_with_locale() -> None: 332 | with temp_locale(*_dd_mm_yyyy_locales): 333 | frozen_date = datetime.date(year=2012, month=1, day=2) 334 | date_freezer = freeze_time(frozen_date) 335 | assert date_freezer.time_to_freeze.date() == frozen_date 336 | 337 | 338 | def test_invalid_type() -> None: 339 | try: 340 | freeze_time(int(4)) # type: ignore 341 | except TypeError: 342 | pass 343 | else: 344 | assert False, "Bad types should raise a TypeError" 345 | 346 | 347 | def test_datetime_object() -> None: 348 | frozen_datetime = datetime.datetime(year=2012, month=11, day=10, 349 | hour=4, minute=15, second=30) 350 | datetime_freezer = freeze_time(frozen_datetime) 351 | regular_freezer = freeze_time('2012-11-10 04:15:30') 352 | assert datetime_freezer.time_to_freeze == regular_freezer.time_to_freeze 353 | 354 | 355 | def test_function_object() -> None: 356 | frozen_datetime = datetime.datetime(year=2012, month=11, day=10, 357 | hour=4, minute=15, second=30) 358 | def function() -> datetime.datetime: return frozen_datetime 359 | 360 | with freeze_time(function): 361 | assert frozen_datetime == datetime.datetime.now() 362 | 363 | 364 | def test_lambda_object() -> None: 365 | frozen_datetime = datetime.datetime(year=2012, month=11, day=10, 366 | hour=4, minute=15, second=30) 367 | with freeze_time(lambda: frozen_datetime): 368 | assert frozen_datetime == datetime.datetime.now() 369 | 370 | 371 | def test_generator_object() -> None: 372 | frozen_datetimes = (datetime.datetime(year=y, month=1, day=1) 373 | for y in range(2010, 2012)) 374 | 375 | with freeze_time(frozen_datetimes): 376 | assert datetime.datetime(2010, 1, 1) == datetime.datetime.now() 377 | 378 | with freeze_time(frozen_datetimes): 379 | assert datetime.datetime(2011, 1, 1) == datetime.datetime.now() 380 | 381 | with pytest.raises(StopIteration): 382 | freeze_time(frozen_datetimes) 383 | 384 | 385 | def test_maya_datetimes() -> None: 386 | if not maya: 387 | raise SkipTest("maya is optional since it's not supported for " 388 | "enough python versions") 389 | 390 | with freeze_time(maya.when("October 2nd, 1997")): 391 | assert datetime.datetime.now() == datetime.datetime( 392 | year=1997, 393 | month=10, 394 | day=2 395 | ) 396 | 397 | 398 | def test_old_datetime_object() -> None: 399 | frozen_datetime = datetime.datetime(year=1, month=7, day=12, 400 | hour=15, minute=6, second=3) 401 | with freeze_time(frozen_datetime): 402 | assert datetime.datetime.now() == frozen_datetime 403 | 404 | 405 | def test_datetime_with_locale() -> None: 406 | with temp_locale(*_dd_mm_yyyy_locales): 407 | frozen_datetime = datetime.datetime(year=2012, month=1, day=2) 408 | date_freezer = freeze_time(frozen_datetime) 409 | assert date_freezer.time_to_freeze == frozen_datetime 410 | 411 | 412 | @freeze_time("2012-01-14") 413 | def test_decorator() -> None: 414 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 415 | 416 | 417 | def test_decorator_wrapped_attribute() -> None: 418 | def to_decorate() -> None: 419 | pass 420 | 421 | wrapped = freeze_time("2014-01-14")(to_decorate) 422 | 423 | assert wrapped.__wrapped__ is to_decorate # type: ignore 424 | 425 | 426 | class Callable: # type: ignore 427 | 428 | def __call__(self, *args: Any, **kws: Any) -> Any: 429 | return (args, kws) 430 | 431 | 432 | @freeze_time("2012-01-14") 433 | class Tester: 434 | 435 | def test_the_class(self) -> None: 436 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 437 | 438 | def test_still_the_same(self) -> None: 439 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 440 | 441 | def test_class_name_preserved_by_decorator(self) -> None: 442 | assert self.__class__.__name__ == "Tester" 443 | 444 | class NotATestClass: 445 | 446 | def perform_operation(self) -> datetime.date: 447 | return datetime.date.today() 448 | 449 | @freeze_time('2001-01-01') 450 | def test_class_decorator_ignores_nested_class(self) -> None: 451 | not_a_test = self.NotATestClass() 452 | assert not_a_test.perform_operation() == datetime.date(2001, 1, 1) 453 | 454 | a_mock = Callable() # type: ignore 455 | 456 | def test_class_decorator_wraps_callable_object_py3(self) -> None: 457 | assert self.a_mock.__wrapped__.__class__ == Callable 458 | 459 | @staticmethod 460 | def helper() -> datetime.date: 461 | return datetime.date.today() 462 | 463 | def test_class_decorator_respects_staticmethod(self) -> None: 464 | assert self.helper() == datetime.date(2012, 1, 14) 465 | 466 | 467 | @freeze_time("Jan 14th, 2012") 468 | def test_nice_datetime() -> None: 469 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 470 | 471 | 472 | @freeze_time("2012-01-14") 473 | def test_datetime_date_method() -> None: 474 | now = datetime.datetime.now() 475 | assert now.date() == FakeDate(2012, 1, 14) 476 | 477 | 478 | def test_context_manager() -> None: 479 | with freeze_time("2012-01-14"): 480 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 14) 481 | assert datetime.datetime.now() != datetime.datetime(2012, 1, 14) 482 | 483 | 484 | def test_nested_context_manager() -> None: 485 | with freeze_time("2012-01-14"): 486 | with freeze_time("2012-12-25"): 487 | _assert_datetime_date_and_time_are_all_equal(datetime.datetime(2012, 12, 25)) 488 | _assert_datetime_date_and_time_are_all_equal(datetime.datetime(2012, 1, 14)) 489 | assert datetime.datetime.now() > datetime.datetime(2013, 1, 1) 490 | 491 | 492 | def _assert_datetime_date_and_time_are_all_equal(expected_datetime: datetime.datetime) -> None: 493 | assert datetime.datetime.now() == expected_datetime 494 | assert datetime.date.today() == expected_datetime.date() 495 | assert datetime.datetime.fromtimestamp(time.time()) == expected_datetime 496 | 497 | 498 | def test_nested_context_manager_with_tz_offsets() -> None: 499 | with freeze_time("2012-01-14 23:00:00", tz_offset=2): 500 | with freeze_time("2012-12-25 19:00:00", tz_offset=6): 501 | assert datetime.datetime.now() == datetime.datetime(2012, 12, 26, 1) 502 | assert datetime.date.today() == datetime.date(2012, 12, 26) 503 | # no assertion for time.time() since it's not affected by tz_offset 504 | assert datetime.datetime.now() == datetime.datetime(2012, 1, 15, 1) 505 | assert datetime.date.today() == datetime.date(2012, 1, 15) 506 | assert datetime.datetime.now() > datetime.datetime(2013, 1, 1) 507 | 508 | 509 | @freeze_time("Jan 14th, 2012") 510 | def test_isinstance_with_active() -> None: 511 | now = datetime.datetime.now() 512 | assert utils.is_fake_datetime(now) 513 | assert utils.is_fake_date(now.date()) 514 | 515 | today = datetime.date.today() 516 | assert utils.is_fake_date(today) 517 | 518 | 519 | def test_isinstance_without_active() -> None: 520 | now = datetime.datetime.now() 521 | assert isinstance(now, datetime.datetime) 522 | assert isinstance(now, datetime.date) 523 | assert isinstance(now.date(), datetime.date) 524 | 525 | today = datetime.date.today() 526 | assert isinstance(today, datetime.date) 527 | 528 | 529 | class TestUnitTestMethodDecorator(unittest.TestCase): 530 | @freeze_time('2013-04-09') 531 | def test_method_decorator_works_on_unittest(self) -> None: 532 | self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today()) 533 | 534 | @freeze_time('2013-04-09', as_kwarg='frozen_time') 535 | def test_method_decorator_works_on_unittest_kwarg_frozen_time(self, frozen_time: Any) -> None: 536 | self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today()) 537 | self.assertEqual(datetime.date(2013, 4, 9), frozen_time.time_to_freeze.date()) 538 | 539 | @freeze_time('2013-04-09', as_kwarg='hello') 540 | def test_method_decorator_works_on_unittest_kwarg_hello(self, **kwargs: Any) -> None: 541 | self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today()) 542 | self.assertEqual(datetime.date(2013, 4, 9), kwargs.get('hello').time_to_freeze.date()) # type: ignore 543 | 544 | @freeze_time(lambda: datetime.date(year=2013, month=4, day=9), as_kwarg='frozen_time') 545 | def test_method_decorator_works_on_unittest_kwarg_frozen_time_with_func(self, frozen_time: Any) -> None: 546 | self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today()) 547 | self.assertEqual(datetime.date(2013, 4, 9), frozen_time.time_to_freeze.date()) 548 | 549 | 550 | @freeze_time('2013-04-09') 551 | class TestUnitTestClassDecorator(unittest.TestCase): 552 | 553 | @classmethod 554 | def setUpClass(cls) -> None: 555 | assert datetime.date(2013, 4, 9) == datetime.date.today() 556 | 557 | def setUp(self) -> None: 558 | self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today()) 559 | 560 | def tearDown(self) -> None: 561 | self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today()) 562 | 563 | @classmethod 564 | def tearDownClass(cls) -> None: 565 | assert datetime.date(2013, 4, 9) == datetime.date.today() 566 | 567 | def test_class_decorator_works_on_unittest(self) -> None: 568 | self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today()) 569 | 570 | def test_class_name_preserved_by_decorator(self) -> None: 571 | self.assertEqual(self.__class__.__name__, "TestUnitTestClassDecorator") 572 | 573 | 574 | @freeze_time('2013-04-09') 575 | class TestUnitTestClassDecoratorWithNoSetUpOrTearDown(unittest.TestCase): 576 | def test_class_decorator_works_on_unittest(self) -> None: 577 | self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today()) 578 | 579 | 580 | class TestUnitTestClassDecoratorSubclass(TestUnitTestClassDecorator): 581 | @classmethod 582 | def setUpClass(cls) -> None: 583 | # the super() call can fail if the class decoration was done wrong 584 | super().setUpClass() 585 | 586 | @classmethod 587 | def tearDownClass(cls) -> None: 588 | # the super() call can fail if the class decoration was done wrong 589 | super().tearDownClass() 590 | 591 | def test_class_name_preserved_by_decorator(self) -> None: 592 | self.assertEqual(self.__class__.__name__, 593 | "TestUnitTestClassDecoratorSubclass") 594 | 595 | 596 | class BaseInheritanceFreezableTests(unittest.TestCase): 597 | @classmethod 598 | def setUpClass(cls) -> None: 599 | pass 600 | 601 | @classmethod 602 | def tearDownClass(cls) -> None: 603 | pass 604 | 605 | 606 | class UnfrozenInheritedTests(BaseInheritanceFreezableTests): 607 | def test_time_is_not_frozen(self) -> None: 608 | # In this class, time should not be frozen - and the below decorated 609 | # class shouldn't affect that 610 | self.assertNotEqual(datetime.date(2013, 4, 9), datetime.date.today()) 611 | 612 | 613 | @freeze_time('2013-04-09') 614 | class FrozenInheritedTests(BaseInheritanceFreezableTests): 615 | def test_time_is_frozen(self) -> None: 616 | # In this class, time should be frozen 617 | self.assertEqual(datetime.date(2013, 4, 9), datetime.date.today()) 618 | 619 | 620 | class TestOldStyleClasses: 621 | def test_direct_method(self) -> None: 622 | # Make sure old style classes (not inheriting from object) is supported 623 | @freeze_time('2013-04-09') 624 | class OldStyleClass: 625 | def method(self) -> datetime.date: 626 | return datetime.date.today() 627 | 628 | assert OldStyleClass().method() == datetime.date(2013, 4, 9) 629 | 630 | def test_inherited_method(self) -> None: 631 | class OldStyleBaseClass: 632 | def inherited_method(self) -> datetime.date: 633 | return datetime.date.today() 634 | 635 | @freeze_time('2013-04-09') 636 | class OldStyleClass(OldStyleBaseClass): 637 | pass 638 | 639 | assert OldStyleClass().inherited_method() == datetime.date(2013, 4, 9) 640 | 641 | 642 | def test_min_and_max() -> None: 643 | freezer = freeze_time("2012-01-14") 644 | real_datetime = datetime.datetime 645 | real_date = datetime.date 646 | 647 | freezer.start() 648 | assert datetime.datetime.min.__class__ == FakeDatetime 649 | assert datetime.datetime.max.__class__ == FakeDatetime 650 | assert datetime.date.min.__class__ == FakeDate 651 | assert datetime.date.max.__class__ == FakeDate 652 | assert datetime.datetime.min.__class__ != real_datetime 653 | assert datetime.datetime.max.__class__ != real_datetime 654 | assert datetime.date.min.__class__ != real_date 655 | assert datetime.date.max.__class__ != real_date 656 | 657 | freezer.stop() 658 | assert datetime.datetime.min.__class__ == datetime.datetime 659 | assert datetime.datetime.max.__class__ == datetime.datetime 660 | assert datetime.date.min.__class__ == datetime.date 661 | assert datetime.date.max.__class__ == datetime.date 662 | assert datetime.datetime.min.__class__ != FakeDatetime 663 | assert datetime.datetime.max.__class__ != FakeDatetime 664 | assert datetime.date.min.__class__ != FakeDate 665 | assert datetime.date.max.__class__ != FakeDate 666 | 667 | 668 | @freeze_time("2014-07-30T01:00:00Z") 669 | def test_freeze_with_timezone_aware_datetime_in_utc() -> None: 670 | """ 671 | utcnow() should always return a timezone naive datetime 672 | """ 673 | utc_now = datetime.datetime.utcnow() 674 | assert utc_now.tzinfo is None 675 | 676 | 677 | @freeze_time("1970-01-01T00:00:00-04:00") 678 | def test_freeze_with_timezone_aware_datetime_in_non_utc() -> None: 679 | """ 680 | we expect the system to behave like a system with UTC-4 timezone 681 | at the beginning of the Epoch (wall clock should be 4 hrs late) 682 | """ 683 | utc_now = datetime.datetime.utcnow() 684 | assert utc_now.tzinfo is None 685 | assert utc_now == datetime.datetime(1970, 1, 1, 4) 686 | 687 | 688 | @freeze_time('2015-01-01') 689 | def test_time_with_nested() -> None: 690 | from time import time 691 | first = 1420070400.0 692 | second = 1420070760.0 693 | 694 | assert time() == first 695 | with freeze_time('2015-01-01T00:06:00'): 696 | assert time() == second 697 | 698 | 699 | @pytest.mark.parametrize("func_name", 700 | ("monotonic", "perf_counter") 701 | ) 702 | def test_monotonic_with_nested(func_name: str) -> None: 703 | __import__("time", fromlist=[func_name]) 704 | invoke_time_func: Callable[[], float] = lambda: getattr(time, func_name)() 705 | 706 | with freeze_time('2015-01-01') as frozen_datetime_1: 707 | initial_t1 = invoke_time_func() 708 | with freeze_time('2015-12-25') as frozen_datetime_2: 709 | initial_t2 = invoke_time_func() 710 | frozen_datetime_2.tick() 711 | assert invoke_time_func() == initial_t2 + 1 712 | assert invoke_time_func() == initial_t1 713 | frozen_datetime_1.tick() 714 | assert invoke_time_func() == initial_t1 + 1 715 | 716 | 717 | def test_should_use_real_time() -> None: 718 | frozen = datetime.datetime(2015, 3, 5) 719 | expected_frozen = 1425513600.0 720 | # TODO: local time seems to leak the local timezone, so this test fails in CI 721 | # expected_frozen_local = (2015, 3, 5, 1, 0, 0, 3, 64, -1) 722 | expected_frozen_gmt = (2015, 3, 5, 0, 0, 0, 3, 64, -1) 723 | expected_clock = 0 724 | 725 | from freezegun import api 726 | api.call_stack_inspection_limit = 100 # just to increase coverage 727 | 728 | timestamp_to_convert = 1579602312 729 | time_tuple = time.gmtime(timestamp_to_convert) 730 | 731 | with freeze_time(frozen): 732 | assert time.time() == expected_frozen 733 | # assert time.localtime() == expected_frozen_local 734 | assert time.gmtime() == expected_frozen_gmt 735 | if HAS_CLOCK: 736 | assert time.clock() == expected_clock # type: ignore[attr-defined] 737 | if HAS_TIME_NS: 738 | assert time.time_ns() == expected_frozen * 1e9 739 | 740 | assert calendar.timegm(time.gmtime()) == expected_frozen 741 | assert calendar.timegm(time_tuple) == timestamp_to_convert 742 | 743 | with freeze_time(frozen, ignore=['_pytest']): 744 | assert time.time() != expected_frozen 745 | # assert time.localtime() != expected_frozen_local 746 | assert time.gmtime() != expected_frozen_gmt 747 | if HAS_CLOCK: 748 | assert time.clock() != expected_clock # type: ignore[attr-defined] 749 | if HAS_TIME_NS: 750 | assert time.time_ns() != expected_frozen * 1e9 751 | 752 | assert calendar.timegm(time.gmtime()) != expected_frozen 753 | assert calendar.timegm(time_tuple) == timestamp_to_convert 754 | 755 | 756 | @pytest.mark.skipif(not HAS_TIME_NS, 757 | reason="time.time_ns is present only on 3.7 and above") 758 | def test_time_ns() -> None: 759 | freezer = freeze_time("2012-01-14") 760 | local_time = datetime.datetime(2012, 1, 14) 761 | utc_time = local_time - datetime.timedelta(seconds=time.timezone) 762 | expected_timestamp = time.mktime(utc_time.timetuple()) 763 | 764 | with freezer: 765 | assert time.time() == expected_timestamp 766 | assert time.time_ns() == expected_timestamp * 1e9 767 | 768 | assert time.time() != expected_timestamp 769 | assert time.time_ns() != expected_timestamp * 1e9 770 | 771 | 772 | @pytest.mark.skipif(not HAS_TIME_NS, 773 | reason="time.time_ns is present only on 3.7 and above") 774 | def test_time_ns_with_microseconds() -> None: 775 | freezer = freeze_time("2024-03-20 18:21:10.12345") 776 | 777 | with freezer: 778 | assert time.time_ns() == 1710958870123450112 779 | 780 | assert time.time_ns() != 1710958870123450112 781 | 782 | 783 | def test_compare_datetime_and_time_with_timezone(monkeypatch: pytest.MonkeyPatch) -> None: 784 | """ 785 | Compare the result of datetime.datetime.now() and time.time() in a non-UTC timezone. These 786 | should be consistent. 787 | """ 788 | try: 789 | with monkeypatch.context() as m, freeze_time("1970-01-01 00:00:00"): 790 | m.setenv("TZ", "Europe/Berlin") 791 | time.tzset() 792 | 793 | now = datetime.datetime.now() 794 | assert now == datetime.datetime.fromtimestamp(time.time()) 795 | assert now == datetime.datetime.utcfromtimestamp(time.time()) 796 | assert now == datetime.datetime.utcnow() 797 | assert now.timestamp() == time.time() 798 | finally: 799 | time.tzset() # set the timezone back to what is was before 800 | 801 | 802 | def test_timestamp_with_tzoffset() -> None: 803 | with freeze_time("2000-01-01", tz_offset=6): 804 | utcnow = datetime.datetime(2000, 1, 1, 0) 805 | nowtz = datetime.datetime(2000, 1, 1, 0, tzinfo=UTC) 806 | now = datetime.datetime(2000, 1, 1, 6) 807 | assert now == datetime.datetime.now() 808 | assert now == datetime.datetime.fromtimestamp(time.time()) 809 | assert now.timestamp() == time.time() 810 | assert nowtz.timestamp() == time.time() 811 | 812 | assert utcnow == datetime.datetime.utcfromtimestamp(time.time()) 813 | assert utcnow == datetime.datetime.utcnow() 814 | 815 | @pytest.mark.skip("timezone handling is currently incorrect") 816 | def test_datetime_in_timezone(monkeypatch: pytest.MonkeyPatch) -> None: 817 | """ 818 | It is assumed that the argument passed to freeze_time is in UTC, unless explicitly indicated 819 | otherwise. Therefore datetime.now() should return the frozen time with an offset. 820 | """ 821 | try: 822 | with monkeypatch.context() as m, freeze_time("1970-01-01 00:00:00"): 823 | m.setenv("TZ", "Europe/Berlin") 824 | time.tzset() 825 | 826 | assert datetime.datetime.now() == datetime.datetime(1970, 1, 1, 1, 0, 0) 827 | finally: 828 | time.tzset() # set the timezone back to what is was before 829 | -------------------------------------------------------------------------------- /tests/test_errors.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import datetime 3 | import sys 4 | from typing import Any, Iterator 5 | 6 | import pytest 7 | from freezegun import freeze_time 8 | 9 | 10 | class ModuleWithError: 11 | """ 12 | A module that triggers an error on __dir__ access. 13 | 14 | This could happen with modules that overrides its __dir__ method and 15 | performing non standard operations. 16 | 17 | One such example is IPython which adds shim modules when certain packages 18 | are imported. (Eg. `import IPython.html`) This leads to errors upon 19 | activating freezegun if the modules being shimmed are not installed. 20 | 21 | See: https://github.com/ipython/ipython/blob/5.8.0/IPython/utils/shimmodule.py#L75 22 | """ 23 | __name__ = 'module_with_error' 24 | __dict__ = {} 25 | 26 | def __init__(self, error_type: Any): 27 | self.error_triggered = False 28 | self.error_type = error_type 29 | 30 | def __dir__(self) -> Any: 31 | try: 32 | raise self.error_type() 33 | finally: 34 | self.error_triggered = True 35 | 36 | 37 | @contextlib.contextmanager 38 | def assert_module_with_raised_error(error_type: Any) -> Iterator[None]: 39 | """Install a module into sys.modules that raises an error upon invoking 40 | __dir__.""" 41 | module = sys.modules['module_with_error'] = ModuleWithError(error_type) # type: ignore 42 | 43 | try: 44 | yield 45 | finally: 46 | del sys.modules['module_with_error'] 47 | 48 | assert module.error_triggered 49 | 50 | 51 | @pytest.mark.parametrize('error_type', [ImportError, TypeError]) 52 | def test_ignore_errors_in_start(error_type: Any) -> None: 53 | with assert_module_with_raised_error(error_type): 54 | freezer = freeze_time(datetime.datetime(2019, 1, 11, 9, 34)) 55 | 56 | try: 57 | freezer.start() 58 | finally: 59 | freezer.stop() 60 | -------------------------------------------------------------------------------- /tests/test_import_alias.py: -------------------------------------------------------------------------------- 1 | from freezegun import freeze_time 2 | from datetime import datetime as datetime_aliased 3 | from time import time as time_aliased 4 | 5 | 6 | @freeze_time("1980-01-01") 7 | def test_datetime_alias() -> None: 8 | assert datetime_aliased.now() == datetime_aliased(1980, 1, 1) 9 | 10 | 11 | @freeze_time("1970-01-01") 12 | def test_time_alias() -> None: 13 | assert time_aliased() == 0.0 14 | 15 | 16 | @freeze_time('2013-04-09') 17 | class TestCallOtherFuncInTestClassDecoratorWithAlias: 18 | 19 | def test_calls_other_method(self) -> None: 20 | assert datetime_aliased(2013, 4, 9) == datetime_aliased.today() 21 | self.some_other_func() 22 | assert datetime_aliased(2013, 4, 9) == datetime_aliased.today() 23 | 24 | def some_other_func(self) -> None: 25 | pass 26 | -------------------------------------------------------------------------------- /tests/test_operations.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import fractions 3 | import pytest 4 | from freezegun import freeze_time 5 | from dateutil.relativedelta import relativedelta 6 | from datetime import timedelta, tzinfo 7 | from tests import utils 8 | from typing import Any, Union 9 | 10 | 11 | @freeze_time("2012-01-14") 12 | def test_addition() -> None: 13 | now = datetime.datetime.now() 14 | later = now + datetime.timedelta(days=1) 15 | other_later = now + relativedelta(days=1) 16 | assert utils.is_fake_datetime(later) 17 | assert utils.is_fake_datetime(other_later) 18 | 19 | today = datetime.date.today() 20 | tomorrow = today + datetime.timedelta(days=1) 21 | other_tomorrow = today + relativedelta(days=1) 22 | assert utils.is_fake_date(tomorrow) 23 | assert utils.is_fake_date(other_tomorrow) 24 | 25 | 26 | @freeze_time("2012-01-14") 27 | def test_subtraction() -> None: 28 | now = datetime.datetime.now() 29 | before = now - datetime.timedelta(days=1) 30 | other_before = now - relativedelta(days=1) 31 | how_long = now - before 32 | assert utils.is_fake_datetime(before) 33 | assert utils.is_fake_datetime(other_before) 34 | assert isinstance(how_long, datetime.timedelta) 35 | 36 | today = datetime.date.today() 37 | yesterday = today - datetime.timedelta(days=1) 38 | other_yesterday = today - relativedelta(days=1) 39 | how_long = today - yesterday 40 | assert utils.is_fake_date(yesterday) 41 | assert utils.is_fake_date(other_yesterday) 42 | assert isinstance(how_long, datetime.timedelta) 43 | 44 | 45 | @freeze_time("2012-01-14") 46 | def test_datetime_timezone_none() -> None: 47 | now = datetime.datetime.now(tz=None) 48 | assert now == datetime.datetime(2012, 1, 14) 49 | 50 | 51 | class GMT5(tzinfo): 52 | def utcoffset(self, dt: Any) -> timedelta: 53 | return timedelta(hours=5) 54 | 55 | def tzname(self, dt: Any) -> str: 56 | return "GMT +5" 57 | 58 | def dst(self, dt: Any) -> timedelta: 59 | return timedelta(0) 60 | 61 | 62 | @freeze_time("2012-01-14 2:00:00") 63 | def test_datetime_timezone_real() -> None: 64 | now = datetime.datetime.now(tz=GMT5()) 65 | assert now == datetime.datetime(2012, 1, 14, 7, tzinfo=GMT5()) 66 | assert now.utcoffset() == timedelta(0, 60 * 60 * 5) 67 | 68 | 69 | @freeze_time("2012-01-14 2:00:00", tz_offset=-4) 70 | def test_datetime_timezone_real_with_offset() -> None: 71 | now = datetime.datetime.now(tz=GMT5()) 72 | assert now == datetime.datetime(2012, 1, 14, 3, tzinfo=GMT5()) 73 | assert now.utcoffset() == timedelta(0, 60 * 60 * 5) 74 | 75 | 76 | @freeze_time("2012-01-14 00:00:00") 77 | def test_astimezone() -> None: 78 | now = datetime.datetime.now(tz=GMT5()) 79 | converted = now.astimezone(GMT5()) 80 | assert utils.is_fake_datetime(converted) 81 | 82 | 83 | @freeze_time("2012-01-14 00:00:00") 84 | def test_astimezone_tz_none() -> None: 85 | now = datetime.datetime.now(tz=GMT5()) 86 | converted = now.astimezone() 87 | assert utils.is_fake_datetime(converted) 88 | 89 | 90 | @freeze_time("2012-01-14 00:00:00") 91 | def test_replace() -> None: 92 | now = datetime.datetime.now() 93 | modified_time = now.replace(year=2013) 94 | assert utils.is_fake_datetime(modified_time) 95 | 96 | today = datetime.date.today() 97 | modified_date = today.replace(year=2013) 98 | assert utils.is_fake_date(modified_date) 99 | 100 | 101 | @freeze_time("Jan 14th, 2020", auto_tick_seconds=15) 102 | def test_auto_tick() -> None: 103 | first_time = datetime.datetime.now() 104 | auto_incremented_time = datetime.datetime.now() 105 | assert first_time + datetime.timedelta(seconds=15) == auto_incremented_time 106 | 107 | 108 | @pytest.mark.parametrize( 109 | "tick,expected_diff", 110 | ( 111 | (datetime.timedelta(milliseconds=1500), 1.5), 112 | (1, 1), 113 | (1.5, 1.5), 114 | (fractions.Fraction(3, 2), 1.5), 115 | ) 116 | ) 117 | def test_auto_and_manual_tick( 118 | tick: Union[ 119 | datetime.timedelta, 120 | int, 121 | float, 122 | # fractions.Fraction, 123 | # Fraction works at runtime, but not at type-checking time 124 | # cf. https://peps.python.org/pep-0484/#the-numeric-tower 125 | ], 126 | expected_diff: float 127 | ) -> None: 128 | first_time = datetime.datetime(2020, 1, 14, 0, 0, 0, 1) 129 | 130 | with freeze_time(first_time, auto_tick_seconds=2) as frozen_time: 131 | frozen_time.tick(tick) 132 | incremented_time = datetime.datetime.now() 133 | expected_time = first_time + datetime.timedelta(seconds=expected_diff) 134 | assert incremented_time == expected_time 135 | 136 | expected_time += datetime.timedelta(seconds=2) # auto_tick_seconds 137 | 138 | frozen_time.tick(tick) 139 | incremented_time = datetime.datetime.now() 140 | expected_time += datetime.timedelta(seconds=expected_diff) 141 | assert incremented_time == expected_time 142 | -------------------------------------------------------------------------------- /tests/test_pickle.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import pickle 3 | from freezegun import freeze_time 4 | 5 | 6 | def assert_pickled_datetimes_equal_original() -> None: 7 | min_datetime = datetime.datetime.min 8 | max_datetime = datetime.datetime.max 9 | min_date = datetime.date.min 10 | max_date = datetime.date.max 11 | now = datetime.datetime.now() 12 | today = datetime.date.today() 13 | utc_now = datetime.datetime.utcnow() 14 | assert pickle.loads(pickle.dumps(min_datetime)) == min_datetime 15 | assert pickle.loads(pickle.dumps(max_datetime)) == max_datetime 16 | assert pickle.loads(pickle.dumps(min_date)) == min_date 17 | assert pickle.loads(pickle.dumps(max_date)) == max_date 18 | assert pickle.loads(pickle.dumps(now)) == now 19 | assert pickle.loads(pickle.dumps(today)) == today 20 | assert pickle.loads(pickle.dumps(utc_now)) == utc_now 21 | 22 | 23 | def test_pickle() -> None: 24 | freezer = freeze_time("2012-01-14") 25 | 26 | freezer.start() 27 | assert_pickled_datetimes_equal_original() 28 | 29 | freezer.stop() 30 | assert_pickled_datetimes_equal_original() 31 | 32 | 33 | def test_pickle_real_datetime() -> None: 34 | real_datetime = datetime.datetime(1970, 2, 1) 35 | pickle.loads(pickle.dumps(real_datetime)) == real_datetime 36 | 37 | freezer = freeze_time("1970-01-01") 38 | freezer.start() 39 | fake_datetime = datetime.datetime.now() 40 | assert pickle.loads(pickle.dumps(fake_datetime)) == fake_datetime 41 | pickle.loads(pickle.dumps(real_datetime)) 42 | freezer.stop() 43 | 44 | assert pickle.loads(pickle.dumps(fake_datetime)) == fake_datetime 45 | assert pickle.loads(pickle.dumps(real_datetime)) == real_datetime 46 | 47 | 48 | def test_pickle_real_date() -> None: 49 | real_date = datetime.date(1970, 2, 1) 50 | assert pickle.loads(pickle.dumps(real_date)) == real_date 51 | 52 | freezer = freeze_time("1970-01-01") 53 | freezer.start() 54 | fake_date = datetime.datetime.now() 55 | assert pickle.loads(pickle.dumps(fake_date)) == fake_date 56 | pickle.loads(pickle.dumps(real_date)) 57 | freezer.stop() 58 | 59 | assert pickle.loads(pickle.dumps(fake_date)) == fake_date 60 | assert pickle.loads(pickle.dumps(real_date)) == real_date 61 | -------------------------------------------------------------------------------- /tests/test_sqlite3.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from freezegun import freeze_time 3 | import sqlite3 4 | 5 | 6 | @freeze_time("2013-01-01") 7 | def test_fake_datetime_select() -> None: 8 | db = sqlite3.connect("/tmp/foo") 9 | db.execute("""select ?""", (datetime.datetime.now(),)) 10 | 11 | 12 | @freeze_time("2013-01-01") 13 | def test_fake_date_select() -> None: 14 | db = sqlite3.connect("/tmp/foo") 15 | db.execute("""select ?""", (datetime.date.today(),)) 16 | -------------------------------------------------------------------------------- /tests/test_ticking.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import sys 3 | import time 4 | 5 | from unittest import mock 6 | import pytest 7 | 8 | from freezegun import freeze_time 9 | from tests import utils 10 | 11 | @utils.cpython_only 12 | def test_ticking_datetime() -> None: 13 | with freeze_time("Jan 14th, 2012", tick=True): 14 | time.sleep(0.001) # Deal with potential clock resolution problems 15 | assert datetime.datetime.now() > datetime.datetime(2012, 1, 14) 16 | 17 | 18 | @pytest.mark.skipif(not hasattr(time, "clock"), 19 | reason="time.clock was removed in Python 3.8") 20 | @utils.cpython_only 21 | def test_ticking_time_clock() -> None: 22 | with freeze_time('2012-01-14 03:21:34', tick=True): 23 | first = time.clock() # type: ignore 24 | time.sleep(0.001) # Deal with potential clock resolution problems 25 | with freeze_time('2012-01-14 03:21:35', tick=True): 26 | second = time.clock() # type: ignore 27 | time.sleep(0.001) # Deal with potential clock resolution problems 28 | 29 | with freeze_time('2012-01-14 03:21:36', tick=True): 30 | third = time.clock() # type: ignore 31 | time.sleep(0.001) 32 | 33 | # Rewind time backwards 34 | with freeze_time('2012-01-14 03:20:00', tick=True): 35 | fourth = time.clock() # type: ignore 36 | time.sleep(0.001) 37 | fifth = time.clock() # type: ignore 38 | 39 | assert first > 0 40 | assert second > first 41 | assert second > 1 42 | assert third > second 43 | assert third > 2 44 | 45 | assert third > fourth 46 | assert second > fourth 47 | assert first > fourth 48 | 49 | assert fifth > fourth 50 | 51 | 52 | @utils.cpython_only 53 | def test_ticking_date() -> None: 54 | with freeze_time("Jan 14th, 2012, 23:59:59.9999999", tick=True): 55 | time.sleep(0.001) # Deal with potential clock resolution problems 56 | assert datetime.date.today() == datetime.date(2012, 1, 15) 57 | 58 | 59 | @utils.cpython_only 60 | def test_ticking_time() -> None: 61 | with freeze_time("Jan 14th, 2012, 23:59:59", tick=True): 62 | time.sleep(0.001) # Deal with potential clock resolution problems 63 | assert time.time() > 1326585599.0 64 | 65 | 66 | @utils.cpython_only 67 | def test_ticking_tick() -> None: 68 | with freeze_time("Jan 14th, 2012, 23:59:59", tick=True) as ft: 69 | ft.tick(61) 70 | time.sleep(0.001) # Deal with potential clock resolution problems 71 | assert datetime.datetime.now().replace( 72 | second=0, microsecond=0 73 | ) == datetime.datetime(2012, 1, 15, 0, 1, 0) 74 | 75 | ft.tick(delta=datetime.timedelta(minutes=2)) 76 | time.sleep(0.001) # Deal with potential clock resolution problems 77 | assert datetime.datetime.now().replace( 78 | second=0, microsecond=0 79 | ) == datetime.datetime(2012, 1, 15, 0, 3, 0) 80 | 81 | 82 | @utils.cpython_only 83 | def test_ticking_move_to() -> None: 84 | with freeze_time("Jan 14th, 2012, 23:59:59", tick=True) as ft: 85 | ft.move_to("Jan 15th, 2012, 00:59:59.999999") 86 | time.sleep(0.001) # Deal with potential clock resolution problems 87 | assert datetime.datetime.now().replace(second=0, microsecond=0) == datetime.datetime(2012, 1, 15, 1, 0, 0) 88 | 89 | 90 | @utils.cpython_only_mark 91 | @pytest.mark.parametrize("func_name", 92 | ("monotonic", "monotonic_ns", "perf_counter", "perf_counter_ns"), 93 | ) 94 | def test_ticking_monotonic(func_name: str) -> None: 95 | if sys.version_info[0:2] >= (3, 7): 96 | # All of these functions should exist in Python 3.7+, so this test helps 97 | # avoid inappropriate skipping when we've accidentally typo-ed the name 98 | # of one of these functions 😅 99 | assert hasattr(time, func_name) 100 | else: 101 | if not hasattr(time, func_name): 102 | pytest.skip( 103 | "time.%s does not exist in the current Python version" % func_name) 104 | 105 | func = getattr(time, func_name) 106 | with freeze_time("Jan 14th, 2012, 23:59:59", tick=True): 107 | initial = func() 108 | time.sleep(0.001) # Deal with potential clock resolution problems 109 | assert func() > initial 110 | 111 | 112 | @mock.patch('freezegun.api._is_cpython', False) 113 | def test_pypy_compat() -> None: 114 | try: 115 | freeze_time("Jan 14th, 2012, 23:59:59", tick=True) 116 | except SystemError: 117 | pass 118 | else: 119 | raise AssertionError("tick=True should error on non-CPython") 120 | 121 | 122 | @mock.patch('freezegun.api._is_cpython', True) 123 | def test_non_pypy_compat() -> None: 124 | try: 125 | freeze_time("Jan 14th, 2012, 23:59:59", tick=True) 126 | except Exception: 127 | raise AssertionError("tick=True should not error on CPython") 128 | -------------------------------------------------------------------------------- /tests/test_utils.py: -------------------------------------------------------------------------------- 1 | from importlib import reload 2 | from unittest import SkipTest, mock 3 | 4 | from freezegun import api 5 | from tests import utils 6 | 7 | 8 | @mock.patch('platform.python_implementation', lambda: 'CPython') 9 | def test_should_not_skip_cpython() -> None: 10 | reload(api) 11 | reload(utils) 12 | function_mock = mock.MagicMock(__name__='function') 13 | try: 14 | utils.cpython_only(function_mock)() 15 | except SkipTest: 16 | raise AssertionError("Test was skipped in CPython") 17 | assert function_mock.called 18 | 19 | 20 | @mock.patch('platform.python_implementation', lambda: 'not-CPython') 21 | def test_should_skip_non_cpython() -> None: 22 | reload(api) 23 | reload(utils) 24 | function_mock = mock.MagicMock(__name__='function', skipped=False) 25 | try: 26 | utils.cpython_only(function_mock)() 27 | except SkipTest: 28 | function_mock.skipped = True 29 | assert not function_mock.called 30 | assert function_mock.skipped 31 | -------------------------------------------------------------------------------- /tests/test_uuid.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import uuid 3 | from typing import Any 4 | 5 | from freezegun import freeze_time 6 | 7 | 8 | def time_from_uuid(value: Any) -> datetime.datetime: 9 | """ 10 | Converts an UUID(1) to it's datetime value 11 | """ 12 | uvalue = value if isinstance(value, uuid.UUID) else uuid.UUID(value) 13 | assert uvalue.version == 1 14 | return (datetime.datetime(1582, 10, 15) + 15 | datetime.timedelta(microseconds=uvalue.time // 10)) 16 | 17 | 18 | def test_uuid1_future() -> None: 19 | """ 20 | Test that we can go back in time after setting a future date. 21 | Normally UUID1 would disallow this, since it keeps track of 22 | the _last_timestamp, but we override that now. 23 | """ 24 | future_target = datetime.datetime(2056, 2, 6, 14, 3, 21) 25 | with freeze_time(future_target): 26 | assert time_from_uuid(uuid.uuid1()) == future_target 27 | 28 | past_target = datetime.datetime(1978, 7, 6, 23, 6, 31) 29 | with freeze_time(past_target): 30 | assert time_from_uuid(uuid.uuid1()) == past_target 31 | 32 | 33 | def test_uuid1_past() -> None: 34 | """ 35 | Test that we can go forward in time after setting some time in the past. 36 | This is simply the opposite of test_uuid1_future() 37 | """ 38 | past_target = datetime.datetime(1978, 7, 6, 23, 6, 31) 39 | with freeze_time(past_target): 40 | assert time_from_uuid(uuid.uuid1()) == past_target 41 | 42 | future_target = datetime.datetime(2056, 2, 6, 14, 3, 21) 43 | with freeze_time(future_target): 44 | assert time_from_uuid(uuid.uuid1()) == future_target 45 | -------------------------------------------------------------------------------- /tests/test_warnings.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import datetime 3 | import sys 4 | import types 5 | import warnings 6 | from typing import Iterator 7 | 8 | from freezegun import freeze_time 9 | 10 | 11 | class ModuleWithWarning: 12 | """ 13 | A module that triggers warnings on attribute access. 14 | 15 | This does not happen with regular modules, there has to be a bit of lazy 16 | module magic going on in order for this to happen. 17 | 18 | Examples of modules that uses this pattern in real projects can be found at: 19 | 20 | py.code - the compiler package import causes a warning to be emitted: 21 | https://github.com/pytest-dev/py/blob/67987e26aadddbbe7d1ec76c16ea9be346ae9811/py/__init__.py 22 | https://github.com/pytest-dev/py/blob/67987e26aadddbbe7d1ec76c16ea9be346ae9811/py/_code/_assertionold.py#L3 23 | 24 | celery.task - the sets module is listed in __all__ in celery.task and freeze_time accesses it: 25 | https://github.com/celery/celery/blob/46c92025cdec07a4a30ad44901cf66cb27346638/celery/task/__init__.py 26 | https://github.com/celery/celery/blob/46c92025cdec07a4a30ad44901cf66cb27346638/celery/task/sets.py 27 | """ 28 | __name__ = 'module_with_warning' 29 | __dict__ = {} 30 | warning_triggered = False 31 | counter = 0 32 | 33 | @property 34 | def attribute_that_emits_a_warning(self) -> None: 35 | # Use unique warning messages to avoid messages being only reported once 36 | self.__class__.counter += 1 37 | warnings.warn(f'this is test warning #{self.__class__.counter}') 38 | self.warning_triggered = True 39 | 40 | 41 | @contextlib.contextmanager 42 | def assert_module_with_emitted_warning() -> Iterator[None]: 43 | """Install a module that triggers warnings into sys.modules and ensure the 44 | warning was triggered in the with-block. """ 45 | module = sys.modules['module_with_warning'] = ModuleWithWarning() # type: ignore 46 | 47 | try: 48 | yield 49 | finally: 50 | del sys.modules['module_with_warning'] 51 | 52 | assert module.warning_triggered 53 | 54 | 55 | @contextlib.contextmanager 56 | def assert_no_warnings() -> Iterator[None]: 57 | """A context manager that makes sure no warnings was emitted.""" 58 | with warnings.catch_warnings(record=True) as caught_warnings: 59 | warnings.filterwarnings('always') 60 | yield 61 | assert not caught_warnings 62 | 63 | 64 | def test_ignore_warnings_in_start() -> None: 65 | """Make sure that modules being introspected in start() does not emit warnings.""" 66 | with assert_module_with_emitted_warning(): 67 | freezer = freeze_time(datetime.datetime(2016, 10, 27, 9, 56)) 68 | 69 | try: 70 | with assert_no_warnings(): 71 | freezer.start() 72 | 73 | finally: 74 | freezer.stop() 75 | 76 | 77 | def test_ignore_warnings_in_stop() -> None: 78 | """Make sure that modules that was loaded after start() does not trigger 79 | warnings in stop()""" 80 | freezer = freeze_time(datetime.datetime(2016, 10, 27, 9, 56)) 81 | freezer.start() 82 | 83 | with assert_module_with_emitted_warning(): 84 | with assert_no_warnings(): 85 | freezer.stop() 86 | -------------------------------------------------------------------------------- /tests/utils.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | from typing import Any, Callable, TYPE_CHECKING, TypeVar 3 | from unittest import SkipTest 4 | 5 | from freezegun.api import FakeDate, FakeDatetime, _is_cpython 6 | 7 | import pytest 8 | 9 | if TYPE_CHECKING: 10 | from typing_extensions import ParamSpec 11 | 12 | P = ParamSpec("P") 13 | 14 | T = TypeVar("T") 15 | 16 | 17 | def is_fake_date(obj: Any) -> bool: 18 | return obj.__class__ is FakeDate 19 | 20 | 21 | def is_fake_datetime(obj: Any) -> bool: 22 | return obj.__class__ is FakeDatetime 23 | 24 | 25 | cpython_only_mark = pytest.mark.skipif( 26 | not _is_cpython, 27 | reason="Requires CPython") 28 | 29 | 30 | def cpython_only(func: "Callable[P, T]") -> "Callable[P, T]": 31 | @wraps(func) 32 | def wrapper(*args: "P.args", **kwargs: "P.kwargs") -> T: 33 | if not _is_cpython: 34 | raise SkipTest("Requires CPython") 35 | return func(*args, **kwargs) 36 | return wrapper 37 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # Tox (http://tox.testrun.org/) is a tool for running tests 2 | # in multiple virtualenvs. This configuration file will run the 3 | # test suite on all supported python versions. To use it, "pip install tox" 4 | # and then run "tox" from this directory. 5 | 6 | [tox] 7 | envlist = py37, py38, py39, py310, py311, py312, pypy3, mypy 8 | 9 | [testenv] 10 | commands = pytest --cov {posargs} 11 | deps = -rrequirements.txt 12 | 13 | [testenv:mypy] 14 | deps = 15 | mypy 16 | commands = mypy freezegun tests --install-types --non-interactive 17 | --------------------------------------------------------------------------------