Skip to content

Commit a4b6ad6

Browse files
committed
v0.1.7: support http request limit
1 parent a628189 commit a4b6ad6

File tree

6 files changed

+40
-10
lines changed

6 files changed

+40
-10
lines changed

html2notion/translate/notion_import.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from notion_client import AsyncClient
66
from notion_client.errors import RequestTimeoutError
77
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type
8-
from ..utils import logger, test_prepare_conf, config
8+
from ..utils import logger, test_prepare_conf, config, rate_limit
99
from ..translate.html2json import html2json_process
1010

1111

@@ -34,6 +34,8 @@ async def process_file(self, file_path: Path):
3434
logger.info(f"Create notion page: {create_result}")
3535
return "succ"
3636

37+
# https://developers.notion.com/reference/request-limits
38+
# The rate limit for incoming requests per integration is an average of three requests per second.
3739
# Doc of create page: https://developers.notion.com/reference/post-page
3840
@retry(stop=stop_after_attempt(5),
3941
wait=wait_exponential(multiplier=1, min=3, max=30),
@@ -47,10 +49,11 @@ async def create_new_page(self, notion_data):
4749
if blocks:
4850
notion_data.pop("children")
4951
first_chunk = chunks[0] if chunks else []
50-
created_page = await self.notion_client.pages.create(**notion_data, children=first_chunk)
51-
page_id = created_page["id"]
52-
for chunk in chunks[1:]:
53-
await self.notion_client.blocks.children.append(page_id, children=chunk)
52+
async with rate_limit:
53+
created_page = await self.notion_client.pages.create(**notion_data, children=first_chunk)
54+
page_id = created_page["id"]
55+
for chunk in chunks[1:]:
56+
await self.notion_client.blocks.children.append(page_id, children=chunk)
5457
return created_page
5558

5659

html2notion/utils/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from .log import logger, setup_logger
2-
from .load_config import read_config, config
2+
from .load_config import read_config, config, rate_limit
33
from pathlib import Path
44

55

@@ -11,4 +11,4 @@ def test_prepare_conf():
1111
logger.info(f"test_prepare_conf, log path({log_path}), conf path({conf_path})")
1212

1313

14-
__all__ = ['logger', 'setup_logger', 'config', 'read_config', 'test_prepare_conf']
14+
__all__ = ['logger', 'setup_logger', 'config', 'read_config', 'test_prepare_conf', 'rate_limit']

html2notion/utils/load_config.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import json
22
import sys
33
from pathlib import Path
4-
4+
from aiolimiter import AsyncLimiter
5+
rate_limit = AsyncLimiter(3, 1)
56
config = {}
67

78

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ aiohttp>=3.8.4
77
anyio>=3.6.2
88
cos-python-sdk-v5>=1.9.23
99
tenacity>=8.2.2
10-
rich>=13.3.4
10+
rich>=13.3.4
11+
aiolimiter>=1.0.0

setup.cfg

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = html2notion
3-
version = 0.1.6
3+
version = 0.1.7
44
author = selfboot
55
author_email = [email protected]
66
description = This tool can accurately convert HTML to Notion notes and is also useful for exporting Evernote notes to Notion.
@@ -26,6 +26,7 @@ install_requires =
2626
rich>=13.3.4
2727
cos-python-sdk-v5>=1.9.23
2828
tenacity>=8.2.2
29+
aiolimiter>=1.0.0
2930

3031
[options.entry_points]
3132
console_scripts =

tests/test_batchimport.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from tempfile import TemporaryDirectory
88
from http import HTTPStatus
99
from html2notion.translate.batch_import import BatchImport
10+
from html2notion.utils import rate_limit
1011
from html2notion.utils.log import log_only_local
1112

1213
process_once_time = 0.5
@@ -41,6 +42,12 @@ def json(self):
4142
return MockResponse(HTTPStatus.OK, content, elapsed_time)
4243

4344

45+
async def mock_notion_create_page(notion_data, *args, **kwargs):
46+
async with rate_limit:
47+
await asyncio.sleep(0.01)
48+
log_only_local(f"mock_notion_create_page")
49+
return "succ"
50+
4451
@pytest.fixture(params=[10, 20])
4552
def temp_dir_fixture(request):
4653
num_files = request.param
@@ -78,3 +85,20 @@ async def test_batch_process(temp_dir_fixture, concurrent_limit):
7885
f"total_time: {total_time}, sync_time: {sync_time}, least_time: {least_time}")
7986
assert total_time >= least_time
8087
assert total_time <= sync_time
88+
89+
90+
@pytest.mark.parametrize("concurrent_limit", [5, 10, 20])
91+
@pytest.mark.asyncio
92+
async def test_reqlimit(temp_dir_fixture, concurrent_limit):
93+
dir_path = temp_dir_fixture
94+
start_time = time.perf_counter()
95+
with patch("html2notion.translate.notion_import.NotionImporter.create_new_page", side_effect=mock_notion_create_page):
96+
batch_processor = BatchImport(dir_path, concurrent_limit=concurrent_limit)
97+
responses = await batch_processor.process_directory()
98+
99+
end_time = time.perf_counter()
100+
total_time = end_time-start_time
101+
num_files = len(list(dir_path.glob('*.html')))
102+
log_only_local(f"file nums: {num_files}, concurrent {concurrent_limit}, total_time: {total_time}")
103+
# The time deviation within 1 second is acceptable here.
104+
assert (total_time >= num_files / 3 - 1)

0 commit comments

Comments
 (0)