Skip to content

Commit

Permalink
Merge branch 'master' into pylint_fix
Browse files Browse the repository at this point in the history
# Conflicts:
#	.pylint_dict.txt
#	spinnman/messages/scp/impl/app_copy_run.py
#	spinnman/processes/application_copy_run_process.py
#	spinnman/processes/get_machine_process.py
#	spinnman/spalloc/session.py
  • Loading branch information
Christian-B committed Nov 3, 2023
2 parents 5b73443 + f50bb65 commit 2f09b53
Show file tree
Hide file tree
Showing 200 changed files with 4,025 additions and 3,190 deletions.
9 changes: 9 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,12 @@

[run]
branch = True

[report]
# Coverage should ignore overloads; they're not real code
exclude_lines =
@overload
\.\.\.$
raise\s+NotImplementedError
if\s+TYPE_CHECKING:
#\s*(pragma|PRAGMA)[:\s]?\s*(no|NO)\s*(cover|COVER)
4 changes: 4 additions & 0 deletions .github/workflows/python_actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ jobs:
path: support
- name: Install pip, etc
uses: ./support/actions/python-tools
- name: Install mypy
run: pip install mypy

- name: Install Spinnaker Dependencies
uses: ./support/actions/install-spinn-deps
Expand All @@ -64,6 +66,8 @@ jobs:
with:
package: spinnman
language: en_GB
- name: Lint with mypy
run: mypy spinnman

validate:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ SpiNNMan.egg-info
.coverage
.cache/
.pytest_cache/
/.mypy_cache/
/scripts/
20 changes: 16 additions & 4 deletions .pylint_dict.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,37 +13,49 @@
# limitations under the License.

# Our abbreviations/names
bmpc
bmp
cpu
iobuf
scp
Spalloc
spinnman
bmpc
ybug
txrx
url
ybug
xy
xys

# Our special words
keepalive
# Our special words ("wrap-arounds" gets split up)
arounds


# Python packages
spinnman
websocket

# Python types
BMPConnectionData
BufferedIOBase
CoreSubset
CPUInfo
CPUInfos
CPUState
DiagnosticFilter
DiagnosticFilterDestination
DiagnosticFilterDefaultRoutingStatus
DiagnosticFilterEmergencyRoutingStatus
DiagnosticFilterPacketType
DiagnosticFilterPayloadStatus
DiagnosticFilterSource
ExecutableType
HeapElement
IOBuffer
PreparedRequest
RawIOBase
RouterError
SCAMPConnection
SCPResult
SpallocJob
SpallocMachine
SpinnakerBootMessage
Expand Down
5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
build-backend = "setuptools.build_meta"

[tool.mypy]
exclude = ["doc", "setup.py", "unittests", "quick_tests"]
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,15 @@ include =
spinnman.cfg
board_test_configuration.cfg
scamp.boot
py.typed

[options.extras_require]
test =
# pytest will be brought in by pytest-cov
pytest-cov
testfixtures
httpretty != 1.0.0
types-requests

[options.entry_points]
console_scripts = get_cores_in_run_state = spinnman.get_cores_in_run_state:main
Expand Down
36 changes: 23 additions & 13 deletions spinnman/connections/abstract_classes/abstract_scp_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from spinn_utilities.abstract_base import (
AbstractBase, abstractmethod, abstractproperty)
from typing import Optional, Tuple
from spinn_utilities.abstract_base import AbstractBase, abstractmethod
from spinnman.messages.scp.enums import SCPResult
from spinnman.messages.scp.abstract_messages import AbstractSCPRequest
from .connection import Connection


Expand All @@ -25,7 +26,7 @@ class AbstractSCPConnection(Connection, metaclass=AbstractBase):
__slots__ = ()

@abstractmethod
def is_ready_to_receive(self, timeout=0):
def is_ready_to_receive(self, timeout: float = 0):
"""
Determines if there is an SCP packet to be read without blocking.
Expand All @@ -34,16 +35,18 @@ def is_ready_to_receive(self, timeout=0):
:return: True if there is an SCP packet to be read
:rtype: bool
"""
raise NotImplementedError

@abstractmethod
def receive_scp_response(self, timeout=1.0):
def receive_scp_response(self, timeout: Optional[float] = 1.0) -> Tuple[
SCPResult, int, bytes, int]:
"""
Receives an SCP response from this connection. Blocks
until a message has been received, or a timeout occurs.
:param int timeout:
The time in seconds to wait for the message to arrive; if not
specified, will wait forever, or until the connection is closed
The time in seconds to wait for the message to arrive; if `None`,
will wait forever, or until the connection is closed
:return: The SCP result, the sequence number, the data of the response
and the offset at which the data starts (i.e., where the SDP
header starts).
Expand All @@ -53,16 +56,18 @@ def receive_scp_response(self, timeout=1.0):
:raise SpinnmanTimeoutException:
If there is a timeout before a message is received
"""
raise NotImplementedError

@abstractmethod
def get_scp_data(self, scp_request):
def get_scp_data(self, scp_request: AbstractSCPRequest) -> bytes:
"""
Returns the data of an SCP request as it would be sent down this
connection.
"""
raise NotImplementedError

@abstractmethod
def send_scp_request(self, scp_request):
def send_scp_request(self, scp_request: AbstractSCPRequest):
"""
Sends an SCP request down this connection.
Expand All @@ -82,21 +87,26 @@ def send_scp_request(self, scp_request):
:raise SpinnmanIOException:
If there is an error sending the message
"""
raise NotImplementedError

