summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott <[email protected]>2022-01-11 13:33:24 -0800
committerScott <[email protected]>2022-01-11 13:33:24 -0800
commitb454ad295eb3024a238d32bf2aef1ebc3c496b44 (patch)
tree89368a7cdad571da9e2c7297190fdce48eb3feb9
parentd2478310649d51e14f8ece57651ca9d925d98793 (diff)
Start using warnings from stdlib.
-rw-r--r--argparse_utils.py12
-rw-r--r--arper.py7
-rwxr-xr-xbase_presence.py15
-rw-r--r--camera_utils.py18
-rw-r--r--decorator_utils.py4
-rw-r--r--executors.py27
-rw-r--r--file_utils.py1
-rw-r--r--google_assistant.py5
-rw-r--r--lockfile.py20
-rw-r--r--logging_utils.py7
-rw-r--r--ml/model_trainer.py5
-rw-r--r--ml/quick_label.py7
-rw-r--r--persistent.py5
-rwxr-xr-xremote_worker.py3
-rw-r--r--smart_home/lights.py13
-rw-r--r--smart_home/outlets.py13
-rw-r--r--smart_home/registry.py13
-rw-r--r--string_utils.py22
-rw-r--r--unittest_utils.py5
19 files changed, 130 insertions, 72 deletions
diff --git a/argparse_utils.py b/argparse_utils.py
index e8c2f56..1c61b24 100644
--- a/argparse_utils.py
+++ b/argparse_utils.py
@@ -114,7 +114,7 @@ def valid_ip(ip: str) -> str:
if s is not None:
return s
msg = f"{ip} is an invalid IP address"
- logger.warning(msg)
+ logger.error(msg)
raise argparse.ArgumentTypeError(msg)
@@ -140,7 +140,7 @@ def valid_mac(mac: str) -> str:
if s is not None:
return s
msg = f"{mac} is an invalid MAC address"
- logger.warning(msg)
+ logger.error(msg)
raise argparse.ArgumentTypeError(msg)
@@ -166,7 +166,7 @@ def valid_percentage(num: str) -> float:
if 0.0 <= n <= 100.0:
return n
msg = f"{num} is an invalid percentage; expected 0 <= n <= 100.0"
- logger.warning(msg)
+ logger.error(msg)
raise argparse.ArgumentTypeError(msg)
@@ -188,7 +188,7 @@ def valid_filename(filename: str) -> str:
if os.path.exists(s):
return s
msg = f"{filename} was not found and is therefore invalid."
- logger.warning(msg)
+ logger.error(msg)
raise argparse.ArgumentTypeError(msg)
@@ -210,7 +210,7 @@ def valid_date(txt: str) -> datetime.date:
if date is not None:
return date
msg = f'Cannot parse argument as a date: {txt}'
- logger.warning(msg)
+ logger.error(msg)
raise argparse.ArgumentTypeError(msg)
@@ -232,7 +232,7 @@ def valid_datetime(txt: str) -> datetime.datetime:
if dt is not None:
return dt
msg = f'Cannot parse argument as datetime: {txt}'
- logger.warning(msg)
+ logger.error(msg)
raise argparse.ArgumentTypeError(msg)
diff --git a/arper.py b/arper.py
index c187023..c0cc9f0 100644
--- a/arper.py
+++ b/arper.py
@@ -6,6 +6,7 @@ import datetime
import logging
import os
from typing import Any, Optional
+import warnings
from overrides import overrides
@@ -126,9 +127,9 @@ class Arper(persistent.Persistent):
if len(cached_state) > config.config['arper_min_entries_to_be_valid']:
return cls(cached_state)
else:
- logger.warning(
- f'{cache_file} sucks, only {len(cached_state)} entries. Deleting it.'
- )
+ msg = f'{cache_file} is invalid: only {len(cached_state)} entries. Deleting it.'
+ warnings.warn(msg)
+ logger.warning(msg)
os.remove(cache_file)
logger.debug('No usable saved state found')
return None
diff --git a/base_presence.py b/base_presence.py
index eff613b..b38b436 100755
--- a/base_presence.py
+++ b/base_presence.py
@@ -5,6 +5,7 @@ from collections import defaultdict
import logging
import re
from typing import Dict, List, Set
+import warnings
# Note: this module is fairly early loaded. Be aware of dependencies.
import argparse_utils
@@ -113,7 +114,9 @@ class PresenceDetection(object):
self.parse_raw_macs_file(raw, Location.CABIN)
except Exception as e:
logger.exception(e)
- logger.warning("Can't see the cabin right now; presence detection impared.")
+ msg = "Can't see the cabin right now; presence detection impared."
+ logger.warning(msg)
+ warnings.warn(msg)
self.dark_locations.add(Location.CABIN)
def update_from_cabin(self) -> None:
@@ -131,7 +134,9 @@ class PresenceDetection(object):
self.parse_raw_macs_file(raw, Location.HOUSE)
except Exception as e:
logger.exception(e)
- logger.warning("Can't see the house right now; presence detection impared.")
+ msg = "Can't see the house right now; presence detection impared."
+ logger.warning(msg)
+ warnings.warn(msg)
self.dark_locations.add(Location.HOUSE)
def read_persisted_macs_file(
@@ -193,9 +198,9 @@ class PresenceDetection(object):
def where_is_person_now(self, name: Person) -> Location:
self.maybe_update()
if len(self.dark_locations) > 0:
- logger.warning(
- f"Can't see {self.dark_locations} right now; answer confidence impacted"
- )
+ msg = f"Can't see {self.dark_locations} right now; answer confidence impacted"
+ logger.warning(msg)
+ warnings.warn(msg)
logger.debug(f'Looking for {name}...')
if name is Person.UNKNOWN:
diff --git a/camera_utils.py b/camera_utils.py
index acf760d..e69eddb 100644
--- a/camera_utils.py
+++ b/camera_utils.py
@@ -6,6 +6,7 @@ import logging
import platform
import subprocess
from typing import NamedTuple, Optional
+import warnings
import cv2 # type: ignore
import numpy as np
@@ -81,7 +82,9 @@ def fetch_camera_image_from_video_server(
return raw
except Exception as e:
logger.exception(e)
- logger.warning(f"Got a bad image or HTTP error from {url}")
+ msg = f"Got a bad image or HTTP error from {url}; returning None."
+ logger.warning(msg)
+ warnings.warn(msg)
return None
@@ -106,6 +109,7 @@ def fetch_camera_image_from_rtsp_stream(
) -> Optional[bytes]:
"""Fetch the raw webcam image straight from the webcam's RTSP stream."""
hostname = blue_iris_camera_name_to_hostname(camera_name)
+ stream = f"rtsp://camera:IaLaIok@{hostname}:554/live"
try:
cmd = [
"/usr/bin/timeout",
@@ -114,7 +118,7 @@ def fetch_camera_image_from_rtsp_stream(
"/usr/local/bin/ffmpeg",
"-y",
"-i",
- f"rtsp://camera:IaLaIok@{hostname}:554/live",
+ f"{stream}",
"-f",
"singlejpeg",
"-vframes",
@@ -130,7 +134,9 @@ def fetch_camera_image_from_rtsp_stream(
return out
except Exception as e:
logger.exception(e)
- logger.warning("Failed to retrieve image from RTSP stream")
+ msg = "Failed to retrieve image via RTSP {stream}, returning None."
+ warnings.warn(msg)
+ logger.warning(msg)
return None
@@ -157,9 +163,9 @@ def _fetch_camera_image(
jpg=jpg,
hsv=hsv,
)
- logger.warning(
- "Failed to retieve image from both video server and direct RTSP stream"
- )
+ msg = "Failed to retieve image from both video server and direct RTSP stream"
+ logger.warning(msg)
+ warnings.warn(msg)
return RawJpgHsv(None, None, None)
diff --git a/decorator_utils.py b/decorator_utils.py
index d5349cc..07ad881 100644
--- a/decorator_utils.py
+++ b/decorator_utils.py
@@ -479,7 +479,7 @@ def deprecated(func):
def wrapper_deprecated(*args, **kwargs):
msg = f"Call to deprecated function {func.__qualname__}"
logger.warning(msg)
- warnings.warn(msg, category=DeprecationWarning)
+ warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
print(msg, file=sys.stderr)
return func(*args, **kwargs)
return wrapper_deprecated
@@ -507,8 +507,8 @@ def thunkify(func):
exc[0] = True
exc[1] = sys.exc_info() # (type, value, traceback)
msg = f"Thunkify has thrown an exception (will be raised on thunk()):\n{traceback.format_exc()}"
- print(msg)
logger.warning(msg)
+ warnings.warn(msg)
finally:
wait_event.set()
diff --git a/executors.py b/executors.py
index cdbb811..dabddf3 100644
--- a/executors.py
+++ b/executors.py
@@ -15,6 +15,7 @@ import subprocess
import threading
import time
from typing import Any, Callable, Dict, List, Optional, Set
+import warnings
import cloudpickle # type: ignore
from overrides import overrides
@@ -490,7 +491,9 @@ class WeightedRandomRemoteWorkerSelectionPolicy(RemoteWorkerSelectionPolicy):
worker.count -= 1
logger.debug(f'Selected worker {worker}')
return worker
- logger.warning("Couldn't find a worker; go fish.")
+ msg = 'Unexpectedly could not find a worker, retrying...'
+ logger.warning(msg)
+ warnings.warn(msg)
return None
@@ -525,7 +528,9 @@ class RoundRobinRemoteWorkerSelectionPolicy(RemoteWorkerSelectionPolicy):
if x >= len(self.workers):
x = 0
if x == self.index:
- logger.warning("Couldn't find a worker; go fish.")
+ msg = 'Unexpectedly could not find a worker, retrying...'
+ logger.warning(msg)
+ warnings.warn(msg)
return None
@@ -768,11 +773,11 @@ class RemoteExecutor(BaseExecutor):
# There's a race condition where someone else
# already finished the work and removed the source
# code file before we could copy it. No biggie.
- logger.warning(
- f'{bundle}: Failed to send instructions to the worker machine... ' +
+ msg = f'{bundle}: Failed to send instructions to the worker machine... ' +
'We\'re a backup and this may be caused by the original (or some ' +
'other backup) already finishing this work. Ignoring this.'
- )
+ logger.warning(msg)
+ warnings.warn(msg)
return None
# Kick off the work. Note that if this fails we let
@@ -844,9 +849,9 @@ class RemoteExecutor(BaseExecutor):
logger.exception(e)
logger.error(f'{bundle}: Something unexpected just happened...')
if p is not None:
- logger.warning(
- f"{bundle}: Failed to wrap up \"done\" bundle, re-waiting on active ssh."
- )
+ msg = f"{bundle}: Failed to wrap up \"done\" bundle, re-waiting on active ssh."
+ logger.warning(msg)
+ warnings.warn(msg)
return self.wait_for_process(p, bundle, depth + 1)
else:
self.status.record_release_worker(
@@ -1059,9 +1064,9 @@ class RemoteExecutor(BaseExecutor):
logger.error(f'{bundle}: At least it\'s only a backup; better luck with the others.')
return None
else:
- logger.warning(
- f'>>> Emergency rescheduling {bundle} because of unexected errors (wtf?!) <<<'
- )
+ msg = f'>>> Emergency rescheduling {bundle} because of unexected errors (wtf?!) <<<'
+ logger.warning(msg)
+ warnings.warn(msg)
return self.launch(bundle, avoid_last_machine)
@overrides
diff --git a/file_utils.py b/file_utils.py
index 67e6f56..176b0da 100644
--- a/file_utils.py
+++ b/file_utils.py
@@ -20,6 +20,7 @@ logger = logging.getLogger(__name__)
# os.remove(file) you fuckwit.
+# os.path.basename too.
def create_path_if_not_exist(path, on_error=None):
diff --git a/google_assistant.py b/google_assistant.py
index 572b4cc..041648c 100644
--- a/google_assistant.py
+++ b/google_assistant.py
@@ -3,6 +3,7 @@
import logging
from typing import NamedTuple
import sys
+import warnings
import requests
import speech_recognition as sr # type: ignore
@@ -93,7 +94,9 @@ def ask_google(cmd: str, *, recognize_speech=True) -> GoogleResponse:
logger.debug(f"Transcription: '{audio_transcription}'")
except sr.UnknownValueError as e:
logger.exception(e)
- logger.warning('Unable to parse Google assistant\'s response.')
+ msg = 'Unable to parse Google assistant\'s response.'
+ logger.warning(msg)
+ warnings.warn(msg)
audio_transcription = None
return GoogleResponse(
success=success,
diff --git a/lockfile.py b/lockfile.py
index d275f40..7f10cc1 100644
--- a/lockfile.py
+++ b/lockfile.py
@@ -8,6 +8,7 @@ import os
import signal
import sys
from typing import Optional
+import warnings
import config
import datetime_utils
@@ -87,7 +88,9 @@ class LockFile(object):
return True
except OSError:
pass
- logger.warning(f'Could not acquire {self.lockfile}.')
+ msg = f'Could not acquire {self.lockfile}.'
+ logger.warning(msg)
+ warnings.warn(msg)
return False
def acquire_with_retries(
@@ -124,6 +127,7 @@ class LockFile(object):
return self
msg = f"Couldn't acquire {self.lockfile}; giving up."
logger.warning(msg)
+ warnings.warn(msg)
raise LockFileException(msg)
def __exit__(self, type, value, traceback):
@@ -132,7 +136,9 @@ class LockFile(object):
duration = ts - self.locktime
if duration >= config.config['lockfile_held_duration_warning_threshold_sec']:
str_duration = datetime_utils.describe_duration_briefly(duration)
- logger.warning(f'Held {self.lockfile} for {str_duration}')
+ msg = f'Held {self.lockfile} for {str_duration}'
+ logger.warning(msg)
+ warnings.warn(msg, stacklevel=2)
self.release()
def __del__(self):
@@ -170,16 +176,18 @@ class LockFile(object):
try:
os.kill(contents.pid, 0)
except OSError:
- logger.warning(f'Lockfile {self.lockfile}\'s pid ({contents.pid}) is stale; ' +
- 'force acquiring')
+ msg = f'Lockfile {self.lockfile}\'s pid ({contents.pid}) is stale; force acquiring'
+ logger.warning(msg)
+ warnings.warn(msg)
self.release()
# Has the lock expiration expired?
if contents.expiration_timestamp is not None:
now = datetime.datetime.now().timestamp()
if now > contents.expiration_datetime:
- logger.warning(f'Lockfile {self.lockfile} expiration time has passed; ' +
- 'force acquiring')
+ msg = f'Lockfile {self.lockfile} expiration time has passed; force acquiring'
+ logger.warning(msg)
+ warnings.warn(msg)
self.release()
except Exception:
pass
diff --git a/logging_utils.py b/logging_utils.py
index de69046..20a57f7 100644
--- a/logging_utils.py
+++ b/logging_utils.py
@@ -13,6 +13,7 @@ import os
import random
import sys
from typing import Callable, Iterable, Mapping, Optional
+import warnings
from overrides import overrides
import pytz
@@ -523,9 +524,9 @@ def initialize_logging(logger=None) -> logging.Logger:
f'Initialized global logging; default logging level is {level_name}.'
)
if config.config['logging_clear_preexisting_handlers'] and preexisting_handlers_count > 0:
- logger.warning(
- 'Logging cleared {preexisting_handlers_count} global handlers (--logging_clear_preexisting_handlers)'
- )
+ msg = 'Logging cleared {preexisting_handlers_count} global handlers (--logging_clear_preexisting_handlers)'
+ logger.warning(msg)
+ warnings.warn(msg)
logger.debug(f'Logging format specification is "{fmt}"')
if config.config['logging_debug_threads']:
logger.debug('...Logging format spec captures tid/pid (--logging_debug_threads)')
diff --git a/ml/model_trainer.py b/ml/model_trainer.py
index acd7218..79ce706 100644
--- a/ml/model_trainer.py
+++ b/ml/model_trainer.py
@@ -12,6 +12,7 @@ import random
import sys
from types import SimpleNamespace
from typing import Any, List, NamedTuple, Optional, Set, Tuple
+import warnings
import numpy as np
from sklearn.model_selection import train_test_split # type:ignore
@@ -247,13 +248,13 @@ class TrainingBlueprint(ABC):
if self.spec.delete_bad_inputs:
msg = f"WARNING: {filename}: missing features or label; expected {self.spec.feature_count} but saw {len(x)}. DELETING."
- print(msg, file=sys.stderr)
logger.warning(msg)
+ warnings.warn(msg)
os.remove(filename)
else:
msg = f"WARNING: {filename}: missing features or label; expected {self.spec.feature_count} but saw {len(x)}. Skipping."
- print(msg, file=sys.stderr)
logger.warning(msg)
+ warnings.warn(msg)
return (X, y)
def make_progress_graph(self) -> None:
diff --git a/ml/quick_label.py b/ml/quick_label.py
index 1ed4296..120ff5f 100644
--- a/ml/quick_label.py
+++ b/ml/quick_label.py
@@ -4,6 +4,7 @@ import glob
import logging
import os
from typing import Callable, List, NamedTuple, Optional, Set
+import warnings
import argparse_utils
import config
@@ -90,9 +91,9 @@ def label(in_spec: InputSpec) -> None:
continue
features = in_spec.image_file_to_features_file(image)
if features is None or not os.path.exists(features):
- logger.warning(
- f'File {image} yielded file {features} which does not exist, SKIPPING.'
- )
+ msg = f'File {image} yielded file {features} which does not exist, SKIPPING.'
+ logger.warning(msg)
+ warnings.warn(msg)
continue
# Render features and image.
diff --git a/persistent.py b/persistent.py
index f6ca0a0..2751572 100644
--- a/persistent.py
+++ b/persistent.py
@@ -7,6 +7,7 @@ import enum
import functools
import logging
from typing import Any
+import warnings
import file_utils
@@ -143,7 +144,9 @@ class persistent_autoloaded_singleton(object):
logger.debug(f'Attempting to load {cls.__name__} from persisted state.')
self.instance = cls.load()
if not self.instance:
- logger.warning('Loading from cache failed.')
+ msg = 'Loading from cache failed.'
+ logger.warning(msg)
+ warnings.warn(msg)
logger.debug(f'Attempting to instantiate {cls.__name__} directly.')
self.instance = cls(*args, **kwargs)
else:
diff --git a/remote_worker.py b/remote_worker.py
index 0086c40..42aeb85 100755
--- a/remote_worker.py
+++ b/remote_worker.py
@@ -51,9 +51,6 @@ cfg.add_argument(
@background_thread
def watch_for_cancel(terminate_event: threading.Event) -> None:
- if platform.node() == 'VIDEO-COMPUTER':
- logger.warning('Background thread not allowed on retarded computers, sorry.')
- return
logger.debug('Starting up background thread...')
p = psutil.Process(os.getpid())
while True:
diff --git a/smart_home/lights.py b/smart_home/lights.py
index e23569a..44b3634 100644
--- a/smart_home/lights.py
+++ b/smart_home/lights.py
@@ -11,6 +11,7 @@ import re
import subprocess
import sys
from typing import Any, Dict, List, Optional, Tuple
+import warnings
from overrides import overrides
import tinytuya as tt
@@ -46,14 +47,18 @@ def tplink_light_command(command: str) -> bool:
result = os.system(command)
signal = result & 0xFF
if signal != 0:
- logger.warning(f'{command} died with signal {signal}')
- logging_utils.hlog("%s died with signal %d" % (command, signal))
+ msg = f'{command} died with signal {signal}'
+ logger.warning(msg)
+ warnings.warn(msg)
+ logging_utils.hlog(msg)
return False
else:
exit_value = result >> 8
if exit_value != 0:
- logger.warning(f'{command} failed, exited {exit_value}')
- logging_utils.hlog("%s failed, exit %d" % (command, exit_value))
+ msg = f'{command} failed, exited {exit_value}'
+ logger.warning(msg)
+ warnings.warn(msg)
+ logging_utils.hlog(msg)
return False
logger.debug(f'{command} succeeded.')
return True
diff --git a/smart_home/outlets.py b/smart_home/outlets.py
index 8fd0948..6cc8d57 100644
--- a/smart_home/outlets.py
+++ b/smart_home/outlets.py
@@ -13,6 +13,7 @@ import re
import subprocess
import sys
from typing import Any, Dict, List, Optional
+import warnings
from meross_iot.http_api import MerossHttpClient
from meross_iot.manager import MerossManager
@@ -48,14 +49,18 @@ def tplink_outlet_command(command: str) -> bool:
result = os.system(command)
signal = result & 0xFF
if signal != 0:
- logger.warning(f'{command} died with signal {signal}')
- logging_utils.hlog("%s died with signal %d" % (command, signal))
+ msg = f'{command} died with signal {signal}'
+ logger.warning(msg)
+ warnings.warn(msg)
+ logging_utils.hlog(msg)
return False
else:
exit_value = result >> 8
if exit_value != 0:
- logger.warning(f'{command} failed, exited {exit_value}')
- logging_utils.hlog("%s failed, exit %d" % (command, exit_value))
+ msg = f'{command} failed, exited {exit_value}'
+ logger.warning(msg)
+ warnings.warn(msg)
+ logging_utils.hlog(msg)
return False
logger.debug(f'{command} succeeded.')
return True
diff --git a/smart_home/registry.py b/smart_home/registry.py
index 23584e1..20fb3f4 100644
--- a/smart_home/registry.py
+++ b/smart_home/registry.py
@@ -3,6 +3,7 @@
import logging
import re
from typing import List, Optional, Set
+import warnings
import argparse_utils
import config
@@ -63,7 +64,9 @@ class SmartHomeRegistry(object):
try:
(mac, name, keywords) = line.split(",")
except ValueError:
- logger.warning(f'SH-CONFIG> {line} is malformed?!')
+ msg = f'SH-CONFIG> {line} is malformed?!'
+ logger.warning(msg)
+ warnings.warn(msg)
continue
mac = mac.strip()
name = name.strip()
@@ -183,11 +186,11 @@ class SmartHomeRegistry(object):
logger.debug(' ...an unknown device (should this be here?)')
return device.Device(name, mac, kws)
except Exception as e:
- logger.warning(
- f'Got exception {e} while trying to communicate with device {name}/{mac}.'
- )
+ logger.exception(e)
return device.Device(name, mac, kws)
- logger.warning(f'{mac} is not a known smart home device, returning None')
+ msg = f'{mac} is not a known smart home device, returning None'
+ logger.warning(msg)
+ warnings.warn(msg)
return None
def query(self, query: str) -> List[device.Device]:
diff --git a/string_utils.py b/string_utils.py
index 45607f3..b93dc93 100644
--- a/string_utils.py
+++ b/string_utils.py
@@ -14,6 +14,7 @@ import string
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple
import unicodedata
from uuid import uuid4
+import warnings
import list_utils
@@ -1097,7 +1098,9 @@ def to_date(in_str: str) -> Optional[datetime.date]:
d.parse(in_str)
return d.get_date()
except dp.ParseException:
- logger.warning(f'Unable to parse date {in_str}.')
+ msg = f'Unable to parse date {in_str}.'
+ logger.warning(msg)
+ warnings.warn(msg)
return None
@@ -1111,7 +1114,9 @@ def valid_date(in_str: str) -> bool:
_ = d.parse(in_str)
return True
except dp.ParseException:
- logger.warning(f'Unable to parse date {in_str}.')
+ msg = f'Unable to parse date {in_str}.'
+ logger.warning(msg)
+ warnings.warn(msg)
return False
@@ -1126,7 +1131,9 @@ def to_datetime(in_str: str) -> Optional[datetime.datetime]:
if type(dt) == datetime.datetime:
return dt
except ValueError:
- logger.warning(f'Unable to parse datetime {in_str}.')
+ msg = f'Unable to parse datetime {in_str}.'
+ logger.warning(msg)
+ warnings.warn(msg)
return None
@@ -1137,7 +1144,9 @@ def valid_datetime(in_str: str) -> bool:
_ = to_datetime(in_str)
if _ is not None:
return True
- logger.warning(f'Unable to parse datetime {in_str}.')
+ msg = f'Unable to parse datetime {in_str}.'
+ logger.warning(msg)
+ warnings.warn(msg)
return False
@@ -1466,8 +1475,9 @@ def chunk(txt: str, chunk_size):
"""
if len(txt) % chunk_size != 0:
- logger.warning(
- f'String to chunk\'s length ({len(txt)} is not an even multiple of chunk_size ({chunk_size})')
+ msg = f'String to chunk\'s length ({len(txt)} is not an even multiple of chunk_size ({chunk_size})'
+ logger.warning(msg)
+ warnings.warn(msg)
for x in range(0, len(txt), chunk_size):
yield txt[x:x+chunk_size]
diff --git a/unittest_utils.py b/unittest_utils.py
index bb1a9b4..270e20d 100644
--- a/unittest_utils.py
+++ b/unittest_utils.py
@@ -18,6 +18,7 @@ import time
import tempfile
from typing import Callable
import unittest
+import warnings
import bootstrap
import config
@@ -78,7 +79,9 @@ def check_method_for_perf_regressions(func: Callable) -> Callable:
perfdb = load_known_test_performance_characteristics()
except Exception as e:
logger.exception(e)
- logger.warning(f'Unable to load perfdb from {_db}')
+ msg = f'Unable to load perfdb from {_db}'
+ logger.warning(msg)
+ warnings.warn(msg)
perfdb = {}
# This is a unique identifier for a test: filepath!function