diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cc46ffea2..a0b609041 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - id: check-added-large-files - id: debug-statements - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.17.1 + rev: v2.3.0 hooks: - id: mypy additional_dependencies: @@ -19,7 +19,7 @@ repos: - types-setuptools - types-python-dateutil - repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.15.7' + rev: 'v0.16.0' hooks: - id: ruff args: ["--fix"] diff --git a/doc/source/conf.py b/doc/source/conf.py index 3e4bf2307..034e3ed7c 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -17,7 +17,7 @@ try: # Available from configobj 5.1.0 - import configobj.validate as validate + from configobj import validate except ModuleNotFoundError: import validate diff --git a/khal/controllers.py b/khal/controllers.py index 9b2b7805e..249067d27 100644 --- a/khal/controllers.py +++ b/khal/controllers.py @@ -220,9 +220,12 @@ def get_events_between( # yes the logic could be simplified, but I believe it's easier # to understand what's going on here this way if notstarted: - if event.allday and event.start < original_start.date(): - continue - elif not event.allday and event.start_local < original_start: + if ( + event.allday + and event.start < original_start.date() + or not event.allday + and event.start_local < original_start + ): continue if seen is not None and event.uid in seen: continue @@ -659,9 +662,12 @@ def edit(collection, search_string, locale, format=None, allow_past=False, conf= events = sorted(collection.search(search_string)) for event in events: if not allow_past: - if event.allday and event.end < now.date(): - continue - elif not event.allday and event.end_local < now: + if ( + event.allday + and event.end < now.date() + or not event.allday + and event.end_local < now + ): continue event_text = textwrap.wrap( human_formatter(format)(event.attributes(relative_to=now)), term_width diff --git a/khal/exceptions.py b/khal/exceptions.py index ca3c9c220..67a5bc351 100644 --- a/khal/exceptions.py +++ b/khal/exceptions.py @@ -23,14 +23,10 @@ class Error(Exception): """base class for all of khal's Exceptions""" - pass - class FatalError(Error): """execution cannot continue""" - pass - class DateTimeParseError(FatalError): pass @@ -43,14 +39,10 @@ class ConfigurationError(FatalError): class UnsupportedFeatureError(Error): """something Failed but we know why""" - pass - class UnsupportedRecurrence(Error): """raised if the RRULE is not understood by dateutil.rrule""" - pass - class InvalidDate(Error): pass diff --git a/khal/icalendar.py b/khal/icalendar.py index 3ccb3d040..1e903af93 100644 --- a/khal/icalendar.py +++ b/khal/icalendar.py @@ -79,7 +79,7 @@ def split_ics(ics: str, random_uid: bool = False, default_timezone=None) -> list try: ics = ics_from_list(events, tzs, random_uid, default_timezone) except Exception as exception: - logger.warn(f"Error when trying to import the event {uid}") + logger.warning(f"Error when trying to import the event {uid}") saved_exception = exception else: out.append(ics) diff --git a/khal/khalendar/backend.py b/khal/khalendar/backend.py index fd6553606..d5012427e 100644 --- a/khal/khalendar/backend.py +++ b/khal/khalendar/backend.py @@ -108,7 +108,7 @@ def at_once(self) -> Iterator["SQLiteDb"]: def _create_dbdir(self) -> None: """create the dbdir if it doesn't exist""" if self.db_path == ":memory:": - return None + return dbdir = self.db_path.rsplit("/", 1)[0] if not path.isdir(dbdir): try: diff --git a/khal/khalendar/event.py b/khal/khalendar/event.py index f660b4d44..94848370c 100644 --- a/khal/khalendar/event.py +++ b/khal/khalendar/event.py @@ -152,7 +152,7 @@ def fromVEvents( vevents["PROTO"] = event if ref is None: - ref = "PROTO" if ref in vevents.keys() else list(vevents.keys())[0] + ref = "PROTO" if ref in vevents else list(vevents.keys())[0] try: if type(vevents[ref]["DTSTART"].dt) is not type(vevents[ref]["DTEND"].dt): raise ValueError("DTSTART and DTEND should be of the same type (datetime or date)") diff --git a/khal/parse_datetime.py b/khal/parse_datetime.py index 56ee7b962..2b31ad5db 100644 --- a/khal/parse_datetime.py +++ b/khal/parse_datetime.py @@ -92,8 +92,8 @@ def datetimefstr( for _ in range(parts): item = dtime_list.pop(0) if " " in item: - logger.warn("detected a space in datetime specification, this can lead to errors.") - logger.warn("Make sure not to quote your datetime specification.") + logger.warning("detected a space in datetime specification, this can lead to errors.") + logger.warning("Make sure not to quote your datetime specification.") if infer_year: dtstart = dt.datetime(*(default_day.timetuple()[:1] + dtstart_struct[1:5])) diff --git a/khal/settings/exceptions.py b/khal/settings/exceptions.py index de8252880..bb382f25d 100644 --- a/khal/settings/exceptions.py +++ b/khal/settings/exceptions.py @@ -25,8 +25,6 @@ class InvalidSettingsError(Error): """Invalid Settings detected""" - pass - class CannotParseConfigFileError(InvalidSettingsError): pass diff --git a/khal/settings/utils.py b/khal/settings/utils.py index 4376023e7..26b459c2d 100644 --- a/khal/settings/utils.py +++ b/khal/settings/utils.py @@ -71,7 +71,7 @@ def is_timedelta(string: str) -> dt.timedelta: raise VdtValueError(f"Invalid timedelta: {string}") -def weeknumber_option(option: str) -> Literal["left", "right"] | Literal[False]: +def weeknumber_option(option: str) -> Literal["left", "right", False]: """checks if *option* is a valid value :param option: the option the user set in the config file diff --git a/khal/ui/__init__.py b/khal/ui/__init__.py index 6b094a7f3..21ff0ee3a 100644 --- a/khal/ui/__init__.py +++ b/khal/ui/__init__.py @@ -410,9 +410,7 @@ def ensure_date(self, day: dt.date) -> None: def days_to_next_already_loaded(self, day: dt.date) -> int: """return number of days until `day` is already loaded into the CalendarWidget""" - if len(self) == 0: - return 0 - elif self[0].date <= day <= self[-1].date: + if len(self) == 0 or self[0].date <= day <= self[-1].date: return 0 elif day <= self[0].date: return (self[0].date - day).days @@ -891,7 +889,7 @@ def duplicate(self) -> None: # which are also copied. If the events' summary is edited it will show # up on disk but not be displayed in khal if self.focus_event is None: - return None + return event = self.focus_event.event.duplicate() try: self.pane.collection.insert(event) @@ -1303,17 +1301,17 @@ def _urwid_palette_entry( colors = {} # Colorcube colorlevels = (0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF) - for r in range(0, 6): - for g in range(0, 6): - for b in range(0, 6): + for r in range(6): + for g in range(6): + for b in range(6): colors[r * 36 + g * 6 + b + 16] = ( colorlevels[r], colorlevels[g], colorlevels[b], ) # Grayscale - graylevels = [0x08 + 10 * i for i in range(0, 24)] - for c in range(0, 24): + graylevels = [0x08 + 10 * i for i in range(24)] + for c in range(24): colors[232 + c] = (graylevels[c],) * 3 # Parse the HTML-style color into the variables r, g, b. if len(color) == 4: diff --git a/khal/ui/base.py b/khal/ui/base.py index a79646bbb..ad0073225 100644 --- a/khal/ui/base.py +++ b/khal/ui/base.py @@ -216,9 +216,7 @@ def is_top_level(self): def on_key_press(self, key): """Handle application-wide key strokes.""" - if key in self.quit_keys: - self.backtrack() - elif key == "esc" and not self.is_top_level(): + if key in self.quit_keys or key == "esc" and not self.is_top_level(): self.backtrack() return key diff --git a/khal/ui/calendarwidget.py b/khal/ui/calendarwidget.py index eae69ed27..05c9ac1ff 100644 --- a/khal/ui/calendarwidget.py +++ b/khal/ui/calendarwidget.py @@ -183,7 +183,7 @@ def set_focus_date(self, a_date: dt.date) -> None: for num, day in enumerate(self.contents[1:8], 1): if day[0].date == a_date: self.focus_position = num - return None + return raise ValueError(f"{a_date} not found in this week") def get_date_column(self, a_date: dt.date) -> int: @@ -415,9 +415,7 @@ def set_focus(self, position: int) -> None: def days_to_next_already_loaded(self, day: dt.date) -> int: """return the number of weeks from the focus to the next week that is already loaded""" - if len(self) == 0: - return 0 - elif self.earliest_date <= day <= self.latest_date: + if len(self) == 0 or self.earliest_date <= day <= self.latest_date: return 0 elif day <= self.earliest_date: return (self.earliest_date - day).days @@ -529,10 +527,12 @@ def _construct_week(self, week: list[dt.date]) -> DateCColumns: :param week: list of datetime.date objects :returns: the week as an CColumns object """ - if self.monthdisplay == "firstday" and 1 in (day.day for day in week): - month_name = calendar.month_abbr[week[-1].month].ljust(4) - attr = "monthname" - elif self.monthdisplay == "firstfullweek" and week[0].day <= 7: + if ( + self.monthdisplay == "firstday" + and 1 in (day.day for day in week) + or self.monthdisplay == "firstfullweek" + and week[0].day <= 7 + ): month_name = calendar.month_abbr[week[-1].month].ljust(4) attr = "monthname" elif self.weeknumbers == "left": diff --git a/khal/ui/widgets.py b/khal/ui/widgets.py index c1af51260..923a5866c 100644 --- a/khal/ui/widgets.py +++ b/khal/ui/widgets.py @@ -305,7 +305,7 @@ def _select_last_selectable(self): def _first_selectable(self): """return sequence number of self.contents last selectable item""" - for j in range(0, len(self._contents)): + for j in range(len(self._contents)): if self._contents[j][0].selectable(): return j return False @@ -373,7 +373,7 @@ def _select_last_selectable(self): def _first_selectable(self): """return sequence number of self._contents last selectable item""" - for j in range(0, len(self.body)): + for j in range(len(self.body)): if self.body[j].selectable(): return j return False diff --git a/tests/cli_test.py b/tests/cli_test.py index 7d6a8441e..cc5baf9ce 100644 --- a/tests/cli_test.py +++ b/tests/cli_test.py @@ -215,7 +215,7 @@ def test_notstarted(runner): result = runner.invoke(main_khal, command.split()) assert not result.exception - result = runner.invoke(main_khal, "list now".split()) + result = runner.invoke(main_khal, ["list", "now"]) assert ( result.output == """Today, 01.06.2015 @@ -232,7 +232,7 @@ def test_notstarted(runner): """ ) assert not result.exception - result = runner.invoke(main_khal, "list now --notstarted".split()) + result = runner.invoke(main_khal, ["list", "now", "--notstarted"]) assert ( result.output == """Today, 01.06.2015 @@ -246,7 +246,7 @@ def test_notstarted(runner): ) assert not result.exception - result = runner.invoke(main_khal, "list now --once".split()) + result = runner.invoke(main_khal, ["list", "now", "--once"]) assert ( result.output == """Today, 01.06.2015 @@ -260,7 +260,7 @@ def test_notstarted(runner): ) assert not result.exception - result = runner.invoke(main_khal, "list now --once --notstarted".split()) + result = runner.invoke(main_khal, ["list", "now", "--once", "--notstarted"]) assert ( result.output == """Today, 01.06.2015 @@ -346,9 +346,9 @@ def test_default_command_empty(runner): def test_invalid_calendar(runner): runner = runner(days=2) - result = runner.invoke(main_khal, ["new"] + "-a one 18:00 myevent".split()) + result = runner.invoke(main_khal, ["new"] + ["-a", "one", "18:00", "myevent"]) assert not result.exception - result = runner.invoke(main_khal, ["new"] + "-a inexistent 18:00 myevent".split()) + result = runner.invoke(main_khal, ["new"] + ["-a", "inexistent", "18:00", "myevent"]) assert result.exception assert result.exit_code == 2 assert "Unknown calendar " in result.output @@ -586,7 +586,7 @@ def test_search_json(runner): def test_no_default_new(runner): runner = runner(default_calendar=False) - result = runner.invoke(main_khal, "new 18:00 beer".split()) + result = runner.invoke(main_khal, ["new", "18:00", "beer"]) assert ( "Error: Invalid value: No default calendar is configured, please provide one explicitly." ) in result.output @@ -604,7 +604,7 @@ def test_print_bad_ics(runner): def test_import(runner, monkeypatch): runner = runner() - result = runner.invoke(main_khal, "import -a one -a two import file.ics".split()) + result = runner.invoke(main_khal, ["import", "-a", "one", "-a", "two", "import", "file.ics"]) assert result.exception assert result.exit_code == 2 assert 'Can\'t use "--include-calendar" / "-a" more than once' in result.output @@ -1018,7 +1018,7 @@ def test_edit(runner): def test_new(runner): runner = runner(print_new="path") - result = runner.invoke(main_khal, "new 13.03.2016 3d Visit".split()) + result = runner.invoke(main_khal, ["new", "13.03.2016", "3d", "Visit"]) assert not result.exception assert result.output.endswith(".ics\n") assert result.output.startswith(str(runner.tmpdir)) @@ -1059,7 +1059,7 @@ def test_new_json(runner): def test_new_interactive(runner): runner = runner(print_new="path") - result = runner.invoke(main_khal, "new -i".split(), "Another event\n13:00 17:00\n\nNone\nn\n") + result = runner.invoke(main_khal, ["new", "-i"], "Another event\n13:00 17:00\n\nNone\nn\n") assert not result.exception assert result.exit_code == 0 @@ -1079,7 +1079,7 @@ def test_new_interactive_extensive(runner): result = runner.invoke( main_khal, - "new -i 15:00 15:30".split(), + ["new", "-i", "15:00", "15:30"], "?\ninvalid\ntwo\n" "Unicce Name\n" "\n" @@ -1105,7 +1105,7 @@ def test_issue_1056(runner): result = runner.invoke( main_khal, - "new -i".split(), + ["new", "-i"], "two\n" "new event\n" "now\n" diff --git a/tests/parse_datetime_test.py b/tests/parse_datetime_test.py index 5cee0346f..687ee3c7e 100644 --- a/tests/parse_datetime_test.py +++ b/tests/parse_datetime_test.py @@ -144,31 +144,31 @@ def test_today(self): @freeze_time("2016-9-19T8:00") def test_tomorrow(self): assert (dt.datetime(2016, 9, 20, 16), False) == guessdatetimefstr( - "tomorrow 16:00 16:00".split(), locale=LOCALE_BERLIN + ["tomorrow", "16:00", "16:00"], locale=LOCALE_BERLIN ) @freeze_time("2016-9-19T8:00") def test_time_tomorrow(self): assert (dt.datetime(2016, 9, 20, 16), False) == guessdatetimefstr( - "16:00".split(), locale=LOCALE_BERLIN, default_day=dt.date(2016, 9, 20) + ["16:00"], locale=LOCALE_BERLIN, default_day=dt.date(2016, 9, 20) ) @freeze_time("2016-9-19T8:00") def test_time_yesterday(self): assert (dt.datetime(2016, 9, 18, 16), False) == guessdatetimefstr( - "Yesterday 16:00".split(), locale=LOCALE_BERLIN, default_day=dt.datetime.today() + ["Yesterday", "16:00"], locale=LOCALE_BERLIN, default_day=dt.datetime.today() ) @freeze_time("2016-9-19") def test_time_weekday(self): assert (dt.datetime(2016, 9, 23, 16), False) == guessdatetimefstr( - "Friday 16:00".split(), locale=LOCALE_BERLIN, default_day=dt.datetime.today() + ["Friday", "16:00"], locale=LOCALE_BERLIN, default_day=dt.datetime.today() ) @freeze_time("2016-9-19 17:53") def test_time_now(self): assert (dt.datetime(2016, 9, 19, 17, 53), False) == guessdatetimefstr( - "now".split(), locale=LOCALE_BERLIN, default_day=dt.datetime.today() + ["now"], locale=LOCALE_BERLIN, default_day=dt.datetime.today() ) @freeze_time("2016-12-30 17:53") @@ -182,10 +182,10 @@ def test_long_not_configured(self): "longdatetimeformat": "", } assert (dt.datetime(2017, 1, 1), True) == guessdatetimefstr( - "2017-1-1".split(), locale=locale, default_day=dt.datetime.today() + ["2017-1-1"], locale=locale, default_day=dt.datetime.today() ) assert (dt.datetime(2017, 1, 1, 16, 30), False) == guessdatetimefstr( - "2017-1-1 16:30".split(), locale=locale, default_day=dt.datetime.today() + ["2017-1-1", "16:30"], locale=locale, default_day=dt.datetime.today() ) @freeze_time("2016-12-30 17:53") @@ -200,10 +200,10 @@ def test_short_format_contains_year(self): "longdatetimeformat": "%Y-%m-%d %H:%M", } assert (dt.datetime(2017, 1, 1), True) == guessdatetimefstr( - "2017-1-1".split(), locale=locale, default_day=dt.datetime.today() + ["2017-1-1"], locale=locale, default_day=dt.datetime.today() ) assert (dt.datetime(2017, 1, 1, 16, 30), False) == guessdatetimefstr( - "2017-1-1 16:30".split(), locale=locale, default_day=dt.datetime.today() + ["2017-1-1", "16:30"], locale=locale, default_day=dt.datetime.today() ) @@ -539,9 +539,8 @@ def test__construct_event_format_de_complexer(): def test_leap_year(): for data_list, vevent in test_set_leap_year: - with freeze_time("1999-1-1"): - with pytest.raises(DateTimeParseError): - event = _construct_event(data_list.split(), locale=LOCALE_BERLIN) + with freeze_time("1999-1-1"), pytest.raises(DateTimeParseError): + event = _construct_event(data_list.split(), locale=LOCALE_BERLIN) with freeze_time("2016-1-1 20:21:22"): event = _construct_event(data_list.split(), locale=LOCALE_BERLIN) assert _replace_uid(event).to_ical() == vevent diff --git a/tests/utils.py b/tests/utils.py index 9c66cd2d2..7d09b5959 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -10,7 +10,7 @@ try: from icalendar.parser import Contentlines # icalendar >= 7.0.0 except ImportError: - from icalendar.cal import Contentlines # icalendar < 7.0.0 # noqa: F401 + from icalendar.cal import Contentlines # icalendar < 7.0.0 CollVdirType = tuple[CalendarCollection, dict[str, Vdir]] diff --git a/tests/utils_test.py b/tests/utils_test.py index 4989b2583..02de4a34d 100644 --- a/tests/utils_test.py +++ b/tests/utils_test.py @@ -16,11 +16,9 @@ def test_relative_timedelta_str(): assert utils.relative_timedelta_str(dt.date(2016, 7, 29)) == "~7 weeks ago" -weekheader = """ Mo Tu We Th Fr Sa Su """ -today_line = """Today""" -calendarline = ( - "Nov 31  1  2  3  4  5  6" -) +weekheader = """\x1b[1m Mo Tu We Th Fr Sa Su \x1b[0m""" +today_line = """\x1b[1mToday\x1b[0m\x1b[0m""" +calendarline = "\x1b[1mNov \x1b[0m\x1b[1;33m31\x1b[0m \x1b[32m 1\x1b[0m \x1b[1;33m 2\x1b[0m \x1b[1;33m 3\x1b[0m \x1b[1;33m 4\x1b[0m \x1b[32m 5\x1b[0m \x1b[32m 6\x1b[0m" def test_last_reset():