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

Add flake8 to project #246

Merged
merged 2 commits into from
Apr 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Python package

on:
push:
push:
branches:
- main
pull_request:
Expand All @@ -17,22 +17,27 @@ jobs:

steps:
- uses: actions/checkout@v2

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .[dev]

- name: Lint
run: |
black --check .
mypy .


- name: Optional linting
continue-on-error: true
run: |
flake8 .

- name: Test with pytest
run: |
pytest
Expand Down
10 changes: 10 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ no_strict_optional = True
no_site_packages = True
ignore_missing_imports = True

[flake8]
filename =
./src/*,
./tests/*,
exclude = .git, __pycache__, *.egg-info, *.json, *.conf, *.env,
per-file-ignores =
__init__.py: F401,
Christdej marked this conversation as resolved.
Show resolved Hide resolved
ignore =
W503
max-line-length = 88

[tool:pytest]
python_files = test_*.py
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
extras_require={
"dev": [
"black",
"flake8",
"mypy",
"myst-parser",
"pytest-dotenv",
Expand Down
19 changes: 12 additions & 7 deletions src/isar/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,27 @@ class Settings(BaseSettings):

# Determines which mission planner module is used by ISAR
# Options: [local echo]
# Selecting a different mission planner module than local may require certain access rights
# Selecting a different mission planner module than local may require certain access
# rights
MISSION_PLANNER: str = Field(default="local")

# Determines which storage modules are used by ISAR
# Comma separated list of modules to use. Each module will be called when storing results from inspections
# Comma separated list of modules to use. Each module will be called when storing
# results from inspections
# Options [local blob slimm]
# Selecting a different storage module than local may require certain access rights
STORAGE: List[str] = Field(default=["local"])

# Determines whether the MQTT publishing module should be enabled or not
# The publishing module will attempt to connect to the MQTT broker configured in "service_connections"
# The publishing module will attempt to connect to the MQTT broker configured in
# "service_connections"
# Options: [false true]
MQTT_ENABLED: bool = Field(default=False)

# Determines whether authentication is enabled for the API or not
# Enabling this requires certain resources available for OAuth2 authentication
# Currently supported authentication is Azure AD (https://github.com/Intility/fastapi-azure-auth)
# Currently supported authentication is Azure AD
# (https://github.com/Intility/fastapi-azure-auth)
AUTHENTICATION_ENABLED: bool = Field(default=False)

# Tenant ID for the Azure tenant with your Azure Active Directory application
Expand Down Expand Up @@ -139,11 +143,12 @@ class Settings(BaseSettings):
default="https://slimmingestapidev.azurewebsites.net/api/Ingest"
)

# Whether the results should be copied directly into the SLIMM datalake or only the metadata
# Whether the results should be copied directly into the SLIMM datalake or only the
# metadata
COPY_FILES_TO_SLIMM_DATALAKE: bool = Field(default=False)

# The configuration of this section is tightly coupled with the metadata that is submitted with the results once
# they have been uploaded.
# The configuration of this section is tightly coupled with the metadata that is
# submitted with the results once they have been uploaded.

# Four digit code indicating facility
PLANT_CODE: str = Field(default="1320")
Expand Down
4 changes: 2 additions & 2 deletions src/isar/mission_planner/echo_planner.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ def _create_inspection_tasks_from_sensor_types(

def _get_robot_pose(self, tag_name: str) -> Pose:
"""
Retrieve robot pose corresponding to inspection of a given tag. For now, this is a temporary hard-coded
solution.
Retrieve robot pose corresponding to inspection of a given tag. For now, this is
a temporary hard-coded solution.
"""
predefined_pose: Pose = predefined_poses[tag_name]

Expand Down
4 changes: 2 additions & 2 deletions src/isar/models/communication/queues/queue_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

class QueueIO:
"""
Creates input and output queue. The queues are defined such that input is from api to state machine
while output is from state machine to api.
Creates input and output queue. The queues are defined such that the input is from
api to state machine while the output is from state machine to api.
"""

def __init__(self, input_size: int = 0, output_size: int = 0):
Expand Down
5 changes: 4 additions & 1 deletion src/isar/models/mission.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ def _set_unique_id(self) -> None:
plant_short_name: str = settings.PLANT_SHORT_NAME
robot_id: str = settings.ROBOT_ID
now: datetime = datetime.utcnow()
self.id = f"{plant_short_name.upper()}{robot_id.upper()}{now.strftime('%d%m%Y%H%M%S%f')[:-3]}"
self.id = (
f"{plant_short_name.upper()}{robot_id.upper()} "
f"{now.strftime('%d%m%Y%H%M%S%f')[:-3]}"
)

def __post_init__(self) -> None:
if self.id is None:
Expand Down
5 changes: 3 additions & 2 deletions src/isar/services/service_connections/mqtt/mqtt_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ def __init__(self, mqtt_queue: Queue) -> None:
password = os.environ["ISAR_MQTT_PASSWORD"]
except KeyError:
self.logger.warning(
"Failed to retrieve ISAR_MQTT_PASSWORD from environment. Attempting with empty string as password."
"Failed to retrieve ISAR_MQTT_PASSWORD from environment. Attempting "
"with empty string as password."
)

self.host: str = settings.MQTT_HOST
Expand Down Expand Up @@ -110,7 +111,7 @@ def _on_backoff(data: dict) -> None:
@staticmethod
def _on_giveup(data: dict) -> None:
logging.getLogger("mqtt_client").error(
"Failed to connect to MQTT Broker within set backoff strategy. Raising error."
"Failed to connect to MQTT Broker within set backoff strategy."
)

@backoff.on_exception(
Expand Down
2 changes: 1 addition & 1 deletion src/isar/services/utilities/json_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def to_object(json_string: str) -> SimpleNamespace:

class EnhancedJSONEncoder(json.JSONEncoder):
"""
Custom JSONEncoder used in this project. Of special note is the ability to encode dataclasses.
Custom JSONEncoder with the ability to encode dataclasses.
"""

def default(self, o):
Expand Down
5 changes: 3 additions & 2 deletions src/isar/services/utilities/queue_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ class QueueUtilities:
@staticmethod
def check_queue(queue: Queue, queue_timeout: int = None) -> Any:
"""
Checks if there is a message on a queue. If a timeout is specified the function will raise a QueueTimeoutError
if there is no message within the timeout. If there is no timeout specified this function will block.
Checks if there is a message on a queue. If a timeout is specified the function
will raise a QueueTimeoutError if there is no message within the timeout. If
there is no timeout specified this function will block.
:param queue: The queue to be checked for a message
:param queue_timeout: Timeout in seconds
:return: Message found on queue
Expand Down
14 changes: 8 additions & 6 deletions src/isar/services/utilities/scheduling_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@

class SchedulingUtilities:
"""
Contains utility functions for scheduling missions from the API. The class handles required thread communication
through queues to the state machine.
Contains utility functions for scheduling missions from the API. The class handles
required thread communication through queues to the state machine.
"""

@inject
Expand All @@ -31,9 +31,10 @@ def ready_to_start_mission(
self,
) -> Tuple[bool, Optional[Tuple[StartMessage, HTTPStatus]]]:
"""
Checks the current mission status by communicating with the state machine thread through queues.
:return: (True, None) if the mission may be started. Otherwise (False, response) with a relevant response
message indicating the cause.
Checks the current mission status by communicating with the state machine thread
through queues.
:return: (True, None) if the mission may be started. Otherwise (False, response)
with a relevant response message indicating the cause.
"""
self.queues.mission_status.input.put(True)
try:
Expand All @@ -58,7 +59,8 @@ def start_mission(self, mission: Mission) -> Tuple[StartMessage, HTTPStatus]:
"""
Starts a mission by communicating with the state machine thread through queues.
:param mission: A Mission containing the mission tasks to be started.
:return: (message, status_code) is returned indicating the success and cause of the operation.
:return: (message, status_code) is returned indicating the success and cause of
the operation.
"""
self.queues.start_mission.input.put(deepcopy(mission))
try:
Expand Down
3 changes: 2 additions & 1 deletion src/isar/state_machine/state_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ def log_task_overview(self, mission: Mission):
"""Log an overview of the tasks in a mission"""
task_status: str = "\n".join(
[
f"{i:>3} {type(task).__name__:<20} {str(task.id)[:8]:<32} -- {task.status}"
f"{i:>3} {type(task).__name__:<20} "
f"{str(task.id)[:8]:<32} -- {task.status}"
for i, task in enumerate(mission.tasks)
]
)
Expand Down
9 changes: 6 additions & 3 deletions src/isar/state_machine/states/initiate_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ def _run(self):
except RobotInvalidTaskExpection:
self.state_machine.current_task.status = TaskStatus.Failed
self.logger.warning(
f"Failed to initiate {type(self.state_machine.current_task).__name__}"
f"Failed to initiate "
f"{type(self.state_machine.current_task).__name__}"
f"Invalid task: {str(self.state_machine.current_task.id)[:8]}"
)
next_state = States.InitiateTask
Expand All @@ -107,14 +108,16 @@ def _run(self):
self.state_machine.current_task.status = TaskStatus.Scheduled
next_state = States.Monitor
self.logger.info(
f"Successfully initiated {type(self.state_machine.current_task).__name__} "
f"Successfully initiated "
f"{type(self.state_machine.current_task).__name__} "
f"task: {str(self.state_machine.current_task.id)[:8]}"
)
break
else:
self.initiate_task_failure_counter += 1
self.logger.info(
f"Initiating task failed #: {str(self.initiate_task_failure_counter)}"
f"Initiating task failed #: "
f"{str(self.initiate_task_failure_counter)}"
)
if (
self.initiate_task_failure_counter
Expand Down
3 changes: 2 additions & 1 deletion src/isar/storage/local_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ def store(self, inspection: Inspection, metadata: MissionMetadata):
metadata_file.write(metadata_bytes)
except IOError as e:
self.logger.warning(
f"Failed open/write for one of the following files: \n{absolute_path}\n{absolute_metadata_path}"
f"Failed open/write for one of the following files: \n"
f"{absolute_path}\n{absolute_metadata_path}"
)
raise StorageException from e
except Exception as e:
Expand Down
3 changes: 2 additions & 1 deletion src/isar/storage/slimm_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ def _ingest(self, inspection, multiform_body, request_url, token):
)
except (RequestException, HTTPError) as e:
self.logger.warning(
f"Failed to upload inspection: {inspection.id} to SLIMM due to a request exception"
f"Failed to upload inspection: {inspection.id} to SLIMM due to a "
f"request exception"
)
raise StorageException from e

Expand Down
7 changes: 4 additions & 3 deletions src/isar/storage/uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,15 +99,16 @@ def _upload(self, upload_item: UploaderQueueItem):
upload_item.increment_retry(self.max_wait_time)
self.logger.warning(
f"Storage handler: {type(upload_item.storage_handler).__name__} "
f"failed to upload inspection: {str(upload_item.inspection.id)[:8]}. "
f"failed to upload inspection: "
f"{str(upload_item.inspection.id)[:8]}. "
f"Retrying in {upload_item.seconds_until_retry()}s."
)
else:
self._internal_upload_queue.remove(upload_item)
self.logger.error(
f"Storage handler: {type(upload_item.storage_handler).__name__} "
f"exceeded max retries to upload inspection: {str(upload_item.inspection.id)[:8]}. "
"Aborting upload."
f"exceeded max retries to upload inspection: "
f"{str(upload_item.inspection.id)[:8]}. Aborting upload."
)

def _process_upload_queue(self):
Expand Down
3 changes: 2 additions & 1 deletion src/robot_interface/models/geometry/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

class Frame(str, Enum):
"""
Robot frame implies the local frame on the operating robot. This will depend on the robots internal mapping.
Robot frame implies the local frame on the operating robot. This will depend on the
robots internal mapping.
Asset frame is the frame of the asset which the robot operates in.
"""

Expand Down
11 changes: 6 additions & 5 deletions src/robot_interface/models/geometry/orientation.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
@dataclass
class Orientation:
"""
This class represents an orientation using quaternions. The quaternion is used throughout the project.
Methods that utilize Euler angles will all follow the yaw, pitch, roll convention which rotates around the ZYX axis
with intrinsic rotations.
This class represents an orientation using quaternions. The quaternion is used
throughout the project.
Methods that utilize Euler angles will all follow the yaw, pitch, roll convention
which rotates around the ZYX axis with intrinsic rotations.
"""

x: float
Expand Down Expand Up @@ -42,8 +43,8 @@ def as_euler(
wrap_angles: bool = False,
) -> list:
"""
Retrieve the orientation as yaw, pitch, roll Euler coordinates. This function uses the ZYX intrinsic rotations
as standard convention.
Retrieve the orientation as yaw, pitch, roll Euler coordinates. This function
uses the ZYX intrinsic rotations as standard convention.
:param degrees: Set to true to retrieve angles as degrees.
:return: List of euler angles [yaw, pitch, roll]
"""
Expand Down
2 changes: 1 addition & 1 deletion src/robot_interface/models/mission/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class InspectionTask(Task):
@dataclass
class MotionTask(Task):
"""
Base class for all tasks which should cause the robot to move, but not return a result.
Base class for all tasks which should move the robot, but not return a result.
"""

pass
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/turtlebot/test_successful_mission.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def test_successful_mission(

_id: int = 2

response = client.post(
_ = client.post(
f"schedule/start-mission?ID={_id}",
headers={"Authorization": "Bearer {}".format(access_token)},
)
Expand Down
1 change: 0 additions & 1 deletion tests/isar/apis/scheduler/test_scheduler_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import pytest

from isar.apis.security.authentication import Authenticator
from isar.config.settings import robot_settings
from isar.mission_planner.local_planner import LocalPlanner
from isar.mission_planner.mission_planner_interface import MissionPlannerError
from isar.models.communication.messages import (
Expand Down
8 changes: 4 additions & 4 deletions tests/isar/models/communication/test_queues.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ def test_queues(self):
assert queues.start_mission is not None
assert (
queues.start_mission.input is not None
and queues.start_mission.input.maxsize is 1
and queues.start_mission.input.maxsize == 1
)
assert (
queues.start_mission.output is not None
and queues.start_mission.output.maxsize is 1
and queues.start_mission.output.maxsize == 1
)
assert queues.stop_mission is not None
assert (
queues.stop_mission.input is not None
and queues.stop_mission.input.maxsize is 1
and queues.stop_mission.input.maxsize == 1
)
assert (
queues.stop_mission.output is not None
and queues.stop_mission.output.maxsize is 1
and queues.stop_mission.output.maxsize == 1
)
1 change: 0 additions & 1 deletion tests/isar/services/readers/test_base_reader.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from dataclasses import asdict
from pathlib import Path
from typing import Any

import pytest
Expand Down