diff --git a/var/spack/repos/builtin/packages/py-pyqt4/package.py b/var/spack/repos/builtin/packages/py-pyqt4/package.py
index d1a90042d95257ce51ddbe5678bd29200083aff6..6c5af83cb265fd3630781804de61ab96106e4746 100644
--- a/var/spack/repos/builtin/packages/py-pyqt4/package.py
+++ b/var/spack/repos/builtin/packages/py-pyqt4/package.py
@@ -4,7 +4,6 @@
 # SPDX-License-Identifier: (Apache-2.0 OR MIT)
 
 from spack import *
-import os
 
 
 class PyPyqt4(SIPPackage):
@@ -29,21 +28,13 @@ class PyPyqt4(SIPPackage):
     version('4.11.3', sha256='853780dcdbe2e6ba785d703d059b096e1fc49369d3e8d41a060be874b8745686',
             url='http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.11.3/PyQt-x11-gpl-4.11.3.tar.gz')
 
-    variant('qsci', default=False, description='Build with QScintilla python bindings')
+    # API files can be installed regardless if QScintilla is installed or not
+    variant('qsci_api', default=False, description='Install PyQt API file for QScintilla')
 
     # Supposedly can also be built with Qt 5 compatibility layer
     depends_on('qt@:4')
-    depends_on('qscintilla', when='+qsci')
     depends_on('py-sip module=PyQt4.sip')
 
-    # For building Qscintilla python bindings
-    resource(name='qscintilla',
-             url='https://www.riverbankcomputing.com/static/Downloads/QScintilla/2.10.2/QScintilla_gpl-2.10.2.tar.gz',
-             sha256='14b31d20717eed95ea9bea4cd16e5e1b72cee7ebac647cba878e0f6db6a65ed0',
-             destination='spack-resource-qscintilla',
-             when='^qscintilla@2.10.2'
-    )
-
     # https://www.riverbankcomputing.com/static/Docs/PyQt4/installation.html
     def configure_file(self):
         return 'configure-ng.py'
@@ -54,52 +45,7 @@ def configure_args(self):
             '--sipdir', self.prefix.share.sip.PyQt4,
             '--stubsdir', join_path(site_packages_dir, 'PyQt4')
         ]
-        if '+qsci' in self.spec:
-            args.extend(['--qsci-api-destdir', self.prefix.share.qsci])
+        if '+qsci_api' in self.spec:
+            args.extend(['--qsci-api',
+                         '--qsci-api-destdir', self.prefix.share.qsci])
         return args
-
-    @run_after('install')
-    def make_qsci(self):
-        if '+qsci' in self.spec:
-            rsrc_py_path = os.path.join(
-                self.stage.source_path,
-                'spack-resource-qscintilla/QScintilla_gpl-' +
-                str(self.spec['qscintilla'].version), 'Python')
-            with working_dir(rsrc_py_path):
-                pydir = join_path(site_packages_dir, 'PyQt4')
-                python = self.spec['python'].command
-                python('configure.py',
-                       '--sip=' + self.spec['py-sip'].prefix.bin.sip,
-                       '--qsci-incdir=' +
-                       self.spec['qscintilla'].prefix.include,
-                       '--qsci-libdir=' + self.spec['qscintilla'].prefix.lib,
-                       '--qsci-sipdir=' + self.prefix.share.sip.PyQt4,
-                       '--apidir=' + self.prefix.share.qsci,
-                       '--destdir=' + pydir,
-                       '--pyqt-sipdir=' + self.prefix.share.sip.PyQt4,
-                       '--sip-incdir=' +
-                       join_path(self.spec['py-sip'].prefix.include,
-                                 'python' +
-                                 str(self.spec['python'].version.up_to(2))),
-                       '--stubsdir=' + pydir)
-
-                # Fix build errors
-                # "QAbstractScrollArea: No such file or directory"
-                # "qprinter.h: No such file or directory"
-                # ".../Qsci.so: undefined symbol: _ZTI10Qsci...."
-                qscipro = FileFilter('Qsci/Qsci.pro')
-                link_qscilibs = 'LIBS += -L' + self.prefix.lib +\
-                    ' -lqscintilla2_qt4'
-                qscipro.filter('TEMPLATE = lib',
-                               'TEMPLATE = lib\nQT += widgets' +
-                               '\nQT += printsupport\n' + link_qscilibs)
-
-                make()
-
-                # Fix installation prefixes
-                makefile = FileFilter('Makefile')
-                makefile.filter(r'\$\(INSTALL_ROOT\)', '')
-                makefile = FileFilter('Qsci/Makefile')
-                makefile.filter(r'\$\(INSTALL_ROOT\)', '')
-
-                make('install')
diff --git a/var/spack/repos/builtin/packages/py-pyqt5/package.py b/var/spack/repos/builtin/packages/py-pyqt5/package.py
index bc37d4dcab1de0ec60340f66c6370b1a96ed7662..e26c66a891c56f3e2f36a45af148c5bab806b5c1 100644
--- a/var/spack/repos/builtin/packages/py-pyqt5/package.py
+++ b/var/spack/repos/builtin/packages/py-pyqt5/package.py
@@ -4,7 +4,6 @@
 # SPDX-License-Identifier: (Apache-2.0 OR MIT)
 
 from spack import *
