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

DM-40151: Fix bug in ImportTestCase #166

Merged
merged 11 commits into from
Jul 24, 2023
1 change: 0 additions & 1 deletion .github/workflows/rebase_checker.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
---
name: Check that 'main' is not merged into the development branch

on: pull_request
Expand Down
6 changes: 3 additions & 3 deletions python/lsst/utils/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

import functools
from collections.abc import Callable
from typing import Any, Type, TypeVar
from typing import Any, ClassVar, Type, TypeVar


class Singleton(type):
Expand All @@ -33,7 +33,7 @@ class Singleton(type):
adjust state of the singleton.
"""

_instances: dict[type, Any] = {}
_instances: ClassVar[dict[type, Any]] = {}

# Signature is intentionally not substitutable for type.__call__ (no *args,
# **kwargs) to require classes that use this metaclass to have no
Expand Down Expand Up @@ -87,7 +87,7 @@ def __getstate__(self: _T) -> dict: # noqa: N807
# Disable default state-setting when unpickled.
return {}

cls.__getstate__ = __getstate__
cls.__getstate__ = __getstate__ # type: ignore[assignment]

def __setstate__(self: _T, state: Any) -> None: # noqa: N807
# Disable default state-setting when copied.
Expand Down
15 changes: 10 additions & 5 deletions python/lsst/utils/packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from __future__ import annotations

import contextlib
import hashlib
import importlib
import io
Expand All @@ -27,7 +28,7 @@
import types
from collections.abc import Mapping
from functools import lru_cache
from typing import Any
from typing import Any, ClassVar

import yaml

Expand Down Expand Up @@ -115,10 +116,9 @@ def getPythonPackages() -> dict[str, str]:
"""
# Attempt to import libraries that only report their version in python
for module_name in PYTHON:
try:
# If it's not available we continue.
with contextlib.suppress(Exception):
importlib.import_module(module_name)
except Exception:
pass # It's not available, so don't care

packages = {"python": sys.version}

Expand Down Expand Up @@ -410,7 +410,12 @@ class Packages(dict):
This is a wrapper around a dict with some convenience methods.
"""

formats = {".pkl": "pickle", ".pickle": "pickle", ".yaml": "yaml", ".json": "json"}
formats: ClassVar[dict[str, str]] = {
".pkl": "pickle",
".pickle": "pickle",
".yaml": "yaml",
".json": "json",
}

def __setstate__(self, state: dict[str, Any]) -> None:
# This only seems to be called for old pickle files where
Expand Down
12 changes: 5 additions & 7 deletions python/lsst/utils/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import contextlib
import functools
import gc
import importlib.resources as resources
import inspect
import itertools
import os
Expand All @@ -44,6 +43,7 @@
import unittest
import warnings
from collections.abc import Callable, Iterable, Iterator, Mapping, Sequence
from importlib import resources
from typing import Any, ClassVar

import numpy
Expand Down Expand Up @@ -129,7 +129,7 @@
class MemoryTestCase(unittest.TestCase):
"""Check for resource leaks."""

ignore_regexps: list[str] = []
ignore_regexps: ClassVar[list[str]] = []
"""List of regexps to ignore when checking for open files."""

@classmethod
Expand Down Expand Up @@ -367,7 +367,7 @@
for mod in cls.PACKAGES:
test_name = "test_import_" + mod.replace(".", "_")

def test_import(*args: Any) -> None:
def test_import(*args: Any, mod=mod) -> None:
self = args[0]
self.assertImport(mod)

Expand All @@ -378,7 +378,7 @@
# If there are no packages listed that is likely a mistake and
# so register a failing test.
if cls._n_registered == 0:
setattr(cls, "test_no_packages_registered", cls._test_no_packages_registered_for_import_testing)
cls.test_no_packages_registered = cls._test_no_packages_registered_for_import_testing

Check warning on line 381 in python/lsst/utils/tests.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/utils/tests.py#L381

Added line #L381 was not covered by tests

def assertImport(self, root_pkg):
for file in resources.files(root_pkg).iterdir():
Expand Down Expand Up @@ -473,10 +473,8 @@
# Use stacklevel 3 so that the warning is reported from the end of the
# with block
warnings.warn(f"Unexpectedly found pre-existing tempfile named {outPath!r}", stacklevel=3)
try:
with contextlib.suppress(OSError):

Check warning on line 476 in python/lsst/utils/tests.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/utils/tests.py#L476

Added line #L476 was not covered by tests
os.remove(outPath)
except OSError:
pass

yield outPath

Expand Down
17 changes: 7 additions & 10 deletions python/lsst/utils/timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import logging
import time
from collections.abc import Callable, Collection, Iterable, Iterator, MutableMapping
from contextlib import contextmanager
from contextlib import contextmanager, suppress
from typing import TYPE_CHECKING, Any

from astropy import units as u
Expand Down Expand Up @@ -109,15 +109,13 @@ def logPairs(
"""
if obj is not None:
if metadata is None:
try:
with suppress(AttributeError):
metadata = obj.metadata
except AttributeError:
pass

