lswapi 0.5.2__tar.gz → 0.5.4__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.
@@ -0,0 +1,10 @@
1
+ *.egg-info
2
+ *.pyc
3
+ .eggs
4
+ /.coverage
5
+ /.version
6
+ /coverage.xml
7
+ /dist
8
+ /env
9
+ /htmlcov
10
+ __pycache__
@@ -1,24 +1,13 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.5
2
2
  Name: lswapi
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: a python client library for easy leaseweb api access
5
- License: Apache-2.0
6
- Author: Nico Di Rocco
7
- Author-email: n.dirocco@global.leaseweb.com
8
- Requires-Python: >= 3.9
9
- Classifier: Development Status :: 5 - Production/Stable
10
- Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
11
- Classifier: Operating System :: Unix
12
- Classifier: Programming Language :: Python
13
- Classifier: Programming Language :: Python :: 3.9
14
- Classifier: Programming Language :: Python :: 3.10
15
- Classifier: Programming Language :: Python :: 3.11
16
- Classifier: Programming Language :: Python :: 3.12
17
- Classifier: Programming Language :: Python :: 3.13
18
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
- Classifier: Topic :: Utilities
20
5
  Project-URL: Homepage, http://developer.leaseweb.com
21
6
  Project-URL: Issues, https://github.com/nrocco/lswapi
7
+ Author-email: Nico Di Rocco <n.dirocco@global.leaseweb.com>
8
+ License-Expression: Apache-2.0
9
+ License-File: LICENSE.txt
10
+ Requires-Python: >=3.9
22
11
  Description-Content-Type: text/markdown
23
12
 
24
13
  # lswapi (beta)
@@ -90,8 +79,7 @@ to `get_leaseweb_api` or via the environment variable `LSW_API_KEY`:
90
79
  >>> servers = await response.json()
91
80
 
92
81
 
93
- Contribute
94
- ----------
82
+ ## Contribute
95
83
 
96
84
  1. Fork it
97
85
  2. Create your feature branch (`git checkout -b my-new-feature`)
@@ -99,4 +87,3 @@ Contribute
99
87
  4. Make sure that tests pass (`make test`)
100
88
  5. Push to the branch (`git push origin my-new-feature`)
101
89
  6. Create new Pull Request
102
-
@@ -67,8 +67,7 @@ to `get_leaseweb_api` or via the environment variable `LSW_API_KEY`:
67
67
  >>> servers = await response.json()
68
68
 
69
69
 
70
- Contribute
71
- ----------
70
+ ## Contribute
72
71
 
73
72
  1. Fork it
74
73
  2. Create your feature branch (`git checkout -b my-new-feature`)
@@ -0,0 +1,59 @@
1
+ [project]
2
+ name = "lswapi"
3
+ dynamic = ["version"]
4
+ description = "a python client library for easy leaseweb api access"
5
+ authors = [
6
+ { name="Nico Di Rocco", email="n.dirocco@global.leaseweb.com" },
7
+ ]
8
+ license = "Apache-2.0"
9
+ readme = "README.md"
10
+ requires-python = ">= 3.9"
11
+ dependencies = []
12
+
13
+ [project.urls]
14
+ Homepage = "http://developer.leaseweb.com"
15
+ Issues = "https://github.com/nrocco/lswapi"
16
+
17
+ [dependency-groups]
18
+ dev = [
19
+ "pytest",
20
+ "pytest-cov",
21
+ "requests-mock",
22
+ "ruff",
23
+ ]
24
+ httpie = [
25
+ "httpie",
26
+ ]
27
+ requests = [
28
+ "requests",
29
+ ]
30
+ aio = [
31
+ "aiodns",
32
+ "aiohttp",
33
+ ]
34
+
35
+ [project.entry-points."httpie.plugins.auth.v1"]
36
+ httpie_lswapi = "lswapi.httpie:ApiAuthPlugin"
37
+
38
+ [build-system]
39
+ requires = ["hatchling", "hatch-vcs"]
40
+ build-backend = "hatchling.build"
41
+
42
+ [tool.hatch.build.targets.sdist]
43
+ include = [
44
+ "src/",
45
+ "pyproject.toml",
46
+ "README.md",
47
+ ]
48
+
49
+ [tool.hatch.version]
50
+ source = "vcs"
51
+
52
+ [tool.pytest.ini_options]
53
+ testpaths = ["tests"]
54
+
55
+ [tool.ruff]
56
+ line-length = 999
57
+
58
+ [tool.ruff.lint]
59
+ ignore = ["E501"] # E501 is the line-too-long rule
@@ -1,10 +1,9 @@
1
- from lswapi import __api_base_url__
2
- from lswapi import __auth_token_url__
1
+ from os import environ
2
+
3
+ from lswapi import __api_base_url__, __auth_token_url__
3
4
  from lswapi.aio.client import LeasewebHttpClient
