Skip to content

Commit 2e81399

Browse files
committed
general: fix all ruff stuff + more mypy coverage
1 parent db83798 commit 2e81399

17 files changed

Lines changed: 310 additions & 273 deletions

ruff.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ lint.extend-select = [
2525
"TRY", # various exception handling rules
2626
"UP", # detect deprecated python stdlib stuff
2727
"FA", # suggest using from __future__ import annotations
28-
"PTH", # pathlib migration
28+
# "PTH", # pathlib migration # FIXME do later.. a bit overwhelming
2929
"ARG", # unused argument checks
3030
"A", # builtin shadowing
3131
"G", # logging stuff

src/orgparse/__init__.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,13 @@
106106
"""
107107
# [[[end]]]
108108

109-
from io import IOBase
109+
from collections.abc import Iterable
110110
from pathlib import Path
111-
from typing import Iterable, Union, Optional, TextIO
111+
from typing import Optional, TextIO, Union
112112

113+
from .node import OrgEnv, OrgNode, parse_lines # todo basenode??
113114

114-
from .node import parse_lines, OrgEnv, OrgNode # todo basenode??
115-
116-
__all__ = ["load", "loads", "loadi"]
115+
__all__ = ["load", "loadi", "loads"]
117116

118117

119118
def load(path: Union[str, Path, TextIO], env: Optional[OrgEnv] = None) -> OrgNode:

src/orgparse/date.py

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,25 @@
1+
from __future__ import annotations
2+
13
import datetime
24
import re
3-
from typing import Union, Tuple, Optional, List
5+
from datetime import timedelta
6+
from typing import Optional, Union
47

58
DateIsh = Union[datetime.date, datetime.datetime]
69

710

8-
def total_seconds(td):
11+
def total_seconds(td: timedelta) -> float:
912
"""Equivalent to `datetime.timedelta.total_seconds`."""
1013
return float(td.microseconds +
1114
(td.seconds + td.days * 24 * 3600) * 10 ** 6) / 10 ** 6
1215

1316

14-
def total_minutes(td):
17+
def total_minutes(td: timedelta) -> float:
1518
"""Alias for ``total_seconds(td) / 60``."""
1619
return total_seconds(td) / 60
1720

1821

19-
def gene_timestamp_regex(brtype, prefix=None, nocookie=False):
22+
def gene_timestamp_regex(brtype: str, prefix: str | None = None, *, nocookie: bool = False) -> str:
2023
"""
2124
Generate timestamp regex for active/inactive/nobrace brace type
2225
@@ -84,15 +87,15 @@ def gene_timestamp_regex(brtype, prefix=None, nocookie=False):
8487
elif brtype == 'nobrace':
8588
(bo, bc) = ('', '')
8689
else:
87-
raise ValueError("brtype='{0!r}' is invalid".format(brtype))
90+
raise ValueError(f"brtype='{brtype!r}' is invalid")
8891

8992
if brtype == 'nobrace':
9093
ignore = r'[\s\w]'
9194
else:
92-
ignore = '[^{bc}]'.format(bc=bc)
95+
ignore = f'[^{bc}]'
9396

9497
if prefix is None:
95-
prefix = '{0}_'.format(brtype)
98+
prefix = f'{brtype}_'
9699

97100
regex_date_time = r"""
98101
(?P<{prefix}year>\d{{4}}) -
@@ -133,7 +136,7 @@ def gene_timestamp_regex(brtype, prefix=None, nocookie=False):
133136
return regex.format(prefix=prefix, ignore=ignore)
134137

135138

136-
def date_time_format(date) -> str:
139+
def date_time_format(date: DateIsh) -> str:
137140
"""
138141
Format a date or datetime in default org format
139142
@@ -165,7 +168,10 @@ def is_same_day(date0, date1) -> bool:
165168
re.VERBOSE)
166169

167170

168-
class OrgDate(object):
171+
_Repeater = tuple[str, int, str]
172+
173+
174+
class OrgDate:
169175

170176
_active_default = True
171177
"""
@@ -184,8 +190,14 @@ class OrgDate(object):
184190
"""
185191
_allow_short_range = True
186192

