Skip to content

Bluetooth socket additions in Python 3.14 #15213

Open

Description

@dlech

Python 3.14 added some additional constants to the socket module for Bluetooth-related sockets.

Related documentation sections:

Changes needed:

  • _socket._Address needs to add int to the union because of "device_id not packed in a tuple is now accepted." for BTPROTO_HCI where device_id is an integer.
  • Windows gets new constants SOL_RFCOMM, SO_BTH_ENCRYPT, SO_BTH_MTU, SO_BTH_MTU_MAX, SO_BTH_MTU_MIN.
  • Linux gets new constants BDADDR_BREDR, BDADDR_LE_PUBLIC, BDADDR_LE_RANDOM, SOL_BLUETOOTH, SOL_L2CAP, SOL_RFCOMM, SOL_SCO, L2CAP_LM, L2CAP_LM_AUTH, L2CAP_LM_ENCRYPT, L2CAP_LM_MASTER, L2CAP_LM_RELIABLE, L2CAP_LM_SECURE, L2CAP_LM_TRUSTED, BT_CHANNEL_POLICY, BT_CHANNEL_POLICY_BREDR_ONLY, BT_CHANNEL_POLICY_BREDR_PREFERRED, BT_CODEC, BT_DEFER_SETUP, BT_FLUSHABLE, BT_FLUSHABLE_OFF, BT_FLUSHABLE_ON, BT_ISO_QOS, BT_MODE, BT_MODE_BASIC, BT_MODE_ERTM, BT_MODE_EXT_FLOWCTL, BT_MODE_LE_FLOWCTL, BT_MODE_STREAMING, BT_PHY, BT_PHY_BR_1M_1SLOT, BT_PHY_BR_1M_3SLOT, BT_PHY_BR_1M_5SLOT, BT_PHY_EDR_2M_1SLOT, BT_PHY_EDR_2M_3SLOT, BT_PHY_EDR_2M_5SLOT, BT_PHY_EDR_3M_1SLOT, BT_PHY_EDR_3M_3SLOT, BT_PHY_EDR_3M_5SLOT, BT_PHY_LE_1M_RX, BT_PHY_LE_1M_TX, BT_PHY_LE_2M_RX, BT_PHY_LE_2M_TX, BT_PHY_LE_CODED_RX, BT_PHY_LE_CODED_TX, BT_PKT_STATUS, BT_POWER, BT_POWER_FORCE_ACTIVE_OFF, BT_POWER_FORCE_ACTIVE_ON, BT_RCVMTU, BT_SECURITY, BT_SECURITY_FIPS, BT_SECURITY_HIGH, BT_SECURITY_LOW, BT_SECURITY_MEDIUM, BT_SECURITY_SDP, BT_SNDMTU, BT_VOICE, BT_VOICE_CVSD_16BIT, BT_VOICE_TRANSPARENT, BT_VOICE_TRANSPARENT_16BIT, HCI_CHANNEL_CONTROL, HCI_CHANNEL_LOGGING, HCI_CHANNEL_MONITOR, HCI_CHANNEL_RAW, HCI_CHANNEL_USER, HCI_DEV_NONE
  • TODO: need someone with BSD to do a runtime check there

Runtime tests where info above comes from:

Windows 11

