Home | History | Annotate | Download | only in util
      1 # Copyright 2016 The TensorFlow Authors. All Rights Reserved.
      2 #
      3 # Licensed under the Apache License, Version 2.0 (the "License");
      4 # you may not use this file except in compliance with the License.
      5 # You may obtain a copy of the License at
      6 #
      7 #     http://www.apache.org/licenses/LICENSE-2.0
      8 #
      9 # Unless required by applicable law or agreed to in writing, software
     10 # distributed under the License is distributed on an "AS IS" BASIS,
     11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 # See the License for the specific language governing permissions and
     13 # limitations under the License.
     14 # ==============================================================================
     15 """Deprecation tests."""
     16 
     17 # pylint: disable=unused-import
     18 from __future__ import absolute_import
     19 from __future__ import division
     20 from __future__ import print_function
     21 
     22 from tensorflow.python.platform import test
     23 from tensorflow.python.platform import tf_logging as logging
     24 from tensorflow.python.util import deprecation
     25 
     26 
     27 class DeprecatedAliasTest(test.TestCase):
     28 
     29   @test.mock.patch.object(logging, "warning", autospec=True)
     30   def test_function_alias(self, mock_warning):
     31     deprecated_func = deprecation.deprecated_alias("deprecated.func",
     32                                                    "real.func",
     33                                                    logging.error)
     34 
     35     logging.error("fake error logged")
     36     self.assertEqual(0, mock_warning.call_count)
     37     deprecated_func("FAKE ERROR!")
     38     self.assertEqual(1, mock_warning.call_count)
     39     # Make sure the error points to the right file.
     40     self.assertRegexpMatches(mock_warning.call_args[0][1],
     41                              r"deprecation_test\.py:")
     42     deprecated_func("ANOTHER FAKE ERROR!")
     43     self.assertEqual(1, mock_warning.call_count)
     44 
     45   @test.mock.patch.object(logging, "warning", autospec=True)
     46   def test_class_alias(self, mock_warning):
     47     class MyClass(object):
     48       """My docstring."""
     49 
     50       init_args = []
     51 
     52       def __init__(self, arg):
     53         MyClass.init_args.append(arg)
     54 
     55     deprecated_cls = deprecation.deprecated_alias("deprecated.cls",
     56                                                   "real.cls",
     57                                                   MyClass)
     58 
     59     print(deprecated_cls.__name__)
     60     print(deprecated_cls.__module__)
     61     print(deprecated_cls.__doc__)
     62 
     63     MyClass("test")
     64     self.assertEqual(0, mock_warning.call_count)
     65     deprecated_cls("deprecated")
     66     self.assertEqual(1, mock_warning.call_count)
     67     # Make sure the error points to the right file.
     68     self.assertRegexpMatches(mock_warning.call_args[0][1],
     69                              r"deprecation_test\.py:")
     70     deprecated_cls("deprecated again")
     71     self.assertEqual(1, mock_warning.call_count)
     72 
     73     self.assertEqual(["test", "deprecated", "deprecated again"],
     74                      MyClass.init_args)
     75 
     76 
     77 class DeprecationTest(test.TestCase):
     78 
     79   @test.mock.patch.object(logging, "warning", autospec=True)
     80   def test_deprecated_once(self, mock_warning):
     81     date = "2016-07-04"
     82     instructions = "This is how you update..."
     83 
     84     @deprecation.deprecated(date, instructions, warn_once=True)
     85     def _fn():
     86       pass
     87 
     88     _fn()
     89     self.assertEqual(1, mock_warning.call_count)
     90     _fn()
     91     self.assertEqual(1, mock_warning.call_count)
     92 
     93   @test.mock.patch.object(logging, "warning", autospec=True)
     94   def test_silence(self, mock_warning):
     95     date = "2016-07-04"
     96     instructions = "This is how you update..."
     97 
     98     @deprecation.deprecated(date, instructions, warn_once=False)
     99     def _fn():
    100       pass
    101 
    102     _fn()
    103     self.assertEqual(1, mock_warning.call_count)
    104 
    105     with deprecation.silence():
    106       _fn()
    107     self.assertEqual(1, mock_warning.call_count)
    108 
    109     _fn()
    110     self.assertEqual(2, mock_warning.call_count)
    111 
    112   def _assert_subset(self, expected_subset, actual_set):
    113     self.assertTrue(
    114         actual_set.issuperset(expected_subset),
    115         msg="%s is not a superset of %s." % (actual_set, expected_subset))
    116 
    117   def test_deprecated_illegal_args(self):
    118     instructions = "This is how you update..."
    119     with self.assertRaisesRegexp(ValueError, "YYYY-MM-DD"):
    120       deprecation.deprecated("", instructions)
    121     with self.assertRaisesRegexp(ValueError, "YYYY-MM-DD"):
    122       deprecation.deprecated("07-04-2016", instructions)
    123     date = "2016-07-04"
    124     with self.assertRaisesRegexp(ValueError, "instructions"):
    125       deprecation.deprecated(date, None)
    126     with self.assertRaisesRegexp(ValueError, "instructions"):
    127       deprecation.deprecated(date, "")
    128 
    129   @test.mock.patch.object(logging, "warning", autospec=True)
    130   def test_no_date(self, mock_warning):
    131     date = None
    132     instructions = "This is how you update..."
    133 
    134     @deprecation.deprecated(date, instructions)
    135     def _fn(arg0, arg1):
    136       """fn doc.
    137 
    138       Args:
    139         arg0: Arg 0.
    140         arg1: Arg 1.
    141 
    142       Returns:
    143         Sum of args.
    144       """
    145       return arg0 + arg1
    146 
    147     self.assertEqual(
    148         "fn doc. (deprecated)"
    149         "\n"
    150         "\nTHIS FUNCTION IS DEPRECATED. It will be removed in a future version."
    151         "\nInstructions for updating:\n%s"
    152         "\n"
    153         "\nArgs:"
    154         "\n  arg0: Arg 0."
    155         "\n  arg1: Arg 1."
    156         "\n"
    157         "\nReturns:"
    158         "\n  Sum of args." % instructions, _fn.__doc__)
    159 
    160     # Assert calling new fn issues log warning.
    161     self.assertEqual(3, _fn(1, 2))
    162     self.assertEqual(1, mock_warning.call_count)
    163     (args, _) = mock_warning.call_args
    164     self.assertRegexpMatches(
    165         args[0], r"deprecated and will be removed")
    166     self._assert_subset(set(["in a future version", instructions]),
    167                         set(args[1:]))
    168 
    169   @test.mock.patch.object(logging, "warning", autospec=True)
    170   def test_static_fn_with_doc(self, mock_warning):
    171     date = "2016-07-04"
    172     instructions = "This is how you update..."
    173 
    174     @deprecation.deprecated(date, instructions)
    175     def _fn(arg0, arg1):
    176       """fn doc.
    177 
    178       Args:
    179         arg0: Arg 0.
    180         arg1: Arg 1.
    181 
    182       Returns:
    183         Sum of args.
    184       """
    185       return arg0 + arg1
    186 
    187     # Assert function docs are properly updated.
    188     self.assertEqual("_fn", _fn.__name__)
    189     self.assertEqual(
    190         "fn doc. (deprecated)"
    191         "\n"
    192         "\nTHIS FUNCTION IS DEPRECATED. It will be removed after %s."
    193         "\nInstructions for updating:\n%s"
    194         "\n"
    195         "\nArgs:"
    196         "\n  arg0: Arg 0."
    197         "\n  arg1: Arg 1."
    198         "\n"
    199         "\nReturns:"
    200         "\n  Sum of args." % (date, instructions), _fn.__doc__)
    201 
    202     # Assert calling new fn issues log warning.
    203     self.assertEqual(3, _fn(1, 2))
    204     self.assertEqual(1, mock_warning.call_count)
    205     (args, _) = mock_warning.call_args
    206     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    207     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    208 
    209   @test.mock.patch.object(logging, "warning", autospec=True)
    210   def test_static_fn_with_one_line_doc(self, mock_warning):
    211     date = "2016-07-04"
    212     instructions = "This is how you update..."
    213 
    214     @deprecation.deprecated(date, instructions)
    215     def _fn(arg0, arg1):
    216       """fn doc."""
    217       return arg0 + arg1
    218 
    219     # Assert function docs are properly updated.
    220     self.assertEqual("_fn", _fn.__name__)
    221     self.assertEqual(
    222         "fn doc. (deprecated)"
    223         "\n"
    224         "\nTHIS FUNCTION IS DEPRECATED. It will be removed after %s."
    225         "\nInstructions for updating:\n%s" % (date, instructions), _fn.__doc__)
    226 
    227     # Assert calling new fn issues log warning.
    228     self.assertEqual(3, _fn(1, 2))
    229     self.assertEqual(1, mock_warning.call_count)
    230     (args, _) = mock_warning.call_args
    231     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    232     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    233 
    234   @test.mock.patch.object(logging, "warning", autospec=True)
    235   def test_static_fn_no_doc(self, mock_warning):
    236     date = "2016-07-04"
    237     instructions = "This is how you update..."
    238 
    239     @deprecation.deprecated(date, instructions)
    240     def _fn(arg0, arg1):
    241       return arg0 + arg1
    242 
    243     # Assert function docs are properly updated.
    244     self.assertEqual("_fn", _fn.__name__)
    245     self.assertEqual(
    246         "DEPRECATED FUNCTION"
    247         "\n"
    248         "\nTHIS FUNCTION IS DEPRECATED. It will be removed after %s."
    249         "\nInstructions for updating:"
    250         "\n%s" % (date, instructions), _fn.__doc__)
    251 
    252     # Assert calling new fn issues log warning.
    253     self.assertEqual(3, _fn(1, 2))
    254     self.assertEqual(1, mock_warning.call_count)
    255     (args, _) = mock_warning.call_args
    256     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    257     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    258 
    259   @test.mock.patch.object(logging, "warning", autospec=True)
    260   def test_instance_fn_with_doc(self, mock_warning):
    261     date = "2016-07-04"
    262     instructions = "This is how you update..."
    263 
    264     class _Object(object):
    265 
    266       def __init(self):
    267         pass
    268 
    269       @deprecation.deprecated(date, instructions)
    270       def _fn(self, arg0, arg1):
    271         """fn doc.
    272 
    273         Args:
    274           arg0: Arg 0.
    275           arg1: Arg 1.
    276 
    277         Returns:
    278           Sum of args.
    279         """
    280         return arg0 + arg1
    281 
    282     # Assert function docs are properly updated.
    283     self.assertEqual(
    284         "fn doc. (deprecated)"
    285         "\n"
    286         "\nTHIS FUNCTION IS DEPRECATED. It will be removed after %s."
    287         "\nInstructions for updating:\n%s"
    288         "\n"
    289         "\nArgs:"
    290         "\n  arg0: Arg 0."
    291         "\n  arg1: Arg 1."
    292         "\n"
    293         "\nReturns:"
    294         "\n  Sum of args." % (date, instructions),
    295         getattr(_Object, "_fn").__doc__)
    296 
    297     # Assert calling new fn issues log warning.
    298     self.assertEqual(3, _Object()._fn(1, 2))
    299     self.assertEqual(1, mock_warning.call_count)
    300     (args, _) = mock_warning.call_args
    301     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    302     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    303 
    304   @test.mock.patch.object(logging, "warning", autospec=True)
    305   def test_instance_fn_with_one_line_doc(self, mock_warning):
    306     date = "2016-07-04"
    307     instructions = "This is how you update..."
    308 
    309     class _Object(object):
    310 
    311       def __init(self):
    312         pass
    313 
    314       @deprecation.deprecated(date, instructions)
    315       def _fn(self, arg0, arg1):
    316         """fn doc."""
    317         return arg0 + arg1
    318 
    319     # Assert function docs are properly updated.
    320     self.assertEqual(
    321         "fn doc. (deprecated)"
    322         "\n"
    323         "\nTHIS FUNCTION IS DEPRECATED. It will be removed after %s."
    324         "\nInstructions for updating:\n%s" % (date, instructions),
    325         getattr(_Object, "_fn").__doc__)
    326 
    327     # Assert calling new fn issues log warning.
    328     self.assertEqual(3, _Object()._fn(1, 2))
    329     self.assertEqual(1, mock_warning.call_count)
    330     (args, _) = mock_warning.call_args
    331     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    332     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    333 
    334   @test.mock.patch.object(logging, "warning", autospec=True)
    335   def test_instance_fn_no_doc(self, mock_warning):
    336     date = "2016-07-04"
    337     instructions = "This is how you update..."
    338 
    339     class _Object(object):
    340 
    341       def __init(self):
    342         pass
    343 
    344       @deprecation.deprecated(date, instructions)
    345       def _fn(self, arg0, arg1):
    346         return arg0 + arg1
    347 
    348     # Assert function docs are properly updated.
    349     self.assertEqual(
    350         "DEPRECATED FUNCTION"
    351         "\n"
    352         "\nTHIS FUNCTION IS DEPRECATED. It will be removed after %s."
    353         "\nInstructions for updating:"
    354         "\n%s" % (date, instructions), getattr(_Object, "_fn").__doc__)
    355 
    356     # Assert calling new fn issues log warning.
    357     self.assertEqual(3, _Object()._fn(1, 2))
    358     self.assertEqual(1, mock_warning.call_count)
    359     (args, _) = mock_warning.call_args
    360     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    361     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    362 
    363   def test_prop_wrong_order(self):
    364     with self.assertRaisesRegexp(
    365         ValueError,
    366         "make sure @property appears before @deprecated in your source code"):
    367       # pylint: disable=unused-variable
    368 
    369       class _Object(object):
    370 
    371         def __init(self):
    372           pass
    373 
    374         @deprecation.deprecated("2016-07-04", "Instructions.")
    375         @property
    376         def _prop(self):
    377           return "prop_wrong_order"
    378 
    379   @test.mock.patch.object(logging, "warning", autospec=True)
    380   def test_prop_with_doc(self, mock_warning):
    381     date = "2016-07-04"
    382     instructions = "This is how you update..."
    383 
    384     class _Object(object):
    385 
    386       def __init(self):
    387         pass
    388 
    389       @property
    390       @deprecation.deprecated(date, instructions)
    391       def _prop(self):
    392         """prop doc.
    393 
    394         Returns:
    395           String.
    396         """
    397         return "prop_with_doc"
    398 
    399     # Assert function docs are properly updated.
    400     self.assertEqual(
    401         "prop doc. (deprecated)"
    402         "\n"
    403         "\nTHIS FUNCTION IS DEPRECATED. It will be removed after %s."
    404         "\nInstructions for updating:"
    405         "\n%s"
    406         "\n"
    407         "\nReturns:"
    408         "\n  String." % (date, instructions), getattr(_Object, "_prop").__doc__)
    409 
    410     # Assert calling new fn issues log warning.
    411     self.assertEqual("prop_with_doc", _Object()._prop)
    412     self.assertEqual(1, mock_warning.call_count)
    413     (args, _) = mock_warning.call_args
    414     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    415     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    416 
    417   @test.mock.patch.object(logging, "warning", autospec=True)
    418   def test_prop_no_doc(self, mock_warning):
    419     date = "2016-07-04"
    420     instructions = "This is how you update..."
    421 
    422     class _Object(object):
    423 
    424       def __init(self):
    425         pass
    426 
    427       @property
    428       @deprecation.deprecated(date, instructions)
    429       def _prop(self):
    430         return "prop_no_doc"
    431 
    432     # Assert function docs are properly updated.
    433     self.assertEqual(
    434         "DEPRECATED FUNCTION"
    435         "\n"
    436         "\nTHIS FUNCTION IS DEPRECATED. It will be removed after %s."
    437         "\nInstructions for updating:"
    438         "\n%s" % (date, instructions), getattr(_Object, "_prop").__doc__)
    439 
    440     # Assert calling new fn issues log warning.
    441     self.assertEqual("prop_no_doc", _Object()._prop)
    442     self.assertEqual(1, mock_warning.call_count)
    443     (args, _) = mock_warning.call_args
    444     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    445     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    446 
    447 
    448 class DeprecatedArgsTest(test.TestCase):
    449 
    450   def _assert_subset(self, expected_subset, actual_set):
    451     self.assertTrue(
    452         actual_set.issuperset(expected_subset),
    453         msg="%s is not a superset of %s." % (actual_set, expected_subset))
    454 
    455   def test_deprecated_illegal_args(self):
    456     instructions = "This is how you update..."
    457     date = "2016-07-04"
    458     with self.assertRaisesRegexp(ValueError, "YYYY-MM-DD"):
    459       deprecation.deprecated_args("", instructions, "deprecated")
    460     with self.assertRaisesRegexp(ValueError, "YYYY-MM-DD"):
    461       deprecation.deprecated_args("07-04-2016", instructions, "deprecated")
    462     with self.assertRaisesRegexp(ValueError, "instructions"):
    463       deprecation.deprecated_args(date, None, "deprecated")
    464     with self.assertRaisesRegexp(ValueError, "instructions"):
    465       deprecation.deprecated_args(date, "", "deprecated")
    466     with self.assertRaisesRegexp(ValueError, "argument"):
    467       deprecation.deprecated_args(date, instructions)
    468 
    469   def test_deprecated_missing_args(self):
    470     date = "2016-07-04"
    471     instructions = "This is how you update..."
    472 
    473     def _fn(arg0, arg1, deprecated=None):
    474       return arg0 + arg1 if deprecated else arg1 + arg0
    475 
    476     # Assert calls without the deprecated argument log nothing.
    477     with self.assertRaisesRegexp(ValueError, "not present.*\\['missing'\\]"):
    478       deprecation.deprecated_args(date, instructions, "missing")(_fn)
    479 
    480   @test.mock.patch.object(logging, "warning", autospec=True)
    481   def test_static_fn_with_doc(self, mock_warning):
    482     date = "2016-07-04"
    483     instructions = "This is how you update..."
    484 
    485     @deprecation.deprecated_args(date, instructions, "deprecated")
    486     def _fn(arg0, arg1, deprecated=True):
    487       """fn doc.
    488 
    489       Args:
    490         arg0: Arg 0.
    491         arg1: Arg 1.
    492         deprecated: Deprecated!
    493 
    494       Returns:
    495         Sum of args.
    496       """
    497       return arg0 + arg1 if deprecated else arg1 + arg0
    498 
    499     # Assert function docs are properly updated.
    500     self.assertEqual("_fn", _fn.__name__)
    501     self.assertEqual(
    502         "fn doc. (deprecated arguments)"
    503         "\n"
    504         "\nSOME ARGUMENTS ARE DEPRECATED. They will be removed after %s."
    505         "\nInstructions for updating:\n%s"
    506         "\n"
    507         "\nArgs:"
    508         "\n  arg0: Arg 0."
    509         "\n  arg1: Arg 1."
    510         "\n  deprecated: Deprecated!"
    511         "\n"
    512         "\nReturns:"
    513         "\n  Sum of args." % (date, instructions), _fn.__doc__)
    514 
    515     # Assert calls without the deprecated argument log nothing.
    516     self.assertEqual(3, _fn(1, 2))
    517     self.assertEqual(0, mock_warning.call_count)
    518 
    519     # Assert calls with the deprecated argument log a warning.
    520     self.assertEqual(3, _fn(1, 2, True))
    521     self.assertEqual(1, mock_warning.call_count)
    522     (args, _) = mock_warning.call_args
    523     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    524     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    525 
    526   @test.mock.patch.object(logging, "warning", autospec=True)
    527   def test_static_fn_with_one_line_doc(self, mock_warning):
    528     date = "2016-07-04"
    529     instructions = "This is how you update..."
    530 
    531     @deprecation.deprecated_args(date, instructions, "deprecated")
    532     def _fn(arg0, arg1, deprecated=True):
    533       """fn doc."""
    534       return arg0 + arg1 if deprecated else arg1 + arg0
    535 
    536     # Assert function docs are properly updated.
    537     self.assertEqual("_fn", _fn.__name__)
    538     self.assertEqual(
    539         "fn doc. (deprecated arguments)"
    540         "\n"
    541         "\nSOME ARGUMENTS ARE DEPRECATED. They will be removed after %s."
    542         "\nInstructions for updating:\n%s" % (date, instructions), _fn.__doc__)
    543 
    544     # Assert calls without the deprecated argument log nothing.
    545     self.assertEqual(3, _fn(1, 2))
    546     self.assertEqual(0, mock_warning.call_count)
    547 
    548     # Assert calls with the deprecated argument log a warning.
    549     self.assertEqual(3, _fn(1, 2, True))
    550     self.assertEqual(1, mock_warning.call_count)
    551     (args, _) = mock_warning.call_args
    552     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    553     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    554 
    555   @test.mock.patch.object(logging, "warning", autospec=True)
    556   def test_static_fn_no_doc(self, mock_warning):
    557     date = "2016-07-04"
    558     instructions = "This is how you update..."
    559 
    560     @deprecation.deprecated_args(date, instructions, "deprecated")
    561     def _fn(arg0, arg1, deprecated=True):
    562       return arg0 + arg1 if deprecated else arg1 + arg0
    563 
    564     # Assert function docs are properly updated.
    565     self.assertEqual("_fn", _fn.__name__)
    566     self.assertEqual(
    567         "DEPRECATED FUNCTION ARGUMENTS"
    568         "\n"
    569         "\nSOME ARGUMENTS ARE DEPRECATED. They will be removed after %s."
    570         "\nInstructions for updating:"
    571         "\n%s" % (date, instructions), _fn.__doc__)
    572 
    573     # Assert calls without the deprecated argument log nothing.
    574     self.assertEqual(3, _fn(1, 2))
    575     self.assertEqual(0, mock_warning.call_count)
    576 
    577     # Assert calls with the deprecated argument log a warning.
    578     self.assertEqual(3, _fn(1, 2, True))
    579     self.assertEqual(1, mock_warning.call_count)
    580     (args, _) = mock_warning.call_args
    581     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    582     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    583 
    584   @test.mock.patch.object(logging, "warning", autospec=True)
    585   def test_varargs(self, mock_warning):
    586     date = "2016-07-04"
    587     instructions = "This is how you update..."
    588 
    589     @deprecation.deprecated_args(date, instructions, "deprecated")
    590     def _fn(arg0, arg1, *deprecated):
    591       return arg0 + arg1 if deprecated else arg1 + arg0
    592 
    593     # Assert calls without the deprecated argument log nothing.
    594     self.assertEqual(3, _fn(1, 2))
    595     self.assertEqual(0, mock_warning.call_count)
    596 
    597     # Assert calls with the deprecated argument log a warning.
    598     self.assertEqual(3, _fn(1, 2, True, False))
    599     self.assertEqual(1, mock_warning.call_count)
    600     (args, _) = mock_warning.call_args
    601     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    602     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    603 
    604   @test.mock.patch.object(logging, "warning", autospec=True)
    605   def test_kwargs(self, mock_warning):
    606     date = "2016-07-04"
    607     instructions = "This is how you update..."
    608 
    609     @deprecation.deprecated_args(date, instructions, "deprecated")
    610     def _fn(arg0, arg1, **deprecated):
    611       return arg0 + arg1 if deprecated else arg1 + arg0
    612 
    613     # Assert calls without the deprecated argument log nothing.
    614     self.assertEqual(3, _fn(1, 2))
    615     self.assertEqual(0, mock_warning.call_count)
    616 
    617     # Assert calls with the deprecated argument log a warning.
    618     self.assertEqual(3, _fn(1, 2, a=True, b=False))
    619     self.assertEqual(1, mock_warning.call_count)
    620     (args, _) = mock_warning.call_args
    621     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    622     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    623 
    624   @test.mock.patch.object(logging, "warning", autospec=True)
    625   def test_positional_and_named(self, mock_warning):
    626     date = "2016-07-04"
    627     instructions = "This is how you update..."
    628 
    629     @deprecation.deprecated_args(date, instructions, "d1", "d2")
    630     def _fn(arg0, d1=None, arg1=2, d2=None):
    631       return arg0 + arg1 if d1 else arg1 + arg0 if d2 else arg0 * arg1
    632 
    633     # Assert calls without the deprecated arguments log nothing.
    634     self.assertEqual(2, _fn(1, arg1=2))
    635     self.assertEqual(0, mock_warning.call_count)
    636 
    637     # Assert calls with the deprecated arguments log warnings.
    638     self.assertEqual(2, _fn(1, None, 2, d2=False))
    639     self.assertEqual(2, mock_warning.call_count)
    640     (args1, _) = mock_warning.call_args_list[0]
    641     self.assertRegexpMatches(args1[0], r"deprecated and will be removed")
    642     self._assert_subset(set(["after " + date, instructions, "d1"]),
    643                         set(args1[1:]))
    644     (args2, _) = mock_warning.call_args_list[1]
    645     self.assertRegexpMatches(args2[0], r"deprecated and will be removed")
    646     self._assert_subset(set(["after " + date, instructions, "d2"]),
    647                         set(args2[1:]))
    648 
    649   @test.mock.patch.object(logging, "warning", autospec=True)
    650   def test_positional_and_named_with_ok_vals(self, mock_warning):
    651     date = "2016-07-04"
    652     instructions = "This is how you update..."
    653 
    654     @deprecation.deprecated_args(date, instructions, ("d1", None),
    655                                  ("d2", "my_ok_val"))
    656     def _fn(arg0, d1=None, arg1=2, d2=None):
    657       return arg0 + arg1 if d1 else arg1 + arg0 if d2 else arg0 * arg1
    658 
    659     # Assert calls without the deprecated arguments log nothing.
    660     self.assertEqual(2, _fn(1, arg1=2))
    661     self.assertEqual(0, mock_warning.call_count)
    662 
    663     # Assert calls with the deprecated arguments log warnings.
    664     self.assertEqual(2, _fn(1, False, 2, d2=False))
    665     self.assertEqual(2, mock_warning.call_count)
    666     (args1, _) = mock_warning.call_args_list[0]
    667     self.assertRegexpMatches(args1[0], r"deprecated and will be removed")
    668     self._assert_subset(set(["after " + date, instructions, "d1"]),
    669                         set(args1[1:]))
    670     (args2, _) = mock_warning.call_args_list[1]
    671     self.assertRegexpMatches(args2[0], r"deprecated and will be removed")
    672     self._assert_subset(set(["after " + date, instructions, "d2"]),
    673                         set(args2[1:]))
    674 
    675     # Assert calls with the deprecated arguments don't log warnings if
    676     # the value matches the 'ok_val'.
    677     mock_warning.reset_mock()
    678     self.assertEqual(3, _fn(1, None, 2, d2="my_ok_val"))
    679     self.assertEqual(0, mock_warning.call_count)
    680 
    681   @test.mock.patch.object(logging, "warning", autospec=True)
    682   def test_deprecated_args_once(self, mock_warning):
    683     date = "2016-07-04"
    684     instructions = "This is how you update..."
    685 
    686     @deprecation.deprecated_args(date, instructions, "arg", warn_once=True)
    687     def _fn(arg=0):  # pylint: disable=unused-argument
    688       pass
    689 
    690     _fn()
    691     self.assertEqual(0, mock_warning.call_count)
    692     _fn(arg=0)
    693     self.assertEqual(1, mock_warning.call_count)
    694     _fn(arg=1)
    695     self.assertEqual(1, mock_warning.call_count)
    696 
    697   @test.mock.patch.object(logging, "warning", autospec=True)
    698   def test_deprecated_multiple_args_once_each(self, mock_warning):
    699     date = "2016-07-04"
    700     instructions = "This is how you update..."
    701 
    702     @deprecation.deprecated_args(date, instructions, "arg0", "arg1",
    703                                  warn_once=True)
    704     def _fn(arg0=0, arg1=0):  # pylint: disable=unused-argument
    705       pass
    706 
    707     _fn(arg0=0)
    708     self.assertEqual(1, mock_warning.call_count)
    709     _fn(arg0=0)
    710     self.assertEqual(1, mock_warning.call_count)
    711     _fn(arg1=0)
    712     self.assertEqual(2, mock_warning.call_count)
    713     _fn(arg0=0)
    714     self.assertEqual(2, mock_warning.call_count)
    715     _fn(arg1=0)
    716     self.assertEqual(2, mock_warning.call_count)
    717 
    718 
    719 class DeprecatedArgValuesTest(test.TestCase):
    720 
    721   def _assert_subset(self, expected_subset, actual_set):
    722     self.assertTrue(
    723         actual_set.issuperset(expected_subset),
    724         msg="%s is not a superset of %s." % (actual_set, expected_subset))
    725 
    726   def test_deprecated_illegal_args(self):
    727     instructions = "This is how you update..."
    728     with self.assertRaisesRegexp(ValueError, "YYYY-MM-DD"):
    729       deprecation.deprecated_arg_values("", instructions, deprecated=True)
    730     with self.assertRaisesRegexp(ValueError, "YYYY-MM-DD"):
    731       deprecation.deprecated_arg_values(
    732           "07-04-2016", instructions, deprecated=True)
    733     date = "2016-07-04"
    734     with self.assertRaisesRegexp(ValueError, "instructions"):
    735       deprecation.deprecated_arg_values(date, None, deprecated=True)
    736     with self.assertRaisesRegexp(ValueError, "instructions"):
    737       deprecation.deprecated_arg_values(date, "", deprecated=True)
    738     with self.assertRaisesRegexp(ValueError, "argument", deprecated=True):
    739       deprecation.deprecated_arg_values(date, instructions)
    740 
    741   @test.mock.patch.object(logging, "warning", autospec=True)
    742   def test_static_fn_with_doc(self, mock_warning):
    743     date = "2016-07-04"
    744     instructions = "This is how you update..."
    745 
    746     @deprecation.deprecated_arg_values(date, instructions, warn_once=False,
    747                                        deprecated=True)
    748     def _fn(arg0, arg1, deprecated=True):
    749       """fn doc.
    750 
    751       Args:
    752         arg0: Arg 0.
    753         arg1: Arg 1.
    754         deprecated: Deprecated!
    755 
    756       Returns:
    757         Sum of args.
    758       """
    759       return arg0 + arg1 if deprecated else arg1 + arg0
    760 
    761     # Assert function docs are properly updated.
    762     self.assertEqual("_fn", _fn.__name__)
    763     self.assertEqual(
    764         "fn doc. (deprecated arguments)"
    765         "\n"
    766         "\nSOME ARGUMENTS ARE DEPRECATED. They will be removed after %s."
    767         "\nInstructions for updating:\n%s"
    768         "\n"
    769         "\nArgs:"
    770         "\n  arg0: Arg 0."
    771         "\n  arg1: Arg 1."
    772         "\n  deprecated: Deprecated!"
    773         "\n"
    774         "\nReturns:"
    775         "\n  Sum of args." % (date, instructions), _fn.__doc__)
    776 
    777     # Assert calling new fn with non-deprecated value logs nothing.
    778     self.assertEqual(3, _fn(1, 2, deprecated=False))
    779     self.assertEqual(0, mock_warning.call_count)
    780 
    781     # Assert calling new fn with deprecated value issues log warning.
    782     self.assertEqual(3, _fn(1, 2, deprecated=True))
    783     self.assertEqual(1, mock_warning.call_count)
    784     (args, _) = mock_warning.call_args
    785     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    786     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    787 
    788     # Assert calling new fn with default deprecated value issues log warning.
    789     self.assertEqual(3, _fn(1, 2))
    790     self.assertEqual(2, mock_warning.call_count)
    791 
    792   @test.mock.patch.object(logging, "warning", autospec=True)
    793   def test_static_fn_with_one_line_doc(self, mock_warning):
    794     date = "2016-07-04"
    795     instructions = "This is how you update..."
    796 
    797     @deprecation.deprecated_arg_values(date, instructions, warn_once=False,
    798                                        deprecated=True)
    799     def _fn(arg0, arg1, deprecated=True):
    800       """fn doc."""
    801       return arg0 + arg1 if deprecated else arg1 + arg0
    802 
    803     # Assert function docs are properly updated.
    804     self.assertEqual("_fn", _fn.__name__)
    805     self.assertEqual(
    806         "fn doc. (deprecated arguments)"
    807         "\n"
    808         "\nSOME ARGUMENTS ARE DEPRECATED. They will be removed after %s."
    809         "\nInstructions for updating:\n%s" % (date, instructions), _fn.__doc__)
    810 
    811     # Assert calling new fn with non-deprecated value logs nothing.
    812     self.assertEqual(3, _fn(1, 2, deprecated=False))
    813     self.assertEqual(0, mock_warning.call_count)
    814 
    815     # Assert calling new fn with deprecated value issues log warning.
    816     self.assertEqual(3, _fn(1, 2, deprecated=True))
    817     self.assertEqual(1, mock_warning.call_count)
    818     (args, _) = mock_warning.call_args
    819     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    820     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    821 
    822     # Assert calling new fn with default deprecated value issues log warning.
    823     self.assertEqual(3, _fn(1, 2))
    824     self.assertEqual(2, mock_warning.call_count)
    825 
    826   @test.mock.patch.object(logging, "warning", autospec=True)
    827   def test_static_fn_no_doc(self, mock_warning):
    828     date = "2016-07-04"
    829     instructions = "This is how you update..."
    830 
    831     @deprecation.deprecated_arg_values(date, instructions, warn_once=False,
    832                                        deprecated=True)
    833     def _fn(arg0, arg1, deprecated=True):
    834       return arg0 + arg1 if deprecated else arg1 + arg0
    835 
    836     # Assert function docs are properly updated.
    837     self.assertEqual("_fn", _fn.__name__)
    838     self.assertEqual(
    839         "DEPRECATED FUNCTION ARGUMENTS"
    840         "\n"
    841         "\nSOME ARGUMENTS ARE DEPRECATED. They will be removed after %s."
    842         "\nInstructions for updating:"
    843         "\n%s" % (date, instructions), _fn.__doc__)
    844 
    845     # Assert calling new fn with non-deprecated value logs nothing.
    846     self.assertEqual(3, _fn(1, 2, deprecated=False))
    847     self.assertEqual(0, mock_warning.call_count)
    848 
    849     # Assert calling new fn issues log warning.
    850     self.assertEqual(3, _fn(1, 2, deprecated=True))
    851     self.assertEqual(1, mock_warning.call_count)
    852     (args, _) = mock_warning.call_args
    853     self.assertRegexpMatches(args[0], r"deprecated and will be removed")
    854     self._assert_subset(set(["after " + date, instructions]), set(args[1:]))
    855 
    856     # Assert calling new fn with default deprecated value issues log warning.
    857     self.assertEqual(3, _fn(1, 2))
    858     self.assertEqual(2, mock_warning.call_count)
    859 
    860   @test.mock.patch.object(logging, "warning", autospec=True)
    861   def test_deprecated_arg_values_once(self, mock_warning):
    862     date = "2016-07-04"
    863     instructions = "This is how you update..."
    864 
    865     @deprecation.deprecated_arg_values(date, instructions, warn_once=True,
    866                                        deprecated=True)
    867     def _fn(deprecated):  # pylint: disable=unused-argument
    868       pass
    869 
    870     _fn(deprecated=False)
    871     self.assertEqual(0, mock_warning.call_count)
    872     _fn(deprecated=True)
    873     self.assertEqual(1, mock_warning.call_count)
    874     _fn(deprecated=True)
    875     self.assertEqual(1, mock_warning.call_count)
    876 
    877   @test.mock.patch.object(logging, "warning", autospec=True)
    878   def test_deprecated_multiple_arg_values_once_each(self, mock_warning):
    879     date = "2016-07-04"
    880     instructions = "This is how you update..."
    881 
    882     @deprecation.deprecated_arg_values(date, instructions, warn_once=True,
    883                                        arg0="forbidden", arg1="disallowed")
    884     def _fn(arg0, arg1):  # pylint: disable=unused-argument
    885       pass
    886 
    887     _fn(arg0="allowed", arg1="also allowed")
    888     self.assertEqual(0, mock_warning.call_count)
    889     _fn(arg0="forbidden", arg1="disallowed")
    890     self.assertEqual(2, mock_warning.call_count)
    891     _fn(arg0="forbidden", arg1="allowed")
    892     self.assertEqual(2, mock_warning.call_count)
    893     _fn(arg0="forbidden", arg1="disallowed")
    894     self.assertEqual(2, mock_warning.call_count)
    895 
    896 
    897 class DeprecationArgumentsTest(test.TestCase):
    898 
    899   def testDeprecatedArgumentLookup(self):
    900     good_value = 3
    901     self.assertEqual(
    902         deprecation.deprecated_argument_lookup("val_new", good_value, "val_old",
    903                                                None), good_value)
    904     self.assertEqual(
    905         deprecation.deprecated_argument_lookup("val_new", None, "val_old",
    906                                                good_value), good_value)
    907     with self.assertRaisesRegexp(ValueError,
    908                                  "Cannot specify both 'val_old' and 'val_new'"):
    909       self.assertEqual(
    910           deprecation.deprecated_argument_lookup("val_new", good_value,
    911                                                  "val_old", good_value),
    912           good_value)
    913 
    914   def testRewriteArgumentDocstring(self):
    915     docs = """Add `a` and `b`
    916 
    917     Args:
    918       a: first arg
    919       b: second arg
    920     """
    921     new_docs = deprecation.rewrite_argument_docstring(
    922         deprecation.rewrite_argument_docstring(docs, "a", "left"), "b", "right")
    923     new_docs_ref = """Add `left` and `right`
    924 
    925     Args:
    926       left: first arg
    927       right: second arg
    928     """
    929     self.assertEqual(new_docs, new_docs_ref)
    930 
    931 
    932 if __name__ == "__main__":
    933   test.main()
    934