pyixapi 0.2.3__tar.gz → 0.2.5__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.
- {pyixapi-0.2.3 → pyixapi-0.2.5}/PKG-INFO +8 -6
- {pyixapi-0.2.3 → pyixapi-0.2.5}/pyixapi/core/api.py +25 -22
- {pyixapi-0.2.3 → pyixapi-0.2.5}/pyixapi/core/endpoint.py +8 -0
- {pyixapi-0.2.3 → pyixapi-0.2.5}/pyixapi/core/query.py +20 -17
- {pyixapi-0.2.3 → pyixapi-0.2.5}/pyixapi/core/response.py +8 -12
- {pyixapi-0.2.3 → pyixapi-0.2.5}/pyixapi/core/util.py +4 -1
- {pyixapi-0.2.3 → pyixapi-0.2.5}/pyproject.toml +15 -31
- {pyixapi-0.2.3 → pyixapi-0.2.5}/LICENSE +0 -0
- {pyixapi-0.2.3 → pyixapi-0.2.5}/README.md +0 -0
- {pyixapi-0.2.3 → pyixapi-0.2.5}/pyixapi/__init__.py +0 -0
- {pyixapi-0.2.3 → pyixapi-0.2.5}/pyixapi/core/__init__.py +0 -0
- {pyixapi-0.2.3 → pyixapi-0.2.5}/pyixapi/core/token.py +0 -0
- {pyixapi-0.2.3 → pyixapi-0.2.5}/pyixapi/models.py +0 -0
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: pyixapi
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Summary: Python API client library for IX-API
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
Author: Guillaume Mazoyer
|
|
7
7
|
Author-email: oss@mazoyer.eu
|
|
8
|
-
Requires-Python: >=3.
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
9
|
Classifier: Intended Audience :: Developers
|
|
10
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
10
|
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
13
11
|
Classifier: Programming Language :: Python :: 3.9
|
|
14
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
14
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
-
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Provides-Extra: dev
|
|
17
|
+
Requires-Dist: PyJWT (>=2.4.0,<2.11)
|
|
18
|
+
Requires-Dist: pytest ; extra == "dev"
|
|
18
19
|
Requires-Dist: requests (>=2.20.0,<3.0)
|
|
20
|
+
Requires-Dist: ruff (>=0.9,<0.10) ; extra == "dev"
|
|
19
21
|
Description-Content-Type: text/markdown
|
|
20
22
|
|
|
21
23
|
# pyixapi
|
|
@@ -29,7 +29,7 @@ from pyixapi.models import (
|
|
|
29
29
|
RoleAssignment,
|
|
30
30
|
)
|
|
31
31
|
|
|
32
|
-
__version__ = "0.2.
|
|
32
|
+
__version__ = "0.2.5"
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
class API(object):
|
|
@@ -48,6 +48,7 @@ class API(object):
|
|
|
48
48
|
access_token="",
|
|
49
49
|
refresh_token="",
|
|
50
50
|
user_agent=f"pyixapi/{__version__}",
|
|
51
|
+
proxies=None,
|
|
51
52
|
):
|
|
52
53
|
self.url = url.rstrip("/")
|
|
53
54
|
self.key = key
|
|
@@ -56,6 +57,7 @@ class API(object):
|
|
|
56
57
|
self.refresh_token = Token.from_jwt(refresh_token) if refresh_token else None
|
|
57
58
|
self.http_session = requests.Session()
|
|
58
59
|
self.user_agent = user_agent
|
|
60
|
+
self.proxies = proxies
|
|
59
61
|
|
|
60
62
|
self.auth = Endpoint(self, "auth")
|
|
61
63
|
self.connections = Endpoint(self, "connections", model=Connection)
|
|
@@ -65,27 +67,17 @@ class API(object):
|
|
|
65
67
|
self.facilities = Endpoint(self, "facilities", model=Facility)
|
|
66
68
|
self.ips = Endpoint(self, "ips", model=IP)
|
|
67
69
|
self.macs = Endpoint(self, "macs", model=MAC)
|
|
68
|
-
self.network_feature_configs = Endpoint(
|
|
69
|
-
self, "network-feature-configs", model=NetworkFeatureConfig
|
|
70
|
-
)
|
|
70
|
+
self.network_feature_configs = Endpoint(self, "network-feature-configs", model=NetworkFeatureConfig)
|
|
71
71
|
self.network_features = Endpoint(self, "network-features", model=NetworkFeature)
|
|
72
|
-
self.network_service_configs = Endpoint(
|
|
73
|
-
self, "network-service-configs", model=NetworkServiceConfig
|
|
74
|
-
)
|
|
72
|
+
self.network_service_configs = Endpoint(self, "network-service-configs", model=NetworkServiceConfig)
|
|
75
73
|
self.network_services = Endpoint(self, "network-services", model=NetworkService)
|
|
76
74
|
self.pops = Endpoint(self, "pops", model=PoP)
|
|
77
75
|
# Version 2+
|
|
78
|
-
self.member_joining_rules = Endpoint(
|
|
79
|
-
self, "member-joining-rules", model=MemberJoiningRule
|
|
80
|
-
)
|
|
76
|
+
self.member_joining_rules = Endpoint(self, "member-joining-rules", model=MemberJoiningRule)
|
|
81
77
|
self.metro_areas = Endpoint(self, "metro-areas", model=MetroArea)
|
|
82
|
-
self.metro_area_networks = Endpoint(
|
|
83
|
-
self, "metro-area-networks", model=MetroAreaNetwork
|
|
84
|
-
)
|
|
78
|
+
self.metro_area_networks = Endpoint(self, "metro-area-networks", model=MetroAreaNetwork)
|
|
85
79
|
self.ports = Endpoint(self, "ports", model=Port)
|
|
86
|
-
self.port_reservations = Endpoint(
|
|
87
|
-
self, "port-reservations", model=PortReservation
|
|
88
|
-
)
|
|
80
|
+
self.port_reservations = Endpoint(self, "port-reservations", model=PortReservation)
|
|
89
81
|
self.roles = Endpoint(self, "roles", model=Role)
|
|
90
82
|
self.role_assignments = Endpoint(self, "role-assignments", model=RoleAssignment)
|
|
91
83
|
|
|
@@ -95,14 +87,16 @@ class API(object):
|
|
|
95
87
|
Get the API version of IX-API.
|
|
96
88
|
"""
|
|
97
89
|
return Request(
|
|
98
|
-
base=self.url,
|
|
90
|
+
base=self.url,
|
|
91
|
+
token=self.access_token,
|
|
92
|
+
http_session=self.http_session,
|
|
93
|
+
user_agent=self.user_agent,
|
|
94
|
+
proxies=self.proxies,
|
|
99
95
|
).get_version()
|
|
100
96
|
|
|
101
97
|
@property
|
|
102
98
|
def accounts(self):
|
|
103
|
-
return Endpoint(
|
|
104
|
-
self, "customers" if self.version == 1 else "accounts", model=Account
|
|
105
|
-
)
|
|
99
|
+
return Endpoint(self, "customers" if self.version == 1 else "accounts", model=Account)
|
|
106
100
|
|
|
107
101
|
@property
|
|
108
102
|
def product_offerings(self):
|
|
@@ -131,7 +125,10 @@ class API(object):
|
|
|
131
125
|
return self.refresh_authentication()
|
|
132
126
|
|
|
133
127
|
r = Request(
|
|
134
|
-
cat(self.url, "auth", "token"),
|
|
128
|
+
cat(self.url, "auth", "token"),
|
|
129
|
+
http_session=self.http_session,
|
|
130
|
+
user_agent=self.user_agent,
|
|
131
|
+
proxies=self.proxies,
|
|
135
132
|
).post(data={"api_key": self.key, "api_secret": self.secret})
|
|
136
133
|
|
|
137
134
|
self.access_token = Token.from_jwt(r["access_token"])
|
|
@@ -147,6 +144,8 @@ class API(object):
|
|
|
147
144
|
cat(self.url, "auth", "refresh"),
|
|
148
145
|
token=self.refresh_token.encoded,
|
|
149
146
|
http_session=self.http_session,
|
|
147
|
+
user_agent=self.user_agent,
|
|
148
|
+
proxies=self.proxies,
|
|
150
149
|
).post(data={"refresh_token": self.refresh_token.encoded})
|
|
151
150
|
|
|
152
151
|
self.access_token = Token.from_jwt(r["access_token"])
|
|
@@ -164,5 +163,9 @@ class API(object):
|
|
|
164
163
|
return {}
|
|
165
164
|
|
|
166
165
|
return Request(
|
|
167
|
-
base=self.url,
|
|
166
|
+
base=self.url,
|
|
167
|
+
token=self.access_token,
|
|
168
|
+
http_session=self.http_session,
|
|
169
|
+
user_agent=self.user_agent,
|
|
170
|
+
proxies=self.proxies,
|
|
168
171
|
).get_health()
|
|
@@ -28,6 +28,8 @@ class Endpoint(object):
|
|
|
28
28
|
base=self.url,
|
|
29
29
|
token=self.api.access_token,
|
|
30
30
|
http_session=self.api.http_session,
|
|
31
|
+
user_agent=self.api.user_agent,
|
|
32
|
+
proxies=self.api.proxies,
|
|
31
33
|
)
|
|
32
34
|
return RecordSet(self, r)
|
|
33
35
|
|
|
@@ -41,6 +43,8 @@ class Endpoint(object):
|
|
|
41
43
|
base=self.url,
|
|
42
44
|
token=self.api.access_token,
|
|
43
45
|
http_session=self.api.http_session,
|
|
46
|
+
user_agent=self.api.user_agent,
|
|
47
|
+
proxies=self.api.proxies,
|
|
44
48
|
)
|
|
45
49
|
return RecordSet(self, r)
|
|
46
50
|
|
|
@@ -74,6 +78,8 @@ class Endpoint(object):
|
|
|
74
78
|
base=self.url,
|
|
75
79
|
token=self.api.access_token,
|
|
76
80
|
http_session=self.api.http_session,
|
|
81
|
+
user_agent=self.api.user_agent,
|
|
82
|
+
proxies=self.api.proxies,
|
|
77
83
|
)
|
|
78
84
|
try:
|
|
79
85
|
return next(RecordSet(self, r), None)
|
|
@@ -94,6 +100,8 @@ class Endpoint(object):
|
|
|
94
100
|
base=self.url,
|
|
95
101
|
token=self.api.access_token,
|
|
96
102
|
http_session=self.api.http_session,
|
|
103
|
+
user_agent=self.api.user_agent,
|
|
104
|
+
proxies=self.api.proxies,
|
|
97
105
|
).post(args[0] if args else kwargs)
|
|
98
106
|
|
|
99
107
|
return self.return_obj(req, self.api, self)
|
|
@@ -14,19 +14,13 @@ class RequestError(Exception):
|
|
|
14
14
|
if r.status_code == 404:
|
|
15
15
|
self.message = f"The requested url: {r.url} could not be found."
|
|
16
16
|
elif r.status_code == 401:
|
|
17
|
-
self.message =
|
|
18
|
-
"Authentication credentials are invalid, tokens renewal required."
|
|
19
|
-
)
|
|
17
|
+
self.message = "Authentication credentials are invalid, tokens renewal required."
|
|
20
18
|
else:
|
|
21
19
|
try:
|
|
22
|
-
self.message = (
|
|
23
|
-
f"The request failed with code {r.status_code} "
|
|
24
|
-
f"{r.reason}: {r.json()}"
|
|
25
|
-
)
|
|
20
|
+
self.message = f"The request failed with code {r.status_code} {r.reason}: {r.json()}"
|
|
26
21
|
except ValueError:
|
|
27
22
|
self.message = (
|
|
28
|
-
f"The request failed with code {r.status_code} "
|
|
29
|
-
f"{r.reason} but details were not found as JSON."
|
|
23
|
+
f"The request failed with code {r.status_code} {r.reason} but details were not found as JSON."
|
|
30
24
|
)
|
|
31
25
|
|
|
32
26
|
super(RequestError, self).__init__(r)
|
|
@@ -48,9 +42,7 @@ class ContentError(Exception):
|
|
|
48
42
|
super(ContentError, self).__init__(req)
|
|
49
43
|
|
|
50
44
|
self.req = req
|
|
51
|
-
self.error = (
|
|
52
|
-
"The server returned invalid (non-JSON) data. Maybe not an IX-API server?"
|
|
53
|
-
)
|
|
45
|
+
self.error = "The server returned invalid (non-JSON) data. Maybe not an IX-API server?"
|
|
54
46
|
|
|
55
47
|
def __str__(self):
|
|
56
48
|
return self.error
|
|
@@ -70,7 +62,14 @@ class Request(object):
|
|
|
70
62
|
"""
|
|
71
63
|
|
|
72
64
|
def __init__(
|
|
73
|
-
self,
|
|
65
|
+
self,
|
|
66
|
+
base,
|
|
67
|
+
http_session,
|
|
68
|
+
filters=None,
|
|
69
|
+
key=None,
|
|
70
|
+
token=None,
|
|
71
|
+
user_agent=None,
|
|
72
|
+
proxies=None,
|
|
74
73
|
):
|
|
75
74
|
self.base = base
|
|
76
75
|
self.filters = filters or None
|
|
@@ -79,15 +78,14 @@ class Request(object):
|
|
|
79
78
|
self.http_session = http_session
|
|
80
79
|
self.url = self.base if not key else cat(self.base, key)
|
|
81
80
|
self.user_agent = user_agent
|
|
81
|
+
self.proxies = proxies
|
|
82
82
|
|
|
83
83
|
def get_openapi(self):
|
|
84
84
|
"""
|
|
85
85
|
Get the OpenAPI Spec.
|
|
86
86
|
"""
|
|
87
87
|
headers = {"Content-Type": "application/json;"}
|
|
88
|
-
req = self.http_session.get(
|
|
89
|
-
cat(self.base, "docs/?format=openapi"), headers=headers
|
|
90
|
-
)
|
|
88
|
+
req = self.http_session.get(cat(self.base, "docs/?format=openapi"), headers=headers)
|
|
91
89
|
if req.ok:
|
|
92
90
|
return req.json()
|
|
93
91
|
else:
|
|
@@ -137,7 +135,12 @@ class Request(object):
|
|
|
137
135
|
params.update(add_params)
|
|
138
136
|
|
|
139
137
|
r = getattr(self.http_session, verb)(
|
|
140
|
-
url_override or self.url,
|
|
138
|
+
url_override or self.url,
|
|
139
|
+
headers=headers,
|
|
140
|
+
params=params,
|
|
141
|
+
json=data,
|
|
142
|
+
user_agent=self.user_agent,
|
|
143
|
+
proxies=self.proxies,
|
|
141
144
|
)
|
|
142
145
|
|
|
143
146
|
if verb == "delete":
|
|
@@ -63,12 +63,8 @@ class RecordSet(object):
|
|
|
63
63
|
|
|
64
64
|
def __next__(self):
|
|
65
65
|
if self._response_cache:
|
|
66
|
-
return self.endpoint.return_obj(
|
|
67
|
-
|
|
68
|
-
)
|
|
69
|
-
return self.endpoint.return_obj(
|
|
70
|
-
next(self.response), self.endpoint.api, self.endpoint
|
|
71
|
-
)
|
|
66
|
+
return self.endpoint.return_obj(self._response_cache.pop(), self.endpoint.api, self.endpoint)
|
|
67
|
+
return self.endpoint.return_obj(next(self.response), self.endpoint.api, self.endpoint)
|
|
72
68
|
|
|
73
69
|
def __len__(self):
|
|
74
70
|
try:
|
|
@@ -250,9 +246,7 @@ class Record(object):
|
|
|
250
246
|
if isinstance(current_val, Record):
|
|
251
247
|
current_val = getattr(current_val, "serialize")(nested=True)
|
|
252
248
|
if isinstance(current_val, list):
|
|
253
|
-
current_val = [
|
|
254
|
-
v.id if isinstance(v, Record) else v for v in current_val
|
|
255
|
-
]
|
|
249
|
+
current_val = [v.id if isinstance(v, Record) else v for v in current_val]
|
|
256
250
|
r[i] = current_val
|
|
257
251
|
return r
|
|
258
252
|
|
|
@@ -265,9 +259,7 @@ class Record(object):
|
|
|
265
259
|
return k, v
|
|
266
260
|
|
|
267
261
|
current = Hashabledict({fmt_dict(k, v) for k, v in self.serialize().items()})
|
|
268
|
-
init = Hashabledict(
|
|
269
|
-
{fmt_dict(k, v) for k, v in self.serialize(init=True).items()}
|
|
270
|
-
)
|
|
262
|
+
init = Hashabledict({fmt_dict(k, v) for k, v in self.serialize(init=True).items()})
|
|
271
263
|
return set([i[0] for i in set(current.items()) ^ set(init.items())])
|
|
272
264
|
|
|
273
265
|
def updates(self):
|
|
@@ -298,6 +290,8 @@ class Record(object):
|
|
|
298
290
|
base=self.endpoint.url,
|
|
299
291
|
token=self.api.access_token,
|
|
300
292
|
http_session=self.api.http_session,
|
|
293
|
+
user_agent=self.user_agent,
|
|
294
|
+
proxies=self.api.proxies,
|
|
301
295
|
)
|
|
302
296
|
if r.patch(updates):
|
|
303
297
|
return True
|
|
@@ -324,5 +318,7 @@ class Record(object):
|
|
|
324
318
|
base=self.endpoint.url,
|
|
325
319
|
token=self.api.access_token,
|
|
326
320
|
http_session=self.api.http_session,
|
|
321
|
+
user_agent=self.user_agent,
|
|
322
|
+
proxies=self.api.proxies,
|
|
327
323
|
)
|
|
328
324
|
return True if r.delete() else False
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
1
4
|
class Hashabledict(dict):
|
|
2
5
|
def __hash__(self):
|
|
3
6
|
return hash(frozenset(self))
|
|
4
7
|
|
|
5
8
|
|
|
6
|
-
def cat(*args, separator="/", trailing=""):
|
|
9
|
+
def cat(*args: Any, separator: str = "/", trailing: str = "") -> str:
|
|
7
10
|
"""
|
|
8
11
|
Concatenate strings given a separator. All items will be parsed as string.
|
|
9
12
|
|
|
@@ -1,47 +1,34 @@
|
|
|
1
|
-
[
|
|
1
|
+
[project]
|
|
2
2
|
name = "pyixapi"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.5"
|
|
4
4
|
description = "Python API client library for IX-API"
|
|
5
|
-
authors = ["Guillaume Mazoyer
|
|
5
|
+
authors = [{ name = "Guillaume Mazoyer", email = "oss@mazoyer.eu" }]
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
license = "Apache-2.0"
|
|
8
|
-
|
|
8
|
+
requires-python = ">=3.9"
|
|
9
9
|
classifiers = [
|
|
10
10
|
"Intended Audience :: Developers",
|
|
11
11
|
"Programming Language :: Python :: 3",
|
|
12
|
-
"Programming Language :: Python :: 3.8",
|
|
13
12
|
"Programming Language :: Python :: 3.9",
|
|
14
13
|
"Programming Language :: Python :: 3.10",
|
|
15
14
|
"Programming Language :: Python :: 3.11",
|
|
15
|
+
"Programming Language :: Python :: 3.12",
|
|
16
|
+
"Programming Language :: Python :: 3.13",
|
|
16
17
|
]
|
|
18
|
+
dependencies = ["PyJWT>=2.4.0,<2.11", "requests>=2.20.0,<3.0"]
|
|
17
19
|
|
|
18
|
-
[
|
|
19
|
-
|
|
20
|
-
PyJWT = ">=2.4.0,<2.9"
|
|
21
|
-
requests = ">=2.20.0,<3.0"
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
dev = ["pytest", "ruff>=0.9,<0.10"]
|
|
22
22
|
|
|
23
|
-
[tool.poetry
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
[tool.poetry]
|
|
24
|
+
requires-poetry = ">=2.0"
|
|
25
|
+
packages = [{ include = "pyixapi" }]
|
|
26
26
|
|
|
27
27
|
[tool.ruff]
|
|
28
|
-
line-length =
|
|
29
|
-
|
|
30
|
-
exclude = [
|
|
31
|
-
".git",
|
|
32
|
-
".tox",
|
|
33
|
-
".venv",
|
|
34
|
-
"env",
|
|
35
|
-
"_build",
|
|
36
|
-
"build",
|
|
37
|
-
"dist",
|
|
38
|
-
"examples",
|
|
39
|
-
"__main__.py",
|
|
40
|
-
]
|
|
28
|
+
line-length = 120
|
|
29
|
+
exclude = [".git", ".tox", ".venv", "env", "_build", "build", "dist", "examples", "__main__.py"]
|
|
41
30
|
|
|
42
31
|
[tool.ruff.lint]
|
|
43
|
-
preview = true
|
|
44
|
-
|
|
45
32
|
select = [
|
|
46
33
|
"C90", # mccabe complexity
|
|
47
34
|
"E", # pycodestyle errors
|
|
@@ -67,12 +54,9 @@ line-ending = "auto"
|
|
|
67
54
|
[tool.ruff.lint.isort]
|
|
68
55
|
known-first-party = ["pyixapi"]
|
|
69
56
|
|
|
70
|
-
[tool.ruff.lint.pycodestyle]
|
|
71
|
-
max-line-length = 88
|
|
72
|
-
|
|
73
57
|
[tool.ruff.lint.mccabe]
|
|
74
58
|
max-complexity = 31
|
|
75
59
|
|
|
76
60
|
[build-system]
|
|
77
|
-
requires = ["poetry-core"]
|
|
61
|
+
requires = ["poetry-core>=2.0"]
|
|
78
62
|
build-backend = "poetry.core.masonry.api"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|