looker-sdk 24.16.1__tar.gz → 24.18.0__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.
Files changed (45) hide show
  1. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/PKG-INFO +1 -1
  2. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/looker_sdk/version.py +1 -1
  3. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/looker_sdk.egg-info/PKG-INFO +1 -1
  4. looker_sdk-24.18.0/looker_sdk.egg-info/SOURCES.txt +13 -0
  5. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/looker_sdk.egg-info/top_level.txt +0 -1
  6. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/setup.py +1 -1
  7. looker_sdk-24.16.1/looker_sdk/rtl/__init__.py +0 -22
  8. looker_sdk-24.16.1/looker_sdk/rtl/api_methods.py +0 -247
  9. looker_sdk-24.16.1/looker_sdk/rtl/api_settings.py +0 -194
  10. looker_sdk-24.16.1/looker_sdk/rtl/auth_session.py +0 -353
  11. looker_sdk-24.16.1/looker_sdk/rtl/auth_token.py +0 -101
  12. looker_sdk-24.16.1/looker_sdk/rtl/constants.py +0 -31
  13. looker_sdk-24.16.1/looker_sdk/rtl/hooks.py +0 -86
  14. looker_sdk-24.16.1/looker_sdk/rtl/model.py +0 -230
  15. looker_sdk-24.16.1/looker_sdk/rtl/requests_transport.py +0 -110
  16. looker_sdk-24.16.1/looker_sdk/rtl/serialize.py +0 -120
  17. looker_sdk-24.16.1/looker_sdk/rtl/transport.py +0 -137
  18. looker_sdk-24.16.1/looker_sdk/sdk/__init__.py +0 -0
  19. looker_sdk-24.16.1/looker_sdk/sdk/api40/__init__.py +0 -1
  20. looker_sdk-24.16.1/looker_sdk/sdk/api40/methods.py +0 -13283
  21. looker_sdk-24.16.1/looker_sdk/sdk/api40/models.py +0 -15641
  22. looker_sdk-24.16.1/looker_sdk/sdk/constants.py +0 -24
  23. looker_sdk-24.16.1/looker_sdk.egg-info/SOURCES.txt +0 -42
  24. looker_sdk-24.16.1/tests/__init__.py +0 -0
  25. looker_sdk-24.16.1/tests/conftest.py +0 -133
  26. looker_sdk-24.16.1/tests/integration/__init__.py +0 -2
  27. looker_sdk-24.16.1/tests/integration/test_methods.py +0 -681
  28. looker_sdk-24.16.1/tests/integration/test_netrc.py +0 -55
  29. looker_sdk-24.16.1/tests/rtl/__init__.py +0 -2
  30. looker_sdk-24.16.1/tests/rtl/test_api_methods.py +0 -216
  31. looker_sdk-24.16.1/tests/rtl/test_api_settings.py +0 -252
  32. looker_sdk-24.16.1/tests/rtl/test_auth_session.py +0 -284
  33. looker_sdk-24.16.1/tests/rtl/test_auth_token.py +0 -70
  34. looker_sdk-24.16.1/tests/rtl/test_requests_transport.py +0 -171
  35. looker_sdk-24.16.1/tests/rtl/test_serialize.py +0 -770
  36. looker_sdk-24.16.1/tests/rtl/test_transport.py +0 -34
  37. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/LICENSE.txt +0 -0
  38. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/MANIFEST.in +0 -0
  39. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/README.rst +0 -0
  40. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/looker_sdk/__init__.py +0 -0
  41. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/looker_sdk/error.py +0 -0
  42. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/looker_sdk/py.typed +0 -0
  43. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/looker_sdk.egg-info/dependency_links.txt +0 -0
  44. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/looker_sdk.egg-info/requires.txt +0 -0
  45. {looker_sdk-24.16.1 → looker_sdk-24.18.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: looker_sdk
3
- Version: 24.16.1
3
+ Version: 24.18.0
4
4
  Summary: Looker REST API
5
5
  Home-page: https://pypi.python.org/pypi/looker_sdk
6
6
  Author: Looker Data Sciences, Inc.
@@ -20,4 +20,4 @@
20
20
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
21
  # THE SOFTWARE.
22
22
 
23
- __version__ = "24.16.1"
23
+ __version__ = "24.18.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: looker-sdk
3
- Version: 24.16.1
3
+ Version: 24.18.0
4
4
  Summary: Looker REST API
5
5
  Home-page: https://pypi.python.org/pypi/looker_sdk
6
6
  Author: Looker Data Sciences, Inc.
@@ -0,0 +1,13 @@
1
+ LICENSE.txt
2
+ MANIFEST.in
3
+ README.rst
4
+ setup.py
5
+ looker_sdk/__init__.py
6
+ looker_sdk/error.py
7
+ looker_sdk/py.typed
8
+ looker_sdk/version.py
9
+ looker_sdk.egg-info/PKG-INFO
10
+ looker_sdk.egg-info/SOURCES.txt
11
+ looker_sdk.egg-info/dependency_links.txt
12
+ looker_sdk.egg-info/requires.txt
13
+ looker_sdk.egg-info/top_level.txt
@@ -49,7 +49,7 @@ setup(
49
49
  keywords=["Looker", "Looker API", "looker_sdk", "Looker API 4.0"],
50
50
  name=NAME,
51
51
  package_data={"looker_sdk": ["py.typed", "looker_sdk/looker-sample.ini"]},
52
- packages=find_packages(),
52
+ packages=find_packages(include=("looker_sdk",)),
53
53
  # restrict python to <=3.9.9 due to https://github.com/looker-open-source/sdk-codegen/issues/944
54
54
  python_requires=">=3.6",
55
55
  url="https://pypi.python.org/pypi/looker_sdk",
@@ -1,22 +0,0 @@
1
- # The MIT License (MIT)
2
- #
3
- # Copyright (c) 2019 Looker Data Sciences, Inc.
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
@@ -1,247 +0,0 @@
1
- # The MIT License (MIT)
2
- #
3
- # Copyright (c) 2019 Looker Data Sciences, Inc.
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- """Functionality for making authenticated API calls
24
- """
25
- import datetime
26
- import json
27
- from typing import Any, MutableMapping, Optional, Sequence, Type, Union
28
- import urllib.parse
29
-
30
- from looker_sdk import error
31
- from looker_sdk.rtl import model
32
- from looker_sdk.rtl import serialize
33
- from looker_sdk.rtl import transport
34
- from looker_sdk.rtl import auth_session
35
-
36
-
37
- TBody = Optional[
38
- Union[
39
- str,
40
- MutableMapping[str, str],
41
- Sequence[str],
42
- Sequence[int],
43
- model.Model,
44
- Sequence[model.Model],
45
- ]
46
- ]
47
- TStructure = Optional[Union[Any, Type[str], serialize.TStructure]]
48
- TReturn = Optional[Union[str, bytes, serialize.TDeserializeReturn]]
49
- TQueryParams = MutableMapping[
50
- str, Union[None, bool, str, int, Sequence[int], Sequence[str], datetime.datetime]
51
- ]
52
-
53
-
54
- class APIMethods:
55
- """Functionality for making authenticated API calls"""
56
-
57
- def __init__(
58
- self,
59
- auth: auth_session.AuthSession,
60
- deserialize: serialize.TDeserialize,
61
- serialize: serialize.TSerialize,
62
- transport: transport.Transport,
63
- api_version: str,
64
- ):
65
- self.auth = auth
66
- self.api_path = urllib.parse.urljoin(
67
- auth.settings.base_url, f"/api/{api_version}/"
68
- )
69
- self.deserialize = deserialize
70
- self.serialize = serialize
71
- self.transport = transport
72
-
73
- def _path(self, path: str) -> str:
74
- if path[0] == "/":
75
- path = path[1:]
76
- return urllib.parse.urljoin(self.api_path, path)
77
-
78
- def __enter__(self) -> "APIMethods":
79
- return self
80
-
81
- def __exit__(self, *exc) -> None:
82
- self.auth.logout()
83
-
84
- def _return(self, response: transport.Response, structure: TStructure) -> TReturn:
85
- encoding = response.encoding
86
- if not response.ok:
87
- value = response.value.decode(encoding=encoding)
88
- sdk_error: error.SDKError
89
- try:
90
- sdk_error = self.deserialize(data=value, structure=error.SDKError) # type: ignore
91
- helper = error.ErrorDocHelper()
92
- (sdk_error.error_doc_url, sdk_error.error_doc) = (
93
- helper.parse_and_lookup(sdk_error.documentation_url)
94
- )
95
- for e in sdk_error.errors:
96
- (e.error_doc_url, e.error_doc) = helper.parse_and_lookup(
97
- e.documentation_url
98
- )
99
- except serialize.DeserializeError:
100
- raise error.SDKError(value)
101
- raise sdk_error
102
- ret: TReturn
103
- if structure is None:
104
- ret = None
105
- elif response.response_mode == transport.ResponseMode.BINARY:
106
- ret = response.value
107
- else:
108
- value = response.value.decode(encoding=encoding)
109
- if structure is Union[str, bytes] or structure is str or value == "": # type: ignore
110
- ret = value
111
- else:
112
- # ignore type: mypy bug doesn't recognized kwarg
113
- # `structure` to partial func
114
- ret = self.deserialize(data=value, structure=structure) # type: ignore
115
- return ret
116
-
117
- def _convert_query_params(
118
- self, query_params: TQueryParams
119
- ) -> MutableMapping[str, str]:
120
- params: MutableMapping[str, str] = {}
121
- for k, v in query_params.items():
122
- if v is None:
123
- continue
124
- if isinstance(v, datetime.datetime):
125
- params[k] = f'{v.isoformat(timespec="minutes")}Z'
126
- elif isinstance(v, str):
127
- params[k] = v
128
- elif isinstance(v, model.DelimSequence):
129
- params[k] = str(v)
130
- else:
131
- params[k] = json.dumps(v)
132
- return params
133
-
134
- @staticmethod
135
- def encode_path_param(value: str) -> str:
136
- if value == urllib.parse.unquote(value):
137
- value = urllib.parse.quote(value, safe="")
138
- return value
139
-
140
- def get(
141
- self,
142
- path: str,
143
- structure: TStructure,
144
- query_params: Optional[TQueryParams] = None,
145
- transport_options: Optional[transport.TransportOptions] = None,
146
- ) -> TReturn:
147
- """GET method"""
148
- params = self._convert_query_params(query_params) if query_params else None
149
- response = self.transport.request(
150
- transport.HttpMethod.GET,
151
- self._path(path),
152
- query_params=params,
153
- body=None,
154
- authenticator=self.auth.authenticate,
155
- transport_options=transport_options,
156
- )
157
- return self._return(response, structure)
158
-
159
- def _get_serialized(self, body: TBody) -> Optional[bytes]:
160
- serialized: Optional[bytes]
161
- if isinstance(body, str):
162
- serialized = body.encode("utf-8")
163
- elif isinstance(body, (list, dict, model.Model)):
164
- serialized = self.serialize(api_model=body) # type: ignore
165
- else:
166
- serialized = None
167
- return serialized
168
-
169
- def post(
170
- self,
171
- path: str,
172
- structure: TStructure,
173
- query_params: Optional[TQueryParams] = None,
174
- body: TBody = None,
175
- transport_options: Optional[transport.TransportOptions] = None,
176
- ) -> TReturn:
177
- """POST method"""
178
- params = self._convert_query_params(query_params) if query_params else None
179
- serialized = self._get_serialized(body)
180
- response = self.transport.request(
181
- transport.HttpMethod.POST,
182
- self._path(path),
183
- query_params=params,
184
- body=serialized,
185
- authenticator=self.auth.authenticate,
186
- transport_options=transport_options,
187
- )
188
- return self._return(response, structure)
189
-
190
- def patch(
191
- self,
192
- path: str,
193
- structure: TStructure,
194
- query_params: Optional[TQueryParams] = None,
195
- body: TBody = None,
196
- transport_options: Optional[transport.TransportOptions] = None,
197
- ) -> TReturn:
198
- """PATCH method"""
199
- params = self._convert_query_params(query_params) if query_params else None
200
- serialized = self._get_serialized(body)
201
- response = self.transport.request(
202
- transport.HttpMethod.PATCH,
203
- self._path(path),
204
- query_params=params,
205
- body=serialized,
206
- authenticator=self.auth.authenticate,
207
- transport_options=transport_options,
208
- )
209
- return self._return(response, structure)
210
-
211
- def put(
212
- self,
213
- path: str,
214
- structure: TStructure = None,
215
- query_params: Optional[TQueryParams] = None,
216
- body: TBody = None,
217
- transport_options: Optional[transport.TransportOptions] = None,
218
- ) -> TReturn:
219
- """PUT method"""
220
- params = self._convert_query_params(query_params) if query_params else None
221
- serialized = self._get_serialized(body)
222
- response = self.transport.request(
223
- transport.HttpMethod.PUT,
224
- self._path(path),
225
- query_params=params,
226
- body=serialized,
227
- authenticator=self.auth.authenticate,
228
- transport_options=transport_options,
229
- )
230
- return self._return(response, structure)
231
-
232
- def delete(
233
- self,
234
- path: str,
235
- structure: TStructure = None,
236
- query_params: Optional[TQueryParams] = None,
237
- transport_options: Optional[transport.TransportOptions] = None,
238
- ) -> TReturn:
239
- """DELETE method"""
240
- response = self.transport.request(
241
- transport.HttpMethod.DELETE,
242
- self._path(path),
243
- body=None,
244
- authenticator=self.auth.authenticate,
245
- transport_options=transport_options,
246
- )
247
- return self._return(response, structure)
@@ -1,194 +0,0 @@
1
- # The MIT License (MIT)
2
- #
3
- # Copyright (c) 2019 Looker Data Sciences, Inc.
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- """Load settings from .ini file and create an ApiSettings object
24
- with the settings as attributes
25
- """
26
- import configparser as cp
27
- import os
28
- import sys
29
- from typing import Dict, Optional, Set, cast
30
- import warnings
31
-
32
- from looker_sdk.rtl import transport
33
-
34
- if sys.version_info >= (3, 8):
35
- from typing import Protocol, TypedDict
36
- else:
37
- from typing_extensions import Protocol, TypedDict
38
- from typing_extensions import Required
39
-
40
-
41
- class SettingsConfig(TypedDict, total=False):
42
- client_id: Required[str]
43
- client_secret: Required[str]
44
- base_url: str
45
- verify_ssl: str
46
- timeout: str
47
- redirect_uri: str
48
- looker_url: str
49
-
50
-
51
- class PApiSettings(transport.PTransportSettings, Protocol):
52
- def read_config(self) -> SettingsConfig:
53
- ...
54
-
55
-
56
- _DEFAULT_INIS = ["looker.ini", "../looker.ini"]
57
-
58
-
59
- class ApiSettings(PApiSettings):
60
- deprecated_settings: Set[str] = {"api_version", "embed_secret", "user_id"}
61
-
62
- def __init__(
63
- self,
64
- *,
65
- filename: str = _DEFAULT_INIS[0],
66
- section: Optional[str] = None,
67
- sdk_version: Optional[str] = "",
68
- env_prefix: Optional[str] = None,
69
- ):
70
- """Configure using a config file and/or environment variables.
71
-
72
- Environment variables will override config file settings. Neither
73
- is necessary but some combination must supply the minimum to
74
- instantiate ApiSettings.
75
-
76
- ENV variables map like this:
77
- <package-prefix>_BASE_URL -> base_url
78
- <package-prefix>_VERIFY_SSL -> verify_ssl
79
-
80
- Args:
81
- filename (str): config file. If specified, the file must exist.
82
- If not specified and the default value of "looker.ini" does not
83
- exist then no error is raised.
84
- section (str): section in config file. If not supplied default to
85
- reading first section.
86
- """
87
- if not os.path.isfile(filename):
88
- if filename and filename not in _DEFAULT_INIS:
89
- raise FileNotFoundError(f"No config file found: '{filename}'")
90
-
91
- self.filename = filename
92
- self.section = section
93
- self.env_prefix = env_prefix
94
- data = self.read_config()
95
- verify_ssl = data.get("verify_ssl")
96
- if verify_ssl is None:
97
- self.verify_ssl = True
98
- else:
99
- self.verify_ssl = self._bool(verify_ssl)
100
- self.base_url = data.get("base_url", "")
101
- self.timeout = int(data.get("timeout", 120))
102
- self.headers = {"Content-Type": "application/json"}
103
- self.agent_tag = f"{transport.AGENT_PREFIX}"
104
- if sdk_version:
105
- self.agent_tag += f" {sdk_version}"
106
-
107
- def read_config(self) -> SettingsConfig:
108
- cfg_parser = cp.ConfigParser()
109
- data: SettingsConfig = {
110
- "client_id": "",
111
- "client_secret": "",
112
- }
113
- try:
114
- config_file = open(self.filename)
115
- except FileNotFoundError:
116
- pass
117
- else:
118
- cfg_parser.read_file(config_file)
119
- config_file.close()
120
- # If section is not specified, use first section in file
121
- section = self.section or cfg_parser.sections()[0]
122
- if not cfg_parser.has_section(section):
123
- raise cp.NoSectionError(section)
124
- self._override_settings(data, dict(cfg_parser[section]))
125
-
126
- if self.env_prefix:
127
- self._override_settings(data, self._override_from_env())
128
- return self._clean_input(data)
129
-
130
- @staticmethod
131
- def _bool(val: str) -> bool:
132
- if val.lower() in ("yes", "y", "true", "t", "1"):
133
- converted = True
134
- elif val.lower() in ("", "no", "n", "false", "f", "0"):
135
- converted = False
136
- else:
137
- raise TypeError
138
- return converted
139
-
140
- def _override_settings(
141
- self, data: SettingsConfig, overrides: Dict[str, str]
142
- ) -> SettingsConfig:
143
- # https://github.com/python/mypy/issues/6262
144
- for setting in SettingsConfig.__annotations__.keys(): # type: ignore
145
- if setting in overrides:
146
- data[setting] = overrides[setting] # type: ignore
147
- return data
148
-
149
- def _override_from_env(self) -> Dict[str, str]:
150
- overrides = {}
151
- base_url = os.getenv(f"{self.env_prefix}_BASE_URL")
152
- if base_url:
153
- overrides["base_url"] = base_url
154
-
155
- verify_ssl = os.getenv(f"{self.env_prefix}_VERIFY_SSL")
156
- if verify_ssl:
157
- overrides["verify_ssl"] = verify_ssl
158
-
159
- timeout = os.getenv(f"{self.env_prefix}_TIMEOUT")
160
- if timeout:
161
- overrides["timeout"] = timeout
162
-
163
- client_id = os.getenv(f"{self.env_prefix}_CLIENT_ID")
164
- if client_id:
165
- overrides["client_id"] = client_id
166
-
167
- client_secret = os.getenv(f"{self.env_prefix}_CLIENT_SECRET")
168
- if client_secret:
169
- overrides["client_secret"] = client_secret
170
-
171
- return overrides
172
-
173
- def _clean_input(self, data: SettingsConfig) -> SettingsConfig:
174
- """Remove surrounding quotes and discard empty strings.
175
- """
176
- cleaned = {}
177
- for setting, value in data.items():
178
- if setting in self.deprecated_settings:
179
- warnings.warn(
180
- message=DeprecationWarning(
181
- f"'{setting}' config setting is deprecated"
182
- )
183
- )
184
- if not isinstance(value, str):
185
- continue
186
- # Remove empty setting values
187
- if value in ['""', "''", ""]:
188
- continue
189
- # Strip quotes from setting values
190
- elif value.startswith(('"', "'")) or value.endswith(('"', "'")):
191
- cleaned[setting] = value.strip("\"'")
192
- else:
193
- cleaned[setting] = value
194
- return cast(SettingsConfig, cleaned)