|
4 | 4 | # -*- coding: utf-8 -*- |
5 | 5 |
|
6 | 6 | import os, uuid |
| 7 | +from datetime import datetime |
7 | 8 | from decimal import Decimal |
8 | 9 | from typing import Iterator |
9 | 10 |
|
@@ -595,6 +596,96 @@ def convert(value): |
595 | 596 | assert value == '123.45' |
596 | 597 |
|
597 | 598 |
|
| 599 | +def test_column_converters(): |
| 600 | + """Test the behavior of column-specific converters.""" |
| 601 | + |
| 602 | + # Create some converters. |
| 603 | + def ucconv(raw): return raw.decode().upper() |
| 604 | + class Converters: |
| 605 | + now = datetime.now() |
| 606 | + def __init__(self, name): self.name = name |
| 607 | + def add_name(self, raw): return f"{raw.decode()} {self.name}" |
| 608 | + @staticmethod |
| 609 | + def show_raw(raw): return raw |
| 610 | + @classmethod |
| 611 | + def dt(cls, raw): return cls.now if b"e" in raw else None |
| 612 | + object1 = Converters("Smith") |
| 613 | + object2 = Converters("Čermák") |
| 614 | + |
| 615 | + # Create & populate a table with the test values. |
| 616 | + names = "Leoš", "Kathy", "Renée", "Abdul", "George" |
| 617 | + values = [[v] for v in names] |
| 618 | + conn = connect() |
| 619 | + cursor1 = conn.cursor() |
| 620 | + cursor2 = conn.cursor() |
| 621 | + cursor1.execute("drop table if exists t1") |
| 622 | + conn.commit() |
| 623 | + cursor1.execute("create table t1(v varchar(50))") |
| 624 | + cursor1.executemany("insert into t1 values (?)", values) |
| 625 | + |
| 626 | + # Confirm that the documented exceptions get raised. |
| 627 | + with pytest.raises(RuntimeError): |
| 628 | + cursor1.set_column_converter(0, ucconv) |
| 629 | + cursor1.execute("select v, v, v, v, v from t1") |
| 630 | + cursor2.execute("select v, v, v, v, v from t1") |
| 631 | + with pytest.raises(IndexError): |
| 632 | + cursor1.set_column_converter(-1, ucconv) |
| 633 | + with pytest.raises(IndexError): |
| 634 | + cursor1.set_column_converter(5, ucconv) |
| 635 | + with pytest.raises(TypeError): |
| 636 | + cursor1.set_column_converter(0, "not callable") |
| 637 | + |
| 638 | + # Register initial conversions. |
| 639 | + cursor1.set_column_converter(0, Converters.dt) |
| 640 | + cursor1.set_column_converter(1, ucconv) |
| 641 | + cursor1.set_column_converter(2, object1.add_name) |
| 642 | + cursor1.set_column_converter(3, Converters.show_raw) |
| 643 | + cursor2.set_column_converter(0, ucconv) |
| 644 | + cursor2.set_column_converter(1, Converters.dt) |
| 645 | + cursor2.set_column_converter(3, Converters.show_raw) |
| 646 | + cursor2.set_column_converter(4, object2.add_name) |
| 647 | + |
| 648 | + # Create the validation tests. |
| 649 | + expected_values = { |
| 650 | + "cursor1": ( |
| 651 | + (Converters.now, 'LEOŠ', 'Leoš Smith', 'Leoš'.encode(), 'Leoš'), |
| 652 | + ('Kathy', 'KATHY', 'Kathy Smith', b'Kathy', 'Kathy'), |
| 653 | + ('Renée', 'RENÉE', 'Renée Smith', 'Renée'.encode(), 'Renée'), |
| 654 | + ('Abdul', None, 'Abdul Smith', b'Abdul', 'Abdul'), |
| 655 | + ('George', 'George', 'George', 'George', 'George'), |
| 656 | + ), |
| 657 | + "cursor2": ( |
| 658 | + ('LEOŠ', Converters.now, 'Leoš', 'Leoš'.encode(), 'Leoš Čermák'), |
| 659 | + ('KATHY', None, 'Kathy', b'Kathy', 'Kathy Čermák'), |
| 660 | + ('RENÉE', Converters.now, 'Renée'.encode(), 'Renée', 'Renée Čermák'), |
| 661 | + ('ABDUL', 'ABDUL', b'Abdul', 'Abdul', 'Abdul Čermák'), |
| 662 | + ('GEORGE', 'GEORGE', b'George', 'George', 'George Čermák'), |
| 663 | + ), |
| 664 | + } |
| 665 | + def check_row(cursor_name, index, row): |
| 666 | + expected = expected_values[cursor_name][index] |
| 667 | + assert tuple(row) == expected |
| 668 | + |
| 669 | + # Fetch a row at a time interleaving the cursors and modifying converter registrations as we go. |
| 670 | + check_row("cursor1", 0, cursor1.fetchone()) |
| 671 | + check_row("cursor2", 0, cursor2.fetchone()) |
| 672 | + cursor1.set_column_converter(0, None) |
| 673 | + check_row("cursor1", 1, cursor1.fetchone()) |
| 674 | + check_row("cursor2", 1, cursor2.fetchone()) |
| 675 | + cursor2.set_column_converter(2, Converters.show_raw) |
| 676 | + cursor2.set_column_converter(3, None) |
| 677 | + check_row("cursor1", 2, cursor1.fetchone()) |
| 678 | + check_row("cursor2", 2, cursor2.fetchone()) |
| 679 | + cursor1.set_column_converter(1, Converters.dt) |
| 680 | + cursor2.set_column_converter(1, ucconv) |
| 681 | + check_row("cursor1", 3, cursor1.fetchone()) |
| 682 | + check_row("cursor2", 3, cursor2.fetchone()) |
| 683 | + for i in range(len(cursor1.description)): |
| 684 | + cursor1.set_column_converter(i, None) |
| 685 | + check_row("cursor1", 4, cursor1.fetchone()) |
| 686 | + check_row("cursor2", 4, cursor2.fetchone()) |
| 687 | + |
| 688 | + |
598 | 689 | def test_refcount_encoding(): |
599 | 690 | """ |
600 | 691 | Ensure we handle the reference count to `encoding` properly. In the past we freed a |
|
0 commit comments