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