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

Pylint strict #52

Merged
merged 13 commits into from
Jan 5, 2024
3 changes: 2 additions & 1 deletion .github/workflows/python_actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ jobs:
uses: ./support/actions/pylint
with:
package: spinnaker_testbase
exitcheck: 39
exitcheck: 31 # Action fails on any message
language: en_GB

- name: Run rat copyright enforcement
if: matrix.python-version == 3.12
Expand Down
33 changes: 33 additions & 0 deletions .pylint_dict.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright (c) 2023 The University of Manchester
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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.

# Our abbreviations/names

# Python types
TextIOBase

# Python packages
pydevd
pyplot
unittest

# Our "special" words
dirs
iobuf
xyz

# Python bits
env

# Misc
21 changes: 14 additions & 7 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,20 @@ confidence=
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=
disable=consider-using-dict-items, missing-module-docstring, unsubscriptable-object

# consider-using-dict-items "for x in foo:" is fine!

# missing-module-docstring expects a comment at import level which we don't do

# False positives for unsubscriptable-object. Mypy better at this class of issue
# See https://github.com/pylint-dev/pylint/issues/1498

# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
# multiple time (only on the command line, not in the configuration file where
# it should appear only once). See also the "--disable" option for examples.
enable=c-extension-no-member
enable=


[REPORTS]
Expand Down Expand Up @@ -265,7 +272,6 @@ notes=FIXME,
XXX



[SIMILARITIES]

# Ignore comments when computing similarities.
Expand All @@ -291,7 +297,7 @@ max-spelling-suggestions=4
spelling-dict=

# List of comma separated words that should not be checked.
spelling-ignore-words=
spelling-ignore-words=ReservedAssignment,noqa,pragma

# A path to a file that contains private dictionary; one word per line.
spelling-private-dict-file=
Expand Down Expand Up @@ -379,8 +385,9 @@ init-import=no
# List of method names used to declare (i.e. assign) instance attributes.
defining-attr-methods=__init__,
__new__,
_new_run_clear,
_machine_clear,
__call__,
_hard_reset,
setUp

# List of member names, which should be excluded from the protected access
# warning.
Expand Down Expand Up @@ -467,4 +474,4 @@ known-third-party=enchant

# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception
overgeneral-exceptions=builtin.Exception
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
[![Python Actions](https://github.com/SpiNNakerManchester/TestBase/actions/workflows/python_actions.yml/badge.svg?branch=main)](https://github.com/SpiNNakerManchester/TestBase/actions/workflows/python_actions.yml) [![Coverage Status](https://coveralls.io/repos/github/SpiNNakerManchester/TestBase/badge.svg)](https://coveralls.io/github/SpiNNakerManchester/TestBase)

This Repository hold classes and script used for integration tests
This Repository hold classes and script used for unit and integration tests

There is need to use this repository unless you want to run some or all integration tests locally
There is need to use this repository unless you want to run some or all tests locally

Documentation
-------------
[TestBase documentation](https://spinnakertestbase.readthedocs.io/)

[Combined PyNN8 python documentation (Excluding TestBase)](http://spinnakermanchester.readthedocs.io)
[Combined python documentation (Excluding TestBase)](http://spinnakermanchester.readthedocs.io)

7 changes: 7 additions & 0 deletions spinnaker_testbase/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.

"""
This Repository hold classes and script used for unit and integration tests

There is need to use this repository unless you want to run some or all tests
locally
"""

from .base_test_case import BaseTestCase
from .root_script_builder import RootScriptBuilder
from .script_checker import ScriptChecker
Expand Down
31 changes: 25 additions & 6 deletions spinnaker_testbase/base_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import os
import random
import sys
from typing import List
from spinn_front_end_common.data import FecDataView
from .root_test_case import RootTestCase

Expand All @@ -23,9 +24,12 @@


class BaseTestCase(RootTestCase):
"""
This extends unittest.TestCase to offer extra functions as needed.
"""

def setUp(self):
self._setUp(sys.modules[self.__module__].__file__)
self._setup(sys.modules[self.__module__].__file__)

def assert_logs_messages(
self, log_records, sub_message, log_level='ERROR', count=1,
Expand All @@ -51,14 +55,29 @@ def assert_logs_messages(
f'"{sub_message}" not found in any {log_level} logs '
f'{count} times, was found {seen} times')

def get_provenance_files(self):
def get_provenance_files(self) -> List[str]:
"""
Gets a list of the Provenance files

:rtype: list(str)
"""
provenance_file_path = FecDataView().get_provenance_dir_path()
return os.listdir(provenance_file_path)

def get_system_iobuf_files(self):
system_iobuf_file_path = (FecDataView.get_system_provenance_dir_path())
def get_system_iobuf_files(self) -> List[str]:
"""
Get a list of the system iobuf files.

:rtype: list(str)
"""
system_iobuf_file_path = FecDataView.get_system_provenance_dir_path()
return os.listdir(system_iobuf_file_path)

def get_app_iobuf_files(self):
app_iobuf_file_path = (FecDataView.get_app_provenance_dir_path())
def get_app_iobuf_files(self) -> List[str]:
"""
Get a list of the application iobuf files.

:rtype: list(str)
"""
app_iobuf_file_path = FecDataView.get_app_provenance_dir_path()
return os.listdir(app_iobuf_file_path)
76 changes: 76 additions & 0 deletions spinnaker_testbase/ping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Copyright (c) 2018 The University of Manchester
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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.

import platform
import subprocess
import time
from typing import Set


class Ping(object):
"""
Platform-independent ping support.
"""

#: The unreachable host cache.
unreachable: Set[str] = set()

@staticmethod
def ping(ip_address):
"""
Send a ping (ICMP ECHO request) to the given host.
SpiNNaker boards support ICMP ECHO when booted.

:param str ip_address:
The IP address to ping. Hostnames can be used, but are not
recommended.
:return:
return code of subprocess; 0 for success, anything else for failure
:rtype: int
"""
if platform.platform().lower().startswith("windows"):
cmd = "ping -n 1 -w 1 "
else:
cmd = "ping -c 1 -W 1 "
process = subprocess.Popen(
cmd + ip_address, shell=True, stdout=subprocess.PIPE)
time.sleep(1.2)
process.stdout.close()
process.wait()
return process.returncode

@staticmethod
def host_is_reachable(ip_address):
"""
Test if a host is unreachable via ICMP ECHO.

.. note::
This information may be cached in various ways. Transient failures
are not necessarily detected or recovered from.

:param str ip_address:
The IP address to ping. Hostnames can be used, but are not
recommended.
:rtype: bool
"""
if ip_address in Ping.unreachable:
return False
tries = 0
while True:
if Ping.ping(ip_address) == 0:
return True
tries += 1
if tries > 10:
Ping.unreachable.add(ip_address)
return False
3 changes: 3 additions & 0 deletions spinnaker_testbase/root_script_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@


class RootScriptBuilder(object):
"""
Looks for example scripts that can be made into integration tests.
"""

def add_script(self, test_file, name, local_path, skip_imports):
"""
Expand Down
25 changes: 20 additions & 5 deletions spinnaker_testbase/root_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,18 @@
from spinn_front_end_common.data import FecDataView

if os.environ.get('CONTINUOUS_INTEGRATION', 'false').lower() == 'true':
max_tries = 3
MAX_TRIES = 3
else:
max_tries = 1
MAX_TRIES = 1


class RootTestCase(unittest.TestCase):
"""
This holds the code shared by the all test and script checkers

def _setUp(self, script):
"""

def _setup(self, script):
# Remove random effect for testing
# Set test_seed to None to allow random
# pylint: disable=attribute-defined-outside-init
Expand Down Expand Up @@ -62,14 +66,25 @@ def error_file(self):
return os.path.join(test_dir, "ErrorFile.txt")

def report(self, message, file_name):
"""
Writes some text to the specified file

The file will be written in the env GLOBAL_REPORTS directory.

If no GLOBAL_REPORTS is defined the timestamp directory
holding the run data is used.

:param str message:
:param str file_name: local file name.
"""
if not message.endswith("\n"):
message += "\n"
global_reports = os.environ.get("GLOBAL_REPORTS", None)
if not global_reports:
try:
global_reports = FecDataView.get_timestamp_dir_path()
except NotSetupException:
# This may happen if you are running none script fiels locally
# This may happen if you are running none script files locally
return

if not os.path.exists(global_reports):
Expand Down Expand Up @@ -112,7 +127,7 @@ def runsafe(self, method, retry_delay=3.0, skip_exceptions=None):
error_file.write(str(ex))
error_file.write("\n")
retries += 1
if retries >= max_tries:
if retries >= MAX_TRIES:
raise ex
except (PacmanValueError, PacmanPartitionException) as ex:
# skip out if on a spin three
Expand Down
19 changes: 14 additions & 5 deletions spinnaker_testbase/script_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,34 @@
import os
import sys
import time
from unittest import SkipTest
import matplotlib
import matplotlib.pyplot as pyplot
from unittest import SkipTest
from .root_test_case import RootTestCase
matplotlib.use('Agg')

# pylint: disable=invalid-name
script_checker_shown = False


# This is a global function as pydevd calls _needsmain when debugging
def mockshow():
"""
This will replace pyplot.show during script tests

This avoids the plots from printed but checks the script tried to
"""
# pylint: disable=global-statement
global script_checker_shown
script_checker_shown = True


class ScriptChecker(RootTestCase):
"""
Will run a script. Typically as part of Integration Tests.
"""

def script_path(self, script):
def _script_path(self, script):
class_file = sys.modules[self.__module__].__file__
integration_tests_directory = os.path.dirname(class_file)
root_dir = os.path.dirname(integration_tests_directory)
Expand All @@ -52,9 +61,9 @@ def check_script(self, script, broken_msg=None, skip_exceptions=None):
# pylint: disable=global-statement
global script_checker_shown

script_path = self.script_path(script)
self._setUp(script_path)

script_path = self._script_path(script)
self._setup(script_path)
# pylint: disable=import-outside-toplevel
plotting = "import matplotlib.pyplot" in (
open(script_path, encoding="utf-8").read())
if plotting:
Expand Down
Loading