diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 98e8665..6aedf51 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -13,5 +13,4 @@ jobs: pipenv-install-options: "--skip-lock" mypy: true black: true - pycodestyle: true - pydocstyle: true + ruff: true diff --git a/Pipfile b/Pipfile index d7db558..f71b59c 100644 --- a/Pipfile +++ b/Pipfile @@ -14,8 +14,6 @@ types-requests = "==2.31.0.9" [dev-packages] towncrier = "==23.6.0" -pydocstyle = {version = "==6.3.0", extras = ["toml"]} -pycodestyle = "==2.11.1" mypy = "==1.6.0" black = "==23.9.1" pytest = "==7.4.2" @@ -23,3 +21,4 @@ pytest-cov = "==4.1.0" tbump = "==6.11.0" sphinx = "==7.2.6" sphinx-jsonschema = "==1.19.1" +ruff = "==0.0.292" diff --git a/Pipfile.lock b/Pipfile.lock index edaac8f..d2cdb1f 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "ee1deb2f073f666c4e14ea529bf27fcb39196a8e47b053a234d42a09e85dbe48" + "sha256": "d53ed6fb1470862096bb4c2daaa0b92756fd42d53788dbce95b11e854f36f781" }, "pipfile-spec": 6, "requires": {}, @@ -841,26 +841,6 @@ "markers": "python_version >= '3.8'", "version": "==1.3.0" }, - "pycodestyle": { - "hashes": [ - "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f", - "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==2.11.1" - }, - "pydocstyle": { - "extras": [ - "toml" - ], - "hashes": [ - "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019", - "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1" - ], - "markers": "python_version >= '3.6'", - "version": "==6.3.0" - }, "pygments": { "hashes": [ "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692", @@ -952,6 +932,30 @@ "markers": "python_version >= '3.7'", "version": "==2.31.0" }, + "ruff": { + "hashes": [ + "sha256:02f29db018c9d474270c704e6c6b13b18ed0ecac82761e4fcf0faa3728430c96", + "sha256:1093449e37dd1e9b813798f6ad70932b57cf614e5c2b5c51005bf67d55db33ac", + "sha256:69654e564342f507edfa09ee6897883ca76e331d4bbc3676d8a8403838e9fade", + "sha256:6bdfabd4334684a4418b99b3118793f2c13bb67bf1540a769d7816410402a205", + "sha256:6c3c91859a9b845c33778f11902e7b26440d64b9d5110edd4e4fa1726c41e0a4", + "sha256:7f67a69c8f12fbc8daf6ae6d36705037bde315abf8b82b6e1f4c9e74eb750f68", + "sha256:87616771e72820800b8faea82edd858324b29bb99a920d6aa3d3949dd3f88fb0", + "sha256:8e087b24d0d849c5c81516ec740bf4fd48bf363cfb104545464e0fca749b6af9", + "sha256:9889bac18a0c07018aac75ef6c1e6511d8411724d67cb879103b01758e110a81", + "sha256:aa7c77c53bfcd75dbcd4d1f42d6cabf2485d2e1ee0678da850f08e1ab13081a8", + "sha256:ac153eee6dd4444501c4bb92bff866491d4bfb01ce26dd2fff7ca472c8df9ad0", + "sha256:b76deb3bdbea2ef97db286cf953488745dd6424c122d275f05836c53f62d4016", + "sha256:be8eb50eaf8648070b8e58ece8e69c9322d34afe367eec4210fdee9a555e4ca7", + "sha256:e854b05408f7a8033a027e4b1c7f9889563dd2aca545d13d06711e5c39c3d003", + "sha256:f160b5ec26be32362d0774964e218f3fcf0a7da299f7e220ef45ae9e3e67101a", + "sha256:f27282bedfd04d4c3492e5c3398360c9d86a295be00eccc63914438b4ac8a83c", + "sha256:f4476f1243af2d8c29da5f235c13dca52177117935e1f9393f9d90f9833f69e4" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==0.0.292" + }, "schema": { "hashes": [ "sha256:f06717112c61895cabc4707752b88716e8420a8819d71404501e114f91043197", diff --git a/docs/source/conf.py b/docs/source/conf.py index 3daf94a..b9ee028 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -15,10 +15,8 @@ # # All configuration values have a default; values that are commented out # serve to show the default. - from pathlib import Path -from typing import List, Dict - +from typing import Dict, List p = Path(os.getcwd()) sys.path.append(str(p.parent.parent)) diff --git a/jira_timemachine/__init__.py b/jira_timemachine/__init__.py index f9d70ef..c9bcb9d 100644 --- a/jira_timemachine/__init__.py +++ b/jira_timemachine/__init__.py @@ -7,19 +7,19 @@ """Module for synchronization of Jira worklogs between different instances.""" -import re import itertools -from datetime import date, timedelta +import re from dataclasses import dataclass -from typing import Iterator, Dict, IO, Optional, Tuple, Union +from datetime import date, timedelta +from typing import IO, Dict, Iterator, Optional, Tuple, Union -import click import arrow -from jira import JIRA +import click import jira -from jira.client import ResultList -from pydantic import BaseModel, HttpUrl, Field, ValidationError import requests +from jira import JIRA +from jira.client import ResultList +from pydantic import BaseModel, Field, HttpUrl, ValidationError from requests import HTTPError __version__ = "1.0.1" @@ -98,8 +98,7 @@ def to_tempo(self) -> dict: class TempoClient: - """ - A client for Tempo Cloud APIs. + """A client for Tempo Cloud APIs. See for the API documentation. """ @@ -111,8 +110,7 @@ def __init__(self, tempo_token: str, account_id: str) -> None: self.session.headers.update({"Authorization": "Bearer %s" % tempo_token}) def get_worklogs(self, from_date: date, single_user: bool = True) -> Iterator[Worklog]: - """ - Return all recent worklogs for the specified user. + """Return all recent worklogs for the specified user. :returns: yields Worklog instances """ @@ -156,8 +154,7 @@ def get_worklogs(self, from_date: date, single_user: bool = True) -> Iterator[Wo url = response_data["metadata"].get("next") def update_worklog(self, worklog: Worklog) -> None: - """ - Update the specified worklog. + """Update the specified worklog. :param worklog: updated worklog data """ @@ -171,8 +168,7 @@ def update_worklog(self, worklog: Worklog) -> None: raise def post_worklog(self, worklog: Worklog) -> None: - """ - Upload a new worklog. + """Upload a new worklog. :param worklog: new worklog data """ @@ -223,8 +219,7 @@ def _issues(self, query: str) -> Iterator[jira.Issue]: return def get_worklogs(self, from_date: date, single_user: bool = True) -> Iterator[Worklog]: - """ - Return all recent worklogs for the specified user. + """Return all recent worklogs for the specified user. :returns: yields Worklog instances """ @@ -258,8 +253,7 @@ def get_client(config: SourceJiraConfig) -> Union[TempoClient, JIRAClient]: def get_worklogs(config: SourceJiraConfig, since: arrow.Arrow, all_users: bool = False) -> Iterator[Worklog]: - """ - Yield user's recent worklogs. + """Yield user's recent worklogs. :param config: JIRA configuration :param since: earliest start time of yielded worklogs @@ -276,8 +270,7 @@ def get_worklogs(config: SourceJiraConfig, since: arrow.Arrow, all_users: bool = def format_time(seconds: int) -> str: - """ - Return *seconds* in a human-readable format (e.g. 25h 15m 45s). + """Return *seconds* in a human-readable format (e.g. 25h 15m 45s). Unlike `timedelta`, we don't aggregate it into days: it's not useful when reporting logged work hours. """ @@ -298,8 +291,7 @@ def format_time(seconds: int) -> str: def match_worklog(source_worklogs: Dict[int, Worklog], worklog: Worklog) -> Optional[Worklog]: - """ - Return a matching source worklog for the given destination worklog. + """Return a matching source worklog for the given destination worklog. :param source_worklogs: a mapping from source JIRA worklog ID to `Worklog` instance :param worklog: destination JIRA worklog diff --git a/newsfragments/285.misc.rst b/newsfragments/285.misc.rst new file mode 100644 index 0000000..03d4e22 --- /dev/null +++ b/newsfragments/285.misc.rst @@ -0,0 +1 @@ +Replace pydocstyle and pycodestyle with ruff diff --git a/pyproject.toml b/pyproject.toml index 3803570..afdb669 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,6 +67,17 @@ line-length = 120 target-version = ['py310'] include = '.*\.pyi?$' +[tool.ruff] +# Decrease the maximum line length to 79 characters. +line-length = 120 +select = [ + "E", # pycodestyle + "F", # pyflakes + "I", # isort + "D", # pydocstyle +] + + [tool.mypy] check_untyped_defs = true mypy_path = "src" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 2294041..0000000 --- a/setup.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[pycodestyle] -max-line-length = 120 -exclude = docs/*,build/*,venv/* diff --git a/tests/test_unit.py b/tests/test_unit.py index 35a99d4..14188bc 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -2,15 +2,15 @@ from datetime import date from io import StringIO -from unittest.mock import patch, call, Mock +from unittest.mock import Mock, call, patch import arrow import click import pytest -from pydantic import HttpUrl, parse_obj_as, TypeAdapter +from pydantic import HttpUrl, TypeAdapter from pydantic_core import Url -from jira_timemachine import Worklog, get_worklogs, format_time, match_worklog, SourceJiraConfig, get_config +from jira_timemachine import SourceJiraConfig, Worklog, format_time, get_config, get_worklogs, match_worklog def test_worklog_to_tempo():