python-substack 0.4.0__py3-none-any.whl → 0.5.0__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.
substack/cli.py CHANGED
@@ -18,7 +18,7 @@ class CLIUsageError(Exception):
18
18
  pass
19
19
 
20
20
 
21
- def _api_from_env(cookies_path=None, publication_url=None):
21
+ def _api_from_env(cookies_path=None, publication_url=None, timeout=None):
22
22
  load_dotenv()
23
23
 
24
24
  cookies_path = cookies_path or os.getenv("COOKIES_PATH")
@@ -30,12 +30,14 @@ def _api_from_env(cookies_path=None, publication_url=None):
30
30
  cookies_path=cookies_path,
31
31
  cookies_string=cookies_string,
32
32
  publication_url=publication_url,
33
+ timeout=timeout,
33
34
  )
34
35
 
35
36
  return Api(
36
37
  email=os.getenv("EMAIL"),
37
38
  password=os.getenv("PASSWORD"),
38
39
  publication_url=publication_url,
40
+ timeout=timeout,
39
41
  )
40
42
 
41
43
 
@@ -370,6 +372,9 @@ def _build_parser():
370
372
  parser.add_argument(
371
373
  "--publication-url", help="Override PUBLICATION_URL for this command."
372
374
  )
375
+ parser.add_argument(
376
+ "--timeout", type=float, help="Timeout in seconds for API requests."
377
+ )
373
378
  parser.add_argument("--json", action="store_true", dest="json_output")
374
379
  parser.add_argument("--version", action="version", version=__version__)
375
380
 
@@ -450,6 +455,7 @@ def main(argv=None):
450
455
  api = _api_from_env(
451
456
  cookies_path=args.cookies,
452
457
  publication_url=args.publication_url,
458
+ timeout=args.timeout,
453
459
  )
454
460
  args.handler(api, args)
455
461
  except CLIUsageError as exc:
substack/exceptions.py CHANGED
@@ -1,32 +1,44 @@
1
- import json
2
-
3
-
4
- class SubstackAPIException(Exception):
5
- def __init__(self, status_code, text):
6
- try:
7
- json_res = json.loads(text)
8
- except ValueError:
9
- self.message = f"Invalid JSON error message from Substack: {text}"
10
- else:
11
- self.message = ", ".join(
12
- list(
13
- map(lambda error: error.get("msg", ""), json_res.get("errors", []))
14
- )
15
- )
16
- self.message = self.message or json_res.get("error", "")
17
- self.status_code = status_code
18
-
19
- def __str__(self):
20
- return f"APIError(code={self.status_code}): {self.message}"
21
-
22
-
23
- class SubstackRequestException(Exception):
24
- def __init__(self, message):
25
- self.message = message
26
-
27
- def __str__(self):
28
- return f"SubstackRequestException: {self.message}"
29
-
30
-
31
- class SectionNotExistsException(SubstackRequestException):
32
- pass
1
+ import json
2
+ import re
3
+
4
+
5
+ def _redact_message(text: str) -> str:
6
+ if not text:
7
+ return text
8
+ # Redact common cookie-like values and session tokens
9
+ # e.g., s%3A... or s:... which are typical for express session cookies
10
+ text = re.sub(r"s%3A[a-zA-Z0-9_\-\.\%]+", "[REDACTED_COOKIE]", text)
11
+ text = re.sub(r"s:[a-zA-Z0-9_\-\.\%]+", "[REDACTED_COOKIE]", text)
12
+ return text
13
+
14
+
15
+ class SubstackAPIException(Exception):
16
+ def __init__(self, status_code, text):
17
+ text = _redact_message(text)
18
+ try:
19
+ json_res = json.loads(text)
20
+ except ValueError:
21
+ self.message = f"Invalid JSON error message from Substack: {text}"
22
+ else:
23
+ self.message = ", ".join(
24
+ list(
25
+ map(lambda error: error.get("msg", ""), json_res.get("errors", []))
26
+ )
27
+ )
28
+ self.message = self.message or json_res.get("error", "")
29
+ self.status_code = status_code
30
+
31
+ def __str__(self):
32
+ return f"APIError(code={self.status_code}): {self.message}"
33
+
34
+
35
+ class SubstackRequestException(Exception):
36
+ def __init__(self, message):
37
+ self.message = _redact_message(message)
38
+
39
+ def __str__(self):
40
+ return f"SubstackRequestException: {self.message}"
41
+
42
+
43
+ class SectionNotExistsException(SubstackRequestException):
44
+ pass
@@ -1,13 +0,0 @@
1
- substack/__init__.py,sha256=jF2QV8aP2zgNMtGcOY7FlX1cZdnUDHoON4qzOiQpqrA,437
2
- substack/api.py,sha256=5dIijlGDZI5bPgX9skTzytDkY1IO6etvCv6PbNFUjyQ,23212
3
- substack/cli.py,sha256=JBCsCdmthjzTeDpOYBLeVKOdNYjBrPuY1o_CIsAIekk,21712
4
- substack/exceptions.py,sha256=BbP5W5UpzFcM5SYIxx6snWD_Rmj7F_YjYIYC_r03gZY,911
5
- substack/mdrender.py,sha256=QGJkdp1isFmhVyTRMogIabAK8OO2xQh9CrjnXZvFTY4,9308
6
- substack/nodes.py,sha256=fTsGO0-lTztcXiloIjXrHVnJes9EcltGcrX-mEV2ceQ,5004
7
- substack/post.py,sha256=qDXu-xzO3nRXtahc-yG2EhxKAXB2hG_Z1nLa55UMBGg,19697
8
- substack_mcp/mcp_server.py,sha256=3VTSSsiAmr4btImWpfoEl7irpTgqI-FH4q5r9r3u3w8,10949
9
- python_substack-0.4.0.dist-info/METADATA,sha256=CYRO_5NDtuzFfyyjI8CNZCLogy2NyMGGbyUBIdSgS5A,8405
10
- python_substack-0.4.0.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
11
- python_substack-0.4.0.dist-info/entry_points.txt,sha256=MKPjaBUd-0PtvxsBviStsVq1c0h8JZ_qUoYEsBK1xJc,236
12
- python_substack-0.4.0.dist-info/licenses/LICENSE,sha256=L6jk148I5HhhVbfUvkO3EO7eAoU5zToLio4-ApkCkxg,1062
13
- python_substack-0.4.0.dist-info/RECORD,,