promnesia 1.2.20240810__py3-none-any.whl → 1.4.20250909__py3-none-any.whl

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.
Files changed (79) hide show
  1. promnesia/__init__.py +18 -4
  2. promnesia/__main__.py +104 -78
  3. promnesia/cannon.py +108 -107
  4. promnesia/common.py +107 -88
  5. promnesia/compare.py +33 -30
  6. promnesia/compat.py +10 -10
  7. promnesia/config.py +37 -34
  8. promnesia/database/common.py +4 -3
  9. promnesia/database/dump.py +13 -13
  10. promnesia/database/load.py +7 -7
  11. promnesia/extract.py +19 -17
  12. promnesia/logging.py +27 -15
  13. promnesia/misc/install_server.py +32 -27
  14. promnesia/server.py +106 -79
  15. promnesia/sources/auto.py +104 -77
  16. promnesia/sources/auto_logseq.py +6 -5
  17. promnesia/sources/auto_obsidian.py +2 -2
  18. promnesia/sources/browser.py +20 -10
  19. promnesia/sources/browser_legacy.py +65 -50
  20. promnesia/sources/demo.py +7 -8
  21. promnesia/sources/fbmessenger.py +3 -3
  22. promnesia/sources/filetypes.py +22 -16
  23. promnesia/sources/github.py +9 -8
  24. promnesia/sources/guess.py +6 -2
  25. promnesia/sources/hackernews.py +7 -9
  26. promnesia/sources/hpi.py +5 -3
  27. promnesia/sources/html.py +11 -7
  28. promnesia/sources/hypothesis.py +3 -2
  29. promnesia/sources/instapaper.py +3 -2
  30. promnesia/sources/markdown.py +22 -12
  31. promnesia/sources/org.py +36 -17
  32. promnesia/sources/plaintext.py +41 -39
  33. promnesia/sources/pocket.py +5 -3
  34. promnesia/sources/reddit.py +24 -26
  35. promnesia/sources/roamresearch.py +5 -2
  36. promnesia/sources/rss.py +6 -8
  37. promnesia/sources/shellcmd.py +21 -11
  38. promnesia/sources/signal.py +27 -26
  39. promnesia/sources/smscalls.py +2 -3
  40. promnesia/sources/stackexchange.py +5 -4
  41. promnesia/sources/takeout.py +37 -34
  42. promnesia/sources/takeout_legacy.py +29 -19
  43. promnesia/sources/telegram.py +18 -12
  44. promnesia/sources/telegram_legacy.py +22 -11
  45. promnesia/sources/twitter.py +7 -6
  46. promnesia/sources/vcs.py +11 -6
  47. promnesia/sources/viber.py +11 -10
  48. promnesia/sources/website.py +8 -7
  49. promnesia/sources/zulip.py +3 -2
  50. promnesia/sqlite.py +13 -7
  51. promnesia/tests/common.py +10 -5
  52. promnesia/tests/server_helper.py +13 -10
  53. promnesia/tests/sources/test_auto.py +2 -3
  54. promnesia/tests/sources/test_filetypes.py +11 -8
  55. promnesia/tests/sources/test_hypothesis.py +10 -6
  56. promnesia/tests/sources/test_org.py +9 -5
  57. promnesia/tests/sources/test_plaintext.py +9 -8
  58. promnesia/tests/sources/test_shellcmd.py +13 -13
  59. promnesia/tests/sources/test_takeout.py +3 -5
  60. promnesia/tests/test_cannon.py +256 -239
  61. promnesia/tests/test_cli.py +12 -8
  62. promnesia/tests/test_compare.py +17 -13
  63. promnesia/tests/test_config.py +7 -8
  64. promnesia/tests/test_db_dump.py +15 -15
  65. promnesia/tests/test_extract.py +17 -10
  66. promnesia/tests/test_indexer.py +24 -18
  67. promnesia/tests/test_server.py +12 -13
  68. promnesia/tests/test_traverse.py +0 -2
  69. promnesia/tests/utils.py +3 -7
  70. promnesia-1.4.20250909.dist-info/METADATA +66 -0
  71. promnesia-1.4.20250909.dist-info/RECORD +80 -0
  72. {promnesia-1.2.20240810.dist-info → promnesia-1.4.20250909.dist-info}/WHEEL +1 -2
  73. promnesia/kjson.py +0 -121
  74. promnesia/sources/__init__.pyi +0 -0
  75. promnesia-1.2.20240810.dist-info/METADATA +0 -54
  76. promnesia-1.2.20240810.dist-info/RECORD +0 -83
  77. promnesia-1.2.20240810.dist-info/top_level.txt +0 -1
  78. {promnesia-1.2.20240810.dist-info → promnesia-1.4.20250909.dist-info}/entry_points.txt +0 -0
  79. {promnesia-1.2.20240810.dist-info → promnesia-1.4.20250909.dist-info/licenses}/LICENSE +0 -0
@@ -1,27 +1,31 @@
1
- from pathlib import Path
2
1
  import shutil
2
+ from pathlib import Path
3
3
 
4
4
  from ..compare import compare_files
5
5
  from .utils import index_urls
6
6
 
7
7
 
8
8
  def test_compare(tmp_path: Path) -> None:
