Opened 3 weeks ago

Closed 3 weeks ago

Last modified 3 weeks ago

#36646 closed New feature (fixed)

oracledb 3.4.0 TypeError: isinstance() arg 2 must be a type, a tuple of types, or a union

Reported by: John Wagenleitner Owned by: Mariusz Felisiak
Component: Database layer (models, ORM) Version: 5.2
Severity: Normal Keywords: Oracle
Cc: John Wagenleitner, Mariusz Felisiak, Simon Charette Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

When running Django v5.2.7 with oracledb 3.4.0, the following traceback is generated. In the 3.4.0 release they changed the definition of Binary from an alias to bytes to a function (https://github.com/oracle/python-oracledb/blob/a71468238ea98f38a331afe0a0664b2fa707a294/src/oracledb/constructors.py#L37-L41). The issue does not occur with oracledb 3.3.0.

Traceback (most recent call last):
  File "C:\Users\johnwa\AppData\Roaming\uv\python\cpython-3.13.7-windows-x86_64-none\Lib\threading.py", line 1043, in _bootstrap_inner
    self.run()
    ~~~~~~~~^^
  File "C:\Users\johnwa\AppData\Roaming\uv\python\cpython-3.13.7-windows-x86_64-none\Lib\threading.py", line 994, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\utils\autoreload.py", line 64, in wrapper
    fn(*args, **kwargs)
    ~~^^^^^^^^^^^^^^^^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\core\management\commands\runserver.py", line 137, in inner_run
    self.check_migrations()
    ~~~~~~~~~~~~~~~~~~~~~^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\core\management\base.py", line 587, in check_migrations
    executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\migrations\executor.py", line 18, in __init__
    self.loader = MigrationLoader(self.connection)
                  ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\migrations\loader.py", line 58, in __init__
    self.build_graph()
    ~~~~~~~~~~~~~~~~^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\migrations\loader.py", line 235, in build_graph
    self.applied_migrations = recorder.applied_migrations()
                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\migrations\recorder.py", line 89, in applied_migrations
    if self.has_table():
       ~~~~~~~~~~~~~~^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\migrations\recorder.py", line 63, in has_table
    with self.connection.cursor() as cursor:
         ~~~~~~~~~~~~~~~~~~~~~~^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\utils\asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\backends\base\base.py", line 320, in cursor
    return self._cursor()
           ~~~~~~~~~~~~^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\backends\base\base.py", line 296, in _cursor
    self.ensure_connection()
    ~~~~~~~~~~~~~~~~~~~~~~^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\utils\asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\backends\base\base.py", line 279, in ensure_connection
    self.connect()
    ~~~~~~~~~~~~^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\utils\asyncio.py", line 26, in inner
    return func(*args, **kwargs)
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\backends\base\base.py", line 258, in connect
    self.init_connection_state()
    ~~~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\backends\oracle\base.py", line 336, in init_connection_state
    cursor.execute(
    ~~~~~~~~~~~~~~^
        "SELECT 1 FROM DUAL WHERE DUMMY %s"
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        % self._standard_operators["contains"],
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        ["X"],
        ^^^^^^
    )
    ^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\backends\oracle\base.py", line 630, in execute
    query, params = self._fix_for_params(query, params, unify_by_values=True)
                    ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\backends\oracle\base.py", line 627, in _fix_for_params
    return query, self._format_params(params)
                  ~~~~~~~~~~~~~~~~~~~^^^^^^^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\backends\oracle\base.py", line 555, in _format_params
    return {k: OracleParam(v, self, True) for k, v in params.items()}
               ~~~~~~~~~~~^^^^^^^^^^^^^^^
  File "C:\Projects\rsvp\.venv\Lib\site-packages\django\db\backends\oracle\base.py", line 441, in __init__
    elif isinstance(param, (Database.Binary, datetime.timedelta)):
         ~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: isinstance() arg 2 must be a type, a tuple of types, or a union

Change History (11)

comment:1 by Natalia Bidart, 3 weeks ago

Cc: Mariusz Felisiak Simon Charette added
Triage Stage: UnreviewedAccepted
Type: UncategorizedNew feature

Thank you John Wagenleitner, do you have availability to work on the fix? It seems that our Database.Date and Database.Timestamp may also be affected?

Setting as "New Feature" since oracledb 3.4.0 was released October 2025.

For reference, commit 869a887819cdac7fcd610f9d9d463ade49ea7de6 contains:

  • src/oracledb/constructors.py

    a b  
    2929# -----------------------------------------------------------------------------
    3030
    3131import datetime
     32from typing import Any
    3233
    3334from . import errors
    3435
    35 # synonyms for the types mandated by the database API
    36 Binary = bytes
    37 Date = datetime.date
    38 Timestamp = datetime.datetime
    3936
     37def Binary(value: Any) -> bytes:
     38    """
     39    Constructs an object holding a binary (long) string value.
     40    """
     41    return bytes(value)
    4042
    41 def Time(hour: int, minute: int, second: int) -> None:
     43
     44def Date(year: int, month: int, day: int) -> datetime.date:
    4245    """
    43     Constructor mandated by the database API for creating a time value. Since
    44     Oracle doesn't support time only values, an exception is raised when this
    45     method is called.
     46    Constructs an object holding a date value.
    4647    """
    47     errors._raise_err(errors.ERR_TIME_NOT_SUPPORTED)
     48    return datetime.date(year, month, day)
    4849
    4950
    5051def DateFromTicks(ticks: float) -> datetime.date:
    def DateFromTicks(ticks: float) -> datetime.date:  
    5657    return datetime.date.fromtimestamp(ticks)
    5758
    5859
     60def Time(hour: int, minute: int, second: int) -> None:
     61    """
     62    Constructor mandated by the database API for creating a time value. Since
     63    Oracle doesn't support time only values, an exception is raised when this
     64    method is called.
     65    """
     66    errors._raise_err(errors.ERR_TIME_NOT_SUPPORTED)
     67
     68
    5969def TimeFromTicks(ticks: float) -> None:
    6070    """
    6171    Constructor mandated by the database API for creating a time value given
    def TimeFromTicks(ticks: float) -> None:  
    6676    errors._raise_err(errors.ERR_TIME_NOT_SUPPORTED)
    6777
    6878
     79def Timestamp(
     80    year: int,
     81    month: int,
     82    day: int,
     83    hour: int = 0,
     84    minute: int = 0,
     85    second: int = 0,
     86) -> datetime.datetime:
     87    """
     88    Constructs an object holding a time stamp value.
     89    """
     90    return datetime.datetime(year, month, day, hour, minute, second)
     91
     92
    6993def TimestampFromTicks(ticks: float) -> datetime.datetime:
    7094    """
    7195    Constructor mandated by the database API for creating a timestamp value

comment:2 by Mariusz Felisiak, 3 weeks ago

Owner: set to Mariusz Felisiak
Status: newassigned

I can work on that, I'd consider backport to Django 5.2 and 6.0. We were always more flexible with fixing support for database drivers (see #31751 or #30331).

in reply to:  2 comment:3 by Natalia Bidart, 3 weeks ago

Replying to Mariusz Felisiak:

I can work on that, I'd consider backport to Django 5.2 and 6.0. We were always more flexible with fixing support for database drivers (see #31751 or #30331).

Thank you Mariusz! Assuming the fix is not too invasive/big/risky, I agree it's OK to backport up to 5.2.

comment:4 by Simon Charette, 3 weeks ago

Has patch: set

Ended up creating the MR after looking at the issue with Mariusz in Palafrugell 🏖️

comment:5 by Mariusz Felisiak, 3 weeks ago

Triage Stage: AcceptedReady for checkin

comment:6 by GitHub <noreply@…>, 3 weeks ago

In 99e03369:

[5.1.x] Refs #36646 -- Doc'd that oracledb < 3.3.0 is required.

comment:7 by Mariusz Felisiak, 3 weeks ago

Patch needs improvement: set
Triage Stage: Ready for checkinAccepted

comment:8 by Mariusz Felisiak, 3 weeks ago

Patch needs improvement: unset
Triage Stage: AcceptedReady for checkin

comment:9 by Mariusz Felisiak <felisiak.mariusz@…>, 3 weeks ago

Resolution: fixed
Status: assignedclosed

In 315dbe6:

Fixed #36646 -- Added compatibility for oracledb 3.4.0.

The Database.Binary, Date, and Timestamp attributes were changed from
aliases to bytes, datetime.date, and datetime.datetime to factory
functions in oracle/python-oracledb@869a887819cdac7fcd610f9d9d463ade49ea7
which made their usage inadequate for isinstance checks.

Thanks John Wagenleitner for the report and Natalia for the triage.

Co-authored-by: Mariusz Felisiak <felisiak.mariusz@…>

comment:10 by Mariusz Felisiak <felisiak.mariusz@…>, 3 weeks ago

In c4309f46:

[6.0.x] Fixed #36646 -- Added compatibility for oracledb 3.4.0.

The Database.Binary, Date, and Timestamp attributes were changed from
aliases to bytes, datetime.date, and datetime.datetime to factory
functions in oracle/python-oracledb@869a887819cdac7fcd610f9d9d463ade49ea7
which made their usage inadequate for isinstance checks.

Thanks John Wagenleitner for the report and Natalia for the triage.

Co-authored-by: Mariusz Felisiak <felisiak.mariusz@…>

Backport of 315dbe675df338ae66c8fa43274a76ecbed7ef67 from main

comment:11 by Mariusz Felisiak <felisiak.mariusz@…>, 3 weeks ago

In 88ef9ea:

[5.2.x] Fixed #36646 -- Added compatibility for oracledb 3.4.0.

The Database.Binary, Date, and Timestamp attributes were changed from
aliases to bytes, datetime.date, and datetime.datetime to factory
functions in oracle/python-oracledb@869a887819cdac7fcd610f9d9d463ade49ea7
which made their usage inadequate for isinstance checks.

Thanks John Wagenleitner for the report and Natalia for the triage.

Co-authored-by: Mariusz Felisiak <felisiak.mariusz@…>

Backport of 315dbe675df338ae66c8fa43274a76ecbed7ef67 from main

Note: See TracTickets for help on using tickets.
Back to Top