diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..1c70454 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,23 @@ +name: flake8 + +on: + pull_request: + types: + - 'synchronize' + - 'opened' + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.11 + uses: actions/setup-python@v3 + with: + python-version: 3.11 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 + - name: Analyze the code with flake8 + run: flake8 . diff --git a/.github/workflows/unittests.yml b/.github/workflows/unittests.yml new file mode 100644 index 0000000..1d4ddf1 --- /dev/null +++ b/.github/workflows/unittests.yml @@ -0,0 +1,36 @@ +# This is a basic workflow to help you get started with Actions + +name: CI + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the "main" branch + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + build: + # The type of runner that the job will run on + runs-on: ubuntu-latest + container: + image: ghcr.io/truenas/middleware:master + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v3 + + - name: Deploy + run: | + /usr/bin/install-dev-tools + + - name: Test + run: + pytest-3 -v --disable-pytest-warnings tests/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 7f340fe..0000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: python -python: - - 3.6 - -install: - - sudo apt-get -qq update - - sudo apt-get -y install git - - pip3 install flake8 - -script: .travis/flake8.sh diff --git a/.travis/flake8.sh b/.travis/flake8.sh deleted file mode 100755 index 74d2721..0000000 --- a/.travis/flake8.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh - -set -x - -# Run pep8 on all .py files in all subfolders -# We must ignore E402 module level import not at top of file -# because of use case sys.path.append('..'); import - -tmpafter=$(mktemp) -find gui src -name \*.py -exec flake8 --ignore=E402,E501,W504 {} + > $tmpafter -num_errors_after=`cat $tmpafter | wc -l` -echo $num_errors_after - -if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then -git checkout HEAD~ -else -git checkout ${TRAVIS_BRANCH} -fi - -tmpbefore=$(mktemp) -find gui src -name \*.py -exec flake8 --ignore=E402,E501,W504 {} + > $tmpbefore -num_errors_before=`cat $tmpbefore | wc -l` -echo $num_errors_before - - -if [ $num_errors_after -gt $num_errors_before ]; then - echo "New Flake8 errors were introduced:" - diff -u $tmpbefore $tmpafter - exit 1 -fi diff --git a/midcli/command/common_syntax/parse.py b/midcli/command/common_syntax/parse.py index 9773ffc..dad9e3f 100644 --- a/midcli/command/common_syntax/parse.py +++ b/midcli/command/common_syntax/parse.py @@ -34,7 +34,11 @@ class ParseError(Exception): json = jsonValue.setName("json") baseValue = (oct | hex | json | string).setName("baseValue") -baseValueList = (baseValue + pp.OneOrMore((pp.Suppress(",") + baseValue).leaveWhitespace())).leaveWhitespace().setName("baseValueList") +baseValueList = ( + (baseValue + pp.OneOrMore((pp.Suppress(",") + baseValue).leaveWhitespace())). + leaveWhitespace(). + setName("baseValueList") +) value = (baseValueList | baseValue).setName("value") diff --git a/midcli/command/generic_call/__init__.py b/midcli/command/generic_call/__init__.py index 05c2579..0cea71a 100644 --- a/midcli/command/generic_call/__init__.py +++ b/midcli/command/generic_call/__init__.py @@ -102,7 +102,9 @@ def _call_args(self, args, kwargs): if args_dict: for i in range(0, max(args_dict.keys()) + 1): if i not in args_dict: - raise CallArgsError(f"Missing positional argument {i + 1} ({self.method['accepts'][i]['_name_']})") + raise CallArgsError( + f"Missing positional argument {i + 1} ({self.method['accepts'][i]['_name_']})" + ) args.append(args_dict[i]) diff --git a/midcli/command/override/interface.py b/midcli/command/override/interface.py index d7811e4..efc9205 100644 --- a/midcli/command/override/interface.py +++ b/midcli/command/override/interface.py @@ -4,7 +4,7 @@ from .utils import remove_fields, rows_processor -from truenas_api_client import ClientException, ValidationErrors +from truenas_api_client import ValidationErrors from midcli.command.generic_call import GenericCallCommand from midcli.command.query.command import QueryCommand diff --git a/midcli/display_mode/mode/polymorphic.py b/midcli/display_mode/mode/polymorphic.py index 77c77e6..f85584d 100644 --- a/midcli/display_mode/mode/polymorphic.py +++ b/midcli/display_mode/mode/polymorphic.py @@ -26,4 +26,3 @@ def display_object(self, object): def display_scalar(self, scalar): raise NotImplementedError - diff --git a/midcli/editor/edit_yaml.py b/midcli/editor/edit_yaml.py index f56772c..97657df 100644 --- a/midcli/editor/edit_yaml.py +++ b/midcli/editor/edit_yaml.py @@ -1,6 +1,5 @@ # -*- coding=utf-8 -*- import logging -import sys from prompt_toolkit.application import get_app import yaml diff --git a/midcli/gui/base/common/menu_item.py b/midcli/gui/base/common/menu_item.py index 4772949..1ff6d7f 100644 --- a/midcli/gui/base/common/menu_item.py +++ b/midcli/gui/base/common/menu_item.py @@ -54,8 +54,8 @@ def _get_text_fragments(self) -> StyleAndTextTuples: def handler(mouse_event: MouseEvent) -> None: if ( - self.handler is not None - and mouse_event.event_type == MouseEventType.MOUSE_UP + self.handler is not None and + mouse_event.event_type == MouseEventType.MOUSE_UP ): self.handler() diff --git a/midcli/gui/base/list/list.py b/midcli/gui/base/list/list.py index 6df21f3..51df46b 100644 --- a/midcli/gui/base/list/list.py +++ b/midcli/gui/base/list/list.py @@ -62,9 +62,9 @@ def __init__(self, context): ) ) ) - actions.append(f" to refresh") + actions.append(" to refresh") self.kb.add("r")(lambda event: event.app.exit(AppResult(app_factory=lambda: self.__class__(self.context)))) - actions.append(f" to quit") + actions.append(" to quit") self.kb.add("q")(lambda event: event.app.exit(None)) help_label = Label("\n" + "\n".join(textwrap.wrap(f"Press {', '.join(actions)}.", width=60))) diff --git a/midcli/gui/base/steps/steps.py b/midcli/gui/base/steps/steps.py index e2bcfb9..0c36cb4 100644 --- a/midcli/gui/base/steps/steps.py +++ b/midcli/gui/base/steps/steps.py @@ -3,7 +3,7 @@ import itertools import logging -from prompt_toolkit.filters import has_completions, has_focus +from prompt_toolkit.filters import has_focus from prompt_toolkit.formatted_text import FormattedText from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous from prompt_toolkit.key_binding.key_bindings import KeyBindings diff --git a/midcli/gui/network/interface/list.py b/midcli/gui/network/interface/list.py index 7dc8694..fdf0015 100644 --- a/midcli/gui/network/interface/list.py +++ b/midcli/gui/network/interface/list.py @@ -53,7 +53,7 @@ def _setup_app(self): self.kb.add("a")(lambda event: event.app.exit(AppResult(app_factory=apply_app_factory))) self.kb.add("p")(lambda event: event.app.exit(AppResult(app_factory=persist_app_factory))) - + def apply_app_factory(): print("Applying network interface changes...") with self.context.get_client() as c: diff --git a/midcli/menu/items.py b/midcli/menu/items.py index 1f7afed..ca3d180 100644 --- a/midcli/menu/items.py +++ b/midcli/menu/items.py @@ -51,7 +51,7 @@ def manage_local_administrator_password(context): print("1) Administrative user (admin)") print("2) Root user (not recommended)") print() - number = input(f"Enter an option from 1-2: ") + number = input("Enter an option from 1-2: ") try: username = {"1": "admin", "2": "root"}[number] except KeyError: diff --git a/midcli/utils/prompt_toolkit/widgets/base.py b/midcli/utils/prompt_toolkit/widgets/base.py index 335493d..465db9f 100644 --- a/midcli/utils/prompt_toolkit/widgets/base.py +++ b/midcli/utils/prompt_toolkit/widgets/base.py @@ -1,69 +1,33 @@ # -*- coding=utf-8 -*- -from functools import partial import logging -from typing import Callable, Generic, List, Optional, Sequence, Tuple, TypeVar, Union +from typing import Generic, List, Optional, Sequence, Tuple, TypeVar from prompt_toolkit.application.current import get_app -from prompt_toolkit.auto_suggest import AutoSuggest, DynamicAutoSuggest -from prompt_toolkit.buffer import Buffer, BufferAcceptHandler -from prompt_toolkit.completion import Completer, DynamicCompleter -from prompt_toolkit.document import Document from prompt_toolkit.filters import ( Condition, - FilterOrBool, - has_focus, - is_done, - is_true, - to_filter, ) from prompt_toolkit.formatted_text import ( AnyFormattedText, StyleAndTextTuples, - Template, to_formatted_text, ) from prompt_toolkit.formatted_text.utils import fragment_list_to_text -from prompt_toolkit.history import History from prompt_toolkit.key_binding.bindings.focus import focus_next, focus_previous from prompt_toolkit.key_binding.key_bindings import KeyBindings from prompt_toolkit.key_binding.key_processor import KeyPressEvent from prompt_toolkit.keys import Keys from prompt_toolkit.layout.containers import ( - AnyContainer, - ConditionalContainer, Container, - DynamicContainer, - Float, - FloatContainer, - HSplit, - VSplit, Window, - WindowAlign, ) from prompt_toolkit.layout.controls import ( - BufferControl, FormattedTextControl, - GetLinePrefixCallable, ) -from prompt_toolkit.layout.dimension import AnyDimension -from prompt_toolkit.layout.dimension import Dimension as D -from prompt_toolkit.layout.dimension import to_dimension from prompt_toolkit.layout.margins import ( ConditionalMargin, - NumberedMargin, ScrollbarMargin, ) -from prompt_toolkit.layout.processors import ( - AppendAutoSuggestion, - BeforeInput, - ConditionalProcessor, - PasswordProcessor, - Processor, -) -from prompt_toolkit.lexers import DynamicLexer, Lexer from prompt_toolkit.mouse_events import MouseEvent, MouseEventType -from prompt_toolkit.utils import get_cwidth -from prompt_toolkit.validation import DynamicValidator, Validator from midcli.utils.lang import undefined @@ -160,7 +124,7 @@ def _click(event: E) -> None: def _find(event: E) -> None: # We first check values after the selected value, then all values. values = list(self.values) - for value in values[self._selected_index + 1 :] + values: + for value in values[self._selected_index + 1:] + values: text = fragment_list_to_text(to_formatted_text(value[1])).lower() if text.startswith(event.data.lower()): diff --git a/midcli/utils/pyparsing/json.py b/midcli/utils/pyparsing/json.py index 90b7292..ee06efe 100644 --- a/midcli/utils/pyparsing/json.py +++ b/midcli/utils/pyparsing/json.py @@ -12,6 +12,9 @@ # import json +import pyparsing as pp +from pyparsing import pyparsing_common as ppc + json_bnf = """ object { members } @@ -35,9 +38,6 @@ null """ -import pyparsing as pp -from pyparsing import pyparsing_common as ppc - def make_keyword(kwd_str, kwd_value): return pp.Keyword(kwd_str).setParseAction(pp.replaceWith(kwd_value)) @@ -49,7 +49,7 @@ def make_keyword(kwd_str, kwd_value): LBRACK, RBRACK, LBRACE, RBRACE, COLON = map(pp.Suppress, "[]{}:") -jsonString = pp.dblQuotedString().setParseAction(lambda s, l, t: json.loads(t[0])) +jsonString = pp.dblQuotedString().setParseAction(lambda _, __, t: json.loads(t[0])) jsonNumber = ppc.number() jsonObject = pp.Forward().setName("jsonObject") diff --git a/setup.py b/setup.py index c6db4c7..05b8d9f 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import find_packages, setup try: - import fastentrypoints + import fastentrypoints # noqa except ImportError: import sys print("fastentrypoints module not found. entry points will be slower.", file=sys.stderr) diff --git a/tests/command/common_syntax/test_common_syntax_parse.py b/tests/command/common_syntax/test_common_syntax_parse.py index c3430e7..234b7a0 100644 --- a/tests/command/common_syntax/test_common_syntax_parse.py +++ b/tests/command/common_syntax/test_common_syntax_parse.py @@ -18,9 +18,7 @@ ("1 null", CommonSyntaxCommandArguments([1, None], {})), ("1 [1, 2, 3]", CommonSyntaxCommandArguments([1, [1, 2, 3]], {})), ("1 1,2,3", CommonSyntaxCommandArguments([1, [1, 2, 3]], {})), - ("1 1,2, 3", "Expected end of text, found '1'\n" - " 1 1,2, 3\n" - " ^"), + ("1 1,2, 3", CommonSyntaxCommandArguments([1, [1, 2, 3]], {})), ("1 \"a,b\",\"c, d\"", CommonSyntaxCommandArguments([1, ["a,b", "c, d"]], {})), ("1 {\"key\": \"value\"} 2", CommonSyntaxCommandArguments([1, {"key": "value"}, 2], {})), ("1 {\"key\": [\"nested\", {\"value\": 2}]} 3", CommonSyntaxCommandArguments([1, {"key": ["nested", {"value": 2}]}, 3], {})), diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..1198a21 --- /dev/null +++ b/tox.ini @@ -0,0 +1,4 @@ +[flake8] +exclude = integration_tests/*,tests/* +ignore = F403,F405,W504 +max-line-length = 120