diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..f4952ba --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: +- package-ecosystem: pip + directory: "/" + schedule: + interval: daily + time: "10:00" + open-pull-requests-limit: 10 diff --git a/.pylintrc b/.pylintrc index ccf3cb6..1db55e1 100644 --- a/.pylintrc +++ b/.pylintrc @@ -10,8 +10,6 @@ ignore=conf.py init-hook="from pylint.config import find_pylintrc; import os, sys; sys.path.append(os.path.dirname(find_pylintrc()))" [pre-commit-hook] -# C0122: Misplaced comparison constant (tests use Yoda conditions). -# C0330: Wrong hanging indentation (conflicts with `black`). # W0621: Redefining name from outer scope (conflicts with pytest fixtures). -disable=C0122,C0330,W0621 -limit=10.0 +disable=W0621 +limit=10 diff --git a/.travis.yml b/.travis.yml index 4f83f82..2d73e11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,6 @@ python: - "3.8" - "3.7" - "3.6" - - "3.5" # Disabled until typed_ast (used by pylint) is fixed. # See https://github.com/python/typed_ast/issues/134 # - "pypy3" diff --git a/countdoom/cli.py b/countdoom/cli.py index c2e03da..a390f0d 100644 --- a/countdoom/cli.py +++ b/countdoom/cli.py @@ -105,7 +105,7 @@ def check_positive(value) -> int: int_value = int(value) if int_value <= 0: raise argparse.ArgumentTypeError( - "{} is an invalid positive integer value".format(value) + f"{value} is an invalid positive integer value" ) return int_value @@ -127,7 +127,7 @@ def check_positive(value) -> int: '--v', '--version', action='version', - version='%(prog)s {version}'.format(version=countdoom.__version__), + version=f'%(prog)s {countdoom.__version__}', ) parser.add_argument( '-h', diff --git a/countdoom/client.py b/countdoom/client.py index 9baf7f1..35060bc 100644 --- a/countdoom/client.py +++ b/countdoom/client.py @@ -160,17 +160,21 @@ async def _fetch(self, url: str, timeout: int = None) -> str: raise CountdoomClientError("Session not started.") try: - async with async_timeout.timeout(timeout): - async with self._session.get(url, timeout=timeout) as resp: - if resp.status != 200: - raise AssertionError - return await resp.text() - except AssertionError: + async with ( + async_timeout.timeout(timeout), + self._session.get(url, timeout=timeout) as resp + ): + if resp.status != 200: + raise AssertionError + return await resp.text() + except AssertionError as exc: await self.close() - raise CountdoomClientError("Page not found.") - except OSError: + raise CountdoomClientError("Page not found.") from exc + except OSError as exc: await self.close() - raise CountdoomClientError("Cannot connect to website. Check URL.") + raise CountdoomClientError( + "Cannot connect to website. Check URL." + ) from exc async def _extract_sentence(self) -> None: """ @@ -187,20 +191,20 @@ async def _extract_sentence(self) -> None: self._sentence = re.sub(r'\s+', ' ', sentence).strip() if not self._sentence: raise ValueError() - except IndexError: + except IndexError as exc: _LOGGER.error( "No sentence found using selector %s. Check for source " "website design changes.", self.SELECTOR, ) - raise CountdoomClientError("No sentence found.") - except ValueError: + raise CountdoomClientError("No sentence found.") from exc + except ValueError as exc: _LOGGER.error( "Empty sentence found using selector %s. Check for source " "website design changes.", self.SELECTOR, ) - raise CountdoomClientError("Empty sentence found.") + raise CountdoomClientError("Empty sentence found.") from exc _LOGGER.debug("Sentence found: %s", self._sentence) async def close(self) -> None: @@ -223,11 +227,11 @@ def _sentence_to_countdown(self) -> None: try: self._countdown = self.sentence_to_countdown(self._sentence) - except AttributeError: + except AttributeError as exc: _LOGGER.error( "Regex pattern yielded no result for : %s", self._sentence ) - raise CountdoomClientError("Sentence not parsable.") + raise CountdoomClientError("Sentence not parsable.") from exc _LOGGER.debug("Countdown value: %s", self._countdown) @classmethod diff --git a/setup.cfg b/setup.cfg index fc7ad27..1bb3986 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,7 +30,8 @@ ignore_missing_imports = true # Configure pytest # See https://docs.pytest.org/ [tool:pytest] -collect_ignore = ['setup.py'] -addopts = --strict +addopts = + --ignore=setup.py, + --strict-markers markers = live diff --git a/setup.py b/setup.py index 2ae4a47..8d2b2d3 100644 --- a/setup.py +++ b/setup.py @@ -15,10 +15,10 @@ from setuptools import find_packages, setup # Load documentation for PyPI. -with open('README.rst') as readme_file: +with open('README.rst', encoding="utf-8") as readme_file: README = readme_file.read() -with open('CHANGELOG.rst') as history_file: +with open('CHANGELOG.rst', encoding="utf-8") as history_file: HISTORY = history_file.read() # Define extra requirements for different installation environments. @@ -88,9 +88,9 @@ # Define URLs. DOCS_URL = 'https://countdoom.readthedocs.io/' -HISTORY_URL = '{}en/latest/changelog.html'.format(DOCS_URL) +HISTORY_URL = f'{DOCS_URL}en/latest/changelog.html' REPO_URL = 'https://github.com/renemarc/countdoom' -ISSUES_URL = '{}/issues'.format(REPO_URL) +ISSUES_URL = f'{REPO_URL}/issues' setup( author='René-Marc Simard', diff --git a/tests/test_cli.py b/tests/test_cli.py index 8c8dfe3..d603725 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -23,7 +23,7 @@ def _setup_mock_server(mocked: aioresponses) -> None: :param mocked: aiohttp response """ - prefix = '