-import os
 
 
 class PyPyqt5(SIPPackage):
@@ -30,7 +29,8 @@ class PyPyqt5(SIPPackage):
     version('5.13.0', sha256='0cdbffe5135926527b61cc3692dd301cd0328dd87eeaf1313e610787c46faff9')
     version('5.12.3', sha256='0db0fa37debab147450f9e052286f7a530404e2aaddc438e97a7dcdf56292110')
 
-    variant('qsci', default=False, description='Build with QScintilla python bindings')
+    # API files can be installed regardless if Qscintilla is installed or not
+    variant('qsci_api', default=False, description='Install PyQt API file for QScintilla')
 
     # Without opengl support, I got the following error:
     # sip: QOpenGLFramebufferObject is undefined
@@ -39,15 +39,6 @@ class PyPyqt5(SIPPackage):
     depends_on('py-enum34', type=('build', 'run'), when='^python@:3.3')
     depends_on('py-sip module=PyQt5.sip', type=('build', 'run'))
     depends_on('py-sip@:4.19.18 module=PyQt5.sip', type=('build', 'run'), when='@:5.13.0')
-    depends_on('qscintilla', when='+qsci')
-
-    # For building Qscintilla python bindings
-    resource(name='qscintilla',
-             url='https://www.riverbankcomputing.com/static/Downloads/QScintilla/2.10.2/QScintilla_gpl-2.10.2.tar.gz',
-             sha256='14b31d20717eed95ea9bea4cd16e5e1b72cee7ebac647cba878e0f6db6a65ed0',
-             destination='spack-resource-qscintilla',
-             when='^qscintilla@2.10.2'
-    )
 
     # https://www.riverbankcomputing.com/static/Docs/PyQt5/installation.html
     def configure_args(self):
@@ -59,55 +50,7 @@ def configure_args(self):
                 self.spec['python'].package.site_packages_dir,
                 'PyQt5'),
         ]
-        if '+qsci' in self.spec:
-            args.extend(['--qsci-api-destdir', self.prefix.share.qsci])
+        if '+qsci_api' in self.spec:
+            args.extend(['--qsci-api',
+                         '--qsci-api-destdir', self.prefix.share.qsci])
         return args
