Skip to content

Commit

Permalink
An experiment with Python type annotations
Browse files Browse the repository at this point in the history
The documentation of them is… imperfect
  • Loading branch information
dkfellows committed Feb 11, 2021
1 parent 4f954fb commit 702638d
Show file tree
Hide file tree
Showing 10 changed files with 49 additions and 43 deletions.
7 changes: 7 additions & 0 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,15 @@
'sphinx.ext.autodoc',
'sphinx.ext.viewcode',
'sphinx.ext.autosummary',
'sphinx.ext.intersphinx',
]

intersphinx_mapping = {
'python': ('https://docs.python.org/3.6', None),
'numpy': ("https://numpy.org/doc/1.19/", None),
'matplotlib': ('https://matplotlib.org', None),
}

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

Expand Down
3 changes: 2 additions & 1 deletion spinn_utilities/abstract_context_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from typing import Iterable
from .abstract_base import AbstractBase, abstractmethod


class AbstractContextManager(object, metaclass=AbstractBase):
""" Closeable class that supports being used as a simple context manager.
"""

__slots__ = []
__slots__: Iterable[str] = []

@abstractmethod
def close(self):
Expand Down
3 changes: 2 additions & 1 deletion spinn_utilities/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import sys
from inspect import getfullargspec
from .overrides import overrides
from typing import ClassVar, List, Tuple