@abstractproperty
def chip_x(self):
@property
@abstractmethod
def chip_x(self) -> int:
"""
The X-coordinate of the chip at which messages sent down this
connection will arrive at first.
:rtype: int
"""
raise NotImplementedError

@abstractproperty
def chip_y(self):
@property
@abstractmethod
def chip_y(self) -> int:
"""
The Y-coordinate of the chip at which messages sent down this
connection will arrive at first.
:rtype: int
"""
raise NotImplementedError
6 changes: 4 additions & 2 deletions spinnman/connections/abstract_classes/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Connection(AbstractContextManager, metaclass=AbstractBase):
__slots__ = ()

@abstractmethod
def is_connected(self):
def is_connected(self) -> bool:
"""
Determines if the medium is connected at this point in time.
Expand All @@ -34,9 +34,11 @@ def is_connected(self):
If there is an error when determining the connectivity of the
medium.
"""
raise NotImplementedError

@abstractmethod
def close(self):
def close(self) -> None:
"""
Closes the connection.
"""
raise NotImplementedError
12 changes: 8 additions & 4 deletions spinnman/connections/abstract_classes/listenable.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Callable, Generic, TypeVar
from spinn_utilities.abstract_base import AbstractBase, abstractmethod
#: :meta private:
T = TypeVar("T")


# Should inherit from Connection, but doesn't for MRO reasons
class Listenable(object, metaclass=AbstractBase):
class Listenable(Generic[T], metaclass=AbstractBase):
"""
An interface for connections that can listen for incoming messages.
Expand All @@ -27,13 +29,14 @@ class Listenable(object, metaclass=AbstractBase):
__slots__ = ()

@abstractmethod
def get_receive_method(self):
def get_receive_method(self) -> Callable[[], T]:
"""
Get the method that receives for this connection.
"""
raise NotImplementedError

@abstractmethod
def is_ready_to_receive(self, timeout=0):
def is_ready_to_receive(self, timeout: float = 0) -> bool:
"""
Determines if there is an SCP packet to be read without blocking.
Expand All @@ -42,3 +45,4 @@ def is_ready_to_receive(self, timeout=0):
:return: True if there is an SCP packet to be read
:rtype: bool
"""
raise NotImplementedError
34 changes: 20 additions & 14 deletions spinnman/connections/connection_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,36 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations
import logging
from threading import Thread
from concurrent.futures import ThreadPoolExecutor
from typing import Callable, Generic, List, TypeVar
from concurrent.futures import ThreadPoolExecutor, Future
from spinn_utilities.log import FormatAdapter
from spinnman.exceptions import SpinnmanEOFException
from spinnman.connections.abstract_classes import Listenable

#: :meta private:
T = TypeVar("T")
logger = FormatAdapter(logging.getLogger(__name__))
_POOL_SIZE = 4
_TIMEOUT = 1


class ConnectionListener(Thread):
class ConnectionListener(Thread, Generic[T]):
"""
Thread that listens to a connection and calls callbacks with new
messages when they arrive.
"""
__slots__ = [
__slots__ = (
"__callback_pool",
"__callbacks",
"__connection",
"__done",
"__timeout"]
"__timeout")

def __init__(self, connection, n_processes=_POOL_SIZE, timeout=_TIMEOUT):
def __init__(self, connection: Listenable[T],
n_processes: int = _POOL_SIZE, timeout: float = _TIMEOUT):
"""
:param Listenable connection: A connection to listen to
:param int n_processes:
Expand All @@ -51,19 +56,20 @@ def __init__(self, connection, n_processes=_POOL_SIZE, timeout=_TIMEOUT):
self.__timeout = timeout
self.__callback_pool = ThreadPoolExecutor(max_workers=n_processes)
self.__done = False
self.__callbacks = set()
self.__callbacks: List[Callable[[T], None]] = []

def __run_step(self, handler):
def __run_step(self, handler: Callable[[], T]):
"""
:param ~collections.abc.Callable handler:
"""
if self.__connection.is_ready_to_receive(timeout=self.__timeout):
message = handler()
for callback in self.__callbacks:
future = self.__callback_pool.submit(callback, message)
future = self.__callback_pool.submit(
callback, message)
future.add_done_callback(self.__done_callback)

def __done_callback(self, future):
def __done_callback(self, future: Future[None]):
"""
:param ~concurrent.futures.Future future:
"""
Expand All @@ -72,7 +78,7 @@ def __done_callback(self, future):
except Exception: # pylint: disable=broad-except
logger.exception("problem in listener call")

def run(self):
def run(self) -> None:
"""
Implements the listening thread.
"""
Expand All @@ -88,17 +94,17 @@ def run(self):
logger.warning("problem when dispatching message",
exc_info=True)

def add_callback(self, callback):
def add_callback(self, callback: Callable[[T], None]):
"""
Add a callback to be called when a message is received.
:param ~collections.abc.Callable callback:
A callable which takes a single parameter, which is the message
received; the result of the callback will be ignored.
"""
self.__callbacks.add(callback)
self.__callbacks.append(callback)

def close(self):
def close(self) -> None:
"""
Closes the listener.
Expand Down
Loading

0 comments on commit 2f09b53

Please sign in to comment.