Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Orange.utils: Some random pylinting #6613

Merged
merged 1 commit into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 50 additions & 8 deletions Orange/tests/test_util.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from itertools import count
import os
import unittest
import warnings
Expand All @@ -6,8 +7,7 @@
import scipy.sparse as sp

from Orange.util import export_globals, flatten, deprecated, try_, deepgetattr, \
OrangeDeprecationWarning, nan_eq, nan_hash_stand
from Orange.data import Table
OrangeDeprecationWarning, nan_eq, nan_hash_stand, Registry, namegen
from Orange.data.util import vstack, hstack, array_equal
from Orange.statistics.util import stats
from Orange.tests.test_statistics import dense_sparse
Expand Down Expand Up @@ -43,10 +43,11 @@ def identity(x):

def test_try_(self):
self.assertTrue(try_(lambda: np.ones(3).any()))
self.assertFalse(try_(lambda: np.whatever()))
self.assertFalse(try_(lambda: 1 / 0))
self.assertEqual(try_(len, default=SOMETHING), SOMETHING)

def test_reprable(self):
# pylint: disable=import-outside-toplevel
from Orange.data import ContinuousVariable
from Orange.preprocess.impute import ReplaceUnknownsRandom
from Orange.statistics.distribution import Continuous
Expand All @@ -66,7 +67,7 @@ def test_reprable(self):
self.assertEqual(repr(logit), 'LogisticRegressionLearner()')

def test_deepgetattr(self):
class a:
class a: # pylint: disable=invalid-name
l = []
self.assertTrue(deepgetattr(a, 'l.__len__.__call__'), a.l.__len__.__call__)
self.assertTrue(deepgetattr(a, 'l.__nx__.__x__', 42), 42)
Expand Down Expand Up @@ -134,10 +135,8 @@ def test_raise_deprecations(self):
warnings.warn('foo', OrangeDeprecationWarning)

def test_stats_sparse(self):
"""
Stats should not fail when trying to calculate mean on sparse data.
GH-2357
"""
# pylint: disable=import-outside-toplevel
from Orange.data import Table
data = Table("iris")
sparse_x = sp.csr_matrix(data.X)
self.assertTrue(stats(data.X).all() == stats(sparse_x).all())
Expand Down Expand Up @@ -198,6 +197,49 @@ def func(i):
self.assertEqual(f(0.1), 0.17)
self.assertEqual(f(1), 0.8)

def test_registry(self):
# pylint: disable=invalid-name, unused-variable
class A(metaclass=Registry):
pass

class BBB(A):
pass

class CC(A):
pass

class DDDD(BBB):
pass

self.assertEqual(set(A), {"BBB", "CC", "DDDD"})
self.assertEqual(str(A), "A({BBB, CC, DDDD})")

class D(metaclass=Registry):
pass

self.assertEqual(set(A), {"BBB", "CC", "DDDD"})
self.assertEqual(str(A), "A({BBB, CC, DDDD})")
self.assertEqual(set(D), set())
self.assertEqual(str(D), "D({})")

class E(D):
pass

self.assertEqual(set(A), {"BBB", "CC", "DDDD"})
self.assertEqual(str(A), "A({BBB, CC, DDDD})")
self.assertEqual(set(D), set("E"))
self.assertEqual(str(D), "D({E})")

def test_namegen(self):
self.assertEqual([name for name, _ in zip(namegen(), range(3))],
["_0", "_1", "_2"])

self.assertEqual([name for name, _ in zip(namegen("foo "), range(3))],
["foo 0", "foo 1", "foo 2"])

self.assertEqual([name for name, _ in zip(namegen("foo ", 2, spec_count=count), range(3))],
["foo 2", "foo 3", "foo 4"])


if __name__ == "__main__":
unittest.main()
46 changes: 26 additions & 20 deletions Orange/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from operator import attrgetter
from itertools import chain, count, repeat

from collections import OrderedDict, namedtuple
from collections import namedtuple
import warnings

