codecov-io: # [Codecov](https://codecov.io/gh/briancurtin/deprecation/pull/47?src=pr&el=h1) Report
> Merging [#47](https://codecov.io/gh/briancurtin/deprecation/pull/47?src=pr&el=desc) into [master](https://codecov.io/gh/briancurtin/deprecation/commit/264e8cd469cdcfc793afedb80d224f48d350a360?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/briancurtin/deprecation/pull/47/graphs/tree.svg?width=650&token=6w8zqu2CEQ&height=150&src=pr)](https://codecov.io/gh/briancurtin/deprecation/pull/47?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##           master    #47   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           1      1           
  Lines          83     92    +9     
  Branches       16     20    +4     
=====================================
+ Hits           83     92    +9
```


| [Impacted Files](https://codecov.io/gh/briancurtin/deprecation/pull/47?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [deprecation.py](https://codecov.io/gh/briancurtin/deprecation/pull/47/diff?src=pr&el=tree#diff-ZGVwcmVjYXRpb24ucHk=) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/briancurtin/deprecation/pull/47?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/briancurtin/deprecation/pull/47?src=pr&el=footer). Last update [264e8cd...85738a1](https://codecov.io/gh/briancurtin/deprecation/pull/47?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

gabbard: @aakum03 : I have not yet reviewed the tests. I will look at them on Monday.
aakum03: Made the requested changes to `deprecation.py`
briancurtin: Thanks a lot for this PR, and thanks @gabbard for having a look as well. I'll give it a full review in the next day or so.
briancurtin: Thanks for the PR! I'm going to merge it and then will put together a release probably later today or early tomorrow, then I'll close out #46 once it's available.
diff --git a/.gitignore b/.gitignore
index 72364f9..0627077 100644
--- a/.gitignore
+++ b/.gitignore
@@ -87,3 +87,6 @@ ENV/
 
 # Rope project settings
 .ropeproject
+
+#Pycharm Files
+.idea/
diff --git a/deprecation.py b/deprecation.py
index ccb7f66..e7903b4 100644
--- a/deprecation.py
+++ b/deprecation.py
@@ -15,6 +15,7 @@ import textwrap
 import warnings
 
 from packaging import version
+from datetime import date
 
 __version__ = "2.0.7"
 
@@ -42,7 +43,8 @@ class DeprecatedWarning(DeprecationWarning):
 
     :param function: The function being deprecated.
     :param deprecated_in: The version that ``function`` is deprecated in
-    :param removed_in: The version that ``function`` gets removed in
+    :param removed_in: The version or :class:`datetime.date` specifying
+                       when ``function`` gets removed.
     :param details: Optional details about the deprecation. Most often
                     this will include directions on what to use instead
                     of the now deprecated code.
@@ -68,7 +70,8 @@ class DeprecatedWarning(DeprecationWarning):
         if self.deprecated_in:
             parts["deprecated"] = " as of %s" % self.deprecated_in
         if self.removed_in:
-            parts["removed"] = " and will be removed in %s" % self.removed_in
+            parts["removed"] = " and will be removed {} {}".format("on" if isinstance(self.removed_in, date) else "in",
+                                                                   self.removed_in)
         if any([self.deprecated_in, self.removed_in, self.details]):
             parts["period"] = "."
         if self.details:
@@ -121,10 +124,11 @@ def deprecated(deprecated_in=None, removed_in=None, current_version=None,
                           means immediate deprecation. If this is not
                           specified, then the `removed_in` and
                           `current_version` arguments are ignored.
-    :param removed_in: The version when the decorated method will be removed.
-                       The default is **None**, specifying that the function
-                       is not currently planned to be removed.
-                       Note: This cannot be set to a value if
+    :param removed_in: The version or :class:`datetime.date` when the decorated
+                       method will be removed. The default is **None**,
+                       specifying that the function is not currently planned
+                       to be removed.
+                       Note: This parameter cannot be set to a value if
                        `deprecated_in=None`.
     :param current_version: The source of version information for the
                             currently running code. This will usually be
@@ -155,8 +159,14 @@ def deprecated(deprecated_in=None, removed_in=None, current_version=None,
     is_unsupported = False
 
     # StrictVersion won't take a None or a "", so make whatever goes to it
-    # is at least *something*.
-    if current_version:
+    # is at least *something*. Compare versions only if removed_in is not
+    # of type datetime.date
+    if isinstance(removed_in, date):
+        if date.today() >= removed_in:
+            is_unsupported = True
+        else:
+            is_deprecated = True
+    elif current_version:
         current_version = version.parse(current_version)
 
         if (removed_in
@@ -183,12 +193,15 @@ def deprecated(deprecated_in=None, removed_in=None, current_version=None,
             # a number of ways the deprecation notice could go. The following
             # makes for a nicely constructed sentence with or without any
             # of the parts.
+
+            # If removed_in is a date, use "removed on"
+            # If removed_in is a version, use "removed in"
             parts = {
                 "deprecated_in":
                     " %s" % deprecated_in if deprecated_in else "",
                 "removed_in":
-                    "\n   This will be removed in %s." %
-                    removed_in if removed_in else "",
+                    "\n   This will be removed {} {}.".format("on" if isinstance(removed_in, date) else "in",
+                                                              removed_in) if removed_in else "",
                 "details":
                     " %s" % details if details else ""}
 

Allow removed_in to be a date
Depending on the versioning scheme used by a project, it can be difficult to tell in advance the version number at which code can be removed. Sometimes, though, it is easier to tell *when* it should be removed. Google Guava, for example, guarantees all methods marked deprecated will be kept around for two years post-deprecation.

It would be convenient to handle this by allowing a date (in some TBD format) to be passed for the `removed_in` argument.
diff --git a/tests/test_deprecation.py b/tests/test_deprecation.py
index 4d294ba..0fd29b3 100644
--- a/tests/test_deprecation.py
+++ b/tests/test_deprecation.py
@@ -16,6 +16,7 @@ import unittest2
 import warnings
 
 import deprecation
+from datetime import date
 
 
 class Test_deprecated(unittest2.TestCase):
@@ -41,6 +42,14 @@ class Test_deprecated(unittest2.TestCase):
                                "details": "some details"},
                       "__doc__": "docstring\n\n.. deprecated:: 1.0"
                                  "\n   This will be removed in 2.0. "
+                                 "some details"},
+                     {"args": {"deprecated_in": "1.0", "removed_in": date(2200, 5, 20)},
+                      "__doc__": "docstring\n\n.. deprecated:: 1.0"
+                                 "\n   This will be removed on 2200-05-20."},
+                     {"args": {"deprecated_in": "1.0", "removed_in": date(2100, 3, 15),
+                               "details": "some details"},
+                      "__doc__": "docstring\n\n.. deprecated:: 1.0"
+                                 "\n   This will be removed on 2100-03-15. "
                                  "some details"}]:
             with self.subTest(**test):
                 @deprecation.deprecated(**test["args"])
@@ -62,6 +71,14 @@ class Test_deprecated(unittest2.TestCase):
                                "details": "some details"},
                       "__doc__": "%s\n\n.. deprecated:: 1.0"
                                  "\n   This will be removed in 2.0. "
+                                 "some details"},
+                     {"args": {"deprecated_in": "1.0", "removed_in": date(2200, 11, 20)},
+                      "__doc__": "%s\n\n.. deprecated:: 1.0"
+                                 "\n   This will be removed on 2200-11-20."},
+                     {"args": {"deprecated_in": "1.0", "removed_in": date(2100, 3, 15),
+                               "details": "some details"},
+                      "__doc__": "%s\n\n.. deprecated:: 1.0"
+                                 "\n   This will be removed on 2100-03-15. "
                                  "some details"}]:
             with self.subTest(**test):
                 @deprecation.deprecated(**test["args"])
@@ -88,6 +105,14 @@ class Test_deprecated(unittest2.TestCase):
                                "details": "some details"},
                       "__doc__": "%s\n\n.. deprecated:: 1.0"
                                  "\n   This will be removed in 2.0. "
+                                 "some details%s"},#####
+                     {"args": {"deprecated_in": "1.0", "removed_in": date(2200, 11, 20)},
+                      "__doc__": "%s\n\n.. deprecated:: 1.0"
+                                 "\n   This will be removed on 2200-11-20.%s"},
+                     {"args": {"deprecated_in": "1.0", "removed_in": date(2100, 3, 15),
+                               "details": "some details"},
+                      "__doc__": "%s\n\n.. deprecated:: 1.0"
+                                 "\n   This will be removed on 2100-03-15. "
                                  "some details%s"}]:
             with self.subTest(**test):
                 deprecation.message_location = "top"
@@ -150,6 +175,24 @@ class Test_deprecated(unittest2.TestCase):
                                "details": "do something else."},
                       "warning": deprecation.UnsupportedWarning,
                       "message": ("method is unsupported as of 2.0. "
+                                  "do something else.")},
+                     {"args": {"deprecated_in": "1.0",
+                               "removed_in": date(2100, 4, 19),
+                               "current_version": "2.0"},
+                      "warning": deprecation.DeprecatedWarning,
+                      "message": ("method is deprecated as of 1.0 "
+                                  "and will be removed on 2100-04-19.")},
+                     {"args": {"deprecated_in": "1.0",
+                               "removed_in": date.today(),
+                               "current_version": "2.0"},
+                      "warning": deprecation.UnsupportedWarning,
+                      "message": "method is unsupported as of %s." % date.today()},
+                     {"args": {"deprecated_in": "1.0",
+                               "removed_in": date(2020, 1, 30),
+                               "current_version": "2.0",
+                               "details": "do something else."},
+                      "warning": deprecation.UnsupportedWarning,
+                      "message": ("method is unsupported as of 2020-01-30. "
                                   "do something else.")}]:
             with self.subTest(**test):
                 class Test(object):

pip install -e .
parse_log_pytest
requirements.txt
coverage codecov flake8 pytest
3.7
test-requirements.txt docs-requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
certifi @ file:///croot/certifi_1671487769961/work/certifi
charset-normalizer==3.4.1
codecov==2.1.13
coverage==7.2.7
-e git+https://github.com/briancurtin/deprecation.git@264e8cd469cdcfc793afedb80d224f48d350a360#egg=deprecation
exceptiongroup==1.2.2
flake8==5.0.4
idna==3.10
importlib-metadata==4.2.0
iniconfig==2.0.0
linecache2==1.0.0
mccabe==0.7.0
packaging==24.0
pluggy==1.2.0
pycodestyle==2.9.1
pyflakes==2.5.0
pytest==7.4.4
requests==2.31.0
six==1.17.0
tomli==2.0.1
traceback2==1.4.0
typing_extensions==4.7.1
unittest2==1.1.0
urllib3==2.0.7
zipp==3.15.0

name: deprecation
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - argparse==1.4.0
      - charset-normalizer==3.4.1
      - codecov==2.1.13
      - coverage==7.2.7
      - exceptiongroup==1.2.2
      - flake8==5.0.4
      - idna==3.10
      - importlib-metadata==4.2.0
      - iniconfig==2.0.0
      - linecache2==1.0.0
      - mccabe==0.7.0
      - packaging==24.0
      - pluggy==1.2.0
      - pycodestyle==2.9.1
      - pyflakes==2.5.0
      - pytest==7.4.4
      - requests==2.31.0
      - six==1.17.0
      - tomli==2.0.1
      - traceback2==1.4.0
      - typing-extensions==4.7.1
      - unittest2==1.1.0
      - urllib3==2.0.7
      - zipp==3.15.0
prefix: /opt/conda/envs/deprecation

tests/test_deprecation.py::Test_deprecated::test_docstring tests/test_deprecation.py::Test_deprecated::test_multiline_docstring tests/test_deprecation.py::Test_deprecated::test_multiline_docstring_top tests/test_deprecation.py::Test_deprecated::test_warning_raised
tests/test_deprecation.py::Test_deprecated::test_DeprecatedWarning_not_raised tests/test_deprecation.py::Test_deprecated::test_args_set_on_base_class tests/test_deprecation.py::Test_deprecated::test_multiline_fallback tests/test_deprecation.py::Test_deprecated::test_removing_without_deprecating tests/test_deprecation.py::Test_fail_if_not_removed::test_DeprecatedWarning_doesnt_fail tests/test_deprecation.py::Test_fail_if_not_removed::test_UnsupportedWarning_causes_failure tests/test_deprecation.py::Test_fail_if_not_removed::test_literal_DeprecatedWarning
.gitignore deprecation.py
.gitignore deprecation.py