'.format(CountdoomClient.SELECTOR[1:]) + prefix = f'

' suffix = '

' string = "IT IS 1 MINUTE TO MIDNIGHT" mocked.get( @@ -119,7 +119,7 @@ async def test_cli_request_version(capsys: CaptureFixture) -> None: assert exception.type == SystemExit assert exception.value.code == 0 - assert "countdoom {}".format(countdoom.__version__) in output[0] + assert f"countdoom {countdoom.__version__}" in output[0] @pytest.mark.asyncio @@ -271,7 +271,7 @@ async def test_cli_request_internal_error(capsys: CaptureFixture) -> None: :param capsys: System-level capture fixture """ with aioresponses() as mocked: - prefix = '

'.format(CountdoomClient.SELECTOR[1:]) + prefix = f'

' suffix = '

' string = ' ' mocked.get( diff --git a/tests/test_client.py b/tests/test_client.py index 0085d9e..bdb325d 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -122,7 +122,7 @@ def response() -> Response: :return: Request response """ - return requests.get(CountdoomClient.CLOCK_URL) + return requests.get(CountdoomClient.CLOCK_URL, timeout=5) def _get_path(string: str) -> str: @@ -142,16 +142,13 @@ def _setup_servers(httpserver: HTTPServer) -> None: :param httpserver: HTTP Server """ - prefix = '

'.format(CountdoomClient.SELECTOR[1:]) + prefix = f'

' suffix = '

' sentences = SENTENCES_VALID + SENTENCES_INVALID - # sentences = SENTENCES_VALID - # for sentence in SENTENCES_INVALID: - # sentences.append(sentence) for sentence in sentences: path = _get_path(sentence[0]) - httpserver.expect_request('/{}'.format(path)).respond_with_data( + httpserver.expect_request(f'/{path}').respond_with_data( prefix + sentence[0] + suffix ) @@ -197,7 +194,7 @@ async def test_valid_sentence(httpserver: HTTPServer, sentence: str) -> None: client = CountdoomClient() path = _get_path(sentence) - clock_url = httpserver.url_for('/{}'.format(path)) + clock_url = httpserver.url_for(f'/{path}') setattr(client, 'CLOCK_URL', clock_url) data = await client.fetch_data() @@ -223,7 +220,7 @@ async def test_valid_countdown( client = CountdoomClient() path = _get_path(sentence) - clock_url = httpserver.url_for('/{}'.format(path)) + clock_url = httpserver.url_for(f'/{path}') setattr(client, 'CLOCK_URL', clock_url) data = await client.fetch_data() @@ -249,7 +246,7 @@ async def test_valid_minutes( client = CountdoomClient() path = _get_path(sentence) - clock_url = httpserver.url_for('/{}'.format(path)) + clock_url = httpserver.url_for(f'/{path}') setattr(client, 'CLOCK_URL', clock_url) data = await client.fetch_data() @@ -275,7 +272,7 @@ async def test_valid_clock( client = CountdoomClient() path = _get_path(sentence) - clock_url = httpserver.url_for('/{}'.format(path)) + clock_url = httpserver.url_for(f'/{path}') setattr(client, 'CLOCK_URL', clock_url) data = await client.fetch_data() @@ -302,7 +299,7 @@ async def test_valid_time( client = CountdoomClient() path = _get_path(sentence) - clock_url = httpserver.url_for('/{}'.format(path)) + clock_url = httpserver.url_for(f'/{path}') setattr(client, 'CLOCK_URL', clock_url) data = await client.fetch_data() @@ -329,7 +326,7 @@ async def test_invalid_minutes( client = CountdoomClient() path = _get_path(sentence) - clock_url = httpserver.url_for('/{}'.format(path)) + clock_url = httpserver.url_for(f'/{path}') setattr(client, 'CLOCK_URL', clock_url) with pytest.raises(CountdoomClientError) as err: await client.fetch_data() @@ -356,7 +353,7 @@ async def test_invalid_clock( client = CountdoomClient() path = _get_path(sentence) - clock_url = httpserver.url_for('/{}'.format(path)) + clock_url = httpserver.url_for(f'/{path}') setattr(client, 'CLOCK_URL', clock_url) with pytest.raises(CountdoomClientError) as err: await client.fetch_data() @@ -383,7 +380,7 @@ async def test_invalid_time( client = CountdoomClient() path = _get_path(sentence) - clock_url = httpserver.url_for('/{}'.format(path)) + clock_url = httpserver.url_for(f'/{path}') setattr(client, 'CLOCK_URL', clock_url) with pytest.raises(CountdoomClientError) as err: await client.fetch_data() @@ -399,16 +396,16 @@ async def test_invalid_selector(httpserver: HTTPServer) -> None: :param httpserver: HTTP Server """ - prefix = '

