|
5 | 5 | import re
|
6 | 6 | import sys
|
7 | 7 | import textwrap
|
| 8 | +import types |
| 9 | +from typing import overload, get_overloads |
8 | 10 | import unittest
|
9 | 11 | from test import support
|
10 | 12 | from test.support import import_helper
|
|
16 | 18 | from test.test_warnings.data import stacklevel as warning_tests
|
17 | 19 |
|
18 | 20 | import warnings as original_warnings
|
| 21 | +from warnings import deprecated |
19 | 22 |
|
20 | 23 |
|
21 | 24 | py_warnings = import_helper.import_fresh_module('warnings',
|
@@ -90,7 +93,7 @@ def test_module_all_attribute(self):
|
90 | 93 | self.assertTrue(hasattr(self.module, '__all__'))
|
91 | 94 | target_api = ["warn", "warn_explicit", "showwarning",
|
92 | 95 | "formatwarning", "filterwarnings", "simplefilter",
|
93 |
| - "resetwarnings", "catch_warnings"] |
| 96 | + "resetwarnings", "catch_warnings", "deprecated"] |
94 | 97 | self.assertSetEqual(set(self.module.__all__),
|
95 | 98 | set(target_api))
|
96 | 99 |
|
@@ -1377,6 +1380,283 @@ def test_late_resource_warning(self):
|
1377 | 1380 | self.assertTrue(err.startswith(expected), ascii(err))
|
1378 | 1381 |
|
1379 | 1382 |
|
| 1383 | +class DeprecatedTests(unittest.TestCase): |
| 1384 | + def test_dunder_deprecated(self): |
| 1385 | + @deprecated("A will go away soon") |
| 1386 | + class A: |
| 1387 | + pass |
| 1388 | + |
| 1389 | + self.assertEqual(A.__deprecated__, "A will go away soon") |
| 1390 | + self.assertIsInstance(A, type) |
| 1391 | + |
| 1392 | + @deprecated("b will go away soon") |
| 1393 | + def b(): |
| 1394 | + pass |
| 1395 | + |
| 1396 | + self.assertEqual(b.__deprecated__, "b will go away soon") |
| 1397 | + self.assertIsInstance(b, types.FunctionType) |
| 1398 | + |
| 1399 | + @overload |
| 1400 | + @deprecated("no more ints") |
| 1401 | + def h(x: int) -> int: ... |
| 1402 | + @overload |
| 1403 | + def h(x: str) -> str: ... |
| 1404 | + def h(x): |
| 1405 | + return x |
| 1406 | + |
| 1407 | + overloads = get_overloads(h) |
| 1408 | + self.assertEqual(len(overloads), 2) |
| 1409 | + self.assertEqual(overloads[0].__deprecated__, "no more ints") |
| 1410 | + |
| 1411 | + def test_class(self): |
| 1412 | + @deprecated("A will go away soon") |
| 1413 | + class A: |
| 1414 | + pass |
| 1415 | + |
| 1416 | + with self.assertWarnsRegex(DeprecationWarning, "A will go away soon"): |
| 1417 | + A() |
| 1418 | + with self.assertWarnsRegex(DeprecationWarning, "A will go away soon"): |
| 1419 | + with self.assertRaises(TypeError): |
| 1420 | + A(42) |
| 1421 | + |
| 1422 | + def test_class_with_init(self): |
| 1423 | + @deprecated("HasInit will go away soon") |
| 1424 | + class HasInit: |
| 1425 | + def __init__(self, x): |
| 1426 | + self.x = x |
| 1427 | + |
| 1428 | + with self.assertWarnsRegex(DeprecationWarning, "HasInit will go away soon"): |
| 1429 | + instance = HasInit(42) |
| 1430 | + self.assertEqual(instance.x, 42) |
| 1431 | + |
| 1432 | + def test_class_with_new(self): |
| 1433 | + has_new_called = False |
| 1434 | + |
| 1435 | + @deprecated("HasNew will go away soon") |
| 1436 | + class HasNew: |
| 1437 | + def __new__(cls, x): |
| 1438 | + nonlocal has_new_called |
| 1439 | + has_new_called = True |
| 1440 | + return super().__new__(cls) |
| 1441 | + |
| 1442 | + def __init__(self, x) -> None: |
| 1443 | + self.x = x |
| 1444 | + |
| 1445 | + with self.assertWarnsRegex(DeprecationWarning, "HasNew will go away soon"): |
| 1446 | + instance = HasNew(42) |
| 1447 | + self.assertEqual(instance.x, 42) |
| 1448 | + self.assertTrue(has_new_called) |
| 1449 | + |
| 1450 | + def test_class_with_inherited_new(self): |
| 1451 | + new_base_called = False |
| 1452 | + |
| 1453 | + class NewBase: |
| 1454 | + def __new__(cls, x): |
| 1455 | + nonlocal new_base_called |
| 1456 | + new_base_called = True |
| 1457 | + return super().__new__(cls) |
| 1458 | + |
| 1459 | + def __init__(self, x) -> None: |
| 1460 | + self.x = x |
| 1461 | + |
| 1462 | + @deprecated("HasInheritedNew will go away soon") |
| 1463 | + class HasInheritedNew(NewBase): |
| 1464 | + pass |
| 1465 | + |
| 1466 | + with self.assertWarnsRegex(DeprecationWarning, "HasInheritedNew will go away soon"): |
| 1467 | + instance = HasInheritedNew(42) |
| 1468 | + self.assertEqual(instance.x, 42) |
| 1469 | + self.assertTrue(new_base_called) |
| 1470 | + |
| 1471 | + def test_class_with_new_but_no_init(self): |
| 1472 | + new_called = False |
| 1473 | + |
| 1474 | + @deprecated("HasNewNoInit will go away soon") |
| 1475 | + class HasNewNoInit: |
| 1476 | + def __new__(cls, x): |
| 1477 | + nonlocal new_called |
| 1478 | + new_called = True |
| 1479 | + obj = super().__new__(cls) |
| 1480 | + obj.x = x |
| 1481 | + return obj |
| 1482 | + |
| 1483 | + with self.assertWarnsRegex(DeprecationWarning, "HasNewNoInit will go away soon"): |
| 1484 | + instance = HasNewNoInit(42) |
| 1485 | + self.assertEqual(instance.x, 42) |
| 1486 | + self.assertTrue(new_called) |
| 1487 | + |
| 1488 | + def test_mixin_class(self): |
| 1489 | + @deprecated("Mixin will go away soon") |
| 1490 | + class Mixin: |
| 1491 | + pass |
| 1492 | + |
| 1493 | + class Base: |
| 1494 | + def __init__(self, a) -> None: |
| 1495 | + self.a = a |
| 1496 | + |
| 1497 | + with self.assertWarnsRegex(DeprecationWarning, "Mixin will go away soon"): |
| 1498 | + class Child(Base, Mixin): |
| 1499 | + pass |
| 1500 | + |
| 1501 | + instance = Child(42) |
| 1502 | + self.assertEqual(instance.a, 42) |
| 1503 | + |
| 1504 | + def test_existing_init_subclass(self): |
| 1505 | + @deprecated("C will go away soon") |
| 1506 | + class C: |
| 1507 | + def __init_subclass__(cls) -> None: |
| 1508 | + cls.inited = True |
| 1509 | + |
| 1510 | + with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"): |
| 1511 | + C() |
| 1512 | + |
| 1513 | + with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"): |
| 1514 | + class D(C): |
| 1515 | + pass |
| 1516 | + |
| 1517 | + self.assertTrue(D.inited) |
| 1518 | + self.assertIsInstance(D(), D) # no deprecation |
| 1519 | + |
| 1520 | + def test_existing_init_subclass_in_base(self): |
| 1521 | + class Base: |
| 1522 | + def __init_subclass__(cls, x) -> None: |
| 1523 | + cls.inited = x |
| 1524 | + |
| 1525 | + @deprecated("C will go away soon") |
| 1526 | + class C(Base, x=42): |
| 1527 | + pass |
| 1528 | + |
| 1529 | + self.assertEqual(C.inited, 42) |
| 1530 | + |
| 1531 | + with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"): |
| 1532 | + C() |
| 1533 | + |
| 1534 | + with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"): |
| 1535 | + class D(C, x=3): |
| 1536 | + pass |
| 1537 | + |
| 1538 | + self.assertEqual(D.inited, 3) |
| 1539 | + |
| 1540 | + def test_init_subclass_has_correct_cls(self): |
| 1541 | + init_subclass_saw = None |
| 1542 | + |
| 1543 | + @deprecated("Base will go away soon") |
| 1544 | + class Base: |
| 1545 | + def __init_subclass__(cls) -> None: |
| 1546 | + nonlocal init_subclass_saw |
| 1547 | + init_subclass_saw = cls |
| 1548 | + |
| 1549 | + self.assertIsNone(init_subclass_saw) |
| 1550 | + |
| 1551 | + with self.assertWarnsRegex(DeprecationWarning, "Base will go away soon"): |
| 1552 | + class C(Base): |
| 1553 | + pass |
| 1554 | + |
| 1555 | + self.assertIs(init_subclass_saw, C) |
| 1556 | + |
| 1557 | + def test_init_subclass_with_explicit_classmethod(self): |
| 1558 | + init_subclass_saw = None |
| 1559 | + |
| 1560 | + @deprecated("Base will go away soon") |
| 1561 | + class Base: |
| 1562 | + @classmethod |
| 1563 | + def __init_subclass__(cls) -> None: |
| 1564 | + nonlocal init_subclass_saw |
| 1565 | + init_subclass_saw = cls |
| 1566 | + |
| 1567 | + self.assertIsNone(init_subclass_saw) |
| 1568 | + |
| 1569 | + with self.assertWarnsRegex(DeprecationWarning, "Base will go away soon"): |
| 1570 | + class C(Base): |
| 1571 | + pass |
| 1572 | + |
| 1573 | + self.assertIs(init_subclass_saw, C) |
| 1574 | + |
| 1575 | + def test_function(self): |
| 1576 | + @deprecated("b will go away soon") |
| 1577 | + def b(): |
| 1578 | + pass |
| 1579 | + |
| 1580 | + with self.assertWarnsRegex(DeprecationWarning, "b will go away soon"): |
| 1581 | + b() |
| 1582 | + |
| 1583 | + def test_method(self): |
| 1584 | + class Capybara: |
| 1585 | + @deprecated("x will go away soon") |
| 1586 | + def x(self): |
| 1587 | + pass |
| 1588 | + |
| 1589 | + instance = Capybara() |
| 1590 | + with self.assertWarnsRegex(DeprecationWarning, "x will go away soon"): |
| 1591 | + instance.x() |
| 1592 | + |
| 1593 | + def test_property(self): |
| 1594 | + class Capybara: |
| 1595 | + @property |
| 1596 | + @deprecated("x will go away soon") |
| 1597 | + def x(self): |
| 1598 | + pass |
| 1599 | + |
| 1600 | + @property |
| 1601 | + def no_more_setting(self): |
| 1602 | + return 42 |
| 1603 | + |
| 1604 | + @no_more_setting.setter |
| 1605 | + @deprecated("no more setting") |
| 1606 | + def no_more_setting(self, value): |
| 1607 | + pass |
| 1608 | + |
| 1609 | + instance = Capybara() |
| 1610 | + with self.assertWarnsRegex(DeprecationWarning, "x will go away soon"): |
| 1611 | + instance.x |
| 1612 | + |
| 1613 | + with py_warnings.catch_warnings(): |
| 1614 | + py_warnings.simplefilter("error") |
| 1615 | + self.assertEqual(instance.no_more_setting, 42) |
| 1616 | + |
| 1617 | + with self.assertWarnsRegex(DeprecationWarning, "no more setting"): |
| 1618 | + instance.no_more_setting = 42 |
| 1619 | + |
| 1620 | + def test_category(self): |
| 1621 | + @deprecated("c will go away soon", category=RuntimeWarning) |
| 1622 | + def c(): |
| 1623 | + pass |
| 1624 | + |
| 1625 | + with self.assertWarnsRegex(RuntimeWarning, "c will go away soon"): |
| 1626 | + c() |
| 1627 | + |
| 1628 | + def test_turn_off_warnings(self): |
| 1629 | + @deprecated("d will go away soon", category=None) |
| 1630 | + def d(): |
| 1631 | + pass |
| 1632 | + |
| 1633 | + with py_warnings.catch_warnings(): |
| 1634 | + py_warnings.simplefilter("error") |
| 1635 | + d() |
| 1636 | + |
| 1637 | + def test_only_strings_allowed(self): |
| 1638 | + with self.assertRaisesRegex( |
| 1639 | + TypeError, |
| 1640 | + "Expected an object of type str for 'message', not 'type'" |
| 1641 | + ): |
| 1642 | + @deprecated |
| 1643 | + class Foo: ... |
| 1644 | + |
| 1645 | + with self.assertRaisesRegex( |
| 1646 | + TypeError, |
| 1647 | + "Expected an object of type str for 'message', not 'function'" |
| 1648 | + ): |
| 1649 | + @deprecated |
| 1650 | + def foo(): ... |
| 1651 | + |
| 1652 | + def test_no_retained_references_to_wrapper_instance(self): |
| 1653 | + @deprecated('depr') |
| 1654 | + def d(): pass |
| 1655 | + |
| 1656 | + self.assertFalse(any( |
| 1657 | + isinstance(cell.cell_contents, deprecated) for cell in d.__closure__ |
| 1658 | + )) |
| 1659 | + |
1380 | 1660 | def setUpModule():
|
1381 | 1661 | py_warnings.onceregistry.clear()
|
1382 | 1662 | c_warnings.onceregistry.clear()
|
|
0 commit comments