187-
def __init__(self, start, end=None, active=None, repeater=None,
188-
warning=None):
193+
def __init__(
194+
self,
195+
start,
196+
end=None,
197+
active: bool | None = None,
198+
repeater: _Repeater | None = None,
199+
warning: _Repeater | None = None,
200+
) -> None:
189201
"""
190202
Create :class:`OrgDate` object
191203
@@ -242,21 +254,23 @@ def _to_date(date) -> DateIsh:
242254
raise ValueError(
243255
"Automatic conversion to the datetime object "
244256
"requires at least 3 elements in the tuple. "
245-
"Only {0} elements are in the given tuple '{1}'."
246-
.format(len(date), date))
257+
f"Only {len(date)} elements are in the given tuple '{date}'."
258+
)
247259
elif isinstance(date, (int, float)):
248260
return datetime.datetime.fromtimestamp(date)
249261
else:
250262
return date
251263

252264
@staticmethod
253-
def _date_to_tuple(date):
265+
def _date_to_tuple(date: DateIsh) -> tuple[int, ...]:
254266
if isinstance(date, datetime.datetime):
255267
return tuple(date.timetuple()[:6])
256268
elif isinstance(date, datetime.date):
257269
return tuple(date.timetuple()[:3])
270+
else:
271+
raise RuntimeError(f"can't happen: {date}")
258272

259-
def __repr__(self):
273+
def __repr__(self) -> str:
260274
args = [
261275
self.__class__.__name__,
262276
self._date_to_tuple(self.start),
@@ -269,36 +283,36 @@ def __repr__(self):
269283
args.pop()
270284
if len(args) > 3 and args[3] is None:
271285
args[3] = self._active_default
272-
return '{0}({1})'.format(args[0], ', '.join(map(repr, args[1:])))
286+
return '{}({})'.format(args[0], ', '.join(map(repr, args[1:])))
273287

274-
def __str__(self):
288+
def __str__(self) -> str:
275289
fence = ("<", ">") if self.is_active() else ("[", "]")
276290

277291
start = date_time_format(self.start)
278292
end = None
279293

280294
if self.has_end():
281295
if self._allow_short_range and is_same_day(self.start, self.end):
282-
start += "--%s" % self.end.strftime("%H:%M")
296+
start += "--{}".format(self.end.strftime("%H:%M"))
283297
else:
284298
end = date_time_format(self.end)
285299

286-
if self._repeater:
287-
start += " %s%d%s" % self._repeater
288-
if self._warning:
289-
start += " %s%d%s" % self._warning
290-
ret = "%s%s%s" % (fence[0], start, fence[1])
300+
if self._repeater is not None:
301+
(x, y, z) = self._repeater
302+
start += f" {x}{y}{z}"
303+
if self._warning is not None:
304+
(x, y, z) = self._warning
305+
start += f" {x}{y}{z}"
306+
ret = f"{fence[0]}{start}{fence[1]}"
291307
if end:
292-
ret += "--%s%s%s" % (fence[0], end, fence[1])
308+
ret += f"--{fence[0]}{end}{fence[1]}"
293309

294310
return ret
295311

296-
def __nonzero__(self):
312+
def __bool__(self) -> bool:
297313
return bool(self._start)
298314

299-
__bool__ = __nonzero__ # PY3
300-
301-
def __eq__(self, other):
315+
def __eq__(self, other) -> bool:
302316
if (isinstance(other, OrgDate) and
303317
self._start is None and
304318
other._start is None):
@@ -309,7 +323,7 @@ def __eq__(self, other):
309323
self._active == other._active)
310324

311325
@property
312-
def start(self):
326+
def start(self) -> DateIsh:
313327
"""
314328
Get date or datetime object
315329
@@ -322,7 +336,7 @@ def start(self):
322336
return self._start
323337