# Exposed here for convenience. Prefer patching to try-finally blocks
Expand Down Expand Up @@ -161,18 +161,18 @@ def deprecated(obj):
... return 'new behavior'
>>> C().old() # doctest: +SKIP
/... OrangeDeprecationWarning: Call to deprecated ... C.old ...
Instead, use C.new() ...
use use C.new() instead ...
'old behavior'
"""
alternative = ('; Instead, use ' + obj) if isinstance(obj, str) else ''
alternative = f'; use {obj} instead' if isinstance(obj, str) else ''

def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
name = '{}.{}'.format(
func.__self__.__class__,
func.__name__) if hasattr(func, '__self__') else func
warnings.warn('Call to deprecated {}{}'.format(name, alternative),
name = func.__name__
if hasattr(func, "__self__"):
name = f'{func.__self__.__class__}.{name}'
warnings.warn(f'Call to deprecated {name}{alternative}',
OrangeDeprecationWarning, stacklevel=2)
return func(*args, **kwargs)
return wrapper
Expand All @@ -181,7 +181,7 @@ def wrapper(*args, **kwargs):


def literal_eval(literal):
import ast
import ast # pylint: disable=import-outside-toplevel
# ast.literal_eval does not parse empty set ¯\_(ツ)_/¯

if literal == "set()":
Expand Down Expand Up @@ -225,11 +225,11 @@ def requirementsSatisfied(required_state, local_state, req_type=None):
for req_string in required_state:
# parse requirement
req = None
for op_str in op_map:
for op_str, op in op_map.items():
split = req_string.split(op_str)
# if operation is not in req_string, continue
if len(split) == 2:
req = _Requirement(split[0], op_map[op_str], split[1])
req = _Requirement(split[0], op, split[1])
break

if req is None:
Expand Down Expand Up @@ -269,7 +269,7 @@ class Registry(type):
def __new__(mcs, name, bases, attrs):
cls = type.__new__(mcs, name, bases, attrs)
if not hasattr(cls, 'registry'):
cls.registry = OrderedDict()
cls.registry = {}
else:
cls.registry[name] = cls
return cls
Expand All @@ -280,11 +280,14 @@ def __iter__(cls):
def __str__(cls):
if cls in cls.registry.values():
return cls.__name__
return '{}({{{}}})'.format(cls.__name__, ', '.join(cls.registry))
return f'{cls.__name__}({{{", ".join(cls.registry)}}})'


# it is what it is, we keep for compatibility:
# pylint: disable=keyword-arg-before-vararg
def namegen(prefix='_', *args, spec_count=count, **kwargs):
"""Continually generate names with `prefix`, e.g. '_1', '_2', ..."""
# pylint: disable=stop-iteration-return
spec_count = iter(spec_count(*args, **kwargs))
while True:
yield prefix + str(next(spec_count))
Expand Down Expand Up @@ -325,6 +328,7 @@ def deepgetattr(obj, attr, default=_NOTSET):


def color_to_hex(color):
# pylint: disable=consider-using-f-string
return "#{:02X}{:02X}{:02X}".format(*color)


Expand All @@ -337,9 +341,9 @@ def inherit_docstrings(cls):
for method in cls.__dict__.values():
if inspect.isfunction(method) and method.__doc__ is None:
for parent in cls.__mro__[1:]:
__doc__ = getattr(parent, method.__name__, None).__doc__
if __doc__:
method.__doc__ = __doc__
doc = getattr(parent, method.__name__, None).__doc__
if doc:
method.__doc__ = doc
break
return cls

Expand Down Expand Up @@ -379,7 +383,7 @@ def interleave(seq1, seq2):
def Reprable_repr_pretty(name, itemsiter, printer, cycle):
# type: (str, Iterable[Tuple[str, Any]], Ipython.lib.pretty.PrettyPrinter, bool) -> None
if cycle:
printer.text("{0}(...)".format("name"))
printer.text(f"{name}(...)")
else:
def printitem(field, value):
printer.text(field + "=")
Expand All @@ -392,9 +396,10 @@ def printsep():
itemsiter = (partial(printitem, *item) for item in itemsiter)
sepiter = repeat(printsep)

with printer.group(len(name) + 1, "{0}(".format(name), ")"):
with printer.group(len(name) + 1, f"{name}(", ")"):
for part in interleave(itemsiter, sepiter):
part()
part()


class _Undef:
Expand Down Expand Up @@ -458,6 +463,7 @@ def _reprable_fields(self):
param.kind not in (param.VAR_POSITIONAL, param.VAR_KEYWORD):
yield param.name, param.default

# pylint: disable=unused-argument
def _reprable_omit_param(self, name, default, value):
if default is value:
return True
Expand Down Expand Up @@ -503,9 +509,9 @@ def __repr__(self):
nameparts = (([str(module)] if module else []) +
[self.__class__.__name__])
name = ".".join(nameparts)
return "{}({})".format(
name, ", ".join("{}={!r}".format(f, v) for f, _, v in self._reprable_items())
)
items = ", ".join(f"{f}={repr(v)}"
for f, _, v in self._reprable_items())
return f"{name}({items})"


def wrap_callback(progress_callback, start=0, end=1):
Expand Down
Loading