diff --git a/pyxll_jupyter/kernel.py b/pyxll_jupyter/kernel.py index 96fbc6c..163e20a 100644 --- a/pyxll_jupyter/kernel.py +++ b/pyxll_jupyter/kernel.py @@ -11,21 +11,26 @@ from .magic import ExcelMagics from ipykernel.kernelapp import IPKernelApp from ipykernel.embed import embed_kernel -from zmq.eventloop import ioloop from pyxll import schedule_call, get_config import pyxll import importlib.util -import pkg_resources import subprocess import threading import logging import ctypes import atexit import queue +import zmq import sys import os import re +if zmq.pyzmq_version_info()[0] >= 17: + from tornado.ioloop import IOLoop +else: + from zmq.eventloop.ioloop import IOLoop + + # Command line args for the different Jupyter subcommands _subcommand_jupyter_args = { # subcommand @@ -182,7 +187,7 @@ def _IPKernelApp_start(self): self.kernel.start() # set up a timer to periodically poll the zmq ioloop - self.loop = ioloop.IOLoop.current() + self.loop = IOLoop.current() def poll_ioloop(): try: @@ -255,6 +260,29 @@ def poll_ioloop(): return ipy +def _check_requirement(requirement): + """Return True if a required package version is installed, False otherwise""" + if sys.version_info[:2] >= (3, 10): + from packaging.requirements import Requirement + import importlib.metadata + + try: + requirement = Requirement(requirement) + version = importlib.metadata.version(requirement.name) + return version in requirement.specifier + except importlib.metadata.PackageNotFoundError: + return False + + else: + import pkg_resources + + try: + pkg_resources.require(requirement) + return True + except (pkg_resources.VersionConflict, pkg_resources.DistributionNotFound): + return + + def _get_jupyter_args(subcommand): """Get the args for a Jupyter subcommand.""" if subcommand not in _subcommand_jupyter_args: @@ -263,15 +291,9 @@ def _get_jupyter_args(subcommand): # Find the args for the installed Jupyter version requirement = "unknown" for requirement, args in _subcommand_jupyter_args[subcommand]: - if requirement is None: + if requirement is None or _check_requirement(requirement): return args - try: - pkg_resources.require(requirement) - return args - except (pkg_resources.VersionConflict, pkg_resources.DistributionNotFound): - pass - raise RuntimeError(f"Requirements for Jupyter {subcommand} not satisfied ({requirement}).") diff --git a/pyxll_jupyter/pyxll.py b/pyxll_jupyter/pyxll.py index 47218da..0f6663e 100644 --- a/pyxll_jupyter/pyxll.py +++ b/pyxll_jupyter/pyxll.py @@ -15,7 +15,6 @@ from pyxll import xlcAlert, get_config, xl_app, xl_macro, schedule_call from functools import partial import ctypes.wintypes -import pkg_resources import logging import sys import os @@ -23,6 +22,20 @@ _log = logging.getLogger(__name__) +if sys.version_info[:2] >= (3, 7): + import importlib.resources + + def _resource_bytes(package, resource_name): + ref = importlib.resources.files(package).joinpath(resource_name) + with ref.open('rb') as fp: + return fp.read() +else: + import pkg_resources + + def _resource_bytes(package, resource_name): + return pkg_resources.resource_stream(package, resource_name).read() + + def _get_qt_app(): """Get or create the Qt application""" app = QApplication.instance() @@ -335,7 +348,7 @@ def ribbon(): if disable_ribbon: return [] - ribbon = pkg_resources.resource_string(__name__, "resources/ribbon.xml") + ribbon = _resource_bytes("pyxll_jupyter", "resources/ribbon.xml").decode("utf-8") return [ (None, ribbon) ] diff --git a/setup.py b/setup.py index a18f13c..752409a 100644 --- a/setup.py +++ b/setup.py @@ -64,6 +64,7 @@ "jupyter >= 1.0.0", "jupyter-client >= 6.0.0", "notebook >= 6.0.0", + "packaging; python_version >= '3.10'", "PySide2; python_version < '3.10'", "PySide6 != 6.4.2; python_version >= '3.10' and platform_machine == 'x86_64'", "PyQt5; python_version >= '3.10' and platform_machine != 'x86_64'",