rowan-python 3.1.10__py3-none-any.whl → 3.1.11__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.
rowan/api_keys.py CHANGED
@@ -21,6 +21,8 @@ class APIKey(BaseModel):
21
21
  :ivar is_revoked: Whether the key has been revoked.
22
22
  :ivar scope: Permission scope ("read", "read_write", or "read_write_delete").
23
23
  :ivar can_manage_api_keys: Whether this key can create/list/revoke other API keys.
24
+ :ivar budget: Maximum credits this key may spend. ``None`` means unlimited.
25
+ :ivar credits_used: Credits spent by this key so far.
24
26
  :ivar scoped_project_uuid: If set, the key can only access this project.
25
27
  :ivar created_by_key_uuid: UUID of the API key used to create this one (if any).
26
28
  :ivar revoked_at: When the key was revoked, if applicable.
@@ -35,6 +37,8 @@ class APIKey(BaseModel):
35
37
  is_revoked: bool
36
38
  scope: str
37
39
  can_manage_api_keys: bool
40
+ budget: float | None = None
41
+ credits_used: float = 0
38
42
  scoped_project_uuid: str | None = None
39
43
  created_by_key_uuid: str | None = None
40
44
  revoked_at: datetime | None = None
@@ -57,6 +61,27 @@ class APIKey(BaseModel):
57
61
  response.raise_for_status()
58
62
  return type(self)(**response.json())
59
63
 
64
+ def refresh(self, in_place: bool = True) -> Self:
65
+ """
66
+ Reload this key's metadata (e.g. ``credits_used``) from the server.
67
+
68
+ :param in_place: If True, update this instance in-place. If False, return new instance.
69
+ :returns: Updated APIKey object.
70
+ :raises ValueError: If this key is no longer present in the account's key list.
71
+ """
72
+ matches = [key for key in list_api_keys(active=None) if key.uuid == self.uuid]
73
+ if not matches:
74
+ raise ValueError(f"API key {self.uuid!r} not found.")
75
+ updated = matches[0]
76
+
77
+ if not in_place:
78
+ return type(self)(**updated.model_dump())
79
+
80
+ for field_name in type(self).model_fields:
81
+ setattr(self, field_name, getattr(updated, field_name))
82
+
83
+ return self
84
+
60
85
 
61
86
  class CreatedAPIKey(BaseModel):
62
87
  """
@@ -78,6 +103,7 @@ def create_api_key(
78
103
  scope: APIKeyScope = "read_write",
79
104
  valid_days: int = 365,
80
105
  scoped_project_uuid: str | None = None,
106
+ budget: float | None = None,
81
107
  ) -> CreatedAPIKey:
82
108
  """
83
109
  Create a new API key.
@@ -89,6 +115,8 @@ def create_api_key(
89
115
  :param scope: Permission scope. One of "read", "read_write", "read_write_delete".
90
116
  :param valid_days: Number of days until the key expires.
91
117
  :param scoped_project_uuid: If provided, restrict the key to a single project.
118
+ :param budget: Maximum credits the key may spend. If not provided, the key has no
119
+ spending limit.
92
120
  :returns: plaintext key together with its metadata; the plaintext key is
93
121
  only returned once — store it immediately.
94
122
  """
@@ -99,6 +127,7 @@ def create_api_key(
99
127
  "scope": scope,
100
128
  "valid_days": valid_days,
101
129
  "scoped_project_uuid": scoped_project_uuid,
130
+ "budget": budget,
102
131
  }
103
132
  with api_client() as client:
104
133
  response = client.post("/api_key", json=payload)
@@ -142,3 +171,28 @@ def revoke_api_key(uuid: str) -> APIKey:
142
171
  response = client.post(f"/api_key/{uuid}/revoke")
143
172
  response.raise_for_status()
144
173
  return APIKey(**response.json())
174
+
175
+
176
+ def update_api_key_budget(uuid: str, budget: float | None) -> APIKey:
177
+ """
178
+ Update the spending budget of an API key.
179
+
180
+ The caller must currently authenticate with an unscoped key that has
181
+ ``can_manage_api_keys`` permission.
182
+
183
+ :param uuid: UUID of the key to update.
184
+ :param budget: New maximum credits the key may spend. Pass ``None`` to remove
185
+ the spending limit.
186
+ :returns: Updated APIKey object.
187
+ """
188
+ with api_client() as client:
189
+ response = client.patch(f"/api_key/{uuid}", json={"budget": budget})
190
+ if response.status_code == 403:
191
+ raise PermissionError(
192
+ "API key update rejected by the server (403). The key you are "
193
+ "authenticating with must be unscoped and have `can_manage_api_keys` "
194
+ "permission. Create a manager key from the Rowan web UI "
195
+ "(Account → API keys) and retry with that key."
196
+ )
197
+ response.raise_for_status()
198
+ return APIKey(**response.json())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rowan-python
3
- Version: 3.1.10
3
+ Version: 3.1.11
4
4
  Summary: Rowan Python Library
5
5
  Project-URL: Homepage, https://github.com/rowansci/rowan-client
6
6
  Project-URL: Bug Tracker, https://github.com/rowansci/rowan-client/issues
@@ -1,5 +1,5 @@
1
1
  rowan/__init__.py,sha256=MhgIDJjrk8M_O9NtBL9I2_e5iTHCcc7Ju3XbBi2wtUA,1788
2
- rowan/api_keys.py,sha256=TvG5l5MmQ3Qt8Z3Y7jCA_lcrwEHuI7xHy-KLbIdQ8_A,4793
2
+ rowan/api_keys.py,sha256=Sv-CYivYUxf32u2TAculXuni422mB3jmHtTxm2cDRRA,6991
3
3
  rowan/calculation.py,sha256=nuUtLTtn9D-FCM9TJl3h5euBgIYqa5fr5r_PSNBS7Xo,3809
4
4
  rowan/config.py,sha256=TejQKSxnzNKKTNL9-2bCLq6RvAh54oVA5Ivl1p_ZT8Q,20899
5
5
  rowan/constants.py,sha256=emCH4m9OL2Hm5E-6mJGM_FgzrK_JrZT-FiKJ6pMNQ4Y,84
@@ -53,7 +53,7 @@ rowan/workflows/solvent_dependent_conformers.py,sha256=Z2xESmcM8WcTsR2TSTUa--Rrh
53
53
  rowan/workflows/spin_states.py,sha256=rjkgie2-XVNIN7O6P93yn6EPv9Ogjy8iTj3ufZIsUgY,9331
54
54
  rowan/workflows/strain.py,sha256=kCW_BlX__sdQG1JVbFZuB-57rkpnxa_JCw3h4uKdGDk,6425
55
55
  rowan/workflows/tautomer_search.py,sha256=mbRl0ZJ7wibueRF8c8_idhXJ1rtwg7LBt--5QwQ_Cck,5767
56
- rowan_python-3.1.10.dist-info/METADATA,sha256=SUJMfB1vlLJiInjerGxUBYjzS7eFXHlaKP5_ma5ctBs,2446
57
- rowan_python-3.1.10.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
58
- rowan_python-3.1.10.dist-info/licenses/LICENSE,sha256=i05z7xEhyrg6f8j0lR3XYjShnF-MJGFQ-DnpsZ8yiVI,1084
59
- rowan_python-3.1.10.dist-info/RECORD,,
56
+ rowan_python-3.1.11.dist-info/METADATA,sha256=pmlTh_uskn9-91pN4XYHT4irvEF2MVo7bBhiumUPZaY,2446
57
+ rowan_python-3.1.11.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
58
+ rowan_python-3.1.11.dist-info/licenses/LICENSE,sha256=i05z7xEhyrg6f8j0lR3XYjShnF-MJGFQ-DnpsZ8yiVI,1084
59
+ rowan_python-3.1.11.dist-info/RECORD,,