From 119d3cdb99fd35827c1a1d5a7298d3f8d86a840e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Fri, 22 Aug 2025 15:17:46 -0300 Subject: [PATCH 01/10] fix: use `PyQt6` instead of `PyQt5` --- ci/deps/actions-311-minimum_versions.yaml | 3 +- ci/deps/actions-311.yaml | 2 +- ci/deps/actions-312.yaml | 2 +- ci/deps/actions-313-downstream_compat.yaml | 2 +- ci/deps/actions-313.yaml | 2 +- doc/source/getting_started/install.rst | 12 ++-- doc/source/user_guide/io.rst | 2 +- environment.yml | 2 +- pandas/compat/_optional.py | 2 +- pandas/io/clipboard/__init__.py | 84 ++++++++++++++-------- pyproject.toml | 4 +- requirements-dev.txt | 2 +- scripts/generate_pip_deps_from_conda.py | 2 +- scripts/tests/data/deps_minimum.toml | 4 +- 14 files changed, 75 insertions(+), 50 deletions(-) diff --git a/ci/deps/actions-311-minimum_versions.yaml b/ci/deps/actions-311-minimum_versions.yaml index 3ff7601f0c6b3..815531d47a75e 100644 --- a/ci/deps/actions-311-minimum_versions.yaml +++ b/ci/deps/actions-311-minimum_versions.yaml @@ -46,7 +46,8 @@ dependencies: - pyarrow=12.0.1 - pyiceberg=0.7.1 - pymysql=1.1.0 - - pyqt=5.15.9 + - pyqt=6.9.1 + - pyqt6=6.9.1 - pyreadstat=1.2.6 - pytables=3.8.0 - python-calamine=0.1.7 diff --git a/ci/deps/actions-311.yaml b/ci/deps/actions-311.yaml index f1a16bfc97656..01ece922b0f0e 100644 --- a/ci/deps/actions-311.yaml +++ b/ci/deps/actions-311.yaml @@ -39,7 +39,7 @@ dependencies: - numexpr>=2.9.0 - odfpy>=1.4.1 - qtpy>=2.3.0 - - pyqt>=5.15.9 + - pyqt>=6.9.1 - openpyxl>=3.1.2 - psycopg2>=2.9.9 - pyarrow>=12.0.1 diff --git a/ci/deps/actions-312.yaml b/ci/deps/actions-312.yaml index 3222f372182ac..01e46bce871fb 100644 --- a/ci/deps/actions-312.yaml +++ b/ci/deps/actions-312.yaml @@ -39,7 +39,7 @@ dependencies: - numexpr>=2.9.0 - odfpy>=1.4.1 - qtpy>=2.3.0 - - pyqt>=5.15.9 + - pyqt>=6.9.1 - openpyxl>=3.1.2 - psycopg2>=2.9.9 - pyarrow>=12.0.1 diff --git a/ci/deps/actions-313-downstream_compat.yaml b/ci/deps/actions-313-downstream_compat.yaml index 663d98020b942..2936cbbd28bc8 100644 --- a/ci/deps/actions-313-downstream_compat.yaml +++ b/ci/deps/actions-313-downstream_compat.yaml @@ -44,7 +44,7 @@ dependencies: - pyarrow>=12.0.1 - pyiceberg>=0.7.1 - pymysql>=1.1.0 - - pyqt>=5.15.9 + - pyqt>=6.9.1 - pyreadstat>=1.2.6 - pytables>=3.8.0 - python-calamine>=0.1.7 diff --git a/ci/deps/actions-313.yaml b/ci/deps/actions-313.yaml index 050d71cbfe45e..022e79b74f3ab 100644 --- a/ci/deps/actions-313.yaml +++ b/ci/deps/actions-313.yaml @@ -40,7 +40,7 @@ dependencies: - numexpr>=2.9.0 - odfpy>=1.4.1 - qtpy>=2.3.0 - - pyqt>=5.15.9 + - pyqt>=6.9.1 - openpyxl>=3.1.2 - psycopg2>=2.9.9 - pyarrow>=12.0.1 diff --git a/doc/source/getting_started/install.rst b/doc/source/getting_started/install.rst index 0002ed869eb31..788f19944f8e1 100644 --- a/doc/source/getting_started/install.rst +++ b/doc/source/getting_started/install.rst @@ -340,12 +340,12 @@ Clipboard Installable with ``pip install "pandas[clipboard]"``. -======================================================================================== ================== =============== ============== -Dependency Minimum Version pip extra Notes -======================================================================================== ================== =============== ============== -`PyQt4 `__/`PyQt5 `__ 5.15.9 clipboard Clipboard I/O -`qtpy `__ 2.3.0 clipboard Clipboard I/O -======================================================================================== ================== =============== ============== +======================================================================================================================================== ================== =============== ============== +Dependency Minimum Version pip extra Notes +======================================================================================================================================== ================== =============== ============== +`PyQt4 `__/`PyQt5 `__/ `PyQt6 `__ 6.9.1 clipboard Clipboard I/O +`qtpy `__ 2.3.0 clipboard Clipboard I/O +======================================================================================================================================== ================== =============== ============== .. note:: diff --git a/doc/source/user_guide/io.rst b/doc/source/user_guide/io.rst index 52038ad4b66c1..82df68490bcba 100644 --- a/doc/source/user_guide/io.rst +++ b/doc/source/user_guide/io.rst @@ -3928,7 +3928,7 @@ We can see that we got the same content back, which we had earlier written to th .. note:: - You may need to install xclip or xsel (with PyQt5, PyQt4 or qtpy) on Linux to use these methods. + You may need to install xclip or xsel (with PyQt6, PyQt5, PyQt4 or qtpy) on Linux to use these methods. .. _io.pickle: diff --git a/environment.yml b/environment.yml index 29ce9e8a03446..532fdaf359678 100644 --- a/environment.yml +++ b/environment.yml @@ -18,7 +18,7 @@ dependencies: - pytest-xdist>=3.4.0 - pytest-qt>=4.4.0 - pytest-localserver - - pyqt>=5.15.9 + - pyqt>=6.9.1 - coverage # required dependencies diff --git a/pandas/compat/_optional.py b/pandas/compat/_optional.py index d15d9c47efe74..b8b13331e7c0f 100644 --- a/pandas/compat/_optional.py +++ b/pandas/compat/_optional.py @@ -55,7 +55,7 @@ "xlsxwriter": "3.2.0", "zstandard": "0.22.0", "qtpy": "2.3.0", - "pyqt5": "5.15.9", + "pyqt6": "6.9.1", } # A mapping from import name to package name (on PyPI) for packages where diff --git a/pandas/io/clipboard/__init__.py b/pandas/io/clipboard/__init__.py index 6491849925e86..ad6bf739af4ae 100644 --- a/pandas/io/clipboard/__init__.py +++ b/pandas/io/clipboard/__init__.py @@ -24,7 +24,7 @@ sudo apt-get install xsel sudo apt-get install wl-clipboard -Otherwise on Linux, you will need the PyQt5 modules installed. +Otherwise on Linux, you will need the PyQt6 modules installed. This module does not work with PyGObject yet. @@ -55,6 +55,7 @@ get_errno, sizeof, ) +import importlib import os import platform from shutil import which as _executable_exists @@ -133,18 +134,55 @@ def paste_osx_pyobjc(): return copy_osx_pyobjc, paste_osx_pyobjc -def init_qt_clipboard(): - global QApplication - # $DISPLAY should exist +def _import_module(modules: list[tuple[str, str | None]]): + """ + Attempt to import from a module from a list inorder. + + Args: + modules: A list of tuples of two elements. The first element + is the module to import from and the second element is + the object to import. If the second element is not provided, + just import the module. + + Returns: + The first successful import. - # Try to import from qtpy, but if that fails try PyQt5 then PyQt4 - try: - from qtpy.QtWidgets import QApplication - except ImportError: + Raises: + ImportError: If couldn't import any module. + AttributeError: If a module doesn't have the expected attribute. + """ + + for module_name, attribute_name in modules: try: - from PyQt5.QtWidgets import QApplication + module = importlib.import_module(module_name) + + if attribute_name is None: + return module + elif hasattr(module, attribute_name): + return getattr(module, attribute_name) + else: + raise AttributeError( + f"Module {module_name} doesn't have attribute {attribute_name}." + ) except ImportError: - from PyQt4.QtGui import QApplication + continue + + raise ImportError( + f"No module from {(module_name for module_name, _ in modules)} could be imported." + ) + + +def init_qt_clipboard(): + # $DISPLAY should exist + global QApplication + + qt_qapplication_bindings = [ + ("qtpy.QtWidgets", "QApplication"), + ("PyQt6.QtWidgets", "QApplication"), + ("PyQt5.QtWidgets", "QApplication"), + ("PyQt4.QtGui", "QApplication"), + ] + QApplication = _import_module(qt_qapplication_bindings) app = QApplication.instance() if app is None: @@ -529,7 +567,7 @@ def determine_clipboard(): Determine the OS/platform and set the copy() and paste() functions accordingly. """ - global Foundation, AppKit, qtpy, PyQt4, PyQt5 + global Foundation, AppKit # Setup for the CYGWIN platform: if ( @@ -576,25 +614,11 @@ def determine_clipboard(): return init_klipper_clipboard() try: - # qtpy is a small abstraction layer that lets you write applications - # using a single api call to either PyQt or PySide. - # https://pypi.python.org/project/QtPy - import qtpy # check if qtpy is installed - except ImportError: - # If qtpy isn't installed, fall back on importing PyQt4. - try: - import PyQt5 # check if PyQt5 is installed - except ImportError: - try: - import PyQt4 # check if PyQt4 is installed - except ImportError: - pass # We want to fail fast for all non-ImportError exceptions. - else: - return init_qt_clipboard() - else: - return init_qt_clipboard() - else: + # Verify installation of pyqt, PyQt{6,5,4} and initialize its clipboard. return init_qt_clipboard() + except ImportError: + # Ignore if Qt isn't available + pass return init_no_clipboard() @@ -618,7 +642,7 @@ def set_clipboard(clipboard): clipboard_types = { "pbcopy": init_osx_pbcopy_clipboard, "pyobjc": init_osx_pyobjc_clipboard, - "qt": init_qt_clipboard, # TODO - split this into 'qtpy', 'pyqt4', and 'pyqt5' + "qt": init_qt_clipboard, # TODO - split this into 'qtpy', 'pyqt4', 'pyqt5' and 'pyqt6' "xclip": init_xclip_clipboard, "xsel": init_xsel_clipboard, "wl-clipboard": init_wl_clipboard, diff --git a/pyproject.toml b/pyproject.toml index 450fd06232d8c..2a1c234929054 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,7 +76,7 @@ html = ['beautifulsoup4>=4.12.3', 'html5lib>=1.1', 'lxml>=4.9.2'] xml = ['lxml>=4.9.2'] plot = ['matplotlib>=3.8.3'] output-formatting = ['jinja2>=3.1.3', 'tabulate>=0.9.0'] -clipboard = ['PyQt5>=5.15.9', 'qtpy>=2.3.0'] +clipboard = ['PyQt6>=6.9.1', 'qtpy>=2.3.0'] compression = ['zstandard>=0.22.0'] timezone = ['pytz>=2023.4'] all = ['adbc-driver-postgresql>=1.2.0', @@ -99,7 +99,7 @@ all = ['adbc-driver-postgresql>=1.2.0', 'pyarrow>=12.0.1', 'pyiceberg>=0.7.1', 'pymysql>=1.1.0', - 'PyQt5>=5.15.9', + 'PyQt6>=6.9.1', 'pyreadstat>=1.2.6', 'pytest>=7.3.2', 'pytest-xdist>=3.4.0', diff --git a/requirements-dev.txt b/requirements-dev.txt index ce0ff91b2c8b3..7b92d78d3dbf9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -11,7 +11,7 @@ pytest-cov pytest-xdist>=3.4.0 pytest-qt>=4.4.0 pytest-localserver -PyQt5>=5.15.9 +PyQt6>=6.9.1 coverage python-dateutil numpy<3 diff --git a/scripts/generate_pip_deps_from_conda.py b/scripts/generate_pip_deps_from_conda.py index f84f79f4862f1..4fa6609c77288 100755 --- a/scripts/generate_pip_deps_from_conda.py +++ b/scripts/generate_pip_deps_from_conda.py @@ -30,7 +30,7 @@ "dask-core": "dask", "seaborn-base": "seaborn", "sqlalchemy": "SQLAlchemy", - "pyqt": "PyQt5", + "pyqt": "PyQt6", } diff --git a/scripts/tests/data/deps_minimum.toml b/scripts/tests/data/deps_minimum.toml index 22a30ed3a83f5..cee6df36756d5 100644 --- a/scripts/tests/data/deps_minimum.toml +++ b/scripts/tests/data/deps_minimum.toml @@ -71,7 +71,7 @@ html = ['beautifulsoup4>=4.9.3', 'html5lib>=1.1', 'lxml>=4.6.3'] xml = ['lxml>=4.6.3'] plot = ['matplotlib>=3.6.1'] output_formatting = ['jinja2>=3.0.0', 'tabulate>=0.8.9'] -clipboard = ['PyQt5>=5.15.1', 'qtpy>=2.3.0'] +clipboard = ['PyQt6>=6.9.1', 'qtpy>=2.3.0'] compression = ['zstandard>=0.15.2'] all = ['beautifulsoup4>=5.9.3', 'bottleneck>=1.3.2', @@ -90,7 +90,7 @@ all = ['beautifulsoup4>=5.9.3', 'psycopg2>=2.9.9', 'pyarrow>=7.0.0', 'pymysql>=1.1.0', - 'PyQt5>=5.15.1', + 'PyQt6>=6.9.1', 'pyreadstat>=1.1.2', 'pytest>=7.3.2', 'pytest-xdist>=3.4.0', From d25b7bb4f959875254c33b2942f4899cb28c67fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sat, 23 Aug 2025 11:10:31 -0300 Subject: [PATCH 02/10] chore: use latest pyqt version available on conda repo --- ci/deps/actions-311-minimum_versions.yaml | 4 ++-- ci/deps/actions-311.yaml | 2 +- ci/deps/actions-312.yaml | 2 +- ci/deps/actions-313-downstream_compat.yaml | 2 +- ci/deps/actions-313.yaml | 2 +- environment.yml | 2 +- pandas/compat/_optional.py | 2 +- pyproject.toml | 4 ++-- requirements-dev.txt | 2 +- scripts/tests/data/deps_minimum.toml | 4 ++-- 10 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ci/deps/actions-311-minimum_versions.yaml b/ci/deps/actions-311-minimum_versions.yaml index 815531d47a75e..0639d2862daa9 100644 --- a/ci/deps/actions-311-minimum_versions.yaml +++ b/ci/deps/actions-311-minimum_versions.yaml @@ -46,8 +46,8 @@ dependencies: - pyarrow=12.0.1 - pyiceberg=0.7.1 - pymysql=1.1.0 - - pyqt=6.9.1 - - pyqt6=6.9.1 + - pyqt=6.7.1 + - pyqt6=6.7.1 - pyreadstat=1.2.6 - pytables=3.8.0 - python-calamine=0.1.7 diff --git a/ci/deps/actions-311.yaml b/ci/deps/actions-311.yaml index 01ece922b0f0e..3e6c3bd60bfc4 100644 --- a/ci/deps/actions-311.yaml +++ b/ci/deps/actions-311.yaml @@ -39,7 +39,7 @@ dependencies: - numexpr>=2.9.0 - odfpy>=1.4.1 - qtpy>=2.3.0 - - pyqt>=6.9.1 + - pyqt>=6.7.1 - openpyxl>=3.1.2 - psycopg2>=2.9.9 - pyarrow>=12.0.1 diff --git a/ci/deps/actions-312.yaml b/ci/deps/actions-312.yaml index 01e46bce871fb..90a204a068460 100644 --- a/ci/deps/actions-312.yaml +++ b/ci/deps/actions-312.yaml @@ -39,7 +39,7 @@ dependencies: - numexpr>=2.9.0 - odfpy>=1.4.1 - qtpy>=2.3.0 - - pyqt>=6.9.1 + - pyqt>=6.7.1 - openpyxl>=3.1.2 - psycopg2>=2.9.9 - pyarrow>=12.0.1 diff --git a/ci/deps/actions-313-downstream_compat.yaml b/ci/deps/actions-313-downstream_compat.yaml index 2936cbbd28bc8..8de5df212f1e5 100644 --- a/ci/deps/actions-313-downstream_compat.yaml +++ b/ci/deps/actions-313-downstream_compat.yaml @@ -44,7 +44,7 @@ dependencies: - pyarrow>=12.0.1 - pyiceberg>=0.7.1 - pymysql>=1.1.0 - - pyqt>=6.9.1 + - pyqt>=6.7.1 - pyreadstat>=1.2.6 - pytables>=3.8.0 - python-calamine>=0.1.7 diff --git a/ci/deps/actions-313.yaml b/ci/deps/actions-313.yaml index 022e79b74f3ab..296bf3911f4e9 100644 --- a/ci/deps/actions-313.yaml +++ b/ci/deps/actions-313.yaml @@ -40,7 +40,7 @@ dependencies: - numexpr>=2.9.0 - odfpy>=1.4.1 - qtpy>=2.3.0 - - pyqt>=6.9.1 + - pyqt>=6.7.1 - openpyxl>=3.1.2 - psycopg2>=2.9.9 - pyarrow>=12.0.1 diff --git a/environment.yml b/environment.yml index 532fdaf359678..51395e80778fc 100644 --- a/environment.yml +++ b/environment.yml @@ -18,7 +18,7 @@ dependencies: - pytest-xdist>=3.4.0 - pytest-qt>=4.4.0 - pytest-localserver - - pyqt>=6.9.1 + - pyqt>=6.7.1 - coverage # required dependencies diff --git a/pandas/compat/_optional.py b/pandas/compat/_optional.py index b8b13331e7c0f..d36d58999b5df 100644 --- a/pandas/compat/_optional.py +++ b/pandas/compat/_optional.py @@ -55,7 +55,7 @@ "xlsxwriter": "3.2.0", "zstandard": "0.22.0", "qtpy": "2.3.0", - "pyqt6": "6.9.1", + "pyqt6": "6.7.1", } # A mapping from import name to package name (on PyPI) for packages where diff --git a/pyproject.toml b/pyproject.toml index 2a1c234929054..b0ddeb13fefb8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,7 +76,7 @@ html = ['beautifulsoup4>=4.12.3', 'html5lib>=1.1', 'lxml>=4.9.2'] xml = ['lxml>=4.9.2'] plot = ['matplotlib>=3.8.3'] output-formatting = ['jinja2>=3.1.3', 'tabulate>=0.9.0'] -clipboard = ['PyQt6>=6.9.1', 'qtpy>=2.3.0'] +clipboard = ['PyQt6>=6.7.1', 'qtpy>=2.3.0'] compression = ['zstandard>=0.22.0'] timezone = ['pytz>=2023.4'] all = ['adbc-driver-postgresql>=1.2.0', @@ -99,7 +99,7 @@ all = ['adbc-driver-postgresql>=1.2.0', 'pyarrow>=12.0.1', 'pyiceberg>=0.7.1', 'pymysql>=1.1.0', - 'PyQt6>=6.9.1', + 'PyQt6>=6.7.1', 'pyreadstat>=1.2.6', 'pytest>=7.3.2', 'pytest-xdist>=3.4.0', diff --git a/requirements-dev.txt b/requirements-dev.txt index 7b92d78d3dbf9..aa7592b8ce05b 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -11,7 +11,7 @@ pytest-cov pytest-xdist>=3.4.0 pytest-qt>=4.4.0 pytest-localserver -PyQt6>=6.9.1 +PyQt6>=6.7.1 coverage python-dateutil numpy<3 diff --git a/scripts/tests/data/deps_minimum.toml b/scripts/tests/data/deps_minimum.toml index cee6df36756d5..6815623bad819 100644 --- a/scripts/tests/data/deps_minimum.toml +++ b/scripts/tests/data/deps_minimum.toml @@ -71,7 +71,7 @@ html = ['beautifulsoup4>=4.9.3', 'html5lib>=1.1', 'lxml>=4.6.3'] xml = ['lxml>=4.6.3'] plot = ['matplotlib>=3.6.1'] output_formatting = ['jinja2>=3.0.0', 'tabulate>=0.8.9'] -clipboard = ['PyQt6>=6.9.1', 'qtpy>=2.3.0'] +clipboard = ['PyQt6>=6.7.1', 'qtpy>=2.3.0'] compression = ['zstandard>=0.15.2'] all = ['beautifulsoup4>=5.9.3', 'bottleneck>=1.3.2', @@ -90,7 +90,7 @@ all = ['beautifulsoup4>=5.9.3', 'psycopg2>=2.9.9', 'pyarrow>=7.0.0', 'pymysql>=1.1.0', - 'PyQt6>=6.9.1', + 'PyQt6>=6.7.1', 'pyreadstat>=1.1.2', 'pytest>=7.3.2', 'pytest-xdist>=3.4.0', From 3f109b6e139a3ecc4dfb1748ff2847a8ffb541e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sat, 23 Aug 2025 21:23:14 -0300 Subject: [PATCH 03/10] ci(deps): install PyQt6 with pip --- ci/deps/actions-311-minimum_versions.yaml | 3 +-- ci/deps/actions-311.yaml | 2 +- ci/deps/actions-312.yaml | 2 +- ci/deps/actions-313-downstream_compat.yaml | 2 +- ci/deps/actions-313.yaml | 2 +- 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ci/deps/actions-311-minimum_versions.yaml b/ci/deps/actions-311-minimum_versions.yaml index 0639d2862daa9..4381f9e183af2 100644 --- a/ci/deps/actions-311-minimum_versions.yaml +++ b/ci/deps/actions-311-minimum_versions.yaml @@ -46,8 +46,6 @@ dependencies: - pyarrow=12.0.1 - pyiceberg=0.7.1 - pymysql=1.1.0 - - pyqt=6.7.1 - - pyqt6=6.7.1 - pyreadstat=1.2.6 - pytables=3.8.0 - python-calamine=0.1.7 @@ -63,4 +61,5 @@ dependencies: - zstandard=0.22.0 - pip: + - pyqt6==6.7.1 - tzdata==2023.3 diff --git a/ci/deps/actions-311.yaml b/ci/deps/actions-311.yaml index 3e6c3bd60bfc4..5c4310030c330 100644 --- a/ci/deps/actions-311.yaml +++ b/ci/deps/actions-311.yaml @@ -39,7 +39,6 @@ dependencies: - numexpr>=2.9.0 - odfpy>=1.4.1 - qtpy>=2.3.0 - - pyqt>=6.7.1 - openpyxl>=3.1.2 - psycopg2>=2.9.9 - pyarrow>=12.0.1 @@ -60,4 +59,5 @@ dependencies: - zstandard>=0.22.0 - pip: + - pyqt>-6.7.1 - tzdata>=2023.3 diff --git a/ci/deps/actions-312.yaml b/ci/deps/actions-312.yaml index 90a204a068460..40656e1d570c0 100644 --- a/ci/deps/actions-312.yaml +++ b/ci/deps/actions-312.yaml @@ -39,7 +39,6 @@ dependencies: - numexpr>=2.9.0 - odfpy>=1.4.1 - qtpy>=2.3.0 - - pyqt>=6.7.1 - openpyxl>=3.1.2 - psycopg2>=2.9.9 - pyarrow>=12.0.1 @@ -60,4 +59,5 @@ dependencies: - zstandard>=0.22.0 - pip: + - pyqt>=6.7.1 - tzdata>=2023.3 diff --git a/ci/deps/actions-313-downstream_compat.yaml b/ci/deps/actions-313-downstream_compat.yaml index 8de5df212f1e5..e3eefa3bb893d 100644 --- a/ci/deps/actions-313-downstream_compat.yaml +++ b/ci/deps/actions-313-downstream_compat.yaml @@ -44,7 +44,6 @@ dependencies: - pyarrow>=12.0.1 - pyiceberg>=0.7.1 - pymysql>=1.1.0 - - pyqt>=6.7.1 - pyreadstat>=1.2.6 - pytables>=3.8.0 - python-calamine>=0.1.7 @@ -71,4 +70,5 @@ dependencies: - pandas-datareader - pyyaml - pip: + - pyqt>=6.7.1 - tzdata>=2023.3 diff --git a/ci/deps/actions-313.yaml b/ci/deps/actions-313.yaml index 296bf3911f4e9..f123b146f5bee 100644 --- a/ci/deps/actions-313.yaml +++ b/ci/deps/actions-313.yaml @@ -40,7 +40,6 @@ dependencies: - numexpr>=2.9.0 - odfpy>=1.4.1 - qtpy>=2.3.0 - - pyqt>=6.7.1 - openpyxl>=3.1.2 - psycopg2>=2.9.9 - pyarrow>=12.0.1 @@ -60,4 +59,5 @@ dependencies: - zstandard>=0.22.0 - pip: + - pyqt>=6.7.1 - tzdata>=2023.3 From ccc9b9ab4862e06e15c70fe7a786a4a716980553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sat, 23 Aug 2025 21:29:55 -0300 Subject: [PATCH 04/10] fix: replace pyqt with pyqt6 --- ci/deps/actions-311.yaml | 2 +- ci/deps/actions-312.yaml | 2 +- ci/deps/actions-313-downstream_compat.yaml | 2 +- ci/deps/actions-313.yaml | 2 +- environment.yml | 2 +- requirements-dev.txt | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ci/deps/actions-311.yaml b/ci/deps/actions-311.yaml index 5c4310030c330..359deabf4904b 100644 --- a/ci/deps/actions-311.yaml +++ b/ci/deps/actions-311.yaml @@ -59,5 +59,5 @@ dependencies: - zstandard>=0.22.0 - pip: - - pyqt>-6.7.1 + - pyqt6>=6.7.1 - tzdata>=2023.3 diff --git a/ci/deps/actions-312.yaml b/ci/deps/actions-312.yaml index 40656e1d570c0..a7e4627257c8f 100644 --- a/ci/deps/actions-312.yaml +++ b/ci/deps/actions-312.yaml @@ -59,5 +59,5 @@ dependencies: - zstandard>=0.22.0 - pip: - - pyqt>=6.7.1 + - pyqt6>=6.7.1 - tzdata>=2023.3 diff --git a/ci/deps/actions-313-downstream_compat.yaml b/ci/deps/actions-313-downstream_compat.yaml index e3eefa3bb893d..d646c2e91b61f 100644 --- a/ci/deps/actions-313-downstream_compat.yaml +++ b/ci/deps/actions-313-downstream_compat.yaml @@ -70,5 +70,5 @@ dependencies: - pandas-datareader - pyyaml - pip: - - pyqt>=6.7.1 + - pyqt6>=6.7.1 - tzdata>=2023.3 diff --git a/ci/deps/actions-313.yaml b/ci/deps/actions-313.yaml index f123b146f5bee..4d05b184f6a9c 100644 --- a/ci/deps/actions-313.yaml +++ b/ci/deps/actions-313.yaml @@ -59,5 +59,5 @@ dependencies: - zstandard>=0.22.0 - pip: - - pyqt>=6.7.1 + - pyqt6>=6.7.1 - tzdata>=2023.3 diff --git a/environment.yml b/environment.yml index 51395e80778fc..965b739e9ff0b 100644 --- a/environment.yml +++ b/environment.yml @@ -18,7 +18,6 @@ dependencies: - pytest-xdist>=3.4.0 - pytest-qt>=4.4.0 - pytest-localserver - - pyqt>=6.7.1 - coverage # required dependencies @@ -122,4 +121,5 @@ dependencies: - jupyterlite-pyodide-kernel - pip: + - pyqt6>=6.7.1 - tzdata>=2023.3 diff --git a/requirements-dev.txt b/requirements-dev.txt index aa7592b8ce05b..11e4effe13ff0 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -11,7 +11,6 @@ pytest-cov pytest-xdist>=3.4.0 pytest-qt>=4.4.0 pytest-localserver -PyQt6>=6.7.1 coverage python-dateutil numpy<3 @@ -85,4 +84,5 @@ requests pygments jupyterlite-core jupyterlite-pyodide-kernel +pyqt6>=6.7.1 tzdata>=2023.3 From 58bd51a955d7801558f73416ddb4f54fc654aebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sat, 23 Aug 2025 21:34:00 -0300 Subject: [PATCH 05/10] chore: use correct capitalization for PyQt --- ci/deps/actions-311-minimum_versions.yaml | 2 +- ci/deps/actions-311.yaml | 2 +- ci/deps/actions-312.yaml | 2 +- ci/deps/actions-313-downstream_compat.yaml | 2 +- ci/deps/actions-313.yaml | 2 +- environment.yml | 2 +- pandas/compat/_optional.py | 2 +- requirements-dev.txt | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ci/deps/actions-311-minimum_versions.yaml b/ci/deps/actions-311-minimum_versions.yaml index 4381f9e183af2..75395c0a20372 100644 --- a/ci/deps/actions-311-minimum_versions.yaml +++ b/ci/deps/actions-311-minimum_versions.yaml @@ -61,5 +61,5 @@ dependencies: - zstandard=0.22.0 - pip: - - pyqt6==6.7.1 + - PyQt6==6.7.1 - tzdata==2023.3 diff --git a/ci/deps/actions-311.yaml b/ci/deps/actions-311.yaml index 359deabf4904b..21ca6c22f1237 100644 --- a/ci/deps/actions-311.yaml +++ b/ci/deps/actions-311.yaml @@ -59,5 +59,5 @@ dependencies: - zstandard>=0.22.0 - pip: - - pyqt6>=6.7.1 + - PyQt6>=6.7.1 - tzdata>=2023.3 diff --git a/ci/deps/actions-312.yaml b/ci/deps/actions-312.yaml index a7e4627257c8f..0d9084ae0c157 100644 --- a/ci/deps/actions-312.yaml +++ b/ci/deps/actions-312.yaml @@ -59,5 +59,5 @@ dependencies: - zstandard>=0.22.0 - pip: - - pyqt6>=6.7.1 + - PyQt6>=6.7.1 - tzdata>=2023.3 diff --git a/ci/deps/actions-313-downstream_compat.yaml b/ci/deps/actions-313-downstream_compat.yaml index d646c2e91b61f..44032054994cc 100644 --- a/ci/deps/actions-313-downstream_compat.yaml +++ b/ci/deps/actions-313-downstream_compat.yaml @@ -70,5 +70,5 @@ dependencies: - pandas-datareader - pyyaml - pip: - - pyqt6>=6.7.1 + - PyQt6>=6.7.1 - tzdata>=2023.3 diff --git a/ci/deps/actions-313.yaml b/ci/deps/actions-313.yaml index 4d05b184f6a9c..370f496f001f1 100644 --- a/ci/deps/actions-313.yaml +++ b/ci/deps/actions-313.yaml @@ -59,5 +59,5 @@ dependencies: - zstandard>=0.22.0 - pip: - - pyqt6>=6.7.1 + - PyQt6>=6.7.1 - tzdata>=2023.3 diff --git a/environment.yml b/environment.yml index 965b739e9ff0b..e46d5d5b8c238 100644 --- a/environment.yml +++ b/environment.yml @@ -121,5 +121,5 @@ dependencies: - jupyterlite-pyodide-kernel - pip: - - pyqt6>=6.7.1 + - PyQt6>=6.7.1 - tzdata>=2023.3 diff --git a/pandas/compat/_optional.py b/pandas/compat/_optional.py index d36d58999b5df..ee1f2ea30dfd7 100644 --- a/pandas/compat/_optional.py +++ b/pandas/compat/_optional.py @@ -55,7 +55,7 @@ "xlsxwriter": "3.2.0", "zstandard": "0.22.0", "qtpy": "2.3.0", - "pyqt6": "6.7.1", + "PyQt6": "6.7.1", } # A mapping from import name to package name (on PyPI) for packages where diff --git a/requirements-dev.txt b/requirements-dev.txt index 11e4effe13ff0..e0169b067d2ac 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -84,5 +84,5 @@ requests pygments jupyterlite-core jupyterlite-pyodide-kernel -pyqt6>=6.7.1 +PyQt6>=6.7.1 tzdata>=2023.3 From 13d75d37539720cd10b9a0ca054f51fcf2346b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 24 Aug 2025 10:12:33 -0300 Subject: [PATCH 06/10] docs(whatsnew): add PyQt6 support to whats new --- doc/source/whatsnew/v3.0.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index d519400834ee1..30183dce82904 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -196,6 +196,7 @@ Other enhancements - :meth:`pandas.concat` will raise a ``ValueError`` when ``ignore_index=True`` and ``keys`` is not ``None`` (:issue:`59274`) - :py:class:`frozenset` elements in pandas objects are now natively printed (:issue:`60690`) - Add ``"delete_rows"`` option to ``if_exists`` argument in :meth:`DataFrame.to_sql` deleting all records of the table before inserting data (:issue:`37210`). +- Added PyQt6 support to resolve ARM64 container build issues (:issue:`61037`) - Added half-year offset classes :class:`HalfYearBegin`, :class:`HalfYearEnd`, :class:`BHalfYearBegin` and :class:`BHalfYearEnd` (:issue:`60928`) - Added support to read and write from and to Apache Iceberg tables with the new :func:`read_iceberg` and :meth:`DataFrame.to_iceberg` functions (:issue:`61383`) - Errors occurring during SQL I/O will now throw a generic :class:`.DatabaseError` instead of the raw Exception type from the underlying driver manager library (:issue:`60748`) From 1a7b2491b22309c6223a7c8fa5f00164483a3c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 24 Aug 2025 10:15:13 -0300 Subject: [PATCH 07/10] docs(install): keep old pyqt5 minimum version --- doc/source/getting_started/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/getting_started/install.rst b/doc/source/getting_started/install.rst index 788f19944f8e1..f6927888f7ad6 100644 --- a/doc/source/getting_started/install.rst +++ b/doc/source/getting_started/install.rst @@ -343,7 +343,7 @@ Installable with ``pip install "pandas[clipboard]"``. ======================================================================================================================================== ================== =============== ============== Dependency Minimum Version pip extra Notes ======================================================================================================================================== ================== =============== ============== -`PyQt4 `__/`PyQt5 `__/ `PyQt6 `__ 6.9.1 clipboard Clipboard I/O +`PyQt4 `__/`PyQt5 `__/ `PyQt6 `__ 5.15.9 clipboard Clipboard I/O `qtpy `__ 2.3.0 clipboard Clipboard I/O ======================================================================================================================================== ================== =============== ============== From ec59ea1da3ff97d63d7a7617ebb68d9c1ab3443f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 24 Aug 2025 10:35:02 -0300 Subject: [PATCH 08/10] fix: turn generator into tuple for correct error message --- pandas/io/clipboard/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pandas/io/clipboard/__init__.py b/pandas/io/clipboard/__init__.py index ad6bf739af4ae..bc040d240e121 100644 --- a/pandas/io/clipboard/__init__.py +++ b/pandas/io/clipboard/__init__.py @@ -168,7 +168,7 @@ def _import_module(modules: list[tuple[str, str | None]]): continue raise ImportError( - f"No module from {(module_name for module_name, _ in modules)} could be imported." + f"No module from {tuple(module_name for module_name, _ in modules)} could be imported." ) From ce99f81615d6d7348035fbb6413eda7a4a26abc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 24 Aug 2025 23:34:22 -0300 Subject: [PATCH 09/10] fix: let `getattr` raise `AttributeError` directly --- pandas/io/clipboard/__init__.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/pandas/io/clipboard/__init__.py b/pandas/io/clipboard/__init__.py index bc040d240e121..d19d5c7668c63 100644 --- a/pandas/io/clipboard/__init__.py +++ b/pandas/io/clipboard/__init__.py @@ -158,12 +158,8 @@ def _import_module(modules: list[tuple[str, str | None]]): if attribute_name is None: return module - elif hasattr(module, attribute_name): - return getattr(module, attribute_name) - else: - raise AttributeError( - f"Module {module_name} doesn't have attribute {attribute_name}." - ) + return getattr(module, attribute_name) + except ImportError: continue From 96aae6ea9a276c9ff5ea84a05b723cef3c3ade47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Tue, 26 Aug 2025 18:35:44 -0300 Subject: [PATCH 10/10] doc: remove extra space between bar and PyQt6 --- doc/source/getting_started/install.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/getting_started/install.rst b/doc/source/getting_started/install.rst index f6927888f7ad6..921dfa67667b9 100644 --- a/doc/source/getting_started/install.rst +++ b/doc/source/getting_started/install.rst @@ -343,7 +343,7 @@ Installable with ``pip install "pandas[clipboard]"``. ======================================================================================================================================== ================== =============== ============== Dependency Minimum Version pip extra Notes ======================================================================================================================================== ================== =============== ============== -`PyQt4 `__/`PyQt5 `__/ `PyQt6 `__ 5.15.9 clipboard Clipboard I/O +`PyQt4 `__/`PyQt5 `__/`PyQt6 `__ 5.15.9 clipboard Clipboard I/O `qtpy `__ 2.3.0 clipboard Clipboard I/O ======================================================================================================================================== ================== =============== ==============