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/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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/28] 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 ======================================================================================================================================== ================== =============== ============== From 2a7ebf70083a130923842aff17b2c0435b09f6f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 7 Sep 2025 12:28:33 -0300 Subject: [PATCH 11/28] docs(install): remove `PyQt4` dependency --- 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 921dfa67667b9..e276763cea2b2 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 +`PyQt5 `__/`PyQt6 `__ 5.15.9 clipboard Clipboard I/O `qtpy `__ 2.3.0 clipboard Clipboard I/O ======================================================================================================================================== ================== =============== ============== From 31b17eb26c4be0c7ddedd0a0b57c0190bfc27c98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 7 Sep 2025 13:12:25 -0300 Subject: [PATCH 12/28] test(clipboard): parametrize qt tests to use multiple versions --- pandas/tests/io/test_clipboard.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/pandas/tests/io/test_clipboard.py b/pandas/tests/io/test_clipboard.py index b5e97314caf03..0e831c3fddc30 100644 --- a/pandas/tests/io/test_clipboard.py +++ b/pandas/tests/io/test_clipboard.py @@ -1,3 +1,4 @@ +import importlib from textwrap import dedent import numpy as np @@ -18,6 +19,7 @@ ) import pandas._testing as tm +import pandas.io.clipboard from pandas.io.clipboard import ( CheckedCall, _stringifyText, @@ -169,8 +171,21 @@ def test_stringify_text(text): _stringifyText(text) -@pytest.fixture -def set_pyqt_clipboard(monkeypatch): +@pytest.fixture( + params=[ + pytest.param(("qtpy.QtWidgets", "QApplication"), marks=[]), + pytest.param(("PyQt6.QtWidgets", "QApplication"), marks=[]), + pytest.param(("PyQt5.QtWidgets", "QApplication"), marks=[]), + ], + ids=["qtpy", "PyQt6", "PyQt5"], +) +def set_pyqt_clipboard(monkeypatch, request): + module, attribute = request.param + qt_module = importlib.import_module(module) + q_application_binding = getattr(qt_module, attribute) + + get_qapp_binding = lambda x: q_application_binding + monkeypatch.setattr(pandas.io.clipboard, "_import_module", get_qapp_binding) qt_cut, qt_paste = init_qt_clipboard() with monkeypatch.context() as m: m.setattr(pd.io.clipboard, "clipboard_set", qt_cut) From 13adf24ab40e152d560137cd21f84cee3d63be54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 7 Sep 2025 13:21:07 -0300 Subject: [PATCH 13/28] test: add xfail mark when a qt version isn't available --- pandas/tests/io/test_clipboard.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/pandas/tests/io/test_clipboard.py b/pandas/tests/io/test_clipboard.py index 0e831c3fddc30..6303e53db51ee 100644 --- a/pandas/tests/io/test_clipboard.py +++ b/pandas/tests/io/test_clipboard.py @@ -173,9 +173,33 @@ def test_stringify_text(text): @pytest.fixture( params=[ - pytest.param(("qtpy.QtWidgets", "QApplication"), marks=[]), - pytest.param(("PyQt6.QtWidgets", "QApplication"), marks=[]), - pytest.param(("PyQt5.QtWidgets", "QApplication"), marks=[]), + pytest.param( + ("qtpy.QtWidgets", "QApplication"), + marks=[ + pytest.mark.xfail( + importlib.util.find_spec("qtpy") is None, + reason="qtpy isn't installed", + ) + ], + ), + pytest.param( + ("PyQt6.QtWidgets", "QApplication"), + marks=[ + pytest.mark.xfail( + importlib.util.find_spec("PyQt6") is None, + reason="PyQt6 isn't installed", + ) + ], + ), + pytest.param( + ("PyQt5.QtWidgets", "QApplication"), + marks=[ + pytest.mark.xfail( + importlib.util.find_spec("PyQt5") is None, + reason="PyQt5 isn't installed", + ) + ], + ), ], ids=["qtpy", "PyQt6", "PyQt5"], ) From 6ab65344eec6758ee8c4aee3fc730034908d08ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 7 Sep 2025 13:50:21 -0300 Subject: [PATCH 14/28] ci: re-add PyQt5 as a CI dependency --- ci/deps/actions-311.yaml | 1 + ci/deps/actions-312.yaml | 1 + ci/deps/actions-313-downstream_compat.yaml | 1 + ci/deps/actions-313.yaml | 1 + 4 files changed, 4 insertions(+) diff --git a/ci/deps/actions-311.yaml b/ci/deps/actions-311.yaml index 21ca6c22f1237..631d8e65042a6 100644 --- a/ci/deps/actions-311.yaml +++ b/ci/deps/actions-311.yaml @@ -44,6 +44,7 @@ dependencies: - pyarrow>=12.0.1 - pyiceberg>=0.7.1 - pymysql>=1.1.0 + - PyQt5>=5.15.9 - pyreadstat>=1.2.6 - pytables>=3.8.0 - python-calamine>=0.1.7 diff --git a/ci/deps/actions-312.yaml b/ci/deps/actions-312.yaml index 0d9084ae0c157..737dc442bfd59 100644 --- a/ci/deps/actions-312.yaml +++ b/ci/deps/actions-312.yaml @@ -44,6 +44,7 @@ dependencies: - pyarrow>=12.0.1 - pyiceberg>=0.7.1 - pymysql>=1.1.0 + - PyQt5>=5.15.9 - pyreadstat>=1.2.6 - pytables>=3.8.0 - python-calamine>=0.1.7 diff --git a/ci/deps/actions-313-downstream_compat.yaml b/ci/deps/actions-313-downstream_compat.yaml index 44032054994cc..fcfdd0f7c058f 100644 --- a/ci/deps/actions-313-downstream_compat.yaml +++ b/ci/deps/actions-313-downstream_compat.yaml @@ -44,6 +44,7 @@ dependencies: - pyarrow>=12.0.1 - pyiceberg>=0.7.1 - pymysql>=1.1.0 + - PyQt5>=5.15.9 - 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 370f496f001f1..12653a7718023 100644 --- a/ci/deps/actions-313.yaml +++ b/ci/deps/actions-313.yaml @@ -44,6 +44,7 @@ dependencies: - psycopg2>=2.9.9 - pyarrow>=12.0.1 - pymysql>=1.1.0 + - PyQt5>=5.15.9 - pyreadstat>=1.2.6 - pytables>=3.8.0 - python-calamine>=0.1.7 From 7f4f9937a54c1afc71746122a1f5edba87404b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 7 Sep 2025 13:55:03 -0300 Subject: [PATCH 15/28] ci(deps): rename PyQt5 to pyqt --- 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 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ci/deps/actions-311.yaml b/ci/deps/actions-311.yaml index 631d8e65042a6..a031d84b3f362 100644 --- a/ci/deps/actions-311.yaml +++ b/ci/deps/actions-311.yaml @@ -44,7 +44,7 @@ dependencies: - pyarrow>=12.0.1 - pyiceberg>=0.7.1 - pymysql>=1.1.0 - - PyQt5>=5.15.9 + - pyqt>=5.15.9 - pyreadstat>=1.2.6 - pytables>=3.8.0 - python-calamine>=0.1.7 diff --git a/ci/deps/actions-312.yaml b/ci/deps/actions-312.yaml index 737dc442bfd59..797af592b505c 100644 --- a/ci/deps/actions-312.yaml +++ b/ci/deps/actions-312.yaml @@ -44,7 +44,7 @@ dependencies: - pyarrow>=12.0.1 - pyiceberg>=0.7.1 - pymysql>=1.1.0 - - PyQt5>=5.15.9 + - pyqt>=5.15.9 - pyreadstat>=1.2.6 - pytables>=3.8.0 - python-calamine>=0.1.7 diff --git a/ci/deps/actions-313-downstream_compat.yaml b/ci/deps/actions-313-downstream_compat.yaml index fcfdd0f7c058f..a13f4fa5bff88 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 - - PyQt5>=5.15.9 + - pyqt>=5.15.9 - 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 12653a7718023..d6309c46a8c9e 100644 --- a/ci/deps/actions-313.yaml +++ b/ci/deps/actions-313.yaml @@ -44,7 +44,7 @@ dependencies: - psycopg2>=2.9.9 - pyarrow>=12.0.1 - pymysql>=1.1.0 - - PyQt5>=5.15.9 + - pyqt>=5.15.9 - pyreadstat>=1.2.6 - pytables>=3.8.0 - python-calamine>=0.1.7 From 1568f0c3a2fedd9892b8b134e78865bf4b4d2066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 7 Sep 2025 15:26:59 -0300 Subject: [PATCH 16/28] test: be more specific about QtWidgets import When PyQt6 is uninstalled, it is still importable, but `PyQt6.QtWidgets` is no longer available. --- pandas/tests/io/test_clipboard.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/tests/io/test_clipboard.py b/pandas/tests/io/test_clipboard.py index 6303e53db51ee..f1fb3438e6e0f 100644 --- a/pandas/tests/io/test_clipboard.py +++ b/pandas/tests/io/test_clipboard.py @@ -177,7 +177,7 @@ def test_stringify_text(text): ("qtpy.QtWidgets", "QApplication"), marks=[ pytest.mark.xfail( - importlib.util.find_spec("qtpy") is None, + importlib.util.find_spec("qtpy.QtWidgets") is None, reason="qtpy isn't installed", ) ], @@ -186,7 +186,7 @@ def test_stringify_text(text): ("PyQt6.QtWidgets", "QApplication"), marks=[ pytest.mark.xfail( - importlib.util.find_spec("PyQt6") is None, + importlib.util.find_spec("PyQt6.QtWidgets") is None, reason="PyQt6 isn't installed", ) ], @@ -195,7 +195,7 @@ def test_stringify_text(text): ("PyQt5.QtWidgets", "QApplication"), marks=[ pytest.mark.xfail( - importlib.util.find_spec("PyQt5") is None, + importlib.util.find_spec("PyQt5.QtWidgets") is None, reason="PyQt5 isn't installed", ) ], From 1fca531ea1a356b7d8626f52883849ad314a10a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 7 Sep 2025 15:44:49 -0300 Subject: [PATCH 17/28] test: fix `ModuleNotFoundError` --- pandas/tests/io/test_clipboard.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pandas/tests/io/test_clipboard.py b/pandas/tests/io/test_clipboard.py index f1fb3438e6e0f..755d668d78906 100644 --- a/pandas/tests/io/test_clipboard.py +++ b/pandas/tests/io/test_clipboard.py @@ -177,7 +177,8 @@ def test_stringify_text(text): ("qtpy.QtWidgets", "QApplication"), marks=[ pytest.mark.xfail( - importlib.util.find_spec("qtpy.QtWidgets") is None, + importlib.util.find_spec("qtpy") is None + or importlib.util.find_spec("qtpy.QtWidgets") is None, reason="qtpy isn't installed", ) ], @@ -186,7 +187,8 @@ def test_stringify_text(text): ("PyQt6.QtWidgets", "QApplication"), marks=[ pytest.mark.xfail( - importlib.util.find_spec("PyQt6.QtWidgets") is None, + importlib.util.find_spec("PyQt6") is None + or importlib.util.find_spec("PyQt6.QtWidgets") is None, reason="PyQt6 isn't installed", ) ], @@ -195,7 +197,8 @@ def test_stringify_text(text): ("PyQt5.QtWidgets", "QApplication"), marks=[ pytest.mark.xfail( - importlib.util.find_spec("PyQt5.QtWidgets") is None, + importlib.util.find_spec("PyQt5") is None + or importlib.util.find_spec("PyQt5.QtWidgets") is None, reason="PyQt5 isn't installed", ) ], From 49734bfa0ed7c0db4e42d6a318453098a8192053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 7 Sep 2025 16:51:15 -0300 Subject: [PATCH 18/28] test: match clipboard with PyQt --- pandas/tests/io/test_clipboard.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pandas/tests/io/test_clipboard.py b/pandas/tests/io/test_clipboard.py index 755d668d78906..8bc5c4a2b18e5 100644 --- a/pandas/tests/io/test_clipboard.py +++ b/pandas/tests/io/test_clipboard.py @@ -217,11 +217,14 @@ def set_pyqt_clipboard(monkeypatch, request): with monkeypatch.context() as m: m.setattr(pd.io.clipboard, "clipboard_set", qt_cut) m.setattr(pd.io.clipboard, "clipboard_get", qt_paste) - yield + yield module, attribute @pytest.fixture -def clipboard(qapp): +def clipboard(set_pyqt_clipboard): + module, attribute = set_pyqt_clipboard + qt_module = importlib.import_module(module) + qapp = getattr(qt_module, attribute) clip = qapp.clipboard() yield clip clip.clear() From d0012e8fbd6e12a67960d2f299544e599c7f87e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 7 Sep 2025 17:33:48 -0300 Subject: [PATCH 19/28] fix: fix PyQt5 dependency generator for conda --- scripts/generate_pip_deps_from_conda.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/generate_pip_deps_from_conda.py b/scripts/generate_pip_deps_from_conda.py index 4fa6609c77288..f84f79f4862f1 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": "PyQt6", + "pyqt": "PyQt5", } From dafc4d05b4d3fe9fd2850fbf52c40b158f1769a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 7 Sep 2025 21:00:11 -0300 Subject: [PATCH 20/28] ci: move dependencies and re-add pyqt5 to min_versions --- ci/deps/actions-311-minimum_versions.yaml | 1 + ci/deps/actions-311.yaml | 2 +- ci/deps/actions-312.yaml | 2 +- ci/deps/actions-313.yaml | 2 +- environment.yml | 1 + requirements-dev.txt | 1 + scripts/tests/data/deps_minimum.toml | 1 + 7 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ci/deps/actions-311-minimum_versions.yaml b/ci/deps/actions-311-minimum_versions.yaml index 75395c0a20372..6f0912944ecdf 100644 --- a/ci/deps/actions-311-minimum_versions.yaml +++ b/ci/deps/actions-311-minimum_versions.yaml @@ -46,6 +46,7 @@ dependencies: - pyarrow=12.0.1 - pyiceberg=0.7.1 - pymysql=1.1.0 + - pyqt=5.15.9 - 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 a031d84b3f362..94fc53a3af16a 100644 --- a/ci/deps/actions-311.yaml +++ b/ci/deps/actions-311.yaml @@ -39,12 +39,12 @@ dependencies: - numexpr>=2.9.0 - odfpy>=1.4.1 - qtpy>=2.3.0 + - pyqt>=5.15.9 - openpyxl>=3.1.2 - psycopg2>=2.9.9 - pyarrow>=12.0.1 - pyiceberg>=0.7.1 - pymysql>=1.1.0 - - pyqt>=5.15.9 - pyreadstat>=1.2.6 - pytables>=3.8.0 - python-calamine>=0.1.7 diff --git a/ci/deps/actions-312.yaml b/ci/deps/actions-312.yaml index 797af592b505c..9d72607511acb 100644 --- a/ci/deps/actions-312.yaml +++ b/ci/deps/actions-312.yaml @@ -39,12 +39,12 @@ dependencies: - numexpr>=2.9.0 - odfpy>=1.4.1 - qtpy>=2.3.0 + - pyqt>=5.15.9 - openpyxl>=3.1.2 - psycopg2>=2.9.9 - pyarrow>=12.0.1 - pyiceberg>=0.7.1 - pymysql>=1.1.0 - - pyqt>=5.15.9 - 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 d6309c46a8c9e..d6067aeede34d 100644 --- a/ci/deps/actions-313.yaml +++ b/ci/deps/actions-313.yaml @@ -40,11 +40,11 @@ dependencies: - numexpr>=2.9.0 - odfpy>=1.4.1 - qtpy>=2.3.0 + - pyqt>=5.15.9 - openpyxl>=3.1.2 - psycopg2>=2.9.9 - pyarrow>=12.0.1 - pymysql>=1.1.0 - - pyqt>=5.15.9 - pyreadstat>=1.2.6 - pytables>=3.8.0 - python-calamine>=0.1.7 diff --git a/environment.yml b/environment.yml index c5b4f85673979..ba2c6b19adce2 100644 --- a/environment.yml +++ b/environment.yml @@ -18,6 +18,7 @@ dependencies: - pytest-xdist>=3.4.0 - pytest-qt>=4.4.0 - pytest-localserver + - pyqt>=5.15.9 - coverage # required dependencies diff --git a/requirements-dev.txt b/requirements-dev.txt index 1f8cf0757c588..28da0b8e6c135 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -11,6 +11,7 @@ pytest-cov pytest-xdist>=3.4.0 pytest-qt>=4.4.0 pytest-localserver +PyQt5>=5.15.9 coverage python-dateutil numpy<3 diff --git a/scripts/tests/data/deps_minimum.toml b/scripts/tests/data/deps_minimum.toml index 6815623bad819..62bdc67150464 100644 --- a/scripts/tests/data/deps_minimum.toml +++ b/scripts/tests/data/deps_minimum.toml @@ -90,6 +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.7.1', 'pyreadstat>=1.1.2', 'pytest>=7.3.2', From 01fd3f417ecf02a07d0eb2a5b7a3c3d784a9b141 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sun, 7 Sep 2025 21:17:36 -0300 Subject: [PATCH 21/28] docs(install): make clipboard compatibility more explicit --- doc/source/getting_started/install.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/source/getting_started/install.rst b/doc/source/getting_started/install.rst index e276763cea2b2..ff2cf35e75ecb 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 -======================================================================================================================================== ================== =============== ============== -`PyQt5 `__/`PyQt6 `__ 5.15.9 clipboard Clipboard I/O -`qtpy `__ 2.3.0 clipboard Clipboard I/O -======================================================================================================================================== ================== =============== ============== +======================================================================================== ================== =============== ============== +Dependency Minimum Version pip extra Notes +======================================================================================== ================== =============== ============== +`PyQt5 `__/`PyQt6 `__ 5.15.9/6.7.1 clipboard Clipboard I/O +`qtpy `__ 2.3.0 clipboard Clipboard I/O +======================================================================================== ================== =============== ============== .. note:: From a1c98df2c14d09f1bf5be0cd468ad64ce4abb1b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Thu, 11 Sep 2025 19:35:34 -0300 Subject: [PATCH 22/28] test: undo changes in clipboard tests --- pandas/tests/io/test_clipboard.py | 53 +++---------------------------- 1 file changed, 4 insertions(+), 49 deletions(-) diff --git a/pandas/tests/io/test_clipboard.py b/pandas/tests/io/test_clipboard.py index 1ad714fb750cb..25834c47c09c6 100644 --- a/pandas/tests/io/test_clipboard.py +++ b/pandas/tests/io/test_clipboard.py @@ -1,4 +1,3 @@ -import importlib from textwrap import dedent import numpy as np @@ -19,7 +18,6 @@ ) import pandas._testing as tm -import pandas.io.clipboard from pandas.io.clipboard import ( CheckedCall, _stringifyText, @@ -171,60 +169,17 @@ def test_stringify_text(text): _stringifyText(text) -@pytest.fixture( - params=[ - pytest.param( - ("qtpy.QtWidgets", "QApplication"), - marks=[ - pytest.mark.xfail( - importlib.util.find_spec("qtpy") is None - or importlib.util.find_spec("qtpy.QtWidgets") is None, - reason="qtpy isn't installed", - ) - ], - ), - pytest.param( - ("PyQt6.QtWidgets", "QApplication"), - marks=[ - pytest.mark.xfail( - importlib.util.find_spec("PyQt6") is None - or importlib.util.find_spec("PyQt6.QtWidgets") is None, - reason="PyQt6 isn't installed", - ) - ], - ), - pytest.param( - ("PyQt5.QtWidgets", "QApplication"), - marks=[ - pytest.mark.xfail( - importlib.util.find_spec("PyQt5") is None - or importlib.util.find_spec("PyQt5.QtWidgets") is None, - reason="PyQt5 isn't installed", - ) - ], - ), - ], - ids=["qtpy", "PyQt6", "PyQt5"], -) -def set_pyqt_clipboard(monkeypatch, request): - module, attribute = request.param - qt_module = importlib.import_module(module) - q_application_binding = getattr(qt_module, attribute) - - get_qapp_binding = lambda x: q_application_binding - monkeypatch.setattr(pandas.io.clipboard, "_import_module", get_qapp_binding) +@pytest.fixture +def set_pyqt_clipboard(monkeypatch): qt_cut, qt_paste = init_qt_clipboard() with monkeypatch.context() as m: m.setattr(pd.io.clipboard, "clipboard_set", qt_cut) m.setattr(pd.io.clipboard, "clipboard_get", qt_paste) - yield module, attribute + yield @pytest.fixture -def clipboard(set_pyqt_clipboard): - module, attribute = set_pyqt_clipboard - qt_module = importlib.import_module(module) - qapp = getattr(qt_module, attribute) +def clipboard(qapp): clip = qapp.clipboard() yield clip clip.clear() From d7028dc51e60dd5c1000209dee2aa03bc983ee81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Thu, 11 Sep 2025 19:40:37 -0300 Subject: [PATCH 23/28] chore: use `PyQt5` for minimum instead of `PyQt6` The CI for `311-minimum_versions` tests the clipboard with `PyQt5`. --- ci/deps/actions-311-minimum_versions.yaml | 1 - pandas/compat/_optional.py | 2 +- pyproject.toml | 4 ++-- scripts/tests/data/deps_minimum.toml | 3 +-- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ci/deps/actions-311-minimum_versions.yaml b/ci/deps/actions-311-minimum_versions.yaml index bd6e8ecdfb3a9..da760bceb49ca 100644 --- a/ci/deps/actions-311-minimum_versions.yaml +++ b/ci/deps/actions-311-minimum_versions.yaml @@ -62,5 +62,4 @@ dependencies: - zstandard=0.23.0 - pip: - - PyQt6==6.7.1 - tzdata==2023.3 diff --git a/pandas/compat/_optional.py b/pandas/compat/_optional.py index 859d8c8759776..6b04b4dd3a141 100644 --- a/pandas/compat/_optional.py +++ b/pandas/compat/_optional.py @@ -55,7 +55,7 @@ "xlsxwriter": "3.2.0", "zstandard": "0.23.0", "qtpy": "2.4.2", - "pyqt6": "6.7.1", + "pyqt5": "5.15.9", } # A mapping from import name to package name (on PyPI) for packages where diff --git a/pyproject.toml b/pyproject.toml index 9ca93eef3166d..60a13d073f289 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,7 +76,7 @@ html = ['beautifulsoup4>=4.12.3', 'html5lib>=1.1', 'lxml>=5.3.0'] xml = ['lxml>=5.3.0'] plot = ['matplotlib>=3.9.3'] output-formatting = ['jinja2>=3.1.5', 'tabulate>=0.9.0'] -clipboard = ['PyQt6>=6.7.1', 'qtpy>=2.4.2'] +clipboard = ['PyQt5>=5.15.9', 'qtpy>=2.4.2'] compression = ['zstandard>=0.23.0'] timezone = ['pytz>=2024.2'] all = ['adbc-driver-postgresql>=1.2.0', @@ -99,7 +99,7 @@ all = ['adbc-driver-postgresql>=1.2.0', 'pyarrow>=13.0.0', 'pyiceberg>=0.8.1', 'pymysql>=1.1.1', - 'PyQt6>=6.7.1', + 'PyQt5>=5.15.9', 'pyreadstat>=1.2.8', 'pytest>=7.3.2', 'pytest-xdist>=3.4.0', diff --git a/scripts/tests/data/deps_minimum.toml b/scripts/tests/data/deps_minimum.toml index 62bdc67150464..22a30ed3a83f5 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.7.1', 'qtpy>=2.3.0'] +clipboard = ['PyQt5>=5.15.1', 'qtpy>=2.3.0'] compression = ['zstandard>=0.15.2'] all = ['beautifulsoup4>=5.9.3', 'bottleneck>=1.3.2', @@ -91,7 +91,6 @@ all = ['beautifulsoup4>=5.9.3', 'pyarrow>=7.0.0', 'pymysql>=1.1.0', 'PyQt5>=5.15.1', - 'PyQt6>=6.7.1', 'pyreadstat>=1.1.2', 'pytest>=7.3.2', 'pytest-xdist>=3.4.0', From 2fb3e83bc0c9a455f1c3e02a7a68026689263738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Thu, 11 Sep 2025 19:44:11 -0300 Subject: [PATCH 24/28] ci(clipboard): use `PyQt6` instead of `PyQt5` for non minimum tests --- ci/deps/actions-311.yaml | 1 - ci/deps/actions-312.yaml | 1 - ci/deps/actions-313-downstream_compat.yaml | 1 - ci/deps/actions-313.yaml | 1 - 4 files changed, 4 deletions(-) diff --git a/ci/deps/actions-311.yaml b/ci/deps/actions-311.yaml index 5fb84e30fb4b9..00808ba53cb35 100644 --- a/ci/deps/actions-311.yaml +++ b/ci/deps/actions-311.yaml @@ -39,7 +39,6 @@ dependencies: - numexpr>=2.10.2 - odfpy>=1.4.1 - qtpy>=2.4.2 - - pyqt>=5.15.9 - openpyxl>=3.1.5 - psycopg2>=2.9.10 - pyarrow>=13.0.0 diff --git a/ci/deps/actions-312.yaml b/ci/deps/actions-312.yaml index a032af63da7ce..558c83e7237f2 100644 --- a/ci/deps/actions-312.yaml +++ b/ci/deps/actions-312.yaml @@ -39,7 +39,6 @@ dependencies: - numexpr>=2.10.2 - odfpy>=1.4.1 - qtpy>=2.4.2 - - pyqt>=5.15.9 - openpyxl>=3.1.5 - psycopg2>=2.9.10 - pyarrow>=13.0.0 diff --git a/ci/deps/actions-313-downstream_compat.yaml b/ci/deps/actions-313-downstream_compat.yaml index a1a6aa606f1fa..cabff7e6f70c4 100644 --- a/ci/deps/actions-313-downstream_compat.yaml +++ b/ci/deps/actions-313-downstream_compat.yaml @@ -44,7 +44,6 @@ dependencies: - pyarrow>=13.0.0 - pyiceberg>=0.8.1 - pymysql>=1.1.1 - - pyqt>=5.15.9 - pyreadstat>=1.2.8 - pytables>=3.10.1 - python-calamine>=0.3.0 diff --git a/ci/deps/actions-313.yaml b/ci/deps/actions-313.yaml index 1e9181dc66d2f..ab685e65570c4 100644 --- a/ci/deps/actions-313.yaml +++ b/ci/deps/actions-313.yaml @@ -40,7 +40,6 @@ dependencies: - numexpr>=2.10.2 - odfpy>=1.4.1 - qtpy>=2.4.2 - - pyqt>=5.15.9 - openpyxl>=3.1.5 - psycopg2>=2.9.10 - pyarrow>=13.0.0 From 25db090cba39e37d2186671d678c57a8f16aff62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Thu, 11 Sep 2025 19:56:47 -0300 Subject: [PATCH 25/28] fix: remove pyqt5 from dev dependencies in favour of pyqt6 --- environment.yml | 3 +-- requirements-dev.txt | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/environment.yml b/environment.yml index 7d05ff4fff6e9..fea993f6e550b 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>=5.15.9 - coverage # required dependencies @@ -122,5 +121,5 @@ dependencies: - jupyterlite-pyodide-kernel - pip: - - PyQt6>=6.7.1 + - PyQt6>=6.7.1 # test dependency - tzdata>=2023.3 diff --git a/requirements-dev.txt b/requirements-dev.txt index 0b19ef27941ed..64f0c64378b31 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 -PyQt5>=5.15.9 coverage python-dateutil numpy<3 From 2b90647747daab054d056b89acb8e22b6833e6e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Sat, 13 Sep 2025 13:17:27 -0300 Subject: [PATCH 26/28] ci(docker-build): build on arm64 --- .github/workflows/code-checks.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/code-checks.yml b/.github/workflows/code-checks.yml index 82b1ef586e5dc..d8fd6c618b360 100644 --- a/.github/workflows/code-checks.yml +++ b/.github/workflows/code-checks.yml @@ -132,15 +132,22 @@ jobs: asv run --quick --dry-run --durations=30 --python=same --show-stderr build_docker_dev_environment: - name: Build Docker Dev Environment - runs-on: ubuntu-24.04 + name: Build Docker Dev Environment (${{ matrix.arch }}) + runs-on: ${{ matrix.platform }} + strategy: + matrix: + include: + - arch: amd64 + platform: ubuntu-24.04 + - arch: arm64 + platform: ubuntu-24.04-arm defaults: run: shell: bash -el {0} concurrency: # https://github.community/t/concurrecy-not-work-for-push/183068/7 - group: ${{ github.event_name == 'push' && github.run_number || github.ref }}-build_docker_dev_environment + group: ${{ github.event_name == 'push' && github.run_number || github.ref }}-build_docker_dev_environment-${{ matrix.arch }} cancel-in-progress: true steps: @@ -153,10 +160,10 @@ jobs: fetch-depth: 0 - name: Build image - run: docker build --pull --no-cache --tag pandas-dev-env . + run: docker build --pull --no-cache --tag pandas-dev-env-${{ matrix.arch }} . - name: Show environment - run: docker run --rm pandas-dev-env python -c "import pandas as pd; print(pd.show_versions())" + run: docker run --rm pandas-dev-env-${{ matrix.arch }} python -c "import pandas as pd; print(pd.show_versions())" requirements-dev-text-installable: name: Test install requirements-dev.txt From 72ca4e9032fb4d35e30e4ae2f7756c8ea0914b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Mon, 15 Sep 2025 08:06:12 -0300 Subject: [PATCH 27/28] fix: add pyside6 support Pyside2 support wasn't added because `pytest-qt>=4.5` doesn't support it. Reference: --- pandas/io/clipboard/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pandas/io/clipboard/__init__.py b/pandas/io/clipboard/__init__.py index d19d5c7668c63..e7c441367e042 100644 --- a/pandas/io/clipboard/__init__.py +++ b/pandas/io/clipboard/__init__.py @@ -175,6 +175,7 @@ def init_qt_clipboard(): qt_qapplication_bindings = [ ("qtpy.QtWidgets", "QApplication"), ("PyQt6.QtWidgets", "QApplication"), + ("PySide6.QtWidgets", "QApplication"), ("PyQt5.QtWidgets", "QApplication"), ("PyQt4.QtGui", "QApplication"), ] From d5df8731d2460a6474d334b81270d8317f1a0f23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Kothe?= Date: Mon, 15 Sep 2025 08:21:25 -0300 Subject: [PATCH 28/28] docs(clipboard): update note on clipboard dependencies Set explicit system dependencies on Linux. Make it explicit that Qt bindings support are limited on Linux and states that it works best on Windows and MacOS. The Qt support on Linux isn't reliable because it depends on an event loop and user-input events to modify the clipboard. [1] [1]: --- doc/source/user_guide/io.rst | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/doc/source/user_guide/io.rst b/doc/source/user_guide/io.rst index fd9edfe8d731e..1b27cc3837e16 100644 --- a/doc/source/user_guide/io.rst +++ b/doc/source/user_guide/io.rst @@ -3928,7 +3928,28 @@ 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 PyQt6, PyQt5, PyQt4 or qtpy) on Linux to use these methods. + On Linux, you may need to install one of these clipboard utilities: + + - wl-clipboard (for Wayland sessions) + - xclip (for X11 sessions) + - xsel (for X11 sessions) + + For example, on Debian-based systems: + + .. code-block:: bash + + sudo apt-get install wl-clipboard + sudo apt-get install xclip + sudo apt-get install xsel + + Alternatively, you can install one of these Python packages, + but their support is limited on Linux and work more reliably + on OSX and Windows: + + - PyQt6 + - PySide6 + - PyQt5 + .. _io.pickle: