forked from kenkeiras/org-rw
Merge pull request 'feat/improvements' (#1) from lyz/org-rw:feat/improvements into develop
Reviewed-on: kenkeiras/org-rw#1 Reviewed-by: kenkeiras <kenkeiras@codigoparallevar.com>
This commit is contained in:
commit
ef893a72a0
639
org_rw/org_rw.py
639
org_rw/org_rw.py
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,7 @@
|
|||||||
import re
|
import re
|
||||||
from typing import List, TypedDict
|
from typing import List, TypedDict
|
||||||
|
|
||||||
|
|
||||||
class HeadlineDict(TypedDict):
|
class HeadlineDict(TypedDict):
|
||||||
linenum: int
|
linenum: int
|
||||||
orig: re.Match
|
orig: re.Match
|
||||||
|
@ -1,14 +1,23 @@
|
|||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import unittest
|
import unittest
|
||||||
from datetime import date
|
|
||||||
from datetime import datetime as DT
|
from datetime import datetime as DT
|
||||||
|
|
||||||
from org_rw import MarkerToken, MarkerType, Timestamp, dumps, load, loads, dom
|
from org_rw import MarkerToken, MarkerType, Timestamp, dumps, load, loads, dom
|
||||||
import org_rw
|
import org_rw
|
||||||
|
|
||||||
from utils.assertions import (BOLD, CODE, HL, ITALIC, SPAN, STRIKE, UNDERLINED,
|
from utils.assertions import (
|
||||||
VERBATIM, WEB_LINK, Doc, Tokens)
|
BOLD,
|
||||||
|
CODE,
|
||||||
|
HL,
|
||||||
|
ITALIC,
|
||||||
|
SPAN,
|
||||||
|
STRIKE,
|
||||||
|
UNDERLINED,
|
||||||
|
VERBATIM,
|
||||||
|
WEB_LINK,
|
||||||
|
Doc,
|
||||||
|
Tokens,
|
||||||
|
)
|
||||||
|
|
||||||
DIR = os.path.dirname(os.path.abspath(__file__))
|
DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
@ -283,13 +292,19 @@ class TestSerde(unittest.TestCase):
|
|||||||
SPAN("\n"),
|
SPAN("\n"),
|
||||||
SPAN(
|
SPAN(
|
||||||
" This is a ",
|
" This is a ",
|
||||||
WEB_LINK("[tricky web link]\u200b", "https://codigoparallevar.com/4"),
|
WEB_LINK(
|
||||||
|
"[tricky web link]\u200b",
|
||||||
|
"https://codigoparallevar.com/4",
|
||||||
|
),
|
||||||
" followed up with some text.\n",
|
" followed up with some text.\n",
|
||||||
),
|
),
|
||||||
SPAN("\n"),
|
SPAN("\n"),
|
||||||
SPAN(
|
SPAN(
|
||||||
" This is [",
|
" This is [",
|
||||||
WEB_LINK("another tricky web link", "https://codigoparallevar.com/5"),
|
WEB_LINK(
|
||||||
|
"another tricky web link",
|
||||||
|
"https://codigoparallevar.com/5",
|
||||||
|
),
|
||||||
"] followed up with some text.\n",
|
"] followed up with some text.\n",
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -306,7 +321,7 @@ class TestSerde(unittest.TestCase):
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
ex.assert_matches(self, doc)
|
ex.assert_matches(self, doc)
|
||||||
@ -471,7 +486,9 @@ class TestSerde(unittest.TestCase):
|
|||||||
+ 'echo "with two lines"\n'
|
+ 'echo "with two lines"\n'
|
||||||
+ "exit 0 # Exit successfully",
|
+ "exit 0 # Exit successfully",
|
||||||
)
|
)
|
||||||
self.assertEqual(snippets[0].arguments.split(), ['shell', ':results', 'verbatim'])
|
self.assertEqual(
|
||||||
|
snippets[0].arguments.split(), ["shell", ":results", "verbatim"]
|
||||||
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
snippets[0].result,
|
snippets[0].result,
|
||||||
"This is a test\n" + "with two lines",
|
"This is a test\n" + "with two lines",
|
||||||
@ -489,10 +506,10 @@ class TestSerde(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
snippets[2].content,
|
snippets[2].content,
|
||||||
'/* This code has to be escaped to\n'
|
"/* This code has to be escaped to\n"
|
||||||
+ ' * avoid confusion with new headlines.\n'
|
+ " * avoid confusion with new headlines.\n"
|
||||||
+ ' */\n'
|
+ " */\n"
|
||||||
+ 'main(){}',
|
+ "main(){}",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_mimic_write_file_05(self):
|
def test_mimic_write_file_05(self):
|
||||||
@ -530,7 +547,7 @@ class TestSerde(unittest.TestCase):
|
|||||||
hl_schedule_range = hl.children[1]
|
hl_schedule_range = hl.children[1]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
hl_schedule_range.scheduled.time,
|
hl_schedule_range.scheduled.time,
|
||||||
Timestamp(True, 2020, 12, 15, "Mar", 0, 5, '++1w')
|
Timestamp(True, 2020, 12, 15, "Mar", 0, 5, "++1w"),
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
hl_schedule_range.scheduled.end_time,
|
hl_schedule_range.scheduled.end_time,
|
||||||
@ -538,7 +555,7 @@ class TestSerde(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
hl_schedule_range.scheduled.repetition,
|
hl_schedule_range.scheduled.repetition,
|
||||||
'++1w',
|
"++1w",
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_update_info_file_05(self):
|
def test_update_info_file_05(self):
|
||||||
@ -591,7 +608,8 @@ class TestSerde(unittest.TestCase):
|
|||||||
MarkerToken(closing=False, tok_type=MarkerType.UNDERLINED_MODE),
|
MarkerToken(closing=False, tok_type=MarkerType.UNDERLINED_MODE),
|
||||||
"markup",
|
"markup",
|
||||||
MarkerToken(closing=True, tok_type=MarkerType.UNDERLINED_MODE),
|
MarkerToken(closing=True, tok_type=MarkerType.UNDERLINED_MODE),
|
||||||
".", "\n"
|
".",
|
||||||
|
"\n",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -625,12 +643,24 @@ class TestSerde(unittest.TestCase):
|
|||||||
print(lists4)
|
print(lists4)
|
||||||
self.assertEqual(len(lists4), 2)
|
self.assertEqual(len(lists4), 2)
|
||||||
|
|
||||||
self.assertEqual(lists4[0][0].content, ["This is a list item...", "\n that spans multiple lines", "\n"])
|
self.assertEqual(
|
||||||
|
lists4[0][0].content,
|
||||||
|
["This is a list item...", "\n that spans multiple lines", "\n"],
|
||||||
|
)
|
||||||
self.assertEqual(lists4[0][0].bullet, "-")
|
self.assertEqual(lists4[0][0].bullet, "-")
|
||||||
self.assertEqual(lists4[0][1].content, ["This is another list item...", "\n that has content on multiple lines", "\n"])
|
self.assertEqual(
|
||||||
|
lists4[0][1].content,
|
||||||
|
[
|
||||||
|
"This is another list item...",
|
||||||
|
"\n that has content on multiple lines",
|
||||||
|
"\n",
|
||||||
|
],
|
||||||
|
)
|
||||||
self.assertEqual(lists4[0][1].bullet, "-")
|
self.assertEqual(lists4[0][1].bullet, "-")
|
||||||
|
|
||||||
self.assertEqual(lists4[1][0].content, ["This is another", "\n multiline list", "\n"])
|
self.assertEqual(
|
||||||
|
lists4[1][0].content, ["This is another", "\n multiline list", "\n"]
|
||||||
|
)
|
||||||
self.assertEqual(lists4[1][0].bullet, "-")
|
self.assertEqual(lists4[1][0].bullet, "-")
|
||||||
|
|
||||||
def test_org_roam_07(self):
|
def test_org_roam_07(self):
|
||||||
@ -674,20 +704,22 @@ class TestSerde(unittest.TestCase):
|
|||||||
""".strip(),
|
""".strip(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_markup_file_09(self):
|
def test_markup_file_09(self):
|
||||||
with open(os.path.join(DIR, "09-markup-on-headline.org")) as f:
|
with open(os.path.join(DIR, "09-markup-on-headline.org")) as f:
|
||||||
doc = load(f)
|
doc = load(f)
|
||||||
|
|
||||||
hl = doc.getTopHeadlines()[0]
|
hl = doc.getTopHeadlines()[0]
|
||||||
print(hl.title)
|
print(hl.title)
|
||||||
self.assertEqual(hl.title.contents, [
|
self.assertEqual(
|
||||||
'Headline ',
|
hl.title.contents,
|
||||||
|
[
|
||||||
|
"Headline ",
|
||||||
MarkerToken(closing=False, tok_type=MarkerType.UNDERLINED_MODE),
|
MarkerToken(closing=False, tok_type=MarkerType.UNDERLINED_MODE),
|
||||||
'with',
|
"with",
|
||||||
MarkerToken(closing=True, tok_type=MarkerType.UNDERLINED_MODE),
|
MarkerToken(closing=True, tok_type=MarkerType.UNDERLINED_MODE),
|
||||||
' markup',
|
" markup",
|
||||||
])
|
],
|
||||||
|
)
|
||||||
|
|
||||||
def test_mimic_write_file_10(self):
|
def test_mimic_write_file_10(self):
|
||||||
with open(os.path.join(DIR, "10-tables.org")) as f:
|
with open(os.path.join(DIR, "10-tables.org")) as f:
|
||||||
@ -708,9 +740,9 @@ class TestSerde(unittest.TestCase):
|
|||||||
|
|
||||||
print(first_table[0])
|
print(first_table[0])
|
||||||
self.assertEqual(len(first_table[0].cells), 3)
|
self.assertEqual(len(first_table[0].cells), 3)
|
||||||
self.assertEqual(first_table[0].cells[0].strip(), 'Header1')
|
self.assertEqual(first_table[0].cells[0].strip(), "Header1")
|
||||||
self.assertEqual(first_table[0].cells[1].strip(), 'Header2')
|
self.assertEqual(first_table[0].cells[1].strip(), "Header2")
|
||||||
self.assertEqual(first_table[0].cells[2].strip(), 'Header3')
|
self.assertEqual(first_table[0].cells[2].strip(), "Header3")
|
||||||
|
|
||||||
hl = hl.children[0]
|
hl = hl.children[0]
|
||||||
|
|
||||||
@ -720,9 +752,9 @@ class TestSerde(unittest.TestCase):
|
|||||||
|
|
||||||
print(first_table[0])
|
print(first_table[0])
|
||||||
self.assertEqual(len(first_table[0].cells), 3)
|
self.assertEqual(len(first_table[0].cells), 3)
|
||||||
self.assertEqual(first_table[0].cells[0].strip(), 'Header1')
|
self.assertEqual(first_table[0].cells[0].strip(), "Header1")
|
||||||
self.assertEqual(first_table[0].cells[1].strip(), 'Header2')
|
self.assertEqual(first_table[0].cells[1].strip(), "Header2")
|
||||||
self.assertEqual(first_table[0].cells[2].strip(), 'Header3')
|
self.assertEqual(first_table[0].cells[2].strip(), "Header3")
|
||||||
|
|
||||||
def test_tables_html_file_10(self):
|
def test_tables_html_file_10(self):
|
||||||
with open(os.path.join(DIR, "10-tables.org")) as f:
|
with open(os.path.join(DIR, "10-tables.org")) as f:
|
||||||
@ -732,27 +764,26 @@ class TestSerde(unittest.TestCase):
|
|||||||
|
|
||||||
tree = hl.as_dom()
|
tree = hl.as_dom()
|
||||||
non_props = [
|
non_props = [
|
||||||
item
|
item for item in tree if not isinstance(item, dom.PropertyDrawerNode)
|
||||||
for item in tree
|
|
||||||
if not isinstance(item, dom.PropertyDrawerNode)
|
|
||||||
]
|
]
|
||||||
self.assertTrue(isinstance(non_props[0], dom.Text)
|
self.assertTrue(
|
||||||
|
isinstance(non_props[0], dom.Text)
|
||||||
and isinstance(non_props[1], dom.TableNode)
|
and isinstance(non_props[1], dom.TableNode)
|
||||||
and isinstance(non_props[2], dom.Text),
|
and isinstance(non_props[2], dom.Text),
|
||||||
'Expected <Text><Table><Text>')
|
"Expected <Text><Table><Text>",
|
||||||
|
)
|
||||||
|
|
||||||
hl = hl.children[0]
|
hl = hl.children[0]
|
||||||
tree = hl.as_dom()
|
tree = hl.as_dom()
|
||||||
non_props = [
|
non_props = [
|
||||||
item
|
item
|
||||||
for item in tree
|
for item in tree
|
||||||
if not (isinstance(item, dom.PropertyDrawerNode)
|
if not (
|
||||||
or isinstance(item, dom.Text))
|
isinstance(item, dom.PropertyDrawerNode) or isinstance(item, dom.Text)
|
||||||
|
)
|
||||||
]
|
]
|
||||||
print_tree(non_props)
|
print_tree(non_props)
|
||||||
self.assertTrue(len(non_props) == 1,
|
self.assertTrue(len(non_props) == 1, "Expected <List>, with only (1) element")
|
||||||
'Expected <List>, with only (1) element')
|
|
||||||
|
|
||||||
def test_nested_lists_html_file_11(self):
|
def test_nested_lists_html_file_11(self):
|
||||||
with open(os.path.join(DIR, "11-nested-lists.org")) as f:
|
with open(os.path.join(DIR, "11-nested-lists.org")) as f:
|
||||||
@ -762,30 +793,38 @@ class TestSerde(unittest.TestCase):
|
|||||||
|
|
||||||
tree = hl.as_dom()
|
tree = hl.as_dom()
|
||||||
non_props = [
|
non_props = [
|
||||||
item
|
item for item in tree if not isinstance(item, dom.PropertyDrawerNode)
|
||||||
for item in tree
|
|
||||||
if not isinstance(item, dom.PropertyDrawerNode)
|
|
||||||
]
|
]
|
||||||
print_tree(non_props)
|
print_tree(non_props)
|
||||||
self.assertTrue((len(non_props) == 1) and (isinstance(non_props[0], dom.ListGroupNode)),
|
self.assertTrue(
|
||||||
'Expected only <List> as top level')
|
(len(non_props) == 1) and (isinstance(non_props[0], dom.ListGroupNode)),
|
||||||
|
"Expected only <List> as top level",
|
||||||
|
)
|
||||||
|
|
||||||
dom_list = non_props[0]
|
dom_list = non_props[0]
|
||||||
children = dom_list.children
|
children = dom_list.children
|
||||||
self.assertTrue(len(children) == 5, 'Expected 5 items inside <List>, 3 texts and 2 sublists')
|
self.assertTrue(
|
||||||
|
len(children) == 5, "Expected 5 items inside <List>, 3 texts and 2 sublists"
|
||||||
|
)
|
||||||
|
|
||||||
# Assert texts
|
# Assert texts
|
||||||
self.assertEqual(children[0].content, ['1'])
|
self.assertEqual(children[0].content, ["1"])
|
||||||
self.assertEqual(children[2].content, ['2'])
|
self.assertEqual(children[2].content, ["2"])
|
||||||
self.assertEqual(children[4].content[0], '3') # Might be ['3', '\n'] but shouldn't be a breaking change
|
self.assertEqual(
|
||||||
|
children[4].content[0], "3"
|
||||||
|
) # Might be ['3', '\n'] but shouldn't be a breaking change
|
||||||
|
|
||||||
# Assert lists
|
# Assert lists
|
||||||
self.assertTrue(isinstance(children[1], dom.ListGroupNode), 'Expected sublist inside "1"')
|
self.assertTrue(
|
||||||
self.assertEqual(children[1].children[0].content, ['1.1'])
|
isinstance(children[1], dom.ListGroupNode), 'Expected sublist inside "1"'
|
||||||
self.assertEqual(children[1].children[1].content, ['1.2'])
|
)
|
||||||
self.assertTrue(isinstance(children[3], dom.ListGroupNode), 'Expected sublist inside "2"')
|
self.assertEqual(children[1].children[0].content, ["1.1"])
|
||||||
self.assertEqual(children[3].children[0].content, ['2.1'])
|
self.assertEqual(children[1].children[1].content, ["1.2"])
|
||||||
self.assertEqual(children[3].children[1].content, ['2.2'])
|
self.assertTrue(
|
||||||
|
isinstance(children[3], dom.ListGroupNode), 'Expected sublist inside "2"'
|
||||||
|
)
|
||||||
|
self.assertEqual(children[3].children[0].content, ["2.1"])
|
||||||
|
self.assertEqual(children[3].children[1].content, ["2.2"])
|
||||||
|
|
||||||
def test_mimic_write_file_12(self):
|
def test_mimic_write_file_12(self):
|
||||||
with open(os.path.join(DIR, "12-headlines-with-skip-levels.org")) as f:
|
with open(os.path.join(DIR, "12-headlines-with-skip-levels.org")) as f:
|
||||||
@ -812,6 +851,10 @@ def print_element(element, indentation, headline):
|
|||||||
if isinstance(element, org_rw.Link):
|
if isinstance(element, org_rw.Link):
|
||||||
print(" " * indentation * 2, "Link:", element.get_raw())
|
print(" " * indentation * 2, "Link:", element.get_raw())
|
||||||
elif isinstance(element, str):
|
elif isinstance(element, str):
|
||||||
print(" " * indentation * 2, "Str[" + element.replace('\n', '<NL>') + "]", type(element))
|
print(
|
||||||
|
" " * indentation * 2,
|
||||||
|
"Str[" + element.replace("\n", "<NL>") + "]",
|
||||||
|
type(element),
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
print_tree(element, indentation, headline)
|
print_tree(element, indentation, headline)
|
||||||
|
84
tests/test_timestamp.py
Normal file
84
tests/test_timestamp.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
"""Test the Timestamp object."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from datetime import date, datetime
|
||||||
|
from org_rw import Timestamp
|
||||||
|
|
||||||
|
|
||||||
|
def test_init_with_datetime() -> None:
|
||||||
|
datetime_obj: datetime = datetime(2024, 7, 20, 15, 45)
|
||||||
|
|
||||||
|
ts: Timestamp = Timestamp(active=True, datetime_=datetime_obj)
|
||||||
|
|
||||||
|
assert ts.active is True
|
||||||
|
assert ts._year == 2024
|
||||||
|
assert ts._month == 7
|
||||||
|
assert ts._day == 20
|
||||||
|
assert ts.hour == 15
|
||||||
|
assert ts.minute == 45
|
||||||
|
assert ts.dow is None
|
||||||
|
assert ts.repetition is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_init_with_date() -> None:
|
||||||
|
date_obj: date = date(2024, 7, 20)
|
||||||
|
|
||||||
|
ts: Timestamp = Timestamp(active=True, datetime_=date_obj)
|
||||||
|
|
||||||
|
assert ts.active is True
|
||||||
|
assert ts._year == 2024
|
||||||
|
assert ts._month == 7
|
||||||
|
assert ts._day == 20
|
||||||
|
assert ts.hour is None
|
||||||
|
assert ts.minute is None
|
||||||
|
assert ts.dow is None
|
||||||
|
assert ts.repetition is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_init_with_year_month_day() -> None:
|
||||||
|
ts: Timestamp = Timestamp(
|
||||||
|
active=True,
|
||||||
|
year=2024,
|
||||||
|
month=7,
|
||||||
|
day=20,
|
||||||
|
hour=15,
|
||||||
|
minute=45,
|
||||||
|
dow="Saturday",
|
||||||
|
repetition=".+1d",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert ts.active is True
|
||||||
|
assert ts._year == 2024
|
||||||
|
assert ts._month == 7
|
||||||
|
assert ts._day == 20
|
||||||
|
assert ts.hour == 15
|
||||||
|
assert ts.minute == 45
|
||||||
|
assert ts.dow == "Saturday"
|
||||||
|
assert ts.repetition == ".+1d"
|
||||||
|
|
||||||
|
|
||||||
|
def test_init_without_required_arguments() -> None:
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
Timestamp(active=True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_init_with_partial_date_info() -> None:
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
Timestamp(active=True, year=2024, month=7)
|
||||||
|
|
||||||
|
|
||||||
|
def test_init_with_datetime_overrides_date_info() -> None:
|
||||||
|
datetime_obj: datetime = datetime(2024, 7, 20, 15, 45)
|
||||||
|
|
||||||
|
ts: Timestamp = Timestamp(
|
||||||
|
active=True, year=2020, month=1, day=1, datetime_=datetime_obj
|
||||||
|
)
|
||||||
|
|
||||||
|
assert ts.active is True
|
||||||
|
assert ts._year == 2024
|
||||||
|
assert ts._month == 7
|
||||||
|
assert ts._day == 20
|
||||||
|
assert ts.hour == 15
|
||||||
|
assert ts.minute == 45
|
||||||
|
assert ts.dow is None
|
||||||
|
assert ts.repetition is None
|
Loading…
Reference in New Issue
Block a user