opa-python-client 2.0.2__tar.gz → 2.0.3__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.
@@ -1,8 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: opa-python-client
3
- Version: 2.0.2
3
+ Version: 2.0.3
4
4
  Summary: Client for connection to the OPA service
5
- Home-page: https://github.com/Turall/OPA-python-client
6
5
  License: MIT
7
6
  Author: Tural Muradov
8
7
  Author-email: tural.muradoov@gmail.com
@@ -21,6 +20,8 @@ Classifier: Programming Language :: Python :: 3 :: Only
21
20
  Requires-Dist: aiofiles (>=24.1.0,<25.0.0)
22
21
  Requires-Dist: aiohttp[speedups] (>=3.10.9,<4.0.0)
23
22
  Requires-Dist: requests (>=2.32.3,<3.0.0)
23
+ Requires-Dist: urllib3 (>=2.5.0,<3.0.0)
24
+ Project-URL: Homepage, https://github.com/Turall/OPA-python-client
24
25
  Project-URL: Repository, https://github.com/Turall/OPA-python-client
25
26
  Description-Content-Type: text/markdown
26
27
 
@@ -380,31 +381,7 @@ await client.delete_data('users')
380
381
 
381
382
  #### Check Permission (Policy Evaluation)
382
383
 
383
- You can evaluate policies with input data using `check_permission`.
384
-
385
- - **Synchronous**:
386
-
387
- ```python
388
- input_data = {"user": "admin"}
389
- policy_name = 'example_policy'
390
- rule_name = 'allow'
391
-
392
- result = client.check_permission(input_data, policy_name, rule_name)
393
- print(result)
394
- ```
395
-
396
- - **Asynchronous**:
397
-
398
- ```python
399
- input_data = {"user": "admin"}
400
- policy_name = 'example_policy'
401
- rule_name = 'allow'
402
-
403
- result = await client.check_permission(input_data, policy_name, rule_name)
404
- print(result)
405
- ```
406
-
407
- Queries a package rule with the given input data
384
+ Evaluate a rule from a known package path. This is the **recommended method** for evaluating OPA decisions.
408
385
 
409
386
  ```python
410
387
 
@@ -448,6 +425,39 @@ print(await client.query_rule(input_data=check_data, package_path="play", rule_n
448
425
 
449
426
  ```
450
427
 
428
+ You can evaluate policies with input data using `check_permission`.
429
+ ### ⚠️ Deprecated: `check_permission()`
430
+
431
+ This method introspects the policy AST to construct a query path dynamically. It introduces unnecessary overhead and is **not recommended** for production use.
432
+
433
+ - **Synchronous**:
434
+
435
+ ```python
436
+ input_data = {"user": "admin"}
437
+ policy_name = 'example_policy'
438
+ rule_name = 'allow'
439
+
440
+ result = client.check_permission(input_data, policy_name, rule_name)
441
+ print(result)
442
+ ```
443
+ > 🔥 Prefer `query_rule()` instead for better performance and maintainability.
444
+
445
+ ### ⚠️ Deprecated: `check_permission()`
446
+
447
+ - **Asynchronous**:
448
+
449
+ ```python
450
+ input_data = {"user": "admin"}
451
+ policy_name = 'example_policy'
452
+ rule_name = 'allow'
453
+
454
+ result = await client.check_permission(input_data, policy_name, rule_name)
455
+ print(result)
456
+ ```
457
+ > 🔥 Prefer `query_rule()` instead for better performance and maintainability.
458
+
459
+
460
+
451
461
  ### Ad-hoc Queries
452
462
 
453
463
  Execute ad-hoc queries directly:
@@ -354,31 +354,7 @@ await client.delete_data('users')
354
354
 
355
355
  #### Check Permission (Policy Evaluation)
356
356
 
357
- You can evaluate policies with input data using `check_permission`.
358
-
359
- - **Synchronous**:
360
-
361
- ```python
362
- input_data = {"user": "admin"}
363
- policy_name = 'example_policy'
364
- rule_name = 'allow'
365
-
366
- result = client.check_permission(input_data, policy_name, rule_name)
367
- print(result)
368
- ```
369
-
370
- - **Asynchronous**:
371
-
372
- ```python
373
- input_data = {"user": "admin"}
374
- policy_name = 'example_policy'
375
- rule_name = 'allow'
376
-
377
- result = await client.check_permission(input_data, policy_name, rule_name)
378
- print(result)
379
- ```
380
-
381
- Queries a package rule with the given input data
357
+ Evaluate a rule from a known package path. This is the **recommended method** for evaluating OPA decisions.
382
358
 
