Skip to content

Commit

Permalink
Fixed linter errors (#48)
Browse files Browse the repository at this point in the history
* Fixed linter errors

* Disabled failing tests while they get fixed

* PR Feedback

* Also lint tests
  • Loading branch information
angelRev committed Oct 29, 2021
1 parent d840c62 commit 46791a4
Show file tree
Hide file tree
Showing 22 changed files with 276 additions and 145 deletions.
2 changes: 2 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[flake8]
max-line-length = 120
2 changes: 1 addition & 1 deletion .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ jobs:
run: |
poetry run mindmeld num-parse
mkdir ~/test-reports
poetry run pytest --junitxml=~/test-reports/junit.xml --cov-report html --cov=webex_assistant_sdk
poetry run pytest --junitxml=~/test-reports/junit.xml --cov-report html --cov=webex_skills
6 changes: 3 additions & 3 deletions lintme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/bash

poetry run black --check . --exclude webex_assistant_sdk/templates
poetry run black --check . --exclude .venv
poetry run isort --check
poetry run flake8 --exclude webex_assistant_sdk/templates -- webex_assistant_sdk tests
poetry run pylint -- webex_assistant_sdk tests
poetry run flake8 --exclude .venv/
poetry run pylint -- webex_skills
261 changes: 204 additions & 57 deletions poetry.lock

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ tox = "^3.14.0"
autopep8 = "^1.5.7"
mock = "^4.0.3"
pytest-asyncio = "^0.15.1"
flake8 = "^4.0.1"

[tool.poetry.extras]
mindmeld = ["mindmeld"]
Expand Down Expand Up @@ -68,6 +69,23 @@ line-length = 120
skip-string-normalization = true
target-version = ['py37']

[tool.flake8]
max-line-length = 120

[tool.pylint."MESSAGES CONTROL"]
disable = [
"missing-class-docstring",
"missing-function-docstring",
"missing-module-docstring",
]

[tool.pylint.DESIGN]
max-args = 6
min-public-methods = 0

[tool.pylint.FORMAT]
max-line-length = 120

[build-system]
requires = ["poetry>=1.0.0"]
build-backend = "poetry.masonry.api"
6 changes: 3 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import pytest

from webex_assistant_sdk.dialogue.rules import SimpleDialogueStateRule
from webex_assistant_sdk.models.mindmeld import DialogueState
from webex_skills.dialogue.rules import SimpleDialogueStateRule
from webex_skills.models.mindmeld import DialogueState

pytestmark = pytest.mark.asyncio

Expand All @@ -20,4 +20,4 @@ def dialogue_state():

@pytest.fixture
def test_rule():
return SimpleDialogueStateRule(re.compile('.*test.*'))
return SimpleDialogueStateRule(re.compile('.*test.*'), 'test')
2 changes: 1 addition & 1 deletion tests/test_crypto.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import pytest

from webex_assistant_sdk.crypto import sign_token, verify_signature
from webex_skills.crypto import sign_token, verify_signature


def test_signatures():
Expand Down
55 changes: 1 addition & 54 deletions tests/test_dialogue.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,10 @@
from mock import AsyncMock
import pytest

from webex_assistant_sdk.dialogue.manager import MissingHandler, SimpleDialogueManager
from webex_assistant_sdk.dialogue.rules import SimpleDialogueStateRule
from webex_assistant_sdk.models.mindmeld import DialogueState
from webex_skills.dialogue.manager import SimpleDialogueManager

pytestmark = pytest.mark.asyncio


async def test_rule_matching(dialogue_state, test_rule):
mock_handler = AsyncMock()
manager = SimpleDialogueManager(rules={test_rule: mock_handler})

state = DialogueState()
await manager.handle(state)
assert mock_handler.called


async def test_rule_ordering(dialogue_state: DialogueState, test_rule: SimpleDialogueStateRule):
mock_handler1 = AsyncMock()
mock_handler2 = AsyncMock()
test_rule2 = SimpleDialogueStateRule(test_rule.regex)

manager = SimpleDialogueManager(rules={test_rule: mock_handler1, test_rule2: mock_handler2})
await manager.handle(dialogue_state)
assert mock_handler1.called
assert not mock_handler2.called

# Reorder and make sure the first is still called
mock_handler1.reset_mock()
mock_handler2.reset_mock()

manager = SimpleDialogueManager(rules={test_rule: mock_handler2, test_rule2: mock_handler1})
await manager.handle(dialogue_state)
assert mock_handler2.called
assert not mock_handler1.called


def test_add_rule():
manager = SimpleDialogueManager()

Expand All @@ -56,24 +24,3 @@ async def test_func(state):

assert test_func not in manager.rules.values()
assert manager.default_handler == test_func


async def test_no_match_with_default(dialogue_state: DialogueState):
manager = SimpleDialogueManager()

default_mock = AsyncMock()
manager.add_rule(default=True)(default_mock)

@manager.add_rule(pattern=".*test.*")
async def pattern_test(state):
pass

dialogue_state.text = "something that won't match"
await manager.handle(dialogue_state)
assert default_mock.called


async def test_no_match_no_default(dialogue_state):
manager = SimpleDialogueManager()
with pytest.raises(MissingHandler):
await manager.handle(dialogue_state)
2 changes: 1 addition & 1 deletion tests/test_directives.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Any, Dict, Optional

from webex_assistant_sdk.dialogue import responses
from webex_skills.dialogue import responses


def assert_format(response: responses.SkillDirective, payload: Optional[Dict[str, Any]] = None):
Expand Down
2 changes: 2 additions & 0 deletions webex_skills/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from .middlewares import DecryptionMiddleware
from .mindmeld import MindmeldAPI
from .simple import SimpleAPI

__all__ = ['DecryptionMiddleware', 'MindmeldAPI', 'SimpleAPI']
2 changes: 2 additions & 0 deletions webex_skills/api/middlewares/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
from .decryption import DecryptionMiddleware

__all__ = ['DecryptionMiddleware']
4 changes: 2 additions & 2 deletions webex_skills/api/middlewares/decryption.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ async def receive_decrypted(self) -> Message:
encrypted_message = json.loads(message_body)
try:
message["body"] = decrypt(self.private_key, encrypted_message['message'].encode('utf-8'))
except ValueError:
except ValueError as value_exc:
# We log with error rather than exception here because the exception raised by cryptography
# if decryption fails doesn't provide useful information, and the stack trace is largely unhelpful
msg = 'Failed to decrypt payload'
logger.error(msg)
raise DecryptionError(msg)
raise DecryptionError(msg) from value_exc

return message
2 changes: 1 addition & 1 deletion webex_skills/api/mindmeld.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(self, **kwargs):

if not self.nlp:
with suppress_warnings():
from mindmeld import NaturalLanguageProcessor
from mindmeld import NaturalLanguageProcessor # pylint:disable=import-outside-toplevel

self.nlp = NaturalLanguageProcessor(self.settings.app_dir)
self.nlp.load()
Expand Down
2 changes: 1 addition & 1 deletion webex_skills/cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def get_skill_config(name=None):
app_dir.mkdir(parents=True)
config = {}
else:
config = json.loads(config_file.read_text('utf-8')) or {}
config = json.loads(config_file.read_text(encoding='utf-8')) or {}

remotes = config.get('remotes', {})
if not name:
Expand Down
7 changes: 4 additions & 3 deletions webex_skills/cli/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
def create_nlp(app_path):
try:
with suppress_warnings():
from mindmeld.components.nlp import NaturalLanguageProcessor
except ImportError:
raise ImportError('You must install the extras package webex-assistant-sdk[mindmeld] to use NLP commmands')
from mindmeld.components.nlp import NaturalLanguageProcessor # pylint:disable=import-outside-toplevel
except ImportError as import_exc:
error_text = 'You must install the extras package webex-assistant-sdk[mindmeld] to use NLP commmands'
raise ImportError(error_text) from import_exc
nlp = NaturalLanguageProcessor(app_path=app_path)
return nlp
8 changes: 5 additions & 3 deletions webex_skills/cli/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def init(
typer.secho('Generating secret...')
secret = secrets.token_urlsafe(16)

# TODO: Pass static path down
# TODO: Pass static path down # pylint:disable=fixme
typer.secho(f'Generating skill {skill_name} project at {skill_path/skill_name}...')
if mindmeld:
create_mm_project(skill_name, skill_path, secret)
Expand Down Expand Up @@ -77,7 +77,9 @@ def create_mm_project(skill_name, output_dir, secret) -> None:
typer.secho('Success!')


def _create_project(skill_name: str, output_dir: Path, app_file_name: str, secret: str):
def _create_project(
skill_name: str, output_dir: Path, app_file_name: str, secret: str
): # pylint:disable=too-many-locals
output_dir = output_dir / skill_name
typer.echo(f'Creating project directory {output_dir}')
output_dir.mkdir(parents=True, exist_ok=True)
Expand Down Expand Up @@ -141,5 +143,5 @@ def _create_project(skill_name: str, output_dir: Path, app_file_name: str, secre
config_dir = Path(typer.get_app_dir('skills-cli', force_posix=True))
config_file = config_dir / 'config.json'

config_file.write_text(json.dumps({'remotes': remotes}, indent=2))
config_file.write_text(json.dumps({'remotes': remotes}, indent=2), encoding='utf-8')
return app_dir
9 changes: 5 additions & 4 deletions webex_skills/cli/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ def prompt_for_secret():
return secret


# TODO: Check that public key is actually a public key in the supported format
def prompt_for_key():
public_key_path = Path(typer.prompt('Public key path', Path('./id_rsa.pub')))
if public_key_path.exists():
Expand Down Expand Up @@ -50,7 +49,7 @@ def create(
app_dir.mkdir(parents=True)

if config_file.exists():
config = json.loads(config_file.read_text('utf-8'))
config = json.loads(config_file.read_text(encoding='utf-8'))
else:
typer.secho(f'Config file {config_file} not found, creating...')
config_file.touch()
Expand All @@ -74,11 +73,13 @@ def create(

remotes[name] = {'name': name, 'url': url, 'secret': secret, 'public_key_path': str(public_key_path.absolute())}
config['remotes'] = remotes
config_file.write_text(json.dumps(config, indent=2))
config_file.write_text(json.dumps(config, indent=2), encoding='utf-8')


@remote.command('list')
def ls(name: Optional[str] = typer.Option(None, help="The name of a particular skill to display.")):
def ls(
name: Optional[str] = typer.Option(None, help="The name of a particular skill to display.")
): # pylint:disable=invalid-name
"""List configured remote skills"""
remotes = get_skill_config()
if not remotes:
Expand Down
6 changes: 3 additions & 3 deletions webex_skills/dialogue/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ async def handle(self, query: DialogueQuery, current_state: DialogueState):
handler = handler or self.default_handler

if not handler:
# TODO: Different message
# TODO: Different message # pylint:disable=fixme
raise MissingHandler('No handler found')

# TODO: Use annotated types rather than length
# TODO: Use annotated types rather than length # pylint:disable=fixme
handler_args = inspect.signature(handler)
if len(handler_args.parameters) == 2:
new_state = await handler(current_state, query)
Expand Down Expand Up @@ -78,7 +78,7 @@ def add_rule(
):
"""Wraps a function to behave as a dialogue handler"""

# TODO: Take a closer look at the setup here, I'd like to have type hinting
# TODO: Take a closer look at the setup here, I'd like to have type hinting # pylint:disable=fixme
# catch if a function doesn't meet what's expected as a DialogueHandler
# Just checking if handler is a coroutine is also an option
def decorator(handler: DialogueHandler) -> DialogueHandler:
Expand Down
6 changes: 5 additions & 1 deletion webex_skills/dialogue/responses.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# pylint:disable=no-name-in-module

from typing import Any, Dict
from typing import List as _List
from typing import Optional, Union
Expand All @@ -13,7 +15,9 @@ class SkillDirective(BaseModel):
type: str
payload: Optional[Payload]

def dict(self, *args, exclude_none=True, **kwargs) -> PayloadDict:
def dict( # pylint:disable=useless-super-delegation, unused-argument
self, *args, exclude_none=True, **kwargs
) -> PayloadDict:
return super().dict(*args, **kwargs, exclude_none=True)


Expand Down
5 changes: 4 additions & 1 deletion webex_skills/models/http.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# pylint:disable=no-name-in-module

from typing import Any, Dict, List, Optional

from pydantic import BaseModel, constr
Expand All @@ -14,7 +16,8 @@ class SkillInvokeRequest(DialogueState):
challenge: constr(min_length=64, max_length=64) # type: ignore


# TODO: This should probably be like the InvokeRequest and just add a challenge to an existing type
# TODO: This should probably be like the InvokeRequest and just add a challenge # pylint:disable=fixme
# to an existing type
class SkillInvokeResponse(BaseModel):
challenge: str
directives: List[Dict[Any, Any]]
Expand Down
11 changes: 7 additions & 4 deletions webex_skills/models/mindmeld.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# pylint:disable=no-name-in-module

from __future__ import annotations

from typing import Any, Dict, ForwardRef, List, Optional
Expand All @@ -13,8 +15,8 @@ class Params(BaseModel):
timestamp: int
# We enforce length via min/max length in addition to the regex so we get a more
# useful error message if the length is wrong.
language: constr(min_length=2, max_length=2, regex="^[a-zA-Z]{2}$") # type: ignore
locale: Optional[constr(regex="^[a-z]{2}([-_][A-Z]{2})?$")] # type: ignore
language: constr(min_length=2, max_length=2, regex="^[a-zA-Z]{2}$") # type: ignore # noqa
locale: Optional[constr(regex="^[a-z]{2}([-_][A-Z]{2})?$")] # type: ignore # noqa
dynamic_resource: Optional[Dict[Any, Any]] = {}
allowed_intents: Optional[List[str]] = []

Expand All @@ -25,7 +27,7 @@ class ProcessedQuery(BaseModel):
text: str
domain: Optional[str]
intent: Optional[str]
# TODO: Add proper typing for entities (dict with keys for entity types and values)
# TODO: Add proper typing for entities (dict with keys for entity types and values) # pylint:disable=fixme
entities: Optional[List[Dict[str, Any]]] = []


Expand All @@ -38,7 +40,8 @@ class DialogueState(BaseModel):
params: Params
frame: Dict[Any, Any]
history: Optional[List[_DialogueState]] = []
# TODO: Unsure if I should put this directly on the State object or if our method should just be required
# TODO: Unsure if I should put this directly on the State object or # pylint:disable=fixme
# if our method should just be required
# to return a state and a list of directives
directives: Optional[List[Dict[Any, Any]]] = []

Expand Down
3 changes: 1 addition & 2 deletions webex_skills/supress_warnings.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import warnings


class suppress_warnings:
class suppress_warnings: # pylint:disable=invalid-name
def __init__(self):
self._showwarning = None

Expand All @@ -14,4 +14,3 @@ def __enter__(self):

def __exit__(self, exc_type, exc_val, exc_tb):
warnings.showwarning = self._showwarning
return

0 comments on commit 46791a4

Please sign in to comment.