if logger is None:
try:
with suppress(AttributeError):
logger = obj.log
except AttributeError:
pass

strList = []
for name, value in pairs:
if metadata is not None:
Expand Down Expand Up @@ -197,10 +195,9 @@ def logInfo(
* Version 1: ``MaxResidentSetSize`` will be stored in bytes.
"""
if metadata is None and obj is not None:
try:
with suppress(AttributeError):
metadata = obj.metadata
except AttributeError:
pass

if metadata is not None:
# Log messages already have timestamps.
utcStr = datetime.datetime.utcnow().isoformat()
Expand Down
14 changes: 3 additions & 11 deletions python/lsst/utils/wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,20 +307,14 @@ def __subclasscheck__(cls, subclass):
# any registered type or true subclass thereof.
if subclass in cls._registry.values():
return True
for v in cls._registry.values():
if issubclass(subclass, v):
return True
return False
return any(issubclass(subclass, v) for v in cls._registry.values())

def __instancecheck__(cls, instance):
# Special method hook for the isinstance built-in: we return true for
# an instance of any registered type or true subclass thereof.
if type(instance) in cls._registry.values():
return True
for v in cls._registry.values():
if isinstance(instance, v):
return True
return False
return any(isinstance(instance, v) for v in cls._registry.values())

def __subclasses__(cls):
"""Return a tuple of all classes that inherit from this class."""
Expand Down Expand Up @@ -396,9 +390,7 @@ def setattrSafe(name, value):
setattrSafe(p, k)
else:
raise ValueError(
"key must have {} elements (one for each of {})".format(
len(cls.TEMPLATE_PARAMS), cls.TEMPLATE_PARAMS
)
f"key must have {len(cls.TEMPLATE_PARAMS)} elements (one for each of {cls.TEMPLATE_PARAMS})"
)

for name, attr in cls._inherited.items():
Expand Down
10 changes: 4 additions & 6 deletions tests/test_getTempFilePath.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ def testBasics(self):
# Path will have unique component so do not test full equality
self.assertIn("test_getTempFilePath_testBasics", tmpFile)
self.assertTrue(tmpFile.endswith(".txt"))
f = open(tmpFile, "w")
f.write("foo\n")
f.close()
with open(tmpFile, "w") as f:
f.write("foo\n")
self.assertFalse(os.path.exists(tmpFile))

def testMultipleCallDepth(self):
Expand All @@ -53,9 +52,8 @@ def runGetTempFile(self, funcName):
# Path will have unique component so do not test full equality
self.assertIn(f"test_getTempFilePath_{funcName}", tmpFile)
self.assertTrue(tmpFile.endswith(".fits"))
f = open(tmpFile, "w")
f.write("foo\n")
f.close()
with open(tmpFile, "w") as f:
f.write("foo\n")
self.assertFalse(os.path.exists(tmpFile))

def runLevel2(self, funcName):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ def test_profile(self):
self.assertEqual(len(cm.output), 2)
self.assertIsNotNone(prof)
self.assertTrue(os.path.exists(tmp.name))
self.assertIsInstance(pstats.Stats(tmp.name), pstats.Stats),
self.assertIsInstance(pstats.Stats(tmp.name), pstats.Stats)


if __name__ == "__main__":
Expand Down