karakeep-python-api 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.
- {karakeep_python_api-0.1.0/karakeep_python_api.egg-info → karakeep_python_api-0.1.2}/PKG-INFO +6 -2
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/README.md +5 -1
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api/__main__.py +72 -26
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api/karakeep_api.py +31 -4
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2/karakeep_python_api.egg-info}/PKG-INFO +6 -2
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/setup.py +1 -1
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/tests/test_karakeep_api.py +99 -0
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/LICENSE +0 -0
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/MANIFEST.in +0 -0
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api/__init__.py +0 -0
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api/datatypes.py +0 -0
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api/openapi_reference.json +0 -0
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api.egg-info/SOURCES.txt +0 -0
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api.egg-info/dependency_links.txt +0 -0
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api.egg-info/entry_points.txt +0 -0
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api.egg-info/requires.txt +0 -0
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api.egg-info/top_level.txt +0 -0
- {karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/setup.cfg +0 -0
{karakeep_python_api-0.1.0/karakeep_python_api.egg-info → karakeep_python_api-0.1.2}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: karakeep_python_api
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Community python client for the Karakeep API.
|
|
5
5
|
Home-page: https://github.com/thiswillbeyourgithub/karakeep_python_api/
|
|
6
6
|
License: GPLv3
|
|
@@ -81,7 +81,7 @@ The following table lists the public methods available in the `KarakeepAPI` clas
|
|
|
81
81
|
| `search_bookmarks` | ✅ | Tested as part of create/delete flow. |
|
|
82
82
|
| `get_a_single_bookmark` | ✅ | Tested as part of create/delete flow. |
|
|
83
83
|
| `delete_a_bookmark` | ✅ | Tested as part of create/delete flow. |
|
|
84
|
-
| `update_a_bookmark` |
|
|
84
|
+
| `update_a_bookmark` | ✅ | Tested for title updates. |
|
|
85
85
|
| `summarize_a_bookmark` | ❌ | |
|
|
86
86
|
| `attach_tags_to_a_bookmark` | ❌ | |
|
|
87
87
|
| `detach_tags_from_a_bookmark` | ❌ | |
|
|
@@ -137,6 +137,7 @@ The client can be configured using the following environment variables:
|
|
|
137
137
|
* `KARAKEEP_PYTHON_API_VERIFY_SSL`: Set to `false` to disable SSL certificate verification (default: `true`).
|
|
138
138
|
* `KARAKEEP_PYTHON_API_VERBOSE`: Set to `true` to enable verbose debug logging for the client and CLI (default: `false`).
|
|
139
139
|
* `KARAKEEP_PYTHON_API_DISABLE_RESPONSE_VALIDATION`: Set to `true` to disable Pydantic validation of API responses. The client will return raw dictionary/list data instead of Pydantic models (default: `false`).
|
|
140
|
+
* `KARAKEEP_PYTHON_API_ENSURE_ASCII`: Set to `true` to escape non-ASCII characters in the JSON output (default: `false`, which means Unicode characters are kept).
|
|
140
141
|
|
|
141
142
|
### Command Line Interface (CLI)
|
|
142
143
|
|
|
@@ -174,6 +175,9 @@ python -m karakeep_python_api get-all-lists | jq '.[0]'
|
|
|
174
175
|
# Create a new bookmark from a link (body provided as JSON string)
|
|
175
176
|
python -m karakeep_python_api create-a-new-bookmark --data '{"type": "link", "url": "https://example.com"}'
|
|
176
177
|
|
|
178
|
+
# Get all tags and ensure ASCII output (e.g., for compatibility with systems that don't handle Unicode well)
|
|
179
|
+
python -m karakeep_python_api --ascii get-all-tags
|
|
180
|
+
|
|
177
181
|
# Dump the raw OpenAPI spec used by the client
|
|
178
182
|
python -m karakeep_python_api --dump-openapi-specification
|
|
179
183
|
```
|
|
@@ -46,7 +46,7 @@ The following table lists the public methods available in the `KarakeepAPI` clas
|
|
|
46
46
|
| `search_bookmarks` | ✅ | Tested as part of create/delete flow. |
|
|
47
47
|
| `get_a_single_bookmark` | ✅ | Tested as part of create/delete flow. |
|
|
48
48
|
| `delete_a_bookmark` | ✅ | Tested as part of create/delete flow. |
|
|
49
|
-
| `update_a_bookmark` |
|
|
49
|
+
| `update_a_bookmark` | ✅ | Tested for title updates. |
|
|
50
50
|
| `summarize_a_bookmark` | ❌ | |
|
|
51
51
|
| `attach_tags_to_a_bookmark` | ❌ | |
|
|
52
52
|
| `detach_tags_from_a_bookmark` | ❌ | |
|
|
@@ -102,6 +102,7 @@ The client can be configured using the following environment variables:
|
|
|
102
102
|
* `KARAKEEP_PYTHON_API_VERIFY_SSL`: Set to `false` to disable SSL certificate verification (default: `true`).
|
|
103
103
|
* `KARAKEEP_PYTHON_API_VERBOSE`: Set to `true` to enable verbose debug logging for the client and CLI (default: `false`).
|
|
104
104
|
* `KARAKEEP_PYTHON_API_DISABLE_RESPONSE_VALIDATION`: Set to `true` to disable Pydantic validation of API responses. The client will return raw dictionary/list data instead of Pydantic models (default: `false`).
|
|
105
|
+
* `KARAKEEP_PYTHON_API_ENSURE_ASCII`: Set to `true` to escape non-ASCII characters in the JSON output (default: `false`, which means Unicode characters are kept).
|
|
105
106
|
|
|
106
107
|
### Command Line Interface (CLI)
|
|
107
108
|
|
|
@@ -139,6 +140,9 @@ python -m karakeep_python_api get-all-lists | jq '.[0]'
|
|
|
139
140
|
# Create a new bookmark from a link (body provided as JSON string)
|
|
140
141
|
python -m karakeep_python_api create-a-new-bookmark --data '{"type": "link", "url": "https://example.com"}'
|
|
141
142
|
|
|
143
|
+
# Get all tags and ensure ASCII output (e.g., for compatibility with systems that don't handle Unicode well)
|
|
144
|
+
python -m karakeep_python_api --ascii get-all-tags
|
|
145
|
+
|
|
142
146
|
# Dump the raw OpenAPI spec used by the client
|
|
143
147
|
python -m karakeep_python_api --dump-openapi-specification
|
|
144
148
|
```
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
from textwrap import dedent
|
|
1
2
|
import inspect
|
|
2
3
|
import json
|
|
3
4
|
import sys
|
|
4
5
|
import os
|
|
5
6
|
import functools
|
|
7
|
+
import re # Import re module
|
|
6
8
|
import click
|
|
7
9
|
import traceback # Moved import to top
|
|
8
10
|
from typing import Any, List, Dict, Optional, Callable, Union, get_origin, get_args, Literal
|
|
@@ -79,6 +81,14 @@ shared_options = [
|
|
|
79
81
|
envvar="KARAKEEP_PYTHON_API_DISABLE_RESPONSE_VALIDATION",
|
|
80
82
|
help="Disable Pydantic validation of API responses (returns raw data).",
|
|
81
83
|
),
|
|
84
|
+
click.option(
|
|
85
|
+
"--ascii",
|
|
86
|
+
"ensure_ascii", # Use 'ensure_ascii' as the destination variable name
|
|
87
|
+
is_flag=True,
|
|
88
|
+
default=False, # Default is False, meaning ensure_ascii=False by default
|
|
89
|
+
envvar="KARAKEEP_PYTHON_API_ENSURE_ASCII",
|
|
90
|
+
help="Escape non-ASCII characters in the JSON output (default: keep Unicode).",
|
|
91
|
+
),
|
|
82
92
|
]
|
|
83
93
|
|
|
84
94
|
|
|
@@ -126,9 +136,9 @@ def print_openapi_spec(ctx, param, value):
|
|
|
126
136
|
is_eager=True, # Process this option before others
|
|
127
137
|
help="Dump the OpenAPI specification JSON to stdout and exit.",
|
|
128
138
|
)
|
|
129
|
-
@add_options(shared_options) # Apply shared options to the group
|
|
139
|
+
@add_options(shared_options) # Apply shared options to the group (ensure_ascii is now included)
|
|
130
140
|
@click.pass_context
|
|
131
|
-
def cli(ctx, base_url, api_key, verify_ssl, verbose, disable_response_validation):
|
|
141
|
+
def cli(ctx, base_url, api_key, verify_ssl, verbose, disable_response_validation, ensure_ascii):
|
|
132
142
|
"""
|
|
133
143
|
Karakeep Python API Command Line Interface.
|
|
134
144
|
|
|
@@ -162,6 +172,7 @@ def cli(ctx, base_url, api_key, verify_ssl, verbose, disable_response_validation
|
|
|
162
172
|
ctx.obj["DISABLE_RESPONSE_VALIDATION"] = (
|
|
163
173
|
disable_response_validation # Store the flag
|
|
164
174
|
)
|
|
175
|
+
ctx.obj["ENSURE_ASCII"] = ensure_ascii # Store the ensure_ascii flag
|
|
165
176
|
|
|
166
177
|
# --- Configure Logger ---
|
|
167
178
|
log_level = "DEBUG" if verbose else "INFO"
|
|
@@ -198,6 +209,7 @@ def create_click_command(
|
|
|
198
209
|
verify_ssl = ctx.obj["VERIFY_SSL"]
|
|
199
210
|
verbose = ctx.obj["VERBOSE"]
|
|
200
211
|
disable_validation = ctx.obj["DISABLE_RESPONSE_VALIDATION"] # Retrieve flag
|
|
212
|
+
ensure_ascii_output = ctx.obj["ENSURE_ASCII"] # Retrieve ensure_ascii flag
|
|
201
213
|
|
|
202
214
|
if not api_key:
|
|
203
215
|
click.echo(
|
|
@@ -238,9 +250,32 @@ def create_click_command(
|
|
|
238
250
|
k: v for k, v in call_args.items() if k in valid_arg_names
|
|
239
251
|
}
|
|
240
252
|
|
|
241
|
-
#
|
|
242
|
-
#
|
|
243
|
-
|
|
253
|
+
# --- JSON Parsing for Dict/List Parameters ---
|
|
254
|
+
# Iterate through the expected parameters from the signature
|
|
255
|
+
for param_name, param_sig in signature.parameters.items():
|
|
256
|
+
if param_name in call_args:
|
|
257
|
+
param_value = call_args[param_name]
|
|
258
|
+
param_annotation = param_sig.annotation
|
|
259
|
+
origin = getattr(param_annotation, "__origin__", None)
|
|
260
|
+
|
|
261
|
+
# Check if the annotation is dict/list or typing.Dict/List
|
|
262
|
+
# and if the received value is a string (needs parsing)
|
|
263
|
+
if (
|
|
264
|
+
(param_annotation in (dict, list) or origin in (dict, list, Dict, List))
|
|
265
|
+
and isinstance(param_value, str)
|
|
266
|
+
):
|
|
267
|
+
try:
|
|
268
|
+
# Attempt to parse the JSON string
|
|
269
|
+
call_args[param_name] = json.loads(param_value)
|
|
270
|
+
logger.debug(f"Parsed JSON string for parameter '{param_name}'.")
|
|
271
|
+
except json.JSONDecodeError as json_err:
|
|
272
|
+
# Handle invalid JSON input from the user
|
|
273
|
+
click.echo(
|
|
274
|
+
f"Error: Invalid JSON provided for parameter '{param_name.replace('_', '-')}': {json_err}",
|
|
275
|
+
err=True,
|
|
276
|
+
)
|
|
277
|
+
click.echo(f"Provided value: {param_value}", err=True)
|
|
278
|
+
ctx.exit(1)
|
|
244
279
|
|
|
245
280
|
# Call the API method
|
|
246
281
|
try:
|
|
@@ -262,9 +297,10 @@ def create_click_command(
|
|
|
262
297
|
# Serialize and print the result
|
|
263
298
|
if result is not None:
|
|
264
299
|
output_data = serialize_output(result)
|
|
300
|
+
# Use ensure_ascii_output flag to control JSON encoding
|
|
265
301
|
click.echo(
|
|
266
|
-
json.dumps(output_data, indent=2)
|
|
267
|
-
)
|
|
302
|
+
json.dumps(output_data, indent=2, ensure_ascii=ensure_ascii_output)
|
|
303
|
+
)
|
|
268
304
|
else:
|
|
269
305
|
# Handle None result (e.g., 204 No Content) gracefully
|
|
270
306
|
# Verbose check is implicitly handled by logger level
|
|
@@ -302,35 +338,45 @@ def create_click_command(
|
|
|
302
338
|
click_params = []
|
|
303
339
|
# Use the docstring from the original method (captured by functools.update_wrapper)
|
|
304
340
|
docstring = api_method.__doc__ or f"Execute the {api_method_name} API operation."
|
|
341
|
+
docstring = dedent(docstring)
|
|
305
342
|
docstring_lines = docstring.split("\n")
|
|
306
|
-
help_text =
|
|
307
|
-
|
|
343
|
+
help_text = " ".join(docstring.split("\n\n")[0].splitlines()).strip() # First lines as short help
|
|
344
|
+
# Full docstring as help
|
|
345
|
+
full_help = docstring
|
|
346
|
+
|
|
347
|
+
# tweak the whitespaces in the full help:
|
|
348
|
+
# full_help = full_help.replace("\n", "\n\n").replace("\n ", " ")
|
|
349
|
+
full_help = full_help.replace("\n ", " ")
|
|
350
|
+
full_help = full_help.replace("\n", "\n\n")
|
|
308
351
|
|
|
309
352
|
# Extract parameter descriptions from the Args section of the docstring
|
|
310
353
|
param_descriptions = {}
|
|
311
354
|
in_args_section = False
|
|
312
355
|
args_section_lines = []
|
|
356
|
+
assert "Returns:" in docstring
|
|
357
|
+
assert "Raises:" in docstring
|
|
313
358
|
for line in docstring_lines:
|
|
314
|
-
stripped_line = line.strip()
|
|
315
|
-
if stripped_line == "Args:":
|
|
316
|
-
in_args_section = True
|
|
359
|
+
stripped_line = line.strip()
|
|
360
|
+
if stripped_line == "Args:":
|
|
361
|
+
in_args_section = True
|
|
317
362
|
elif (
|
|
318
363
|
stripped_line == "Returns:" or stripped_line == "Raises:"
|
|
319
|
-
):
|
|
320
|
-
in_args_section = False # Stop capturing when Returns/Raises section starts
|
|
321
|
-
elif in_args_section and stripped_line:
|
|
322
|
-
args_section_lines.append(stripped_line)
|
|
323
|
-
#
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
]
|
|
364
|
+
):
|
|
365
|
+
in_args_section = False # Stop capturing when Returns/Raises section starts
|
|
366
|
+
elif in_args_section and stripped_line:
|
|
367
|
+
args_section_lines.append(stripped_line)
|
|
368
|
+
# Use regex to capture 'param_name: description' structure, allowing leading whitespace
|
|
369
|
+
# Pattern: ^\s+ (parameter_name): \s* (description) $
|
|
370
|
+
match = re.match(r"^\s+([a-zA-Z_][a-zA-Z0-9_]*):\s+(.*)$", line)
|
|
371
|
+
if match and match.group(1) != "Example":
|
|
372
|
+
param_name = match.group(1)
|
|
373
|
+
description = match.group(2).strip()
|
|
374
|
+
param_descriptions[param_name] = description
|
|
375
|
+
logger.trace(f"Parsed docstring param: '{param_name}' -> '{description}'")
|
|
376
|
+
else:
|
|
377
|
+
param_descriptions[param_name] += " " + stripped_line
|
|
333
378
|
|
|
379
|
+
# Removed breakpoint() that was added for debugging
|
|
334
380
|
# Add parameters from signature to Click command
|
|
335
381
|
for param in params: # Use the filtered list from signature inspection
|
|
336
382
|
param_name_cli = param.name.replace("_", "-") # Use kebab-case for CLI options
|
|
@@ -85,7 +85,7 @@ class KarakeepAPI:
|
|
|
85
85
|
"""
|
|
86
86
|
|
|
87
87
|
# Version reflects the client library version, updated by bumpver
|
|
88
|
-
VERSION: str = "0.1.
|
|
88
|
+
VERSION: str = "0.1.2"
|
|
89
89
|
|
|
90
90
|
def __init__(
|
|
91
91
|
self,
|
|
@@ -209,7 +209,7 @@ class KarakeepAPI:
|
|
|
209
209
|
self.strict_response_parsing = (
|
|
210
210
|
strict_response_parsing # Currently unused but kept
|
|
211
211
|
)
|
|
212
|
-
self.last_request_time: float =
|
|
212
|
+
self.last_request_time: float = time.monotonic() # Initialize timestamp for rate limiting
|
|
213
213
|
|
|
214
214
|
# --- Response Validation Setting ---
|
|
215
215
|
# Argument takes precedence over environment variable
|
|
@@ -463,7 +463,7 @@ class KarakeepAPI:
|
|
|
463
463
|
logger.warning(
|
|
464
464
|
f"Error encounterd during requests. Trial={trial}/{max_trial}. Retrying after a small wait.\nError: {e}"
|
|
465
465
|
)
|
|
466
|
-
time.sleep(trial *
|
|
466
|
+
time.sleep(trial * 3)
|
|
467
467
|
else:
|
|
468
468
|
raise
|
|
469
469
|
|
|
@@ -590,7 +590,7 @@ class KarakeepAPI:
|
|
|
590
590
|
) from e
|
|
591
591
|
|
|
592
592
|
@optional_typecheck
|
|
593
|
-
def _enforce_rate_limit(self, min_interval_sec: float =
|
|
593
|
+
def _enforce_rate_limit(self, min_interval_sec: float = 2.0) -> None:
|
|
594
594
|
"""
|
|
595
595
|
Ensures a minimum time interval between consecutive API calls.
|
|
596
596
|
|
|
@@ -855,6 +855,7 @@ class KarakeepAPI:
|
|
|
855
855
|
"""
|
|
856
856
|
Delete a bookmark by its ID. Corresponds to DELETE /bookmarks/{bookmarkId}.
|
|
857
857
|
|
|
858
|
+
Args:
|
|
858
859
|
bookmark_id: The ID (string) of the bookmark to delete.
|
|
859
860
|
|
|
860
861
|
Returns:
|
|
@@ -901,6 +902,7 @@ class KarakeepAPI:
|
|
|
901
902
|
Summarize a bookmark by its ID. Corresponds to POST /bookmarks/{bookmarkId}/summarize.
|
|
902
903
|
This triggers the summarization process and returns the updated bookmark record (partially).
|
|
903
904
|
|
|
905
|
+
Args:
|
|
904
906
|
bookmark_id: The ID (string) of the bookmark to summarize.
|
|
905
907
|
|
|
906
908
|
Returns:
|
|
@@ -924,6 +926,7 @@ class KarakeepAPI:
|
|
|
924
926
|
"""
|
|
925
927
|
Attach one or more tags to a bookmark. Corresponds to POST /bookmarks/{bookmarkId}/tags.
|
|
926
928
|
|
|
929
|
+
Args:
|
|
927
930
|
bookmark_id: The ID (string) of the bookmark.
|
|
928
931
|
tags_data: Dictionary specifying the tags to attach. Must contain a "tags" key
|
|
929
932
|
which is a list of objects, each having *either* "tagId" (string) *or* "tagName" (string).
|
|
@@ -950,6 +953,7 @@ class KarakeepAPI:
|
|
|
950
953
|
"""
|
|
951
954
|
Detach one or more tags from a bookmark. Corresponds to DELETE /bookmarks/{bookmarkId}/tags.
|
|
952
955
|
|
|
956
|
+
Args:
|
|
953
957
|
bookmark_id: The ID (string) of the bookmark.
|
|
954
958
|
tags_data: Dictionary specifying the tags to detach. Must contain a "tags" key
|
|
955
959
|
which is a list of objects, each having *either* "tagId" (string) *or* "tagName" (string).
|
|
@@ -976,6 +980,7 @@ class KarakeepAPI:
|
|
|
976
980
|
"""
|
|
977
981
|
Get all highlights associated with a specific bookmark. Corresponds to GET /bookmarks/{bookmarkId}/highlights.
|
|
978
982
|
|
|
983
|
+
Args:
|
|
979
984
|
bookmark_id: The ID (string) of the bookmark.
|
|
980
985
|
|
|
981
986
|
Returns:
|
|
@@ -1023,6 +1028,7 @@ class KarakeepAPI:
|
|
|
1023
1028
|
"""
|
|
1024
1029
|
Attach a new asset to a bookmark. Corresponds to POST /bookmarks/{bookmarkId}/assets.
|
|
1025
1030
|
|
|
1031
|
+
Args:
|
|
1026
1032
|
bookmark_id: The ID (string) of the bookmark.
|
|
1027
1033
|
asset_data: Dictionary specifying the asset to attach. Must contain "id" (string) and "assetType" (string enum).
|
|
1028
1034
|
Example: `{"id": "asset_id_string", "assetType": "screenshot"}`
|
|
@@ -1054,6 +1060,7 @@ class KarakeepAPI:
|
|
|
1054
1060
|
Replace an existing asset associated with a bookmark with a new one.
|
|
1055
1061
|
Corresponds to PUT /bookmarks/{bookmarkId}/assets/{assetId}.
|
|
1056
1062
|
|
|
1063
|
+
Args:
|
|
1057
1064
|
bookmark_id: The ID (string) of the bookmark.
|
|
1058
1065
|
asset_id: The ID (string) of the asset to be replaced.
|
|
1059
1066
|
new_asset_data: Dictionary specifying the new asset ID. Must contain "assetId" (string).
|
|
@@ -1074,6 +1081,7 @@ class KarakeepAPI:
|
|
|
1074
1081
|
"""
|
|
1075
1082
|
Detach an asset from a bookmark. Corresponds to DELETE /bookmarks/{bookmarkId}/assets/{assetId}.
|
|
1076
1083
|
|
|
1084
|
+
Args:
|
|
1077
1085
|
bookmark_id: The ID (string) of the bookmark.
|
|
1078
1086
|
asset_id: The ID (string) of the asset to detach.
|
|
1079
1087
|
|
|
@@ -1092,6 +1100,7 @@ class KarakeepAPI:
|
|
|
1092
1100
|
"""
|
|
1093
1101
|
Get all lists for the current user. Corresponds to GET /lists.
|
|
1094
1102
|
|
|
1103
|
+
Returns:
|
|
1095
1104
|
List[datatypes.ListModel]: A list of list objects.
|
|
1096
1105
|
If response validation is disabled, returns the raw API response (dict/list).
|
|
1097
1106
|
|
|
@@ -1135,6 +1144,7 @@ class KarakeepAPI:
|
|
|
1135
1144
|
"""
|
|
1136
1145
|
Create a new list (manual or smart). Corresponds to POST /lists.
|
|
1137
1146
|
|
|
1147
|
+
Args:
|
|
1138
1148
|
list_data: Dictionary containing the data for the new list. Requires "name" (string) and "icon" (string).
|
|
1139
1149
|
Optional fields include "description", "parentId", "type" ('manual' or 'smart'), "query".
|
|
1140
1150
|
See the OpenAPI spec for details. Example: `{"name": "My List", "icon": "📚"}`
|
|
@@ -1163,6 +1173,7 @@ class KarakeepAPI:
|
|
|
1163
1173
|
"""
|
|
1164
1174
|
Get a single list by its ID. Corresponds to GET /lists/{listId}.
|
|
1165
1175
|
|
|
1176
|
+
Args:
|
|
1166
1177
|
list_id: The ID (string) of the list to retrieve.
|
|
1167
1178
|
|
|
1168
1179
|
Returns:
|
|
@@ -1188,6 +1199,7 @@ class KarakeepAPI:
|
|
|
1188
1199
|
"""
|
|
1189
1200
|
Delete a list by its ID. Corresponds to DELETE /lists/{listId}.
|
|
1190
1201
|
|
|
1202
|
+
Args:
|
|
1191
1203
|
list_id: The ID (string) of the list to delete.
|
|
1192
1204
|
|
|
1193
1205
|
Returns:
|
|
@@ -1208,6 +1220,7 @@ class KarakeepAPI:
|
|
|
1208
1220
|
Update a list by its ID. Corresponds to PATCH /lists/{listId}.
|
|
1209
1221
|
Allows updating fields like "name", "description", "icon", "parentId", "query".
|
|
1210
1222
|
|
|
1223
|
+
Args:
|
|
1211
1224
|
list_id: The ID (string) of the list to update.
|
|
1212
1225
|
update_data: A dictionary containing the fields to update (e.g., `{"name": "new name"}`).
|
|
1213
1226
|
See the OpenAPI spec for allowed fields.
|
|
@@ -1241,6 +1254,7 @@ class KarakeepAPI:
|
|
|
1241
1254
|
"""
|
|
1242
1255
|
Get the bookmarks contained within a specific list. Corresponds to GET /lists/{listId}/bookmarks.
|
|
1243
1256
|
|
|
1257
|
+
Args:
|
|
1244
1258
|
list_id: The ID (string) of the list.
|
|
1245
1259
|
limit: Maximum number of bookmarks to return (optional).
|
|
1246
1260
|
cursor: Pagination cursor for the next page (optional).
|
|
@@ -1274,6 +1288,7 @@ class KarakeepAPI:
|
|
|
1274
1288
|
"""
|
|
1275
1289
|
Add a bookmark to a specific list. Corresponds to PUT /lists/{listId}/bookmarks/{bookmarkId}.
|
|
1276
1290
|
|
|
1291
|
+
Args:
|
|
1277
1292
|
list_id: The ID (string) of the list.
|
|
1278
1293
|
bookmark_id: The ID (string) of the bookmark to add.
|
|
1279
1294
|
|
|
@@ -1292,6 +1307,7 @@ class KarakeepAPI:
|
|
|
1292
1307
|
"""
|
|
1293
1308
|
Remove a bookmark from a specific list. Corresponds to DELETE /lists/{listId}/bookmarks/{bookmarkId}.
|
|
1294
1309
|
|
|
1310
|
+
Args:
|
|
1295
1311
|
list_id: The ID (string) of the list.
|
|
1296
1312
|
bookmark_id: The ID (string) of the bookmark to remove.
|
|
1297
1313
|
|
|
@@ -1310,6 +1326,7 @@ class KarakeepAPI:
|
|
|
1310
1326
|
"""
|
|
1311
1327
|
Get all tags for the current user. Corresponds to GET /tags.
|
|
1312
1328
|
|
|
1329
|
+
Returns:
|
|
1313
1330
|
List[datatypes.Tag1]: A list of tag objects, including bookmark counts.
|
|
1314
1331
|
If response validation is disabled, returns the raw API response (dict/list).
|
|
1315
1332
|
|
|
@@ -1351,6 +1368,7 @@ class KarakeepAPI:
|
|
|
1351
1368
|
"""
|
|
1352
1369
|
Get a single tag by its ID. Corresponds to GET /tags/{tagId}.
|
|
1353
1370
|
|
|
1371
|
+
Args:
|
|
1354
1372
|
tag_id: The ID (string) of the tag to retrieve.
|
|
1355
1373
|
|
|
1356
1374
|
Returns:
|
|
@@ -1376,6 +1394,7 @@ class KarakeepAPI:
|
|
|
1376
1394
|
"""
|
|
1377
1395
|
Delete a tag by its ID. Corresponds to DELETE /tags/{tagId}.
|
|
1378
1396
|
|
|
1397
|
+
Args:
|
|
1379
1398
|
tag_id: The ID (string) of the tag to delete.
|
|
1380
1399
|
|
|
1381
1400
|
Returns:
|
|
@@ -1396,6 +1415,7 @@ class KarakeepAPI:
|
|
|
1396
1415
|
Update a tag by its ID. Currently only supports updating the "name".
|
|
1397
1416
|
Corresponds to PATCH /tags/{tagId}.
|
|
1398
1417
|
|
|
1418
|
+
Args:
|
|
1399
1419
|
tag_id: The ID (string) of the tag to update.
|
|
1400
1420
|
update_data: A dictionary containing the fields to update. Must include "name" (string).
|
|
1401
1421
|
Example: `{"name": "new tag name"}`
|
|
@@ -1429,6 +1449,7 @@ class KarakeepAPI:
|
|
|
1429
1449
|
"""
|
|
1430
1450
|
Get the bookmarks associated with a specific tag. Corresponds to GET /tags/{tagId}/bookmarks.
|
|
1431
1451
|
|
|
1452
|
+
Args:
|
|
1432
1453
|
tag_id: The ID (string) of the tag.
|
|
1433
1454
|
limit: Maximum number of bookmarks to return (optional).
|
|
1434
1455
|
cursor: Pagination cursor for the next page (optional).
|
|
@@ -1493,6 +1514,7 @@ class KarakeepAPI:
|
|
|
1493
1514
|
"""
|
|
1494
1515
|
Create a new highlight on a bookmark. Corresponds to POST /highlights.
|
|
1495
1516
|
|
|
1517
|
+
Args:
|
|
1496
1518
|
highlight_data: Dictionary containing the data for the new highlight. Requires "bookmarkId" (string),
|
|
1497
1519
|
"startOffset" (number), "endOffset" (number). Optional fields include "color", "text", "note".
|
|
1498
1520
|
See the OpenAPI spec for details. Example: `{"bookmarkId": "...", "startOffset": 10, "endOffset": 25}`
|
|
@@ -1521,6 +1543,7 @@ class KarakeepAPI:
|
|
|
1521
1543
|
"""
|
|
1522
1544
|
Get a single highlight by its ID. Corresponds to GET /highlights/{highlightId}.
|
|
1523
1545
|
|
|
1546
|
+
Args:
|
|
1524
1547
|
highlight_id: The ID (string) of the highlight to retrieve.
|
|
1525
1548
|
|
|
1526
1549
|
Returns:
|
|
@@ -1549,6 +1572,7 @@ class KarakeepAPI:
|
|
|
1549
1572
|
Delete a highlight by its ID. Corresponds to DELETE /highlights/{highlightId}.
|
|
1550
1573
|
Note: Unlike most DELETE endpoints, this returns the deleted highlight object on success (status 200).
|
|
1551
1574
|
|
|
1575
|
+
Args:
|
|
1552
1576
|
highlight_id: The ID (string) of the highlight to delete.
|
|
1553
1577
|
|
|
1554
1578
|
Returns:
|
|
@@ -1577,6 +1601,7 @@ class KarakeepAPI:
|
|
|
1577
1601
|
Update a highlight by its ID. Currently only supports updating the "color".
|
|
1578
1602
|
Corresponds to PATCH /highlights/{highlightId}.
|
|
1579
1603
|
|
|
1604
|
+
Args:
|
|
1580
1605
|
highlight_id: The ID (string) of the highlight to update.
|
|
1581
1606
|
update_data: A dictionary containing the fields to update. Must include "color" (string enum).
|
|
1582
1607
|
See `datatypes.Color` enum. Example: `{"color": "red"}`
|
|
@@ -1604,6 +1629,7 @@ class KarakeepAPI:
|
|
|
1604
1629
|
"""
|
|
1605
1630
|
Get information about the current authenticated user. Corresponds to GET /users/me.
|
|
1606
1631
|
|
|
1632
|
+
Returns:
|
|
1607
1633
|
dict: A dictionary containing user information ('id', 'name', 'email').
|
|
1608
1634
|
Validation is not performed on this response type by default.
|
|
1609
1635
|
|
|
@@ -1619,6 +1645,7 @@ class KarakeepAPI:
|
|
|
1619
1645
|
"""
|
|
1620
1646
|
Get statistics about the current authenticated user's data. Corresponds to GET /users/me/stats.
|
|
1621
1647
|
|
|
1648
|
+
Returns:
|
|
1622
1649
|
dict: A dictionary containing user statistics ('numBookmarks', 'numFavorites', 'numArchived', etc.).
|
|
1623
1650
|
Validation is not performed on this response type by default.
|
|
1624
1651
|
|
{karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2/karakeep_python_api.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: karakeep_python_api
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Community python client for the Karakeep API.
|
|
5
5
|
Home-page: https://github.com/thiswillbeyourgithub/karakeep_python_api/
|
|
6
6
|
License: GPLv3
|
|
@@ -81,7 +81,7 @@ The following table lists the public methods available in the `KarakeepAPI` clas
|
|
|
81
81
|
| `search_bookmarks` | ✅ | Tested as part of create/delete flow. |
|
|
82
82
|
| `get_a_single_bookmark` | ✅ | Tested as part of create/delete flow. |
|
|
83
83
|
| `delete_a_bookmark` | ✅ | Tested as part of create/delete flow. |
|
|
84
|
-
| `update_a_bookmark` |
|
|
84
|
+
| `update_a_bookmark` | ✅ | Tested for title updates. |
|
|
85
85
|
| `summarize_a_bookmark` | ❌ | |
|
|
86
86
|
| `attach_tags_to_a_bookmark` | ❌ | |
|
|
87
87
|
| `detach_tags_from_a_bookmark` | ❌ | |
|
|
@@ -137,6 +137,7 @@ The client can be configured using the following environment variables:
|
|
|
137
137
|
* `KARAKEEP_PYTHON_API_VERIFY_SSL`: Set to `false` to disable SSL certificate verification (default: `true`).
|
|
138
138
|
* `KARAKEEP_PYTHON_API_VERBOSE`: Set to `true` to enable verbose debug logging for the client and CLI (default: `false`).
|
|
139
139
|
* `KARAKEEP_PYTHON_API_DISABLE_RESPONSE_VALIDATION`: Set to `true` to disable Pydantic validation of API responses. The client will return raw dictionary/list data instead of Pydantic models (default: `false`).
|
|
140
|
+
* `KARAKEEP_PYTHON_API_ENSURE_ASCII`: Set to `true` to escape non-ASCII characters in the JSON output (default: `false`, which means Unicode characters are kept).
|
|
140
141
|
|
|
141
142
|
### Command Line Interface (CLI)
|
|
142
143
|
|
|
@@ -174,6 +175,9 @@ python -m karakeep_python_api get-all-lists | jq '.[0]'
|
|
|
174
175
|
# Create a new bookmark from a link (body provided as JSON string)
|
|
175
176
|
python -m karakeep_python_api create-a-new-bookmark --data '{"type": "link", "url": "https://example.com"}'
|
|
176
177
|
|
|
178
|
+
# Get all tags and ensure ASCII output (e.g., for compatibility with systems that don't handle Unicode well)
|
|
179
|
+
python -m karakeep_python_api --ascii get-all-tags
|
|
180
|
+
|
|
177
181
|
# Dump the raw OpenAPI spec used by the client
|
|
178
182
|
python -m karakeep_python_api --dump-openapi-specification
|
|
179
183
|
```
|
|
@@ -7,7 +7,7 @@ with open("README.md", "r") as readme:
|
|
|
7
7
|
|
|
8
8
|
setup(
|
|
9
9
|
name="karakeep_python_api",
|
|
10
|
-
version="0.1.
|
|
10
|
+
version="0.1.2",
|
|
11
11
|
description="Community python client for the Karakeep API.", # Simplified description
|
|
12
12
|
long_description=long_description,
|
|
13
13
|
long_description_content_type="text/markdown",
|
|
@@ -4,6 +4,7 @@ import subprocess
|
|
|
4
4
|
import random
|
|
5
5
|
import string
|
|
6
6
|
import time
|
|
7
|
+
import json # Added for CLI test payload generation
|
|
7
8
|
|
|
8
9
|
# Import API, errors, and datatypes from the main package
|
|
9
10
|
from karakeep_python_api import KarakeepAPI, APIError, AuthenticationError, datatypes
|
|
@@ -484,6 +485,104 @@ def test_create_and_delete_bookmark(karakeep_client: KarakeepAPI):
|
|
|
484
485
|
)
|
|
485
486
|
|
|
486
487
|
|
|
488
|
+
def test_update_bookmark_title(karakeep_client: KarakeepAPI):
|
|
489
|
+
"""Test updating a bookmark's title via API and CLI."""
|
|
490
|
+
created_bookmark_id = None
|
|
491
|
+
# Generate a unique suffix for the initial title to avoid collisions
|
|
492
|
+
timestamp = int(time.time())
|
|
493
|
+
random_suffix = "".join(random.choices(string.ascii_lowercase + string.digits, k=6))
|
|
494
|
+
original_title = f"Initial Test Title {timestamp}-{random_suffix}"
|
|
495
|
+
test_url = "https://en.wikipedia.org/wiki/PageForBookmarkUpdateTest" # A distinct URL for this test
|
|
496
|
+
|
|
497
|
+
target_api_title = "this is a test title"
|
|
498
|
+
target_cli_title = "this is a test title (CLI)"
|
|
499
|
+
|
|
500
|
+
try:
|
|
501
|
+
# 1. Create a bookmark with an initial title
|
|
502
|
+
print(f"\nAttempting to create bookmark for URL: {test_url} with original title: '{original_title}'")
|
|
503
|
+
created_bookmark = karakeep_client.create_a_new_bookmark(
|
|
504
|
+
type="link", url=test_url, title=original_title
|
|
505
|
+
)
|
|
506
|
+
assert isinstance(created_bookmark, datatypes.Bookmark), "Response should be a Bookmark model"
|
|
507
|
+
created_bookmark_id = created_bookmark.id
|
|
508
|
+
assert created_bookmark.id, "Created bookmark must have an ID"
|
|
509
|
+
assert created_bookmark.title == original_title, f"Initial title '{created_bookmark.title}' does not match expected '{original_title}'"
|
|
510
|
+
print(f"✓ Successfully created bookmark ID: {created_bookmark_id}, Title: '{created_bookmark.title}'")
|
|
511
|
+
|
|
512
|
+
# 2. Update the bookmark's title using the API client
|
|
513
|
+
print(f"\nAttempting to update bookmark ID {created_bookmark_id} title to: '{target_api_title}' via API")
|
|
514
|
+
update_payload_api = {"title": target_api_title}
|
|
515
|
+
updated_bookmark_partial = karakeep_client.update_a_bookmark(
|
|
516
|
+
bookmark_id=created_bookmark_id, update_data=update_payload_api
|
|
517
|
+
)
|
|
518
|
+
assert isinstance(updated_bookmark_partial, dict), "Update response should be a dict"
|
|
519
|
+
assert updated_bookmark_partial.get("title") == target_api_title, \
|
|
520
|
+
f"Partial response title '{updated_bookmark_partial.get('title')}' does not match target API title '{target_api_title}'"
|
|
521
|
+
print(f"✓ API call to update_a_bookmark successful. Partial response title: '{updated_bookmark_partial.get('title')}'")
|
|
522
|
+
|
|
523
|
+
# 3. Verify the API update by fetching the bookmark again
|
|
524
|
+
print(f"\nFetching bookmark ID {created_bookmark_id} to verify API title update.")
|
|
525
|
+
retrieved_bookmark_after_api_update = karakeep_client.get_a_single_bookmark(
|
|
526
|
+
bookmark_id=created_bookmark_id
|
|
527
|
+
)
|
|
528
|
+
assert isinstance(retrieved_bookmark_after_api_update, datatypes.Bookmark)
|
|
529
|
+
assert retrieved_bookmark_after_api_update.title == target_api_title, \
|
|
530
|
+
f"Retrieved bookmark title '{retrieved_bookmark_after_api_update.title}' does not match expected API-updated title '{target_api_title}'"
|
|
531
|
+
print(f"✓ Successfully verified bookmark title updated by API to: '{retrieved_bookmark_after_api_update.title}'")
|
|
532
|
+
|
|
533
|
+
# 4. Test CLI equivalent for updating the bookmark's title
|
|
534
|
+
print(f"\n Running CLI equivalent to update title to: '{target_cli_title}'")
|
|
535
|
+
cli_update_payload_json = json.dumps({"title": target_cli_title})
|
|
536
|
+
# Ensure the JSON string is properly quoted for the shell command
|
|
537
|
+
cli_update_command = f"python -m karakeep_python_api update-a-bookmark --bookmark-id {created_bookmark_id} --update-data '{cli_update_payload_json}'"
|
|
538
|
+
|
|
539
|
+
try:
|
|
540
|
+
subprocess.run(
|
|
541
|
+
cli_update_command,
|
|
542
|
+
shell=True,
|
|
543
|
+
check=True,
|
|
544
|
+
capture_output=True,
|
|
545
|
+
text=True,
|
|
546
|
+
)
|
|
547
|
+
print("✓ CLI update command executed successfully.")
|
|
548
|
+
|
|
549
|
+
# 5. Verify CLI update by fetching the bookmark again
|
|
550
|
+
print(f"\nFetching bookmark ID {created_bookmark_id} to verify CLI title update.")
|
|
551
|
+
retrieved_bookmark_after_cli_update = karakeep_client.get_a_single_bookmark(
|
|
552
|
+
bookmark_id=created_bookmark_id
|
|
553
|
+
)
|
|
554
|
+
assert isinstance(retrieved_bookmark_after_cli_update, datatypes.Bookmark)
|
|
555
|
+
assert retrieved_bookmark_after_cli_update.title == target_cli_title, \
|
|
556
|
+
f"Retrieved bookmark title '{retrieved_bookmark_after_cli_update.title}' after CLI update does not match expected '{target_cli_title}'"
|
|
557
|
+
print(f"✓ Successfully verified bookmark title updated by CLI to: '{retrieved_bookmark_after_cli_update.title}'")
|
|
558
|
+
|
|
559
|
+
except subprocess.CalledProcessError as e:
|
|
560
|
+
print(f" CLI update command failed with exit code {e.returncode}")
|
|
561
|
+
print(f" Command: {cli_update_command}")
|
|
562
|
+
print(f" Stdout: {e.stdout}")
|
|
563
|
+
print(f" Stderr: {e.stderr}")
|
|
564
|
+
pytest.fail(f"CLI command for update-a-bookmark failed: {e}")
|
|
565
|
+
except Exception as e:
|
|
566
|
+
pytest.fail(f"An unexpected error occurred running the CLI update command: {e}")
|
|
567
|
+
|
|
568
|
+
except (APIError, AuthenticationError) as e:
|
|
569
|
+
pytest.fail(f"API error during bookmark title update test: {e}")
|
|
570
|
+
except Exception as e:
|
|
571
|
+
pytest.fail(f"An unexpected error occurred during bookmark title update test: {e}")
|
|
572
|
+
finally:
|
|
573
|
+
# 6. Delete the bookmark (ensure cleanup)
|
|
574
|
+
if created_bookmark_id:
|
|
575
|
+
print(f"\nAttempting to delete bookmark ID: {created_bookmark_id} during cleanup.")
|
|
576
|
+
try:
|
|
577
|
+
karakeep_client.delete_a_bookmark(bookmark_id=created_bookmark_id)
|
|
578
|
+
print(f"✓ Successfully deleted bookmark ID: {created_bookmark_id}")
|
|
579
|
+
except Exception as e:
|
|
580
|
+
# Log cleanup failure but don't obscure original test failure if any
|
|
581
|
+
print(f"Error during cleanup: Failed to delete bookmark {created_bookmark_id}: {e}")
|
|
582
|
+
# Depending on test policy, you might re-raise or just log
|
|
583
|
+
# pytest.fail(f"Failed to delete bookmark {created_bookmark_id} during cleanup: {e}")
|
|
584
|
+
|
|
585
|
+
|
|
487
586
|
# --- Test User Info/Stats Endpoints ---
|
|
488
587
|
|
|
489
588
|
def test_get_current_user_stats(karakeep_client: KarakeepAPI):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api/openapi_reference.json
RENAMED
|
File without changes
|
{karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api.egg-info/requires.txt
RENAMED
|
File without changes
|
{karakeep_python_api-0.1.0 → karakeep_python_api-0.1.2}/karakeep_python_api.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|