Python 3.14.2 (tags/v3.14.2:df79316, Dec  5 2025, 17:18:21) [MSC v.1944 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> [x for x in socket.__all__ if x.startswith("SOL_")]
['SOL_IP', 'SOL_RFCOMM', 'SOL_SOCKET', 'SOL_TCP', 'SOL_UDP']
>>> socket.SOL_RFCOMM
3
>>> [x for x in socket.__all__ if x.startswith("SO_")]
['SO_ACCEPTCONN', 'SO_BROADCAST', 'SO_BTH_ENCRYPT', 'SO_BTH_MTU', 'SO_BTH_MTU_MAX', 'SO_BTH_MTU_MIN', 'SO_DEBUG', 'SO_DONTROUTE', 'SO_ERROR', 'SO_EXCLUSIVEADDRUSE', 'SO_KEEPALIVE', 'SO_LINGER', 'SO_OOBINLINE', 'SO_ORIGINAL_DST', 'SO_RCVBUF', 'SO_RCVLOWAT', 'SO_RCVTIMEO', 'SO_REUSEADDR', 'SO_SNDBUF', 'SO_SNDLOWAT', 'SO_SNDTIMEO', 'SO_TYPE', 'SO_USELOOPBACK']
>>> socket.SO_BTH_ENCRYPT, socket.SO_BTH_MTU, socket.SO_BTH_MTU_MAX, socket.SO_BTH_MTU_MIN
(2, -2147483641, -2147483640, -2147483638)

Debian Testing

Python 3.14.2 (main, Dec  8 2025, 11:53:33) [GCC 15.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import socket
>>> [x for x in socket.__all__ if x.startswith("BDADDR_")]
['BDADDR_ANY', 'BDADDR_BREDR', 'BDADDR_LE_PUBLIC', 'BDADDR_LE_RANDOM', 'BDADDR_LOCAL']
>>> socket.BDADDR_BREDR, socket.BDADDR_LE_PUBLIC, socket.BDADDR_LE_RANDOM
(0, 1, 2)
>>> [x for x in socket.__all__ if x.startswith("SO_")]
['SO_ACCEPTCONN', 'SO_BINDTODEVICE', 'SO_BINDTOIFINDEX', 'SO_BROADCAST', 'SO_DEBUG', 'SO_DOMAIN', 'SO_DONTROUTE', 'SO_ERROR', 'SO_INCOMING_CPU', 'SO_J1939_ERRQUEUE', 'SO_J1939_FILTER', 'SO_J1939_PROMISC', 'SO_J1939_SEND_PRIO', 'SO_KEEPALIVE', 'SO_LINGER', 'SO_MARK', 'SO_OOBINLINE', 'SO_ORIGINAL_DST', 'SO_PASSCRED', 'SO_PASSSEC', 'SO_PEERCRED', 'SO_PEERSEC', 'SO_PRIORITY', 'SO_PROTOCOL', 'SO_RCVBUF', 'SO_RCVLOWAT', 'SO_RCVTIMEO', 'SO_REUSEADDR', 'SO_REUSEPORT', 'SO_SNDBUF', 'SO_SNDLOWAT', 'SO_SNDTIMEO', 'SO_TYPE', 'SO_VM_SOCKETS_BUFFER_MAX_SIZE', 'SO_VM_SOCKETS_BUFFER_MIN_SIZE', 'SO_VM_SOCKETS_BUFFER_SIZE']
>>> {x: eval(f"socket.{x}") for x in socket.__all__ if x.startswith("SOL_")}
{'SOL_ALG': 279, 'SOL_BLUETOOTH': 274, 'SOL_CAN_BASE': 100, 'SOL_CAN_RAW': 101, 'SOL_HCI': 0, 'SOL_IP': 0, 'SOL_L2CAP': 6, 'SOL_RDS': 276, 'SOL_RFCOMM': 18, 'SOL_SCO': 17, 'SOL_SOCKET': 1, 'SOL_TCP': 6, 'SOL_TIPC': 271, 'SOL_UDP': 17}
>>> {x: eval(f"socket.{x}") for x in socket.__all__ if x.startswith("L2CAP_")}
{'L2CAP_LM': 3, 'L2CAP_LM_AUTH': 2, 'L2CAP_LM_ENCRYPT': 4, 'L2CAP_LM_MASTER': 1, 'L2CAP_LM_RELIABLE': 16, 'L2CAP_LM_SECURE': 32, 'L2CAP_LM_TRUSTED': 8}
>>> {x: eval(f"socket.{x}") for x in socket.__all__ if x.startswith("BT_")}
{'BT_CHANNEL_POLICY': 10, 'BT_CHANNEL_POLICY_BREDR_ONLY': 0, 'BT_CHANNEL_POLICY_BREDR_PREFERRED': 1, 'BT_CODEC': 19, 'BT_DEFER_SETUP': 7, 'BT_FLUSHABLE': 8, 'BT_FLUSHABLE_OFF': 0, 'BT_FLUSHABLE_ON': 1, 'BT_ISO_QOS': 17, 'BT_MODE': 15, 'BT_MODE_BASIC': 0, 'BT_MODE_ERTM': 1, 'BT_MODE_EXT_FLOWCTL': 4, 'BT_MODE_LE_FLOWCTL': 3, 'BT_MODE_STREAMING': 2, 'BT_PHY': 14, 'BT_PHY_BR_1M_1SLOT': 1, 'BT_PHY_BR_1M_3SLOT': 2, 'BT_PHY_BR_1M_5SLOT': 4, 'BT_PHY_EDR_2M_1SLOT': 8, 'BT_PHY_EDR_2M_3SLOT': 16, 'BT_PHY_EDR_2M_5SLOT': 32, 'BT_PHY_EDR_3M_1SLOT': 64, 'BT_PHY_EDR_3M_3SLOT': 128, 'BT_PHY_EDR_3M_5SLOT': 256, 'BT_PHY_LE_1M_RX': 1024, 'BT_PHY_LE_1M_TX': 512, 'BT_PHY_LE_2M_RX': 4096, 'BT_PHY_LE_2M_TX': 2048, 'BT_PHY_LE_CODED_RX': 16384, 'BT_PHY_LE_CODED_TX': 8192, 'BT_PKT_STATUS': 16, 'BT_POWER': 9, 'BT_POWER_FORCE_ACTIVE_OFF': 0, 'BT_POWER_FORCE_ACTIVE_ON': 1, 'BT_RCVMTU': 13, 'BT_SECURITY': 4, 'BT_SECURITY_FIPS': 4, 'BT_SECURITY_HIGH': 3, 'BT_SECURITY_LOW': 1, 'BT_SECURITY_MEDIUM': 2, 'BT_SECURITY_SDP': 0, 'BT_SNDMTU': 12, 'BT_VOICE': 11, 'BT_VOICE_CVSD_16BIT': 96, 'BT_VOICE_TRANSPARENT': 3, 'BT_VOICE_TRANSPARENT_16BIT': 99}
>>> {x: eval(f"socket.{x}") for x in socket.__all__ if x.startswith("HCI_")}
{'HCI_CHANNEL_CONTROL': 3, 'HCI_CHANNEL_LOGGING': 4, 'HCI_CHANNEL_MONITOR': 2, 'HCI_CHANNEL_RAW': 0, 'HCI_CHANNEL_USER': 1, 'HCI_DATA_DIR': 1, 'HCI_DEV_NONE': 65535, 'HCI_FILTER': 2, 'HCI_TIME_STAMP': 3}

Activity

  1. srittau commented on Jan 15, 2026

    @srittau
    Collaborator

    Any PRs to add missing items are welcome!

  2. added
    stubs: incompleteAnnotations or sub-modules missing from an existing package or module
    on Jan 15, 2026
  3. Divyanshudevtalla commented on Jan 27, 2026

    @Divyanshudevtalla

    Hi! I’m new to typeshed and would like to work on this issue if it`s still available.

  4. srittau commented on Jan 27, 2026

    @srittau
    Collaborator

    All typeshed issues are free-for-all, unless they are specifically assigned to some one.

  5. dlech commented on Jan 27, 2026

    @dlech
    Author

    @Divyanshudevtalla, if you are going to work on this, I suggest to read the discussion in #15209. It will help to understand some of the thinking behind why some of the Bluetooth stuff is the way it is in the code. It is a bit unusual because some builds of Python don't include these Bluetooth constants (like the ones on GitHub CI and the ones installed by uv) in addition to the cross-platform and Python version considerations.

  6. Divyanshudevtalla commented on Jan 27, 2026

    @Divyanshudevtalla

    Hi @dlech, thank you for the guidance! I've read through the discussion in #15209 and have followed the suggested approach for organizing these constants. I have now raised a Pull Request here: #15330 (comment) l. Could you please take a look when you have a moment? Thanks!

  7. Divyanshudevtalla commented on Jan 28, 2026

    @Divyanshudevtalla

    Hi @dlech, I have been working on resolving the CI failures and have managed to clear about 44 of them. I've added the Bluetooth constants to socket.pyi and _socket.pyi under the (3, 14) guard and updated the stubtest allowlist.

    However, I'm currently stuck with 19 remaining failures related to redefinition errors. Even though I've removed the duplicate blocks I found, the logs still show conflicts between the new definitions (Line 586) and existing ones (Line 677).

    Since I'm a student contributor, could you please guide me on whether I should be moving the existing constants into the new version-guarded block, or if there's a specific organization for these Bluetooth stubs that I'm missing? Thank you for your patience!

  8. Divyanshudevtalla commented on Jan 28, 2026

    @Divyanshudevtalla

    Hi @dlech, I've realized that my current branch has become quite cluttered with merge conflicts and duplicate constants, which is causing many of the current CI failures.
    ​I've learned a lot about the typeshed workflow and Git commands like commit --amend and push --force during this process. To ensure the final contribution is clean and professional, I would like to restart this repository from a fresh branch. I'm confident I can apply what I've learned to work more effectively this time. Thank you for your patience

  9. dlech commented on Jun 11, 2026

    @dlech
    Author

    I suggest to re-open this since only Linux was addressed in the commit that automatically closed the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    stubs: incompleteAnnotations or sub-modules missing from an existing package or module

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions