diffusr 0.1.0__py3-none-any.whl → 0.2.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.
diffusr/__init__.py CHANGED
@@ -6,6 +6,7 @@
6
6
  """
7
7
 
8
8
  from ._client import DEFAULT_BASE_URL, TERMINAL_STATUSES, Diffusr
9
+ from ._config import config_path, read_config
9
10
  from ._errors import DiffusrError
10
11
  from ._verify import SIGNATURE_TOLERANCE_SEC, verify_callback
11
12
 
@@ -13,8 +14,10 @@ __all__ = [
13
14
  "Diffusr",
14
15
  "DiffusrError",
15
16
  "verify_callback",
17
+ "config_path",
18
+ "read_config",
16
19
  "DEFAULT_BASE_URL",
17
20
  "TERMINAL_STATUSES",
18
21
  "SIGNATURE_TOLERANCE_SEC",
19
22
  ]
20
- __version__ = "0.1.0"
23
+ __version__ = "0.2.0"
diffusr/_client.py CHANGED
@@ -11,11 +11,12 @@ import urllib.request
11
11
  import uuid
12
12
  from typing import Any, Callable, Dict, Mapping, Optional, Union
13
13
 
14
+ from ._config import config_path, read_config # noqa: F401
14
15
  from ._errors import DiffusrError
15
16
  from ._verify import verify_callback as _verify_callback
16
17
 
17
18
  DEFAULT_BASE_URL = "https://app.diffusr.ai/api/v2"
18
- SDK_VERSION = "0.1.0"
19
+ SDK_VERSION = "0.2.0"
19
20
  TERMINAL_STATUSES = ("SUCCEEDED", "FAILED", "CANCELED")
20
21
  # Never sleep longer than this on one automatic retry; raise instead.
21
22
  MAX_AUTO_RETRY_WAIT_SEC = 30
@@ -30,7 +31,10 @@ _CONTENT_TYPES = {
30
31
  class Diffusr:
31
32
  """Client for the diffusr REST API (v2). Bodies and answers are the API's own snake_case JSON.
32
33
 
33
- api_key dfsr_live_... or dfsr_test_... (free). Default: the DIFFUSR_KEY environment variable.
34
+ api_key dfsr_live_... or dfsr_test_... (free). Default: the DIFFUSR_KEY environment variable,
35
+ then the key `npx diffusr login` saved (see config_path()).
36
+ base_url Default: DIFFUSR_BASE_URL, then the saved file's base_url (only when the key came
37
+ from the file), then https://app.diffusr.ai/api/v2.
34
38
  """
35
39
 
36
40
  def __init__(
@@ -43,10 +47,16 @@ class Diffusr:
43
47
  timeout: float = 60.0,
44
48
  ) -> None:
45
49
  key = api_key or os.environ.get("DIFFUSR_KEY")
50
+ file_base: Optional[str] = None
46
51
  if not key:
47
- raise DiffusrError(0, "MISSING_API_KEY", "Pass an API key or set DIFFUSR_KEY.")
52
+ saved = read_config()
53
+ if saved:
54
+ key = saved["key"]
55
+ file_base = saved.get("base_url") or None
56
+ if not key:
57
+ raise DiffusrError(0, "MISSING_API_KEY", "Pass an API key, set DIFFUSR_KEY, or run `npx diffusr login`.")
48
58
  self._key = key
49
- self._base = (base_url or DEFAULT_BASE_URL).rstrip("/")
59
+ self._base = (base_url or os.environ.get("DIFFUSR_BASE_URL") or file_base or DEFAULT_BASE_URL).rstrip("/")
50
60
  self._max_retries = max_retries
51
61
  self._retry_base = retry_base
52
62
  self._timeout = timeout
@@ -191,6 +201,21 @@ class Diffusr:
191
201
  def rotate_callback_secret(self) -> Dict[str, Any]:
192
202
  return self._request("POST", "/callbacks/secret/rotate", body={})
193
203
 
204
+ def call(
205
+ self,
206
+ method: str,
207
+ path: str,
208
+ query: Optional[Mapping[str, Any]] = None,
209
+ body: Optional[Dict[str, Any]] = None,
210
+ headers: Optional[Dict[str, str]] = None,
211
+ ) -> Dict[str, Any]:
212
+ """Call any API path the typed methods do not cover, e.g. call("GET", "/me").
213
+ Same retries and errors as every other call. Add your own Idempotency-Key
214
+ header for a make call."""
215
+ q = {k: (str(v).lower() if isinstance(v, bool) else v) for k, v in (query or {}).items() if v is not None}
216
+ suffix = f"?{urllib.parse.urlencode(q)}" if q else ""
217
+ return self._request(method, path + suffix, body=body, headers=headers)
218
+
194
219
  @staticmethod
195
220
  def verify_callback(
196
221
  raw_body: Union[str, bytes],
diffusr/_config.py ADDED
@@ -0,0 +1,29 @@
1
+ """The key `npx diffusr login` saves, so Python code can use it too."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import sys
8
+ from typing import Any, Dict, Optional
9
+
10
+
11
+ def config_path() -> str:
12
+ """$XDG_CONFIG_HOME/diffusr/config.json, else ~/.config/diffusr/config.json; on Windows %APPDATA%\\diffusr\\config.json."""
13
+ if sys.platform == "win32":
14
+ base = os.environ.get("APPDATA") or os.path.join(os.path.expanduser("~"), "AppData", "Roaming")
15
+ else:
16
+ base = os.environ.get("XDG_CONFIG_HOME") or os.path.join(os.path.expanduser("~"), ".config")
17
+ return os.path.join(base, "diffusr", "config.json")
18
+
19
+
20
+ def read_config() -> Optional[Dict[str, Any]]:
21
+ """The saved config (it has a "key"), or None when there is none. Never raises."""
22
+ try:
23
+ with open(config_path(), "r", encoding="utf-8") as f:
24
+ data = json.load(f)
25
+ except (OSError, ValueError):
26
+ return None
27
+ if isinstance(data, dict) and isinstance(data.get("key"), str) and data["key"]:
28
+ return data
29
+ return None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: diffusr
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: The diffusr API client: make videos, images and music from code. Free price checks, idempotent starts, wait_for, signed callbacks.
5
5
  Project-URL: Homepage, https://diffusr.ai
