notekey 0.1.0__tar.gz → 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {notekey-0.1.0 → notekey-0.1.2}/PKG-INFO +12 -2
- {notekey-0.1.0 → notekey-0.1.2}/README.md +10 -1
- {notekey-0.1.0 → notekey-0.1.2}/notekey/main.py +16 -14
- {notekey-0.1.0 → notekey-0.1.2}/notekey/markdown.py +9 -5
- {notekey-0.1.0 → notekey-0.1.2}/notekey/template.py +2 -2
- {notekey-0.1.0 → notekey-0.1.2}/notekey/utils.py +1 -3
- {notekey-0.1.0 → notekey-0.1.2}/notekey.egg-info/PKG-INFO +12 -2
- {notekey-0.1.0 → notekey-0.1.2}/notekey.egg-info/requires.txt +1 -0
- {notekey-0.1.0 → notekey-0.1.2}/pyproject.toml +2 -1
- {notekey-0.1.0 → notekey-0.1.2}/tests/test_main.py +83 -35
- {notekey-0.1.0 → notekey-0.1.2}/tests/test_markdown.py +8 -7
- {notekey-0.1.0 → notekey-0.1.2}/notekey/__init__.py +0 -0
- {notekey-0.1.0 → notekey-0.1.2}/notekey.egg-info/SOURCES.txt +0 -0
- {notekey-0.1.0 → notekey-0.1.2}/notekey.egg-info/dependency_links.txt +0 -0
- {notekey-0.1.0 → notekey-0.1.2}/notekey.egg-info/entry_points.txt +0 -0
- {notekey-0.1.0 → notekey-0.1.2}/notekey.egg-info/top_level.txt +0 -0
- {notekey-0.1.0 → notekey-0.1.2}/setup.cfg +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: notekey
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: CLI utilities for Obsidian vault notes: initialize note bases, search markdown files, and read note content.
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: black>=26.3.1
|
|
7
8
|
Requires-Dist: python-frontmatter>=1.1.0
|
|
8
9
|
Requires-Dist: readtime>=3.0.0
|
|
9
10
|
|
|
@@ -38,7 +39,9 @@ notekey search [path] [--tags TAGS] [--filename NAME] [--content TEXT]
|
|
|
38
39
|
notekey read FILENAME
|
|
39
40
|
```
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
For `init`, omitting `path` uses the current directory.
|
|
43
|
+
|
|
44
|
+
For `search` and `read`, omitting `path` uses the `OBSIDIAN_VAULT` environment variable. If that variable is not set, it uses the current directory.
|
|
42
45
|
|
|
43
46
|
```bash
|
|
44
47
|
export OBSIDIAN_VAULT="/path/to/your/vault"
|
|
@@ -46,6 +49,13 @@ export OBSIDIAN_VAULT="/path/to/your/vault"
|
|
|
46
49
|
|
|
47
50
|
### Initialize a note folder
|
|
48
51
|
|
|
52
|
+
```bash
|
|
53
|
+
cd /path/to/vault/Projects/MyProject
|
|
54
|
+
notekey init
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or pass the target folder explicitly:
|
|
58
|
+
|
|
49
59
|
```bash
|
|
50
60
|
notekey init /path/to/vault/Projects/MyProject
|
|
51
61
|
```
|
|
@@ -29,7 +29,9 @@ notekey search [path] [--tags TAGS] [--filename NAME] [--content TEXT]
|
|
|
29
29
|
notekey read FILENAME
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
For `init`, omitting `path` uses the current directory.
|
|
33
|
+
|
|
34
|
+
For `search` and `read`, omitting `path` uses the `OBSIDIAN_VAULT` environment variable. If that variable is not set, it uses the current directory.
|
|
33
35
|
|
|
34
36
|
```bash
|
|
35
37
|
export OBSIDIAN_VAULT="/path/to/your/vault"
|
|
@@ -37,6 +39,13 @@ export OBSIDIAN_VAULT="/path/to/your/vault"
|
|
|
37
39
|
|
|
38
40
|
### Initialize a note folder
|
|
39
41
|
|
|
42
|
+
```bash
|
|
43
|
+
cd /path/to/vault/Projects/MyProject
|
|
44
|
+
notekey init
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Or pass the target folder explicitly:
|
|
48
|
+
|
|
40
49
|
```bash
|
|
41
50
|
notekey init /path/to/vault/Projects/MyProject
|
|
42
51
|
```
|
|
@@ -45,12 +45,16 @@ def _build_filters(name: str, tags: str | None = None) -> str:
|
|
|
45
45
|
return ", ".join(f'"{tag}"' for tag in filters)
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def _create_base(
|
|
48
|
+
def _create_base(
|
|
49
|
+
target: Path, name: str, tags: str | None = None, force: bool = False
|
|
50
|
+
) -> Path:
|
|
49
51
|
vault_root = _find_vault_root(target)
|
|
50
52
|
folder = target.relative_to(vault_root).as_posix()
|
|
51
53
|
filters = _build_filters(name, tags)
|
|
52
54
|
base_path = target / f"{name}.base"
|
|
53
|
-
content = BASE_FILTER_TEMPLATE.format(
|
|
55
|
+
content = BASE_FILTER_TEMPLATE.format(
|
|
56
|
+
folder=folder, name=name, filters=filters
|
|
57
|
+
).lstrip()
|
|
54
58
|
|
|
55
59
|
if base_path.exists() and not force:
|
|
56
60
|
raise FileExistsError(f"Base file already exists: {base_path}")
|
|
@@ -124,9 +128,7 @@ def _search_files(
|
|
|
124
128
|
filename_exact, filename_val = (
|
|
125
129
|
_parse_filter(filename) if filename else (False, None)
|
|
126
130
|
)
|
|
127
|
-
content_exact, content_val = (
|
|
128
|
-
_parse_filter(content) if content else (False, None)
|
|
129
|
-
)
|
|
131
|
+
content_exact, content_val = _parse_filter(content) if content else (False, None)
|
|
130
132
|
parsed_tags: list[tuple[bool, str]] = (
|
|
131
133
|
[_parse_filter(t) for t in tags] if tags else []
|
|
132
134
|
)
|
|
@@ -199,11 +201,7 @@ def _display_search_results(results: list[Markdown], vault_root: Path) -> None:
|
|
|
199
201
|
|
|
200
202
|
for md in results:
|
|
201
203
|
rel = _relative_to_vault(md, vault_root)
|
|
202
|
-
tags_str = (
|
|
203
|
-
", ".join(md.tags[:5])
|
|
204
|
-
+ ("..." if len(md.tags) > 5 else "")
|
|
205
|
-
or "—"
|
|
206
|
-
)
|
|
204
|
+
tags_str = ", ".join(md.tags[:5]) + ("..." if len(md.tags) > 5 else "") or "—"
|
|
207
205
|
print(
|
|
208
206
|
f" {rel:<{name_width}} "
|
|
209
207
|
f"tags: [{tags_str}] "
|
|
@@ -236,7 +234,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
236
234
|
"path",
|
|
237
235
|
nargs="?",
|
|
238
236
|
default=None,
|
|
239
|
-
help="Target folder (defaults to
|
|
237
|
+
help="Target folder (defaults to current directory)",
|
|
240
238
|
)
|
|
241
239
|
init_parser.add_argument(
|
|
242
240
|
"-t",
|
|
@@ -250,7 +248,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
250
248
|
)
|
|
251
249
|
|
|
252
250
|
# -- search -------------------------------------------------------------
|
|
253
|
-
search_parser = subparsers.add_parser(
|
|
251
|
+
search_parser = subparsers.add_parser(
|
|
252
|
+
"search", help="Search markdown files in the vault"
|
|
253
|
+
)
|
|
254
254
|
search_parser.add_argument(
|
|
255
255
|
"path",
|
|
256
256
|
nargs="?",
|
|
@@ -274,7 +274,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
274
274
|
)
|
|
275
275
|
|
|
276
276
|
# -- read ---------------------------------------------------------------
|
|
277
|
-
read_parser = subparsers.add_parser(
|
|
277
|
+
read_parser = subparsers.add_parser(
|
|
278
|
+
"read", help="Read a single markdown file by name or path"
|
|
279
|
+
)
|
|
278
280
|
read_parser.add_argument(
|
|
279
281
|
"filename",
|
|
280
282
|
help="Filename or path to match — first match wins (prefix = for exact)",
|
|
@@ -288,7 +290,7 @@ def main() -> None:
|
|
|
288
290
|
args = parser.parse_args()
|
|
289
291
|
|
|
290
292
|
if args.command == "init":
|
|
291
|
-
target = _get_folder(
|
|
293
|
+
target = _get_folder(args.path)
|
|
292
294
|
current_location = str(target)
|
|
293
295
|
folder_name = target.name
|
|
294
296
|
base_path = _create_base(target, folder_name, tags=args.tags, force=args.force)
|
|
@@ -5,7 +5,12 @@ from typing import Any
|
|
|
5
5
|
import frontmatter
|
|
6
6
|
import readtime
|
|
7
7
|
|
|
8
|
-
from notekey.utils import
|
|
8
|
+
from notekey.utils import (
|
|
9
|
+
extract_inline_tags,
|
|
10
|
+
extract_markdown_links,
|
|
11
|
+
extract_wiki_links,
|
|
12
|
+
normalize_size,
|
|
13
|
+
)
|
|
9
14
|
|
|
10
15
|
|
|
11
16
|
class Markdown:
|
|
@@ -37,7 +42,8 @@ class Markdown:
|
|
|
37
42
|
self.name = self._path.stem
|
|
38
43
|
self._size_bytes: float = float(stat.st_size)
|
|
39
44
|
self.size = self._size_bytes
|
|
40
|
-
|
|
45
|
+
created_ts = getattr(stat, "st_birthtime", stat.st_ctime)
|
|
46
|
+
self.created_at = datetime.datetime.fromtimestamp(created_ts)
|
|
41
47
|
self.updated_at = datetime.datetime.fromtimestamp(stat.st_mtime)
|
|
42
48
|
|
|
43
49
|
# Full raw content (frontmatter + body) — needed for link extraction.
|
|
@@ -83,6 +89,4 @@ class Markdown:
|
|
|
83
89
|
return normalize_size(self._size_bytes)
|
|
84
90
|
|
|
85
91
|
def __repr__(self) -> str:
|
|
86
|
-
return (
|
|
87
|
-
f"<Markdown filename='{self.name}' size='{self._normalize_size()}'>"
|
|
88
|
-
)
|
|
92
|
+
return f"<Markdown filename='{self.name}' size='{self._normalize_size()}'>"
|
|
@@ -20,9 +20,7 @@ def extract_inline_tags(text: str) -> set[str]:
|
|
|
20
20
|
# Match #identifier (with optional /path). The tag body cannot
|
|
21
21
|
# contain a trailing period so that ``#tag.`` correctly yields ``tag``.
|
|
22
22
|
pattern = re.compile(
|
|
23
|
-
r"(?:^|(?<=\s))"
|
|
24
|
-
r"#([a-zA-Z_][a-zA-Z0-9_/-]*)"
|
|
25
|
-
r"(?=\s|$|[.,;:!?)])"
|
|
23
|
+
r"(?:^|(?<=\s))" r"#([a-zA-Z_][a-zA-Z0-9_/-]*)" r"(?=\s|$|[.,;:!?)])"
|
|
26
24
|
)
|
|
27
25
|
|
|
28
26
|
for match in pattern.finditer(body):
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: notekey
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: CLI utilities for Obsidian vault notes: initialize note bases, search markdown files, and read note content.
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: black>=26.3.1
|
|
7
8
|
Requires-Dist: python-frontmatter>=1.1.0
|
|
8
9
|
Requires-Dist: readtime>=3.0.0
|
|
9
10
|
|
|
@@ -38,7 +39,9 @@ notekey search [path] [--tags TAGS] [--filename NAME] [--content TEXT]
|
|
|
38
39
|
notekey read FILENAME
|
|
39
40
|
```
|
|
40
41
|
|
|
41
|
-
|
|
42
|
+
For `init`, omitting `path` uses the current directory.
|
|
43
|
+
|
|
44
|
+
For `search` and `read`, omitting `path` uses the `OBSIDIAN_VAULT` environment variable. If that variable is not set, it uses the current directory.
|
|
42
45
|
|
|
43
46
|
```bash
|
|
44
47
|
export OBSIDIAN_VAULT="/path/to/your/vault"
|
|
@@ -46,6 +49,13 @@ export OBSIDIAN_VAULT="/path/to/your/vault"
|
|
|
46
49
|
|
|
47
50
|
### Initialize a note folder
|
|
48
51
|
|
|
52
|
+
```bash
|
|
53
|
+
cd /path/to/vault/Projects/MyProject
|
|
54
|
+
notekey init
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Or pass the target folder explicitly:
|
|
58
|
+
|
|
49
59
|
```bash
|
|
50
60
|
notekey init /path/to/vault/Projects/MyProject
|
|
51
61
|
```
|
|
@@ -4,11 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "notekey"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.2"
|
|
8
8
|
description = "CLI utilities for Obsidian vault notes: initialize note bases, search markdown files, and read note content."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
11
11
|
dependencies = [
|
|
12
|
+
"black>=26.3.1",
|
|
12
13
|
"python-frontmatter>=1.1.0",
|
|
13
14
|
"readtime>=3.0.0",
|
|
14
15
|
]
|
|
@@ -25,7 +25,6 @@ from notekey.main import (
|
|
|
25
25
|
main,
|
|
26
26
|
)
|
|
27
27
|
|
|
28
|
-
|
|
29
28
|
# ---------------------------------------------------------------------------
|
|
30
29
|
# Fixtures: a mini vault with several .md files
|
|
31
30
|
# ---------------------------------------------------------------------------
|
|
@@ -39,8 +38,7 @@ def vault(tmp_path: Path) -> Path:
|
|
|
39
38
|
(root / ".obsidian").mkdir()
|
|
40
39
|
|
|
41
40
|
# File A: has tags "python" and "web", mentions "flask"
|
|
42
|
-
(root / "flask-app.md").write_text(
|
|
43
|
-
"""\
|
|
41
|
+
(root / "flask-app.md").write_text("""\
|
|
44
42
|
---
|
|
45
43
|
title: Flask App
|
|
46
44
|
tags: [python, web]
|
|
@@ -48,12 +46,10 @@ tags: [python, web]
|
|
|
48
46
|
# Flask App
|
|
49
47
|
|
|
50
48
|
Building a web app with #flask.
|
|
51
|
-
"""
|
|
52
|
-
)
|
|
49
|
+
""")
|
|
53
50
|
|
|
54
51
|
# File B: has tags "python" and "data", mentions "pandas"
|
|
55
|
-
(root / "pandas-guide.md").write_text(
|
|
56
|
-
"""\
|
|
52
|
+
(root / "pandas-guide.md").write_text("""\
|
|
57
53
|
---
|
|
58
54
|
title: Pandas Guide
|
|
59
55
|
tags: [python, data]
|
|
@@ -61,12 +57,10 @@ tags: [python, data]
|
|
|
61
57
|
# Pandas Guide
|
|
62
58
|
|
|
63
59
|
Working with #pandas and dataframes.
|
|
64
|
-
"""
|
|
65
|
-
)
|
|
60
|
+
""")
|
|
66
61
|
|
|
67
62
|
# File C: has tags "javascript" and "web", mentions "react"
|
|
68
|
-
(root / "react-setup.md").write_text(
|
|
69
|
-
"""\
|
|
63
|
+
(root / "react-setup.md").write_text("""\
|
|
70
64
|
---
|
|
71
65
|
title: React Setup
|
|
72
66
|
tags: [javascript, web]
|
|
@@ -74,30 +68,25 @@ tags: [javascript, web]
|
|
|
74
68
|
# React Setup
|
|
75
69
|
|
|
76
70
|
Getting started with #react.
|
|
77
|
-
"""
|
|
78
|
-
)
|
|
71
|
+
""")
|
|
79
72
|
|
|
80
73
|
# File D: no frontmatter tags, but has inline #tag, no frontmatter
|
|
81
|
-
(root / "scratchpad.md").write_text(
|
|
82
|
-
"""\
|
|
74
|
+
(root / "scratchpad.md").write_text("""\
|
|
83
75
|
# Scratchpad
|
|
84
76
|
|
|
85
77
|
Random thoughts and #ideas.
|
|
86
|
-
"""
|
|
87
|
-
)
|
|
78
|
+
""")
|
|
88
79
|
|
|
89
80
|
# File E: in subdirectory
|
|
90
81
|
sub = root / "deep"
|
|
91
82
|
sub.mkdir()
|
|
92
|
-
(sub / "hidden-note.md").write_text(
|
|
93
|
-
"""\
|
|
83
|
+
(sub / "hidden-note.md").write_text("""\
|
|
94
84
|
---
|
|
95
85
|
title: Hidden
|
|
96
86
|
tags: [python, devops]
|
|
97
87
|
---
|
|
98
88
|
Hidden note about #docker and #kubernetes.
|
|
99
|
-
"""
|
|
100
|
-
)
|
|
89
|
+
""")
|
|
101
90
|
|
|
102
91
|
return root
|
|
103
92
|
|
|
@@ -115,7 +104,9 @@ class TestPathAndVaultHelpers:
|
|
|
115
104
|
monkeypatch.setenv("OBSIDIAN_VAULT", "/env/vault")
|
|
116
105
|
assert _resolve_path() == "/env/vault"
|
|
117
106
|
|
|
118
|
-
def test_resolve_path_default_current_dir(
|
|
107
|
+
def test_resolve_path_default_current_dir(
|
|
108
|
+
self, monkeypatch: pytest.MonkeyPatch
|
|
109
|
+
) -> None:
|
|
119
110
|
monkeypatch.delenv("OBSIDIAN_VAULT", raising=False)
|
|
120
111
|
assert _resolve_path() == "."
|
|
121
112
|
|
|
@@ -206,7 +197,9 @@ class TestFilenameAndRelativePathHelpers:
|
|
|
206
197
|
"deep/hidden-note",
|
|
207
198
|
)
|
|
208
199
|
|
|
209
|
-
def test_filename_candidates_outside_vault(
|
|
200
|
+
def test_filename_candidates_outside_vault(
|
|
201
|
+
self, tmp_path: Path, vault: Path
|
|
202
|
+
) -> None:
|
|
210
203
|
note = tmp_path / "outside.md"
|
|
211
204
|
note.write_text("outside")
|
|
212
205
|
assert _filename_candidates(note, vault) == ("outside", "outside.md", "outside")
|
|
@@ -252,7 +245,13 @@ class TestSearchFiles:
|
|
|
252
245
|
results = _search_files(vault)
|
|
253
246
|
# 5 .md files across the vault (4 in root + 1 in deep/)
|
|
254
247
|
names = {md.name for md in results}
|
|
255
|
-
assert names == {
|
|
248
|
+
assert names == {
|
|
249
|
+
"flask-app",
|
|
250
|
+
"pandas-guide",
|
|
251
|
+
"react-setup",
|
|
252
|
+
"scratchpad",
|
|
253
|
+
"hidden-note",
|
|
254
|
+
}
|
|
256
255
|
assert len(results) == 5
|
|
257
256
|
|
|
258
257
|
def test_filename_filter(self, vault: Path) -> None:
|
|
@@ -489,9 +488,7 @@ class TestDisplayFile:
|
|
|
489
488
|
assert "Building a web app with #flask." in captured.out
|
|
490
489
|
assert "---" in captured.out
|
|
491
490
|
|
|
492
|
-
def test_no_extra_output(
|
|
493
|
-
self, vault: Path, capsys: pytest.CaptureFixture
|
|
494
|
-
) -> None:
|
|
491
|
+
def test_no_extra_output(self, vault: Path, capsys: pytest.CaptureFixture) -> None:
|
|
495
492
|
from notekey.markdown import Markdown
|
|
496
493
|
|
|
497
494
|
raw = (vault / "flask-app.md").read_text()
|
|
@@ -509,7 +506,10 @@ class TestDisplayFile:
|
|
|
509
506
|
|
|
510
507
|
class TestMainCommandBranches:
|
|
511
508
|
def test_main_init(
|
|
512
|
-
self,
|
|
509
|
+
self,
|
|
510
|
+
vault: Path,
|
|
511
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
512
|
+
capsys: pytest.CaptureFixture,
|
|
513
513
|
) -> None:
|
|
514
514
|
target = vault / "new-project"
|
|
515
515
|
target.mkdir()
|
|
@@ -522,8 +522,33 @@ class TestMainCommandBranches:
|
|
|
522
522
|
assert (target / "new-project.base").exists()
|
|
523
523
|
assert (target / "new-project.md").exists()
|
|
524
524
|
|
|
525
|
+
def test_main_init_without_path_uses_current_directory(
|
|
526
|
+
self,
|
|
527
|
+
vault: Path,
|
|
528
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
529
|
+
capsys: pytest.CaptureFixture,
|
|
530
|
+
) -> None:
|
|
531
|
+
target = vault / "01-PROJECTS" / "OpenClaw"
|
|
532
|
+
target.mkdir(parents=True)
|
|
533
|
+
monkeypatch.setenv("OBSIDIAN_VAULT", str(vault))
|
|
534
|
+
monkeypatch.chdir(target)
|
|
535
|
+
monkeypatch.setattr(sys, "argv", ["notekey", "init", "--force"])
|
|
536
|
+
|
|
537
|
+
main()
|
|
538
|
+
|
|
539
|
+
captured = capsys.readouterr()
|
|
540
|
+
assert f"Created base: {target / 'OpenClaw.base'}" in captured.out
|
|
541
|
+
assert f"Created markdown: {target / 'OpenClaw.md'}" in captured.out
|
|
542
|
+
assert (target / "OpenClaw.base").exists()
|
|
543
|
+
assert (target / "OpenClaw.md").exists()
|
|
544
|
+
assert not (vault / "vault.base").exists()
|
|
545
|
+
assert not (vault / "vault.md").exists()
|
|
546
|
+
|
|
525
547
|
def test_main_search_uses_obsidian_vault_default(
|
|
526
|
-
self,
|
|
548
|
+
self,
|
|
549
|
+
vault: Path,
|
|
550
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
551
|
+
capsys: pytest.CaptureFixture,
|
|
527
552
|
) -> None:
|
|
528
553
|
monkeypatch.setenv("OBSIDIAN_VAULT", str(vault))
|
|
529
554
|
monkeypatch.setattr(sys, "argv", ["notekey", "search", "--tags", "python"])
|
|
@@ -535,7 +560,10 @@ class TestMainCommandBranches:
|
|
|
535
560
|
assert "flask-app.md" in captured.out
|
|
536
561
|
|
|
537
562
|
def test_main_read_no_results(
|
|
538
|
-
self,
|
|
563
|
+
self,
|
|
564
|
+
vault: Path,
|
|
565
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
566
|
+
capsys: pytest.CaptureFixture,
|
|
539
567
|
) -> None:
|
|
540
568
|
monkeypatch.setenv("OBSIDIAN_VAULT", str(vault))
|
|
541
569
|
monkeypatch.setattr(sys, "argv", ["notekey", "read", "missing-note"])
|
|
@@ -546,7 +574,10 @@ class TestMainCommandBranches:
|
|
|
546
574
|
assert "No file found matching: missing-note" in captured.out
|
|
547
575
|
|
|
548
576
|
def test_main_read_single_result(
|
|
549
|
-
self,
|
|
577
|
+
self,
|
|
578
|
+
vault: Path,
|
|
579
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
580
|
+
capsys: pytest.CaptureFixture,
|
|
550
581
|
) -> None:
|
|
551
582
|
monkeypatch.setenv("OBSIDIAN_VAULT", str(vault))
|
|
552
583
|
monkeypatch.setattr(sys, "argv", ["notekey", "read", "=flask-app"])
|
|
@@ -557,7 +588,10 @@ class TestMainCommandBranches:
|
|
|
557
588
|
assert captured.out == (vault / "flask-app.md").read_text()
|
|
558
589
|
|
|
559
590
|
def test_main_read_multiple_results(
|
|
560
|
-
self,
|
|
591
|
+
self,
|
|
592
|
+
vault: Path,
|
|
593
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
594
|
+
capsys: pytest.CaptureFixture,
|
|
561
595
|
) -> None:
|
|
562
596
|
monkeypatch.setenv("OBSIDIAN_VAULT", str(vault))
|
|
563
597
|
monkeypatch.setattr(sys, "argv", ["notekey", "read", "a"])
|
|
@@ -568,12 +602,17 @@ class TestMainCommandBranches:
|
|
|
568
602
|
assert "Multiple matches" in captured.out
|
|
569
603
|
|
|
570
604
|
def test_script_entrypoint(
|
|
571
|
-
self,
|
|
605
|
+
self,
|
|
606
|
+
vault: Path,
|
|
607
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
608
|
+
capsys: pytest.CaptureFixture,
|
|
572
609
|
) -> None:
|
|
573
610
|
monkeypatch.setenv("OBSIDIAN_VAULT", str(vault))
|
|
574
611
|
monkeypatch.setattr(sys, "argv", ["notekey", "read", "=flask-app"])
|
|
575
612
|
|
|
576
|
-
runpy.run_path(
|
|
613
|
+
runpy.run_path(
|
|
614
|
+
str(Path(__file__).parents[1] / "notekey" / "main.py"), run_name="__main__"
|
|
615
|
+
)
|
|
577
616
|
|
|
578
617
|
captured = capsys.readouterr()
|
|
579
618
|
assert captured.out == (vault / "flask-app.md").read_text()
|
|
@@ -600,7 +639,16 @@ class TestBuildParser:
|
|
|
600
639
|
def test_search_accepts_all_flags(self) -> None:
|
|
601
640
|
parser = build_parser()
|
|
602
641
|
args = parser.parse_args(
|
|
603
|
-
[
|
|
642
|
+
[
|
|
643
|
+
"search",
|
|
644
|
+
"/some/path",
|
|
645
|
+
"--tags",
|
|
646
|
+
"a,b",
|
|
647
|
+
"--filename",
|
|
648
|
+
"test",
|
|
649
|
+
"--content",
|
|
650
|
+
"hello",
|
|
651
|
+
]
|
|
604
652
|
)
|
|
605
653
|
assert args.path == "/some/path"
|
|
606
654
|
assert args.tags == "a,b"
|
|
@@ -6,7 +6,12 @@ from pathlib import Path
|
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
8
8
|
from notekey.markdown import Markdown
|
|
9
|
-
from notekey.utils import
|
|
9
|
+
from notekey.utils import (
|
|
10
|
+
extract_inline_tags,
|
|
11
|
+
extract_markdown_links,
|
|
12
|
+
extract_wiki_links,
|
|
13
|
+
normalize_size,
|
|
14
|
+
)
|
|
10
15
|
|
|
11
16
|
# ---------------------------------------------------------------------------
|
|
12
17
|
# Fixtures
|
|
@@ -236,9 +241,7 @@ class TestMarkdownInit:
|
|
|
236
241
|
|
|
237
242
|
|
|
238
243
|
class TestMarkdownTags:
|
|
239
|
-
def test_tags_combined_from_frontmatter_and_inline(
|
|
240
|
-
self, sample_md: Path
|
|
241
|
-
) -> None:
|
|
244
|
+
def test_tags_combined_from_frontmatter_and_inline(self, sample_md: Path) -> None:
|
|
242
245
|
md = Markdown(sample_md)
|
|
243
246
|
# frontmatter: project, documentation, obsidian
|
|
244
247
|
# inline: python, testing, project/active, topic/obsidian/plugins
|
|
@@ -309,9 +312,7 @@ class TestMarkdownMetadata:
|
|
|
309
312
|
assert md.metadata["created"] == datetime.date(2024, 1, 15)
|
|
310
313
|
assert md.metadata["status"] == "active"
|
|
311
314
|
|
|
312
|
-
def test_no_frontmatter_metadata(
|
|
313
|
-
self, sample_md_no_frontmatter: Path
|
|
314
|
-
) -> None:
|
|
315
|
+
def test_no_frontmatter_metadata(self, sample_md_no_frontmatter: Path) -> None:
|
|
315
316
|
md = Markdown(sample_md_no_frontmatter)
|
|
316
317
|
assert md.metadata == {}
|
|
317
318
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|