383
359
  ```python
384
360
 
@@ -422,6 +398,39 @@ print(await client.query_rule(input_data=check_data, package_path="play", rule_n
422
398
 
423
399
  ```
424
400
 
401
+ You can evaluate policies with input data using `check_permission`.
402
+ ### ⚠️ Deprecated: `check_permission()`
403
+
404
+ This method introspects the policy AST to construct a query path dynamically. It introduces unnecessary overhead and is **not recommended** for production use.
405
+
406
+ - **Synchronous**:
407
+
408
+ ```python
409
+ input_data = {"user": "admin"}
410
+ policy_name = 'example_policy'
411
+ rule_name = 'allow'
412
+
413
+ result = client.check_permission(input_data, policy_name, rule_name)
414
+ print(result)
415
+ ```
416
+ > 🔥 Prefer `query_rule()` instead for better performance and maintainability.
417
+
418
+ ### ⚠️ Deprecated: `check_permission()`
419
+
420
+ - **Asynchronous**:
421
+
422
+ ```python
423
+ input_data = {"user": "admin"}
424
+ policy_name = 'example_policy'
425
+ rule_name = 'allow'
426
+
427
+ result = await client.check_permission(input_data, policy_name, rule_name)
428
+ print(result)
429
+ ```
430
+ > 🔥 Prefer `query_rule()` instead for better performance and maintainability.
431
+
432
+
433
+
425
434
  ### Ad-hoc Queries
426
435
 
427
436
  Execute ad-hoc queries directly:
@@ -11,7 +11,7 @@ def create_opa_client(async_mode=False, *args, **kwargs):
11
11
  return OpaClient(*args, **kwargs)
12
12
 
13
13
 
14
- __version__ = "2.0.2"
14
+ __version__ = "2.0.3"
15
15
  __author__ = "Tural Muradov"
16
16
  __license__ = "MIT"
17
17
 
@@ -23,6 +23,7 @@ class BaseClient:
23
23
  ssl: bool = False,
24
24
  cert: Optional[Union[str, tuple]] = None,
25
25
  headers: Optional[dict] = None,
26
+ retries: int = 2,
26
27
  timeout: float = 1.5,
27
28
  ):
28
29
  if not isinstance(port, int):
@@ -34,6 +35,7 @@ class BaseClient:
34
35
  self.ssl = ssl
35
36
  self.cert = cert
36
37
  self.timeout = timeout
38
+ self.retries = retries
37
39
 
38
40
  self.schema = "https://" if ssl else "http://"
39
41
  self.root_url = f"{self.schema}{self.host}:{self.port}/{self.version}"
@@ -41,8 +43,6 @@ class BaseClient:
41
43
  self.headers = headers
42
44
 
43
45
  self._session = None # Will be initialized in the subclass
44
- self.retries = 2
45
- self.timeout = 1.5
46
46
 
47
47
  def _build_url(
48
48
  self, path: str, query_params: Dict[str, str] = None
@@ -1,5 +1,6 @@
1
1
  import os
2
2
  import threading
3
+ import warnings
3
4
  from typing import Dict, Optional
4
5
  from urllib.parse import urlencode
5
6
 
@@ -9,15 +10,15 @@ from urllib3.util.retry import Retry
9
10
 
10
11
  from .base import BaseClient
11
12
  from .errors import (
12
- CheckPermissionError,
13
- ConnectionsError,
14
- DeleteDataError,
15
- DeletePolicyError,
16
- FileError,
17
- PathNotFoundError,
18
- PolicyNotFoundError,
19
- RegoParseError,
20
- TypeException,
13
+ CheckPermissionError,
14
+ ConnectionsError,
15
+ DeleteDataError,
16
+ DeletePolicyError,
17
+ FileError,
18
+ PathNotFoundError,
19
+ PolicyNotFoundError,
20
+ RegoParseError,
21
+ TypeException,
21
22
  )
22
23
 
23
24
 
@@ -61,7 +62,9 @@ class OpaClient(BaseClient):
61
62
  session.mount("https://", adapter)
62
63
 
63
64
  if self.ssl:
64
- session.verify = self.cert
65
+ session.verify = self.ssl
66
+ if self.cert:
67
+ session.cert = self.cert
65
68
 
66
69
  return session
67
70
 
@@ -201,7 +204,9 @@ class OpaClient(BaseClient):
201
204
  bool: True if the policy was successfully updated.
202
205
  """
203
206
  if not os.path.isfile(filepath):
204
- raise FileError("file_not_found",f"'{filepath}' is not a valid file")
207
+ raise FileError(
208
+ "file_not_found", f"'{filepath}' is not a valid file"
209
+ )
205
210
 
206
211
  with open(filepath, "r", encoding="utf-8") as file:
207
212
  policy_str = file.read()
@@ -299,7 +304,9 @@ class OpaClient(BaseClient):
299
304
  policy_raw = policy.get("result", {}).get("raw", "")
300
305
 
301
306
  if not policy_raw:
302
- raise PolicyNotFoundError("resource_not_found", "Policy content is empty")
307
+ raise PolicyNotFoundError(
308
+ "resource_not_found", "Policy content is empty"
309
+ )
303
310
 
304
311
  full_path = os.path.join(path or "", filename)
305
312
 
@@ -308,7 +315,9 @@ class OpaClient(BaseClient):
308
315
  file.write(policy_raw)
309
316
  return True
310
317
  except OSError as e:
311
- raise PathNotFoundError("path_not_found", f"Failed to write to '{full_path}'") from e
318
+ raise PathNotFoundError(
319
+ "path_not_found", f"Failed to write to '{full_path}'"
320
+ ) from e
312
321
 
313
322
  def get_policy(self, policy_name: str) -> dict:
314
323
  """
@@ -383,6 +392,11 @@ class OpaClient(BaseClient):
383
392
  Returns:
384
393
  dict: The result of the permission check.
385
394
  """
395
+ warnings.warn(
396
+ "check_permission is deprecated and will be removed in a future release. Use `query_rule` instead.",
397
+ DeprecationWarning,
398
+ stacklevel=2
399
+ )
386
400
  policy = self.get_policy(policy_name)
387
401
  ast = policy.get("result", {}).get("ast", {})
388
402
  package_path = "/".join(
@@ -394,7 +408,8 @@ class OpaClient(BaseClient):
394
408
 
395
409
  if rule_name not in rules:
396
410
  raise CheckPermissionError(
397
- "resource_not_found", f"Rule '{rule_name}' not found in policy '{policy_name}'"
411
+ "resource_not_found",
412
+ f"Rule '{rule_name}' not found in policy '{policy_name}'",
398
413
  )
399
414
 
400
415
  url = f"{self.root_url}/{package_path}/{rule_name}"
@@ -1,5 +1,5 @@
1
1
  import asyncio
2
- import json
2
+ import warnings
3
3
  import os
4
4
  import ssl
5
5
  from typing import Dict, Optional, Union
@@ -50,9 +50,6 @@ class AsyncOpaClient:
50
50
  headers: Optional[dict] = None,
51
51
  timeout: float = 1.5,
52
52
  ):
53
- if not isinstance(port, int):
54
- raise TypeError("The port must be an integer")
55
-
56
53
  self.host = host.strip()
57
54
  self.port = port
58
55
  self.version = version
@@ -221,9 +218,8 @@ class AsyncOpaClient:
221
218
  ) as response:
222
219
  if response.status == 200:
223
220
  return True
224
- else:
225
- error = await response.json()
226
- raise RegoParseError(error.get("code"), error.get("message"))
221
+ error = await response.json()
222
+ raise RegoParseError(error.get("code"), error.get("message"))
227
223
 
228
224
  async def update_policy_from_file(
229
225
  self, filepath: str, endpoint: str
@@ -426,6 +422,11 @@ class AsyncOpaClient:
426
422
  Returns:
427
423
  dict: The result of the permission check.
428
424
  """
425
+ warnings.warn(
426
+ "check_permission is deprecated and will be removed in a future release. Use `query_rule` instead.",
427
+ DeprecationWarning,
428
+ stacklevel=2
429
+ )
429
430
  policy = await self.get_policy(policy_name)
430
431
  ast = policy.get("result", {}).get("ast", {})
431
432
  package_path = "/".join(
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "opa-python-client"
3
- version = "2.0.2"
3
+ version = "2.0.3"
4
4
  description = "Client for connection to the OPA service"
5
5
  authors = ["Tural Muradov <tural.muradoov@gmail.com>"]
6
6
  license = "MIT"
@@ -25,6 +25,7 @@ python = "^3.9"
25
25
  requests = "^2.32.3"
26
26
  aiohttp = {extras = ["speedups"], version = "^3.10.9"}
27
27
  aiofiles = "^24.1.0"
28
+ urllib3 = "^2.5.0"
28
29
 
29
30
  [tool.poetry.group.dev.dependencies]
30
31
  pytest = "^8.3.3"