4
5
  from lswapi.aio.middleware import LoggingMiddleware
5
- from lswapi.aio.oauth import AccessTokenFileStore
6
- from lswapi.aio.oauth import LeasewebOAuthMiddleware
7
- from os import environ
6
+ from lswapi.aio.oauth import AccessTokenFileStore, LeasewebOAuthMiddleware
8
7
 
9
8
 
10
9
  def get_leaseweb_api(api_key=None, client_id=None, client_secret=None, base_url=None, token_url=None, token_store=None):
@@ -15,13 +14,16 @@ def get_leaseweb_api(api_key=None, client_id=None, client_secret=None, base_url=
15
14
  if api_key:
16
15
  return LeasewebHttpClient(base_url, middlewares=[LoggingMiddleware(debug=False)], headers={"X-Lsw-Auth": api_key})
17
16
  if not client_id or not client_secret:
18
- raise Exception("No authentication method specified")
19
- return LeasewebHttpClient(base_url, middlewares=[
20
- LeasewebOAuthMiddleware(
21
- client_id,
22
- client_secret,
23
- token_url,
24
- store=AccessTokenFileStore(token_store) if token_store else None,
25
- ),
26
- LoggingMiddleware(debug=False),
27
- ])
17
+ raise RuntimeError("No authentication method specified")
18
+ return LeasewebHttpClient(
19
+ base_url,
20
+ middlewares=[
21
+ LeasewebOAuthMiddleware(
22
+ client_id,
23
+ client_secret,
24
+ token_url,
25
+ store=AccessTokenFileStore(token_store) if token_store else None,
26
+ ),
27
+ LoggingMiddleware(debug=False),
28
+ ],
29
+ )
@@ -1,8 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  from aiohttp import ClientSession
2
4
 
3
5
 
4
6
  class LeasewebHttpClient:
5
- def __init__(self, *args, middlewares: list[callable] = [], **kwargs):
7
+ def __init__(self, *args, middlewares: list[callable] | None = None, **kwargs):
8
+ if middlewares is None:
9
+ middlewares = []
6
10
  self.middlewares = middlewares
7
11
  self.__create_session = lambda: ClientSession(*args, **kwargs)
8
12
  self.session = None
@@ -10,10 +14,9 @@ class LeasewebHttpClient:
10
14
  async def _run_middlewares(self, method, url, next_func, **kwargs):
11
15
  async def middleware_chain(index, method, url, **kwargs):
12
16
  if index < len(self.middlewares):
13
- return await self.middlewares[index](
14
- method, url, lambda m, u, **k: middleware_chain(index + 1, m, u, **k), **kwargs
15
- )
17
+ return await self.middlewares[index](method, url, lambda m, u, **k: middleware_chain(index + 1, m, u, **k), **kwargs)
16
18
  return await next_func(method, url, **kwargs)
19
+
17
20
  return await middleware_chain(0, method, url, **kwargs)
18
21
 
19
22
  async def _request(self, method, url, **kwargs):
@@ -1,9 +1,10 @@
1
- import aiohttp
2
1
  import asyncio
3
2
  import json
4
3
  import os
5
4
  import time
6
5
 
6
+ import aiohttp
7
+
7
8
  from lswapi.aio.client import LeasewebHttpClient
8
9
  from lswapi.aio.middleware import LoggingMiddleware
9
10
 
@@ -0,0 +1,55 @@
1
+ """
2
+ LswApi auth plugin for HTTPie.
3
+ """
4
+
5
+ from hashlib import md5
6
+ from json import dumps, loads
7
+ from pathlib import Path
8
+ from time import time
9
+
10
+ from httpie.cli.definition import parser as httpie_args_parser
11
+ from httpie.plugins import AuthPlugin
12
+
13
+ from lswapi import __auth_token_url__
14
+ from lswapi.requests.oauth import fetch_access_token
15
+
16
+
17
+ class LswApiAuth:
18
+ def __init__(self, client_id, client_secret):
19
+ if not client_id:
20
+ raise ValueError("client_id is required")
21
+ self.client_id = client_id
22
+ if not client_secret:
23
+ raise ValueError("client_secret is required")
24
+ self.client_secret = client_secret
25
+ options = httpie_args_parser.args
26
+ self.token_url = options.auth_token_url or __auth_token_url__
27
+ hash = md5(self.token_url.encode("utf-8")).hexdigest()
28
+ self.token_store = Path.home() / ".cache" / "httpie" / f"{hash}.json"
29
+
30
+ def __call__(self, r):
31
+ if self.token_store.exists():
32
+ token = loads(self.token_store.read_text())
33
+ if "expires_at" in token and token["expires_at"] > time():
34
+ r.headers["Authorization"] = "{token_type} {access_token}".format(**token)
35
+ return r
36
+
37
+ token = fetch_access_token(self.token_url, self.client_id, self.client_secret)
38
+ self.token_store.parent.mkdir(parents=True, exist_ok=True)
39
+ self.token_store.write_text(dumps(token))
40
+
41
+ r.headers["Authorization"] = "{token_type} {access_token}".format(**token)
42
+ return r
43
+
44
+
45
+ class ApiAuthPlugin(AuthPlugin):
46
+ name = "LswApi Oauth"
47
+ auth_type = "lswapi"
48
+ description = "Leaseweb Api Oauth Authentication"
49
+
50
+ params = httpie_args_parser.add_argument_group(title="LswApi Oauth OAuth2.0 options")
51
+
52
+ params.add_argument("--auth-token-url", default=None, metavar="LSW_AUTH_URL", help="OAuth 2.0 Token endpoint URI")
53
+
54
+ def get_auth(self, username, password):
55
+ return LswApiAuth(username, password)
@@ -1,9 +1,8 @@
1
- from lswapi import __api_base_url__
2
- from lswapi import __auth_token_url__
3
- from lswapi.requests.session import LeasewebSession
4
- from lswapi.requests.session import LeasewebApiKeyAuth
5
1
  from os import environ
6
2
 
3
+ from lswapi import __api_base_url__, __auth_token_url__
4
+ from lswapi.requests.session import LeasewebApiKeyAuth, LeasewebSession
5
+
7
6
 
8
7
  def get_leaseweb_api(api_key=None, client_id=None, client_secret=None, base_url=None, token_url=None, token_store=None):
9
8
  base_url = base_url or environ.get("LSW_BASE_URL", __api_base_url__)
@@ -12,10 +11,10 @@ def get_leaseweb_api(api_key=None, client_id=None, client_secret=None, base_url=
12
11
  api_key = api_key or environ.get("LSW_API_KEY")
13
12
  if api_key:
14
13
  api = LeasewebSession(base_url=base_url)
15
- api.auth = (LeasewebApiKeyAuth(api_key))
14
+ api.auth = LeasewebApiKeyAuth(api_key)
16
15
  return api
17
16
  client_id = client_id or environ.get("LSW_CLIENT_ID")
18
17
  client_secret = client_secret or environ.get("LSW_CLIENT_SECRET")
19
18
  if client_id and client_secret:
20
19
  return LeasewebSession(base_url=base_url, client_id=client_id, client_secret=client_secret, token_url=token_url, token_store=token_store)
21
- raise Exception("No authentication method specified")
20
+ raise RuntimeError("No authentication method specified")
@@ -1,11 +1,12 @@
1
- from requests import post
2
1
  from time import time
3
2
 
3
+ from requests import post
4
+
4
5
 
5
6
  def fetch_access_token(token_url, client_id, client_secret):
6
7
  response = post(token_url, auth=(client_id, client_secret), data={"grant_type": "client_credentials"})
7
8
  if response.status_code != 200:
8
- raise Exception("Could not obtain an access token")
9
+ raise RuntimeError("Could not obtain an access token")
9
10
  token = response.json()
10
11
  token["created_at"] = int(time())
11
12
  token["expires_at"] = token["created_at"] + token["expires_in"] - 10
@@ -1,11 +1,12 @@
1
- from json import load, dump
2
- from lswapi import __api_base_url__
3
- from lswapi import __auth_token_url__
4
- from lswapi.requests.oauth import fetch_access_token
1
+ from json import dump, load
5
2
  from os import path
3
+ from time import time
4
+
6
5
  from requests import Session
7
6
  from requests.auth import AuthBase
8
- from time import time
7
+
8
+ from lswapi import __api_base_url__, __auth_token_url__
9
+ from lswapi.requests.oauth import fetch_access_token
9
10
 
10
11
 
11
12
  class LeasewebApiKeyAuth(AuthBase):
@@ -29,10 +30,9 @@ class LeasewebSession(Session):
29
30
  super().__init__()
30
31
 
31
32
  def _fetch_access_token(self):
32
- if not self.access_token and self.token_store:
33
- if path.exists(self.token_store):
34
- with open(self.token_store, "r") as file:
35
- self.access_token = load(file)
33
+ if not self.access_token and self.token_store and path.exists(self.token_store):
34
+ with open(self.token_store, "r") as file:
35
+ self.access_token = load(file)
36
36
  if self.access_token and self.access_token.get("expires_at") > time():
37
37
  return self.access_token
38
38
  self.access_token = fetch_access_token(self.token_url, self.client_id, self.client_secret)
@@ -41,7 +41,9 @@ class LeasewebSession(Session):
41
41
  dump(self.access_token, file)
42
42
  return self.access_token
43
43
 
44
- def request(self, method, url, data=None, headers={}, **kwargs):
44
+ def request(self, method, url, data=None, headers=None, **kwargs):
45
+ if headers is None:
46
+ headers = {}
45
47
  if self.client_id and self.client_secret:
46
48
  headers["Authorization"] = "{token_type} {access_token}".format(**self._fetch_access_token())
47
49
  if not url.startswith("http"):
@@ -1,63 +0,0 @@
1
- [project]
2
- name = "lswapi"
3
- version = "0.5.2"
4
- description = "a python client library for easy leaseweb api access"
5
- authors = [
6
- {name = "Nico Di Rocco",email = "n.dirocco@global.leaseweb.com"}
7
- ]
8
- classifiers = [
9
- "Development Status :: 5 - Production/Stable",
10
- "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
11
- "Operating System :: Unix",
12
- "Programming Language :: Python",
13
- "Programming Language :: Python :: 3.9",
14
- "Programming Language :: Python :: 3.10",
15
- "Programming Language :: Python :: 3.11",
16
- "Programming Language :: Python :: 3.12",
17
- "Programming Language :: Python :: 3.13",
18
- "Topic :: Software Development :: Libraries :: Python Modules",
19
- "Topic :: Utilities"
20
- ]
21
- license = "Apache-2.0"
22
- readme = "README.md"
23
- requires-python = ">= 3.9"
24
- dependencies = [
25
- ]
26
-
27
- [project.urls]
28
- Homepage = "http://developer.leaseweb.com"
29
- Issues = "https://github.com/nrocco/lswapi"
30
-
31
-
32
- [project.entry-points."httpie.plugins.auth.v1"]
33
- httpie_lswapi = "lswapi.httpie:ApiAuthPlugin"
34
-
35
-
36
- [build-system]
37
- requires = ["poetry-core>=2.0.0,<3.0.0"]
38
- build-backend = "poetry.core.masonry.api"
39
-
40
-
41
- [tool.poetry.group.dev.dependencies]
42
- flake8 = "^7.3.0"
43
- pytest = "^8.4.1"
44
- pytest-cov = "^6.2.1"
45
- requests-mock = "^1.12.1"
46
-
47
-
48
- [tool.poetry.group.httpie.dependencies]
49
- httpie = "^3.2.4"
50
-
51
-
52
- [tool.poetry.group.requests.dependencies]
53
- requests = "^2.32.4"
54
-
55
-
56
- [tool.poetry.group.aio.dependencies]
57
- aiodns = "^3.5.0"
58
- aiohttp = "^3.12.15"
59
-
60
-
61
- [tool.pytest.ini_options]
62
- testpaths = ["tests"]
63
-
@@ -1,43 +0,0 @@
1
- """
2
- LswApi auth plugin for HTTPie.
3
- """
4
- from httpie.plugins import AuthPlugin
5
- from json import loads, dumps
6
- from lswapi import __auth_token_url__
7
- from lswapi.requests import fetch_access_token
8
- from os import path
9
- from time import time
10
-
11
-
12
- __token_store__ = path.expanduser("~/.lswapi.token")
13
-
14
-
15
- class LswApiAuth(object):
16
- def __init__(self, client_id, client_secret):
17
- self.client_id = client_id
18
- self.client_secret = client_secret
19
-
20
- def __call__(self, r):
21
- if path.exists(__token_store__):
22
- with open(__token_store__, "r") as file:
23
- token = loads(file.read())
24
- if "expires_at" in token and token["expires_at"] > time():
25
- r.headers["Authorization"] = "{token_type} {access_token}".format(**token)
26
- return r
27
-
28
- token = fetch_access_token(__auth_token_url__, self.client_id, self.client_secret)
29
-
30
- with open(__token_store__, "w") as file:
31
- file.write(dumps(token))
32
-
33
- r.headers["Authorization"] = "{token_type} {access_token}".format(**token)
34
- return r
35
-
36
-
37
- class ApiAuthPlugin(AuthPlugin):
38
- name = "LswApi Oauth"
39
- auth_type = "lswapi"
40
- description = "Leaseweb Api Oauth Authentication"
41
-
42
- def get_auth(self, username, password):
43
- return LswApiAuth(username, password)
File without changes
File without changes
@@ -1,5 +1,5 @@
1
- import time
2
1
  import logging
2
+ import time
3
3
 
4
4
 
5
5
  class LoggingMiddleware: