diff -pruN 0.31.2+dfsg1-4/.cz.yaml 0.31.4+dfsg1-1/.cz.yaml
--- 0.31.2+dfsg1-4/.cz.yaml	2023-08-29 11:34:42.000000000 +0000
+++ 0.31.4+dfsg1-1/.cz.yaml	2025-08-27 13:39:30.000000000 +0000
@@ -1,7 +1,8 @@
+---
 commitizen:
   name: cz_conventional_commits
   tag_format: v$version
-  version: 0.31.2
+  version: 0.31.4
   version_files:
   - setup.py
   - docs/conf.py
diff -pruN 0.31.2+dfsg1-4/.github/workflows/publish.yml 0.31.4+dfsg1-1/.github/workflows/publish.yml
--- 0.31.2+dfsg1-4/.github/workflows/publish.yml	1970-01-01 00:00:00.000000000 +0000
+++ 0.31.4+dfsg1-1/.github/workflows/publish.yml	2025-08-27 13:39:30.000000000 +0000
@@ -0,0 +1,39 @@
+name: Publish Python 🐍 distributions 📦 to PyPI
+on: push
+jobs:
+  build-n-publish:
+    name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@master
+      - name: Set up Python 3.10
+        uses: actions/setup-python@v3
+        with:
+          python-version: "3.10"
+      - name: Setup pandoc for changelog conversion
+        run: sudo apt update && sudo apt install -y pandoc
+      - name: Write pypi's readme
+        run: |
+          cat README.rst > to-pypi.rst
+          echo "" >> to-pypi.rst
+          pandoc -s --to rst -o /dev/stdout CHANGELOG.md | tee -a to-pypi.rst
+          mv to-pypi.rst README.rst
+      - name: Install pypa/build
+        run: >-
+          python -m
+          pip install
+          build
+          --user
+      - name: Build a binary wheel and a source tarball
+        run: >-
+          python -m
+          build
+          --sdist
+          --wheel
+          --outdir dist/
+          .
+      - name: Publish distribution 📦 to PyPI
+        if: startsWith(github.ref, 'refs/tags')
+        uses: pypa/gh-action-pypi-publish@release/v1
+        with:
+          password: ${{ secrets.PYPI_API_TOKEN }}
diff -pruN 0.31.2+dfsg1-4/.github/workflows/tests-and-lint.yml 0.31.4+dfsg1-1/.github/workflows/tests-and-lint.yml
--- 0.31.2+dfsg1-4/.github/workflows/tests-and-lint.yml	2023-08-29 11:34:42.000000000 +0000
+++ 0.31.4+dfsg1-1/.github/workflows/tests-and-lint.yml	2025-08-27 13:39:30.000000000 +0000
@@ -12,12 +12,12 @@ jobs:
     strategy:
       max-parallel: 4
       matrix:
-        python-version: [2.7, 3.6, 3.7, 3.8, 3.9, "3.10"]
+        python-version: [3.7, 3.8, 3.9, "3.10", 3.11]
 
     steps:
       - uses: actions/checkout@v2
       - name: Set up Python ${{ matrix.python-version }}
-        uses: actions/setup-python@v2
+        uses: actions/setup-python@v5
         with:
           python-version: ${{ matrix.python-version }}
       - name: Install Dependencies
diff -pruN 0.31.2+dfsg1-4/bmemcached/protocol.py 0.31.4+dfsg1-1/bmemcached/protocol.py
--- 0.31.2+dfsg1-4/bmemcached/protocol.py	2023-08-29 11:34:42.000000000 +0000
+++ 0.31.4+dfsg1-1/bmemcached/protocol.py	2025-08-27 13:39:30.000000000 +0000
@@ -1,15 +1,15 @@
 from datetime import datetime, timedelta
 import logging
-import re
 import socket
 import struct
 import threading
 try:
-    from urllib import splitport  # type: ignore
+    from urlparse import SplitResult  # type: ignore[import-not-found]
 except ImportError:
-    from urllib.parse import splitport  # type: ignore
+    from urllib.parse import SplitResult  # type: ignore[import-not-found]
 
 import zlib
+from ipaddress import ip_address
 from io import BytesIO
 import six
 from six import binary_type, text_type
@@ -145,9 +145,7 @@ class Protocol(threading.local):
 
         try:
             if self.host:
-                self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-                self.connection.settimeout(self.socket_timeout)
-                self.connection.connect((self.host, self.port))
+                self.connection = socket.create_connection((self.host, self.port), self.socket_timeout)
 
                 if self.tls_context:
                     self.connection = self.tls_context.wrap_socket(
@@ -175,18 +173,40 @@ class Protocol(threading.local):
 
         Port defaults to 11211.
 
+        When using IPv6 with a specified port, the address must be enclosed in brackets.
+        If the port is not specified, brackets are optional.
+
         >>> split_host_port('127.0.0.1:11211')
         ('127.0.0.1', 11211)
         >>> split_host_port('127.0.0.1')
         ('127.0.0.1', 11211)
+        >>> split_host_port('::1')
+        ('::1', 11211)
+        >>> split_host_port('[::1]')
+        ('::1', 11211)
+        >>> split_host_port('[::1]:11211')
+        ('::1', 11211)
         """
-        host, port = splitport(server)
-        if port is None:
-            port = 11211
-        port = int(port)
-        if re.search(':.*$', host):
-            host = re.sub(':.*$', '', host)
-        return host, port
+        default_port = 11211
+
+        def is_ip_address(address):
+            try:
+                ip_address(address)
+                return True
+            except ValueError:
+                return False
+
+        if is_ip_address(server):
+            return server, default_port
+
+        if server.startswith('['):
+            host, _, port = server[1:].partition(']')
+            if not is_ip_address(host):
+                raise ValueError('{} is not a valid IPv6 address'.format(server))
+            return host, default_port if not port else int(port.lstrip(':'))
+
+        u = SplitResult("", server, "", "", "")
+        return u.hostname, 11211 if u.port is None else u.port
 
     def _read_socket(self, size):
         """
diff -pruN 0.31.2+dfsg1-4/debian/changelog 0.31.4+dfsg1-1/debian/changelog
--- 0.31.2+dfsg1-4/debian/changelog	2024-12-20 08:56:01.000000000 +0000
+++ 0.31.4+dfsg1-1/debian/changelog	2025-08-27 13:40:06.000000000 +0000
@@ -1,3 +1,9 @@
+python-binary-memcached (0.31.4+dfsg1-1) experimental; urgency=medium
+
+  * New upstream release.
+
+ -- Thomas Goirand <zigo@debian.org>  Wed, 27 Aug 2025 15:40:06 +0200
+
 python-binary-memcached (0.31.2+dfsg1-4) unstable; urgency=medium
 
   * d/watch: use mode=git, dversionmangle=auto (Closes: #1090746).
diff -pruN 0.31.2+dfsg1-4/requirements_test.txt 0.31.4+dfsg1-1/requirements_test.txt
--- 0.31.2+dfsg1-4/requirements_test.txt	2023-08-29 11:34:42.000000000 +0000
+++ 0.31.4+dfsg1-1/requirements_test.txt	2025-08-27 13:39:30.000000000 +0000
@@ -1,7 +1,5 @@
 flake8~=4.0; python_version > '2.7'
 flake8~=3.9; python_version < '3.0'
-m2r~=0.2.1
-mistune<2 # m2r breaks with newer versions
 mock~=4.0; python_version > '2.7'
 mock~=3.0; python_version < '3.0'
 pytest-cov~=3.0; python_version > '2.7'
diff -pruN 0.31.2+dfsg1-4/setup.py 0.31.4+dfsg1-1/setup.py
--- 0.31.2+dfsg1-4/setup.py	2023-08-29 11:34:42.000000000 +0000
+++ 0.31.4+dfsg1-1/setup.py	2025-08-27 13:39:30.000000000 +0000
@@ -14,7 +14,7 @@ version_dependant_requirements = [
 
 setup(
     name="python-binary-memcached",
-    version="0.31.2",
+    version="0.31.4",
     author="Jayson Reis",
     author_email="santosdosreis@gmail.com",
     description="A pure python module to access memcached via its binary protocol with SASL auth support",
diff -pruN 0.31.2+dfsg1-4/test/conftest.py 0.31.4+dfsg1-1/test/conftest.py
--- 0.31.2+dfsg1-4/test/conftest.py	2023-08-29 11:34:42.000000000 +0000
+++ 0.31.4+dfsg1-1/test/conftest.py	2025-08-27 13:39:30.000000000 +0000
@@ -41,3 +41,16 @@ def memcached_socket():
     yield p
     p.kill()
     p.wait()
+
+
+@pytest.yield_fixture(scope="session", autouse=True)
+def memcached_ipv6():
+    p = subprocess.Popen(
+        ["memcached", "-l::1"],
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+    )
+    time.sleep(0.1)
+    yield p
+    p.kill()
+    p.wait()
diff -pruN 0.31.2+dfsg1-4/test/test_server_parsing.py 0.31.4+dfsg1-1/test/test_server_parsing.py
--- 0.31.2+dfsg1-4/test/test_server_parsing.py	2023-08-29 11:34:42.000000000 +0000
+++ 0.31.4+dfsg1-1/test/test_server_parsing.py	2025-08-27 13:39:30.000000000 +0000
@@ -26,10 +26,38 @@ class TestServerParsing(unittest.TestCas
         self.assertEqual(server.host, os.environ['MEMCACHED_HOST'])
         self.assertEqual(server.port, 11211)
 
-    def testInvalidPort(self):
-        server = bmemcached.protocol.Protocol('{}:blah'.format(os.environ['MEMCACHED_HOST']))
-        self.assertEqual(server.host, os.environ['MEMCACHED_HOST'])
+    def testIPv6(self):
+        server = bmemcached.protocol.Protocol('[::1]')
+        self.assertEqual(server.host, '::1')
+        self.assertEqual(server.port, 11211)
+        server = bmemcached.protocol.Protocol('::1')
+        self.assertEqual(server.host, '::1')
+        self.assertEqual(server.port, 11211)
+        server = bmemcached.protocol.Protocol('[2001:db8::2]')
+        self.assertEqual(server.host, '2001:db8::2')
         self.assertEqual(server.port, 11211)
+        server = bmemcached.protocol.Protocol('2001:db8::2')
+        self.assertEqual(server.host, '2001:db8::2')
+        self.assertEqual(server.port, 11211)
+        # Since `2001:db8::2:8080` is a valid IPv6 address,
+        # it is ambiguous whether to split it into `2001:db8::2` and `8080`
+        # or treat it as `2001:db8::2:8080`.
+        # Therefore, it will be treated as `2001:db8::2:8080`.
+        server = bmemcached.protocol.Protocol('2001:db8::2:8080')
+        self.assertEqual(server.host, '2001:db8::2:8080')
+        self.assertEqual(server.port, 11211)
+        server = bmemcached.protocol.Protocol('[::1]:5000')
+        self.assertEqual(server.host, '::1')
+        self.assertEqual(server.port, 5000)
+        server = bmemcached.protocol.Protocol('[2001:db8::2]:5000')
+        self.assertEqual(server.host, '2001:db8::2')
+        self.assertEqual(server.port, 5000)
+
+    def testInvalidPort(self):
+        with self.assertRaises(ValueError):
+            bmemcached.protocol.Protocol('{}:blah'.format(os.environ['MEMCACHED_HOST']))
+        with self.assertRaises(ValueError):
+            bmemcached.protocol.Protocol('[::1]:blah')
 
     def testNonStandardPort(self):
         server = bmemcached.protocol.Protocol('{}:5000'.format(os.environ['MEMCACHED_HOST']))