9
- idx = index_urls({
10
- 'https://example.com': None,
11
- 'https://en.wikipedia.org/wiki/Saturn_V': None,
12
- 'https://plato.stanford.edu/entries/qualia': None,
13
- })
9
+ idx = index_urls(
10
+ {
11
+ 'https://example.com': None,
12
+ 'https://en.wikipedia.org/wiki/Saturn_V': None,
13
+ 'https://plato.stanford.edu/entries/qualia': None,
14
+ }
15
+ )
14
16
  idx(tmp_path)
15
- db = tmp_path / 'promnesia.sqlite'
17
+ db = tmp_path / 'promnesia.sqlite'
16
18
  old_db = tmp_path / 'promnesia-old.sqlite'
17
19
  shutil.move(str(db), str(old_db))
18
20
 
19
- idx2 = index_urls({
20
- 'https://example.com': None,
21
- 'https://www.reddit.com/r/explainlikeimfive/comments/1ev6e0/eli5entropy': None,
22
- 'https://en.wikipedia.org/wiki/Saturn_V': None,
23
- 'https://plato.stanford.edu/entries/qualia': None,
24
- })
21
+ idx2 = index_urls(
22
+ {
23
+ 'https://example.com': None,
24
+ 'https://www.reddit.com/r/explainlikeimfive/comments/1ev6e0/eli5entropy': None,
25
+ 'https://en.wikipedia.org/wiki/Saturn_V': None,
26
+ 'https://plato.stanford.edu/entries/qualia': None,
27
+ }
28
+ )
25
29
  idx2(tmp_path)
26
30
 
27
31
  # should not crash, as there are more links in the new database
@@ -1,15 +1,14 @@
1
+ from __future__ import annotations
2
+
1
3
  from contextlib import contextmanager
2
4
  from pathlib import Path
3
5
  from tempfile import TemporaryDirectory
4
- from typing import Union, List
5
-
6
- from ..common import Source
7
- from ..config import import_config, Config
8
-
9
6
 
10
- from more_itertools import ilen
11
7
  import pytest
8
+ from more_itertools import ilen
12
9
 
10
+ from ..common import Source
11
+ from ..config import Config, import_config
13
12
  from .common import throw
14
13
 
15
14
 
@@ -22,7 +21,7 @@ def make(body: str) -> Config:
22
21
 
23
22
 
24
23
  @contextmanager
25
- def with_config(cfg: Union[str, Config]):
24
+ def with_config(cfg: str | Config):
26
25
  from .. import config as C
27
26
 
28
27
  assert not C.has()
@@ -35,7 +34,7 @@ def with_config(cfg: Union[str, Config]):
35
34
  C.reset()
36
35
 
37
36
 
38
- def index(cfg: Union[str, Config], check=True) -> List[Exception]:
37
+ def index(cfg: str | Config, *, check: bool = True) -> list[Exception]:
39
38
  from ..__main__ import _do_index
40
39
 
41
40
  with with_config(cfg):
@@ -4,30 +4,29 @@ from concurrent.futures import ProcessPoolExecutor
4
4
  from datetime import datetime, timedelta, timezone
5
5
  from pathlib import Path
6
6
  from tempfile import TemporaryDirectory
7
- from typing import Any, Iterable
7
+ from typing import Any
8
+ from zoneinfo import ZoneInfo
8
9
 
9
-
10
- from hypothesis import settings, given
11
- from hypothesis.strategies import from_type
12
10
  # NOTE: pytest ... -s --hypothesis-verbosity=debug is useful for seeing what hypothesis is doing
13
11
  import pytest
14
- import pytz
15
-
12
+ from hypothesis import given, settings
13
+ from hypothesis.strategies import from_type
16
14
 
17
15
  from ..common import Loc
18
16
  from ..database.common import DbVisit
19
17
  from ..database.dump import visits_to_sqlite
20
18
  from ..database.load import get_all_db_visits
21
19
  from ..sqlite import sqlite_connection