-
-    @run_after('install')
-    def make_qsci(self):
-        if '+qsci' in self.spec:
-            rsrc_py_path = os.path.join(
-                self.stage.source_path,
-                'spack-resource-qscintilla/QScintilla_gpl-' +
-                str(self.spec['qscintilla'].version), 'Python')
-            with working_dir(rsrc_py_path):
-                pydir = join_path(
-                    self.prefix,
-                    self.spec['python'].package.site_packages_dir,
-                    'PyQt5')
-                python = self.spec['python'].command
-                python('configure.py', '--pyqt=PyQt5',
-                       '--sip=' + self.spec['py-sip'].prefix.bin.sip,
-                       '--qsci-incdir=' +
-                       self.spec['qscintilla'].prefix.include,
-                       '--qsci-libdir=' + self.spec['qscintilla'].prefix.lib,
-                       '--qsci-sipdir=' + self.prefix.share.sip.PyQt5,
-                       '--apidir=' + self.prefix.share.qsci,
-                       '--destdir=' + pydir,
-                       '--pyqt-sipdir=' + self.prefix.share.sip.PyQt5,
-                       '--sip-incdir=' +
-                       join_path(self.spec['py-sip'].prefix.include,
-                                 'python' +
-                                 str(self.spec['python'].version.up_to(2))),
-                       '--stubsdir=' + pydir)
-
-                # Fix build errors
-                # "QAbstractScrollArea: No such file or directory"
-                # "qprinter.h: No such file or directory"
-                # ".../Qsci.so: undefined symbol: _ZTI10Qsci...."
-                qscipro = FileFilter('Qsci/Qsci.pro')
-                link_qscilibs = 'LIBS += -L' + self.prefix.lib +\
-                    ' -lqscintilla2_qt5'
-                qscipro.filter('TEMPLATE = lib',
-                               'TEMPLATE = lib\nQT += widgets' +
-                               '\nQT += printsupport\n' + link_qscilibs)
-
-                make()
-
-                # Fix installation prefixes
-                makefile = FileFilter('Makefile')
-                makefile.filter(r'\$\(INSTALL_ROOT\)', '')
-                makefile = FileFilter('Qsci/Makefile')
-                makefile.filter(r'\$\(INSTALL_ROOT\)', '')
-
-                make('install')
diff --git a/var/spack/repos/builtin/packages/qgis/package.py b/var/spack/repos/builtin/packages/qgis/package.py
index 4a6d6a3583b423d0049b73c3e4b0fc484b4e5b17..89536068da274aaa770d03622c3a4940ad18ded7 100644
--- a/var/spack/repos/builtin/packages/qgis/package.py
+++ b/var/spack/repos/builtin/packages/qgis/package.py
@@ -74,9 +74,9 @@ class Qgis(CMakePackage):
     depends_on('qwtpolar')
     depends_on('expat@1.95:')
     depends_on('qca@2.2.1')
-    depends_on('py-pyqt4 +qsci', when='@2')
-    depends_on('py-pyqt5@5.3: +qsci', when='@3')
-    depends_on('qscintilla')
+    depends_on('py-pyqt4', when='@2')
+    depends_on('py-pyqt5@5.3:', when='@3')
+    depends_on('qscintilla +python')
     depends_on('qjson')
     depends_on('py-requests', type=('build', 'run'))  # TODO: is build dependency necessary?
     depends_on('py-psycopg2', type=('build', 'run'))  # TODO: is build dependency necessary?
diff --git a/var/spack/repos/builtin/packages/qscintilla/package.py b/var/spack/repos/builtin/packages/qscintilla/package.py
index 9bee719c1361621b53bab0d0990722b265108996..ec20257676d043320716c0f62f4b47f4586a0d46 100644
--- a/var/spack/repos/builtin/packages/qscintilla/package.py
+++ b/var/spack/repos/builtin/packages/qscintilla/package.py
@@ -20,10 +20,14 @@ class Qscintilla(QMakePackage):
     version('2.10.2', sha256='14b31d20717eed95ea9bea4cd16e5e1b72cee7ebac647cba878e0f6db6a65ed0', preferred=True)
 
     variant('designer', default=False, description="Enable pluging for Qt-Designer")
-    # No 'python' variant, since Python bindings will be
-    # built by PyQt5+qsci instead
+    variant('python', default=False, description="Build python bindings")
 
     depends_on('qt')
+    depends_on('py-pyqt5 +qsci_api', type=('build', 'run'),  when='+python ^qt@5')
+    depends_on('py-pyqt4 +qsci_api', type=('build', 'run'),  when='+python ^qt@4')
+    depends_on('python',   type=('build', 'run'),  when='+python')
+
+    extends('python', when='+python')
 
     @run_before('qmake')
     def chdir(self):
@@ -66,3 +70,73 @@ def postinstall(self):
                 makefile.filter(r'\$\(INSTALL_ROOT\)' +
                                 self.spec['qt'].prefix, '$(INSTALL_ROOT)')
                 make('install')
+
+    @run_after('install')
+    def make_qsci(self):
+        if '+python' in self.spec:
+            if '^py-pyqt4' in self.spec:
+                py_pyqtx = 'py-pyqt4'
+                pyqtx = 'PyQt4'
+            elif '^py-pyqt5' in self.spec:
+                py_pyqtx = 'py-pyqt5'
+                pyqtx = 'PyQt5'
+
+            with working_dir(join_path(self.stage.source_path, 'Python')):
+                pydir = join_path(
+                    self.prefix,
+                    self.spec['python'].package.site_packages_dir,
+                    pyqtx)
+                mkdirp(os.path.join(self.prefix.share.sip, pyqtx))
+                python = self.spec['python'].command
+                python('configure.py', '--pyqt=' + pyqtx,
+                       '--sip=' + self.spec['py-sip'].prefix.bin.sip,
+                       '--qsci-incdir=' + self.spec.prefix.include,
+                       '--qsci-libdir=' + self.spec.prefix.lib,
+                       '--qsci-sipdir=' +
+                       os.path.join(self.prefix.share.sip, pyqtx),
+                       '--apidir=' + self.prefix.share.qsci,
+                       '--destdir=' + pydir,
+                       '--pyqt-sipdir=' + os.path.join(
+                           self.spec[py_pyqtx].prefix.share.sip, pyqtx),
+                       '--sip-incdir=' +
+                       join_path(self.spec['py-sip'].prefix.include,
+                                 'python' +
+                                 str(self.spec['python'].version.up_to(2))),
+                       '--stubsdir=' + pydir)
+
+                # Fix build errors
+                # "QAbstractScrollArea: No such file or directory"
+                # "qprinter.h: No such file or directory"
+                # ".../Qsci.so: undefined symbol: _ZTI10Qsci...."
+                qscipro = FileFilter('Qsci/Qsci.pro')
+                if '^qt@4' in self.spec:
+                    qtx = 'qt4'
+                elif '^qt@5' in self.spec:
+                    qtx = 'qt5'
+
+                link_qscilibs = 'LIBS += -L' + self.prefix.lib +\
+                    ' -lqscintilla2_' + qtx
+                qscipro.filter('TEMPLATE = lib',
+                               'TEMPLATE = lib\nQT += widgets' +
+                               '\nQT += printsupport\n' + link_qscilibs)
+
+                make()
+
+                # Fix installation prefixes
+                makefile = FileFilter('Makefile')
+                makefile.filter(r'\$\(INSTALL_ROOT\)', '')
+                makefile = FileFilter('Qsci/Makefile')
+                makefile.filter(r'\$\(INSTALL_ROOT\)', '')
+
+                make('install')
+
+    @run_after('install')
+    def extend_path_setup(self):
+        # See github issue #14121 and PR #15297
+        module = self.spec['py-sip'].variants['module'].value
+        if module != 'sip':
+            module = module.split('.')[0]
+            with working_dir(site_packages_dir):
+                with open(os.path.join(module, '__init__.py'), 'w') as f:
+                    f.write('from pkgutil import extend_path\n')
+                    f.write('__path__ = extend_path(__path__, __name__)\n')