Compare commits

...

26 Commits

Author SHA1 Message Date
8280949f23 Merge pull request 'feat: Simplify headline state setting.' (#12) from feat/cleaner-state-setting into develop
All checks were successful
Testing / pytest (push) Successful in 32s
Testing / mypy (push) Successful in 33s
Testing / style-formatting (push) Successful in 36s
Testing / style-sorted-imports (push) Successful in 25s
Testing / stability-extra-test (push) Successful in 29s
Reviewed-on: kenkeiras/org-rw#12
2024-10-07 21:26:19 +00:00
Sergio Martínez Portela
691ce30a68 Simplify state setting, update .is_todo/.is_done props.
All checks were successful
Testing / pytest (push) Successful in 28s
Testing / mypy (push) Successful in 35s
Testing / style-formatting (push) Successful in 30s
Testing / style-sorted-imports (push) Successful in 24s
Testing / stability-extra-test (push) Successful in 32s
2024-10-07 23:23:15 +02:00
48de06abc7 Merge pull request 'feat: Name code snippets' (#11) from feat/named-code-snippets into develop
All checks were successful
Testing / pytest (push) Successful in 28s
Testing / mypy (push) Successful in 37s
Testing / style-formatting (push) Successful in 33s
Testing / style-sorted-imports (push) Successful in 24s
Testing / stability-extra-test (push) Successful in 26s
Reviewed-on: kenkeiras/org-rw#11
2024-10-06 22:26:00 +00:00
Sergio Martínez Portela
d4b40e404d Apply autoformatter.
All checks were successful
Testing / pytest (push) Successful in 28s
Testing / mypy (push) Successful in 37s
Testing / style-formatting (push) Successful in 33s
Testing / style-sorted-imports (push) Successful in 25s
Testing / stability-extra-test (push) Successful in 30s
2024-10-05 10:08:41 +02:00
Sergio Martínez Portela
5432c23202 Explicitly extract code block language.
Some checks failed
Testing / pytest (push) Successful in 26s
Testing / mypy (push) Successful in 35s
Testing / style-formatting (push) Failing after 30s
Testing / style-sorted-imports (push) Successful in 35s
Testing / stability-extra-test (push) Successful in 28s
2024-09-30 23:55:07 +02:00
Sergio Martínez Portela
8fe3c27595 Read names for code blocks. 2024-09-30 23:39:37 +02:00
Sergio Martínez Portela
1dc6eb0b43 fix: On OrgDoc.get_code_snippets, consider headlines of all levels. 2024-09-30 22:59:04 +02:00
5019b44dd5 Merge pull request 'Feat: Complete tags property' (#10) from feat/consider-file-tags-on-headlines into develop
All checks were successful
Testing / pytest (push) Successful in 28s
Testing / mypy (push) Successful in 36s
Testing / style-formatting (push) Successful in 28s
Testing / style-sorted-imports (push) Successful in 32s
Testing / stability-extra-test (push) Successful in 35s
Reviewed-on: kenkeiras/org-rw#10
2024-09-03 18:33:03 +00:00
Sergio Martínez Portela
78bc57e55d Fix formatting.
All checks were successful
Testing / pytest (push) Successful in 27s
Testing / mypy (push) Successful in 30s
Testing / style-formatting (push) Successful in 28s
Testing / style-sorted-imports (push) Successful in 22s
Testing / stability-extra-test (push) Successful in 36s
2024-09-01 23:51:38 +02:00
Sergio Martínez Portela
d4b0d0301f Test and implement org-use-tag-inheritance.
Some checks failed
Testing / pytest (push) Has been cancelled
Testing / mypy (push) Has been cancelled
Testing / style-formatting (push) Has been cancelled
Testing / style-sorted-imports (push) Has been cancelled
Testing / stability-extra-test (push) Has been cancelled
2024-09-01 23:51:10 +02:00
Sergio Martínez Portela
92078617fc Add tests and implement org-tags-exclude-from-inheritance.
Some checks failed
Testing / pytest (push) Successful in 25s
Testing / mypy (push) Successful in 31s
Testing / style-formatting (push) Failing after 31s
Testing / style-sorted-imports (push) Successful in 30s
Testing / stability-extra-test (push) Successful in 27s
2024-09-01 23:46:10 +02:00
Sergio Martínez Portela
852f472374 Implement OrgDoc .shallow_tags .
Some checks failed
Testing / pytest (push) Successful in 27s
Testing / mypy (push) Successful in 32s
Testing / style-formatting (push) Failing after 42s
Testing / style-sorted-imports (push) Successful in 28s
Testing / stability-extra-test (push) Successful in 28s
2024-09-01 23:37:26 +02:00
Sergio Martínez Portela
570e6bb764 Implement OrgDoc .tags. 2024-09-01 23:35:33 +02:00
Sergio Martínez Portela
e0306bf3a5 Add (failing) test for tags property read.
Some checks failed
Testing / pytest (push) Failing after 26s
Testing / mypy (push) Successful in 37s
Testing / style-formatting (push) Failing after 31s
Testing / style-sorted-imports (push) Successful in 25s
Testing / stability-extra-test (push) Has been cancelled
2024-09-01 23:35:23 +02:00
bfe60271eb Merge pull request 'Fix text parsing issues' (#9) from fix/require-whitespace-for-list-item-tag-separator into develop
All checks were successful
Testing / pytest (push) Successful in 33s
Testing / mypy (push) Successful in 32s
Testing / style-formatting (push) Successful in 26s
Testing / style-sorted-imports (push) Successful in 32s
Testing / stability-extra-test (push) Successful in 33s
Reviewed-on: kenkeiras/org-rw#9
2024-09-01 12:10:25 +00:00
Sergio Martínez Portela
4af4cda44b Fix formatting.
All checks were successful
Testing / pytest (push) Successful in 31s
Testing / mypy (push) Successful in 41s
Testing / style-formatting (push) Successful in 43s
Testing / style-sorted-imports (push) Successful in 28s
Testing / stability-extra-test (push) Successful in 41s
2024-08-22 00:26:11 +02:00
Sergio Martínez Portela
5552b3324b Handle ] which not close link descriptions or references.
Some checks failed
Testing / stability-extra-test (push) Waiting to run
Testing / pytest (push) Successful in 38s
Testing / style-sorted-imports (push) Waiting to run
Testing / mypy (push) Successful in 46s
Testing / style-formatting (push) Has been cancelled
2024-08-22 00:21:02 +02:00
Sergio Martínez Portela
f31c64c242 Properly track which tokens are used for closing formats. 2024-08-22 00:20:54 +02:00
Sergio Martínez Portela
490b36887a Require space before list item tag separator. 2024-08-22 00:20:15 +02:00
05718e1001 Merge pull request 'feat: Apply and check autoformatting' (#8) from apply-and-check-autoformatting into develop
All checks were successful
Testing / pytest (push) Successful in 43s
Testing / mypy (push) Successful in 54s
Testing / style-formatting (push) Successful in 55s
Testing / style-sorted-imports (push) Successful in 28s
Testing / stability-extra-test (push) Successful in 53s
Reviewed-on: kenkeiras/org-rw#8
2024-08-19 21:41:32 +00:00
Sergio Martínez Portela
e991074346 fix: Apply import sorting.
All checks were successful
Testing / pytest (push) Successful in 30s
Testing / mypy (push) Successful in 34s
Testing / style-formatting (push) Successful in 29s
Testing / style-sorted-imports (push) Successful in 26s
Testing / stability-extra-test (push) Successful in 32s
2024-08-18 22:49:33 +02:00
Sergio Martínez Portela
66b42e0b96 feat: Add script to apply formatting tools. 2024-08-18 22:49:06 +02:00
Sergio Martínez Portela
c6d8575ae5 test: Test sorted imports. 2024-08-18 22:47:42 +02:00
Sergio Martínez Portela
8ca480ad77 fix: Apply black formatter. 2024-08-18 22:47:24 +02:00
Sergio Martínez Portela
0a55c64551 test: Add formatting check to CI/CD. 2024-08-18 22:47:24 +02:00
991781a249 Merge pull request 'feat: enhance type annotations and formatting' (#7) from lyz/org-rw:feat/small-improvements into develop
All checks were successful
Testing / pytest (push) Successful in 37s
Testing / mypy (push) Successful in 34s
Testing / stability-extra-test (push) Successful in 51s
Reviewed-on: kenkeiras/org-rw#7
Reviewed-by: kenkeiras <kenkeiras@codigoparallevar.com>
2024-08-18 20:16:16 +00:00
10 changed files with 393 additions and 61 deletions

View File

@ -23,6 +23,26 @@ jobs:
- run: pip install mypy - run: pip install mypy
- run: mypy org_rw --check-untyped-defs - run: mypy org_rw --check-untyped-defs
style-formatting:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v3
- run: apt-get update && apt-get install -y python3-pip
- run: pip install -e .
- run: pip install black
- run: black --check .
style-sorted-imports:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v3
- run: apt-get update && apt-get install -y python3-pip
- run: pip install -e .
- run: pip install isort
- run: isort --profile black --check .
stability-extra-test: stability-extra-test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:

View File

@ -41,11 +41,12 @@ class ListGroupNode:
self.children.append(child) self.children.append(child)
def get_raw(self): def get_raw(self):
return '\n'.join([c.get_raw() for c in self.children]) return "\n".join([c.get_raw() for c in self.children])
def __repr__(self): def __repr__(self):
return "<List: {}>".format(len(self.children)) return "<List: {}>".format(len(self.children))
class TableNode: class TableNode:
def __init__(self): def __init__(self):
self.children = [] self.children = []
@ -56,21 +57,24 @@ class TableNode:
def __repr__(self): def __repr__(self):
return "<Table: {}>".format(len(self.children)) return "<Table: {}>".format(len(self.children))
class TableSeparatorRow: class TableSeparatorRow:
def __init__(self, orig=None): def __init__(self, orig=None):
self.orig = orig self.orig = orig
class TableRow: class TableRow:
def __init__(self, cells, orig=None): def __init__(self, cells, orig=None):
self.cells = cells self.cells = cells
self.orig = orig self.orig = orig
class Text: class Text:
def __init__(self, content): def __init__(self, content):
self.content = content self.content = content
def get_raw(self): def get_raw(self):
return ''.join(self.content.get_raw()) return "".join(self.content.get_raw())
class ListItem: class ListItem:
@ -105,21 +109,24 @@ class CodeBlock(BlockNode):
def __repr__(self): def __repr__(self):
return "<Code: {}>".format(len(self.lines or [])) return "<Code: {}>".format(len(self.lines or []))
DomNode = Union[DrawerNode,
PropertyNode,
ListGroupNode,
TableNode,
TableSeparatorRow,
TableRow,
Text,
ListItem,
BlockNode,
]
ContainerDomNode = Union[DrawerNode, DomNode = Union[
ListGroupNode, DrawerNode,
TableNode, PropertyNode,
BlockNode, ListGroupNode,
] TableNode,
TableSeparatorRow,
TableRow,
Text,
ListItem,
BlockNode,
]
ContainerDomNode = Union[
DrawerNode,
ListGroupNode,
TableNode,
BlockNode,
]
from .utils import get_raw_contents from .utils import get_raw_contents

View File

@ -1,6 +1,5 @@
from __future__ import annotations from __future__ import annotations
from typing import Dict, Optional, TextIO
from datetime import timedelta
import collections import collections
import difflib import difflib
import logging import logging
@ -9,12 +8,21 @@ import re
import sys import sys
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
from enum import Enum from enum import Enum
from typing import cast, Iterator, List, Literal, Optional, Tuple, TypedDict, Union from typing import (
Dict,
from .types import HeadlineDict Iterator,
List,
Literal,
Optional,
TextIO,
Tuple,
TypedDict,
Union,
cast,
)
from . import dom from . import dom
from .types import HeadlineDict
DEBUG_DIFF_CONTEXT = 10 DEBUG_DIFF_CONTEXT = 10
@ -95,7 +103,7 @@ PLANNING_RE = re.compile(
r")+\s*" r")+\s*"
) )
LIST_ITEM_RE = re.compile( LIST_ITEM_RE = re.compile(
r"(?P<indentation>\s*)((?P<bullet>[*\-+])|((?P<counter>\d|[a-zA-Z])(?P<counter_sep>[.)]))) ((?P<checkbox_indentation>\s*)\[(?P<checkbox_value>[ Xx])\])?((?P<tag_indentation>\s*)(?P<tag>.*?)::)?(?P<content>.*)" r"(?P<indentation>\s*)((?P<bullet>[*\-+])|((?P<counter>\d|[a-zA-Z])(?P<counter_sep>[.)]))) ((?P<checkbox_indentation>\s*)\[(?P<checkbox_value>[ Xx])\])?((?P<tag_indentation>\s*)((?P<tag>.*?)\s::))?(?P<content>.*)"
) )
IMPLICIT_LINK_RE = re.compile(r"(https?:[^<> ]*[a-zA-Z0-9])") IMPLICIT_LINK_RE = re.compile(r"(https?:[^<> ]*[a-zA-Z0-9])")
@ -105,7 +113,7 @@ BEGIN_BLOCK_RE = re.compile(r"^\s*#\+BEGIN_(?P<subtype>[^ ]+)(?P<arguments>.*)$"
END_BLOCK_RE = re.compile(r"^\s*#\+END_(?P<subtype>[^ ]+)\s*$", re.I) END_BLOCK_RE = re.compile(r"^\s*#\+END_(?P<subtype>[^ ]+)\s*$", re.I)
RESULTS_DRAWER_RE = re.compile(r"^\s*:results:\s*$", re.I) RESULTS_DRAWER_RE = re.compile(r"^\s*:results:\s*$", re.I)
CodeSnippet = collections.namedtuple( CodeSnippet = collections.namedtuple(
"CodeSnippet", ("name", "content", "result", "arguments") "CodeSnippet", ("name", "content", "result", "language", "arguments")
) )
# Groupings # Groupings
@ -329,7 +337,7 @@ class Headline:
self.priority = priority self.priority = priority
self.title_start = title_start self.title_start = title_start
self.title = parse_content_block([RawLine(linenum=start_line, line=title)]) self.title = parse_content_block([RawLine(linenum=start_line, line=title)])
self.state = state self._state = state
self.tags_start = tags_start self.tags_start = tags_start
self.shallow_tags = tags self.shallow_tags = tags
self.contents = contents self.contents = contents
@ -718,6 +726,42 @@ class Headline:
def id(self, value): def id(self, value):
self.set_property("ID", value) self.set_property("ID", value)
@property
def state(self) -> HeadlineState:
return self._state
@state.setter
def state(self, new_state: Union[None, str, HeadlineState]) -> None:
"""
Update the state of a Headline. If the state is a known one it will update it's TODO/DONE properties.
Args:
new_state (str|HeadlineState): New state, either it's literal value or it's structure.
"""
if new_state is None:
self.is_todo = False
self.is_done = False
# TODO: Check & log if appropriate?
self._state = None
return
if isinstance(new_state, str):
new_state = HeadlineState(name=new_state)
state_name = new_state["name"]
if state_name in [kw["name"] for kw in self.doc.todo_keywords]:
self.is_todo = True
self.is_done = False
# TODO: Check & log if appropriate?
elif state_name in [kw["name"] for kw in self.doc.done_keywords]:
self.is_todo = False
self.is_done = True
# TODO: Check, log & if appropriate?
else:
# TODO: Should we raise a warning, raise an exception, update the is_todo/is_done?
pass
self._state = new_state
@property @property
def clock(self): def clock(self):
times = [] times = []
@ -744,11 +788,20 @@ class Headline:
return times return times
@property @property
def tags(self): def tags(self) -> list[str]:
if isinstance(self.parent, OrgDoc): parent_tags = self.parent.tags
return list(self.shallow_tags) if self.doc.environment.get("org-use-tag-inheritance"):
else: accepted_tags = []
return list(self.shallow_tags) + self.parent.tags for tag in self.doc.environment.get("org-use-tag-inheritance"):
if tag in parent_tags:
accepted_tags.append(tag)
parent_tags = accepted_tags
elif self.doc.environment.get("org-tags-exclude-from-inheritance"):
for tag in self.doc.environment.get("org-tags-exclude-from-inheritance"):
if tag in parent_tags:
parent_tags.remove(tag)
return list(self.shallow_tags) + parent_tags
def add_tag(self, tag: str): def add_tag(self, tag: str):
self.shallow_tags.append(tag) self.shallow_tags.append(tag)
@ -865,6 +918,12 @@ class Headline:
sections = [] sections = []
arguments = None arguments = None
names_by_line = {}
for kw in self.keywords:
if kw.key == "NAME":
names_by_line[kw.linenum] = kw.value
name = None
for delimiter in self.delimiters: for delimiter in self.delimiters:
if ( if (
delimiter.delimiter_type == DelimiterLineType.BEGIN_BLOCK delimiter.delimiter_type == DelimiterLineType.BEGIN_BLOCK
@ -873,6 +932,12 @@ class Headline:
line_start = delimiter.linenum line_start = delimiter.linenum
inside_code = True inside_code = True
arguments = delimiter.arguments arguments = delimiter.arguments
name_line = line_start - 1
if name_line in names_by_line:
name = names_by_line[name_line]
else:
name = None
elif ( elif (
delimiter.delimiter_type == DelimiterLineType.END_BLOCK delimiter.delimiter_type == DelimiterLineType.END_BLOCK
and delimiter.type_data.subtype.lower() == "src" and delimiter.type_data.subtype.lower() == "src"
@ -887,14 +952,26 @@ class Headline:
# the content parsing must be re-thinked # the content parsing must be re-thinked
contents = contents[:-1] contents = contents[:-1]
language = None
if arguments is not None:
arguments = arguments.strip()
if " " in arguments:
language = arguments[: arguments.index(" ")]
arguments = arguments[arguments.index(" ") + 1 :]
else:
language = arguments
arguments = None
sections.append( sections.append(
{ {
"line_first": start + 1, "line_first": start + 1,
"line_last": end - 1, "line_last": end - 1,
"content": contents, "content": contents,
"arguments": arguments, "arguments": arguments,
"language": language,
"name": name,
} }
) )
name = None
arguments = None arguments = None
line_start = None line_start = None
@ -943,13 +1020,18 @@ class Headline:
results = [] results = []
for section in sections: for section in sections:
name = None
content = section["content"] content = section["content"]
code_result = section.get("result", None) code_result = section.get("result", None)
arguments = section.get("arguments", None) arguments = section.get("arguments", None)
language = section.get("language", None)
name = section.get("name", None)
results.append( results.append(
CodeSnippet( CodeSnippet(
name=name, content=content, result=code_result, arguments=arguments content=content,
result=code_result,
arguments=arguments,
language=language,
name=name,
) )
) )
@ -1903,7 +1985,12 @@ def tokenize_contents(contents: str) -> List[TokenItems]:
continue continue
# Possible link close or open of description # Possible link close or open of description
if char == "]" and len(contents) > i + 1 and in_link: if (
char == "]"
and len(contents) > i + 1
and in_link
and contents[i + 1] in "]["
):
if contents[i + 1] == "]": if contents[i + 1] == "]":
cut_string() cut_string()
@ -1954,6 +2041,7 @@ def tokenize_contents(contents: str) -> List[TokenItems]:
cut_string() cut_string()
tokens.append((TOKEN_TYPE_CLOSE_MARKER, char)) tokens.append((TOKEN_TYPE_CLOSE_MARKER, char))
has_changed = True has_changed = True
closes.remove(i)
if not has_changed: if not has_changed:
text.append(char) text.append(char)
@ -2044,7 +2132,7 @@ def dump_contents(raw):
content = "\n".join(content_lines) content = "\n".join(content_lines)
checkbox = f"[{raw.checkbox_value}]" if raw.checkbox_value else "" checkbox = f"[{raw.checkbox_value}]" if raw.checkbox_value else ""
tag = ( tag = (
f"{raw.tag_indentation}{token_list_to_raw(raw.tag or '')}::" f"{raw.tag_indentation}{token_list_to_raw(raw.tag or '')} ::"
if raw.tag or raw.tag_indentation if raw.tag or raw.tag_indentation
else "" else ""
) )
@ -2223,6 +2311,7 @@ class OrgDoc:
): ):
self.todo_keywords = [HeadlineState(name=kw) for kw in DEFAULT_TODO_KEYWORDS] self.todo_keywords = [HeadlineState(name=kw) for kw in DEFAULT_TODO_KEYWORDS]
self.done_keywords = [HeadlineState(name=kw) for kw in DEFAULT_DONE_KEYWORDS] self.done_keywords = [HeadlineState(name=kw) for kw in DEFAULT_DONE_KEYWORDS]
self.environment = environment
keywords_set_in_file = False keywords_set_in_file = False
for keyword in keywords: for keyword in keywords:
@ -2266,6 +2355,17 @@ class OrgDoc:
def path(self): def path(self):
return self._path return self._path
@property
def tags(self) -> list[str]:
for kw in self.keywords:
if kw.key == "FILETAGS":
return kw.value.strip(":").split(":")
return []
@property
def shallow_tags(self) -> list[str]:
return self.tags
## Querying ## Querying
def get_links(self): def get_links(self):
for headline in self.headlines: for headline in self.headlines:
@ -2303,7 +2403,7 @@ class OrgDoc:
yield hl yield hl
def get_code_snippets(self): def get_code_snippets(self):
for headline in self.headlines: for headline in self.getAllHeadlines():
yield from headline.get_code_snippets() yield from headline.get_code_snippets()
# Writing # Writing
@ -2314,8 +2414,8 @@ class OrgDoc:
tags = ":" + ":".join(headline.shallow_tags) + ":" tags = ":" + ":".join(headline.shallow_tags) + ":"
state = "" state = ""
if headline.state: if headline._state:
state = headline.state["name"] + " " state = headline._state["name"] + " "
raw_title = token_list_to_raw(headline.title.contents) raw_title = token_list_to_raw(headline.title.contents)
tags_padding = "" tags_padding = ""

View File

@ -1,9 +1,19 @@
import uuid import uuid
from .org_rw import (Bold, Code, Headline, Italic, Line, RawLine, ListItem, Strike, Text, from .org_rw import (
Underlined, Verbatim) Bold,
Code,
from .org_rw import dump_contents Headline,
Italic,
Line,
ListItem,
RawLine,
Strike,
Text,
Underlined,
Verbatim,
dump_contents,
)
def get_hl_raw_contents(doc: Headline) -> str: def get_hl_raw_contents(doc: Headline) -> str:

11
scripts/apply-formatting.sh Executable file
View File

@ -0,0 +1,11 @@
#!/bin/sh
set -eu
cd "`dirname $0`"
cd ..
set -x
isort --profile black .
black .

View File

@ -9,6 +9,7 @@
:CREATED: [2020-01-01 Wed 01:01] :CREATED: [2020-01-01 Wed 01:01]
:END: :END:
#+NAME: first-code-name
#+BEGIN_SRC shell :results verbatim #+BEGIN_SRC shell :results verbatim
echo "This is a test" echo "This is a test"
echo "with two lines" echo "with two lines"

13
tests/13-tags.org Normal file
View File

@ -0,0 +1,13 @@
#+TITLE: 13-Tags
#+DESCRIPTION: Simple org file to test tags
#+FILETAGS: :filetag:
* Level 1 :h1tag:
:PROPERTIES:
:ID: 13-tags
:CREATED: [2020-01-01 Wed 01:01]
:END:
** Level2 :h2tag:
* Level 1-1 :otherh1tag:
** Level2 :otherh2tag:

View File

@ -2,9 +2,6 @@ import os
import unittest import unittest
from datetime import datetime as DT from datetime import datetime as DT
from org_rw import MarkerToken, MarkerType, Timestamp, dumps, load, loads, dom
import org_rw
from utils.assertions import ( from utils.assertions import (
BOLD, BOLD,
CODE, CODE,
@ -19,6 +16,9 @@ from utils.assertions import (
Tokens, Tokens,
) )
import org_rw
from org_rw import MarkerToken, MarkerType, Timestamp, dom, dumps, load, loads
DIR = os.path.dirname(os.path.abspath(__file__)) DIR = os.path.dirname(os.path.abspath(__file__))
@ -480,20 +480,22 @@ class TestSerde(unittest.TestCase):
snippets = list(doc.get_code_snippets()) snippets = list(doc.get_code_snippets())
self.assertEqual(len(snippets), 3) self.assertEqual(len(snippets), 3)
self.assertEqual(snippets[0].name, "first-code-name")
self.assertEqual(snippets[0].language, "shell")
self.assertEqual( self.assertEqual(
snippets[0].content, snippets[0].content,
'echo "This is a test"\n' 'echo "This is a test"\n'
+ 'echo "with two lines"\n' + 'echo "with two lines"\n'
+ "exit 0 # Exit successfully", + "exit 0 # Exit successfully",
) )
self.assertEqual( self.assertEqual(snippets[0].arguments.split(), [":results", "verbatim"])
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",
) )
self.assertEqual(snippets[1].name, None)
self.assertEqual(snippets[1].language, "shell")
self.assertEqual( self.assertEqual(
snippets[1].content, snippets[1].content,
'echo "This is another test"\n' 'echo "This is another test"\n'
@ -504,6 +506,8 @@ class TestSerde(unittest.TestCase):
snippets[1].result, "This is another test\n" + "with two lines too" snippets[1].result, "This is another test\n" + "with two lines too"
) )
self.assertEqual(snippets[2].name, None)
self.assertEqual(snippets[2].language, "c")
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"
@ -834,12 +838,12 @@ class TestSerde(unittest.TestCase):
self.assertEqual(dumps(doc), orig) self.assertEqual(dumps(doc), orig)
def test_add_todo_keywords_programatically(self): def test_add_todo_keywords_programatically(self):
orig = '''* NEW_TODO_STATE First entry orig = """* NEW_TODO_STATE First entry
* NEW_DONE_STATE Second entry''' * NEW_DONE_STATE Second entry"""
doc = loads(orig, environment={ doc = loads(
'org-todo-keywords': "NEW_TODO_STATE | NEW_DONE_STATE" orig, environment={"org-todo-keywords": "NEW_TODO_STATE | NEW_DONE_STATE"}
}) )
self.assertEqual(doc.headlines[0].is_todo, True) self.assertEqual(doc.headlines[0].is_todo, True)
self.assertEqual(doc.headlines[0].is_done, False) self.assertEqual(doc.headlines[0].is_done, False)
@ -849,14 +853,14 @@ class TestSerde(unittest.TestCase):
self.assertEqual(dumps(doc), orig) self.assertEqual(dumps(doc), orig)
def test_add_todo_keywords_in_file(self): def test_add_todo_keywords_in_file(self):
orig = '''#+TODO: NEW_TODO_STATE | NEW_DONE_STATE orig = """#+TODO: NEW_TODO_STATE | NEW_DONE_STATE
* NEW_TODO_STATE First entry * NEW_TODO_STATE First entry
* NEW_DONE_STATE Second entry''' * NEW_DONE_STATE Second entry"""
doc = loads(orig, environment={ doc = loads(
'org-todo-keywords': "NEW_TODO_STATE | NEW_DONE_STATE" orig, environment={"org-todo-keywords": "NEW_TODO_STATE | NEW_DONE_STATE"}
}) )
self.assertEqual(doc.headlines[0].is_todo, True) self.assertEqual(doc.headlines[0].is_todo, True)
self.assertEqual(doc.headlines[0].is_done, False) self.assertEqual(doc.headlines[0].is_done, False)
@ -865,6 +869,161 @@ class TestSerde(unittest.TestCase):
self.assertEqual(dumps(doc), orig) self.assertEqual(dumps(doc), orig)
def test_mimic_write_file_13(self):
with open(os.path.join(DIR, "13-tags.org")) as f:
orig = f.read()
doc = loads(orig)
self.assertEqual(dumps(doc), orig)
def test_tag_property_read_13(self):
with open(os.path.join(DIR, "13-tags.org")) as f:
orig = f.read()
doc = loads(orig)
self.assertEqual(doc.tags, ["filetag"])
h1_1, h1_2 = doc.getTopHeadlines()
self.assertEqual(sorted(h1_1.tags), ["filetag", "h1tag"])
self.assertEqual(sorted(h1_2.tags), ["filetag", "otherh1tag"])
h1_1_h2 = h1_1.children[0]
self.assertEqual(sorted(h1_1_h2.tags), ["filetag", "h1tag", "h2tag"])
h1_2_h2 = h1_2.children[0]
self.assertEqual(sorted(h1_2_h2.tags), ["filetag", "otherh1tag", "otherh2tag"])
def test_shallow_tag_property_read_13(self):
with open(os.path.join(DIR, "13-tags.org")) as f:
orig = f.read()
doc = loads(orig)
self.assertEqual(doc.shallow_tags, ["filetag"])
h1_1, h1_2 = doc.getTopHeadlines()
self.assertEqual(sorted(h1_1.shallow_tags), ["h1tag"])
self.assertEqual(sorted(h1_2.shallow_tags), ["otherh1tag"])
h1_1_h2 = h1_1.children[0]
self.assertEqual(sorted(h1_1_h2.shallow_tags), ["h2tag"])
h1_2_h2 = h1_2.children[0]
self.assertEqual(sorted(h1_2_h2.shallow_tags), ["otherh2tag"])
def test_exclude_tags_from_inheritance_property_read_13(self):
with open(os.path.join(DIR, "13-tags.org")) as f:
orig = f.read()
doc = loads(
orig,
{
"org-tags-exclude-from-inheritance": ("h1tag", "otherh2tag"),
},
)
self.assertEqual(doc.tags, ["filetag"])
h1_1, h1_2 = doc.getTopHeadlines()
self.assertEqual(sorted(h1_1.tags), ["filetag", "h1tag"])
self.assertEqual(sorted(h1_2.tags), ["filetag", "otherh1tag"])
h1_1_h2 = h1_1.children[0]
self.assertEqual(sorted(h1_1_h2.tags), ["filetag", "h2tag"])
h1_2_h2 = h1_2.children[0]
self.assertEqual(sorted(h1_2_h2.tags), ["filetag", "otherh1tag", "otherh2tag"])
def test_select_tags_to_inheritance_property_read_13(self):
with open(os.path.join(DIR, "13-tags.org")) as f:
orig = f.read()
doc = loads(
orig,
{
"org-tags-exclude-from-inheritance": ("h1tag", "otherh2tag"),
"org-use-tag-inheritance": ("h1tag",),
},
)
self.assertEqual(doc.tags, ["filetag"])
h1_1, h1_2 = doc.getTopHeadlines()
self.assertEqual(sorted(h1_1.tags), ["h1tag"])
self.assertEqual(sorted(h1_2.tags), ["otherh1tag"])
h1_1_h2 = h1_1.children[0]
self.assertEqual(sorted(h1_1_h2.tags), ["h1tag", "h2tag"])
h1_2_h2 = h1_2.children[0]
self.assertEqual(sorted(h1_2_h2.tags), ["otherh2tag"])
def test_update_headline_from_none_to_todo(self):
orig = "* First entry"
doc = loads(orig)
self.assertEqual(doc.headlines[0].is_todo, False)
self.assertEqual(doc.headlines[0].is_done, False)
self.assertEqual(doc.headlines[0].state, None)
doc.headlines[0].state = "TODO"
self.assertEqual(doc.headlines[0].is_todo, True)
self.assertEqual(doc.headlines[0].is_done, False)
self.assertEqual(doc.headlines[0].state["name"], "TODO")
self.assertEqual(dumps(doc), "* TODO First entry")
def test_update_headline_from_none_to_done(self):
orig = "* First entry"
doc = loads(orig)
self.assertEqual(doc.headlines[0].is_todo, False)
self.assertEqual(doc.headlines[0].is_done, False)
self.assertEqual(doc.headlines[0].state, None)
doc.headlines[0].state = org_rw.HeadlineState(name="DONE")
self.assertEqual(doc.headlines[0].is_todo, False)
self.assertEqual(doc.headlines[0].is_done, True)
self.assertEqual(doc.headlines[0].state["name"], "DONE")
self.assertEqual(dumps(doc), "* DONE First entry")
def test_update_headline_from_todo_to_none(self):
orig = "* TODO First entry"
doc = loads(orig)
self.assertEqual(doc.headlines[0].is_todo, True)
self.assertEqual(doc.headlines[0].is_done, False)
self.assertEqual(doc.headlines[0].state["name"], "TODO")
doc.headlines[0].state = None
self.assertEqual(doc.headlines[0].is_todo, False)
self.assertEqual(doc.headlines[0].is_done, False)
self.assertEqual(doc.headlines[0].state, None)
self.assertEqual(dumps(doc), "* First entry")
def test_update_headline_from_todo_to_done(self):
orig = "* TODO First entry"
doc = loads(orig)
self.assertEqual(doc.headlines[0].is_todo, True)
self.assertEqual(doc.headlines[0].is_done, False)
self.assertEqual(doc.headlines[0].state["name"], "TODO")
doc.headlines[0].state = "DONE"
self.assertEqual(doc.headlines[0].is_todo, False)
self.assertEqual(doc.headlines[0].is_done, True)
self.assertEqual(doc.headlines[0].state["name"], "DONE")
self.assertEqual(dumps(doc), "* DONE First entry")
def test_update_headline_from_done_to_todo(self):
orig = "* DONE First entry"
doc = loads(orig)
self.assertEqual(doc.headlines[0].is_todo, False)
self.assertEqual(doc.headlines[0].is_done, True)
self.assertEqual(doc.headlines[0].state["name"], "DONE")
doc.headlines[0].state = org_rw.HeadlineState(name="TODO")
self.assertEqual(doc.headlines[0].is_todo, True)
self.assertEqual(doc.headlines[0].is_done, False)
self.assertEqual(doc.headlines[0].state["name"], "TODO")
self.assertEqual(dumps(doc), "* TODO First entry")
def print_tree(tree, indentation=0, headline=None): def print_tree(tree, indentation=0, headline=None):
for element in tree: for element in tree:

View File

@ -1,7 +1,9 @@
"""Test the Timestamp object.""" """Test the Timestamp object."""
import pytest
from datetime import date, datetime from datetime import date, datetime
import pytest
from org_rw import Timestamp from org_rw import Timestamp

View File

@ -2,8 +2,17 @@ import collections
import unittest import unittest
from datetime import datetime from datetime import datetime
from org_rw import (Bold, Code, Italic, Line, Strike, Text, Underlined, from org_rw import (
Verbatim, get_raw_contents) Bold,
Code,
Italic,
Line,
Strike,
Text,
Underlined,
Verbatim,
get_raw_contents,
)
def timestamp_to_datetime(ts): def timestamp_to_datetime(ts):