From: Scott Gasch Date: Sat, 30 Apr 2022 17:59:48 +0000 (-0700) Subject: Add some docs and doctests to things that have 0% coverage. X-Git-Url: https://wannabe.guru.org/gitweb/?a=commitdiff_plain;h=f2901184ccb5415cf40b3ec61c128a6a59ab3aa7;p=python_utils.git Add some docs and doctests to things that have 0% coverage. --- diff --git a/persistent.py b/persistent.py index 27aa4b3..b42a5c0 100644 --- a/persistent.py +++ b/persistent.py @@ -64,7 +64,23 @@ class Persistent(ABC): def was_file_written_today(filename: str) -> bool: - """Returns True if filename was written today.""" + """Returns True if filename was written today. + + >>> import os + >>> filename = f'/tmp/testing_persistent_py_{os.getpid()}' + >>> os.system(f'touch {filename}') + 0 + >>> was_file_written_today(filename) + True + >>> os.system(f'touch -d 1974-04-15T01:02:03.99 {filename}') + 0 + >>> was_file_written_today(filename) + False + >>> os.system(f'/bin/rm -f {filename}') + 0 + >>> was_file_written_today(filename) + False + """ if not file_utils.does_file_exist(filename): return False @@ -82,7 +98,22 @@ def was_file_written_within_n_seconds( """Returns True if filename was written within the pas limit_seconds seconds. + >>> import os + >>> filename = f'/tmp/testing_persistent_py_{os.getpid()}' + >>> os.system(f'touch {filename}') + 0 + >>> was_file_written_within_n_seconds(filename, 60) + True + >>> import time + >>> time.sleep(2.0) + >>> was_file_written_within_n_seconds(filename, 2) + False + >>> os.system(f'/bin/rm -f {filename}') + 0 + >>> was_file_written_within_n_seconds(filename, 60) + False """ + if not file_utils.does_file_exist(filename): return False @@ -169,3 +200,9 @@ class persistent_autoloaded_singleton(object): return self.instance return _load + + +if __name__ == '__main__': + import doctest + + doctest.testmod() diff --git a/smart_home/cameras.py b/smart_home/cameras.py index 8643611..e8f164b 100644 --- a/smart_home/cameras.py +++ b/smart_home/cameras.py @@ -5,6 +5,7 @@ """Utilities for dealing with the webcams.""" import logging +from typing import Optional import scott_secrets import smart_home.device as dev @@ -29,9 +30,12 @@ class BaseCamera(dev.Device): super().__init__(name.strip(), mac.strip(), keywords) self.camera_name = BaseCamera.camera_mapping.get(name, None) - def get_stream_url(self) -> str: + def get_stream_url(self) -> Optional[str]: + """Get the URL for the webcam's live stream. Return None on error.""" + name = self.camera_name - assert name is not None + if not name: + return None if name == 'driveway': return f'http://10.0.0.226:8080/{scott_secrets.SHINOBI_KEY1}/mjpeg/{scott_secrets.SHINOBI_KEY2}/driveway' else: diff --git a/smart_home/chromecasts.py b/smart_home/chromecasts.py index 408ccf0..3bd45f1 100644 --- a/smart_home/chromecasts.py +++ b/smart_home/chromecasts.py @@ -2,7 +2,7 @@ # © Copyright 2021-2022, Scott Gasch -"""Utilities for dealing with the webcams.""" +"""Utilities for dealing with the chromecasts.""" import atexit import datetime diff --git a/smart_home/registry.py b/smart_home/registry.py index d63474d..8ca7f3b 100644 --- a/smart_home/registry.py +++ b/smart_home/registry.py @@ -83,9 +83,9 @@ class SmartHomeRegistry(object): self._keywords_by_name[name] = keywords self._keywords_by_mac[mac] = keywords self._names_by_mac[mac] = name - self.index_device(name, keywords, mac) + self._index_device(name, keywords, mac) - def index_device(self, name: str, keywords: str, mac: str) -> None: + def _index_device(self, name: str, keywords: str, mac: str) -> None: properties = [("name", name)] tags = set() for kw in keywords.split(): @@ -111,9 +111,30 @@ class SmartHomeRegistry(object): return s def get_keywords_by_name(self, name: str) -> Optional[str]: + """Given the name of a device, get its keywords. + + >>> reg = SmartHomeRegistry('/home/scott/bin/network_mac_addresses.txt') + >>> reg.get_keywords_by_name('near_kitchen_lamp') + 'wifi smart light goog meross test' + + >>> reg.get_keywords_by_name('unknown') is None + True + + """ return self._keywords_by_name.get(name, None) def get_macs_by_name(self, name: str) -> Set[str]: + """Given the name of a device, get its MAC address(es) + + >>> reg = SmartHomeRegistry('/home/scott/bin/network_mac_addresses.txt') + >>> reg.get_macs_by_name('near_kitchen_lamp') + {'34:29:8F:12:34:8E'} + + >>> reg.get_macs_by_name('unknown') + set() + + """ + retval = set() for (mac, lname) in self._names_by_mac.items(): if name in lname: @@ -121,6 +142,19 @@ class SmartHomeRegistry(object): return retval def get_macs_by_keyword(self, keyword: str) -> Set[str]: + """Given a keyword, return the set of MAC address(es) that have + that keyword. + + >>> reg = SmartHomeRegistry('/home/scott/bin/network_mac_addresses.txt') + >>> r = reg.get_macs_by_keyword('test') + >>> e = set(['34:29:8F:12:26:74' , '34:29:8F:12:34:8E']) + >>> r == e + True + + >>> reg.get_macs_by_keyword('unknown') + set() + + """ retval = set() for (mac, keywords) in self._keywords_by_mac.items(): if keyword in keywords: @@ -128,11 +162,15 @@ class SmartHomeRegistry(object): return retval def get_device_by_name(self, name: str) -> Optional[device.Device]: + """Given a name, return its Device object.""" + if name in self._macs_by_name: return self.get_device_by_mac(self._macs_by_name[name]) return None def get_all_devices(self) -> List[device.Device]: + """Return a list of all known devices.""" + retval = [] for mac, _ in self._keywords_by_mac.items(): if mac is not None: @@ -142,6 +180,8 @@ class SmartHomeRegistry(object): return retval def get_device_by_mac(self, mac: str) -> Optional[device.Device]: + """Given a MAC address, return its Device object.""" + if mac in self._keywords_by_mac: name = self._names_by_mac[mac] kws = self._keywords_by_mac[mac] @@ -197,7 +237,7 @@ class SmartHomeRegistry(object): return None def query(self, query: str) -> List[device.Device]: - """Evaluates a lighting query expression formed of keywords to search + """Evaluates a device query expression formed of keywords to search for, logical operators (and, or, not), and parenthesis. Returns a list of matching lights. """ @@ -211,3 +251,9 @@ class SmartHomeRegistry(object): if dev is not None: retval.append(dev) return retval + + +if __name__ == '__main__': + import doctest + + doctest.testmod() diff --git a/smart_home/thermometers.py b/smart_home/thermometers.py index 73117bc..4531c61 100644 --- a/smart_home/thermometers.py +++ b/smart_home/thermometers.py @@ -28,6 +28,22 @@ class ThermometerRegistry(object): } def read_temperature(self, location: str, *, convert_to_fahrenheit=False) -> Optional[float]: + """Read the current value of a thermometer (in celsius unless + convert_to_fahrenheit is True) and return it. Return None on + error. + + >>> registry = ThermometerRegistry() + >>> registry.read_temperature('unknown') is None + True + + >>> temp = registry.read_temperature('house_computer_closet') + >>> temp is None + False + >>> temp > 0.0 + True + + """ + record = self.thermometers.get(location, None) if record is None: logger.error( @@ -36,6 +52,7 @@ class ThermometerRegistry(object): self.thermometers.keys(), ) return None + url = f'http://{record[0]}/~pi/{record[1]}' logger.debug('Constructed URL: %s', url) try: @@ -54,3 +71,9 @@ class ThermometerRegistry(object): if www is not None: www.close() return temp + + +if __name__ == '__main__': + import doctest + + doctest.testmod()