6
6
  Project-URL: Documentation, https://diffusr.ai/docs
@@ -33,7 +33,7 @@ Mint keys on the Developers page in the app.
33
33
  ```python
34
34
  from diffusr import Diffusr
35
35
 
36
- diffusr = Diffusr() # reads DIFFUSR_KEY
36
+ diffusr = Diffusr() # reads DIFFUSR_KEY, else the key `npx diffusr login` saved
37
37
 
38
38
  # 1. Ask the price. Free: nothing is made, nothing is spent.
39
39
  quote = diffusr.make_video(
@@ -81,6 +81,28 @@ def hook():
81
81
  constant-time compare, two signatures after a secret rotation, a 5-minute
82
82
  timestamp window, `whsec_` prefix handled.
83
83
 
84
+ ## Command line
85
+
86
+ Install the `diffusr` command with npm (it needs Node 18+) and log in once:
87
+
88
+ ```sh
89
+ npx diffusr login # shows a code, opens your browser, saves a key
90
+ npx diffusr whoami
91
+ npx diffusr make_image "a mug on a sunlit table"
92
+ ```
93
+
94
+ For scripts and agents add `--json` (only the JSON answer on stdout) and
95
+ `--yes` (allow spending; without it nothing is made). `npx diffusr help` lists
96
+ every command.
97
+
98
+ **Where the key is saved.** `~/.config/diffusr/config.json` (or
99
+ `$XDG_CONFIG_HOME/diffusr/config.json`; on Windows `%APPDATA%\diffusr\config.json`).
100
+ Only you can read it. `Diffusr()` uses it when no key is given.
101
+
102
+ **Which key wins.** The `api_key` argument, then the `DIFFUSR_KEY` variable,
103
+ then the saved file. The API address: the `base_url` argument, then
104
+ `DIFFUSR_BASE_URL`, then the saved file's `base_url`.
105
+
84
106
  ## Methods
85
107
 
86
108
  | Method | REST |
@@ -0,0 +1,10 @@
1
+ diffusr/__init__.py,sha256=Z_R1dexi8lV4krgWfXaecz5PRhcxkDtHo5fQgAaaQgg,600
2
+ diffusr/_client.py,sha256=iHriOw2jy9gahN63oHR-bk5wkVc7qMQcfms1UgYU5R8,13289
3
+ diffusr/_config.py,sha256=idMnlOT4UI1ic85e_ISsfpCGdUGq39XVxCrkB3q-NGU,1053
4
+ diffusr/_errors.py,sha256=YWrBDO9lzEqHgckTc66uLcpETfvbWmeyhmB2rFVJkzc,1184
5
+ diffusr/_verify.py,sha256=d5pNG-qf3CtXWuI5Ejm614kB96o0shMPE9xa34s_ZgM,1954
6
+ diffusr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ diffusr-0.2.0.dist-info/METADATA,sha256=ImEyCP3N1Lu_t0he-VrS6WCuiBqhNq1f9tOqp_K0KEw,6019
8
+ diffusr-0.2.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
9
+ diffusr-0.2.0.dist-info/licenses/LICENSE,sha256=1l1mtdgkeKxmibt4kNGlmLRfZhUQWNx-rJB0lQD_dWY,1064
10
+ diffusr-0.2.0.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- diffusr/__init__.py,sha256=GJu0H3cqXBbB8VunnZqrzq0rqAGhOGSlEpP0i1Fasvo,516
2
- diffusr/_client.py,sha256=MMyVDdmff1nIQSuiFKf_9c0sJDITUSY-fkNQjodgKyI,12005
3
- diffusr/_errors.py,sha256=YWrBDO9lzEqHgckTc66uLcpETfvbWmeyhmB2rFVJkzc,1184
4
- diffusr/_verify.py,sha256=d5pNG-qf3CtXWuI5Ejm614kB96o0shMPE9xa34s_ZgM,1954
5
- diffusr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- diffusr-0.1.0.dist-info/METADATA,sha256=XzZOenx6fc7hiOEjMPj-vlsA6LzXjNtfus3xZDAC76A,5151
7
- diffusr-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
8
- diffusr-0.1.0.dist-info/licenses/LICENSE,sha256=1l1mtdgkeKxmibt4kNGlmLRfZhUQWNx-rJB0lQD_dWY,1064
9
- diffusr-0.1.0.dist-info/RECORD,,