libtimed 0.6.4__tar.gz → 0.7.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,7 +1,8 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: libtimed
3
- Version: 0.6.4
3
+ Version: 0.7.3
4
4
  Summary: Library to intreact with timed webapp.
5
+ License-File: LICENSE
5
6
  Author: Arthur Deierlein
6
7
  Author-email: arthur.deierlein@adfinis.com
7
8
  Requires-Python: >=3.10,<4.0
@@ -9,8 +10,10 @@ Classifier: Programming Language :: Python :: 3
9
10
  Classifier: Programming Language :: Python :: 3.10
10
11
  Classifier: Programming Language :: Python :: 3.11
11
12
  Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
12
15
  Requires-Dist: inflection (>=0.5.1,<0.6.0)
13
- Requires-Dist: keyring (>=24.1.0,<25.0.0)
16
+ Requires-Dist: keyring (>=24.1,<26.0)
14
17
  Requires-Dist: requests (>=2.31.0,<3.0.0)
15
18
  Requires-Dist: requests-cache (>=1.1.0,<2.0.0)
16
19
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "libtimed"
3
- version = "0.6.4"
3
+ version = "0.7.3"
4
4
  description = "Library to intreact with timed webapp."
5
5
  authors = [
6
6
  "Arthur Deierlein <arthur.deierlein@adfinis.com>",
@@ -12,16 +12,15 @@ packages = [{ include = "libtimed", from = "src" }]
12
12
  [tool.poetry.dependencies]
13
13
  python = "^3.10"
14
14
  requests = "^2.31.0"
15
- keyring = "^24.1.0"
15
+ keyring = ">=24.1,<26.0"
16
16
  inflection = "^0.5.1"
17
17
  requests-cache = "^1.1.0"
18
18
 
19
19
  [tool.poetry.group.dev.dependencies]
20
- black = "^23.3.0"
21
- pytest = "^7.3.2"
20
+ pytest = ">=7.3.2,<9.0.0"
22
21
  pytest-coverage = "^0.0"
23
22
  pdbpp = "^0.10.3"
24
- ruff = "^0.0.282"
23
+ ruff = ">=0.3.4,<0.5.0"
25
24
 
26
25
  # Dependencies only used in examples
27
26
  [tool.poetry.group.example.dependencies]
@@ -29,9 +28,16 @@ pyfzf = "^0.3.1"
29
28
 
30
29
 
31
30
  [tool.ruff]
32
- line-length = 88
33
- select = ["F","E","W","I","N", "D", "B", "Q", "C4", "UP", "PT", "T20", "RET", "RUF", "SLF", "SIM", "C90"]
31
+ line-length = 100
34
32
  src = ["src", "examples", "test"]
33
+
34
+ [tool.ruff.format]
35
+ quote-style = "double"
36
+ indent-style = "space"
37
+ docstring-code-format = true
38
+
39
+ [tool.ruff.lint]
40
+ select = ["F","E","W","I","N", "D", "B", "Q", "C4", "UP", "PT", "T20", "RET", "RUF", "SLF", "SIM", "C90"]
35
41
  ignore = [
36
42
  # make docstrings optional
37
43
  "D100",
@@ -45,19 +51,37 @@ ignore = [
45
51
  "D211",
46
52
  "D213",
47
53
  "RUF012",
48
- # line length is handled by black
54
+ # line length is handled by formatter
49
55
  "E501",
50
56
  # disable this for now
51
57
  "SLF001",
58
+ # https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
59
+ "W191",
60
+ "E111",
61
+ "E114",
62
+ "E117",
63
+ "D206",
64
+ "D300",
65
+ "Q000",
66
+ "Q001",
67
+ "Q002",
68
+ "Q003",
69
+ "COM812",
70
+ "COM819",
71
+ "ISC001",
72
+ "ISC002",
73
+ # this conflicts for some reason
74
+ "D203"
52
75
  ]
53
76
 
54
- [tool.ruff.mccabe]
77
+ [tool.ruff.lint.mccabe]
55
78
  max-complexity = 8
56
79
 
57
- [tool.ruff.extend-per-file-ignores]
80
+ [tool.ruff.lint.extend-per-file-ignores]
58
81
  "examples/*.py" = ["T201"]
82
+ "src/libtimed/oidc.py" = ["T201"]
59
83
 
60
- [tool.ruff.isort]
84
+ [tool.ruff.lint.isort]
61
85
  known-first-party = ["libtimed"]
62
86
  combine-as-imports = true
63
87
 
@@ -46,16 +46,10 @@ class BaseModel:
46
46
  def _deserialize(self, item, included): # noqa: C901
47
47
  for key, value in item["attributes"].items():
48
48
  transform = next(
49
- (
50
- transform
51
- for name, _, transform in self.__class__.attributes
52
- if name == key
53
- ),
49
+ (transform for name, _, transform in self.__class__.attributes if name == key),
54
50
  None,
55
51
  )
56
- item["attributes"][key] = (
57
- (transform).deserialize(value) if transform else value
58
- )
52
+ item["attributes"][key] = (transform).deserialize(value) if transform else value
59
53
  relationships = item.get("relationships")
60
54
  if not relationships:
61
55
  return item
@@ -79,9 +73,7 @@ class BaseModel:
79
73
  None,
80
74
  )
81
75
  if included_item:
82
- included_model = self.client._type_map[item_type](
83
- self.client
84
- )
76
+ included_model = self.client._type_map[item_type](self.client)
85
77
  included_items.append(
86
78
  included_model._deserialize(included_item, included)
87
79
  )
@@ -130,9 +122,7 @@ class BaseModel:
130
122
  item = self._deserialize(item, resp.get("included", []))
131
123
  return resp if raw else resp.get("data")
132
124
 
133
- def post(
134
- self, attributes: dict | None = None, relationships: dict | None = None
135
- ) -> Response:
125
+ def post(self, attributes: dict | None = None, relationships: dict | None = None) -> Response:
136
126
  json = self._parse_post_json(attributes, relationships)
137
127
  return self.client.session.post(self.url, json=json)
138
128
 
@@ -151,9 +141,7 @@ class BaseModel:
151
141
  def resource_name(cls):
152
142
  return underscore(cls.__name__).replace("_", "-")
153
143
 
154
- def _parse_post_json(
155
- self, attributes: dict | None, relationships: dict | None
156
- ) -> dict:
144
+ def _parse_post_json(self, attributes: dict | None, relationships: dict | None) -> dict:
157
145
  return {
158
146
  "data": {
159
147
  "attributes": self._parse_attributes(attributes),
@@ -6,7 +6,7 @@ import http.server
6
6
  import json
7
7
  import time
8
8
  import webbrowser
9
- from urllib.parse import urlparse
9
+ from urllib.parse import parse_qs, urlparse
10
10
 
11
11
  import keyring
12
12
  import requests
@@ -19,14 +19,12 @@ class OIDCHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
19
19
  url_path = self.path
20
20
  # get the "code" parameter from the query string
21
21
  try:
22
- OIDCHTTPRequestHandler.code = urlparse(url_path).query.split("=")[2]
22
+ OIDCHTTPRequestHandler.code = parse_qs(urlparse(url_path).query)["code"][0]
23
23
  except IndexError:
24
24
  self.send_response(400)
25
25
  self.send_header("Content-type", "text/html")
26
26
  self.end_headers()
27
- self.wfile.write(
28
- b"<html><body><h1>Authorization failed.</h1></body></html>"
29
- )
27
+ self.wfile.write(b"<html><body><h1>Authorization failed.</h1></body></html>")
30
28
  return
31
29
  # send the response
32
30
  self.send_response(200)
@@ -93,12 +91,17 @@ class OIDCClient:
93
91
 
94
92
  resp = {}
95
93
  while "access_token" not in resp:
96
- resp = requests.post(self.token_endpoint, data={"client_id": self.client_id, "grant_type": "urn:ietf:params:oauth:grant-type:device_code", "device_code": device_code}).json()
94
+ resp = requests.post(
95
+ self.token_endpoint,
96
+ data={
97
+ "client_id": self.client_id,
98
+ "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
99
+ "device_code": device_code,
100
+ },
101
+ ).json()
97
102
  print(resp)
98
103
  time.sleep(5)
99
- access_token = resp["access_token"]
100
- return access_token
101
-
104
+ return resp["access_token"]
102
105
 
103
106
  def get_token(self):
104
107
  # construct the token request
@@ -112,10 +115,8 @@ class OIDCClient:
112
115
  token_response = requests.post(self.token_endpoint, data=token_request)
113
116
  # check for errors
114
117
  if token_response.status_code != 200:
115
- print( # noqa: T201
116
- f"Error: {token_response.status_code} {token_response.reason}\n"
117
- )
118
- print(token_response.text) # noqa: T201
118
+ print(f"Error: {token_response.status_code} {token_response.reason}\n")
119
+ print(token_response.text)
119
120
  return False
120
121
  # get the access token
121
122
  return token_response.json()["access_token"]
@@ -151,7 +152,7 @@ class OIDCClient:
151
152
 
152
153
  self.autoconfig()
153
154
  if self.use_device_flow:
154
- if token:= self.start_device_flow():
155
+ if token := self.start_device_flow():
155
156
  self.keyring_set(token)
156
157
  return token
157
158
  return False
@@ -6,12 +6,10 @@ TIME_FORMAT = "%H:%M:%S"
6
6
 
7
7
 
8
8
  class SerializationError(ValueError):
9
-
10
9
  """Error raised only inside of Transforms when trying to (de)serialize values."""
11
10
 
12
11
 
13
12
  class BaseTransform:
14
-
15
13
  """Base class for serializers."""
16
14
 
17
15
  @staticmethod
@@ -26,12 +24,9 @@ class BaseTransform:
26
24
 
27
25
 
28
26
  class Type(BaseTransform):
29
-
30
27
  """Transform for types."""
31
28
 
32
- def __init__(
33
- self, type: type, allow_none: bool = True, pipe: Callable = lambda x: x
34
- ):
29
+ def __init__(self, type: type, allow_none: bool = True, pipe: Callable = lambda x: x):
35
30
  self.type = type
36
31
  self.pipe = pipe
37
32
  self.allow_none = allow_none
@@ -51,7 +46,6 @@ class Type(BaseTransform):
51
46
 
52
47
 
53
48
  class Duration(BaseTransform):
54
-
55
49
  """Transform for durations."""
56
50
 
57
51
  @staticmethod
@@ -83,7 +77,6 @@ class RelationShipProperty:
83
77
 
84
78
 
85
79
  class Relationship(BaseTransform):
86
-
87
80
  """Transform for relationships. This is very hacky and should be replaced with a better solution."""
88
81
 
89
82
  def __init__(self, related_model) -> None:
@@ -102,9 +95,7 @@ class Relationship(BaseTransform):
102
95
  data = {}
103
96
  if isinstance(value, RelationShipProperty):
104
97
  if not client:
105
- raise SerializationError(
106
- "Client has to be passed when using a property"
107
- )
98
+ raise SerializationError("Client has to be passed when using a property")
108
99
  try:
109
100
  data["data"] = getattr(self.related_model(client), value.property_name)
110
101
  except AttributeError as e:
@@ -118,9 +109,7 @@ class Relationship(BaseTransform):
118
109
 
119
110
  def deserialize(self, value: dict) -> dict | None:
120
111
  data = value.get("data") or {}
121
- if (
122
- recieved_type := (data or {}).get("type")
123
- ) != self.related_model.resource_name:
112
+ if (recieved_type := (data or {}).get("type")) != self.related_model.resource_name:
124
113
  if recieved_type is None:
125
114
  return None
126
115
  raise SerializationError(
@@ -130,7 +119,6 @@ class Relationship(BaseTransform):
130
119
 
131
120
 
132
121
  class Date(BaseTransform):
133
-
134
122
  """Transform for dates."""
135
123
 
136
124
  @staticmethod
@@ -150,7 +138,6 @@ class Date(BaseTransform):
150
138
 
151
139
 
152
140
  class Time(BaseTransform):
153
-
154
141
  """Transform for times."""
155
142
 
156
143
  def __init__(self, return_datetime: bool = False):
@@ -175,9 +162,7 @@ class Time(BaseTransform):
175
162
  return value.strftime(TIME_FORMAT) if value else None
176
163
 
177
164
  def deserialize(self, value) -> time | datetime | None:
178
- return (
179
- self._return_value(datetime.strptime(value, "%H:%M:%S")) if value else None
180
- )
165
+ return self._return_value(datetime.strptime(value, "%H:%M:%S")) if value else None
181
166
 
182
167
 
183
168
  class Enum(BaseTransform):
File without changes
File without changes