_LEVELS = {
'debug': logging.DEBUG,
Expand Down Expand Up @@ -149,7 +150,7 @@ class FormatAdapter(logging.LoggerAdapter):
"""
__kill_level = logging.CRITICAL + 1
__repeat_at_end = logging.WARNING
__repeat_messages = []
__repeat_messages: ClassVar[List[Tuple[str]]] = []
__write_normal = True
__report_file = None

Expand Down
3 changes: 2 additions & 1 deletion spinn_utilities/logger_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import Set

_already_issued = set()
_already_issued: Set[str] = set()


def warn_once(logger, msg):
Expand Down
3 changes: 2 additions & 1 deletion spinn_utilities/matrix/abstract_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from typing import Iterable
from spinn_utilities.abstract_base import AbstractBase, abstractmethod


class AbstractMatrix(object, metaclass=AbstractBase):
""" A rectangular 2D collection of data.
"""
__slots__ = []
__slots__: Iterable[str] = []

@abstractmethod
def get_data(self, x, y):
Expand Down
10 changes: 2 additions & 8 deletions spinn_utilities/overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@

import inspect

try:
# pylint: disable=no-member
_introspector = inspect.getfullargspec # @UndefinedVariable
except AttributeError:
_introspector = inspect.getargspec


class overrides(object):
""" A decorator for indicating that a method overrides another method in\
Expand Down Expand Up @@ -83,8 +77,8 @@ def __match_defaults(default_args, super_defaults, extend_ok):

def __verify_method_arguments(self, method):
""" Check that the arguments match. """
method_args = _introspector(method)
super_args = _introspector(self._superclass_method)
method_args = inspect.getfullargspec(method)
super_args = inspect.getfullargspec(self._superclass_method)
all_args = [
arg for arg in method_args.args
if arg not in self._additional_arguments]
Expand Down
3 changes: 2 additions & 1 deletion spinn_utilities/ping.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
import platform
import subprocess
import time
from typing import Set


class Ping(object):
unreachable = set()
unreachable: Set[str] = set()

@staticmethod
def ping(ipaddr):
Expand Down
3 changes: 2 additions & 1 deletion spinn_utilities/progress_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from spinn_utilities.overrides import overrides
from spinn_utilities import logger_utils
import spinn_utilities
from typing import Dict, List

logger = FormatAdapter(logging.getLogger(__name__))

Expand Down Expand Up @@ -223,7 +224,7 @@ class _EnhancedProgressBar(ProgressBar):

_line_no = 0
_seq_id = 0
_step_characters = defaultdict(list)
_step_characters: Dict[int,List[str]] = defaultdict(list)
_ENABLE_DATES = (
"0401", "0214", "0427", "0428", "0429", "0430", "0501", "0502",
"0503", "0504", "0505", "0506", "0507", "0508", "0509", "0510")
Expand Down
4 changes: 2 additions & 2 deletions spinn_utilities/ranged/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
population views and assemblies.
"""

from .abstract_dict import AbstractDict
from .abstract_dict import AbstractDict, KeyType, ValueType
from .abstract_list import AbstractList, DualList, SingleList
from .abstract_sized import AbstractSized
from .abstract_view import AbstractView
Expand All @@ -31,4 +31,4 @@
__all__ = [
"AbstractDict", "AbstractList", "DualList", "SingleList", "AbstractSized",
"AbstractView", "MultipleValuesException", "RangeDictionary",
"RangedList", "RangedListOfList"]
"RangedList", "RangedListOfList", "KeyType", "ValueType"]
53 changes: 26 additions & 27 deletions spinn_utilities/ranged/abstract_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,26 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

from typing import Iterable, Union, List, Iterator, Tuple
from spinn_utilities.abstract_base import AbstractBase, abstractmethod

#: The type of keys
KeyType = Union[str, Iterable[str], None]
#: The type of values
ValueType = Union[int, float, Iterable[int], Iterable[float]]


class AbstractDict(object, metaclass=AbstractBase):
""" Base class for the :py:class:`RangeDictionary` and *all* views.\
This allows the users to not have to worry if they have a view.
"""
__slots__ = []
__slots__: Iterable[str] = []

@abstractmethod
def get_value(self, key):
def get_value(self, key: KeyType) -> ValueType:
""" Gets a single shared value for all IDs covered by this view.
:param key: The key or keys to get the value of. Use None for all
:type key: str or iterable(str) or None
:return: If key is a str, this returns the single object.
If key is iterable (list, tuple, set, etc) of str (or None),
returns a dictionary object
Expand All @@ -37,14 +42,16 @@ def get_value(self, key):
"""

@abstractmethod
def keys(self):
def keys(self) -> Iterable[KeyType]:
""" Returns the keys in the dictionary
:return: keys in the dict
"""

@abstractmethod
def set_value(self, key, value, use_list_as_value=False):
def set_value(
self, key: KeyType, value: ValueType,
use_list_as_value: bool = False) -> None:
""" Resets a already existing key to the new value. \
All IDs in the whole range or view will have this key set.
Expand All @@ -59,14 +66,13 @@ def set_value(self, key, value, use_list_as_value=False):
(Currently multiple ranges not yet supported.)
:param key: key to value being set
:type key: str
:param value: any object
:param use_list_as_value: True if the value *is* a list
:raise KeyError: If a new key is being used.
"""

@abstractmethod
def ids(self):
def ids(self) -> List[int]:
""" Returns the IDs in range or view.\
If the view is setup with IDs out of numerical order the order used\
to create the view is maintained.
Expand All @@ -77,16 +83,15 @@ def ids(self):
ID `4` and not `2`
:return: list of IDs
:rtype: list(int)
"""

@abstractmethod
def iter_all_values(self, key, update_save=False):
def iter_all_values(self, key: KeyType, update_save: bool = False
) -> Iterator[ValueType]:
""" Iterates over the value(s) for all IDs covered by this view. \
There will be one yield for each ID even if values are repeated.
:param key: The key or keys to get the value of. Use None for all keys
:type key: str or iterable(str) or None
:param update_save: If set True the iteration will work even if values
are updated during iteration. If left False the iterator may be
faster but behaviour is *undefined* and *unchecked* if *any*
Expand All @@ -96,7 +101,8 @@ def iter_all_values(self, key, update_save=False):
yields dictionary objects
"""

def get_ranges(self, key=None):
def get_ranges(self, key: KeyType = None
) -> List[Tuple[int, int, ValueType]]:
""" Lists the ranges(s) for all IDs covered by this view. \
There will be one yield for each range which may cover one or\
more IDs.
Expand All @@ -106,7 +112,6 @@ def get_ranges(self, key=None):
by any updates.
:param key: The key or keys to get the value of. Use None for all
:type key: str or iterable(str) or None
:return: List of tuples of (`start`, `stop`, `value`).
`start` is *inclusive* so is the first ID in the range.
`stop` is *exclusive* so is the last ID in the range + 1.
Expand All @@ -117,7 +122,7 @@ def get_ranges(self, key=None):
return list(self.iter_ranges(key=key))

@abstractmethod
def iter_ranges(self, key=None):
def iter_ranges(self, key: KeyType = None):
""" Iterates over the ranges(s) for all IDs covered by this view.\
There will be one yield for each range which may cover one or\
more IDs.
Expand All @@ -128,7 +133,6 @@ def iter_ranges(self, key=None):
changed during iteration.
:param key: The key or keys to get the value of. Use None for all
:type key: str or iterable(str) or None
:return: yields tuples of (`start`, `stop`, `value`).
`start` is *inclusive* so is the first ID in the range.
`stop` is *exclusive* so is the last ID in the range + 1.
Expand All @@ -138,7 +142,7 @@ def iter_ranges(self, key=None):
"""

@abstractmethod
def get_default(self, key):
def get_default(self, key: KeyType) -> ValueType:
""" Gets the default value for a single key.\
Unless changed, the default is the original value.
Expand All @@ -151,7 +155,7 @@ def get_default(self, key):
:return: default for this key.
"""

def items(self):
def items(self) -> List[Tuple[KeyType, ValueType]]:
""" Returns a list of (``key``, ``value``) tuples.\
Works only if the whole ranges/view has single values.
Expand All @@ -169,7 +173,7 @@ def items(self):
results.append((key, value))
return results

def iteritems(self):
def iteritems(self) -> Iterator[Tuple[KeyType, ValueType]]:
""" Iterates over the (``key``, ``value``) tuples. \
Works only if the whole ranges/view has single values.
Expand All @@ -187,7 +191,7 @@ def iteritems(self):
for key in self.keys():
yield (key, self.get_value(key))

def values(self):
def values(self) -> List[ValueType]:
""" Returns a list of values.\
Works only if the whole ranges/view has single values.
Expand All @@ -205,7 +209,7 @@ def values(self):
results.append(value)
return results

def itervalues(self):
def itervalues(self) -> Iterator[ValueType]:
""" Iterates over the values.\
Works only if the whole ranges/view has single values.
Expand All @@ -223,39 +227,34 @@ def itervalues(self):
for key in self.keys():
yield self.get_value(key)

def __contains__(self, key):
def __contains__(self, key: KeyType):
""" Checks if the key is a dictionary key or a range ID.
:param key: Dictionary key or ID to check
:type key: str or int
:return: True if the str key is one of the dict keys or
if the int key is one of the range IDs. Otherwise False
:rtype: bool
"""
if isinstance(key, str):
return key in self.keys()
if isinstance(key, int):
return key in self.ids()
raise KeyError("Unexpected key type: {}".format(type(key)))

def has_key(self, key):
def has_key(self, key: KeyType) -> bool:
""" As the Deprecated dict ``has_keys`` function.
.. note::
Int keys to IDs are not supported.
:param key: the key
:type key: str
:return: If the key is in dict
:rtype: bool
"""
return key in self.keys()

def reset(self, key):
def reset(self, key: KeyType):
""" Sets the value(s) for a single key back to the default value.
:param key: Existing dict key
:type key: str
:param default: Value to be used by reset
"""
self.set_value(key, self.get_default(key=key))

0 comments on commit 702638d

Please sign in to comment.