diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 9b61cb1f5..000000000 --- a/.flake8 +++ /dev/null @@ -1,36 +0,0 @@ -[flake8] -# TODO tests should not be excluded (one day...) -include = param numbergen -exclude = - .venv, - jupyter_execute, - .git, - __pycache__, - .eggs, - *.egg, - doc, - dist, - build, - _build, - tests, - .ipynb_checkpoints -ignore = E114, - E116, - E126, - E128, - E129, - E2, - E3, - E4, - E5, - E731, - E701, - E702, - E703, - E704, - E722, - E741, - E742, - E743, - W503, - W504, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 147e37873..6ee48d11c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,21 +1,22 @@ default_stages: [pre-commit] repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 hooks: - - id: check-builtin-literals - - id: check-case-conflict - - id: check-docstring-first - - id: check-toml - - id: detect-private-key - - id: end-of-file-fixer + - id: check-builtin-literals + - id: check-case-conflict + - id: check-docstring-first + - id: check-toml + - id: detect-private-key + - id: end-of-file-fixer exclude: (\.min\.js$|\.svg$) - - id: trailing-whitespace -- repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 + - id: trailing-whitespace + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.7.3 hooks: - - id: flake8 -- repo: https://github.com/hoxbro/clean_notebook - rev: v0.1.10 - hooks: - - id: clean-notebook + - id: ruff + exclude: \.ipynb + - repo: https://github.com/hoxbro/clean_notebook + rev: v0.1.15 + hooks: + - id: clean-notebook diff --git a/numbergen/__init__.py b/numbergen/__init__.py index ee1dfbf74..29e916f29 100644 --- a/numbergen/__init__.py +++ b/numbergen/__init__.py @@ -14,7 +14,7 @@ import param -from param import __version__ # noqa: API import +from param import __version__ class TimeAware(param.Parameterized): """ @@ -763,5 +763,5 @@ def __call__(self): else: return val -_public = list({_k for _k,_v in locals().items() if isinstance(_v,type) and issubclass(_v,NumberGenerator)}) -__all__ = _public +_public = {_k for _k,_v in locals().items() if isinstance(_v,type) and issubclass(_v,NumberGenerator)} +__all__ = ["__version__", *_public] diff --git a/param/ipython.py b/param/ipython.py index 27414f664..baaceed1a 100644 --- a/param/ipython.py +++ b/param/ipython.py @@ -91,7 +91,7 @@ def param_docstrings(self, info, max_col_len=100, only_changed=False): contents = [] displayed_params = [] for name in self.sort_by_precedence(params): - if only_changed and not (name in changed): + if only_changed and name not in changed: continue displayed_params.append((name, params[name])) @@ -155,7 +155,7 @@ def _build_table(self, info, order, max_col_len=40, only_changed=False): ordering = self.sort_by_precedence(params) for name in ordering: p = params[name] - if only_changed and not (name in changed): + if only_changed and name not in changed: continue constant = 'C' if p.constant else 'V' diff --git a/param/parameterized.py b/param/parameterized.py index 2576659dc..f5ce4f5ca 100644 --- a/param/parameterized.py +++ b/param/parameterized.py @@ -23,12 +23,6 @@ import warnings from inspect import getfullargspec -# Allow this file to be used standalone if desired, albeit without JSON serialization -try: - from . import serializer -except ImportError: - serializer = None - from collections import defaultdict, namedtuple, OrderedDict from functools import partial, wraps, reduce from html import escape @@ -39,6 +33,7 @@ from contextlib import contextmanager from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL +from . import serializer from ._utils import ( DEFAULT_SIGNATURE, ParamDeprecationWarning as _ParamDeprecationWarning, @@ -67,7 +62,7 @@ try: from .ipython import ParamPager, ipython_async_executor as async_executor param_pager = ParamPager(metaclass=True) # Generates param description - except ImportError: + except ModuleNotFoundError: from ._utils import async_executor else: from ._utils import async_executor @@ -340,8 +335,6 @@ def edit_constant(parameterized): p.constant = False try: yield - except: - raise finally: for (p, const) in zip(params, constants): p.constant = const @@ -359,8 +352,6 @@ def discard_events(parameterized): list(parameterized.param._events)) try: yield - except: - raise finally: parameterized.param._BATCH_WATCH = batch_watch parameterized.param._state_watchers = watchers @@ -1311,8 +1302,6 @@ def deserialize(cls, value): return value def schema(self, safe=False, subset=None, mode='json'): - if serializer is None: - raise ImportError('Cannot import serializer.py needed to generate schema') if mode not in self._serializers: raise KeyError(f'Mode {mode!r} not in available serialization formats {list(self._serializers.keys())!r}') return self._serializers[mode].param_schema(self.__class__.__name__, self, @@ -1747,7 +1736,7 @@ def is_equal(cls, obj1, obj2): @classmethod def compare_iterator(cls, obj1, obj2): - if type(obj1) != type(obj2) or len(obj1) != len(obj2): + if type(obj1) is not type(obj2) or len(obj1) != len(obj2): return False for o1, o2 in zip(obj1, obj2): if not cls.is_equal(o1, o2): @@ -1756,7 +1745,7 @@ def compare_iterator(cls, obj1, obj2): @classmethod def compare_mapping(cls, obj1, obj2): - if type(obj1) != type(obj2) or len(obj1) != len(obj2): return False + if type(obj1) is not type(obj2) or len(obj1) != len(obj2): return False for k in obj1: if k in obj2: if not cls.is_equal(obj1[k], obj2[k]): @@ -2354,7 +2343,7 @@ def _update(self_, arg=Undefined, /, **kwargs): raise ValueError(f"{k!r} is not a parameter of {self_.cls.__name__}") try: setattr(self_or_cls, k, v) - except: + except Exception: self_._BATCH_WATCH = False raise @@ -2386,7 +2375,7 @@ def set_param(self_, *args,**kwargs): """ self_or_cls = self_.self_or_cls if args: - if len(args) == 2 and not args[0] in kwargs and not kwargs: + if len(args) == 2 and args[0] not in kwargs and not kwargs: kwargs[args[0]] = args[1] else: raise ValueError("Invalid positional arguments for %s.set_param" % @@ -3388,7 +3377,7 @@ def __set_name(mcs, name, dict_): """ name_param = dict_.get("name", None) if name_param is not None: - if not type(name_param) is String: + if type(name_param) is not String: raise TypeError( f"Parameterized class {name!r} cannot override " f"the 'name' Parameter with type {type(name_param)}. " diff --git a/param/parameters.py b/param/parameters.py index 61f82d8a0..81a0a8c69 100644 --- a/param/parameters.py +++ b/param/parameters.py @@ -1404,7 +1404,7 @@ def serialize(cls, value): if not isinstance(v, (dt.datetime, dt.date)): # i.e np.datetime64 v = v.astype(dt.datetime) # Separate date and datetime to deserialize to the right type. - if type(v) == dt.date: + if type(v) is dt.date: v = v.strftime("%Y-%m-%d") else: v = v.strftime("%Y-%m-%dT%H:%M:%S.%f") @@ -1958,7 +1958,7 @@ def _ensure_value_is_in_objects(self, val): Subclasses can override if they support multiple items on a list, to check each item instead. """ - if not (val in self.objects): + if val not in self.objects: self._objects.append(val) def get_range(self): diff --git a/param/serializer.py b/param/serializer.py index ab094ba51..56fb2a52c 100644 --- a/param/serializer.py +++ b/param/serializer.py @@ -252,7 +252,7 @@ def objectselector_schema(cls, p, safe=False): schema = {'anyOf': allowed_types} schema['enum'] = p.objects return schema - except: + except Exception: if safe is True: msg = ('ObjectSelector cannot be guaranteed to be safe for ' 'serialization due to unserializable type in objects') @@ -267,7 +267,7 @@ def selector_schema(cls, p, safe=False): schema = {'anyOf': allowed_types} schema['enum'] = p.objects return schema - except: + except Exception: if safe is True: msg = ('Selector cannot be guaranteed to be safe for ' 'serialization due to unserializable type in objects') diff --git a/param/version.py b/param/version.py index ed8637406..a54eeb9c7 100644 --- a/param/version.py +++ b/param/version.py @@ -18,7 +18,9 @@ __author__ = 'Jean-Luc Stevens' -import os, subprocess, json +import os +import subprocess +import json def run_cmd(args, cwd=None): kwargs = {} @@ -192,7 +194,7 @@ def git_fetch(self, cmd='git', as_string=False): output = self._output_from_file() if output is not None: self._update_from_vcs(output) - except: pass + except Exception: pass if output is None: # glob pattern (not regexp) matching vX.Y.Z* tags output = run_cmd([cmd, 'describe', '--long', '--match', @@ -251,7 +253,7 @@ def _output_from_file(self, entry='git_describe'): vfile = os.path.join(os.path.dirname(self.fpath), '.version') with open(vfile) as f: return json.loads(f.read()).get(entry, None) - except: # File may be missing if using pip + git archive + except Exception: # File may be missing if using pip + git archive return None @@ -427,7 +429,7 @@ def setup_version(cls, setup_path, reponame, archive_commit=None, if git_describe is not None: info['git_describe'] = git_describe - except: pass + except Exception: pass if git_describe is None: extracted_directory_tag = Version.extract_directory_tag(setup_path, reponame) @@ -436,7 +438,7 @@ def setup_version(cls, setup_path, reponame, archive_commit=None, try: with open(os.path.join(setup_path, pkgname, '.version'), 'w') as f: f.write(json.dumps({'extracted_directory_tag':extracted_directory_tag})) - except: + except Exception: print('Error in setup_version: could not write .version file.') @@ -449,7 +451,7 @@ def setup_version(cls, setup_path, reponame, archive_commit=None, try: with open(os.path.join(setup_path, pkgname, '.version'), 'w') as f: f.write(json.dumps(info)) - except: + except Exception: print('Error in setup_version: could not write .version file.') return info['version_string'] @@ -515,10 +517,7 @@ def get_setupcfg_version(): parse setup.cfg...but then git export-subst would not work. """ - try: - import configparser - except ImportError: - import ConfigParser as configparser # python2 (also prevents dict-like access) + import configparser import re cfg = "setup.cfg" autover_section = 'tool:autover' diff --git a/pyproject.toml b/pyproject.toml index cc4dababe..d02fe0f23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -113,3 +113,19 @@ asyncio_default_fixture_loop_scope="function" [tool.coverage.report] omit = ["param/version.py"] + +[tool.ruff] +fix = true +include = ["*.py"] + +[tool.ruff.lint] +select = ["W", "E", "F"] +ignore = [ + "E402", # Module level import not at top of file + "E501", # Line too long + "E701", # Multiple statements on one line + "E712", # Comparison to true should be is + "E731", # Do not assign a lambda expression, use a def + "E741", # Ambiguous variable name + "F405", # From star imports +] diff --git a/tests/testclassselector.py b/tests/testclassselector.py index 93416735d..6b1fe4a36 100644 --- a/tests/testclassselector.py +++ b/tests/testclassselector.py @@ -110,7 +110,7 @@ def _check_defaults(self, p): assert p.allow_None is True assert p.instantiate is True assert p.is_instance is True - assert p.class_ == dict + assert p.class_ is dict def test_defaults_class(self): class P(param.Parameterized): diff --git a/tests/testcomparator.py b/tests/testcomparator.py index 92d4e90fe..20faf522a 100644 --- a/tests/testcomparator.py +++ b/tests/testcomparator.py @@ -7,11 +7,11 @@ try: import numpy as np -except ImportError: +except ModuleNotFoundError: np = None try: import pandas as pd -except ImportError: +except ModuleNotFoundError: pd = None _now = datetime.datetime.now() diff --git a/tests/testdateparam.py b/tests/testdateparam.py index 2e4d92660..e0ca5e368 100644 --- a/tests/testdateparam.py +++ b/tests/testdateparam.py @@ -11,7 +11,7 @@ try: import numpy as np -except: +except ModuleNotFoundError: np = None from .utils import check_defaults diff --git a/tests/testdaterangeparam.py b/tests/testdaterangeparam.py index b0c90c029..6fb43a5eb 100644 --- a/tests/testdaterangeparam.py +++ b/tests/testdaterangeparam.py @@ -12,7 +12,7 @@ try: import numpy as np -except: +except ModuleNotFoundError: np = None # Assuming tests of range parameter cover most of what's needed to diff --git a/tests/testdefaults.py b/tests/testdefaults.py index c9bde14cc..8cad01040 100644 --- a/tests/testdefaults.py +++ b/tests/testdefaults.py @@ -23,11 +23,11 @@ try: import numpy # noqa -except ImportError: +except ModuleNotFoundError: skip.append('Array') try: import pandas # noqa -except ImportError: +except ModuleNotFoundError: skip.append('DataFrame') skip.append('Series') @@ -61,7 +61,7 @@ class P(param.Parameterized): for slot in param.parameterized.get_all_slots(parameter): # Handled in a special way, skip it - if type(parameter) == param.Composite and slot == 'objtype': + if type(parameter) is param.Composite and slot == 'objtype': continue assert getattr(P.param.p, slot) is not param.Undefined # Handled in a special way, skip it @@ -82,7 +82,7 @@ class P(param.Parameterized): for slot in param.parameterized.get_all_slots(parameter): # Handled in a special way, skip it - if type(parameter) == param.Composite and slot == 'objtype': + if type(parameter) is param.Composite and slot == 'objtype': continue assert getattr(inst.param.p, slot) is not param.Undefined # Handled in a special way, skip it diff --git a/tests/testfiledeserialization.py b/tests/testfiledeserialization.py index 5dda91ba3..39ededc0f 100644 --- a/tests/testfiledeserialization.py +++ b/tests/testfiledeserialization.py @@ -12,7 +12,7 @@ try: import numpy as np ndarray = np.array([[1,2,3],[4,5,6]]) -except: +except ModuleNotFoundError: np = ndarray = None try: @@ -20,19 +20,19 @@ pd_ver = pd.__version__.split('.') df = pd.DataFrame({'A':[1,2,3], 'B':[1.1,2.2,3.3]}) modern_pd = pd if (int(pd_ver[0]) >= 1 and int(pd_ver[1]) >= 2) else None -except: +except ModuleNotFoundError: pd = df1 = df2 = modern_pd = None # The writer could be xlsxwriter, but the sufficient condition is the presence of # openpyxl try: import openpyxl as xlsxm -except: +except ModuleNotFoundError: xlsxm = None try: import odf as ods -except: +except ModuleNotFoundError: ods = None # prior to pandas version 1.2, xlrd was always the default excel reader (though it @@ -42,27 +42,27 @@ import xlrd as xls if int(xls.__version__.split('.')[0]) > 2: raise Exception() -except: +except ModuleNotFoundError: if modern_pd is None: xlsxm = None try: import pyarrow.feather as feather -except: +except ModuleNotFoundError: feather = None try: import fastparquet as parquet -except: +except ModuleNotFoundError: parquet = None try: import pyarrow as parquet -except: +except ModuleNotFoundError: pass try: import tables as hdf5 -except: +except ModuleNotFoundError: hdf5 = None np_skip = skipIf(np is None, "NumPy is not available") diff --git a/tests/testipythonmagic.py b/tests/testipythonmagic.py index 2a1e68dab..9c9a2e4fd 100644 --- a/tests/testipythonmagic.py +++ b/tests/testipythonmagic.py @@ -9,7 +9,7 @@ try: import IPython # noqa -except ImportError: +except ModuleNotFoundError: import os if os.getenv('PARAM_TEST_IPYTHON','0') == '1': raise ImportError("PARAM_TEST_IPYTHON=1 but ipython not available.") diff --git a/tests/testjsonserialization.py b/tests/testjsonserialization.py index f83c025f9..e11dc63c1 100644 --- a/tests/testjsonserialization.py +++ b/tests/testjsonserialization.py @@ -11,7 +11,7 @@ try: from jsonschema import validate, ValidationError -except ImportError: +except ModuleNotFoundError: import os if os.getenv('PARAM_TEST_JSONSCHEMA','0') == '1': raise ImportError("PARAM_TEST_JSONSCHEMA=1 but jsonschema not available.") @@ -25,7 +25,7 @@ ndarray = np.array([[1,2,3],[4,5,6]]) npdt1 = np.datetime64(now) npdt2 = np.datetime64(after_now) -except: +except ModuleNotFoundError: np, ndarray, npdt1, npdt2 = None, None, None, None np_skip = skipIf(np is None, "NumPy is not available") @@ -36,7 +36,7 @@ df2 = pd.DataFrame({'A':[1.1,2.2,3.3], 'B':[1.1,2.2,3.3]}) pdts1 = pd.Timestamp(now) pdts2 = pd.Timestamp(after_now) -except: +except ModuleNotFoundError: pd, df1, df2, pdts1, pdts2 = None, None, None, None, None pd_skip = skipIf(pd is None, "pandas is not available") diff --git a/tests/testlist.py b/tests/testlist.py index 8f7e64387..4a6405e55 100644 --- a/tests/testlist.py +++ b/tests/testlist.py @@ -166,14 +166,14 @@ class B(A): assert B.param.p.default == [0] assert B.param.p.instantiate is True assert B.param.p.bounds == (0, None) - assert B.param.p.item_type == int + assert B.param.p.item_type is int b = B() assert b.param.p.default == [0] assert b.param.p.instantiate is True assert b.param.p.bounds == (0, None) - assert b.param.p.item_type == int + assert b.param.p.item_type is int def test_inheritance_behavior5(self): class A(param.Parameterized): diff --git a/tests/testnumberparameter.py b/tests/testnumberparameter.py index a796d2e7d..56f2d34c8 100644 --- a/tests/testnumberparameter.py +++ b/tests/testnumberparameter.py @@ -10,7 +10,7 @@ try: import numpy as np -except ImportError: +except ModuleNotFoundError: np = None class TestNumberParameters(unittest.TestCase): diff --git a/tests/testnumpy.py b/tests/testnumpy.py index 6a9bc8033..3bc47d256 100644 --- a/tests/testnumpy.py +++ b/tests/testnumpy.py @@ -11,7 +11,7 @@ try: import numpy import numpy.testing -except ImportError: +except ModuleNotFoundError: if os.getenv('PARAM_TEST_NUMPY','0') == '1': raise ImportError("PARAM_TEST_NUMPY=1 but numpy not available.") else: @@ -19,7 +19,7 @@ def _is_array_and_equal(test, ref): - if not type(test) == numpy.ndarray: + if type(test) is not numpy.ndarray: raise AssertionError numpy.testing.assert_array_equal(test,ref) diff --git a/tests/testpandas.py b/tests/testpandas.py index 38134e22b..6227cded2 100644 --- a/tests/testpandas.py +++ b/tests/testpandas.py @@ -12,7 +12,7 @@ try: import pandas -except ImportError: +except ModuleNotFoundError: if os.getenv('PARAM_TEST_PANDAS','0') == '1': raise ImportError("PARAM_TEST_PANDAS=1 but pandas not available.") else: diff --git a/tests/testpickle.py b/tests/testpickle.py index 1bb16a8eb..cafe0d767 100644 --- a/tests/testpickle.py +++ b/tests/testpickle.py @@ -5,15 +5,15 @@ try: import cloudpickle -except ImportError: +except ModuleNotFoundError: cloudpickle = None try: import numpy as np -except: +except ModuleNotFoundError: np = None try: import pandas as pd -except: +except ModuleNotFoundError: pd = None diff --git a/tests/testreactive.py b/tests/testreactive.py index 68e6b9ee3..c4fff0f16 100644 --- a/tests/testreactive.py +++ b/tests/testreactive.py @@ -7,7 +7,7 @@ try: import numpy as np -except ImportError: +except ModuleNotFoundError: if os.getenv('PARAM_TEST_NUMPY','0') == '1': raise ImportError("PARAM_TEST_NUMPY=1 but numpy not available.") else: @@ -15,7 +15,7 @@ try: import pandas as pd -except ImportError: +except ModuleNotFoundError: if os.getenv('PARAM_TEST_PANDAS','0') == '1': raise ImportError("PARAM_TEST_PANDAS=1 but pandas not available.") else: diff --git a/tests/testtimedependent.py b/tests/testtimedependent.py index 40d32d867..85cdeadd2 100644 --- a/tests/testtimedependent.py +++ b/tests/testtimedependent.py @@ -13,7 +13,7 @@ try: import gmpy -except ImportError: +except ModuleNotFoundError: import os if os.getenv('PARAM_TEST_GMPY','0') == '1': raise ImportError("PARAM_TEST_GMPY=1 but gmpy not available.") @@ -57,7 +57,8 @@ def test_time_int_until(self): def test_time_int_eq(self): t = param.Time(time_type=int) s = param.Time(time_type=int) - t(3); s(3) + t(3) + s(3) self.assertEqual(t == s, True) def test_time_int_context(self): diff --git a/tests/testtupleparam.py b/tests/testtupleparam.py index 542d3fc92..15f2905b2 100644 --- a/tests/testtupleparam.py +++ b/tests/testtupleparam.py @@ -8,7 +8,7 @@ try: import numpy as np -except: +except ModuleNotFoundError: np = None diff --git a/tests/testutils.py b/tests/testutils.py index 7fd54b99c..d060a314d 100644 --- a/tests/testutils.py +++ b/tests/testutils.py @@ -14,12 +14,12 @@ try: import numpy as np -except ImportError: +except ModuleNotFoundError: np = None try: import pandas as pd -except ImportError: +except ModuleNotFoundError: pd = None now = dt.datetime.now() @@ -60,7 +60,7 @@ def test_guess_param_types(val, p): assert 'key' in output out_param = output['key'] assert isinstance(out_param, p) - if not type(out_param) == param.Parameter: + if type(out_param) is not param.Parameter: assert out_param.default is val assert out_param.constant