diff --git a/Orange/canvas/__main__.py b/Orange/canvas/__main__.py index 975874e27d1..b42d0dc13aa 100644 --- a/Orange/canvas/__main__.py +++ b/Orange/canvas/__main__.py @@ -15,8 +15,8 @@ from collections import defaultdict from datetime import date from urllib.request import urlopen, Request +from packaging.version import Version -import pkg_resources import yaml from AnyQt.QtGui import QColor, QDesktopServices, QIcon, QPalette @@ -101,9 +101,8 @@ def run(self): self.resultReady.emit(contents) def compare_versions(latest): - version = pkg_resources.parse_version skipped = settings.value('startup/latest-skipped-version', "", type=str) - if version(latest) <= version(current) or \ + if Version(latest) <= Version(current) or \ latest == skipped: return @@ -187,8 +186,6 @@ def pull_notifications(): if not check_notifs: return None - Version = pkg_resources.parse_version - # create settings_dict for notif requirements purposes (read-only) spec = canvasconfig.spec + config.spec settings_dict = canvasconfig.Settings(defaults=spec, store=settings) @@ -198,7 +195,7 @@ def pull_notifications(): if ep.dist is not None] installed = defaultdict(lambda: "-1") for addon in installed_list: - installed[addon.project_name] = addon.version + installed[addon.name] = addon.version # get set of already displayed notification IDs, stored in settings["notifications/displayed"] displayedIDs = literal_eval(settings.value("notifications/displayed", "set()", str)) diff --git a/Orange/canvas/config.py b/Orange/canvas/config.py index fcef63a1b43..d3db38c5490 100644 --- a/Orange/canvas/config.py +++ b/Orange/canvas/config.py @@ -2,6 +2,8 @@ Orange Canvas Configuration """ +import pkgutil + import random import uuid import warnings @@ -11,15 +13,16 @@ from typing import Dict, Any, Optional, Iterable, List import packaging.version -import pkg_resources import requests from AnyQt.QtGui import ( - QPainter, QFont, QFontMetrics, QColor, QPixmap, QIcon, QGuiApplication + QPainter, QFont, QFontMetrics, QColor, QImage, QPixmap, QIcon, + QGuiApplication ) from AnyQt.QtCore import Qt, QPoint, QRect, QSettings from orangecanvas import config as occonfig +from orangecanvas.config import entry_points, EntryPoint from orangecanvas.utils.settings import config_slot from orangewidget.workflow import config from orangewidget.settings import set_widget_settings_dir_components @@ -61,6 +64,11 @@ spec = [config_slot(*t) for t in spec] +def _pixmap_from_pkg_data(package, path, format): + contents = pkgutil.get_data(package, path) + return QPixmap.fromImage(QImage.fromData(contents, format)) + + class Config(config.Config): """ Orange application configuration @@ -96,17 +104,16 @@ def application_icon(): """ Return the main application icon. """ - path = pkg_resources.resource_filename( - __name__, "icons/orange-256.png" + return QIcon( + _pixmap_from_pkg_data(__package__, "icons/orange-256.png", "png") ) - return QIcon(path) @staticmethod def splash_screen(): splash_n = random.randint(1, 3) - path = pkg_resources.resource_filename( - __name__, f"icons/orange-splash-screen-{splash_n:02}.png") - pm = QPixmap(path) + pm = _pixmap_from_pkg_data( + __name__, f"icons/orange-splash-screen-{splash_n:02}.png", "png" + ) version = Config.ApplicationVersion if version: @@ -138,9 +145,9 @@ def widgets_entry_points(): # Ensure the 'this' distribution's ep is the first. iter_entry_points # yields them in unspecified order. all_eps = sorted( - pkg_resources.iter_entry_points(WIDGETS_ENTRY), + entry_points(group=WIDGETS_ENTRY), key=lambda ep: - 0 if ep.dist.project_name.lower() == "orange3" else 1 + 0 if ep.dist.name.lower() == "orange3" else 1 ) return iter(all_eps) @@ -173,18 +180,18 @@ def core_packages(): @staticmethod def examples_entry_points(): - # type: () -> Iterable[pkg_resources.EntryPoint] + # type: () -> Iterable[EntryPoint] """ Return an iterator over the entry points yielding 'Example Workflows' """ # `iter_entry_points` yields them in unspecified order, so we order # them by name. The default is at the beginning, unless another # entrypoint precedes it alphabetically (e.g. starting with '!'). - default_ep = pkg_resources.EntryPoint( - "000-Orange3", "Orange.canvas.workflows", - dist=pkg_resources.get_distribution("Orange3")) + default_ep = EntryPoint( + "000-Orange3", "Orange.canvas.workflows", "orange.widgets.tutorials", + ) - all_ep = list(pkg_resources.iter_entry_points("orange.widgets.tutorials")) + all_ep = list(entry_points(group="orange.widgets.tutorials")) all_ep.append(default_ep) all_ep.sort(key=lambda x: x.name) return iter(all_ep) diff --git a/Orange/tests/test_third_party.py b/Orange/tests/test_third_party.py index aaad0cbcdf1..7657c385eac 100644 --- a/Orange/tests/test_third_party.py +++ b/Orange/tests/test_third_party.py @@ -1,11 +1,11 @@ from unittest import TestCase -from pkg_resources import parse_version +from packaging.version import Version class TestPkgResources(TestCase): def test_parse_version(self): - self.assertGreater(parse_version('3.4.1'), parse_version('3.4.0')) - self.assertGreater(parse_version('3.4.1'), parse_version('3.4.dev')) - self.assertGreater(parse_version('3.4.1'), parse_version('3.4.1.dev')) - self.assertLess(parse_version('3.4.1'), parse_version('3.4.2.dev')) + self.assertGreater(Version('3.4.1'), Version('3.4.0')) + self.assertGreater(Version('3.4.1'), Version('3.4.dev')) + self.assertGreater(Version('3.4.1'), Version('3.4.1.dev')) + self.assertLess(Version('3.4.1'), Version('3.4.2.dev')) diff --git a/Orange/util.py b/Orange/util.py index 2a1581677de..d3a9d1073dc 100644 --- a/Orange/util.py +++ b/Orange/util.py @@ -6,11 +6,11 @@ import datetime import math import functools +import importlib.resources from contextlib import contextmanager +from importlib.metadata import distribution from typing import TYPE_CHECKING, Callable, Union, Optional from weakref import WeakKeyDictionary - -import pkg_resources from enum import Enum as _Enum from functools import wraps, partial from operator import attrgetter @@ -115,20 +115,18 @@ def resource_filename(path): """ Return the resource filename path relative to the Orange package. """ - return pkg_resources.resource_filename("Orange", path) + path = importlib.resources.files("Orange").joinpath(path) + return str(path) def get_entry_point(dist, group, name): """ Load and return the entry point from the distribution. - - Unlike `pkg_resources.load_entry_point`, this function does not check - for requirements. Calling this function is preferred because of developers - who experiment with different versions and have inconsistent configurations. """ - dist = pkg_resources.get_distribution(dist) - ep = dist.get_entry_info(group, name) - return ep.resolve() + dist = distribution(dist) + eps = dist.entry_points.select(group=group, name=name) + ep = next(iter(eps)) + return ep.load() def deprecated(obj): diff --git a/Orange/widgets/__init__.py b/Orange/widgets/__init__.py index 1b88afbf4ba..488995e50e4 100644 --- a/Orange/widgets/__init__.py +++ b/Orange/widgets/__init__.py @@ -4,17 +4,16 @@ import os import sysconfig -import pkg_resources - from orangecanvas.registry import CategoryDescription from orangecanvas.registry.utils import category_from_package_globals +from orangecanvas.utils.pkgmeta import get_distribution import orangewidget.workflow.discovery # Entry point for main Orange categories/widgets discovery def widget_discovery(discovery): # type: (orangewidget.workflow.discovery.WidgetDiscovery) -> None - dist = pkg_resources.get_distribution("Orange3") + dist = get_distribution("Orange3") pkgs = [ "Orange.widgets.data", "Orange.widgets.visualize", diff --git a/Orange/widgets/data/owpreprocess.py b/Orange/widgets/data/owpreprocess.py index 872cfcd50cf..7a6e7342699 100644 --- a/Orange/widgets/data/owpreprocess.py +++ b/Orange/widgets/data/owpreprocess.py @@ -1,5 +1,5 @@ from collections import OrderedDict -import pkg_resources +import importlib.resources import numpy @@ -993,7 +993,8 @@ def __init__(self, name, qualname, category, description, viewclass): def icon_path(basename): - return pkg_resources.resource_filename(__name__, "icons/" + basename) + path = importlib.resources.files(__package__).joinpath("icons/" + basename) + return str(path) PREPROCESS_ACTIONS = [ diff --git a/Orange/widgets/tests/test_workflows.py b/Orange/widgets/tests/test_workflows.py index 12a07bc9e32..630eee450b5 100644 --- a/Orange/widgets/tests/test_workflows.py +++ b/Orange/widgets/tests/test_workflows.py @@ -2,11 +2,12 @@ from os import listdir, environ from os.path import isfile, join, dirname import unittest -import pkg_resources +from unittest import mock from AnyQt.QtTest import QTest from orangecanvas.registry import WidgetRegistry +from orangecanvas.config import EntryPoint from orangewidget.workflow import widgetsscheme from Orange.canvas.config import Config @@ -63,17 +64,17 @@ def test_scheme_examples(self): QTest.qWait(0) def test_examples_order(self): - # register test entrypoints - dist_orange = pkg_resources.working_set.by_key['orange3'] - ep_map = dist_orange.get_entry_map() - ep_first = pkg_resources.EntryPoint('!Testname', 'orangecontrib.any_addon.tutorials') - ep_last = pkg_resources.EntryPoint('exampletutorials', 'orangecontrib.other_addon.tutorials') - ep_map['orange.widgets.tutorials'] = {ep_first.name: ep_first, ep_last.name: ep_last} + ep_first = EntryPoint( + '!Testname', 'orangecontrib.any_addon.tutorials', '') + ep_last = EntryPoint( + 'exampletutorials', 'orangecontrib.other_addon.tutorials', '') - ep_names = [point.name for point in Config.examples_entry_points()] + def entry_points(*_, **__): + return ep_first, ep_last + + with mock.patch("Orange.canvas.config.entry_points", entry_points): + ep_names = [point.name for point in Config.examples_entry_points()] self.assertLess(ep_names.index(ep_first.name), ep_names.index("000-Orange3")) self.assertLess(ep_names.index("000-Orange3"), ep_names.index(ep_last.name)) - - del ep_map['orange.widgets.tutorials'] diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index 4ab208c4bc7..9b65b01966e 100644 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -37,8 +37,8 @@ requirements: run: - python # GUI requirements - - orange-canvas-core >=0.1.30,<0.2a - - orange-widget-base >=4.22.0 + - orange-canvas-core >=0.2.0,<0.3a + - orange-widget-base >=4.23.0 - anyqt >=0.2.0 - pyqt >=5.12,!=5.15.1,<6.0 - matplotlib-base >=3.2.0 @@ -66,7 +66,6 @@ requirements: - requests - scikit-learn >=1.3.0 - scipy >=1.9 - - serverfiles - setuptools >=51.0.0 - xgboost >=1.7.4 - xlsxwriter diff --git a/i18n/si.jaml b/i18n/si.jaml index b3775d00e4a..a17061b0c9d 100644 --- a/i18n/si.jaml +++ b/i18n/si.jaml @@ -364,8 +364,10 @@ canvas/config.py: canvas_settings_dir: false def `application_icon`: icons/orange-256.png: false + png: false def `splash_screen`: icons/orange-splash-screen-{splash_n:02}.png: false + png: false .: false Helvetica: false '#000000': false diff --git a/pylintrc b/pylintrc index 3f5d492f311..02e76ac05b2 100644 --- a/pylintrc +++ b/pylintrc @@ -332,4 +332,4 @@ int-import-graph= overgeneral-exceptions=builtins.Exception [isort] -known-standard-library=pkg_resources +known-standard-library= diff --git a/requirements-core.txt b/requirements-core.txt index d09fb9d0c9c..db1c8d514f2 100644 --- a/requirements-core.txt +++ b/requirements-core.txt @@ -12,6 +12,7 @@ networkx numpy>=1.20.0 openpyxl openTSNE>=0.6.1,!=0.7.0 # 0.7.0 segfaults +packaging pandas>=1.4.0,!=1.5.0,!=2.0.0 pip>=18.0 python-louvain>=0.13 @@ -20,7 +21,6 @@ requests scikit-learn>=1.3.0 scipy>=1.9 serverfiles # for Data Sets synchronization -setuptools>=51.0.0 xgboost>=1.7.4 # Reading Excel files xlrd>=1.2.0 diff --git a/requirements-gui.txt b/requirements-gui.txt index a451226f7c4..e94cc275c9c 100644 --- a/requirements-gui.txt +++ b/requirements-gui.txt @@ -1,5 +1,5 @@ -orange-canvas-core>=0.1.30,<0.2a -orange-widget-base>=4.22.0 +orange-canvas-core>=0.2a,<0.3a +orange-widget-base>=4.23.0 AnyQt>=0.2.0 diff --git a/tox.ini b/tox.ini index 828c708e716..bb3b2dabc0a 100644 --- a/tox.ini +++ b/tox.ini @@ -36,8 +36,8 @@ deps = latest: https://github.com/biolab/orange-canvas-core/archive/refs/heads/master.zip#egg=orange-canvas-core latest: https://github.com/biolab/orange-widget-base/archive/refs/heads/master.zip#egg=orange-widget-base # GUI requirements - oldest: orange-canvas-core==0.1.30 - oldest: orange-widget-base==4.22.0 + oldest: orange-canvas-core==0.2.0 + oldest: orange-widget-base==4.23.0 oldest: AnyQt==0.2.0 oldest: matplotlib==3.2.0 oldest: pygments==2.8.0 @@ -64,7 +64,6 @@ deps = oldest: scikit-learn==1.3.0 oldest: scipy==1.9 # oldest: serverfiles - oldest: setuptools==51.0.0 oldest: xgboost==1.7.4 oldest: xlrd==1.2.0 # oldest: xlsxwriter