324338
@property
325-
def end(self):
339+
def end(self) -> DateIsh:
326340
"""
327341
Get date or datetime object
328342
@@ -404,11 +418,11 @@ def _as_datetime(date) -> datetime.datetime:
404418
return date
405419

406420
@staticmethod
407-
def _daterange_from_groupdict(dct, prefix='') -> Tuple[List, Optional[List]]:
421+
def _daterange_from_groupdict(dct, prefix='') -> tuple[list, Optional[list]]:
408422
start_keys = ['year', 'month', 'day', 'hour' , 'min']
409423
end_keys = ['year', 'month', 'day', 'end_hour', 'end_min']
410424
start_range = list(map(int, filter(None, (dct[prefix + k] for k in start_keys))))
411-
end_range: Optional[List]
425+
end_range: Optional[list]
412426
end_range = list(map(int, filter(None, (dct[prefix + k] for k in end_keys))))
413427
if len(end_range) < len(end_keys):
414428
end_range = None
@@ -419,7 +433,7 @@ def _datetuple_from_groupdict(cls, dct, prefix=''):
419433
return cls._daterange_from_groupdict(dct, prefix=prefix)[0]
420434

421435
@classmethod
422-
def list_from_str(cls, string: str) -> List['OrgDate']:
436+
def list_from_str(cls, string: str) -> list[OrgDate]:
423437
"""
424438
Parse string and return a list of :class:`OrgDate` objects
425439
@@ -447,8 +461,8 @@ def list_from_str(cls, string: str) -> List['OrgDate']:
447461
prefix = 'inactive_'
448462
active = False
449463
rangedash = '--['
450-
repeater: Optional[Tuple[str, int, str]] = None
451-
warning: Optional[Tuple[str, int, str]] = None
464+
repeater: Optional[tuple[str, int, str]] = None
465+
warning: Optional[tuple[str, int, str]] = None
452466
if mdict[prefix + 'repeatpre'] is not None:
453467
keys = [prefix + 'repeat' + suffix for suffix in cookie_suffix]
454468
values = [mdict[k] for k in keys]
@@ -471,12 +485,12 @@ def list_from_str(cls, string: str) -> List['OrgDate']:
471485
odate = cls(
472486
*cls._daterange_from_groupdict(mdict, prefix),
473487
active=active, repeater=repeater, warning=warning)
474-
return [odate] + cls.list_from_str(rest)
488+
return [odate, *cls.list_from_str(rest)]
475489
else:
476490
return []
477491

478492
@classmethod
479-
def from_str(cls, string):
493+
def from_str(cls, string: str) -> OrgDate:
480494
"""
481495
Parse string and return an :class:`OrgDate` objects.
482496
@@ -500,7 +514,7 @@ def from_str(cls, string):
500514
def compile_sdc_re(sdctype):
501515
brtype = 'inactive' if sdctype == 'CLOSED' else 'active'
502516
return re.compile(
503-
r'^(?!\#).*{0}:\s+{1}'.format(
517+
r'^(?!\#).*{}:\s+{}'.format(
504518
sdctype,
505519
gene_timestamp_regex(brtype, prefix='', nocookie=True)),
506520
re.VERBOSE)
@@ -528,8 +542,8 @@ def from_str(cls, string):
528542
end_dict.update({'hour': end_hour, 'min': end_min})
529543
end = cls._datetuple_from_groupdict(end_dict)
530544
cookie_suffix = ['pre', 'num', 'dwmy']
531-
repeater: Optional[Tuple[str, int, str]] = None
532-
warning: Optional[Tuple[str, int, str]] = None
545+
repeater: Optional[tuple[str, int, str]] = None
546+
warning: Optional[tuple[str, int, str]] = None
533547
prefix = ''
534548
if mdict[prefix + 'repeatpre'] is not None:
535549
keys = [prefix + 'repeat' + suffix for suffix in cookie_suffix]
@@ -588,7 +602,7 @@ def __init__(self, start, end=None, duration=None, active=None):
588602
"""
589603
Create OrgDateClock object
590604
"""
591-
super(OrgDateClock, self).__init__(start, end, active=active)
605+
super().__init__(start, end, active=active)
592606
self._duration = duration
593607

594608
@property
@@ -625,7 +639,7 @@ def is_duration_consistent(self):
625639
self._duration == total_minutes(self.duration))
626640

627641
@classmethod
628-
def from_str(cls, line: str) -> 'OrgDateClock':
642+
def from_str(cls, line: str) -> OrgDateClock:
629643
"""
630644
Get CLOCK from given string.
631645
@@ -674,26 +688,26 @@ class OrgDateRepeatedTask(OrgDate):
674688

675689
_active_default = False
676690

677-
def __init__(self, start, before, after, active=None):
678-
super(OrgDateRepeatedTask, self).__init__(start, active=active)
691+
def __init__(self, start, before: str, after: str, active=None) -> None:
692+
super().__init__(start, active=active)
679693
self._before = before
680694
self._after = after
681695

682-
def __repr__(self):
683-
args = [self._date_to_tuple(self.start), self.before, self.after]
696+
def __repr__(self) -> str:
697+
args: list = [self._date_to_tuple(self.start), self.before, self.after]
684698
if self._active is not self._active_default:
685699
args.append(self._active)
686-
return '{0}({1})'.format(
700+
return '{}({})'.format(
687701
self.__class__.__name__, ', '.join(map(repr, args)))
688702

689-
def __eq__(self, other):
690-
return super(OrgDateRepeatedTask, self).__eq__(other) and \
703+
def __eq__(self, other) -> bool:
704+
return super().__eq__(other) and \
691705
isinstance(other, self.__class__) and \
692706
self._before == other._before and \
693707
self._after == other._after
694708

695709
@property
696-
def before(self):
710+
def before(self) -> str:
697711
"""
698712
The state of task before marked as done.
699713
@@ -705,7 +719,7 @@ def before(self):
705719
return self._before
706720

707721
@property
708-
def after(self):
722+
def after(self) -> str:
709723
"""
710724
The state of task after marked as done.
711725

src/orgparse/extra.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import re
2-
from typing import List, Sequence, Dict, Iterator, Iterable, Union, Optional, Type
1+
from __future__ import annotations
32

3+
import re
4+
from collections.abc import Iterator, Sequence
5+
from typing import Optional, Union
46

57
RE_TABLE_SEPARATOR = re.compile(r'\s*\|(\-+\+)*\-+\|')
68
RE_TABLE_ROW = re.compile(r'\s*\|([^|]+)+\|')
@@ -10,12 +12,12 @@
1012
Row = Sequence[str]
1113

1214
class Table:
13-
def __init__(self, lines: List[str]) -> None:
15+
def __init__(self, lines: list[str]) -> None:
1416
self._lines = lines
1517

1618
@property
1719
def blocks(self) -> Iterator[Sequence[Row]]:
18-
group: List[Row] = []
20+
group: list[Row] = []
1921
first = True
2022
for r in self._pre_rows():
2123
if r is None:
@@ -49,7 +51,7 @@ def _pre_rows(self) -> Iterator[Optional[Row]]:
4951
# TODO use iparse helper?
5052

5153
@property
52-
def as_dicts(self) -> 'AsDictHelper':
54+
def as_dicts(self) -> AsDictHelper:
5355
bl = list(self.blocks)
5456
if len(bl) != 2:
5557
raise RuntimeError('Need two-block table to non-ambiguously guess column names')
@@ -69,9 +71,9 @@ def __init__(self, columns: Sequence[str], rows: Sequence[Row]) -> None:
6971
self.columns = columns
7072
self._rows = rows
7173

72-
def __iter__(self) -> Iterator[Dict[str, str]]:
74+
def __iter__(self) -> Iterator[dict[str, str]]:
7375
for x in self._rows:
74-
yield {k: v for k, v in zip(self.columns, x)}
76+
yield dict(zip(self.columns, x))
7577

7678

7779
class Gap:
@@ -89,8 +91,8 @@ def to_rich_text(text: str) -> Iterator[Rich]:
8991
At the moment only tables are supported.
9092
'''
9193
lines = text.splitlines(keepends=True)
92-
group: List[str] = []
93-
last: Type[Rich] = Gap
94+
group: list[str] = []
95+
last: type[Rich] = Gap
9496
def emit() -> Rich:
9597
nonlocal group, last
9698
if last is Gap:

0 commit comments

Comments
 (0)