diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..8dd399a --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +max-line-length = 88 +extend-ignore = E203 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3dec764 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,39 @@ +name: Python CI + +on: + push: + branches: [ master, main ] + pull_request: + branches: [ master, main ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest pytest-mock black + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=88 --statistics + - name: Check formatting with black + run: | + black --check . + - name: Test with pytest + run: | + PYTHONPATH=. pytest tests/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..561c1ff --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +.PHONY: test lint clean + +test: + PYTHONPATH=. .venv/bin/pytest tests/ + +lint: + .venv/bin/flake8 discogs_cli tests + +clean: + rm -rf .pytest_cache + rm -rf discogs_cli/__pycache__ + rm -rf tests/__pycache__ diff --git a/discogs_cli/discogs.py b/discogs_cli/discogs.py index 2c716da..6fee393 100644 --- a/discogs_cli/discogs.py +++ b/discogs_cli/discogs.py @@ -97,7 +97,7 @@ def _separator(self, title): return ( " -- [ " + click.style(title, fg=Discogs.LABEL_COLOUR) - + " ] {line}".format(title=title, line="-" * (MAX - 4 - len(title) - RIGHT)) + + " ] {line}".format(line="-" * (MAX - 4 - len(title) - RIGHT)) ) def _page_artists(self, artists, page=1, end=1): @@ -220,7 +220,8 @@ def show(self): class Artist(Discogs): """ Nightmares On Wax - Members : George Evelyn [640294], Kevin Harper [427445], Robin Taylor-Firth [31653] + Members : George Evelyn [640294], Kevin Harper [427445], + Robin Taylor-Firth [31653] Variations : N O W, N.O.W, N.O.W., Nightmare On Wax, Nights On Wax, NoW In groups : --[ Profile ] ------------------------------------ @@ -364,6 +365,7 @@ def show(self): .get("average") ) ) + out.append(self._separator("Tracklist")) for t in self.discogs.data["tracklist"]: duration = " {0}".format(t.get("duration")) diff --git a/discogs_cli/ext/utils.py b/discogs_cli/ext/utils.py index 230eca3..eeab968 100644 --- a/discogs_cli/ext/utils.py +++ b/discogs_cli/ext/utils.py @@ -16,7 +16,6 @@ import re import shlex -import six from prompt_toolkit.completion import Completion from ..completions import META_LOOKUP @@ -143,19 +142,6 @@ def _find_collection_matches(self, word, collection, fuzzy): name, -len(word), display=display, display_meta=display_meta ) - def _shlex_split(self, text): - """Wrapper for shlex, because it does not seem to handle unicode in 2.6. - - :type text: str - :param text: A string to split. - - :rtype: list - :return: A list that contains words for each split element of text. - """ - if six.PY2: - text = text.encode("utf-8") - return shlex.split(text) - def _safe_split(self, text): """Safely splits the input text. @@ -168,7 +154,6 @@ def _safe_split(self, text): :return: A list that contains words for each split element of text. """ try: - words = self._shlex_split(text) - return words - except: + return shlex.split(text) + except ValueError: return text diff --git a/discogs_cli/main.py b/discogs_cli/main.py index a085b71..742226a 100755 --- a/discogs_cli/main.py +++ b/discogs_cli/main.py @@ -3,13 +3,14 @@ import subprocess import click -from prompt_toolkit import prompt +from prompt_toolkit import PromptSession from prompt_toolkit.history import InMemoryHistory from prompt_toolkit.lexers import PygmentsLexer -from prompt_toolkit.styles.pygments import style_from_pygments_cls +from prompt_toolkit.styles import style_from_pygments_cls from pygments.styles import get_style_by_name from .__init__ import __version__ + # from .completion import command_completer from .ext.completer import Completer from .ext.utils import TextUtils @@ -31,28 +32,28 @@ def execute(cmd): def cli(): history = InMemoryHistory() + session = PromptSession(history=history) style = style_from_pygments_cls(get_style_by_name("monokai")) lexer = PygmentsLexer(DiscogsCliLexer) completer = Completer(fuzzy_match=False, text_utils=TextUtils()) SYNTAX = "Syntax: ogs [options]" - click.secho(" _ _ _ _ ", fg="yellow") - click.secho(" __| (_)___ ___ ___ __ _ ___ ___| (_)", fg="yellow") - click.secho(" / _` | / __|/ __/ _ \ / _` / __|_____ / __| | |", fg="yellow") - click.secho("| (_| | \__ \ (_| (_) | (_| \__ \_____| (__| | |", fg="yellow") - click.secho(" \__,_|_|___/\___\___/ \__, |___/ \___|_|_|", fg="yellow") - click.secho(" |___/", fg="yellow") + click.secho(r" _ _ _ _ ", fg="yellow") + click.secho(r" __| (_)___ ___ ___ __ _ ___ ___| (_)", fg="yellow") + click.secho(r" / _` | / __|/ __/ _ \ / _` / __|_____ / __| | |", fg="yellow") + click.secho(r"| (_| | \__ \ (_| (_) | (_| \__ \_____| (__| | |", fg="yellow") + click.secho(r" \__,_|_|___/\___\___/ \__, |___/ \___|_|_|", fg="yellow") + click.secho(r" |___/", fg="yellow") click.echo("Version:" + __version__) click.echo(SYNTAX) while True: try: - text = prompt( + text = session.prompt( "discogs-cli >>> ", style=style, - history=history, completer=completer, lexer=lexer, ) diff --git a/requirements.txt b/requirements.txt index ab34b12..eb9f579 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,6 +7,5 @@ oauthlib==3.2.2 prompt-toolkit==3.0.43 Pygments==2.17.2 requests==2.31.0 -six==1.16.0 urllib3==2.1.0 wcwidth==0.2.13 diff --git a/setup.py b/setup.py index d803753..ab7e52b 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,7 @@ "Pygments>=2.15.0", "click>=6.7", "discogs-client>=2.2.1", - "prompt-toolkit>=1.0.13", + "prompt-toolkit>=3.0.0", "requests>=2.31.0", ], entry_points={ @@ -43,8 +43,9 @@ classifiers=[ "Environment :: Console", "Development Status :: 5 - Production/Stable", - "Programming Language :: Python :: 2.6", - "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], diff --git a/tests/test_utils.py b/tests/test_utils.py new file mode 100644 index 0000000..7ffbf08 --- /dev/null +++ b/tests/test_utils.py @@ -0,0 +1,30 @@ +from discogs_cli.ext.utils import TextUtils + + +class TestTextUtils: + def setup_method(self): + self.utils = TextUtils() + + def test_get_tokens(self): + text = "ogs artist 123" + tokens = self.utils.get_tokens(text) + assert tokens == ["ogs", "artist", "123"] + + def test_get_tokens_quoted(self): + text = 'ogs search "massive attack"' + tokens = self.utils.get_tokens(text) + assert tokens == ["ogs", "search", "massive attack"] + + def test_last_token(self): + text = "ogs artist" + assert self.utils._last_token(text) == "artist" + + def test_safe_split_valid(self): + text = "hello world" + assert self.utils._safe_split(text) == ["hello", "world"] + + def test_safe_split_invalid_quote(self): + # shlex raises ValueError on unclosed quotes + text = 'hello "world' + # The updated code catches ValueError and returns the original text + assert self.utils._safe_split(text) == text diff --git a/tests/test_version.py b/tests/test_version.py new file mode 100644 index 0000000..73cf690 --- /dev/null +++ b/tests/test_version.py @@ -0,0 +1,5 @@ +from discogs_cli import __version__ + + +def test_version(): + assert __version__ is not None