'.format(CountdoomClient.SELECTOR[1:]) + prefix = f'

' suffix = '

' string = "IT IS 1 AND A HALF MINUTE TO MIDNIGHT" path = 'test_invalid_selector' - httpserver.expect_request('/{}'.format(path)).respond_with_data( + httpserver.expect_request(f'/{path}').respond_with_data( prefix + string + suffix ) client = CountdoomClient() - clock_url = httpserver.url_for('/{}'.format(path)) + clock_url = httpserver.url_for(f'/{path}') selector = '.wrong-id-' + str(random.randint(0, 100000000)) setattr(client, 'CLOCK_URL', clock_url) setattr(client, 'SELECTOR', selector) @@ -425,16 +422,16 @@ async def test_htmlized_sentence(httpserver: HTTPServer) -> None: :param httpserver: HTTP Server """ - prefix = '

'.format(CountdoomClient.SELECTOR[1:]) + prefix = f'

' suffix = '

' string = " IT IS STILL 1 MINUTE TO MIDNIGHT" path = 'test_htmlized_sentence' - httpserver.expect_request('/{}'.format(path)).respond_with_data( + httpserver.expect_request(f'/{path}').respond_with_data( prefix + string + suffix ) client = CountdoomClient() - clock_url = httpserver.url_for('/{}'.format(path)) + clock_url = httpserver.url_for(f'/{path}') setattr(client, 'CLOCK_URL', clock_url) data = await client.fetch_data() @@ -450,16 +447,16 @@ async def test_empty_sentence(httpserver: HTTPServer) -> None: :param httpserver: HTTP Server """ - prefix = '

'.format(CountdoomClient.SELECTOR[1:]) + prefix = f'

' suffix = '

' string = ' ' path = 'test_empty_sentence' - httpserver.expect_request('/{}'.format(path)).respond_with_data( + httpserver.expect_request(f'/{path}').respond_with_data( prefix + string + suffix ) client = CountdoomClient() - clock_url = httpserver.url_for('/{}'.format(path)) + clock_url = httpserver.url_for(f'/{path}') setattr(client, 'CLOCK_URL', clock_url) with pytest.raises(CountdoomClientError) as err: await client.fetch_data() @@ -500,16 +497,16 @@ async def test_formatted_time(httpserver: HTTPServer) -> None: :param httpserver: HTTP Server """ - prefix = '

'.format(CountdoomClient.SELECTOR[1:]) + prefix = f'

' suffix = '

' string = "IT IS 16 MINUTES TO MIDNIGHT" path = 'test_formatted_time' - httpserver.expect_request('/{}'.format(path)).respond_with_data( + httpserver.expect_request(f'/{path}').respond_with_data( prefix + string + suffix ) client = CountdoomClient() - clock_url = httpserver.url_for('/{}'.format(path)) + clock_url = httpserver.url_for(f'/{path}') setattr(client, 'CLOCK_URL', clock_url) data = await client.fetch_data() @@ -526,7 +523,7 @@ async def test_url_not_found(httpserver: HTTPServer) -> None: """ path = 'test_url_not_found' client = CountdoomClient() - clock_url = httpserver.url_for('/{}'.format(path)) + clock_url = httpserver.url_for(f'/{path}') setattr(client, 'CLOCK_URL', clock_url) with pytest.raises(CountdoomClientError) as err: await client.fetch_data() diff --git a/tests/test_package.py b/tests/test_package.py index 468428c..8356388 100644 --- a/tests/test_package.py +++ b/tests/test_package.py @@ -19,11 +19,11 @@ def test_package_main() -> None: This check uses a subprocess to validate the package, since the package's __main__.py cannot be imported for tests. """ - process = subprocess.Popen( + with subprocess.Popen( ['python', '-m', 'countdoom', '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, - ) - stdout = process.communicate()[0] + ) as process: + stdout = process.communicate()[0] - assert 'countdoom {}'.format(countdoom.__version__) in str(stdout) + assert f'countdoom {countdoom.__version__}' in str(stdout) diff --git a/tox.ini b/tox.ini index ff18480..a4d76a1 100644 --- a/tox.ini +++ b/tox.ini @@ -6,7 +6,7 @@ # See https://tox.readthedocs.io/ [tox] -envlist = lint, py3{5,6,7,8}, pypy3 +envlist = lint, py3{6,7,8}, pypy3 skip_missing_interpreters = true [travis] @@ -14,7 +14,6 @@ python = 3.8: py38 3.7: py37 3.6: py36 - 3.5: py35 [testenv] setenv =