22
-
23
- from .common import gc_control, running_on_ci
24
-
25
-
26
- HSETTINGS: dict[str, Any] = dict(
27
- derandomize=True,
28
- deadline=timedelta(seconds=2), # sometimes slow on ci
20
+ from .common import (
21
+ gc_control, # noqa: F401
22
+ running_on_ci,
29
23
  )
30
24
 
25
+ HSETTINGS: dict[str, Any] = {
26
+ 'derandomize': True,
27
+ 'deadline': timedelta(seconds=2), # sometimes slow on ci
28
+ }
29
+
31
30
 
32
31
  def test_no_visits(tmp_path: Path) -> None:
33
32
  visits: list[DbVisit] = []
@@ -45,7 +44,7 @@ def test_no_visits(tmp_path: Path) -> None:
45
44
 
46
45
  def test_one_visit(tmp_path: Path) -> None:
47
46
  dt = datetime.fromisoformat('2023-11-14T23:11:01')
48
- dt = pytz.timezone('Europe/Warsaw').localize(dt)
47
+ dt = dt.replace(tzinfo=ZoneInfo('Europe/Warsaw'))
49
48
  visit = DbVisit(
50
49
  norm_url='google.com',
51
50
  orig_url='https://google.com',
@@ -157,7 +156,8 @@ def test_random_visit(visit: DbVisit) -> None:
157
156
 
158
157
 
159
158
  _dt_naive = datetime.fromisoformat('2023-11-14T23:11:01')
160
- _dt_aware = pytz.timezone('America/New_York').localize(_dt_naive)
159
+ _dt_aware = _dt_naive.replace(tzinfo=ZoneInfo('America/New_York'))
160
+
161
161
 
162
162
  def make_testvisit(i: int) -> DbVisit:
163
163
  return DbVisit(
@@ -1,12 +1,16 @@
1
1
  from datetime import datetime, timezone
2
2
 
3
- from ..common import Visit, DbVisit, Loc, Source
4
- from ..extract import extract_visits
5
-
6
- from .common import get_testdata, unwrap, running_on_ci, gc_control
7
-
8
- from more_itertools import ilen
9
3
  import pytest
4
+ from more_itertools import ilen
5
+
6
+ from ..common import DbVisit, Loc, Source, Visit
7
+ from ..extract import extract_visits
8
+ from .common import (
9
+ gc_control, # noqa: F401
10
+ get_testdata,
11
+ running_on_ci,
12
+ unwrap,
13
+ )
10
14
 
11
15
 
12
16
  def test_with_error() -> None:
@@ -31,10 +35,12 @@ def test_urls_are_normalised() -> None:
31
35
  from ..sources import shellcmd
32
36
  from ..sources.plaintext import extract_from_path
33
37
 
34
- visits = list(extract_visits(
35
- source=Source(shellcmd.index, extract_from_path(get_testdata('normalise'))),
36
- src='whatever',
37
- ))
38
+ visits = list(
39
+ extract_visits(
40
+ source=Source(shellcmd.index, extract_from_path(get_testdata('normalise'))),
41
+ src='whatever',
42
+ )
43
+ )
38
44
  assert len(visits) == 7
39
45
 
40
46
  assert {unwrap(v).norm_url for v in visits} == {
@@ -55,6 +61,7 @@ def test_benchmark(count: int, gc_control) -> None:
55
61
  pytest.skip("test would be too slow on CI, only meant to run manually")
56
62
 
57
63
  from ..sources import demo
64
+
58
65
  source = Source(demo.index, count=count)
59
66
 
60
67
  total = ilen(extract_visits(source=source, src='whatever'))
@@ -1,14 +1,18 @@
1
1
  from collections import Counter
2
2
  from pathlib import Path
3
- from subprocess import check_call, Popen
3
+ from subprocess import Popen, check_call
4
+
5
+ import pytest
4
6
 
5
7
  from ..__main__ import do_index, read_example_config
6
8
  from ..common import DbVisit, _is_windows
7
9
  from ..database.load import get_all_db_visits
8
-
9
- import pytest
10
-
11
- from .common import get_testdata, promnesia_bin, reset_filters, write_config
10
+ from .common import (
11
+ get_testdata,
12
+ promnesia_bin,
13
+ reset_filters, # noqa: F401
14
+ write_config,
15
+ )
12
16
 
13
17
 
14
18
  def get_stats(tmp_path: Path) -> Counter:
@@ -28,7 +32,7 @@ def test_indexing_mode(tmp_path: Path, mode: str) -> None:
28
32
  from promnesia.common import Source
29
33
  from promnesia.sources import demo
30
34
 
31
- SOURCES = [
35
+ SOURCES = [ # noqa: F841
32
36
  Source(demo.index, count=10, base_dt='2000-01-01', delta=30, name='demo1'),
33
37
  Source(demo.index, count=20, base_dt='2001-01-01', delta=30, name='demo2'),
34
38
  ]
@@ -44,7 +48,7 @@ def test_indexing_mode(tmp_path: Path, mode: str) -> None:
44
48
  from promnesia.common import Source
45
49
  from promnesia.sources import demo
46
50
 
47
- SOURCES = [
51
+ SOURCES = [ # noqa: F841
48
52
  Source(demo.index, count=30, base_dt='2005-01-01', delta=30, name='demo2'),
49
53
  Source(demo.index, count=40, base_dt='2010-01-01', delta=30, name='demo3'),
50
54
  ]
@@ -69,7 +73,7 @@ def test_concurrent_indexing(tmp_path: Path) -> None:
69
73
  from promnesia.common import Source
70
74
  from promnesia.sources import demo
71
75
 
72
- SOURCES = [Source(demo.index, count=10)]
76
+ SOURCES = [Source(demo.index, count=10)] # noqa: F841
73
77
 
74
78
  cfg_fast_path = tmp_path / 'cfg_fast.py'
75
79
  write_config(cfg_fast_path, cfg_fast)
@@ -78,7 +82,7 @@ def test_concurrent_indexing(tmp_path: Path) -> None:
78
82
  from promnesia.common import Source
79
83
  from promnesia.sources import demo
80
84
 
81
- SOURCES = [Source(demo.index, count=100_000)]
85
+ SOURCES = [Source(demo.index, count=100_000)] # noqa: F841
82
86
 
83
87
  cfg_slow_path = tmp_path / 'cfg_slow.py'
84
88
  write_config(cfg_slow_path, cfg_slow)
@@ -97,7 +101,7 @@ def test_concurrent_indexing(tmp_path: Path) -> None:
97
101
  fasts = [
98
102
  Popen(promnesia_bin('index', '--config', cfg_fast_path, '--overwrite'))
99
103
  for _ in range(10)
100
- ]
104
+ ] # fmt: skip
101
105
  for fast in fasts:
102
106
  assert fast.wait() == 0, fast # should succeed
103
107
  total_runs += 1
@@ -118,11 +122,11 @@ def test_filter(tmp_path: Path, reset_filters) -> None:
118
122
  from promnesia.sources import shellcmd
119
123
  from promnesia.sources.plaintext import extract_from_path
120
124
 
121
- FILTERS = [
125
+ FILTERS = [ # noqa: F841
122
126
  domain_to_filter,
123
127
  ]
124
128
 
125
- SOURCES = [Source(shellcmd.index, extract_from_path(testdata))]
129
+ SOURCES = [Source(shellcmd.index, extract_from_path(testdata))] # noqa: F841
126
130
 
127
131
  cfg_path = tmp_path / 'config.py'
128
132
  write_config(cfg_path, cfg, testdata=testdata, domain_to_filter=domain_to_filter)
@@ -143,7 +147,7 @@ def test_weird_urls(tmp_path: Path) -> None:
143
147
  from promnesia.sources import shellcmd
144
148
  from promnesia.sources.plaintext import extract_from_path
145
149
 
146
- SOURCES = [Source(shellcmd.index, extract_from_path(testdata))]
150
+ SOURCES = [Source(shellcmd.index, extract_from_path(testdata))] # noqa: F841
147
151
 
148
152
  cfg_path = tmp_path / 'config.py'
149
153
  write_config(cfg_path, cfg, testdata=get_testdata('weird.txt'))
@@ -172,7 +176,7 @@ def test_errors_during_indexing(tmp_path: Path) -> None:
172
176
  def indexer2():
173
177
  raise RuntimeError("in this case indexer itself crashed")
174
178
 
175
- SOURCES = [Source(indexer1), Source(indexer2)]
179
+ SOURCES = [Source(indexer1), Source(indexer2)] # noqa: F841
176
180
 
177
181
  cfg_path = tmp_path / 'config.py'
178
182
  write_config(cfg_path, cfg)
@@ -190,9 +194,11 @@ def test_hook(tmp_path: Path) -> None:
190
194
  from promnesia.common import Source
191
195
  from promnesia.sources import demo
192
196
 
193
- SOURCES = [Source(demo.index, count=7, name='somename')]
197
+ SOURCES = [Source(demo.index, count=7, name='somename')] # noqa: F841
198
+
199
+ from collections.abc import Iterator
200
+ from typing import cast
194
201
 
195
- from typing import cast, Iterator
196
202
  from promnesia.common import DbVisit, Loc, Res
197
203
  from promnesia.sources import demo
198
204
 
@@ -204,7 +210,7 @@ def test_hook(tmp_path: Path) -> None:
204
210
  if 'page1' in nurl:
205
211
  yield visit._replace(norm_url='patched.com')
206
212
  elif 'page2' in nurl:
207
- raise Exception('boom') # deliberately crash
213
+ raise RuntimeError('boom') # deliberately crash
208
214
  elif 'page3' in nurl:
209
215
  # just don't yield anything! it will be omitted
210
216
  pass
@@ -235,7 +241,7 @@ def test_example_config(tmp_path: Path) -> None:
235
241
  if _is_windows:
236
242
  pytest.skip("doesn't work on Windows: example config references /usr/include paths")
237
243
 
238
- config = read_example_config() + '\n' + f'OUTPUT_DIR = "{str(tmp_path)}"'
244
+ config = read_example_config() + '\n' + f'OUTPUT_DIR = "{tmp_path!s}"'
239
245
  cfg_path = tmp_path / 'example_config.py'
240
246
  cfg_path.write_text(config)
241
247
 
@@ -5,7 +5,6 @@ from subprocess import Popen
5
5
  import pytest
6
6
 
7
7
  from ..__main__ import do_index
8
-
9
8
  from .common import promnesia_bin, write_config
10
9
  from .server_helper import run_server
11
10
 
@@ -34,7 +33,7 @@ def test_status_ok(tmp_path: Path) -> None:
34
33
  from promnesia.common import Source
35
34
  from promnesia.sources import demo
36
35
 
37
- SOURCES = [Source(demo.index, count=10)]
36
+ SOURCES = [Source(demo.index, count=10)] # noqa: F841
38
37
 
39
38
  cfg_path = tmp_path / 'config.py'
40
39
  write_config(cfg_path, cfg)
@@ -57,7 +56,7 @@ def test_visits(tmp_path: Path) -> None:
57
56
  from promnesia.common import Source
58
57
  from promnesia.sources import demo
59
58
 
60
- SOURCES = [Source(demo.index, base_dt='2000-01-01', delta=30 * 60)]
59
+ SOURCES = [Source(demo.index, base_dt='2000-01-01', delta=30 * 60)] # noqa: F841
61
60
 
62
61
  cfg_path = tmp_path / 'config.py'
63
62
  write_config(cfg_path, cfg)
@@ -82,7 +81,7 @@ def test_visits_hierarchy(tmp_path: Path) -> None:
82
81
  def cfg() -> None:
83
82
  from datetime import datetime
84
83
 
85
- from promnesia.common import Source, Visit, Loc
84
+ from promnesia.common import Loc, Source, Visit
86
85
  from promnesia.sources import demo
87
86
 
88
87
  def indexer():
@@ -117,7 +116,7 @@ def test_visits_hierarchy(tmp_path: Path) -> None:
117
116
  context='I am comment 1',
118
117
  )
119
118
 
120
- SOURCES = [Source(indexer)]
119
+ SOURCES = [Source(indexer)] # noqa: F841
121
120
 
122
121
  cfg_path = tmp_path / 'config.py'
123
122
  write_config(cfg_path, cfg)
@@ -139,7 +138,7 @@ def test_visited(tmp_path: Path) -> None:
139
138
  from promnesia.common import Source
140
139
  from promnesia.sources import demo
141
140
 
142
- SOURCES = [Source(demo.index, base_dt='2000-01-01', delta=30 * 60)]
141
+ SOURCES = [Source(demo.index, base_dt='2000-01-01', delta=30 * 60)] # noqa: F841
143
142
 
144
143
  cfg_path = tmp_path / 'config.py'
145
144
  write_config(cfg_path, cfg)
@@ -163,7 +162,7 @@ def test_search(tmp_path: Path) -> None:
163
162
  def cfg() -> None:
164
163
  from datetime import datetime
165
164
 
166
- from promnesia.common import Source, Visit, Loc
165
+ from promnesia.common import Loc, Source, Visit
167
166
  from promnesia.sources import demo
168
167
 
169
168
  def indexer():
@@ -185,7 +184,7 @@ def test_search(tmp_path: Path) -> None:
185
184
  )
186
185
  yield from visits[3:]
187
186
 
188
- SOURCES = [Source(indexer)]
187
+ SOURCES = [Source(indexer)] # noqa: F841
189
188
 
190
189
  cfg_path = tmp_path / 'config.py'
191
190
  write_config(cfg_path, cfg)
@@ -215,7 +214,7 @@ def test_search_around(tmp_path: Path) -> None:
215
214
  from promnesia.sources import demo
216
215
 
217
216
  # generates 60 visits within 10 mins of each other -- so spanning over 10 hours
218
- SOURCES = [Source(demo.index, count=60, base_dt='2000-01-01T00:00:00+03:00', delta=10 * 60)]
217
+ SOURCES = [Source(demo.index, count=60, base_dt='2000-01-01T00:00:00+03:00', delta=10 * 60)] # noqa: F841
219
218
 
220
219
  cfg_path = tmp_path / 'config.py'
221
220
  write_config(cfg_path, cfg)
@@ -235,8 +234,8 @@ def test_search_around(tmp_path: Path) -> None:
235
234
  ).json()
236
235
  visits = rj['visits']
237
236
  assert len(visits) == 18 # 6 per hour * 3
238
- assert visits[0 ]['dt'] == '01 Jan 2000 02:00:00 +0300'
239
- assert visits[-1]['dt'] == '01 Jan 2000 04:50:00 +0300'
237
+ assert visits[0 ]['dt'] == '01 Jan 2000 02:00:00 +0300' # fmt: skip
238
+ assert visits[-1]['dt'] == '01 Jan 2000 04:50:00 +0300' # fmt: skip
240
239
 
241
240
 
242
241
  @pytest.mark.parametrize('mode', ['update', 'overwrite'])
@@ -249,7 +248,7 @@ def test_query_while_indexing(tmp_path: Path, mode: str) -> None:
249
248
  from promnesia.common import Source
250
249
  from promnesia.sources import demo
251
250
 
252
- SOURCES = [Source(demo.index, count=1_000, name=run_id)]
251
+ SOURCES = [Source(demo.index, count=1_000, name=run_id)] # noqa: F841
253
252
 
254
253
  cfg_path = tmp_path / f'config{run_id}.py'
255
254
  write_config(cfg_path, cfg, run_id=run_id)
@@ -285,7 +284,7 @@ def test_query_while_indexing(tmp_path: Path, mode: str) -> None:
285
284
  # S.EnvConfig.set(S.ServerConfig(
286
285
  # # TODO populate with test db and benchmark properly...
287
286
  # db=Path('/todo'),
288
- # timezone=pytz.utc,
287
+ # timezone=timezone.utc,
289
288
  # ))
290
289
  # links = [f'https://reddit.com/whatever{i}.html' for i in range(count)]
291
290
  # res = S.visited(links)
@@ -1,10 +1,8 @@
1
1
  from unittest.mock import patch
2
2
 
3
3
  from ..common import traverse
4
-
5
4
  from .common import get_testdata
6
5
 
7
-
8
6
  testDataPath = get_testdata('traverse')
9
7
 
10
8
 
promnesia/tests/utils.py CHANGED
@@ -1,17 +1,13 @@
1
+ from collections.abc import Mapping, Sequence
1
2
  from datetime import datetime, timedelta
2
3
  from pathlib import Path
3
- from typing import Mapping, Optional, Sequence, Tuple, Union
4
4
 
5
- from ..common import Source, Loc, Visit
5
+ from ..common import Loc, Source, Visit
6
6
  from ..database.dump import visits_to_sqlite
7
7
  from ..extract import extract_visits
8
8
 
9
-
10
9
  # TODO a bit shit... why did I make it dict at first??
11
- Urls = Union[
12
- Mapping[str, Optional[str]],
13
- Sequence[Tuple[str, Optional[str]]],
14
- ]
10
+ Urls = Mapping[str, str | None] | Sequence[tuple[str, str | None]]
15
11
 
16
12
 
17
13
  def index_urls(urls: Urls, *, source_name: str = 'test'):
@@ -0,0 +1,66 @@
1
+ Metadata-Version: 2.4
2
+ Name: promnesia
3
+ Version: 1.4.20250909
4
+ Summary: Enhancement of your browsing history
5
+ Project-URL: Homepage, https://github.com/karlicoss/promnesia
6
+ Author-email: "Dima Gerasimov (@karlicoss)" <karlicoss@gmail.com>
7
+ Maintainer-email: "Dima Gerasimov (@karlicoss)" <karlicoss@gmail.com>
8
+ License: MIT License
9
+
10
+ Copyright (c) 2018 Dmitrii Gerasimov
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+ License-File: LICENSE
30
+ Requires-Python: >=3.10
31
+ Requires-Dist: more-itertools
32
+ Requires-Dist: platformdirs
33
+ Requires-Dist: promnesia[indexer]
34
+ Requires-Dist: promnesia[server]
35
+ Requires-Dist: sqlalchemy>=2.0
36
+ Requires-Dist: typing-extensions
37
+ Requires-Dist: tzlocal
38
+ Provides-Extra: all
39
+ Requires-Dist: beautifulsoup4; extra == 'all'
40
+ Requires-Dist: fastapi; extra == 'all'
41
+ Requires-Dist: hpi; extra == 'all'
42
+ Requires-Dist: logzero; extra == 'all'
43
+ Requires-Dist: lxml; extra == 'all'
44
+ Requires-Dist: mistletoe; extra == 'all'
45
+ Requires-Dist: orgparse>=0.3.0; extra == 'all'
46
+ Requires-Dist: python-magic; extra == 'all'
47
+ Requires-Dist: urlextract; extra == 'all'
48
+ Requires-Dist: uvicorn[standard]; extra == 'all'
49
+ Provides-Extra: hpi
50
+ Requires-Dist: hpi; extra == 'hpi'
51
+ Provides-Extra: html
52
+ Requires-Dist: beautifulsoup4; extra == 'html'
53
+ Requires-Dist: lxml; extra == 'html'
54
+ Provides-Extra: indexer
55
+ Requires-Dist: urlextract; extra == 'indexer'
56
+ Provides-Extra: markdown
57
+ Requires-Dist: mistletoe; extra == 'markdown'
58
+ Provides-Extra: optional
59
+ Requires-Dist: logzero; extra == 'optional'
60
+ Requires-Dist: python-magic; extra == 'optional'
61
+ Provides-Extra: org
62
+ Requires-Dist: orgparse>=0.3.0; extra == 'org'
63
+ Provides-Extra: server
64
+ Requires-Dist: fastapi; extra == 'server'
65
+ Requires-Dist: uvicorn[standard]; extra == 'server'
66
+ Provides-Extra: telegram
@@ -0,0 +1,80 @@
1
+ promnesia/__init__.py,sha256=Q7WL7vUhCs0hU3fyzCskxob6tMX37mLh8WkxMsIgTDA,468
2
+ promnesia/__main__.py,sha256=WkfigB6vaT5ySdloeI6Y-ky48F3bv8cGKPuRvKo8SUY,15488
3
+ promnesia/cannon.py,sha256=RfVuoyyXCt7z0us0wIPInwKkdYauDiAkHREpLwxQ0DI,24523
4
+ promnesia/common.py,sha256=OUvqwbgO_5-XalhaITUJdZbM944Q3rgOGHQU19RmClE,19955
5
+ promnesia/compare.py,sha256=D9CsIS3h1htH-P1EhNU7UtEKWkNr5cyKuSE2MRv6kng,4591
6
+ promnesia/compat.py,sha256=cxk8ZOv0LnSwLCab_UrsxQWcKiiZnWgf2xK13L-ql4w,456
7
+ promnesia/config.py,sha256=CIswmf9J2lt5j0_gt0W-cLotyKx_6Koi49zUY81v-qc,4774
8
+ promnesia/extract.py,sha256=zR-DR8TXIktXapzn8RgH9j6YUzRn307lHnMA69W-AVI,2815
9
+ promnesia/logging.py,sha256=8uO7kUR798j_4P7gaJ9jFC27xnT3wklO8mdNrpqicOk,6150
10
+ promnesia/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ promnesia/server.py,sha256=E5gctU6KebhCPSG2ggcZlPZWPWcQc3Pd_pD7fLKzjvg,14918
12
+ promnesia/sqlite.py,sha256=m6OqHUCu0qRIrjSBmjhpXviujy0BshFYxlZJAVPBJA4,1389
13
+ promnesia/database/common.py,sha256=9iENKM0pRL1PKrSAhGzNreDFsQNQBIGZWKuV5E8aalw,1843
14
+ promnesia/database/dump.py,sha256=ZSP-W5fBEiJ2QHgb6ejxnw9_2tDv7K2orRx8_ljgWqk,6085
15
+ promnesia/database/load.py,sha256=kP-HdNL_3q94P_Cj5pnxarHqFgCVJFrsbr0vogDRtFU,1357
16
+ promnesia/misc/__init__.pyi,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ promnesia/misc/config_example.py,sha256=MXxJCVjm9MrEwPR8SHTwkJRDcqWcqJWtfJg_M6yhRMo,364
18
+ promnesia/misc/install_server.py,sha256=6elwODITGQ8D2ACcQ_fchBCTsIojDByiqKkdaRg8qm8,4850
19
+ promnesia/sources/auto.py,sha256=yJRGfXqsaP3q9gAxrXc6a1QxIpNxFXeQR-Ofi82uq5E,11991
20
+ promnesia/sources/auto_logseq.py,sha256=1npVDEXB8wAM6XwTku0nZb57yQ9mM7vWct9albgvGxw,445
21
+ promnesia/sources/auto_obsidian.py,sha256=UoVWAkdCbh7Ij3JFIy6ylYOd6776c7-z2xaR6b8snYc,205
22
+ promnesia/sources/browser.py,sha256=6yQQB63Bmpw7RvFqbFlDH5Q87fq-HvJzMnRt1lYcnYA,3078
23
+ promnesia/sources/browser_legacy.py,sha256=v3JFpiTiblsoR3XnGz_7CdnYWyqDRZTa4nFTDxD5Cco,10637
24
+ promnesia/sources/demo.py,sha256=9am9Wo4qB1PZF62UOkUuNfeDe5Yt_cReZ65IiU-8mlc,1032
25
+ promnesia/sources/fbmessenger.py,sha256=Ubk5y3VMRJhy7Qn_koTI491CsKJyULbPdcQ7ymta3qE,996
26
+ promnesia/sources/filetypes.py,sha256=lSPncSHDjyBPJ-d4UYroMozrlk6Fj4wtIG2pecq-QV0,3663
27
+ promnesia/sources/github.py,sha256=BmRKX8cpvhS0FeudiUKJgqUGbvAzngeWrDBn8PrWrhw,2808
28
+ promnesia/sources/guess.py,sha256=5fTm1OIoXuj2TtavTgfMhQeVb7iqMJ9qbDTSTtUVIEk,807
29
+ promnesia/sources/hackernews.py,sha256=XzIZF0QEEBism9iZtJwokoVcOrBVQ4byw62FOM9r-cw,1190
30
+ promnesia/sources/hpi.py,sha256=K8cqkkEJ497Eghs7YTcSugjkoZihAoI0KUYm5yyuj8Y,397
31
+ promnesia/sources/html.py,sha256=ZmSNoIBE6TtJ-50kbvC6RtRjSfQgWebr2UbB2CtXoyQ,1281
32
+ promnesia/sources/hypothesis.py,sha256=qbiP6xJN53A6YkSUgA7hUa3ZD4KmtUnhJfAzXUHdt0o,1387
33
+ promnesia/sources/instapaper.py,sha256=4a_vsv-Z4bsvy5xN27Hk2Q8RCph1zj7D4odBuiVFhqU,989
34
+ promnesia/sources/markdown.py,sha256=nvOoqtw2zA8a_ohXcpaPRlD-6GXcV6XPaxE1_d18_1Q,3935
35
+ promnesia/sources/org.py,sha256=hghDQYHLGrZlTIbOM5GGhtUPZd4jq60EReH43ihUEk8,5849
36
+ promnesia/sources/plaintext.py,sha256=z20yVEslSh19EmVQnN1v0iI2tYuJDD2S98TNTNkNigc,3097
37
+ promnesia/sources/pocket.py,sha256=ZURxchsv5VxAN3H9j-qAVOw6yOaiDEfFvIBBk5NRhGo,1174
38
+ promnesia/sources/reddit.py,sha256=tjRGlmOivF1pOccd4p6XDalhev0sCMMkdcVNKLumdeI,5663
39
+ promnesia/sources/roamresearch.py,sha256=KgFViu-PKDjQOj3oeBefRsfc2rbYKIOR2GBkH2-XDL4,1078
40
+ promnesia/sources/rss.py,sha256=5Yy5QhoWz5WWBAa7gdQrZSv5m8PuG1gOTCnJxHp1kaw,827
41
+ promnesia/sources/shellcmd.py,sha256=56p7FMMLhkpBsTt1s9dvbfNOBCKME_Epzl8o4x-gpBk,2692
42
+ promnesia/sources/signal.py,sha256=R963RmHmskisHUOnCGmEY8GAv0ohFhv4VtFCsCIkiNM,14857
43
+ promnesia/sources/smscalls.py,sha256=h33FgASh1AJtRNZ6g2dW9DoZ0gWAWoTREEjmRs_vbnE,773
44
+ promnesia/sources/stackexchange.py,sha256=-wsdmcwe_RJXmU94Cw0DeQFIRkx-VUIz3hc7iAdaVxg,660
45
+ promnesia/sources/takeout.py,sha256=oOw7jUCYCxNfbFcMphsPnpvYZ4FlDULyXTOA9BWrGRE,7858
46
+ promnesia/sources/takeout_legacy.py,sha256=tjoXGvkH3EvHYBt6xd1swnai4VP4obLQou_gnMOBOtc,4223
47
+ promnesia/sources/telegram.py,sha256=WXmRhIHWAIlIRN_kqwbScLDvQzB79cMZ0oaIwY_tHPY,3003
48
+ promnesia/sources/telegram_legacy.py,sha256=e-wQQ8jOGJvqKQovItNiVbOaPbYagCsNlgLtUp2Indk,4521
49
+ promnesia/sources/twitter.py,sha256=Vu3xNifTkfIDYvLsOVtGV2QOa6gQ_PF7wNtABKYCGD0,1887
50
+ promnesia/sources/vcs.py,sha256=-OKOBpc4-N-9ErN4KczF8UlF9NOKygvNoCPD8o-Zo7Y,1663
51
+ promnesia/sources/viber.py,sha256=jtvRInsvaKS8mR8IXGnNEoYE9LdrV191XIv-VCryKdQ,6008
52
+ promnesia/sources/website.py,sha256=OWvO-GlrBzAmqr1v2v8Q-Zpnmx-mg01JycdOyT1CmJc,1830
53
+ promnesia/sources/zulip.py,sha256=GN8YiUmFa3jLXFmNst47YILu0U-kPawbxDKY-Mtobf0,770
54
+ promnesia/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
+ promnesia/tests/common.py,sha256=_pB58QT3-nVOOUYYQb96I1TFf5fbfFShWJRWANbCyK8,4094
56
+ promnesia/tests/server_helper.py,sha256=m5Fc-d3byixlKXR2EEFUoEkQ9eM7hjLVpG41-VcOk2E,2210
57
+ promnesia/tests/test_cannon.py,sha256=Q8uTi7VaJRG3tTn6o2p7Fk-jcx_h3ooSWczS38hZQIQ,13737
58
+ promnesia/tests/test_cli.py,sha256=_jDJzjPXsYk1UDqwaM1KNfNxkcC4TsmblPs_s-wzZes,1472
59
+ promnesia/tests/test_compare.py,sha256=51dKDKoU58h7SvLwAE60eFhUNaflaagkS2TUleEsAu8,1005
60
+ promnesia/tests/test_config.py,sha256=VqX2R0UcXmxp8O-p6C5Unk9W2Iv3RkIhvKe3ga84GHc,6534
61
+ promnesia/tests/test_db_dump.py,sha256=-YETzoY2CmCw9bYhettTOI80yOI_dfHjvAzkfngY48E,6787
62
+ promnesia/tests/test_extract.py,sha256=AAVHySrUo8YGuYNXhhmDHYnSAnhWpPJ9fHedycoI0v0,2367
63
+ promnesia/tests/test_extract_urls.py,sha256=Ybez16VdYoveL9b_50Ca9vbMogWHOvFqDOJPGZrzQJg,1961
64
+ promnesia/tests/test_indexer.py,sha256=0CdYLevIGIZxTJrobhviy59EovRXeaYxHzb4jrwJtpw,9120
65
+ promnesia/tests/test_server.py,sha256=doBcn791eN43B2u6P1U4kbxnLWzp8xFXXzMfW8zsBB0,10914
66
+ promnesia/tests/test_traverse.py,sha256=DUN-NbrZ8b6MKrtZaOavAnZelMPk_aqN2ylvRV7UqHo,1350
67
+ promnesia/tests/utils.py,sha256=wsqNgQBncGXVCQLr9XMkknnd3pejo5FSChaRVNpuwgY,1071
68
+ promnesia/tests/sources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
69
+ promnesia/tests/sources/test_auto.py,sha256=wWqf7x_BXeVSYTDIaD6ZXqqRvqk312biY4iOGcMa4PY,1848
70
+ promnesia/tests/sources/test_filetypes.py,sha256=v0Dpi9N2_3HTbJC7jPPfzOSaPL0QQxCxapO1kr1BhK4,1401
71
+ promnesia/tests/sources/test_hypothesis.py,sha256=E7BE6Q2n6sVIn2j4k0N3N_eJmIb2ufEZoOcCyPcmVZk,1444
72
+ promnesia/tests/sources/test_org.py,sha256=j9qsJK91HHn1cdbLQEs72Jf0rma9Z8q8mrh6WZiDwcA,2610
73
+ promnesia/tests/sources/test_plaintext.py,sha256=X5jY9dsDXFo0Dj8X4XmPw6DYlcyorQQV3NuY-m30baE,865
74
+ promnesia/tests/sources/test_shellcmd.py,sha256=yogl5hDk6Sr91TWVq35b25JZ_6h9ZIMWyYoj2jlav2g,719
75
+ promnesia/tests/sources/test_takeout.py,sha256=HVreW_4pZP8TjGNmrJva5JJfkexmOwwr7cRxwU1Qg_Q,1557
76
+ promnesia-1.4.20250909.dist-info/METADATA,sha256=ZCFCUfr4QuO-Le41FwzBCisZRCrn-i6xpF30FAwjYSE,2871
77
+ promnesia-1.4.20250909.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
78
+ promnesia-1.4.20250909.dist-info/entry_points.txt,sha256=hz1qfzQSRh4kkVkJWk4hnYqE9A1nobEbKlLG_0nNxzE,54
79
+ promnesia-1.4.20250909.dist-info/licenses/LICENSE,sha256=rgO9acPmnw53ZBxiXBdp8kfxmRcekhg_Q7HN65BPihs,1074
80
+ promnesia-1.4.20250909.dist-info/RECORD,,
@@ -1,5 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (72.1.0)
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
-